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.ICobolArrayFloatBinding;
15  import com.legstar.coxb.ICobolFloatBinding;
16  import com.legstar.coxb.convert.ICobolFloatConverter;
17  import com.legstar.coxb.convert.CobolConversionException;
18  import com.legstar.coxb.host.HostData;
19  import com.legstar.coxb.host.HostException;
20  import com.legstar.coxb.host.HostFloat;
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   * float to cobol comp-1.
28   * 
29   * @author Fady Moussallam
30   * 
31   */
32  public class CobolFloatSimpleConverter extends CobolSimpleConverter
33          implements ICobolFloatConverter {
34  
35      /**
36       * @param cobolContext the Cobol compiler parameters in effect
37       */
38      public CobolFloatSimpleConverter(final CobolContext cobolContext) {
39          super(cobolContext);
40      }
41  
42      /** {@inheritDoc} */
43      public int toHost(
44              final ICobolFloatBinding ce,
45              final byte[] hostTarget,
46              final int offset)
47              throws HostException {
48          int newOffset = 0;
49          try {
50              newOffset = toHostSingle(ce.getFloatValue(),
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 ICobolArrayFloatBinding ce,
62              final byte[] hostTarget,
63              final int offset,
64              final int currentOccurs)
65              throws HostException {
66          int newOffset = offset;
67          try {
68              for (Float javaSource : ce.getFloatList()) {
69                  newOffset = toHostSingle(javaSource,
70                          hostTarget,
71                          newOffset);
72              }
73              /* If necessary, fill in the array with missing items */
74              for (int i = ce.getFloatList().size(); i < currentOccurs; i++) {
75                  newOffset = toHostSingle(Float.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 ICobolFloatBinding ce,
88              final byte[] hostSource,
89              final int offset)
90              throws HostException {
91          int newOffset = offset;
92          try {
93              Float javaFloat = fromHostSingle(ce.getByteLength(),
94                      hostSource,
95                      newOffset);
96              ce.setFloatValue(javaFloat);
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 ICobolArrayFloatBinding ce,
107             final byte[] hostSource,
108             final int offset,
109             final int currentOccurs)
110             throws HostException {
111         List < Float > lArray = new ArrayList < Float >();
112         int newOffset = offset;
113         try {
114             for (int i = 0; i < currentOccurs; i++) {
115                 Float javaFloat = fromHostSingle(ce.getItemByteLength(),
116                         hostSource,
117                         newOffset);
118                 lArray.add(javaFloat);
119                 newOffset += ce.getItemByteLength();
120             }
121             ce.setFloatList(lArray);
122         } catch (CobolConversionException e) {
123             throwHostException(ce, e);
124         }
125         return newOffset;
126     }
127 
128     /**
129      * Converts a Java Float to a host comp-1.
130      * 
131      * @param javaFloat java float 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 Float javaFloat,
139             final byte[] hostTarget,
140             final int offset)
141             throws CobolConversionException {
142 
143         /* Comp-1 are 4 byte long */
144         int cobolByteLength = 4;
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         Float localFloat = javaFloat;
156         if (localFloat == null) {
157             localFloat = 0f;
158         }
159 
160         /* Host floats do not support NaN or infinite. */
161         if (localFloat.isInfinite()) {
162             throw (new CobolConversionException(
163                     "Infinite floats are not supported",
164                     new HostData(hostTarget), offset));
165         }
166         if (localFloat.isNaN()) {
167             throw (new CobolConversionException(
168                     "NaN floats 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 ((localFloat.floatValue() == 0.0f)
177                 ||
178                 (localFloat.floatValue() == -0.0f)) {
179             for (int i = 0; i < 4; i++) {
180                 hostTarget[offset + i] = 0;
181             }
182             return offset + cobolByteLength;
183         }
184 
185         /* Parse the Java float to get sign, exponent and mantissa */
186         HostFloat jF = parseJavaFloat(localFloat);
187 
188         /* Create a representation of the corresponding host float */
189         HostFloat hF = new HostFloat();
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         /* Now assemble the host float 4 bytes */
210         int hostIntBits = createHostFloat(hF);
211 
212         /* Store the bytes in the host buffer */
213         for (int i = 0; i < 4; i++) {
214             hostTarget[offset + i] = (byte) ((hostIntBits >>> (24 - i * 8)) & 0xff);
215         }
216 
217         return offset + cobolByteLength;
218     }
219 
220     /**
221      * Converts a host comp-1 to a Java Float.
222      * 
223      * @param cobolByteLength host byte length
224      * @param hostSource source host buffer
225      * @param offset offset in source host buffer
226      * @return offset after host buffer is read
227      * @throws CobolConversionException if conversion fails
228      */
229     public static final Float fromHostSingle(
230             final int cobolByteLength,
231             final byte[] hostSource,
232             final int offset)
233             throws CobolConversionException {
234 
235         int lastOffset = offset + cobolByteLength;
236 
237         /*
238          * Check that we are still within the host source range.
239          * If not, consider the host optimized its payload by truncating
240          * trailing nulls in which case, we just need to initialize and return.
241          */
242         if (lastOffset > hostSource.length) {
243             return Float.valueOf(0f);
244         }
245 
246         /* Create a host float representation from the bytes we have */
247         int hostIntBits = 0;
248         for (int i = 0; i < 4; i++) {
249             hostIntBits = hostIntBits
250                     | ((hostSource[offset + (3 - i)] & 0xff) << (i * 8));
251         }
252         HostFloat hF = parseHostFloat(hostIntBits);
253 
254         /* Create a representation of the corresponding java float */
255         HostFloat jF = new HostFloat();
256         jF.setSign(hF.getSign());
257 
258         /*
259          * Host exponent is hexadecimal based while java is binary based.
260          * There is also an additional shift for non-zero values due to
261          * the 1-plus" normalized java specs.
262          */
263         if (hF.getMantissa() != 0) {
264             jF.setExponent((4 * hF.getExponent()) - 1);
265         }
266 
267         /* First assume same getMantissa() */
268         jF.setMantissa(hF.getMantissa());
269 
270         /* In java the 24th bit needs to be one */
271         while ((jF.getMantissa() > 0) && (jF.getMantissa() & 0x00800000) == 0) {
272             jF.setMantissa(jF.getMantissa() << 1);
273             jF.setExponent(jF.getExponent() - 1);
274         }
275 
276         return createJavaFloat(jF);
277     }
278 
279     /**
280      * Extracts the sign, exponent and getMantissa() from a Java float.
281      * 
282      * @param f the float to extract components from
283      * @return a class holding the various components
284      */
285     public static final HostFloat parseJavaFloat(final float f) {
286         HostFloat jF = new HostFloat();
287         /* Zero is a special case */
288         if (f == 0.0f) {
289             return jF;
290         }
291 
292         int javaIntBits = Float.floatToIntBits(f);
293 
294         /* First bit left (bit 31) is the sign: 0 = positive, 1 = negative */
295         jF.setSign((javaIntBits & 0x80000000) >>> 31);
296 
297         /*
298          * Bits 30-23 (8 bits) represents the exponent offset by 127, this
299          * number is called excess so you get the exponent as
300          * E= excess - 127. This is a binary exponent ( 2 pow(E)).
301          * Furthermore, the "1-plus" normalized representation has the decimal
302          * point after the implicit initial 1. Here we elect to store the
303          * exponent for decimal point before that initial 1.
304          */
305         int excess = (javaIntBits & 0x7f800000) >>> 23;
306         jF.setExponent(excess - 127 + 1);
307 
308         /*
309          * Bits 22-0 (23 bits) represents the getMantissa() in a form called
310          * "1-plus" normalized. This means that the real getMantissa() is
311          * actually * 1.b(23)b(22)...b(0) where the initial "1" is implicit.
312          * This code will explicitly add 1 in front of the getMantissa().
313          */
314         int orMask = 1 << 23;
315         jF.setMantissa(javaIntBits & 0x007fffff | orMask);
316 
317         return jF;
318     }
319 
320     /**
321      * Reconstruct a java float from its sign, exponent and getMantissa()
322      * components.
323      * 
324      * @param jF a class holding the various components
325      * @return a Java float
326      */
327     public static final float createJavaFloat(final HostFloat jF) {
328         /* First check if this is a zero value */
329         if (jF.getExponent() == 0 && jF.getMantissa() == 0) {
330             return 0f;
331         }
332 
333         /* Get rid of the leading 1 which needs to be implicit */
334         int javaIntBits = jF.getMantissa() & 0x007fffff;
335         javaIntBits = javaIntBits | ((jF.getExponent() + 127) << 23);
336         javaIntBits = javaIntBits | (jF.getSign() << 31);
337         return Float.intBitsToFloat(javaIntBits);
338     }
339 
340     /**
341      * Extracts the sign, exponent and getMantissa() from a Host float .
342      * 
343      * @param hostIntBits the bit sequence representing the host float
344      * @return a class holding the various components
345      */
346     public static final HostFloat parseHostFloat(final int hostIntBits) {
347 
348         HostFloat hF = new HostFloat();
349 
350         /* First bit left (bit 31) is the sign: 0 = positive, 1 = negative */
351         hF.setSign((hostIntBits & 0x80000000) >>> 31);
352 
353         /*
354          * Bits 30-24 (7 bits) represents the exponent offset by 64, this
355          * number is called excess so you get the exponent as
356          * E= excess - 64
357          */
358         int excess = (hostIntBits & 0x7f000000) >>> 24;
359         if (excess != 0) {
360             hF.setExponent((excess - 64));
361         }
362 
363         /* Bits 23-0 (24 bits) represents the getMantissa(). */
364         hF.setMantissa(hostIntBits & 0x00ffffff);
365 
366         return hF;
367     }
368 
369     /**
370      * Reconstruct a host float from its sign, exponent and getMantissa()
371      * components.
372      * 
373      * @param hF a class holding the various components
374      * @return the bit sequence representing the host float
375      */
376     public static final int createHostFloat(final HostFloat hF) {
377 
378         /* First check if this is a zero value */
379         if (hF.getExponent() == 0 && hF.getMantissa() == 0) {
380             return 0;
381         }
382 
383         int hostIntBits = hF.getMantissa();
384         hostIntBits = hostIntBits | ((hF.getExponent() + 64) << 24);
385         hostIntBits = hostIntBits | (hF.getSign() << 31);
386         return hostIntBits;
387     }
388 }