ArduinoField.java

/*******************************************************************************
 * jArduino: Arduino C++ Code Generation From Java
 * Copyright 2020 Tony Washer
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package net.sourceforge.jarduino.util;

/**
 * Field Parse/Set methods.
 */
public final class ArduinoField {
    /**
     * Byte mask.
     */
    private static final int BYTEMASK = 0xFF;

    /**
     * Private constructor.
     */
    private ArduinoField() {
    }

    /**
     * Parse a littleEndian field.
     * @param pBuffer the buffer to parse
     * @param pStart the start bit
     * @param pLength the bit length
     * @param pSigned is the field signed?
     * @return the parsed value
     */
    public static long parseLEField(final byte[] pBuffer,
                                    final int pStart,
                                    final int pLength,
                                    final boolean pSigned) {
        /* Create the signing mask */
        final long mySignMask = pSigned && (pLength < Long.SIZE) ? -(1L << pLength) : 0;
        final long myTestMask = 1L << (pLength - 1);

        /* Determine the byte that contains the most significant bit and the number of relevant bits */
        final int myMSB = pStart + pLength - 1;
        final int myBits = 1 + (myMSB % Byte.SIZE);
        int myPos = myMSB / Byte.SIZE;

        /* If we have a short value */
        if (myBits >= pLength) {
            final int bitsToShift = pStart % Byte.SIZE;
            final long myValue = ((pBuffer[myPos] & BYTEMASK) >> bitsToShift) & ((1 << pLength) - 1);
            return (myValue & myTestMask) == 0 ? myValue : myValue | mySignMask;
        }

        /* Access starting value */
        long myValue = pBuffer[myPos] & ((1 << myBits) - 1);

        /* Look at remaining bits */
        int myRemaining = pLength - myBits;

        /* Process whole bytes */
        while (myRemaining >= Byte.SIZE) {
            myValue <<= Byte.SIZE;
            myValue += pBuffer[--myPos] & BYTEMASK;
            myRemaining -= Byte.SIZE;
        }

        /* If we have remaining bits */
        if (myRemaining > 0) {
            myValue <<= myRemaining;
            myValue += (pBuffer[--myPos] >> (Byte.SIZE - myRemaining)) & ((1 << myRemaining) - 1);
        }

        /* return the value */
        return (myValue & myTestMask) == 0 ? myValue : myValue | mySignMask;
    }

    /**
     * Parse a bigEndian field.
     * @param pBuffer the buffer to parse
     * @param pStart the start bit
     * @param pLength the bit length
     * @param pSigned is the field signed?
     * @return the parsed value
     */
    public static long parseBEField(final byte[] pBuffer,
                                    final int pStart,
                                    final int pLength,
                                    final boolean pSigned) {
        /* Create the signing mask */
        final long mySignMask = pSigned && (pLength < Long.SIZE) ? -(1L << pLength) : 0;
        final long myTestMask = 1L << (pLength - 1);

        /* Determine the byte that contains the most significant bit and the number of relevant bits */
        final int myBits = 1 + (pStart % Byte.SIZE);
        int myPos = pStart / Byte.SIZE;

        /* If we have a short value */
        if (myBits >= pLength) {
            final long myValue = ((pBuffer[myPos] & BYTEMASK) >> (myBits - pLength)) & ((1 << pLength) - 1);
            return (myValue & myTestMask) == 0 ? myValue : myValue | mySignMask;
        }

        /* Access starting value */
        long myValue = pBuffer[myPos] & ((1 << myBits) - 1);

        /* Look at remaining bits */
        int myRemaining = pLength - myBits;

        /* Process whole bytes */
        while (myRemaining >= Byte.SIZE) {
            myValue <<= Byte.SIZE;
            myValue += pBuffer[++myPos] & BYTEMASK;
            myRemaining -= Byte.SIZE;
        }

        /* If we have remaining bits */
        if (myRemaining > 0) {
            myValue <<= myRemaining;
            myValue += (pBuffer[++myPos] >> (Byte.SIZE - myRemaining)) & ((1 << myRemaining) - 1);
        }

        /* return the value */
        return (myValue & myTestMask) == 0 ? myValue : myValue | mySignMask;
    }

    /**
     * Set a littleEndian field.
     * @param pBuffer the buffer to set
     * @param pStart the start bit
     * @param pLength the bit length
     * @param pValue the value to set
     */
    public static void setLEField(final byte[] pBuffer,
                                  final int pStart,
                                  final int pLength,
                                  final long pValue) {
        /* Determine the byte that contains the least significant bit and the number of relevant bits */
        final int myBits = Byte.SIZE - (pStart % Byte.SIZE);
        int myPos = pStart / Byte.SIZE;

        /* If we have a short value */
        if (myBits >= pLength) {
            final int bitsToShift = pStart % Byte.SIZE;
            final int myMask = ((1 << pLength) - 1) << bitsToShift;
            pBuffer[myPos] &= ~myMask;
            pBuffer[myPos] |= (pValue << bitsToShift) & myMask;
            return;
        }

        /* Set starting value */
        final int bitsToShift = Byte.SIZE - myBits;
        int myMask = BYTEMASK << bitsToShift;
        pBuffer[myPos] &= ~myMask;
        pBuffer[myPos] |= (pValue << bitsToShift) & myMask;

        /* Look at remaining bits */
        int myRemaining = pLength - myBits;
        long myValue = pValue >> myBits;

        /* Process whole bytes */
        while (myRemaining >= Byte.SIZE) {
            pBuffer[++myPos] = (byte) myValue;
            myRemaining -= Byte.SIZE;
            myValue >>= Byte.SIZE;
        }

        /* If we have remaining bits */
        if (myRemaining > 0) {
            myMask = (1 << myRemaining) - 1;
            pBuffer[++myPos] &= ~myMask;
            pBuffer[myPos] |= myValue & myMask;
        }
    }

