Skip to content
This repository was archived by the owner on Jun 7, 2023. It is now read-only.

Commit f43d606

Browse files
committed
speed optimization (java 7 compatible) thx @th0br0
1 parent a9168ef commit f43d606

File tree

2 files changed

+224
-39
lines changed

2 files changed

+224
-39
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<modelVersion>4.0.0</modelVersion>
55
<groupId>org.iota</groupId>
66
<artifactId>jota</artifactId>
7-
<version>0.9.2-SNAPSHOT</version>
7+
<version>0.9.3-SNAPSHOT</version>
88
<name>JOTA</name>
99
<description>JOTA library is a simple Java wrapper around IOTA Node's JSON-REST HTTP interface.</description>
1010

src/main/java/jota/pow/Kerl.java

Lines changed: 223 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
package jota.pow;
22

3+
import jota.utils.Pair;
34
import org.bouncycastle.jcajce.provider.digest.Keccak;
45

5-
import java.math.BigInteger;
6-
import java.util.Arrays;
6+
import java.util.LinkedHashSet;
7+
import java.util.Set;
78

89
public class Kerl extends JCurl {
910

@@ -17,75 +18,259 @@ public class Kerl extends JCurl {
1718
private byte[] byte_state;
1819
private int[] trit_state;
1920

20-
public Kerl() {
21+
private static final int[] HALF_3 = new int[]{
22+
0xa5ce8964, 0x9f007669, 0x1484504f, 0x3ade00d9, 0x0c24486e, 0x50979d57, 0x79a4c702, 0x48bbae36, 0xa9f6808b, 0xaa06a805, 0xa87fabdf, 0x5e69ebef};
23+
private static int BYTE_LENGTH = 48;
24+
private static int INT_LENGTH = BYTE_LENGTH / 4;
25+
26+
Kerl() {
2127
super();
2228
this.keccak = new Keccak.Digest384();
2329
this.byte_state = new byte[BYTE_HASH_LENGTH];
2430
this.trit_state = new int[HASH_LENGTH];
2531
}
2632

27-
public static BigInteger convertTritsToBigint(final int[] trits, final int offset, final int size) {
33+
private static final long toUnsignedLong(int i) {
34+
return i & 0xFFFFFFFFL;
35+
}
2836

29-
BigInteger value = BigInteger.ZERO;
37+
private static int toUnsignedInt(byte x) {
38+
return x & 0xff;
39+
}
3040

31-
for (int i = size; i-- > 0; ) {
41+
private static int sum(int[] toSum) {
42+
int sum = 0;
43+
for (int i = 0; i < toSum.length; i++) {
44+
sum += toSum[i];
45+
}
46+
return sum;
47+
}
3248

33-
value = value.multiply(BigInteger.valueOf(RADIX)).add(BigInteger.valueOf(trits[offset + i]));
49+
public static byte[] convertTritsToBytes(final int[] trits) {
50+
if (trits.length != Kerl.HASH_LENGTH) {
51+
throw new RuntimeException("Input trits length must be " + Kerl.HASH_LENGTH + "in length");
3452
}
53+
int[] base = new int[INT_LENGTH];
3554

36-
return value;
37-
}
55+
Set<Integer> setUniqueNumbers = new LinkedHashSet<Integer>();
56+
for (int x : trits) {
57+
setUniqueNumbers.add(x);
58+
}
59+
if (setUniqueNumbers.size() == 1 && setUniqueNumbers.contains(-1)) {
60+
base = HALF_3.clone();
61+
bigint_not(base);
62+
bigint_add(base, 1);
63+
} else {
64+
int size = INT_LENGTH;
65+
for (int i = Kerl.HASH_LENGTH - 1; i-- > 0; ) {
66+
{ // Multiply by radix
67+
int sz = size;
68+
int carry = 0;
69+
70+
for (int j = 0; j < sz; j++) {
71+
// full_mul
72+
long v = Kerl.toUnsignedLong(base[j]) * (Kerl.toUnsignedLong(RADIX)) + Kerl.toUnsignedLong(carry);
73+
carry = (int) ((v >> Integer.SIZE) & 0xFFFFFFFF);
74+
base[j] = (int) (v & 0xFFFFFFFF);
75+
}
76+
77+
if (carry > 0) {
78+
base[sz] = carry;
79+
size += 1;
80+
}
81+
}
82+
final int in = trits[i] + 1;
83+
{ // Add
84+
int sz = bigint_add(base, in);
85+
if (sz > size) {
86+
size = sz;
87+
}
88+
}
89+
}
3890

39-
private static BigInteger convertBytesToBigInt(final byte[] bytes, final int offset, final int size) {
91+
if (sum(base) != 0) {
92+
if (bigint_cmp(HALF_3, base) <= 0) {
93+
// base is >= HALF_3.
94+
// just do base - HALF_3
95+
base = bigint_sub(base, HALF_3);
96+
} else {
97+
// we don't have a wrapping sub.
98+
// so we need to be clever.
99+
base = bigint_sub(HALF_3, base);
100+
bigint_not(base);
101+
bigint_add(base, 1);
102+
}
103+
}
104+
105+
}
40106

41-
return new BigInteger(Arrays.copyOfRange(bytes, offset, offset + size));
107+
byte[] out = new byte[BYTE_LENGTH];
108+
109+
for (int i = 0; i < INT_LENGTH; i++) {
110+
out[i * 4 + 0] = (byte) ((base[INT_LENGTH - 1 - i] & 0xFF000000) >> 24);
111+
out[i * 4 + 1] = (byte) ((base[INT_LENGTH - 1 - i] & 0x00FF0000) >> 16);
112+
out[i * 4 + 2] = (byte) ((base[INT_LENGTH - 1 - i] & 0x0000FF00) >> 8);
113+
out[i * 4 + 3] = (byte) ((base[INT_LENGTH - 1 - i] & 0x000000FF) >> 0);
114+
}
115+
return out;
42116
}
43117

44-
private static int[] convertBigintToTrits(final BigInteger value, int size) {
118+
public static int[] convertBytesToTrits(byte[] bytes) {
119+
int[] base = new int[INT_LENGTH];
120+
int[] out = new int[243];
121+
out[Kerl.HASH_LENGTH - 1] = 0;
45122

46-
int[] destination = new int[size];
47-
BigInteger absoluteValue = value.compareTo(BigInteger.ZERO) < 0 ? value.negate() : value;
48-
for (int i = 0; i < size; i++) {
123+
if (bytes.length != BYTE_LENGTH) {
124+
throw new RuntimeException("Input base must be " + BYTE_LENGTH + " in length");
125+
}
49126

50-
BigInteger[] divRemainder = absoluteValue.divideAndRemainder(BigInteger.valueOf(RADIX));
51-
int remainder = divRemainder[1].intValue();
52-
absoluteValue = divRemainder[0];
127+
for (int i = 0; i < INT_LENGTH; i++) {
128+
base[INT_LENGTH - 1 - i] = Kerl.toUnsignedInt(bytes[i * 4]) << 24;
129+
base[INT_LENGTH - 1 - i] |= Kerl.toUnsignedInt(bytes[i * 4 + 1]) << 16;
130+
base[INT_LENGTH - 1 - i] |= Kerl.toUnsignedInt(bytes[i * 4 + 2]) << 8;
131+
base[INT_LENGTH - 1 - i] |= Kerl.toUnsignedInt(bytes[i * 4 + 3]);
132+
}
53133

54-
if (remainder > MAX_TRIT_VALUE) {
134+
if (bigint_cmp(base, HALF_3) == 0) {
135+
int val = 0;
136+
if (base[0] > 0) {
137+
val = -1;
138+
} else if (base[0] < 0) {
139+
val = 1;
140+
}
141+
for (int i = 0; i < Kerl.HASH_LENGTH - 1; i++) {
142+
out[i] = val;
143+
}
55144

56-
remainder = MIN_TRIT_VALUE;
57-
absoluteValue = absoluteValue.add(BigInteger.ONE);
145+
} else {
146+
boolean flipTrits = false;
147+
// See if we have a positive or negative two's complement number.
148+
if (Kerl.toUnsignedLong(base[INT_LENGTH - 1]) >> 31 != 0) {
149+
// negative value.
150+
bigint_not(base);
151+
if (bigint_cmp(base, HALF_3) > 0) {
152+
base = bigint_sub(base, HALF_3);
153+
flipTrits = true;
154+
} else {
155+
bigint_add(base, 1);
156+
base = bigint_sub(HALF_3, base);
157+
}
158+
} else {
159+
// positive. we need to shift right by HALF_3
160+
base = bigint_add(HALF_3, base);
58161
}
59-
destination[i] = remainder;
60-
}
61162

62-
if (value.compareTo(BigInteger.ZERO) < 0) {
163+
int size = INT_LENGTH;
164+
165+
int remainder = 0;
166+
for (int i = 0; i < Kerl.HASH_LENGTH - 1; i++) {
167+
{ //div_rem
168+
remainder = 0;
63169

64-
for (int i = 0; i < size; i++) {
170+
for (int j = size - 1; j >= 0; j--) {
171+
long lhs = (Kerl.toUnsignedLong(remainder) << 32) | Kerl.toUnsignedLong(base[j]);
172+
long rhs = Kerl.toUnsignedLong(RADIX);
173+
174+
int q = (int) (lhs / rhs);
175+
int r = (int) (lhs % rhs);
176+
base[j] = q;
177+
remainder = r;
178+
}
179+
}
180+
out[i] = remainder - 1;
181+
}
65182

66-
destination[i] = -destination[i];
183+
if (flipTrits) {
184+
for (int i = 0; i < out.length; i++) {
185+
out[i] = -out[i];
186+
}
67187
}
68188
}
69189

70-
return destination;
190+
return out;
71191
}
72192

73-
private static byte[] convertBigintToBytes(final BigInteger value, int size) {
193+
private static void bigint_not(int[] base) {
194+
for (int i = 0; i < base.length; i++) {
195+
base[i] = ~base[i];
196+
}
197+
}
198+
199+
private static int bigint_add(int[] base, final int rh) {
200+
Pair<Integer, Boolean> res = full_add(base[0], rh, false);
201+
base[0] = res.low;
74202

75-
final byte[] result = new byte[BYTE_HASH_LENGTH];
203+
int j = 1;
204+
while (res.hi) {
205+
res = full_add(base[j], 0, true);
206+
base[j] = res.low;
207+
j += 1;
208+
}
76209

77-
final byte[] bytes = value.toByteArray();
78-
int i = 0;
79-
while (i + bytes.length < BYTE_HASH_LENGTH) {
210+
return j;
211+
}
80212

81-
result[i++] = (byte) (bytes[0] < 0 ? -1 : 0);
213+
private static int[] bigint_add(final int[] lh, final int[] rh) {
214+
int[] out = new int[INT_LENGTH];
215+
boolean carry = false;
216+
Pair<Integer, Boolean> ret;
217+
for (int i = 0; i < INT_LENGTH; i++) {
218+
ret = full_add(lh[i], rh[i], carry);
219+
out[i] = ret.low;
220+
carry = ret.hi;
82221
}
83-
for (int j = bytes.length; j-- > 0; ) {
84222

85-
result[i++] = bytes[bytes.length - 1 - j];
223+
if (carry) {
224+
throw new RuntimeException("Exceeded max value.");
225+
}
226+
227+
return out;
228+
}
229+
230+
private static int bigint_cmp(final int[] lh, final int[] rh) {
231+
for (int i = INT_LENGTH - 1; i >= 0; i--) {
232+
int ret = Long.compare(Kerl.toUnsignedLong(lh[i]), Kerl.toUnsignedLong(rh[i]));
233+
if (ret != 0) {
234+
return ret;
235+
}
236+
}
237+
return 0;
238+
}
239+
240+
private static int[] bigint_sub(final int[] lh, final int[] rh) {
241+
int[] out = new int[INT_LENGTH];
242+
boolean noborrow = true;
243+
Pair<Integer, Boolean> ret;
244+
for (int i = 0; i < INT_LENGTH; i++) {
245+
ret = full_add(lh[i], ~rh[i], noborrow);
246+
out[i] = ret.low;
247+
noborrow = ret.hi;
248+
}
249+
250+
if (!noborrow) {
251+
throw new RuntimeException("noborrow");
252+
}
253+
254+
return out;
255+
}
256+
257+
private static Pair<Integer, Boolean> full_add(final int ia, final int ib, final boolean carry) {
258+
long a = Kerl.toUnsignedLong(ia);
259+
long b = Kerl.toUnsignedLong(ib);
260+
261+
long v = a + b;
262+
long l = v >> 32;
263+
long r = v & 0xFFFFFFFF;
264+
boolean carry1 = l != 0;
265+
266+
if (carry) {
267+
v = r + 1;
86268
}
269+
l = (v >> 32) & 0xFFFFFFFF;
270+
r = v & 0xFFFFFFFF;
271+
boolean carry2 = l != 0;
87272

88-
return result;
273+
return new Pair<>((int) r, carry1 || carry2);
89274
}
90275

91276
@Override
@@ -108,7 +293,7 @@ public Kerl absorb(final int[] trits, int offset, int length) {
108293

109294
//convert to bits
110295
trit_state[HASH_LENGTH - 1] = 0;
111-
byte[] bytes = convertBigintToBytes(convertTritsToBigint(trit_state, 0, HASH_LENGTH), BYTE_HASH_LENGTH);
296+
byte[] bytes = convertTritsToBytes(trit_state);
112297

113298
//run keccak
114299
keccak.update(bytes);
@@ -128,7 +313,7 @@ public int[] squeeze(final int[] trits, int offset, int length) {
128313

129314
byte_state = this.keccak.digest();
130315
//convert to trits
131-
trit_state = convertBigintToTrits(convertBytesToBigInt(byte_state, 0, BYTE_HASH_LENGTH), HASH_LENGTH);
316+
trit_state = convertBytesToTrits(byte_state);
132317

133318
//copy with offset
134319
trit_state[HASH_LENGTH - 1] = 0;

0 commit comments

Comments
 (0)