Skip to content

Commit d2e768e

Browse files
committed
fix #154
1 parent 7a5ef5a commit d2e768e

File tree

14 files changed

+570
-72
lines changed

14 files changed

+570
-72
lines changed

pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@
3737
</developer>
3838
</developers>
3939
<dependencies>
40+
<dependency>
41+
<groupId>it.unimi.dsi</groupId>
42+
<artifactId>fastutil</artifactId>
43+
<version>8.5.15</version>
44+
</dependency>
4045
<dependency>
4146
<groupId>org.junit.jupiter</groupId>
4247
<artifactId>junit-jupiter</artifactId>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.maxmind.db;
2+
3+
import java.util.function.ToIntBiFunction;
4+
5+
@FunctionalInterface
6+
interface BiInt2IntFunction extends ToIntBiFunction<Integer, Integer> {
7+
8+
@Override
9+
default int applyAsInt(Integer integer, Integer integer2) {
10+
return apply(integer, integer);
11+
}
12+
13+
int apply(int integer0, int integer1);
14+
15+
}
Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
package com.maxmind.db;
2+
3+
import it.unimi.dsi.fastutil.bytes.ByteBigList;
4+
import it.unimi.dsi.fastutil.ints.Int2IntFunction;
5+
import it.unimi.dsi.fastutil.longs.Long2LongFunction;
6+
import java.nio.BufferUnderflowException;
7+
import java.nio.ByteBuffer;
8+
import java.nio.ByteOrder;
9+
import java.util.Objects;
10+
11+
/**
12+
* This provides minimal functionality from {@link ByteBuffer} that is required for this library,
13+
* but it is modified to work with files larger than Java's normal memory mapped file size limit.
14+
*/
15+
final class BigByteBuffer {
16+
17+
public static final boolean NATIVE_IS_BIG_ENDIAN =
18+
ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN;
19+
20+
public static BigByteBuffer wrap(ByteBigList bytes) {
21+
return new BigByteBuffer(bytes, 0, bytes.size64(), bytes.size64());
22+
}
23+
24+
// copied from Unsafe.java
25+
private static long toUnsignedLong(byte n) {
26+
return n & 0xffL;
27+
}
28+
29+
// copied from Unsafe.java
30+
private static int toUnsignedInt(byte n) {
31+
return n & 0xff;
32+
}
33+
34+
private static final BiInt2IntFunction PICK_POS =
35+
NATIVE_IS_BIG_ENDIAN
36+
? (int top, int pos) -> top - pos
37+
: (int top, int pos) -> pos;
38+
39+
private static final Int2IntFunction CONV_ENDIAN_INT =
40+
NATIVE_IS_BIG_ENDIAN
41+
? Int2IntFunction.identity()
42+
: Integer::reverseBytes;
43+
44+
private static final Long2LongFunction CONV_ENDIAN_LONG =
45+
NATIVE_IS_BIG_ENDIAN
46+
? Long2LongFunction.identity()
47+
: Long::reverseBytes;
48+
49+
final ByteBigList bytes;
50+
final long capacity;
51+
long position;
52+
long limit;
53+
54+
private BigByteBuffer(
55+
ByteBigList bytes,
56+
long position,
57+
long limit,
58+
long capacity
59+
) {
60+
this.bytes = bytes;
61+
this.capacity = capacity;
62+
this.limit = limit;
63+
this.position = position;
64+
}
65+
66+
/**
67+
* {@link ByteBuffer#get()}
68+
*/
69+
public byte get() {
70+
return bytes.getByte(nextGetIndex(1));
71+
}
72+
73+
/**
74+
* {@link ByteBuffer#get(int)}
75+
*/
76+
public byte get(long index) {
77+
return bytes.getByte(checkIndex(index));
78+
}
79+
80+
/**
81+
* {@link ByteBuffer#get(byte[])}
82+
*/
83+
public BigByteBuffer get(byte[] dst) {
84+
return get(dst, 0, dst.length);
85+
}
86+
87+
/**
88+
* {@link ByteBuffer#get(byte[], int, int)} )}
89+
*/
90+
public BigByteBuffer get(byte[] dst, int offset, int length) {
91+
Objects.checkFromIndexSize(offset, length, dst.length);
92+
long pos = position();
93+
if (length > limit() - pos) {
94+
throw new BufferUnderflowException();
95+
}
96+
97+
this.bytes.getElements(pos, dst, offset, length);
98+
99+
position(pos + length);
100+
return this;
101+
}
102+
103+
public long getLong() {
104+
long index = nextGetIndex(8);
105+
byte i0 = get(index);
106+
byte i1 = get(++index);
107+
byte i2 = get(++index);
108+
byte i3 = get(++index);
109+
byte i4 = get(++index);
110+
byte i5 = get(++index);
111+
byte i6 = get(++index);
112+
byte i7 = get(++index);
113+
return CONV_ENDIAN_LONG.applyAsLong(((toUnsignedLong(i0) << PICK_POS.apply(56, 0))
114+
| (toUnsignedLong(i1) << PICK_POS.apply(56, 8))
115+
| (toUnsignedLong(i2) << PICK_POS.apply(56, 16))
116+
| (toUnsignedLong(i3) << PICK_POS.apply(56, 24))
117+
| (toUnsignedLong(i4) << PICK_POS.apply(56, 32))
118+
| (toUnsignedLong(i5) << PICK_POS.apply(56, 40))
119+
| (toUnsignedLong(i6) << PICK_POS.apply(56, 48))
120+
| (toUnsignedLong(i7) << PICK_POS.apply(56, 56))));
121+
}
122+
123+
public int getInt() {
124+
long index = nextGetIndex(4);
125+
byte i0 = get(index);
126+
byte i1 = get(++index);
127+
byte i2 = get(++index);
128+
byte i3 = get(++index);
129+
return CONV_ENDIAN_INT.applyAsInt(((toUnsignedInt(i0) << PICK_POS.apply(24, 0))
130+
| (toUnsignedInt(i1) << PICK_POS.apply(24, 8))
131+
| (toUnsignedInt(i2) << PICK_POS.apply(24, 16))
132+
| (toUnsignedInt(i3) << PICK_POS.apply(24, 24))));
133+
}
134+
135+
/**
136+
* {@link ByteBuffer#getDouble()}
137+
*/
138+
public double getDouble() {
139+
return Double.longBitsToDouble(getLong());
140+
}
141+
142+
/**
143+
* {@link ByteBuffer#getFloat()}
144+
*/
145+
public float getFloat() {
146+
return Float.intBitsToFloat(getInt());
147+
}
148+
149+
/**
150+
* {@link ByteBuffer#position()}
151+
*/
152+
public long position() {
153+
return position;
154+
}
155+
156+
// copied from Buffer.java
157+
/**
158+
* {@link ByteBuffer#position(int)}
159+
*/
160+
public BigByteBuffer position(final long newPosition) {
161+
if (newPosition > limit | newPosition < 0) {
162+
throw createPositionException(newPosition);
163+
}
164+
position = newPosition;
165+
return this;
166+
}
167+
168+
/**
169+
* {@link ByteBuffer#limit()}
170+
*/
171+
public long limit() {
172+
return limit;
173+
}
174+
175+
/**
176+
* {@link ByteBuffer#limit(int)}
177+
*/
178+
public BigByteBuffer limit(final long newLimit) {
179+
if (newLimit > capacity | newLimit < 0) {
180+
throw createLimitException(newLimit);
181+
}
182+
limit = newLimit;
183+
if (position > newLimit) {
184+
position = newLimit;
185+
}
186+
return this;
187+
}
188+
189+
/**
190+
* {@link ByteBuffer#capacity()}
191+
*/
192+
long capacity() {
193+
return capacity;
194+
}
195+
196+
// copied from Buffer.java
197+
/**
198+
* Verify that {@code 0 < newPosition <= limit}
199+
*
200+
* @param newPosition
201+
* The new position value
202+
*
203+
* @throws IllegalArgumentException
204+
* If the specified position is out of bounds.
205+
*/
206+
private IllegalArgumentException createPositionException(long newPosition) {
207+
String msg = null;
208+
209+
if (newPosition > limit) {
210+
msg = "newPosition > limit: (" + newPosition + " > " + limit + ")";
211+
} else { // assume negative
212+
assert newPosition < 0 : "newPosition expected to be negative";
213+
msg = "newPosition < 0: (" + newPosition + " < 0)";
214+
}
215+
216+
return new IllegalArgumentException(msg);
217+
}
218+
219+
// copied from Buffer.java
220+
/**
221+
* Verify that {@code 0 < newLimit <= capacity}
222+
*
223+
* @param newLimit
224+
* The new limit value
225+
*
226+
* @throws IllegalArgumentException
227+
* If the specified limit is out of bounds.
228+
*/
229+
private IllegalArgumentException createLimitException(long newLimit) {
230+
String msg = null;
231+
232+
if (newLimit > capacity) {
233+
msg = "newLimit > capacity: (" + newLimit + " > " + capacity + ")";
234+
} else { // assume negative
235+
assert newLimit < 0 : "newLimit expected to be negative";
236+
msg = "newLimit < 0: (" + newLimit + " < 0)";
237+
}
238+
239+
return new IllegalArgumentException(msg);
240+
}
241+
242+
// copied from Buffer.java
243+
/**
244+
* {@link ByteBuffer#nextGetIndex(int)}
245+
*/
246+
long nextGetIndex(long nb) { // package-private
247+
long p = position;
248+
if (limit - p < nb) {
249+
throw new BufferUnderflowException();
250+
}
251+
position = p + nb;
252+
return p;
253+
}
254+
255+
// copied from Buffer.java
256+
/**
257+
* Checks the given index against the limit, throwing an {@link
258+
* IndexOutOfBoundsException} if it is not smaller than the limit
259+
* or is smaller than zero.
260+
*/
261+
long checkIndex(long i) { // package-private
262+
if ((i < 0) || (i >= limit)) {
263+
throw new IndexOutOfBoundsException();
264+
}
265+
return i;
266+
}
267+
268+
/**
269+
* Get a {@link ByteBuffer} for the current buffer's position. {@link #position()}
270+
* is forwarded by the number of bytes.
271+
*
272+
* @param limit is the number of bytes from {@link #position()} to put into the
273+
* {@link ByteBuffer}.
274+
* @throws BufferUnderflowException if there aren't enough bytes remaining
275+
*/
276+
public ByteBuffer getByteBuffer(int limit) {
277+
/*
278+
A more optimal solution might be to create a ByteBuffer implementation that
279+
is backed by bytes.subList(position, limit).
280+
*/
281+
final long position = nextGetIndex(limit);
282+
final byte[] bufferBytes = new byte[limit];
283+
bytes.getElements(position, bufferBytes, 0, limit);
284+
return ByteBuffer.wrap(bufferBytes);
285+
}
286+
287+
/**
288+
* {@link ByteBuffer#duplicate()}
289+
*/
290+
public BigByteBuffer duplicate() {
291+
return new BigByteBuffer(
292+
bytes,
293+
position(),
294+
limit(),
295+
capacity());
296+
}
297+
298+
}

0 commit comments

Comments
 (0)