BitUtil.java

package nl.tudelft.simulation.language.util;

import java.io.Serializable;
import java.math.BigInteger;
import java.util.BitSet;

import org.djutils.exceptions.Throw;

/**
 * Utilities for the BitSet class.
 * <p>
 * Copyright (c) 2009-2024 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
 * for project information <a href="https://simulation.tudelft.nl/" target="_blank"> https://simulation.tudelft.nl</a>. The DSOL
 * project is distributed under a three-clause BSD-style license, which can be found at
 * <a href="https://https://simulation.tudelft.nl/dsol/docs/latest/license.html" target="_blank">
 * https://https://simulation.tudelft.nl/dsol/docs/latest/license.html</a>.
 * </p>
 * @author <a href="https://www.linkedin.com/in/peterhmjacobs">Peter Jacobs </a>
 * @author <a href="https://www.tudelft.nl/averbraeck" target="_blank"> Alexander Verbraeck</a>
 */
public final class BitUtil implements Serializable
{
    /** The default serial version UID for serializable classes. */
    private static final long serialVersionUID = 1L;

    /** Constructor is not called for utility class. */
    private BitUtil()
    {
        // Utility class
    }

    /**
     * @param bits BitSet; the bitset to convert
     * @return Returns a byte array of at least length 1. The most significant bit in the result is guaranteed not to be a 1
     *         (since BitSet does not support sign extension). The byte-ordering of the result is big-endian which means the
     *         most significant bit is in element 0. The bit at index 0 of the bit set is assumed to be the least significant
     *         bit.
     */
    public static byte[] toByteArray(final BitSet bits)
    {
        synchronized (bits)
        {
            byte[] bytes = new byte[bits.length() / 8 + 1];
            for (int i = 0; i < bits.length(); i++)
            {
                if (bits.get(i))
                {
                    bytes[bytes.length - i / 8 - 1] |= 1 << (i % 8);
                }
            }
            return bytes;
        }
    }

    /**
     * returns the bitset of an integer value.
     * @param value int; the value
     * @param length int; the length of the bitSet to produce
     * @return the BitSet
     */
    public static BitSet fromInt(final int value, final int length)
    {
        return BitUtil.fromInteger(Integer.valueOf(value), length);
    }

    /**
     * returns the bitset of an integer value.
     * @param value Integer; the value
     * @param length int; the length of
     * @return the BitSet
     */
    public static BitSet fromInteger(final Integer value, final int length) 
    {
        Throw.when(length <= 0, IllegalArgumentException.class, "BitUtil.fromInt should have a positive number of bits");
        Throw.when(length > 31, IllegalArgumentException.class, "BitUtil.fromInt can have maximum 31 bits");
        Throw.when(value.intValue() < 0, IllegalArgumentException.class, "BitUtil.fromInt can have only positive values");
        return BitUtil.fromByteArray(new BigInteger(value.toString()).toByteArray());
    }

    /**
     * @param bits BitSet; the bitset to convert
     * @param length int; the length of the set
     * @return Returns an int. The most significant bit in the result is guaranteed not to be a 1 (since BitSet does not support
     *         sign extension). The int-ordering of the result is big-endian which means the most significant bit is in element
     *         0. The bit at index 0 of the bit set is assumed to be the least significant bit.
     */
    public static int toInt(final BitSet bits, final int length)
    {
        Throw.when(length <= 0, IllegalArgumentException.class, "BitUtil.toInt should have a positive number of bits");
        Throw.when(length > 31, IllegalArgumentException.class, "BitUtil.toInt can have maximum 31 bits");
        byte[] bytes = BitUtil.toByteArray(bits);
        return new BigInteger(bytes).intValue();
    }

    /**
     * constructs a new BitSet from a string in the "110110" format, or the {0, 1, 3, 5, 8, 12} format. Note that for the binary
     * representation, the LEAST SIGNIFICANT BIT COMES FIRST. So, 001 represents the value 4 and not 1.
     * @param value String; the value
     * @return the BitSet
     */
    public static BitSet fromString(final String value)
    {
        if (!value.trim().startsWith("{"))
        {
            BitSet set = new BitSet(value.length());
            for (int i = 0; i < value.length(); i++)
            {
                if (value.charAt(i) == '1')
                {
                    set.set(i, true);
                }
                else if (value.charAt(i) == '0')
                {
                    set.set(i, false);
                }
                else
                {
                    throw new IllegalArgumentException("value should only contain ones and zeros. Try 110011");
                }
            }
            return set;
        }
        BitSet set = new BitSet();
        String array = value.trim();
        if (!array.endsWith("}"))
        {
            throw new IllegalArgumentException("value that starts with { should end with }");
        }
        array = array.substring(1, array.length() - 1).trim();
        if (array.length() == 0)
        {
            return set;
        }
        String[] bits = array.split(",");
        for (int i = 0; i < bits.length; i++)
        {
            bits[i] = bits[i].trim();
            set.set(Integer.valueOf(bits[i]).intValue());
        }
        return set;
    }

    /**
     * @param bytes byte[]; the byteArray
     * @return Returns a bitset containing the values in bytes.The byte-ordering of bytes must be big-endian which means the
     *         most significant bit is in element 0.
     */
    public static BitSet fromByteArray(final byte[] bytes)
    {
        BitSet bits = new BitSet();
        for (int i = 0; i < bytes.length * 8; i++)
        {
            if ((bytes[bytes.length - i / 8 - 1] & (1 << (i % 8))) > 0)
            {
                bits.set(i);
            }
        }
        return bits;
    }

    /**
     * returns a one-size BitSet with value.
     * @param value boolean; the value of the bitSet
     * @return the BitSet
     */
    public static BitSet fromBoolean(final boolean value)
    {
        BitSet result = new BitSet(1);
        result.set(0, value);
        return result;
    }
}