Skip to content

Commit 5e91dce

Browse files
committed
Merge pull request #14
2 parents 807fed9 + 2026a85 commit 5e91dce

File tree

10 files changed

+8212
-652
lines changed

10 files changed

+8212
-652
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44
/target
55
/.idea
66
/*.iml
7+
*.jfr

pom.xml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
<logback-classic.version>1.1.3</logback-classic.version>
2828
<hamcrest-all.version>1.3</hamcrest-all.version>
2929
<junit.version>4.12</junit.version>
30+
<jmh.version>1.21</jmh.version>
31+
<spf4j-jmh.version>8.6.19</spf4j-jmh.version>
3032
</properties>
3133

3234
<developers>
@@ -214,6 +216,21 @@
214216
<artifactId>hamcrest-all</artifactId>
215217
<scope>test</scope>
216218
</dependency>
219+
<dependency>
220+
<groupId>org.openjdk.jmh</groupId>
221+
<artifactId>jmh-core</artifactId>
222+
<scope>test</scope>
223+
</dependency>
224+
<dependency>
225+
<groupId>org.openjdk.jmh</groupId>
226+
<artifactId>jmh-generator-annprocess</artifactId>
227+
<scope>test</scope>
228+
</dependency>
229+
<dependency>
230+
<groupId>org.spf4j</groupId>
231+
<artifactId>spf4j-jmh</artifactId>
232+
<scope>test</scope>
233+
</dependency>
217234
</dependencies>
218235

219236
<dependencyManagement>
@@ -243,6 +260,24 @@
243260
<artifactId>hamcrest-all</artifactId>
244261
<version>${hamcrest-all.version}</version>
245262
</dependency>
263+
<dependency>
264+
<groupId>org.openjdk.jmh</groupId>
265+
<artifactId>jmh-core</artifactId>
266+
<version>${jmh.version}</version>
267+
<scope>test</scope>
268+
</dependency>
269+
<dependency>
270+
<groupId>org.openjdk.jmh</groupId>
271+
<artifactId>jmh-generator-annprocess</artifactId>
272+
<version>${jmh.version}</version>
273+
<scope>test</scope>
274+
</dependency>
275+
<dependency>
276+
<groupId>org.spf4j</groupId>
277+
<artifactId>spf4j-jmh</artifactId>
278+
<version>${spf4j-jmh.version}</version>
279+
<scope>test</scope>
280+
</dependency>
246281
</dependencies>
247282
</dependencyManagement>
248283

src/main/java/com/arangodb/velocypack/VPackBuilder.java

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.io.UnsupportedEncodingException;
2424
import java.math.BigDecimal;
2525
import java.math.BigInteger;
26+
import java.nio.charset.StandardCharsets;
2627
import java.sql.Timestamp;
2728
import java.util.ArrayList;
2829
import java.util.Arrays;
@@ -46,6 +47,7 @@
4647
import com.arangodb.velocypack.internal.DefaultVPackBuilderOptions;
4748
import com.arangodb.velocypack.internal.Value;
4849
import com.arangodb.velocypack.internal.util.NumberUtil;
50+
import com.fasterxml.jackson.core.io.CharTypes;
4951

5052
/**
5153
* @author Mark Vollmary
@@ -216,7 +218,7 @@ public void append(final VPackBuilder builder, final VPackSlice value) throws VP
216218
private int size;
217219
private final List<Integer> stack; // Start positions of open
218220
// objects/arrays
219-
private final Map<Integer, List<Integer>> index; // Indices for starts
221+
private List<Integer>[] index; // Indices for starts
220222
// of
221223
// subindex
222224
private boolean keyWritten; // indicates that in the current object the key
@@ -233,7 +235,7 @@ public VPackBuilder(final BuilderOptions options) {
233235
size = 0;
234236
buffer = new byte[10];
235237
stack = new ArrayList<Integer>();
236-
index = new HashMap<Integer, List<Integer>>();
238+
index = new List[4];
237239
}
238240

239241
public BuilderOptions getOptions() {
@@ -546,7 +548,7 @@ private void set(final Value item) throws VPackBuilderException {
546548
throw new VPackBuilderUnexpectedValueException(ValueType.UINT, Long.class, Integer.class,
547549
BigInteger.class);
548550
}
549-
if (-1 == vUInt.compareTo(BigInteger.ZERO)) {
551+
if (vUInt.compareTo(BigInteger.ZERO) < 0) {
550552
throw new VPackBuilderUnexpectedValueException(ValueType.UINT, "non-negative", Long.class,
551553
Integer.class, BigInteger.class);
552554
}
@@ -621,21 +623,17 @@ private void appendSQLTimestamp(final Timestamp value) {
621623
}
622624

623625
private void appendString(final String value) throws VPackBuilderException {
624-
try {
625-
final byte[] bytes = value.getBytes("UTF-8");
626-
final int length = bytes.length;
627-
if (length <= 126) {
628-
// short string
629-
add((byte) (0x40 + length));
630-
} else {
631-
// long string
632-
add((byte) 0xbf);
633-
appendLength(length);
634-
}
635-
appendString(bytes);
636-
} catch (final UnsupportedEncodingException e) {
637-
throw new VPackBuilderException(e);
626+
final byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
627+
final int length = bytes.length;
628+
if (length <= 126) {
629+
// short string
630+
add((byte) (0x40 + length));
631+
} else {
632+
// long string
633+
add((byte) 0xbf);
634+
appendLength(length);
638635
}
636+
appendString(bytes);
639637
}
640638

641639
private void appendString(final byte[] bytes) {
@@ -670,7 +668,11 @@ private void addObject(final boolean unindexed) {
670668
private void addCompoundValue(final byte head) {
671669
// an Array or Object is started:
672670
stack.add(size);
673-
index.put(stack.size() - 1, new ArrayList<Integer>());
671+
if (index.length < stack.size()) {
672+
// ensureCapacity
673+
index = Arrays.copyOf(index, index.length * 2);
674+
}
675+
index[stack.size() - 1] = new ArrayList<Integer>();
674676
add(head);
675677
// Will be filled later with bytelength and nr subs
676678
size += 8;
@@ -682,12 +684,12 @@ private void appendLength(final long length) {
682684
}
683685

684686
private void reportAdd() {
685-
final Collection<Integer> depth = index.get(stack.size() - 1);
687+
final Collection<Integer> depth = index[stack.size() - 1];
686688
depth.add(size - stack.get(stack.size() - 1));
687689
}
688690

689691
private void cleanupAdd() {
690-
final List<Integer> depth = index.get(stack.size() - 1);
692+
final List<Integer> depth = index[stack.size() - 1];
691693
depth.remove(depth.size() - 1);
692694
}
693695

@@ -708,7 +710,7 @@ protected VPackBuilder close(final boolean sort)
708710
}
709711
final byte head = head();
710712
final boolean isArray = head == 0x06 || head == 0x13;
711-
final List<Integer> in = index.get(stack.size() - 1);
713+
final List<Integer> in = index[stack.size() - 1];
712714
final int tos = stack.get(stack.size() - 1);
713715
if (in.isEmpty()) {
714716
return closeEmptyArrayOrObject(tos, isArray);
@@ -990,10 +992,10 @@ private VPackBuilder closeArray(final int tos, final List<Integer> in) {
990992
}
991993

992994
private static class SortEntry {
993-
private final VPackSlice slice;
995+
private final VPackStringSlice slice;
994996
private final int offset;
995997

996-
public SortEntry(final VPackSlice slice, final int offset) {
998+
public SortEntry(final VPackStringSlice slice, final int offset) {
997999
super();
9981000
this.slice = slice;
9991001
this.offset = offset;
@@ -1002,17 +1004,18 @@ public SortEntry(final VPackSlice slice, final int offset) {
10021004

10031005
private void sortObjectIndex(final int start, final List<Integer> offsets)
10041006
throws VPackKeyTypeException, VPackNeedAttributeTranslatorException {
1005-
final List<VPackBuilder.SortEntry> attributes = new ArrayList<VPackBuilder.SortEntry>();
1006-
for (final Integer offset : offsets) {
1007-
attributes.add(new SortEntry(new VPackSlice(buffer, start + offset).makeKey(), offset));
1007+
VPackBuilder.SortEntry[] attributes = new VPackBuilder.SortEntry[offsets.size()];
1008+
for (int i = 0; i < offsets.size(); i++) {
1009+
Integer offset = offsets.get(i);
1010+
attributes[i] = new SortEntry(new VPackSlice(buffer, start + offset).makeKey().getAsStringSlice(), offset);
10081011
}
10091012
final Comparator<SortEntry> comparator = new Comparator<SortEntry>() {
10101013
@Override
10111014
public int compare(final SortEntry o1, final SortEntry o2) {
1012-
return o1.slice.getAsString().compareTo(o2.slice.getAsString());
1015+
return o1.slice.compareTo(o2.slice);
10131016
}
10141017
};
1015-
Collections.sort(attributes, comparator);
1018+
Arrays.sort(attributes, comparator);
10161019
offsets.clear();
10171020
for (final SortEntry sortEntry : attributes) {
10181021
offsets.add(sortEntry.offset);

src/main/java/com/arangodb/velocypack/VPackSlice.java

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,12 @@
2424
import java.math.BigDecimal;
2525
import java.math.BigInteger;
2626
import java.nio.charset.Charset;
27+
import java.nio.charset.StandardCharsets;
2728
import java.util.Arrays;
2829
import java.util.Date;
2930
import java.util.Iterator;
3031
import java.util.Map.Entry;
32+
import java.lang.Comparable;
3133

3234
import com.arangodb.velocypack.exception.VPackException;
3335
import com.arangodb.velocypack.exception.VPackKeyTypeException;
@@ -294,6 +296,10 @@ public java.sql.Timestamp getAsSQLTimestamp() {
294296
}
295297

296298
public String getAsString() {
299+
return getAsStringSlice().toString();
300+
}
301+
302+
public VPackStringSlice getAsStringSlice() {
297303
if (!isString()) {
298304
throw new VPackValueTypeException(ValueType.STRING);
299305
}
@@ -308,12 +314,12 @@ private boolean isLongString() {
308314
return head() == (byte) 0xbf;
309315
}
310316

311-
private String getShortString() {
312-
return new String(vpack, start + 1, length(), Charset.forName("UTF-8"));
317+
private VPackStringSlice getShortString() {
318+
return new VPackStringSlice(vpack, start + 1, length());
313319
}
314320

315-
private String getLongString() {
316-
return new String(vpack, start + 9, getLongStringLength(), Charset.forName("UTF-8"));
321+
private VPackStringSlice getLongString() {
322+
return new VPackStringSlice(vpack, start + 9, getLongStringLength());
317323
}
318324

319325
private int getLongStringLength() {
@@ -574,6 +580,7 @@ private VPackSlice searchObjectKeyBinary(
574580
long l = 0;
575581
long r = n - 1;
576582

583+
byte[] attributeBytes = attribute.getBytes(StandardCharsets.UTF_8);
577584
for (;;) {
578585
// midpoint
579586
final long index = l + ((r - l) / 2);
@@ -582,14 +589,14 @@ private VPackSlice searchObjectKeyBinary(
582589
final VPackSlice key = new VPackSlice(vpack, (int) (start + keyIndex));
583590
int res = 0;
584591
if (key.isString()) {
585-
res = key.compareString(attribute);
592+
res = key.getAsStringSlice().compareToBytes(attributeBytes);
586593
} else if (key.isInteger()) {
587594
// translate key
588595
if (!useTranslator) {
589596
// no attribute translator
590597
throw new VPackNeedAttributeTranslatorException();
591598
}
592-
res = key.translateUnchecked().compareString(attribute);
599+
res = key.translateUnchecked().getAsStringSlice().compareToBytes(attributeBytes);
593600
} else {
594601
// invalid key
595602
result = new VPackSlice();
@@ -816,4 +823,5 @@ public boolean equals(final Object obj) {
816823
return true;
817824
}
818825

826+
819827
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.arangodb.velocypack;
2+
3+
import java.nio.charset.StandardCharsets;
4+
5+
/**
6+
* Wrapper around a {@link ValueType.STRING} supporting fast bytewise comparison.
7+
*
8+
* @see https://github.com/arangodb/velocypack/blob/master/VelocyPack.md#objects
9+
*/
10+
public class VPackStringSlice implements Comparable<VPackStringSlice> {
11+
private byte[] vpack;
12+
/**
13+
* Index of the string bytes within {@link vpack},
14+
* i.e. tag byte and length are somewhere before this index.
15+
*/
16+
private int start;
17+
private int length;
18+
19+
public VPackStringSlice(byte[] vpack, int start, int length) {
20+
this.vpack = vpack;
21+
this.start = start;
22+
this.length = length;
23+
}
24+
25+
@Override
26+
public int compareTo(VPackStringSlice o) {
27+
return compareToBytes(o.vpack, o.start, o.length);
28+
}
29+
30+
public int compareToBytes(byte[] other) {
31+
return compareToBytes(other, 0, other.length);
32+
}
33+
34+
public int compareToBytes(byte[] other, int off, int oLen) {
35+
for (int i = 0; i < length && i < oLen; i++) {
36+
int c = (vpack[start + i] & 0xff) - (other[off + i] & 0xff);
37+
if (c != 0) return c;
38+
}
39+
return length - oLen;
40+
}
41+
42+
@Override
43+
public String toString() {
44+
return new String(vpack, start, length, StandardCharsets.UTF_8);
45+
}
46+
}

0 commit comments

Comments
 (0)