blob: 32e9d0b406a8abebdc124e2a75d88b2791c05d77 [file] [log] [blame]
Carmelo Cascone6b32c992016-04-13 11:53:09 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Carmelo Cascone6b32c992016-04-13 11:53:09 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onlab.util;
18
Carmelo Cascone6b32c992016-04-13 11:53:09 -070019import com.google.common.base.Objects;
20
21import java.nio.ByteBuffer;
22import java.nio.ByteOrder;
Carmelo Casconeaa8b6292016-04-13 14:27:06 -070023import java.util.Arrays;
Carmelo Cascone6b32c992016-04-13 11:53:09 -070024
25import static com.google.common.base.Preconditions.checkArgument;
Carmelo Cascone00a59962017-06-16 17:51:49 +090026import static com.google.common.base.Preconditions.checkNotNull;
27import static java.lang.String.format;
Carmelo Cascone6b32c992016-04-13 11:53:09 -070028import static org.apache.commons.lang3.ArrayUtils.reverse;
29
30/**
Carmelo Cascone8a571af2018-04-06 23:17:04 -070031 * Immutable sequence of bytes, assumed to represent a value in {@link
32 * ByteOrder#BIG_ENDIAN BIG_ENDIAN} order.
Carmelo Cascone6b32c992016-04-13 11:53:09 -070033 * <p>
34 * Sequences can be created copying from an already existing representation of a
35 * sequence of bytes, such as {@link ByteBuffer} or {@code byte[]}; or by
36 * copying bytes from a primitive data type, such as {@code long}, {@code int}
37 * or {@code short}. In the first case, bytes are assumed to be already given in
38 * big-endian order, while in the second case big-endianness is enforced by this
39 * class.
40 */
41public final class ImmutableByteSequence {
42
Carmelo Cascone8a571af2018-04-06 23:17:04 -070043 private enum BitwiseOp {
44 AND,
45 OR,
46 XOR
47 }
48
Carmelo Cascone6b32c992016-04-13 11:53:09 -070049 /*
50 Actual bytes are backed by a byte buffer.
51 The order of a newly-created byte buffer is always BIG_ENDIAN.
52 */
53 private ByteBuffer value;
Daniele Moro7aa13e62021-02-23 15:28:07 +010054 private boolean isAscii = false;
Carmelo Cascone6b32c992016-04-13 11:53:09 -070055
56 /**
Carmelo Cascone8a571af2018-04-06 23:17:04 -070057 * Private constructor. Creates a new byte sequence object backed by the
58 * passed ByteBuffer.
Carmelo Cascone6b32c992016-04-13 11:53:09 -070059 *
60 * @param value a byte buffer
61 */
62 private ImmutableByteSequence(ByteBuffer value) {
63 this.value = value;
64 // Rewind buffer so it's ready to be read.
65 // No write operation should be performed on it from now on.
66 this.value.rewind();
67 }
68
Daniele Moro7aa13e62021-02-23 15:28:07 +010069 private ImmutableByteSequence(ByteBuffer value, boolean isAscii) {
70 this(value);
71 this.isAscii = isAscii;
72 }
73
Carmelo Cascone6b32c992016-04-13 11:53:09 -070074 /**
75 * Creates a new immutable byte sequence with the same content and order of
76 * the passed byte array.
77 *
78 * @param original a byte array value
Carmelo Casconeaa8b6292016-04-13 14:27:06 -070079 * @return a new immutable byte sequence
Carmelo Cascone6b32c992016-04-13 11:53:09 -070080 */
81 public static ImmutableByteSequence copyFrom(byte[] original) {
82 checkArgument(original != null && original.length > 0,
83 "Cannot copy from an empty or null array");
84 return new ImmutableByteSequence(
85 ByteBuffer.allocate(original.length).put(original));
86 }
87
88 /**
Carmelo Cascone17fc9e42016-05-31 11:29:21 -070089 * Creates a new immutable byte sequence with the same content and order of
90 * the passed byte array, from/to the given indexes (inclusive).
91 *
92 * @param original a byte array value
Carmelo Casconeb10194c2017-08-23 19:16:51 +020093 * @param fromIdx starting index
94 * @param toIdx ending index
Carmelo Cascone17fc9e42016-05-31 11:29:21 -070095 * @return a new immutable byte sequence
96 */
97 public static ImmutableByteSequence copyFrom(byte[] original, int fromIdx, int toIdx) {
98 checkArgument(original != null && original.length > 0,
99 "Cannot copy from an empty or null array");
100 checkArgument(toIdx >= fromIdx && toIdx < original.length, "invalid indexes");
101 ByteBuffer buffer = ByteBuffer.allocate((toIdx - fromIdx) + 1);
102 for (int i = fromIdx; i <= toIdx; i++) {
103 buffer.put(original[i]);
104 }
105 return new ImmutableByteSequence(buffer);
106 }
107
108 /**
Carmelo Cascone6b32c992016-04-13 11:53:09 -0700109 * Creates a new immutable byte sequence copying bytes from the given
110 * ByteBuffer {@link ByteBuffer}. If the byte buffer order is not big-endian
111 * bytes will be copied in reverse order.
112 *
113 * @param original a byte buffer
114 * @return a new byte buffer object
115 */
116 public static ImmutableByteSequence copyFrom(ByteBuffer original) {
117 checkArgument(original != null && original.capacity() > 0,
118 "Cannot copy from an empty or null byte buffer");
119
120 byte[] bytes = new byte[original.capacity()];
121
122 // copy bytes from original buffer
123 original.rewind();
124 original.get(bytes);
125
126 if (original.order() == ByteOrder.LITTLE_ENDIAN) {
127 // FIXME: this can be improved, e.g. read bytes in reverse order from original
128 reverse(bytes);
129 }
130
131 return new ImmutableByteSequence(ByteBuffer.wrap(bytes));
132 }
133
134 /**
Daniele Moro7aa13e62021-02-23 15:28:07 +0100135 * Creates a new immutable byte sequence from the given string.
136 *
137 * @param original a string
138 * @return a new byte buffer object
139 */
140 public static ImmutableByteSequence copyFrom(String original) {
141 checkArgument(original != null && original.length() > 0,
142 "Cannot copy from an empty or null string");
143 return new ImmutableByteSequence(ByteBuffer.allocate(original.length())
144 .put(original.getBytes()),
145 true);
146 }
147
148 /**
Carmelo Cascone6b32c992016-04-13 11:53:09 -0700149 * Creates a new byte sequence of 8 bytes containing the given long value.
150 *
151 * @param original a long value
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700152 * @return a new immutable byte sequence
Carmelo Cascone6b32c992016-04-13 11:53:09 -0700153 */
154 public static ImmutableByteSequence copyFrom(long original) {
155 return new ImmutableByteSequence(
156 ByteBuffer.allocate(Long.BYTES).putLong(original));
157 }
158
159 /**
160 * Creates a new byte sequence of 4 bytes containing the given int value.
161 *
162 * @param original an int value
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700163 * @return a new immutable byte sequence
Carmelo Cascone6b32c992016-04-13 11:53:09 -0700164 */
165 public static ImmutableByteSequence copyFrom(int original) {
166 return new ImmutableByteSequence(
167 ByteBuffer.allocate(Integer.BYTES).putInt(original));
168 }
169
170 /**
171 * Creates a new byte sequence of 2 bytes containing the given short value.
172 *
173 * @param original a short value
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700174 * @return a new immutable byte sequence
Carmelo Cascone6b32c992016-04-13 11:53:09 -0700175 */
176 public static ImmutableByteSequence copyFrom(short original) {
177 return new ImmutableByteSequence(
178 ByteBuffer.allocate(Short.BYTES).putShort(original));
179 }
180
181 /**
182 * Creates a new byte sequence of 1 byte containing the given value.
183 *
184 * @param original a byte value
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700185 * @return a new immutable byte sequence
Carmelo Cascone6b32c992016-04-13 11:53:09 -0700186 */
187 public static ImmutableByteSequence copyFrom(byte original) {
188 return new ImmutableByteSequence(
189 ByteBuffer.allocate(Byte.BYTES).put(original));
190 }
191
192 /**
Daniele Moro53a3cdf2021-05-17 14:49:31 +0200193 * Creates a new immutable byte sequence while trimming or expanding the
194 * content of the given byte buffer to fit the given bit-width. Calling this
195 * method has the same behavior as
196 * {@code ImmutableByteSequence.copyFrom(original).fit(bitWidth)}.
197 *
198 * @param original a byte buffer value
199 * @param bitWidth a non-zero positive integer
200 * @return a new immutable byte sequence
201 * @throws ByteSequenceTrimException if the byte buffer cannot be fitted
202 */
203 public static ImmutableByteSequence copyAndFit(ByteBuffer original, int bitWidth)
204 throws ByteSequenceTrimException {
205 checkArgument(original != null && original.capacity() > 0,
206 "Cannot copy from an empty or null byte buffer");
207 checkArgument(bitWidth > 0,
208 "bit-width must be a non-zero positive integer");
209 if (original.order() == ByteOrder.LITTLE_ENDIAN) {
210 // FIXME: this can be improved, e.g. read bytes in reverse order from original
211 byte[] newBytes = new byte[original.capacity()];
212 original.get(newBytes);
213 reverse(newBytes);
214 return internalCopyAndFit(ByteBuffer.wrap(newBytes), bitWidth);
215 } else {
216 return internalCopyAndFit(original.duplicate(), bitWidth);
217 }
218 }
219
220 private static ImmutableByteSequence internalCopyAndFit(ByteBuffer byteBuf, int bitWidth)
221 throws ByteSequenceTrimException {
222 final int byteWidth = (bitWidth + 7) / 8;
223 final ByteBuffer newByteBuffer = ByteBuffer.allocate(byteWidth);
224 byteBuf.rewind();
225 if (byteWidth >= byteBuf.capacity()) {
226 newByteBuffer.position(byteWidth - byteBuf.capacity());
227 newByteBuffer.put(byteBuf);
228 } else {
229 for (int i = 0; i < byteBuf.capacity() - byteWidth; i++) {
230 if (byteBuf.get(i) != 0) {
231 throw new ByteSequenceTrimException(byteBuf, bitWidth);
232 }
233 }
234 newByteBuffer.put(byteBuf.position(byteBuf.capacity() - byteWidth));
235 }
236 return new ImmutableByteSequence(newByteBuffer);
237 }
238
239 /**
Brian O'Connoraf1d12e2017-08-17 12:21:48 -0700240 * Creates a new byte sequence of the given size where all bits are 0.
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700241 *
242 * @param size number of bytes
243 * @return a new immutable byte sequence
244 */
245 public static ImmutableByteSequence ofZeros(int size) {
Brian O'Connoraf1d12e2017-08-17 12:21:48 -0700246 // array is initialized to all 0's by default
247 return new ImmutableByteSequence(ByteBuffer.wrap(new byte[size]));
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700248 }
249
250 /**
Brian O'Connoraf1d12e2017-08-17 12:21:48 -0700251 * Creates a new byte sequence of the given size where all bits are 1.
Carmelo Casconeaa8b6292016-04-13 14:27:06 -0700252 *
253 * @param size number of bytes
254 * @return a new immutable byte sequence
255 */
256 public static ImmutableByteSequence ofOnes(int size) {
257 byte[] bytes = new byte[size];
258 Arrays.fill(bytes, (byte) 0xFF);
259 return new ImmutableByteSequence(ByteBuffer.wrap(bytes));
260 }
261
262 /**
Brian O'Connoraf1d12e2017-08-17 12:21:48 -0700263 * Creates a new byte sequence that is prefixed with specified number of
264 * zeros if val = 0 or ones if val = 0xff.
265 *
Carmelo Casconeb10194c2017-08-23 19:16:51 +0200266 * @param size number of total bytes
Brian O'Connoraf1d12e2017-08-17 12:21:48 -0700267 * @param prefixBits number of bits in prefix
Carmelo Casconeb10194c2017-08-23 19:16:51 +0200268 * @param val 0 for prefix of zeros; 0xff for prefix of ones
Brian O'Connoraf1d12e2017-08-17 12:21:48 -0700269 * @return new immutable byte sequence
270 */
271 static ImmutableByteSequence prefix(int size, long prefixBits, byte val) {
272 checkArgument(val == 0 || val == (byte) 0xff, "Val must be 0 or 0xff");
273 byte[] bytes = new byte[size];
274 int prefixBytes = (int) (prefixBits / Byte.SIZE);
275 Arrays.fill(bytes, 0, prefixBytes, val);
276 Arrays.fill(bytes, prefixBytes, bytes.length, (byte) ~val);
277 int partialBits = (int) (prefixBits % Byte.SIZE);
278 if (partialBits != 0) {
279 bytes[prefixBytes] = val == 0 ?
280 (byte) (0xff >> partialBits) : (byte) (0xff << Byte.SIZE - partialBits);
281 }
282 return new ImmutableByteSequence(ByteBuffer.wrap(bytes));
283 }
284
285 /**
Carmelo Cascone8a571af2018-04-06 23:17:04 -0700286 * Creates a new byte sequence that is prefixed with specified number of
287 * zeros.
Brian O'Connoraf1d12e2017-08-17 12:21:48 -0700288 *
Carmelo Casconeb10194c2017-08-23 19:16:51 +0200289 * @param size number of total bytes
Brian O'Connoraf1d12e2017-08-17 12:21:48 -0700290 * @param prefixBits number of bits in prefix
291 * @return new immutable byte sequence
292 */
293 public static ImmutableByteSequence prefixZeros(int size, long prefixBits) {
294 return prefix(size, prefixBits, (byte) 0);
295 }
296
297 /**
Carmelo Cascone8a571af2018-04-06 23:17:04 -0700298 * Creates a new byte sequence that is prefixed with specified number of
299 * ones.
Brian O'Connoraf1d12e2017-08-17 12:21:48 -0700300 *
Carmelo Casconeb10194c2017-08-23 19:16:51 +0200301 * @param size number of total bytes
Brian O'Connoraf1d12e2017-08-17 12:21:48 -0700302 * @param prefixBits number of bits in prefix
303 * @return new immutable byte sequence
304 */
305 public static ImmutableByteSequence prefixOnes(int size, long prefixBits) {
306 return prefix(size, prefixBits, (byte) 0xff);
307 }
308
309 /**
Carmelo Cascone6b32c992016-04-13 11:53:09 -0700310 * Returns a view of this sequence as a read-only {@link ByteBuffer}.
311 * <p>
312 * The returned buffer will have position 0, while limit and capacity will
313 * be set to this sequence {@link #size()}. The buffer order will be
314 * big-endian.
315 *
316 * @return a read-only byte buffer
317 */
318 public ByteBuffer asReadOnlyBuffer() {
319 // position, limit and capacity set rewind at constructor
320 return value.asReadOnlyBuffer();
321 }
322
323 /**
324 * Gets the number of bytes in this sequence.
325 *
326 * @return an integer value
327 */
328 public int size() {
329 return this.value.capacity();
330 }
331
332 /**
333 * Creates a new byte array view of this sequence.
334 *
335 * @return a new byte array
336 */
337 public byte[] asArray() {
338 ByteBuffer bb = asReadOnlyBuffer();
339 byte[] bytes = new byte[size()];
340 bb.get(bytes);
341 return bytes;
342 }
343
Carmelo Cascone8a571af2018-04-06 23:17:04 -0700344 private ImmutableByteSequence doBitwiseOp(ImmutableByteSequence other, BitwiseOp op) {
345 checkArgument(other != null && this.size() == other.size(),
346 "Other sequence must be non null and with same size as this");
347 byte[] newBytes = new byte[this.size()];
348 byte[] thisBytes = this.asArray();
349 byte[] otherBytes = other.asArray();
350 for (int i = 0; i < this.size(); i++) {
351 switch (op) {
352 case AND:
353 newBytes[i] = (byte) (thisBytes[i] & otherBytes[i]);
354 break;
355 case OR:
356 newBytes[i] = (byte) (thisBytes[i] | otherBytes[i]);
357 break;
358 case XOR:
359 newBytes[i] = (byte) (thisBytes[i] ^ otherBytes[i]);
360 break;
361 default:
362 throw new IllegalArgumentException(
363 "Unknown bitwise operator " + op.name());
364 }
365 }
366 return ImmutableByteSequence.copyFrom(newBytes);
367 }
368
369 /**
370 * Returns a new byte sequence corresponding to the result of a bitwise AND
371 * operation between this sequence and the given other, i.e. {@code this &
372 * other}.
373 *
374 * @param other other byte sequence
375 * @return new byte sequence
376 * @throws IllegalArgumentException if other sequence is null or its size is
377 * different than this sequence size
378 */
379 public ImmutableByteSequence bitwiseAnd(ImmutableByteSequence other) {
380 return doBitwiseOp(other, BitwiseOp.AND);
381 }
382
383 /**
384 * Returns a new byte sequence corresponding to the result of a bitwise OR
385 * operation between this sequence and the given other, i.e. {@code this |
386 * other}.
387 *
388 * @param other other byte sequence
389 * @return new byte sequence
390 * @throws IllegalArgumentException if other sequence is null or its size is
391 * different than this sequence size
392 */
393 public ImmutableByteSequence bitwiseOr(ImmutableByteSequence other) {
394 return doBitwiseOp(other, BitwiseOp.OR);
395 }
396
397 /**
398 * Returns a new byte sequence corresponding to the result of a bitwise XOR
399 * operation between this sequence and the given other, i.e. {@code this ^
400 * other}.
401 *
402 * @param other other byte sequence
403 * @return new byte sequence
404 * @throws IllegalArgumentException if other sequence is null or its size is
405 * different than this sequence size
406 */
407 public ImmutableByteSequence bitwiseXor(ImmutableByteSequence other) {
408 return doBitwiseOp(other, BitwiseOp.XOR);
409 }
410
Carmelo Cascone6b32c992016-04-13 11:53:09 -0700411 @Override
412 public int hashCode() {
413 return value.hashCode();
414 }
415
416 @Override
417 public boolean equals(Object obj) {
418 if (this == obj) {
419 return true;
420 }
421 if (obj == null || getClass() != obj.getClass()) {
422 return false;
423 }
424 final ImmutableByteSequence other = (ImmutableByteSequence) obj;
425 return Objects.equal(this.value, other.value);
426 }
427
Carmelo Casconeb10194c2017-08-23 19:16:51 +0200428 /**
Carmelo Cascone8a571af2018-04-06 23:17:04 -0700429 * Returns the index of the most significant bit (MSB), assuming a bit
430 * numbering scheme of type "LSB 0", i.e. the bit numbering starts at zero
431 * for the least significant bit (LSB). The MSB index of a byte sequence of
432 * zeros will be -1.
Carmelo Casconeb10194c2017-08-23 19:16:51 +0200433 * <p>
Carmelo Cascone8a571af2018-04-06 23:17:04 -0700434 * As an example, the following conditions always hold true: {@code
Carmelo Casconeb10194c2017-08-23 19:16:51 +0200435 * ImmutableByteSequence.copyFrom(0).msbIndex() == -1
436 * ImmutableByteSequence.copyFrom(1).msbIndex() == 0
437 * ImmutableByteSequence.copyFrom(2).msbIndex() == 1
438 * ImmutableByteSequence.copyFrom(3).msbIndex() == 1
439 * ImmutableByteSequence.copyFrom(4).msbIndex() == 2
Carmelo Cascone8a571af2018-04-06 23:17:04 -0700440 * ImmutableByteSequence.copyFrom(512).msbIndex() == 9 }
Carmelo Casconeb10194c2017-08-23 19:16:51 +0200441 *
442 * @return index of the MSB, -1 if the sequence has all bytes set to 0
443 */
444 public int msbIndex() {
445 int index = (size() * 8) - 1;
446 byteLoop:
447 for (int i = 0; i < size(); i++) {
448 byte b = value.get(i);
449 if (b != 0) {
450 for (int j = 7; j >= 0; j--) {
451 byte mask = (byte) ((1 << j) - 1);
452 if ((b & ~mask) != 0) {
453 break byteLoop;
454 }
455 index--;
456 }
457 }
458 index -= 8;
459 }
460 return index;
461 }
462
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800463 /**
Daniele Moro7aa13e62021-02-23 15:28:07 +0100464 * Returns the ASCII representation of the byte sequence if the content can
465 * be interpreted as an ASCII string, otherwise returns the hexadecimal
466 * representation of this byte sequence, e.g.0xbeef. The length of the
467 * returned string is not representative of the length of the byte sequence,
468 * as all padding zeros are removed.
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800469 *
470 * @return hexadecimal representation
471 */
Carmelo Cascone6b32c992016-04-13 11:53:09 -0700472 @Override
473 public String toString() {
Daniele Moro7aa13e62021-02-23 15:28:07 +0100474 if (this.isAscii()) {
475 return new String(value.array());
476 } else {
477 return "0x" + HexString
478 .toHexString(value.array(), "")
479 // Remove leading zeros, but leave one if string is all zeros.
480 .replaceFirst("^0+(?!$)", "");
481 }
482 }
483
484 /**
485 * Checks if the content can be interpreted as an ASCII printable string.
486 *
487 * @return True if the content can be interpreted as an ASCII printable
488 * string, false otherwise
489 */
490 public boolean isAscii() {
491 return isAscii;
Carmelo Cascone6b32c992016-04-13 11:53:09 -0700492 }
Carmelo Cascone00a59962017-06-16 17:51:49 +0900493
494 /**
Carmelo Cascone8a571af2018-04-06 23:17:04 -0700495 * Trims or expands a copy of this byte sequence so to fit the given
496 * bit-width. When trimming, the operations is deemed to be safe only if the
497 * trimmed bits are zero, i.e. it is safe to trim only when {@code bitWidth
498 * > msbIndex()}, otherwise an exception will be thrown. When expanding, the
499 * sequence will be padded with zeros. The returned byte sequence will have
500 * minimum size to contain the given bit-width.
501 *
502 * @param bitWidth a non-zero positive integer
503 * @return a new byte sequence
504 * @throws ByteSequenceTrimException if the byte sequence cannot be fitted
505 */
506 public ImmutableByteSequence fit(int bitWidth) throws ByteSequenceTrimException {
507 return doFit(this, bitWidth);
508 }
509
Carmelo Cascone8a571af2018-04-06 23:17:04 -0700510 private static ImmutableByteSequence doFit(ImmutableByteSequence original,
511 int bitWidth)
Carmelo Cascone00a59962017-06-16 17:51:49 +0900512 throws ByteSequenceTrimException {
513
514 checkNotNull(original, "byte sequence cannot be null");
515 checkArgument(bitWidth > 0, "bit-width must be a non-zero positive integer");
516
Daniele Moro48079832021-05-18 12:20:46 +0200517 int newByteWidth = (bitWidth + 7) / 8;
Carmelo Cascone00a59962017-06-16 17:51:49 +0900518
Carmelo Casconeb10194c2017-08-23 19:16:51 +0200519 if (bitWidth == original.size() * 8) {
520 // No need to fit.
521 return original;
522 }
523
524 ByteBuffer newBuffer = ByteBuffer.allocate(newByteWidth);
Carmelo Cascone00a59962017-06-16 17:51:49 +0900525
526 if (newByteWidth > original.size()) {
Carmelo Casconeb10194c2017-08-23 19:16:51 +0200527 // Pad extra bytes with 0's.
528 int numPadBytes = newByteWidth - original.size();
529 for (int i = 0; i < numPadBytes; i++) {
530 newBuffer.put((byte) 0x00);
531 }
532 newBuffer.put(original.asReadOnlyBuffer());
533 } else {
534 // Trim sequence.
535 if (bitWidth > original.msbIndex()) {
536 int diff = original.size() - newByteWidth;
537 ByteBuffer originalBuffer = original.asReadOnlyBuffer();
538 for (int i = diff; i < original.size(); i++) {
539 newBuffer.put(originalBuffer.get(i));
Carmelo Cascone00a59962017-06-16 17:51:49 +0900540 }
541 } else {
Carmelo Casconeb10194c2017-08-23 19:16:51 +0200542 throw new ByteSequenceTrimException(original, bitWidth);
Carmelo Cascone00a59962017-06-16 17:51:49 +0900543 }
Carmelo Cascone00a59962017-06-16 17:51:49 +0900544 }
545
Carmelo Casconeb10194c2017-08-23 19:16:51 +0200546 return new ImmutableByteSequence(newBuffer);
Carmelo Cascone00a59962017-06-16 17:51:49 +0900547 }
548
549 /**
Daniele Moro53a3cdf2021-05-17 14:49:31 +0200550 * Signals a trim exception during byte sequence creation.
Carmelo Cascone00a59962017-06-16 17:51:49 +0900551 */
552 public static class ByteSequenceTrimException extends Exception {
Carmelo Casconeb10194c2017-08-23 19:16:51 +0200553 ByteSequenceTrimException(ImmutableByteSequence original, int bitWidth) {
554 super(format("cannot trim %s into a %d bits long value",
555 original, bitWidth));
Carmelo Cascone00a59962017-06-16 17:51:49 +0900556 }
Daniele Moro53a3cdf2021-05-17 14:49:31 +0200557
558 ByteSequenceTrimException(ByteBuffer original, int bitWidth) {
559 super(format("cannot trim %s (ByteBuffer) into a %d bits long value",
560 original, bitWidth));
561 }
Carmelo Cascone00a59962017-06-16 17:51:49 +0900562 }
Ray Milkeybb23e0b2016-08-02 17:00:21 -0700563}