All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.legstar.coxb.convert.simple.CobolDoubleSimpleConverter Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Copyright (c) 2011 LegSem.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser Public License v2.1
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * 
 * Contributors:
 *     LegSem - initial API and implementation
 ******************************************************************************/
package com.legstar.coxb.convert.simple;

import com.legstar.coxb.CobolContext;
import com.legstar.coxb.ICobolArrayDoubleBinding;
import com.legstar.coxb.ICobolDoubleBinding;
import com.legstar.coxb.convert.ICobolDoubleConverter;
import com.legstar.coxb.convert.CobolConversionException;
import com.legstar.coxb.host.HostData;
import com.legstar.coxb.host.HostDouble;
import com.legstar.coxb.host.HostException;

import java.util.ArrayList;
import java.util.List;

/**
 * This is a concrete implementation of marshal/unmarshal operations of java
 * double to cobol comp-2.
 * 
 * @author Fady Moussallam
 * 
 */
public class CobolDoubleSimpleConverter extends CobolSimpleConverter
        implements ICobolDoubleConverter {

    /**
     * @param cobolContext the Cobol compiler parameters in effect
     */
    public CobolDoubleSimpleConverter(final CobolContext cobolContext) {
        super(cobolContext);
    }

    /** {@inheritDoc} */
    public int toHost(
            final ICobolDoubleBinding ce,
            final byte[] hostTarget,
            final int offset)
            throws HostException {
        int newOffset = 0;
        try {
            newOffset = toHostSingle(ce.getDoubleValue(),
                    hostTarget,
                    offset);
        } catch (CobolConversionException e) {
            throwHostException(ce, e);
        }
        return newOffset;
    }

    /** {@inheritDoc} */
    public int toHost(
            final ICobolArrayDoubleBinding ce,
            final byte[] hostTarget,
            final int offset,
            final int currentOccurs)
            throws HostException {
        int newOffset = offset;
        try {
            for (Double javaSource : ce.getDoubleList()) {
                newOffset = toHostSingle(javaSource,
                        hostTarget,
                        newOffset);
            }
            /* If necessary, fill in the array with missing items */
            for (int i = ce.getDoubleList().size(); i < currentOccurs; i++) {
                newOffset = toHostSingle(Double.valueOf(0),
                        hostTarget,
                        newOffset);
            }
        } catch (CobolConversionException e) {
            throwHostException(ce, e);
        }
        return newOffset;
    }

    /** {@inheritDoc} */
    public int fromHost(
            final ICobolDoubleBinding ce,
            final byte[] hostSource,
            final int offset)
            throws HostException {
        int newOffset = offset;
        try {
            Double javaDouble = fromHostSingle(ce.getByteLength(),
                    hostSource,
                    newOffset);
            ce.setDoubleValue(javaDouble);
            newOffset += ce.getByteLength();
        } catch (CobolConversionException e) {
            throwHostException(ce, e);
        }
        return newOffset;
    }

    /** {@inheritDoc} */
    public int fromHost(
            final ICobolArrayDoubleBinding ce,
            final byte[] hostSource,
            final int offset,
            final int currentOccurs)
            throws HostException {
        List < Double > lArray = new ArrayList < Double >();
        int newOffset = offset;
        try {
            for (int i = 0; i < currentOccurs; i++) {
                Double javaDouble = fromHostSingle(ce.getItemByteLength(),
                        hostSource,
                        newOffset);
                lArray.add(javaDouble);
                newOffset += ce.getItemByteLength();
            }
            ce.setDoubleList(lArray);
        } catch (CobolConversionException e) {
            throwHostException(ce, e);
        }
        return newOffset;
    }

    /**
     * Converts a Java Double to a host comp-2.
     * 
     * @param javaDouble java double to convert
     * @param hostTarget target host buffer
     * @param offset offset in target host buffer
     * @return offset after host buffer is updated
     * @throws CobolConversionException if conversion fails
     */
    public static final int toHostSingle(
            final Double javaDouble,
            final byte[] hostTarget,
            final int offset)
            throws CobolConversionException {

        /* Comp-2 are 8 byte long */
        int cobolByteLength = 8;

        /* Check that we are still within the host target range */
        int lastOffset = offset + cobolByteLength;
        if (lastOffset > hostTarget.length) {
            throw (new CobolConversionException(
                    "Attempt to write past end of host source buffer",
                    new HostData(hostTarget), offset));
        }

        /* Provide a default if input is null */
        Double localDouble = javaDouble;
        if (localDouble == null) {
            localDouble = 0d;
        }

        /* Host doubles do not support NaN or infinite. */
        if (localDouble.isInfinite()) {
            throw (new CobolConversionException(
                    "Infinite doubles are not supported",
                    new HostData(hostTarget), offset));
        }
        if (localDouble.isNaN()) {
            throw (new CobolConversionException(
                    "NaN doubles are not supported",
                    new HostData(hostTarget), offset));
        }

        /*
         * Treat the zero case separatly because the bit layout is not
         * consistent.
         */
        if ((localDouble.doubleValue() == 0.0f)
                ||
                (localDouble.doubleValue() == -0.0f)) {
            for (int i = 0; i < 8; i++) {
                hostTarget[offset + i] = 0;
            }
            return offset + cobolByteLength;
        }

        /* Parse the Java double to get sign, exponent and mantissa */
        HostDouble jF = parseJavaDouble(localDouble);

        /* Create a representation of the corresponding host double */
        HostDouble hF = new HostDouble();
        hF.setSign(jF.getSign());

        /*
         * The java exponent is a binary offset while the host exponent is an
         * hexadecimal offset. This means host exponent values are multiple
         * or 4. If the java exponent is not a multiple of 4 we then need
         * to shift the mantissa which might result in loss of precision.
         */
        int r = jF.getExponent() % 4;
        int mantissaShift = 0;
        if (r <= 0) {
            mantissaShift = -1 * r;
        } else {
            mantissaShift = 4 - r;
        }

        hF.setExponent((jF.getExponent() + mantissaShift) / 4);
        hF.setMantissa(jF.getMantissa() >> mantissaShift);

        /*
         * The host double mantissa is stored on 56 bits while java is on 53
         * (52 + 1 plus).
         * bits so we need to padd the java mantissa with an additional 3 bits
         * to the right
         */
        hF.setMantissa(hF.getMantissa() << 3);

        /* Now assemble the host double 8 bytes */
        long hostLongBits = createHostDouble(hF);

        /* Store the bytes in the host buffer */
        for (int i = 0; i < 8; i++) {
            hostTarget[offset + i] =
                    (byte) ((hostLongBits >>> (56 - i * 8)) & 0xff);
        }

        return offset + cobolByteLength;
    }

    /**
     * Converts a host comp-2 to a Java Double.
     * 
     * @param cobolByteLength host byte length
     * @param hostSource source host buffer
     * @param offset offset in source host buffer
     * @return offset after host buffer is read
     * @throws CobolConversionException if conversion fails
     */
    public static final Double fromHostSingle(
            final int cobolByteLength,
            final byte[] hostSource,
            final int offset)
            throws CobolConversionException {

        int lastOffset = offset + cobolByteLength;

        /*
         * Check that we are still within the host source range.
         * If not, consider the host optimized its payload by truncating
         * trailing nulls in which case, we just need to initialize and return.
         */
        if (lastOffset > hostSource.length) {
            return Double.valueOf(0d);
        }

        /* Create a host double representation from the bytes we have */
        long hostLongBits = 0;
        for (int i = 0; i < 8; i++) {
            hostLongBits = hostLongBits
                    | ((long) (hostSource[offset + (7 - i)] & 0xff) << (i * 8));
        }
        HostDouble hF = parseHostDouble(hostLongBits);

        /* Create a representation of the corresponding java double */
        HostDouble jF = new HostDouble();
        jF.setSign(hF.getSign());

        /*
         * Host exponent is hexadecimal based while java is binary based.
         * There is also an additional shift for non-zero values due to
         * the 1-plus" normalized java specs.
         */
        if (hF.getMantissa() != 0) {
            jF.setExponent((4 * hF.getExponent()) - 1);
        }

        /*
         * The java mantissa is 53 bits while the host is 56. This
         * means there is a systematic loss of precision.
         */
        jF.setMantissa(hF.getMantissa() >>> 3);

        /* In java the 53th bit needs to be one */
        while ((jF.getMantissa() > 0L)
                &&
                (jF.getMantissa() & 0x0010000000000000L) == 0) {
            jF.setMantissa(jF.getMantissa() << 1);
            jF.setExponent(jF.getExponent() - 1);
        }

        return createJavaDouble(jF);
    }

    /**
     * Extracts the sign, exponent and mantissa from a Java double.
     * 
     * @param f the double to extract components from
     * @return a class holding the various components
     */
    public static final HostDouble parseJavaDouble(final double f) {

        HostDouble jF = new HostDouble();

        /* Zero is a special case */
        if (f == 0.0f) {
            return jF;
        }

        long javaLongBits = Double.doubleToLongBits(f);

        /* First bit left (bit 63) is the sign: 0 = positive, 1 = negative */
        jF.setSign((int) ((javaLongBits & 0x8000000000000000L) >>> 63));

        /*
         * Bits 62-52 (11 bits) represents the exponent offset by 1023, this
         * number is called excess so you get the exponent as E= excess - 1023
         * Furthermore, the "1-plus" normalized representation has the decimal
         * point after the implicit initial 1. Here we elect to store the
         * exponent for decimal point before that initial 1.
         */
        int excess = (int) ((javaLongBits >> 52) & 0x7ffL);
        jF.setExponent(excess - 1023 + 1);

        /*
         * Bits 51-0 (52 bits) represents the mantissa in a form called
         * "1-plus" normalized. This means that the real mantissa is actually
         * 1.b(51)b(50)...b(0) where the intiial "1" is implicit.
         * This code will explicitly add 1 in front of the mantissa.
         */
        long orMask = 1L << 52;
        jF.setMantissa(javaLongBits & 0x000fffffffffffffL | orMask);

        return jF;
    }

    /**
     * Reconstruct a java double from its sign, exponent and mantissa
     * components.
     * 
     * @param jF a class holding the various components
     * @return a Java double
     */
    public static final double createJavaDouble(final HostDouble jF) {

        /* First check if this is a zero value */
        if (jF.getExponent() == 0 && jF.getMantissa() == 0) {
            return 0d;
        }

        /* Get rid of the leading 1 which needs to be implicit */
        long javaLongBits = jF.getMantissa() & 0x000fffffffffffffL;
        javaLongBits = javaLongBits | ((long) (jF.getExponent() + 1023) << 52);
        javaLongBits = javaLongBits | ((long) jF.getSign() << 63);
        return Double.longBitsToDouble(javaLongBits);
    }

    /**
     * Extracts the sign, exponent and mantissa from a Host double .
     * 
     * @param hostLongBits the bit sequence representing the host double
     * @return a class holding the various components
     */
    public static final HostDouble parseHostDouble(final long hostLongBits) {
        HostDouble hF = new HostDouble();

        /* First bit left (bit 63) is the sign: 0 = positive, 1 = negative */
        hF.setSign((int) ((hostLongBits & 0x8000000000000000L) >>> 63));

        /*
         * Bits 62-56 (7 bits) represents the exponent offset by 64, this
         * number is called excess so you get the exponent as
         * E= excess - 64
         */
        int excess = (int) ((hostLongBits & 0x7f00000000000000L) >>> 56);
        if (excess != 0) {
            hF.setExponent((excess - 64));
        }

        /* Bits 55-0 (56 bits) represents the mantissa. */
        hF.setMantissa(hostLongBits & 0x00ffffffffffffffL);

        return hF;
    }

    /**
     * Reconstruct a host double from its sign, exponent and mantissa
     * components.
     * 
     * @param hF a class holding the various components
     * @return the bit sequence representing the host double
     */
    public static final long createHostDouble(final HostDouble hF) {

        /* First check if this is a zero value */
        if (hF.getExponent() == 0 && hF.getMantissa() == 0) {
            return 0L;
        }

        long hostLongBits = hF.getMantissa();
        hostLongBits = hostLongBits | ((long) (hF.getExponent() + 64) << 56);
        hostLongBits = hostLongBits | ((long) hF.getSign() << 63);
        return hostLongBits;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy