forked from maxmind/MaxMind-DB-Reader-java
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
564 additions
and
64 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,315 @@ | ||
package com.maxmind.db; | ||
|
||
import it.unimi.dsi.fastutil.bytes.ByteBigList; | ||
import it.unimi.dsi.fastutil.ints.Int2IntFunction; | ||
import it.unimi.dsi.fastutil.longs.Long2LongFunction; | ||
import java.nio.BufferUnderflowException; | ||
import java.nio.ByteBuffer; | ||
import java.nio.ByteOrder; | ||
import java.util.Objects; | ||
|
||
/** | ||
* This provides minimal functionality from {@link ByteBuffer} that is required for this library, | ||
* but it is modified to work with files larger than Java's normal memory mapped file size limit. | ||
*/ | ||
final class BigByteBuffer { | ||
|
||
public static final boolean NATIVE_IS_BIG_ENDIAN = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN; | ||
|
||
public static BigByteBuffer wrap(ByteBigList bytes) { | ||
return new BigByteBuffer(bytes, 0, bytes.size64(), bytes.size64()); | ||
} | ||
|
||
// copied from Unsafe.java | ||
private static long toUnsignedLong(byte n) { return n & 0xffl; } | ||
|
||
// copied from Unsafe.java | ||
private static int toUnsignedInt(byte n) { return n & 0xff; } | ||
|
||
// copied from Unsafe.java | ||
private static int pickPos(int top, int pos) { return NATIVE_IS_BIG_ENDIAN ? top - pos : pos; } | ||
|
||
private static Int2IntFunction CONV_ENDIAN_INT = | ||
NATIVE_IS_BIG_ENDIAN ? | ||
Int2IntFunction.identity() : | ||
Integer::reverseBytes; | ||
|
||
// copied from Unsafe.java | ||
private static Long2LongFunction CONV_ENDIAN_LONG = | ||
NATIVE_IS_BIG_ENDIAN ? | ||
Long2LongFunction.identity() : | ||
Long::reverseBytes; | ||
|
||
final ByteBigList bytes; | ||
long position; | ||
long limit; | ||
final long capacity; | ||
|
||
private BigByteBuffer(ByteBigList bytes) { | ||
this(bytes, 0, bytes.size64(), bytes.size64()); | ||
} | ||
|
||
private BigByteBuffer( | ||
ByteBigList bytes, | ||
long position, | ||
long limit, | ||
long capacity | ||
) { | ||
this.bytes = bytes; | ||
this.capacity = capacity; | ||
this.limit = limit; | ||
this.position = position; | ||
} | ||
|
||
/** | ||
* {@link ByteBuffer#get()} | ||
*/ | ||
public byte get() { | ||
return bytes.getByte(nextGetIndex()); | ||
} | ||
|
||
/** | ||
* {@link ByteBuffer#get(int)} | ||
*/ | ||
public byte get(long index) { | ||
return bytes.getByte(checkIndex(index)); | ||
} | ||
|
||
/** | ||
* {@link ByteBuffer#get(byte[])} | ||
*/ | ||
public BigByteBuffer get(byte[] dst) { | ||
return get(dst, 0, dst.length); | ||
} | ||
|
||
/** | ||
* {@link ByteBuffer#get(byte[], int, int)} )} | ||
*/ | ||
public BigByteBuffer get(byte[] dst, int offset, int length) { | ||
Objects.checkFromIndexSize(offset, length, dst.length); | ||
long pos = position(); | ||
if (length > limit() - pos) { | ||
throw new BufferUnderflowException(); | ||
} | ||
|
||
getArray(pos, dst, offset, length); | ||
|
||
position(pos + length); | ||
return this; | ||
} | ||
|
||
public long getLong() { | ||
byte i0 = get(nextGetIndex()); | ||
byte i1 = get(nextGetIndex()); | ||
byte i2 = get(nextGetIndex()); | ||
byte i3 = get(nextGetIndex()); | ||
byte i4 = get(nextGetIndex()); | ||
byte i5 = get(nextGetIndex()); | ||
byte i6 = get(nextGetIndex()); | ||
byte i7 = get(nextGetIndex()); | ||
return CONV_ENDIAN_LONG.applyAsLong(((toUnsignedLong(i0) << pickPos(56, 0)) | ||
| (toUnsignedLong(i1) << pickPos(56, 8)) | ||
| (toUnsignedLong(i2) << pickPos(56, 16)) | ||
| (toUnsignedLong(i3) << pickPos(56, 24)) | ||
| (toUnsignedLong(i4) << pickPos(56, 32)) | ||
| (toUnsignedLong(i5) << pickPos(56, 40)) | ||
| (toUnsignedLong(i6) << pickPos(56, 48)) | ||
| (toUnsignedLong(i7) << pickPos(56, 56)))); | ||
} | ||
|
||
public int getInt() { | ||
byte i0 = get(nextGetIndex()); | ||
byte i1 = get(nextGetIndex()); | ||
byte i2 = get(nextGetIndex()); | ||
byte i3 = get(nextGetIndex()); | ||
return CONV_ENDIAN_INT.applyAsInt(((toUnsignedInt(i0) << pickPos(24, 0)) | ||
| (toUnsignedInt(i1) << pickPos(24, 8)) | ||
| (toUnsignedInt(i2) << pickPos(24, 16)) | ||
| (toUnsignedInt(i3) << pickPos(24, 24)))); | ||
} | ||
|
||
/** | ||
* {@link ByteBuffer#getDouble()} | ||
*/ | ||
public double getDouble() { | ||
return Double.longBitsToDouble(getLong()); | ||
} | ||
|
||
/** | ||
* {@link ByteBuffer#getFloat()} | ||
*/ | ||
public float getFloat() { | ||
return Float.intBitsToFloat(getInt()); | ||
} | ||
|
||
/** | ||
* {@link ByteBuffer#position()} | ||
*/ | ||
public long position() { | ||
return position; | ||
} | ||
|
||
/** | ||
* {@link ByteBuffer#position(int)} | ||
*/ | ||
public BigByteBuffer position(final long newPosition) { | ||
if (newPosition > limit | newPosition < 0) { | ||
throw createPositionException(newPosition); | ||
} | ||
position = newPosition; | ||
return this; | ||
} | ||
|
||
/** | ||
* {@link ByteBuffer#limit()} | ||
*/ | ||
public long limit() { | ||
return limit; | ||
} | ||
|
||
/** | ||
* {@link ByteBuffer#limit(int)} | ||
*/ | ||
public BigByteBuffer limit(final long newLimit) { | ||
if (newLimit > capacity | newLimit < 0) { | ||
throw createLimitException(newLimit); | ||
} | ||
limit = newLimit; | ||
if (position > newLimit) { | ||
position = newLimit; | ||
} | ||
return this; | ||
} | ||
|
||
/** | ||
* {@link ByteBuffer#capacity()} | ||
*/ | ||
long capacity() { | ||
return capacity; | ||
} | ||
|
||
// copied from Buffer.java | ||
/** | ||
* Verify that {@code 0 < newPosition <= limit} | ||
* | ||
* @param newPosition | ||
* The new position value | ||
* | ||
* @throws IllegalArgumentException | ||
* If the specified position is out of bounds. | ||
*/ | ||
private IllegalArgumentException createPositionException(long newPosition) { | ||
String msg = null; | ||
|
||
if (newPosition > limit) { | ||
msg = "newPosition > limit: (" + newPosition + " > " + limit + ")"; | ||
} else { // assume negative | ||
assert newPosition < 0 : "newPosition expected to be negative"; | ||
msg = "newPosition < 0: (" + newPosition + " < 0)"; | ||
} | ||
|
||
return new IllegalArgumentException(msg); | ||
} | ||
|
||
// copied from Buffer.java | ||
/** | ||
* Verify that {@code 0 < newLimit <= capacity} | ||
* | ||
* @param newLimit | ||
* The new limit value | ||
* | ||
* @throws IllegalArgumentException | ||
* If the specified limit is out of bounds. | ||
*/ | ||
private IllegalArgumentException createLimitException(long newLimit) { | ||
String msg = null; | ||
|
||
if (newLimit > capacity) { | ||
msg = "newLimit > capacity: (" + newLimit + " > " + capacity + ")"; | ||
} else { // assume negative | ||
assert newLimit < 0 : "newLimit expected to be negative"; | ||
msg = "newLimit < 0: (" + newLimit + " < 0)"; | ||
} | ||
|
||
return new IllegalArgumentException(msg); | ||
} | ||
|
||
// copied from Buffer.java | ||
/** | ||
* Checks the current position against the limit, throwing a {@link | ||
* BufferUnderflowException} if it is not smaller than the limit, and then | ||
* increments the position. | ||
* | ||
* @return The current position value, before it is incremented | ||
*/ | ||
long nextGetIndex() { // package-private | ||
long p = position; | ||
if (p >= limit) { | ||
throw new BufferUnderflowException(); | ||
} | ||
position = p + 1; | ||
return p; | ||
} | ||
|
||
// copied from Buffer.java | ||
/** | ||
* {@link ByteBuffer#nextGetIndex(int)} | ||
*/ | ||
long nextGetIndex(long nb) { // package-private | ||
long p = position; | ||
if (limit - p < nb) { | ||
throw new BufferUnderflowException(); | ||
} | ||
position = p + nb; | ||
return p; | ||
} | ||
|
||
// copied from Buffer.java | ||
/** | ||
* Checks the given index against the limit, throwing an {@link | ||
* IndexOutOfBoundsException} if it is not smaller than the limit | ||
* or is smaller than zero. | ||
*/ | ||
long checkIndex(long i) { // package-private | ||
if ((i < 0) || (i >= limit)) { | ||
throw new IndexOutOfBoundsException(); | ||
} | ||
return i; | ||
} | ||
|
||
/** | ||
* Get a {@link ByteBuffer} for the current buffer's position and limit. The position | ||
* is forwarded by the number of bytes. | ||
* | ||
* @param limit is the number of bytes from {@link #position()} to put into the | ||
* {@link ByteBuffer}. | ||
* @throws BufferUnderflowException if there aren't enough bytes remaining | ||
*/ | ||
public ByteBuffer getByteBuffer(int limit) { | ||
final long position = nextGetIndex(limit); | ||
final byte[] bufferBytes = new byte[limit]; | ||
bytes.getElements(position, bufferBytes, 0, limit); | ||
return ByteBuffer.wrap(bufferBytes).asReadOnlyBuffer(); | ||
} | ||
|
||
private BigByteBuffer getArray(long index, byte[] dst, int offset, int length) { | ||
final long end = offset + length; | ||
long j = index; | ||
for (int i = offset; i < end; i++, j++) { | ||
dst[i] = get(j); | ||
} | ||
return this; | ||
} | ||
|
||
/** | ||
* {@link ByteBuffer#duplicate()} | ||
*/ | ||
public BigByteBuffer duplicate() { | ||
return new BigByteBuffer( | ||
bytes, | ||
position(), | ||
limit(), | ||
capacity()); | ||
} | ||
|
||
} |
Oops, something went wrong.