Skip to content

Commit 3e28bc2

Browse files
committed
Add feature for gathering parts of the array as primitives
1 parent 2e51cf3 commit 3e28bc2

File tree

6 files changed

+291
-93
lines changed

6 files changed

+291
-93
lines changed

CHANGELOG

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## v0.4.4
44

5+
* add feature for gathering parts of the array as primitives (e.g. intAt(int position))
6+
57
## v0.4.3
68

79
* add toFloat/toDouble

README.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,11 +303,18 @@ Bytes.wrap(array).lengthBit(); //8 * array.length
303303
Bytes.wrap(array).isEmpty();
304304
```
305305

306-
And others:
306+
Accessing part of the array as primitives from arbitrary position:
307307

308308
```java
309-
Bytes.wrap(array).bitAt(4); // 0010 1000 -> true
309+
Bytes.wrap(array).bitAt(4); // 0010 1000 -> false
310310
Bytes.wrap(array).byteAt(14);
311+
Bytes.wrap(array).intAt(4);
312+
Bytes.wrap(array).longAt(6);
313+
```
314+
315+
And others:
316+
317+
```java
311318
Bytes.wrap(array).count(0x01); //occurrences of 0x01
312319
Bytes.wrap(array).entropy();
313320
```

src/main/java/at/favre/lib/bytes/Bytes.java

Lines changed: 104 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,8 @@ public Bytes not() {
661661
}
662662

663663
/**
664-
* Bitwise left shifting of internal byte array.
664+
* Bitwise left shifting of internal byte array (i.e. <code>&#x3C;&#x3C;</code>). Unlike {@link BigInteger}'s implementation, this one will never
665+
* grow or shrink the underlying array. Either a bit is pushed out of the array or a zero is pushed in.
665666
* <p>
666667
* See the considerations about possible in-place operation in {@link #transform(BytesTransformer)}.
667668
*
@@ -674,7 +675,9 @@ public Bytes leftShift(int shiftCount) {
674675
}
675676

676677
/**
677-
* Bitwise unsigned/logical right shifting of internal byte array (i.e. <code>&#x3E;&#x3E;&#x3E;</code>).
678+
* Bitwise unsigned/logical right shifting of internal byte array (i.e. <code>&#x3E;&#x3E;&#x3E;</code>). Unlike
679+
* {@link BigInteger}'s implementation, this one will never grow or shrink the underlying array. Either a bit is pushed
680+
* out of the array or a zero is pushed in.
678681
* <p>
679682
* See the considerations about possible in-place operation in {@link #transform(BytesTransformer)}.
680683
*
@@ -948,6 +951,21 @@ public int lastIndexOf(byte target) {
948951
return Util.lastIndexOf(internalArray(), target, 0, length());
949952
}
950953

954+
/**
955+
* Returns the {@code bit} value as boolean at the specified index. Bit index 0 is the LSB, so for example byte word
956+
* <code>1000 0000</code> has <code>bitAt(0) == false</code> and <code>bitAt(7) == true</code>.
957+
*
958+
* @param bitIndex the index of the {@code bit} value.
959+
* @return true if bit at given index is set, false otherwise
960+
* @throws IndexOutOfBoundsException if the {@code bitIndex} argument is negative or not less than the length of this array in bits.
961+
*/
962+
public boolean bitAt(int bitIndex) {
963+
if (bitIndex < 0 || bitIndex > lengthBit()) {
964+
throw new IndexOutOfBoundsException("cannot get bit from index out of bounds: " + bitIndex);
965+
}
966+
return ((byteAt(length() - 1 - (bitIndex / 8)) >>> bitIndex % 8) & 1) != 0;
967+
}
968+
951969
/**
952970
* Returns the {@code byte} value at the specified index.
953971
* An index ranges from {@code 0} to {@code length() - 1}. The first {@code char} value of the sequence
@@ -965,18 +983,55 @@ public byte byteAt(int index) {
965983
}
966984

967985
/**
968-
* Returns the {@code bit} value as boolean at the specified index. Bit index 0 is the LSB, so for example byte word
969-
* <code>1000 0000</code> has <code>bitAt(0) == false</code> and <code>bitAt(7) == true</code>.
986+
* Returns the {@code char} value at the specified index.
987+
* Reads the primitive from given index and the following byte and interprets it according to byte order.
970988
*
971-
* @param bitIndex the index of the {@code bit} value.
972-
* @return true if bit at given index is set, false otherwise
973-
* @throws IndexOutOfBoundsException if the {@code bitIndex} argument is negative or not less than the length of this array in bits.
989+
* @param index the index of the {@code char} value.
990+
* @return the {@code char} value at the specified index of the underlying byte array.
991+
* @throws IndexOutOfBoundsException if the {@code index} argument is negative or length is greater than index - 2
974992
*/
975-
public boolean bitAt(int bitIndex) {
976-
if (bitIndex < 0 || bitIndex > lengthBit()) {
977-
throw new IndexOutOfBoundsException("cannot get bit from index out of bounds: " + bitIndex);
978-
}
979-
return ((byteAt(length() - 1 - (bitIndex / 8)) >>> bitIndex % 8) & 1) != 0;
993+
public char charAt(int index) {
994+
Util.checkIndexBounds(length(), index, 2, "char");
995+
return ((ByteBuffer) ByteBuffer.wrap(internalArray()).order(byteOrder).position(index)).getChar();
996+
}
997+
998+
/**
999+
* Returns the {@code short} value at the specified index.
1000+
* Reads the primitive from given index and the following byte and interprets it according to byte order.
1001+
*
1002+
* @param index the index of the {@code short} value.
1003+
* @return the {@code short} value at the specified index of the underlying byte array.
1004+
* @throws IndexOutOfBoundsException if the {@code index} argument is negative or length is greater than index - 2
1005+
*/
1006+
public short shortAt(int index) {
1007+
Util.checkIndexBounds(length(), index, 2, "short");
1008+
return ((ByteBuffer) ByteBuffer.wrap(internalArray()).order(byteOrder).position(index)).getShort();
1009+
}
1010+
1011+
/**
1012+
* Returns the {@code int} value at the specified index.
1013+
* Reads the primitive from given index and the following 3 bytes and interprets it according to byte order.
1014+
*
1015+
* @param index the index of the {@code int} value.
1016+
* @return the {@code int} value at the specified index of the underlying byte array.
1017+
* @throws IndexOutOfBoundsException if the {@code int} argument is negative or length is greater than index - 4
1018+
*/
1019+
public int intAt(int index) {
1020+
Util.checkIndexBounds(length(), index, 4, "int");
1021+
return ((ByteBuffer) ByteBuffer.wrap(internalArray()).order(byteOrder).position(index)).getInt();
1022+
}
1023+
1024+
/**
1025+
* Returns the {@code long} value at the specified index.
1026+
* Reads the primitive from given index and the following 7 bytes and interprets it according to byte order.
1027+
*
1028+
* @param index the index of the {@code long} value.
1029+
* @return the {@code long} value at the specified index of the underlying byte array.
1030+
* @throws IndexOutOfBoundsException if the {@code long} argument is negative or length is greater than index - 8
1031+
*/
1032+
public long longAt(int index) {
1033+
Util.checkIndexBounds(length(), index, 8, "long");
1034+
return ((ByteBuffer) ByteBuffer.wrap(internalArray()).order(byteOrder).position(index)).getLong();
9801035
}
9811036

9821037
/**
@@ -1279,107 +1334,103 @@ public BigInteger toBigInteger() {
12791334
}
12801335

12811336
/**
1282-
* If the underlying byte array is smaller than or equal to 1 byte / 8 bit returns unsigned two-complement
1337+
* If the underlying byte array is exactly 1 byte / 8 bit long, returns unsigned two-complement
12831338
* representation for a Java byte value.
1339+
* <p>
1340+
* If you just want to get the first element as {@code byte}, see {@link #byteAt(int)}, using index zero.
12841341
*
12851342
* @return the byte representation
1286-
* @throws UnsupportedOperationException if byte array is longer than 1 byte
1343+
* @throws IllegalStateException if byte array has length not equal to 1
12871344
* @see <a href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html">Primitive Types</a>
12881345
*/
12891346
public byte toByte() {
1290-
if (length() > 1) {
1291-
throw new UnsupportedOperationException("cannot convert to byte if length > 1 byte");
1292-
}
1293-
return internalBuffer().get();
1347+
Util.checkExactLength(length(), 1, "byte");
1348+
return internalArray()[0];
12941349
}
12951350

12961351
/**
1297-
* If the underlying byte array is smaller than or equal to 2 byte / 16 bit returns unsigned two-complement
1352+
* If the underlying byte array is exactly 2 byte / 16 bit long, return unsigned two-complement
12981353
* representation for a Java char integer value. The output is dependent on the set {@link #byteOrder()}.
1354+
* <p>
1355+
* If you just want to get the first 2 bytes as {@code char}, see {@link #charAt(int)} using index zero.
12991356
*
13001357
* @return the int representation
1301-
* @throws UnsupportedOperationException if byte array is longer than 2 byte
1358+
* @throws IllegalStateException if byte array has length not equal to 2
13021359
* @see <a href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html">Primitive Types</a>
13031360
*/
13041361
public char toChar() {
1305-
if (length() > 2) {
1306-
throw new UnsupportedOperationException("cannot convert to char if length > 2 byte");
1307-
}
1308-
return resize(2).internalBuffer().getChar();
1362+
Util.checkExactLength(length(), 2, "char");
1363+
return internalBuffer().getChar();
13091364
}
13101365

13111366
/**
1312-
* If the underlying byte array is smaller than or equal to 2 byte / 16 bit returns signed two-complement
1367+
* If the underlying byte array is exactly 2 byte / 16 bit long, return signed two-complement
13131368
* representation for a Java short integer value. The output is dependent on the set {@link #byteOrder()}.
1369+
* <p>
1370+
* If you just want to get the first 2 bytes as {@code short}, see {@link #shortAt(int)} using index zero.
13141371
*
13151372
* @return the int representation
1316-
* @throws UnsupportedOperationException if byte array is longer than 2 byte
1373+
* @throws IllegalStateException if byte array has length not equal to 2
13171374
* @see <a href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html">Primitive Types</a>
13181375
*/
13191376
public short toShort() {
1320-
if (length() > 2) {
1321-
throw new UnsupportedOperationException("cannot convert to short if length > 2 byte");
1322-
}
1323-
return resize(2).internalBuffer().getShort();
1377+
Util.checkExactLength(length(), 2, "short");
1378+
return internalBuffer().getShort();
13241379
}
13251380

13261381
/**
1327-
* If the underlying byte array is smaller than or equal to 4 byte / 32 bit returns signed two-complement
1382+
* If the underlying byte array is exactly 4 byte / 32 bit long, return signed two-complement
13281383
* representation for a Java signed integer value. The output is dependent on the set {@link #byteOrder()}.
1384+
* <p>
1385+
* If you just want to get the first 4 bytes as {@code int}, see {@link #intAt(int)} using index zero.
13291386
*
13301387
* @return the int representation
1331-
* @throws UnsupportedOperationException if byte array is longer than 4 byte
1388+
* @throws IllegalStateException if byte array has length not equal to 4
13321389
* @see <a href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html">Primitive Types</a>
13331390
*/
13341391
public int toInt() {
1335-
if (length() > 4) {
1336-
throw new UnsupportedOperationException("cannot convert to int if length > 4 byte");
1337-
}
1338-
return resize(4).internalBuffer().getInt();
1392+
Util.checkExactLength(length(), 4, "int");
1393+
return internalBuffer().getInt();
13391394
}
13401395

13411396
/**
1342-
* If the underlying byte array is smaller than or equal to 8 byte / 64 bit returns signed two-complement
1397+
* If the underlying byte array is exactly 8 byte / 64 bit long, return signed two-complement
13431398
* representation for a Java signed long integer value. The output is dependent on the set {@link #byteOrder()}.
1399+
* <p>
1400+
* If you just want to get the first 4 bytes as {@code long}, see {@link #longAt(int)} using index zero.
13441401
*
13451402
* @return the long representation
1346-
* @throws UnsupportedOperationException if byte array is longer than 8 byte
1403+
* @throws IllegalStateException if byte array has length not equal to 8
13471404
* @see <a href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html">Primitive Types</a>
13481405
*/
13491406
public long toLong() {
1350-
if (length() > 8) {
1351-
throw new UnsupportedOperationException("cannot convert to long if length > 8 byte");
1352-
}
1407+
Util.checkExactLength(length(), 8, "long");
13531408
return resize(8).internalBuffer().getLong();
13541409
}
13551410

13561411
/**
1357-
* If the underlying byte array is smaller than or equal to 4 byte / 32 bit returns the
1412+
* If the underlying byte array is exactly 4 byte / 32 bit long, return the
13581413
* representation for a Java float value. The output is dependent on the set {@link #byteOrder()}.
13591414
*
13601415
* @return the float representation
1361-
* @throws UnsupportedOperationException if byte array is longer than 4 byte
1416+
* @throws IllegalStateException if byte array has length not equal to 4
13621417
* @see <a href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html">Primitive Types</a>
13631418
*/
13641419
public float toFloat() {
1365-
if (length() > 4) {
1366-
throw new UnsupportedOperationException("cannot convert to float if length > 4 byte");
1367-
}
1368-
return resize(4).internalBuffer().getFloat();
1420+
Util.checkExactLength(length(), 4, "float");
1421+
return internalBuffer().getFloat();
13691422
}
13701423

13711424
/**
1372-
* If the underlying byte array is smaller than or equal to 8 byte / 64 bit returns the
1425+
* If the underlying byte array is exactly 8 byte / 64 bit long, return the
13731426
* representation for a Java float value. The output is dependent on the set {@link #byteOrder()}.
13741427
*
1375-
* @return the float representation
1376-
* @throws UnsupportedOperationException if byte array is longer than 8 byte
1428+
* @return the double representation
1429+
* @throws IllegalStateException if byte array has length not equal to 8
13771430
* @see <a href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html">Primitive Types</a>
13781431
*/
13791432
public double toDouble() {
1380-
if (length() > 8) {
1381-
throw new UnsupportedOperationException("cannot convert to float if length > 8 byte");
1382-
}
1433+
Util.checkExactLength(length(), 8, "double");
13831434
return resize(8).internalBuffer().getDouble();
13841435
}
13851436

src/main/java/at/favre/lib/bytes/Util.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,18 @@ static byte[] shiftRight(byte[] byteArray, int shiftBitCount) {
414414
return byteArray;
415415
}
416416

417+
static void checkIndexBounds(int length, int index, int primitiveLength, String type) {
418+
if (index < 0 || index + primitiveLength > length) {
419+
throw new IndexOutOfBoundsException("cannot get " + type + " from index out of bounds: " + index);
420+
}
421+
}
422+
423+
static void checkExactLength(int length, int expectedLength, String type) {
424+
if (length != expectedLength) {
425+
throw new IllegalStateException("cannot convert to " + type + " if length != " + expectedLength + " bytes");
426+
}
427+
}
428+
417429
/*
418430
=================================================================================================
419431
Copyright 2011 Twitter, Inc.

0 commit comments

Comments
 (0)