blob: c83c6b2d3557e5fe2523945bcc51333aebd1e9f0 [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
19import com.google.common.testing.EqualsTester;
Yuta HIGUCHI6800ced2017-11-20 11:15:37 -080020import org.apache.commons.lang3.RandomUtils;
Carmelo Casconeb10194c2017-08-23 19:16:51 +020021import org.junit.Assert;
Brian O'Connoraf1d12e2017-08-17 12:21:48 -070022import org.junit.Rule;
Carmelo Cascone6b32c992016-04-13 11:53:09 -070023import org.junit.Test;
Brian O'Connoraf1d12e2017-08-17 12:21:48 -070024import org.junit.rules.ExpectedException;
Carmelo Cascone6b32c992016-04-13 11:53:09 -070025
26import java.nio.ByteBuffer;
27import java.nio.ByteOrder;
Daniele Moro53a3cdf2021-05-17 14:49:31 +020028import java.util.Arrays;
Carmelo Cascone6b32c992016-04-13 11:53:09 -070029import java.util.Random;
30
Carmelo Casconed047bd22017-08-29 21:36:07 +020031import static java.lang.Integer.max;
Carmelo Casconeb10194c2017-08-23 19:16:51 +020032import static java.lang.String.format;
Carmelo Cascone6b32c992016-04-13 11:53:09 -070033import static org.hamcrest.MatcherAssert.assertThat;
34import static org.hamcrest.Matchers.equalTo;
35import static org.hamcrest.Matchers.is;
36
37public class ImmutableByteSequenceTest {
Carmelo Casconed047bd22017-08-29 21:36:07 +020038 public static final int MIN_RAND_FIT_VALUE = 0xf;
39 public static final int MAX_RAND_FIT_VALUE = 0x7fffffff;
Brian O'Connoraf1d12e2017-08-17 12:21:48 -070040 @Rule
41 public ExpectedException thrown = ExpectedException.none();
Carmelo Cascone6b32c992016-04-13 11:53:09 -070042
43 @Test
44 public void testCopy() throws Exception {
45
46 byte byteValue = (byte) 1;
Yuta HIGUCHI6800ced2017-11-20 11:15:37 -080047 short shortValue = byteValue;
48 int intValue = byteValue;
49 long longValue = byteValue;
Carmelo Cascone6b32c992016-04-13 11:53:09 -070050 byte[] arrayValue = new byte[64];
51 arrayValue[63] = byteValue;
52 ByteBuffer bufferValue = ByteBuffer.allocate(64).put(arrayValue);
53
54 ImmutableByteSequence bsByte = ImmutableByteSequence.copyFrom(byteValue);
55 ImmutableByteSequence bsShort = ImmutableByteSequence.copyFrom(shortValue);
56 ImmutableByteSequence bsInt = ImmutableByteSequence.copyFrom(intValue);
57 ImmutableByteSequence bsLong = ImmutableByteSequence.copyFrom(longValue);
58 ImmutableByteSequence bsArray = ImmutableByteSequence.copyFrom(arrayValue);
59 ImmutableByteSequence bsBuffer = ImmutableByteSequence.copyFrom(bufferValue);
60
61 assertThat("byte sequence of a byte value must have size 1",
62 bsByte.size(), is(equalTo(1)));
63 assertThat("byte sequence of a short value must have size 2",
64 bsShort.size(), is(equalTo(2)));
65 assertThat("byte sequence of an int value must have size 4",
66 bsInt.size(), is(equalTo(4)));
67 assertThat("byte sequence of a long value must have size 8",
68 bsLong.size(), is(equalTo(8)));
69 assertThat("byte sequence of a byte array value must have same size of the array",
70 bsArray.size(), is(equalTo(arrayValue.length)));
71 assertThat("byte sequence of a byte buffer value must have same size of the buffer",
72 bsBuffer.size(), is(equalTo(bufferValue.capacity())));
73
74 String errStr = "incorrect byte sequence value";
75
76 assertThat(errStr, bsByte.asArray()[0], is(equalTo(byteValue)));
77 assertThat(errStr, bsShort.asArray()[1], is(equalTo(byteValue)));
78 assertThat(errStr, bsInt.asArray()[3], is(equalTo(byteValue)));
79 assertThat(errStr, bsLong.asArray()[7], is(equalTo(byteValue)));
80 assertThat(errStr, bsArray.asArray()[63], is(equalTo(byteValue)));
81 assertThat(errStr, bsBuffer.asArray()[63], is(equalTo(byteValue)));
82 }
83
84 @Test
Daniele Moro53a3cdf2021-05-17 14:49:31 +020085 public void testCopyAndFit() throws Exception {
86 int originalByteWidth = 3;
87 int paddedByteWidth = 4;
88 int trimmedByteWidth = 2;
89 int indexFirstNonZeroByte = 1;
90
91 byte byteValue = (byte) 1;
92 byte[] arrayValue = new byte[originalByteWidth];
93 arrayValue[indexFirstNonZeroByte] = byteValue;
94 ByteBuffer bufferValue = ByteBuffer.allocate(originalByteWidth).put(arrayValue);
95
96 ImmutableByteSequence bsBuffer = ImmutableByteSequence.copyAndFit(
97 bufferValue, originalByteWidth * 8);
98 ImmutableByteSequence bsBufferTrimmed = ImmutableByteSequence.copyAndFit(
99 bufferValue, trimmedByteWidth * 8);
100 ImmutableByteSequence bsBufferPadded = ImmutableByteSequence.copyAndFit(
101 bufferValue, paddedByteWidth * 8);
102
103 assertThat("byte sequence of the byte buffer must be 3 bytes long",
104 bsBuffer.size(), is(equalTo(originalByteWidth)));
105 assertThat("byte sequence of the byte buffer must be 3 bytes long",
106 bsBufferTrimmed.size(), is(equalTo(trimmedByteWidth)));
107 assertThat("byte sequence of the byte buffer must be 3 bytes long",
108 bsBufferPadded.size(), is(equalTo(paddedByteWidth)));
109
110 String errStr = "incorrect byte sequence value";
111
112 assertThat(errStr, bsBuffer.asArray()[indexFirstNonZeroByte], is(equalTo(byteValue)));
113 assertThat(errStr, bsBufferTrimmed.asArray()[indexFirstNonZeroByte - 1], is(equalTo(byteValue)));
114 assertThat(errStr, bsBufferPadded.asArray()[indexFirstNonZeroByte + 1], is(equalTo(byteValue)));
115 assertThat(errStr, bsBufferPadded.asArray()[paddedByteWidth - 1], is(equalTo((byte) 0x00)));
116 }
117
118 @Test
119 public void testCopyAndFitEndianness() throws Exception {
120 int originalByteWidth = 4;
121 int indexByteNonZeroBig = 1;
122 int indexByteNonZeroLittle = 2;
123 byte byteValue = (byte) 1;
124
125 ByteBuffer bbBigEndian = ByteBuffer
126 .allocate(originalByteWidth)
127 .order(ByteOrder.BIG_ENDIAN);
128 bbBigEndian.put(indexByteNonZeroBig, byteValue);
129 ImmutableByteSequence bsBufferCopyBigEndian =
130 ImmutableByteSequence.copyAndFit(bbBigEndian, originalByteWidth * 8);
131
132 ByteBuffer bbLittleEndian = ByteBuffer
133 .allocate(originalByteWidth)
134 .order(ByteOrder.LITTLE_ENDIAN);
135 bbLittleEndian.put(indexByteNonZeroLittle, byteValue);
136 ImmutableByteSequence bsBufferCopyLittleEndian =
137 ImmutableByteSequence.copyAndFit(bbLittleEndian, originalByteWidth * 8);
138
139 // creates a new sequence from primitive type
140 byte[] arrayValue = new byte[originalByteWidth];
141 arrayValue[indexByteNonZeroBig] = byteValue;
142 ImmutableByteSequence bsArrayCopy =
143 ImmutableByteSequence.copyFrom(arrayValue);
144
145 new EqualsTester()
146 // big-endian byte array cannot be equal to little-endian array
147 .addEqualityGroup(bbBigEndian.array())
148 .addEqualityGroup(bbLittleEndian.array())
149 // all byte sequences must be equal
150 .addEqualityGroup(bsBufferCopyBigEndian,
151 bsBufferCopyLittleEndian,
152 bsArrayCopy)
153 // byte buffer views of all sequences must be equal
154 .addEqualityGroup(bsBufferCopyBigEndian.asReadOnlyBuffer(),
155 bsBufferCopyLittleEndian.asReadOnlyBuffer(),
156 bsArrayCopy.asReadOnlyBuffer())
157 // byte buffer orders of all sequences must be ByteOrder.BIG_ENDIAN
158 .addEqualityGroup(bsBufferCopyBigEndian.asReadOnlyBuffer().order(),
159 bsBufferCopyLittleEndian.asReadOnlyBuffer().order(),
160 bsArrayCopy.asReadOnlyBuffer().order(),
161 ByteOrder.BIG_ENDIAN)
162 .testEquals();
163 }
164
165 @Test
166 public void testIllegalCopyAndFit() throws Exception {
167 int originalByteWidth = 3;
168 int trimmedByteWidth = 1;
169 int indexFirstNonZeroByte = 1;
170
171 byte byteValue = (byte) 1;
172 byte[] arrayValue = new byte[originalByteWidth];
173 arrayValue[indexFirstNonZeroByte] = byteValue;
174 ByteBuffer bufferValue = ByteBuffer.allocate(originalByteWidth).put(arrayValue);
175
176 try {
177 ImmutableByteSequence.copyAndFit(bufferValue, trimmedByteWidth * 8);
178 Assert.fail(format("Expect ByteSequenceTrimException due to value = %s and bitWidth %d",
179 Arrays.toString(arrayValue), trimmedByteWidth * 8));
180 } catch (ImmutableByteSequence.ByteSequenceTrimException e) {
181 // We expect this.
182 }
183 }
184
185 @Test
Carmelo Cascone6b32c992016-04-13 11:53:09 -0700186 public void testEndianness() throws Exception {
187
Yuta HIGUCHI6800ced2017-11-20 11:15:37 -0800188 long longValue = RandomUtils.nextLong();
Carmelo Cascone6b32c992016-04-13 11:53:09 -0700189
190 // creates a new sequence from a big-endian buffer
191 ByteBuffer bbBigEndian = ByteBuffer
192 .allocate(8)
193 .order(ByteOrder.BIG_ENDIAN)
194 .putLong(longValue);
195 ImmutableByteSequence bsBufferCopyBigEndian =
196 ImmutableByteSequence.copyFrom(bbBigEndian);
197
198 // creates a new sequence from a little-endian buffer
199 ByteBuffer bbLittleEndian = ByteBuffer
200 .allocate(8)
201 .order(ByteOrder.LITTLE_ENDIAN)
202 .putLong(longValue);
203 ImmutableByteSequence bsBufferCopyLittleEndian =
204 ImmutableByteSequence.copyFrom(bbLittleEndian);
205
206 // creates a new sequence from primitive type
207 ImmutableByteSequence bsLongCopy =
208 ImmutableByteSequence.copyFrom(longValue);
209
210
211 new EqualsTester()
212 // big-endian byte array cannot be equal to little-endian array
213 .addEqualityGroup(bbBigEndian.array())
214 .addEqualityGroup(bbLittleEndian.array())
215 // all byte sequences must be equal
216 .addEqualityGroup(bsBufferCopyBigEndian,
217 bsBufferCopyLittleEndian,
218 bsLongCopy)
219 // byte buffer views of all sequences must be equal
220 .addEqualityGroup(bsBufferCopyBigEndian.asReadOnlyBuffer(),
221 bsBufferCopyLittleEndian.asReadOnlyBuffer(),
222 bsLongCopy.asReadOnlyBuffer())
223 // byte buffer orders of all sequences must be ByteOrder.BIG_ENDIAN
224 .addEqualityGroup(bsBufferCopyBigEndian.asReadOnlyBuffer().order(),
225 bsBufferCopyLittleEndian.asReadOnlyBuffer().order(),
226 bsLongCopy.asReadOnlyBuffer().order(),
227 ByteOrder.BIG_ENDIAN)
228 .testEquals();
229 }
Brian O'Connoraf1d12e2017-08-17 12:21:48 -0700230
231 @Test
232 public void testBitSetMethods() throws Exception {
233 // All zeros tests
234 assertThat("3 bytes, all 0's",
Carmelo Casconeb10194c2017-08-23 19:16:51 +0200235 ImmutableByteSequence.ofZeros(3),
236 is(equalTo(ImmutableByteSequence.copyFrom(
237 new byte[]{0, 0, 0}))));
Brian O'Connoraf1d12e2017-08-17 12:21:48 -0700238 assertThat("3 bytes, all 0's via prefix",
Carmelo Casconeb10194c2017-08-23 19:16:51 +0200239 ImmutableByteSequence.prefixZeros(3, 3 * Byte.SIZE),
240 is(equalTo(ImmutableByteSequence.copyFrom(
241 new byte[]{0, 0, 0}))));
Brian O'Connoraf1d12e2017-08-17 12:21:48 -0700242
243 // All ones tests
244 assertThat("3 bytes, all 1's",
Carmelo Casconeb10194c2017-08-23 19:16:51 +0200245 ImmutableByteSequence.ofZeros(3),
246 is(equalTo(ImmutableByteSequence.copyFrom(
247 new byte[]{0, 0, 0}))));
Brian O'Connoraf1d12e2017-08-17 12:21:48 -0700248 assertThat("3 bytes, all 1's via prefix",
Carmelo Casconeb10194c2017-08-23 19:16:51 +0200249 ImmutableByteSequence.prefixOnes(3, 3 * Byte.SIZE),
250 is(equalTo(ImmutableByteSequence.copyFrom(
251 new byte[]{(byte) 0xff, (byte) 0xff, (byte) 0xff}))));
Brian O'Connoraf1d12e2017-08-17 12:21:48 -0700252
253 // Zero prefix tests
254 assertThat("2 bytes, prefixed with 5 0's",
Carmelo Casconeb10194c2017-08-23 19:16:51 +0200255 ImmutableByteSequence.prefix(2, 5, (byte) 0),
256 is(equalTo(ImmutableByteSequence.copyFrom(
257 new byte[]{(byte) 0x7, (byte) 0xff}))));
Brian O'Connoraf1d12e2017-08-17 12:21:48 -0700258 assertThat("4 bytes, prefixed with 16 0's",
Carmelo Casconeb10194c2017-08-23 19:16:51 +0200259 ImmutableByteSequence.prefix(4, 16, (byte) 0),
260 is(equalTo(ImmutableByteSequence.copyFrom(
261 new byte[]{0, 0, (byte) 0xff, (byte) 0xff}))));
Brian O'Connoraf1d12e2017-08-17 12:21:48 -0700262 assertThat("4 bytes, prefixed with 20 0's",
Carmelo Casconeb10194c2017-08-23 19:16:51 +0200263 ImmutableByteSequence.prefix(4, 20, (byte) 0),
264 is(equalTo(ImmutableByteSequence.copyFrom(
265 new byte[]{0, 0, (byte) 0x0f, (byte) 0xff}))));
Brian O'Connoraf1d12e2017-08-17 12:21:48 -0700266 assertThat("8 bytes, prefixed with 36 0's",
Carmelo Casconeb10194c2017-08-23 19:16:51 +0200267 ImmutableByteSequence.prefixZeros(8, 38),
268 is(equalTo(ImmutableByteSequence.copyFrom(
269 new byte[]{0, 0, 0, 0, (byte) 0x03, (byte) 0xff, (byte) 0xff, (byte) 0xff}))));
Brian O'Connoraf1d12e2017-08-17 12:21:48 -0700270
271 // Ones prefix tests
272 assertThat("2 bytes, prefixed with 5 1's",
Carmelo Casconeb10194c2017-08-23 19:16:51 +0200273 ImmutableByteSequence.prefix(2, 5, (byte) 0xff),
274 is(equalTo(ImmutableByteSequence.copyFrom(
275 new byte[]{(byte) 0xf8, 0}))));
Brian O'Connoraf1d12e2017-08-17 12:21:48 -0700276 assertThat("4 bytes, prefixed with 16 1's",
Carmelo Casconeb10194c2017-08-23 19:16:51 +0200277 ImmutableByteSequence.prefix(4, 16, (byte) 0xff),
278 is(equalTo(ImmutableByteSequence.copyFrom(
279 new byte[]{(byte) 0xff, (byte) 0xff, 0, 0}))));
Brian O'Connoraf1d12e2017-08-17 12:21:48 -0700280 assertThat("4 bytes, prefixed with 20 1's",
Carmelo Casconeb10194c2017-08-23 19:16:51 +0200281 ImmutableByteSequence.prefix(4, 20, (byte) 0xff),
282 is(equalTo(ImmutableByteSequence.copyFrom(
283 new byte[]{(byte) 0xff, (byte) 0xff, (byte) 0xf0, 0}))));
Brian O'Connoraf1d12e2017-08-17 12:21:48 -0700284 assertThat("8 bytes, prefixed with 10 1's",
Carmelo Casconeb10194c2017-08-23 19:16:51 +0200285 ImmutableByteSequence.prefixOnes(8, 10),
286 is(equalTo(ImmutableByteSequence.copyFrom(
287 new byte[]{(byte) 0xff, (byte) 0xc0, 0, 0, 0, 0, 0, 0}))));
Brian O'Connoraf1d12e2017-08-17 12:21:48 -0700288 }
289
290 @Test
291 public void testBadPrefixVal() {
292 thrown.expect(IllegalArgumentException.class);
293 thrown.reportMissingExceptionWithMessage(
294 "Expect IllegalArgumentException due to val = 0x7");
295 ImmutableByteSequence.prefix(5, 10, (byte) 0x7);
296 }
Carmelo Casconeb10194c2017-08-23 19:16:51 +0200297
298 @Test
299 public void testMsbIndex() {
300 assertThat("Value 0 should have MSB index -1",
301 ImmutableByteSequence.copyFrom(0).msbIndex(), is(-1));
302 for (int i = 0; i < 63; i++) {
303 long value = (long) Math.pow(2, i);
304 assertThat(format("Value %d should have MSB index %d", value, i),
305 ImmutableByteSequence.copyFrom(value).msbIndex(), is(i));
306 }
307 }
308
309 private void checkIllegalFit(ImmutableByteSequence bytes, int bitWidth) {
310 try {
Carmelo Cascone8a571af2018-04-06 23:17:04 -0700311 bytes.fit(bitWidth);
Carmelo Casconeb10194c2017-08-23 19:16:51 +0200312 Assert.fail(format("Except ByteSequenceTrimException due to value = %s and bitWidth %d",
313 bytes.toString(), bitWidth));
314 } catch (ImmutableByteSequence.ByteSequenceTrimException e) {
315 // We expect this.
316 }
317 }
318
319 private void checkLegalFit(ImmutableByteSequence bytes, int bitWidth)
320 throws ImmutableByteSequence.ByteSequenceTrimException {
Carmelo Cascone8a571af2018-04-06 23:17:04 -0700321 ImmutableByteSequence fitBytes = bytes.fit(bitWidth);
322 ImmutableByteSequence sameBytes = fitBytes.fit(bytes.size() * 8);
Carmelo Casconeb10194c2017-08-23 19:16:51 +0200323 assertThat(format("Fitted value %s (re-extended to %s) not equal to original value %s",
324 fitBytes, sameBytes, bytes),
325 sameBytes,
326 is(equalTo(bytes)));
327 }
328
329 @Test
330 public void testFit() throws ImmutableByteSequence.ByteSequenceTrimException {
331 // Test fit by forcing a given MSB index.
332 for (int msbIndex = 0; msbIndex < 32; msbIndex++) {
333 long value = (long) Math.pow(2, msbIndex);
334 ImmutableByteSequence bytes = ImmutableByteSequence.copyFrom(value);
335 checkLegalFit(bytes, msbIndex + 1);
336 if (msbIndex != 0) {
337 checkIllegalFit(bytes, msbIndex);
338 }
339 }
340 }
341
342 @Test
343 public void testRandomFit() throws ImmutableByteSequence.ByteSequenceTrimException {
344 // Test fit against the computed MSB index.
345 Random random = new Random();
346 for (int i = 0; i < 1000; i++) {
Carmelo Casconed047bd22017-08-29 21:36:07 +0200347 int randValue = random.nextInt((MAX_RAND_FIT_VALUE - MIN_RAND_FIT_VALUE) + 1) + MIN_RAND_FIT_VALUE;
348 ImmutableByteSequence bytes = ImmutableByteSequence.copyFrom((long) randValue);
Carmelo Casconeb10194c2017-08-23 19:16:51 +0200349 int msbIndex = bytes.msbIndex();
Carmelo Casconed047bd22017-08-29 21:36:07 +0200350 // Truncate.
351 checkIllegalFit(bytes, max(msbIndex - random.nextInt(16), 1));
352 // Expand.
353 checkLegalFit(bytes, msbIndex + 2 + random.nextInt(128));
354 // Fit to same bit-width of original value.
Carmelo Casconeb10194c2017-08-23 19:16:51 +0200355 checkLegalFit(bytes, msbIndex + 1);
356 }
357 }
Carmelo Cascone8a571af2018-04-06 23:17:04 -0700358
359 @Test
360 public void testBitwiseOperations() {
361 Random random = new Random();
362 long long1 = random.nextLong();
363 long long2 = random.nextLong();
364
365 ImmutableByteSequence bs1 = ImmutableByteSequence.copyFrom(long1);
366 ImmutableByteSequence bs2 = ImmutableByteSequence.copyFrom(long2);
367
368 ImmutableByteSequence andBs = bs1.bitwiseAnd(bs2);
369 ImmutableByteSequence orBs = bs1.bitwiseOr(bs2);
370 ImmutableByteSequence xorBs = bs1.bitwiseXor(bs2);
371
372 assertThat("Invalid bitwise AND result",
373 andBs.asReadOnlyBuffer().getLong(), is(long1 & long2));
374 assertThat("Invalid bitwise OR result",
375 orBs.asReadOnlyBuffer().getLong(), is(long1 | long2));
376 assertThat("Invalid bitwise XOR result",
377 xorBs.asReadOnlyBuffer().getLong(), is(long1 ^ long2));
378 }
379}