View Javadoc

1   /*******************************************************************************
2    * Copyright (c) 2015 LegSem.
3    * All rights reserved. This program and the accompanying materials
4    * are made available under the terms of the GNU Lesser Public License v2.1
5    * which accompanies this distribution, and is available at
6    * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
7    * 
8    * Contributors:
9    *     LegSem - initial API and implementation
10   ******************************************************************************/
11  package com.legstar.coxb.convert.simple;
12  
13  import com.legstar.coxb.CobolContext;
14  import com.legstar.coxb.ICobolArrayDoubleBinding;
15  import com.legstar.coxb.ICobolDoubleBinding;
16  import com.legstar.coxb.convert.ICobolDoubleConverter;
17  import com.legstar.coxb.convert.CobolConversionException;
18  import com.legstar.coxb.host.HostData;
19  import com.legstar.coxb.host.HostDouble;
20  import com.legstar.coxb.host.HostException;
21  
22  import java.util.ArrayList;
23  import java.util.List;
24  
25  /**
26   * This is a concrete implementation of marshal/unmarshal operations of java
27   * double to cobol comp-2.
28   * 
29   * @author Fady Moussallam
30   * 
31   */
32  public class CobolDoubleSimpleConverter extends CobolSimpleConverter
33          implements ICobolDoubleConverter {
34  
35      /**
36       * @param cobolContext the Cobol compiler parameters in effect
37       */
38      public CobolDoubleSimpleConverter(final CobolContext cobolContext) {
39          super(cobolContext);
40      }
41  
42      /** {@inheritDoc} */
43      public int toHost(
44              final ICobolDoubleBinding ce,
45              final byte[] hostTarget,
46              final int offset)
47              throws HostException {
48          int newOffset = 0;
49          try {
50              newOffset = toHostSingle(ce.getDoubleValue(),
51                      hostTarget,
52                      offset);
53          } catch (CobolConversionException e) {
54              throwHostException(ce, e);
55          }
56          return newOffset;
57      }
58  
59      /** {@inheritDoc} */
60      public int toHost(
61              final ICobolArrayDoubleBinding ce,
62              final byte[] hostTarget,
63              final int offset,
64              final int currentOccurs)
65              throws HostException {
66          int newOffset = offset;
67          try {
68              for (Double javaSource : ce.getDoubleList()) {
69                  newOffset = toHostSingle(javaSource,
70                          hostTarget,
71                          newOffset);
72              }
73              /* If necessary, fill in the array with missing items */
74              for (int i = ce.getDoubleList().size(); i < currentOccurs; i++) {
75                  newOffset = toHostSingle(Double.valueOf(0),
76                          hostTarget,
77                          newOffset);
78              }
79          } catch (CobolConversionException e) {
80              throwHostException(ce, e);
81          }
82          return newOffset;
83      }
84  
85      /** {@inheritDoc} */
86      public int fromHost(
87              final ICobolDoubleBinding ce,
88              final byte[] hostSource,
89              final int offset)
90              throws HostException {
91          int newOffset = offset;
92          try {
93              Double javaDouble = fromHostSingle(ce.getByteLength(),
94                      hostSource,
95                      newOffset);
96              ce.setDoubleValue(javaDouble);
97              newOffset += ce.getByteLength();
98          } catch (CobolConversionException e) {
99              throwHostException(ce, e);
100         }
101         return newOffset;
102     }
103 
104     /** {@inheritDoc} */
105     public int fromHost(
106             final ICobolArrayDoubleBinding ce,
107             final byte[] hostSource,
108             final int offset,
109             final int currentOccurs)
110             throws HostException {
111         List < Double > lArray = new ArrayList < Double >();
112         int newOffset = offset;
113         try {
114             for (int i = 0; i < currentOccurs; i++) {
115                 Double javaDouble = fromHostSingle(ce.getItemByteLength(),
116                         hostSource,
117                         newOffset);
118                 lArray.add(javaDouble);
119                 newOffset += ce.getItemByteLength();
120             }
121             ce.setDoubleList(lArray);
122         } catch (CobolConversionException e) {
123             throwHostException(ce, e);
124         }
125         return newOffset;
126     }
127 
128     /**
129      * Converts a Java Double to a host comp-2.
130      * 
131      * @param javaDouble java double to convert
132      * @param hostTarget target host buffer
133      * @param offset offset in target host buffer
134      * @return offset after host buffer is updated
135      * @throws CobolConversionException if conversion fails
136      */
137     public static final int toHostSingle(
138             final Double javaDouble,
139             final byte[] hostTarget,
140             final int offset)
141             throws CobolConversionException {
142 
143         /* Comp-2 are 8 byte long */
144         int cobolByteLength = 8;
145 
146         /* Check that we are still within the host target range */
147         int lastOffset = offset + cobolByteLength;
148         if (lastOffset > hostTarget.length) {
149             throw (new CobolConversionException(
150                     "Attempt to write past end of host source buffer",
151                     new HostData(hostTarget), offset));
152         }
153 
154         /* Provide a default if input is null */
155         Double localDouble = javaDouble;
156         if (localDouble == null) {
157             localDouble = 0d;
158         }
159 
160         /* Host doubles do not support NaN or infinite. */
161         if (localDouble.isInfinite()) {
162             throw (new CobolConversionException(
163                     "Infinite doubles are not supported",
164                     new HostData(hostTarget), offset));
165         }
166         if (localDouble.isNaN()) {
167             throw (new CobolConversionException(
168                     "NaN doubles are not supported",
169                     new HostData(hostTarget), offset));
170         }
171 
172         /*
173          * Treat the zero case separatly because the bit layout is not
174          * consistent.
175          */
176         if ((localDouble.doubleValue() == 0.0f)
177                 ||
178                 (localDouble.doubleValue() == -0.0f)) {
179             for (int i = 0; i < 8; i++) {
180                 hostTarget[offset + i] = 0;
181             }
182             return offset + cobolByteLength;
183         }
184 
185         /* Parse the Java double to get sign, exponent and mantissa */
186         HostDouble jF = parseJavaDouble(localDouble);
187 
188         /* Create a representation of the corresponding host double */
189         HostDouble hF = new HostDouble();
190         hF.setSign(jF.getSign());
191 
192         /*
193          * The java exponent is a binary offset while the host exponent is an
194          * hexadecimal offset. This means host exponent values are multiple
195          * or 4. If the java exponent is not a multiple of 4 we then need
196          * to shift the mantissa which might result in loss of precision.
197          */
198         int r = jF.getExponent() % 4;
199         int mantissaShift = 0;
200         if (r <= 0) {
201             mantissaShift = -1 * r;
202         } else {
203             mantissaShift = 4 - r;
204         }
205 
206         hF.setExponent((jF.getExponent() + mantissaShift) / 4);
207         hF.setMantissa(jF.getMantissa() >> mantissaShift);
208 
209         /*
210          * The host double mantissa is stored on 56 bits while java is on 53
211          * (52 + 1 plus).
212          * bits so we need to padd the java mantissa with an additional 3 bits
213          * to the right
214          */
215         hF.setMantissa(hF.getMantissa() << 3);
216 
217         /* Now assemble the host double 8 bytes */
218         long hostLongBits = createHostDouble(hF);
219 
220         /* Store the bytes in the host buffer */
221         for (int i = 0; i < 8; i++) {
222             hostTarget[offset + i] =
223                     (byte) ((hostLongBits >>> (56 - i * 8)) & 0xff);
224         }
225 
226         return offset + cobolByteLength;
227     }
228 
229     /**
230      * Converts a host comp-2 to a Java Double.
231      * 
232      * @param cobolByteLength host byte length
233      * @param hostSource source host buffer
234      * @param offset offset in source host buffer
235      * @return offset after host buffer is read
236      * @throws CobolConversionException if conversion fails
237      */
238     public static final Double fromHostSingle(
239             final int cobolByteLength,
240             final byte[] hostSource,
241             final int offset)
242             throws CobolConversionException {
243 
244         int lastOffset = offset + cobolByteLength;
245 
246         /*
247          * Check that we are still within the host source range.
248          * If not, consider the host optimized its payload by truncating
249          * trailing nulls in which case, we just need to initialize and return.
250          */
251         if (lastOffset > hostSource.length) {
252             return Double.valueOf(0d);
253         }
254 
255         /* Create a host double representation from the bytes we have */
256         long hostLongBits = 0;
257         for (int i = 0; i < 8; i++) {
258             hostLongBits = hostLongBits
259                     | ((long) (hostSource[offset + (7 - i)] & 0xff) << (i * 8));
260         }
261         HostDouble hF = parseHostDouble(hostLongBits);
262 
263         /* Create a representation of the corresponding java double */
264         HostDouble jF = new HostDouble();
265         jF.setSign(hF.getSign());
266 
267         /*
268          * Host exponent is hexadecimal based while java is binary based.
269          * There is also an additional shift for non-zero values due to
270          * the 1-plus" normalized java specs.
271          */
272         if (hF.getMantissa() != 0) {
273             jF.setExponent((4 * hF.getExponent()) - 1);
274         }
275 
276         /*
277          * The java mantissa is 53 bits while the host is 56. This
278          * means there is a systematic loss of precision.
279          */
280         jF.setMantissa(hF.getMantissa() >>> 3);
281 
282         /* In java the 53th bit needs to be one */
283         while ((jF.getMantissa() > 0L)
284                 &&
285                 (jF.getMantissa() & 0x0010000000000000L) == 0) {
286             jF.setMantissa(jF.getMantissa() << 1);
287             jF.setExponent(jF.getExponent() - 1);
288         }
289 
290         return createJavaDouble(jF);
291     }
292 
293     /**
294      * Extracts the sign, exponent and mantissa from a Java double.
295      * 
296      * @param f the double to extract components from
297      * @return a class holding the various components
298      */
299     public static final HostDouble parseJavaDouble(final double f) {
300 
301         HostDouble jF = new HostDouble();
302 
303         /* Zero is a special case */
304         if (f == 0.0f) {
305             return jF;
306         }
307 
308         long javaLongBits = Double.doubleToLongBits(f);
309 
310         /* First bit left (bit 63) is the sign: 0 = positive, 1 = negative */
311         jF.setSign((int) ((javaLongBits & 0x8000000000000000L) >>> 63));
312 
313         /*
314          * Bits 62-52 (11 bits) represents the exponent offset by 1023, this
315          * number is called excess so you get the exponent as E= excess - 1023
316          * Furthermore, the "1-plus" normalized representation has the decimal
317          * point after the implicit initial 1. Here we elect to store the
318          * exponent for decimal point before that initial 1.
319          */
320         int excess = (int) ((javaLongBits >> 52) & 0x7ffL);
321         jF.setExponent(excess - 1023 + 1);
322 
323         /*
324          * Bits 51-0 (52 bits) represents the mantissa in a form called
325          * "1-plus" normalized. This means that the real mantissa is actually
326          * 1.b(51)b(50)...b(0) where the intiial "1" is implicit.
327          * This code will explicitly add 1 in front of the mantissa.
328          */
329         long orMask = 1L << 52;
330         jF.setMantissa(javaLongBits & 0x000fffffffffffffL | orMask);
331 
332         return jF;
333     }
334 
335     /**
336      * Reconstruct a java double from its sign, exponent and mantissa
337      * components.
338      * 
339      * @param jF a class holding the various components
340      * @return a Java double
341      */
342     public static final double createJavaDouble(final HostDouble jF) {
343 
344         /* First check if this is a zero value */
345         if (jF.getExponent() == 0 && jF.getMantissa() == 0) {
346             return 0d;
347         }
348 
349         /* Get rid of the leading 1 which needs to be implicit */
350         long javaLongBits = jF.getMantissa() & 0x000fffffffffffffL;
351         javaLongBits = javaLongBits | ((long) (jF.getExponent() + 1023) << 52);
352         javaLongBits = javaLongBits | ((long) jF.getSign() << 63);
353         return Double.longBitsToDouble(javaLongBits);
354     }
355 
356     /**
357      * Extracts the sign, exponent and mantissa from a Host double .
358      * 
359      * @param hostLongBits the bit sequence representing the host double
360      * @return a class holding the various components
361      */
362     public static final HostDouble parseHostDouble(final long hostLongBits) {
363         HostDouble hF = new HostDouble();
364 
365         /* First bit left (bit 63) is the sign: 0 = positive, 1 = negative */
366         hF.setSign((int) ((hostLongBits & 0x8000000000000000L) >>> 63));
367 
368         /*
369          * Bits 62-56 (7 bits) represents the exponent offset by 64, this
370          * number is called excess so you get the exponent as
371          * E= excess - 64
372          */
373         int excess = (int) ((hostLongBits & 0x7f00000000000000L) >>> 56);
374         if (excess != 0) {
375             hF.setExponent((excess - 64));
376         }
377 
378         /* Bits 55-0 (56 bits) represents the mantissa. */
379         hF.setMantissa(hostLongBits & 0x00ffffffffffffffL);
380 
381         return hF;
382     }
383 
384     /**
385      * Reconstruct a host double from its sign, exponent and mantissa
386      * components.
387      * 
388      * @param hF a class holding the various components
389      * @return the bit sequence representing the host double
390      */
391     public static final long createHostDouble(final HostDouble hF) {
392 
393         /* First check if this is a zero value */
394         if (hF.getExponent() == 0 && hF.getMantissa() == 0) {
395             return 0L;
396         }
397 
398         long hostLongBits = hF.getMantissa();
399         hostLongBits = hostLongBits | ((long) (hF.getExponent() + 64) << 56);
400         hostLongBits = hostLongBits | ((long) hF.getSign() << 63);
401         return hostLongBits;
402     }
403 }