Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
shawjef3 committed Jan 14, 2025
1 parent 7a5ef5a commit 40b42d5
Show file tree
Hide file tree
Showing 12 changed files with 564 additions and 64 deletions.
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@
</developer>
</developers>
<dependencies>
<dependency>
<groupId>it.unimi.dsi</groupId>
<artifactId>fastutil</artifactId>
<version>8.5.15</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
Expand Down
315 changes: 315 additions & 0 deletions src/main/java/com/maxmind/db/BigByteBuffer.java
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());
}

}
Loading

0 comments on commit 40b42d5

Please sign in to comment.