Skip to content

Commit 4e3ad4e

Browse files
committed
Add inplace left and right shift with bug in right shift
1 parent d14f4fd commit 4e3ad4e

File tree

3 files changed

+111
-4
lines changed

3 files changed

+111
-4
lines changed

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

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

2222
package at.favre.lib.bytes;
2323

24-
import java.math.BigInteger;
2524
import java.security.MessageDigest;
2625
import java.security.NoSuchAlgorithmException;
2726
import java.util.Objects;
@@ -144,14 +143,14 @@ public enum Type {
144143

145144
@Override
146145
public byte[] transform(byte[] currentArray, boolean inPlace) {
147-
BigInteger bigInt = new BigInteger(currentArray);
146+
byte[] out = inPlace ? currentArray : Bytes.from(currentArray).array();
148147

149148
switch (type) {
150149
default:
151150
case LEFT_SHIFT:
152-
return bigInt.shiftLeft(shiftCount).toByteArray();
151+
return Util.shiftLeft(out, shiftCount);
153152
case RIGHT_SHIFT:
154-
return bigInt.shiftRight(shiftCount).toByteArray();
153+
return Util.shiftRight(out, shiftCount);
155154
}
156155
}
157156

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

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,56 @@ static String toString(Bytes bytes) {
362362
return bytes.length() + " bytes " + preview;
363363
}
364364

365+
/**
366+
* Shifts input byte array shiftBitCount bits left. This method will alter the input byte array.
367+
*/
368+
static byte[] shiftLeft(byte[] data, int shiftBitCount) {
369+
final int shiftMod = shiftBitCount % 8; //6 % 8 = 2
370+
final byte carryMask = (byte) ((1 << shiftMod) - 1); // 0000 0010 << 2 = 0000 1000 - 0000 0001 = 0000 0111
371+
final int offsetBytes = (shiftBitCount / 8); // = 0
372+
373+
int sourceIndex;
374+
for (int i = 0; i < data.length; i++) {
375+
sourceIndex = i + offsetBytes;
376+
if (sourceIndex >= data.length) {
377+
data[i] = 0;
378+
} else {
379+
byte src = data[sourceIndex];
380+
byte dst = (byte) (src << shiftMod);
381+
if (sourceIndex + 1 < data.length) {
382+
dst |= data[sourceIndex + 1] >>> (8 - shiftMod) & carryMask;
383+
}
384+
data[i] = dst;
385+
}
386+
}
387+
return data;
388+
}
389+
390+
/**
391+
* Shifts input byte array shiftBitCount bits right. This method will alter the input byte array.
392+
*/
393+
static byte[] shiftRight(byte[] data, int shiftBitCount) {
394+
final int shiftMod = shiftBitCount % 8;
395+
final byte carryMask = (byte) (0xFF << (8 - shiftBitCount));
396+
final int offset = (shiftBitCount / 8);
397+
398+
int sourceIndex;
399+
for (int i = data.length - 1; i >= 0; i--) {
400+
sourceIndex = i - offset;
401+
if (sourceIndex < 0) {
402+
data[i] = 0;
403+
} else {
404+
byte src = data[sourceIndex];
405+
byte dst = (byte) (src >> shiftMod);
406+
if (sourceIndex - 1 >= 0) {
407+
dst |= data[sourceIndex - 1] << (8 - shiftMod) & carryMask;
408+
}
409+
data[i] = dst;
410+
}
411+
}
412+
return data;
413+
}
414+
365415
/*
366416
=================================================================================================
367417
Copyright 2011 Twitter, Inc.

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

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@
2323

2424
import org.junit.Test;
2525

26+
import java.math.BigInteger;
2627
import java.util.Arrays;
2728
import java.util.List;
29+
import java.util.Random;
2830

2931
import static junit.framework.TestCase.assertTrue;
3032
import static org.junit.Assert.*;
@@ -223,4 +225,60 @@ private static void testReverse(byte[] input, int fromIndex, int toIndex, byte[]
223225
Util.reverse(input, fromIndex, toIndex);
224226
assertTrue(Arrays.equals(expectedOutput, input));
225227
}
228+
229+
@Test
230+
public void testLeftShift() {
231+
byte[] test = new byte[]{0, 0, 1, 0};
232+
assertArrayEquals(new byte[]{0, 1, 0, 0}, Util.shiftLeft(new byte[]{0, 0, -128, 0}, 1));
233+
assertArrayEquals(new byte[]{0, 1, 0, 0}, Util.shiftLeft(new byte[]{0, 0, 64, 0}, 2));
234+
assertArrayEquals(new byte[]{1, 1, 1, 0}, Util.shiftLeft(new byte[]{-128, -128, -128, -128}, 1));
235+
assertArrayEquals(new byte[]{0, 0, 2, 0}, Util.shiftLeft(Bytes.from(test).array(), 1));
236+
assertArrayEquals(new byte[]{0, 0, 4, 0}, Util.shiftLeft(Bytes.from(test).array(), 2));
237+
assertArrayEquals(new byte[]{0, 0, 8, 0}, Util.shiftLeft(Bytes.from(test).array(), 3));
238+
assertArrayEquals(new byte[]{0, 1, 0, 0}, Util.shiftLeft(Bytes.from(test).array(), 8));
239+
assertArrayEquals(new byte[]{0, 2, 0, 0}, Util.shiftLeft(Bytes.from(test).array(), 9));
240+
assertArrayEquals(new byte[]{1, 0, 0, 0}, Util.shiftLeft(Bytes.from(test).array(), 16));
241+
assertArrayEquals(new byte[]{2, 0, 0, 0}, Util.shiftLeft(Bytes.from(test).array(), 17));
242+
assertArrayEquals(new byte[]{-128, 0, 0, 0}, Util.shiftLeft(Bytes.from(test).array(), 23));
243+
assertArrayEquals(new byte[]{0, 0, 0, 0}, Util.shiftLeft(Bytes.from(test).array(), 24));
244+
assertArrayEquals(new byte[]{0, 0, 0, 0}, Util.shiftLeft(Bytes.from(test).array(), 24));
245+
246+
assertSame(test, Util.shiftLeft(test, 1));
247+
248+
for (int i = 0; i < 1000; i++) {
249+
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));
252+
}
253+
}
254+
255+
@Test
256+
public void testRightShift() {
257+
byte[] test = new byte[]{0, 0, 16, 0};
258+
assertArrayEquals(new byte[]{0, -128, -128, -128}, Util.shiftRight(new byte[]{1, 1, 1, 1}, 1));
259+
assertArrayEquals(new byte[]{0, -128, 66, 0}, Util.shiftRight(new byte[]{2, 1, 8, 2}, 2));
260+
assertArrayEquals(new byte[]{0, -128, 66, 0}, new BigInteger(new byte[]{2, 1, 8, 2}).shiftRight(2).toByteArray());
261+
assertArrayEquals(new byte[]{0, 0, 0, -128}, Util.shiftRight(Bytes.from(test).array(), 5));
262+
assertArrayEquals(new byte[]{0, 0, 0, -128}, Util.shiftRight(new byte[]{0, 0, 1, 0}, 1));
263+
assertArrayEquals(new byte[]{0, 0, 8, 0}, Util.shiftRight(Bytes.from(test).array(), 1));
264+
assertArrayEquals(new byte[]{0, 0, 4, 0}, Util.shiftRight(Bytes.from(test).array(), 2));
265+
assertArrayEquals(new byte[]{0, 0, 2, 0}, Util.shiftRight(Bytes.from(test).array(), 3));
266+
assertArrayEquals(new byte[]{0, 0, 1, 0}, Util.shiftRight(Bytes.from(test).array(), 4));
267+
268+
assertSame(test, Util.shiftRight(test, 1));
269+
270+
271+
for (int i = 0; i < 1000; i++) {
272+
int shift = 1;
273+
Bytes rnd = Bytes.random(8);
274+
byte[] expected = new BigInteger(rnd.array()).shiftRight(shift).toByteArray();
275+
byte[] actual = Util.shiftRight(rnd.copy().array(), shift);
276+
277+
System.out.println("Original \t" + rnd.encodeBinary());
278+
System.out.println("Expected \t " + Bytes.wrap(expected).encodeBinary());
279+
System.out.println("Actual \t " + Bytes.wrap(actual).encodeBinary() + "\n\n");
280+
281+
assertArrayEquals(expected, actual);
282+
}
283+
}
226284
}

0 commit comments

Comments
 (0)