1 package nl.tudelft.simulation.language.util;
2
3 import java.math.BigInteger;
4 import java.util.BitSet;
5
6 import org.djutils.exceptions.Throw;
7
8 /**
9 * Utilities for the BitSet class.
10 * <p>
11 * Copyright (c) 2009-2025 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
12 * for project information <a href="https://simulation.tudelft.nl/dsol/manual/" target="_blank">DSOL Manual</a>. The DSOL
13 * project is distributed under a three-clause BSD-style license, which can be found at
14 * <a href="https://simulation.tudelft.nl/dsol/docs/latest/license.html" target="_blank">DSOL License</a>.
15 * </p>
16 * @author <a href="https://www.linkedin.com/in/peterhmjacobs">Peter Jacobs </a>
17 * @author <a href="https://github.com/averbraeck" target="_blank"> Alexander Verbraeck</a>
18 */
19 public final class BitUtil
20 {
21 /** Constructor is not called for utility class. */
22 private BitUtil()
23 {
24 // Utility class
25 }
26
27 /**
28 * @param bits the bitset to convert
29 * @return Returns a byte array of at least length 1. The most significant bit in the result is guaranteed not to be a 1
30 * (since BitSet does not support sign extension). The byte-ordering of the result is big-endian which means the
31 * most significant bit is in element 0. The bit at index 0 of the bit set is assumed to be the least significant
32 * bit.
33 */
34 public static byte[] toByteArray(final BitSet bits)
35 {
36 synchronized (bits)
37 {
38 byte[] bytes = new byte[bits.length() / 8 + 1];
39 for (int i = 0; i < bits.length(); i++)
40 {
41 if (bits.get(i))
42 {
43 bytes[bytes.length - i / 8 - 1] |= 1 << (i % 8);
44 }
45 }
46 return bytes;
47 }
48 }
49
50 /**
51 * returns the bitset of an integer value.
52 * @param value the value
53 * @param length the length of the bitSet to produce
54 * @return the BitSet
55 */
56 public static BitSet fromInt(final int value, final int length)
57 {
58 return BitUtil.fromInteger(Integer.valueOf(value), length);
59 }
60
61 /**
62 * returns the bitset of an integer value.
63 * @param value the value
64 * @param length the length of
65 * @return the BitSet
66 */
67 public static BitSet fromInteger(final Integer value, final int length)
68 {
69 Throw.when(length <= 0, IllegalArgumentException.class, "BitUtil.fromInt should have a positive number of bits");
70 Throw.when(length > 31, IllegalArgumentException.class, "BitUtil.fromInt can have maximum 31 bits");
71 Throw.when(value.intValue() < 0, IllegalArgumentException.class, "BitUtil.fromInt can have only positive values");
72 return BitUtil.fromByteArray(new BigInteger(value.toString()).toByteArray());
73 }
74
75 /**
76 * @param bits the bitset to convert
77 * @param length the length of the set
78 * @return Returns an int. The most significant bit in the result is guaranteed not to be a 1 (since BitSet does not support
79 * sign extension). The int-ordering of the result is big-endian which means the most significant bit is in element
80 * 0. The bit at index 0 of the bit set is assumed to be the least significant bit.
81 */
82 public static int toInt(final BitSet bits, final int length)
83 {
84 Throw.when(length <= 0, IllegalArgumentException.class, "BitUtil.toInt should have a positive number of bits");
85 Throw.when(length > 31, IllegalArgumentException.class, "BitUtil.toInt can have maximum 31 bits");
86 byte[] bytes = BitUtil.toByteArray(bits);
87 return new BigInteger(bytes).intValue();
88 }
89
90 /**
91 * 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
92 * representation, the LEAST SIGNIFICANT BIT COMES FIRST. So, 001 represents the value 4 and not 1.
93 * @param value the value
94 * @return the BitSet
95 */
96 public static BitSet fromString(final String value)
97 {
98 if (!value.trim().startsWith("{"))
99 {
100 BitSet set = new BitSet(value.length());
101 for (int i = 0; i < value.length(); i++)
102 {
103 if (value.charAt(i) == '1')
104 {
105 set.set(i, true);
106 }
107 else if (value.charAt(i) == '0')
108 {
109 set.set(i, false);
110 }
111 else
112 {
113 throw new IllegalArgumentException("value should only contain ones and zeros. Try 110011");
114 }
115 }
116 return set;
117 }
118 BitSet set = new BitSet();
119 String array = value.trim();
120 if (!array.endsWith("}"))
121 {
122 throw new IllegalArgumentException("value that starts with { should end with }");
123 }
124 array = array.substring(1, array.length() - 1).trim();
125 if (array.length() == 0)
126 {
127 return set;
128 }
129 String[] bits = array.split(",");
130 for (int i = 0; i < bits.length; i++)
131 {
132 bits[i] = bits[i].trim();
133 set.set(Integer.valueOf(bits[i]).intValue());
134 }
135 return set;
136 }
137
138 /**
139 * @param bytes the byteArray
140 * @return Returns a bitset containing the values in bytes.The byte-ordering of bytes must be big-endian which means the
141 * most significant bit is in element 0.
142 */
143 public static BitSet fromByteArray(final byte[] bytes)
144 {
145 BitSet bits = new BitSet();
146 for (int i = 0; i < bytes.length * 8; i++)
147 {
148 if ((bytes[bytes.length - i / 8 - 1] & (1 << (i % 8))) > 0)
149 {
150 bits.set(i);
151 }
152 }
153 return bits;
154 }
155
156 /**
157 * returns a one-size BitSet with value.
158 * @param value the value of the bitSet
159 * @return the BitSet
160 */
161 public static BitSet fromBoolean(final boolean value)
162 {
163 BitSet result = new BitSet(1);
164 result.set(0, value);
165 return result;
166 }
167 }