    /**
     * Set a bigEndian field.
     * @param pBuffer the buffer to set
     * @param pStart the start bit
     * @param pLength the bit length
     * @param pValue the value to set
     */
    public static void setBEField(final byte[] pBuffer,
                                  final int pStart,
                                  final int pLength,
                                  final long pValue) {
        /* Determine the byte that contains the most significant bit and the number of relevant bits */
        int myBits = 1 + (pStart % Byte.SIZE);
        int myPos = pStart / Byte.SIZE;

        /* If we have a short value */
        if (myBits >= pLength) {
            final int bitsToShift = myBits - pLength;
            final int myMask = ((1 << pLength) - 1) << bitsToShift;
            pBuffer[myPos] &= ~myMask;
            pBuffer[myPos] |= (pValue << bitsToShift) & myMask;
            return;
        }

        /* Now look for the least significant bit */
        final int myXtra = pLength - myBits - 1;
        myPos += 1 + myXtra / Byte.SIZE;
        myBits = 1 + myXtra % Byte.SIZE;

        /* Set starting value */
        final int bitsToShift = Byte.SIZE - myBits;
        int myMask = BYTEMASK << bitsToShift;
        pBuffer[myPos] &= ~myMask;
        pBuffer[myPos] |= (pValue << bitsToShift) & myMask;

        /* Look at remaining bits */
        int myRemaining = pLength - myBits;
        long myValue = pValue >> myBits;

        /* Process whole bytes */
        while (myRemaining >= Byte.SIZE) {
            pBuffer[--myPos] = (byte) myValue;
            myRemaining -= Byte.SIZE;
            myValue >>= Byte.SIZE;
        }

        /* If we have remaining bits */
        if (myRemaining > 0) {
            myMask = (1 << myRemaining) - 1;
            pBuffer[--myPos] &= ~myMask;
            pBuffer[myPos] |= myValue & myMask;
        }
    }

    /**
     * Set a littleEndian bitMap for field.
     * @param pBuffer the buffer to set
     * @param pStart the start bit
     * @param pLength the bit length
     */
    public static void setLEFieldBits(final byte[] pBuffer,
                                      final int pStart,
                                      final int pLength) {
        /* Determine the byte that contains the least significant bit and the number of relevant bits */
        final int myBits = Byte.SIZE - (pStart % Byte.SIZE);
        int myPos = pStart / Byte.SIZE;

        /* If we have a short value */
        if (myBits >= pLength) {
            final int bitsToShift = pStart % Byte.SIZE;
            final int myMask = ((1 << pLength) - 1) << bitsToShift;
            pBuffer[myPos] |= myMask;
            return;
        }

        /* Set starting value */
        final int bitsToShift = Byte.SIZE - myBits;
        int myMask = BYTEMASK << bitsToShift;
        pBuffer[myPos] |= myMask;

        /* Look at remaining bits */
        int myRemaining = pLength - myBits;

        /* Process whole bytes */
        while (myRemaining >= Byte.SIZE) {
            pBuffer[++myPos] = (byte) -1;
            myRemaining -= Byte.SIZE;
        }

        /* If we have remaining bits */
        if (myRemaining > 0) {
            myMask = (1 << myRemaining) - 1;
            pBuffer[++myPos] |= myMask;
        }
    }

    /**
     * Set a bigEndian bitMap for field.
     * @param pBuffer the buffer to set
     * @param pStart the start bit
     * @param pLength the bit length
     */
    public static void setBEFieldBits(final byte[] pBuffer,
                                      final int pStart,
                                      final int pLength) {
        /* Determine the byte that contains the most significant bit and the number of relevant bits */
        int myBits = 1 + (pStart % Byte.SIZE);
        int myPos = pStart / Byte.SIZE;

        /* If we have a short value */
        if (myBits >= pLength) {
            final int bitsToShift = myBits - pLength;
            final int myMask = ((1 << pLength) - 1) << bitsToShift;
            pBuffer[myPos] |= myMask;
            return;
        }

        /* Now look for the least significant bit */
        final int myXtra = pLength - myBits - 1;
        myPos += 1 + myXtra / Byte.SIZE;
        myBits = 1 + myXtra % Byte.SIZE;

        /* Set starting value */
        final int bitsToShift = Byte.SIZE - myBits;
        int myMask = BYTEMASK << bitsToShift;
        pBuffer[myPos] |= myMask;

        /* Look at remaining bits */
        int myRemaining = pLength - myBits;

        /* Process whole bytes */
        while (myRemaining >= Byte.SIZE) {
            pBuffer[--myPos] = (byte) -1;
            myRemaining -= Byte.SIZE;
        }

        /* If we have remaining bits */
        if (myRemaining > 0) {
            myMask = (1 << myRemaining) - 1;
            pBuffer[--myPos] |= myMask;
        }
    }
}