Skip to content

Commit 1e7fc18

Browse files
committed
msgpack-lite: fix BigInteger pack / unpack
Fixes #27.
1 parent 8741aba commit 1e7fc18

File tree

3 files changed

+80
-7
lines changed

3 files changed

+80
-7
lines changed

src/main/java/org/tarantool/MsgPackLite.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ public class MsgPackLite {
3030
protected final int MAX_31BIT = 0x7fffffff;
3131
protected final long MAX_32BIT = 0xffffffffL;
3232

33+
protected final BigInteger BI_MIN_LONG = BigInteger.valueOf(Long.MIN_VALUE);
34+
protected final BigInteger BI_MAX_LONG = BigInteger.valueOf(Long.MAX_VALUE);
35+
protected final BigInteger BI_MAX_64BIT = BigInteger.valueOf(2).pow(64).subtract(BigInteger.ONE);
36+
3337
//these values are from http://wiki.msgpack.org/display/MSGPACK/Format+specification
3438
protected final byte MP_NULL = (byte) 0xc0;
3539
protected final byte MP_FALSE = (byte) 0xc2;
@@ -91,6 +95,23 @@ public void pack(Object item, OutputStream os) throws IOException {
9195
out.write(MP_DOUBLE);
9296
out.writeDouble((Double) item);
9397
} else {
98+
if (item instanceof BigInteger) {
99+
BigInteger value = (BigInteger) item;
100+
boolean isPositive = value.signum() >= 0;
101+
if (isPositive && value.compareTo(BI_MAX_64BIT) > 0 ||
102+
value.compareTo(BI_MIN_LONG) < 0)
103+
throw new IllegalArgumentException(
104+
"Cannot encode BigInteger as MsgPack: out of -2^63..2^64-1 range");
105+
if (isPositive && value.compareTo(BI_MAX_LONG) > 0) {
106+
byte[] data = value.toByteArray();
107+
// data can contain leading zero bytes
108+
for (int i = 0; i < data.length - 8; ++i)
109+
assert data[i] == 0;
110+
out.write(MP_UINT64);
111+
out.write(data, data.length - 8, 8);
112+
return;
113+
}
114+
}
94115
long value = item instanceof Code ? ((Code) item).getId() : ((Number) item).longValue();
95116
if (value >= 0) {
96117
if (value <= MAX_7BIT) {
@@ -238,6 +259,10 @@ public Object unpack(InputStream is) throws IOException {
238259
} else {
239260
//this is a little bit more tricky, since we don't have unsigned longs
240261
byte[] bytes = new byte[]{
262+
(byte) ((v >> 56) & 0xff),
263+
(byte) ((v >> 48) & 0xff),
264+
(byte) ((v >> 40) & 0xff),
265+
(byte) ((v >> 32) & 0xff),
241266
(byte) ((v >> 24) & 0xff),
242267
(byte) ((v >> 16) & 0xff),
243268
(byte) ((v >> 8) & 0xff),

src/test/java/org/tarantool/AbstractTarantoolConnectorIT.java

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import org.junit.jupiter.api.BeforeAll;
55
import org.opentest4j.AssertionFailedError;
66

7+
import java.math.BigInteger;
78
import java.io.IOException;
89
import java.net.InetSocketAddress;
910
import java.net.Socket;
@@ -180,27 +181,49 @@ protected TarantoolConnection openConnection() {
180181
}
181182
}
182183

183-
protected List<?> consoleSelect(String spaceName, Object key) {
184-
StringBuilder sb = new StringBuilder("box.space.");
185-
sb.append(spaceName);
186-
sb.append(":select{");
184+
private void appendBigInteger(StringBuilder sb, BigInteger value) {
185+
sb.append(value);
186+
sb.append(value.signum() >= 0 ? "ULL" : "LL");
187+
}
188+
189+
private void appendKey(StringBuilder sb, Object key) {
187190
if (List.class.isAssignableFrom(key.getClass())) {
188-
List parts = (List)key;
191+
List parts = (List) key;
189192
for (int i = 0; i < parts.size(); i++) {
190193
if (i != 0)
191194
sb.append(", ");
192195
Object k = parts.get(i);
193-
if (k.getClass().isAssignableFrom(String.class)) {
196+
if (k instanceof BigInteger) {
197+
appendBigInteger(sb, (BigInteger) k);
198+
} else if (k instanceof String) {
194199
sb.append('\'');
195200
sb.append(k);
196201
sb.append('\'');
197202
} else {
198203
sb.append(k);
199204
}
200205
}
206+
} else if (key instanceof BigInteger) {
207+
appendBigInteger(sb, (BigInteger) key);
201208
} else {
202209
sb.append(key);
203210
}
211+
}
212+
213+
protected List<?> consoleSelect(String spaceName, Object key) {
214+
StringBuilder sb = new StringBuilder("box.space.");
215+
sb.append(spaceName);
216+
sb.append(":select{");
217+
appendKey(sb, key);
218+
sb.append("}");
219+
return console.eval(sb.toString());
220+
}
221+
222+
protected List<?> consoleDelete(String spaceName, Object key) {
223+
StringBuilder sb = new StringBuilder("box.space.");
224+
sb.append(spaceName);
225+
sb.append(":delete{");
226+
appendKey(sb, key);
204227
sb.append("}");
205228
return console.eval(sb.toString());
206229
}

src/test/java/org/tarantool/AbstractTarantoolOpsIT.java

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import org.junit.jupiter.api.Test;
44
import org.junit.jupiter.api.function.Executable;
55

6+
import java.math.BigInteger;
67
import java.util.Arrays;
78
import java.util.Collections;
89
import java.util.List;
@@ -61,6 +62,25 @@ public void testInsertSimple() {
6162

6263
// Check it actually was inserted.
6364
checkTupleResult(consoleSelect(SPACE_NAME, 100), tup);
65+
66+
// Leave the database in a clean state.
67+
consoleDelete(SPACE_NAME, 100);
68+
}
69+
70+
@Test
71+
public void testInsertBigInteger() {
72+
BigInteger id = BigInteger.valueOf(2).pow(64).subtract(BigInteger.ONE);
73+
74+
List tup = Arrays.asList(id, "big");
75+
List<?> res = getOps().insert(SPACE_ID, tup);
76+
77+
checkTupleResult(res, tup);
78+
79+
// Check it actually was inserted.
80+
checkTupleResult(consoleSelect(SPACE_NAME, id), tup);
81+
82+
// Leave the database in a clean state.
83+
consoleDelete(SPACE_NAME, id);
6484
}
6585

6686
@Test
@@ -70,8 +90,13 @@ public void testInsertMultiPart() {
7090

7191
checkTupleResult(res, tup);
7292

93+
List<?> id = Arrays.asList(100, "hundred");
94+
7395
// Check it actually was inserted.
74-
checkTupleResult(consoleSelect(MULTIPART_SPACE_NAME, Arrays.asList(100, "hundred")), tup);
96+
checkTupleResult(consoleSelect(MULTIPART_SPACE_NAME, id), tup);
97+
98+
// Leave the database in a clean state.
99+
consoleDelete(MULTIPART_SPACE_NAME, id);
75100
}
76101

77102
@Test

0 commit comments

Comments
 (0)