Skip to content

Commit d032c19

Browse files
committed
Add correct right shift and fix bug in bitAt
1 parent c1785aa commit d032c19

File tree

6 files changed

+75
-25
lines changed

6 files changed

+75
-25
lines changed

CHANGELOG

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
## v0.4.3
44

55
* add toFloat/toDouble
6+
* add resize mode (from 0 or length)
7+
* fix bitAt bug
8+
* add in place byte array shift feature
69

710
## v0.4.2
811

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

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,16 @@ public static Bytes from(CharSequence string, Charset charset) {
382382
return wrap(string.toString().getBytes(charset));
383383
}
384384

385+
/**
386+
* Parses a big endian binary string (e.g. <code>10010001</code>)
387+
*
388+
* @param binaryString the encoded string
389+
* @return decoded instance
390+
*/
391+
public static Bytes parseBinary(String binaryString) {
392+
return parse(binaryString, new BinaryToTextEncoding.BaseRadix(2));
393+
}
394+
385395
/**
386396
* Parsing of octal encoded byte arrays.
387397
*
@@ -955,17 +965,18 @@ public byte byteAt(int index) {
955965
}
956966

957967
/**
958-
* Returns the {@code bit} value as boolean at the specified index.
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>.
959970
*
960971
* @param bitIndex the index of the {@code bit} value.
961972
* @return true if bit at given index is set, false otherwise
962973
* @throws IndexOutOfBoundsException if the {@code bitIndex} argument is negative or not less than the length of this array in bits.
963974
*/
964975
public boolean bitAt(int bitIndex) {
965-
if (bitIndex < 0 || bitIndex > length() * 8) {
976+
if (bitIndex < 0 || bitIndex > lengthBit()) {
966977
throw new IndexOutOfBoundsException("cannot get bit from index out of bounds: " + bitIndex);
967978
}
968-
return (byteAt(bitIndex / 8) >> (bitIndex % 8) & 1) == 1;
979+
return ((byteAt(length() - 1 - (bitIndex / 8)) >>> bitIndex % 8) & 1) != 0;
969980
}
970981

971982
/**

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ static byte[] shiftLeft(byte[] data, int shiftBitCount) {
392392
*/
393393
static byte[] shiftRight(byte[] data, int shiftBitCount) {
394394
final int shiftMod = shiftBitCount % 8;
395-
final byte carryMask = (byte) (0xFF << (8 - shiftBitCount));
395+
final byte carryMask = (byte) (0xFF << (8 - shiftMod));
396396
final int offset = (shiftBitCount / 8);
397397

398398
int sourceIndex;

src/test/java/at/favre/lib/bytes/BytesMiscTest.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@
2323

2424
import org.junit.Test;
2525

26+
import java.math.BigInteger;
2627
import java.nio.ByteOrder;
2728
import java.nio.ReadOnlyBufferException;
2829
import java.util.NoSuchElementException;
30+
import java.util.Random;
2931

3032
import static org.junit.Assert.*;
3133

@@ -172,6 +174,7 @@ public void byteAt() throws Exception {
172174

173175
@Test
174176
public void bitAt() throws Exception {
177+
175178
for (int i = 0; i < 8; i++) {
176179
assertFalse(Bytes.allocate(1).bitAt(i));
177180
}
@@ -185,17 +188,30 @@ public void bitAt() throws Exception {
185188
assertFalse(Bytes.from((byte) 8).bitAt(2));
186189
assertTrue(Bytes.from((byte) 8).bitAt(3));
187190
assertFalse(Bytes.from((byte) 8).bitAt(4));
191+
assertFalse(Bytes.from((byte) 0b11010000).bitAt(0));
192+
assertFalse(Bytes.from((byte) 0b10010000).bitAt(0));
193+
assertTrue(Bytes.from((byte) 0b10010001).bitAt(0));
194+
assertFalse(Bytes.parseBinary("101111110101100100110010011111001011101110110011011000010000000").bitAt(54));
188195

189196
try {
190197
Bytes.allocate(1).bitAt(8);
191198
fail();
192-
} catch (IndexOutOfBoundsException e) {
199+
} catch (IndexOutOfBoundsException ignored) {
193200
}
194201

195202
try {
196203
Bytes.allocate(16).bitAt(-1);
197204
fail();
198-
} catch (IndexOutOfBoundsException e) {
205+
} catch (IndexOutOfBoundsException ignored) {
206+
}
207+
}
208+
209+
@Test
210+
public void bitAtAgainstRefImpl() throws Exception {
211+
for (int i = 0; i < 1000; i++) {
212+
Bytes rnd = Bytes.random(4 + new Random().nextInt(8));
213+
int index = new Random().nextInt(rnd.lengthBit() - 1);
214+
assertEquals(new BigInteger(rnd.array()).testBit(index), rnd.bitAt(index));
199215
}
200216
}
201217

src/test/java/at/favre/lib/bytes/BytesTransformTest.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -261,12 +261,12 @@ public void leftShift() throws Exception {
261261

262262
@Test
263263
public void rightShift() throws Exception {
264-
assertArrayEquals(new byte[]{4}, Bytes.from(8).rightShift(1).array());
265-
assertArrayEquals(new byte[]{2}, Bytes.from(8).rightShift(2).array());
266-
assertArrayEquals(new byte[]{1}, Bytes.from(8).rightShift(3).array());
267-
assertArrayEquals(new byte[]{0}, Bytes.from(8).rightShift(4).array());
268-
assertArrayEquals(new byte[]{example_bytes_two[0]}, Bytes.from(example_bytes_two).rightShift(8).array());
269-
assertArrayEquals(new byte[1], Bytes.from(example_bytes_two).rightShift(16).array());
264+
assertArrayEquals(new byte[]{4}, Bytes.from((byte) 8).rightShift(1).array());
265+
assertArrayEquals(new byte[]{2}, Bytes.from((byte) 8).rightShift(2).array());
266+
assertArrayEquals(new byte[]{1}, Bytes.from((byte) 8).rightShift(3).array());
267+
assertArrayEquals(new byte[]{0}, Bytes.from((byte) 8).rightShift(4).array());
268+
assertArrayEquals(new byte[]{0, example_bytes_two[0]}, Bytes.from(example_bytes_two).rightShift(8).array());
269+
assertArrayEquals(new byte[2], Bytes.from(example_bytes_two).rightShift(16).array());
270270
}
271271

272272
@Test

src/test/java/at/favre/lib/bytes/UtilTest.java

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
package at.favre.lib.bytes;
2323

24+
import org.junit.Ignore;
2425
import org.junit.Test;
2526

2627
import java.math.BigInteger;
@@ -244,11 +245,25 @@ public void testLeftShift() {
244245
assertArrayEquals(new byte[]{0, 0, 0, 0}, Util.shiftLeft(Bytes.from(test).array(), 24));
245246

246247
assertSame(test, Util.shiftLeft(test, 1));
248+
}
247249

250+
@Test
251+
@Ignore
252+
public void testLeftShiftAgainstRefImpl() {
248253
for (int i = 0; i < 1000; i++) {
249254
int shift = 1;
250-
Bytes rnd = Bytes.random(2 + new Random().nextInt(128));
251-
assertArrayEquals(Bytes.wrap(new BigInteger(rnd.array()).shiftLeft(shift).toByteArray()).resize(rnd.length()).array(), Util.shiftLeft(rnd.copy().array(), shift));
255+
Bytes rnd = Bytes.random(4 + new Random().nextInt(14));
256+
257+
258+
byte[] expected = Bytes.from(new BigInteger(rnd.array()).shiftLeft(shift).toByteArray()).resize(rnd.length(), BytesTransformer.ResizeTransformer.Mode.RESIZE_KEEP_FROM_MAX_LENGTH).array();
259+
byte[] actual = Bytes.from(Util.shiftLeft(rnd.copy().array(), shift)).resize(rnd.length(), BytesTransformer.ResizeTransformer.Mode.RESIZE_KEEP_FROM_MAX_LENGTH).array();
260+
261+
System.out.println("Original \t" + rnd.encodeBinary() + " << " + shift);
262+
System.out.println("Expected \t" + Bytes.wrap(expected).encodeBinary());
263+
System.out.println("Actual \t" + Bytes.wrap(actual).encodeBinary() + "\n\n");
264+
265+
assertArrayEquals(expected, actual);
266+
assertEquals(Bytes.wrap(expected).encodeHex(), Bytes.wrap(actual).encodeHex());
252267
}
253268
}
254269

@@ -268,19 +283,24 @@ public void testRightShift() {
268283
assertArrayEquals(new byte[]{0, 0, 1, 0}, Util.shiftRight(Bytes.from(test).array(), 4));
269284

270285
assertSame(test, Util.shiftRight(test, 1));
286+
}
271287

272-
288+
@Test
289+
public void testRightShiftAgainstRefImpl() {
273290
for (int i = 0; i < 1000; i++) {
274-
int shift = 1;
275-
Bytes rnd = Bytes.random(8);
276-
byte[] expected = new BigInteger(rnd.array()).shiftRight(shift).toByteArray();
277-
byte[] actual = Util.shiftRight(rnd.copy().array(), shift);
278-
279-
System.out.println("Original \t" + rnd.encodeBinary());
280-
System.out.println("Expected \t" + Bytes.wrap(expected).encodeBinary());
281-
System.out.println("Actual \t" + Bytes.wrap(actual).encodeBinary() + "\n\n");
282-
283-
assertArrayEquals(expected, actual);
291+
int shift = new Random().nextInt(64);
292+
Bytes rnd = Bytes.random(4 + new Random().nextInt(12));
293+
if (!rnd.bitAt(rnd.lengthBit() - 1)) { //only unsigned
294+
byte[] expected = Bytes.from(new BigInteger(rnd.array()).shiftRight(shift).toByteArray()).resize(rnd.length(), BytesTransformer.ResizeTransformer.Mode.RESIZE_KEEP_FROM_MAX_LENGTH).array();
295+
byte[] actual = Bytes.from(Util.shiftRight(rnd.copy().array(), shift)).resize(rnd.length(), BytesTransformer.ResizeTransformer.Mode.RESIZE_KEEP_FROM_MAX_LENGTH).array();
296+
297+
// System.out.println("Original \t" + rnd.encodeBinary() + " >> " + shift);
298+
// System.out.println("Expected \t" + Bytes.wrap(expected).encodeBinary());
299+
// System.out.println("Actual \t" + Bytes.wrap(actual).encodeBinary() + "\n\n");
300+
301+
assertArrayEquals(expected, actual);
302+
assertEquals(Bytes.wrap(expected).encodeHex(), Bytes.wrap(actual).encodeHex());
303+
}
284304
}
285305
}
286306
}

0 commit comments

Comments
 (0)