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