Skip to content

Commit 529af2e

Browse files
committed
Allow Reader to accept any method of reading bytes.
`bufferHolderReference` can be volatile instead of atomic, since operations like `getAndSet` aren't used.
1 parent a467a42 commit 529af2e

17 files changed

+594
-95
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package com.maxmind.db;
2+
3+
import java.nio.ByteBuffer;
4+
5+
/**
6+
* A {@link ByteReader} implemented by a {@link ByteBuffer}.
7+
*/
8+
public class ByteBufferByteReader extends IntLimitedByteReader {
9+
protected final ByteBuffer bytes;
10+
11+
/**
12+
* Create a {@link ByteReader} from a {@link ByteBuffer}.
13+
*/
14+
public ByteBufferByteReader(final ByteBuffer bytes) {
15+
this.bytes = bytes;
16+
}
17+
18+
@Override
19+
public byte[] getBytes(final int length) {
20+
final byte[] dst = new byte[length];
21+
bytes.get(dst);
22+
return dst;
23+
}
24+
25+
@Override
26+
public byte get() {
27+
return bytes.get();
28+
}
29+
30+
@Override
31+
public byte get(long index) {
32+
return super.get(index);
33+
}
34+
35+
@Override
36+
protected byte get(final int index) {
37+
return bytes.get(index);
38+
}
39+
40+
@Override
41+
public long capacity() {
42+
return bytes.capacity();
43+
}
44+
45+
@Override
46+
protected ByteReader position(final int newPosition) {
47+
bytes.position(newPosition);
48+
return this;
49+
}
50+
51+
@Override
52+
public long position() {
53+
return bytes.position();
54+
}
55+
56+
@Override
57+
public ByteReader duplicate() {
58+
return new ByteBufferByteReader(bytes.duplicate());
59+
}
60+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package com.maxmind.db;
2+
3+
import java.nio.ByteBuffer;
4+
5+
/**
6+
* <p>This provides functionality from {@link ByteBuffer} that is required for this library.
7+
* The default implementation is {@link ByteBufferByteReader}, which just redirects method calls
8+
* to a {@link ByteBuffer}.Another implementation might be preferable. In that case, you can pass
9+
* it to {@link Reader#Reader(ByteReader, String, NodeCache)}.</p>
10+
*
11+
* <p>Methods in implementations should throw the same exceptions as the methods with the same name
12+
* in {@link ByteBuffer}.</p>
13+
*
14+
* <p>If your implementation can only handle positions of up to <code>Integer.MAX_VALUE</code>,
15+
* implement {@link IntLimitedByteReader}.</p>
16+
*/
17+
public interface ByteReader {
18+
19+
/**
20+
* See {@link ByteBuffer#position()}
21+
*/
22+
long position();
23+
24+
/**
25+
* See {@link ByteBuffer#position(int)}
26+
*/
27+
ByteReader position(final long newPosition);
28+
29+
/**
30+
* See {@link ByteBuffer#capacity()}.
31+
*/
32+
long capacity();
33+
34+
/**
35+
* Get the byte at {@link ByteReader#position()}. See {@link ByteBuffer#get()}
36+
*/
37+
byte get();
38+
39+
/**
40+
* Get the byte at the given index. See {@link ByteBuffer#get(int)}
41+
*/
42+
byte get(final long index);
43+
44+
/**
45+
* Read the requested number of bytes starting at {@link ByteReader#position()}.
46+
* It moves {@link ByteReader#position()} forward by the same amount.
47+
*/
48+
byte[] getBytes(final int length);
49+
50+
/**
51+
* This is intended for creating a copy of this collection that is safe
52+
* for use by a new thread.</p>
53+
*
54+
* See {@link java.nio.ByteBuffer#duplicate()}
55+
*/
56+
ByteReader duplicate();
57+
}

reader/src/main/java/com/maxmind/db/BufferHolder.java renamed to reader/src/main/java/com/maxmind/db/ByteReaderHolder.java

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,15 @@
1010
import java.nio.channels.FileChannel;
1111
import java.nio.channels.FileChannel.MapMode;
1212

13-
final class BufferHolder {
13+
final class ByteReaderHolder {
1414
// DO NOT PASS OUTSIDE THIS CLASS. Doing so will remove thread safety.
15-
private final ByteBuffer buffer;
15+
private final ByteReader reader;
1616

17-
BufferHolder(File database, FileMode mode) throws IOException {
17+
ByteReaderHolder(ByteReader reader) {
18+
this.reader = reader;
19+
}
20+
21+
ByteReaderHolder(File database, FileMode mode) throws IOException {
1822
try (
1923
final RandomAccessFile file = new RandomAccessFile(database, "r");
2024
final FileChannel channel = file.getChannel()
@@ -26,21 +30,23 @@ final class BufferHolder {
2630
+ database.getName()
2731
+ " into memory. Unexpected end of stream.");
2832
}
29-
this.buffer = buf.asReadOnlyBuffer();
33+
this.reader = new ByteBufferByteReader(buf.asReadOnlyBuffer());
3034
} else {
31-
this.buffer = channel.map(MapMode.READ_ONLY, 0, channel.size()).asReadOnlyBuffer();
35+
final ByteBuffer mappedBuffer =
36+
channel.map(MapMode.READ_ONLY, 0, channel.size()).asReadOnlyBuffer();
37+
this.reader = new ByteBufferByteReader(mappedBuffer);
3238
}
3339
}
3440
}
3541

3642
/**
37-
* Construct a ThreadBuffer from the provided URL.
43+
* Construct a {@link ByteReaderHolder} from the provided {@link InputStream}.
3844
*
3945
* @param stream the source of my bytes.
4046
* @throws IOException if unable to read from your source.
41-
* @throws NullPointerException if you provide a NULL InputStream
47+
* @throws NullPointerException if you provide a {@code null} InputStream
4248
*/
43-
BufferHolder(InputStream stream) throws IOException {
49+
ByteReaderHolder(InputStream stream) throws IOException {
4450
if (null == stream) {
4551
throw new NullPointerException("Unable to use a NULL InputStream");
4652
}
@@ -50,14 +56,15 @@ final class BufferHolder {
5056
while (-1 != (br = stream.read(bytes))) {
5157
baos.write(bytes, 0, br);
5258
}
53-
this.buffer = ByteBuffer.wrap(baos.toByteArray()).asReadOnlyBuffer();
59+
final ByteBuffer readOnlyBuffer = ByteBuffer.wrap(baos.toByteArray()).asReadOnlyBuffer();
60+
this.reader = new ByteBufferByteReader(readOnlyBuffer);
5461
}
5562

5663
/*
5764
* Returns a duplicate of the underlying ByteBuffer. The returned ByteBuffer
5865
* should not be shared between threads.
5966
*/
60-
ByteBuffer get() {
67+
public ByteReader get() {
6168
// The Java API docs for buffer state:
6269
//
6370
// Buffers are not safe for use by multiple concurrent threads. If a buffer is to be
@@ -75,6 +82,6 @@ ByteBuffer get() {
7582
// operations on the original buffer object, the risk of not synchronizing this call seems
7683
// relatively low and worth taking for the performance benefit when lookups are being done
7784
// from many threads.
78-
return this.buffer.duplicate();
85+
return reader.duplicate();
7986
}
8087
}

reader/src/main/java/com/maxmind/db/CacheKey.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,17 @@
88
* @param <T> the type of value
99
*/
1010
public final class CacheKey<T> {
11-
private final int offset;
11+
private final long offset;
1212
private final Class<T> cls;
1313
private final java.lang.reflect.Type type;
1414

15-
CacheKey(int offset, Class<T> cls, java.lang.reflect.Type type) {
15+
CacheKey(long offset, Class<T> cls, java.lang.reflect.Type type) {
1616
this.offset = offset;
1717
this.cls = cls;
1818
this.type = type;
1919
}
2020

21-
int getOffset() {
21+
long getOffset() {
2222
return this.offset;
2323
}
2424

@@ -58,7 +58,7 @@ public boolean equals(Object o) {
5858

5959
@Override
6060
public int hashCode() {
61-
int result = offset;
61+
int result = Long.hashCode(offset);
6262
result = 31 * result + (cls == null ? 0 : cls.hashCode());
6363
result = 31 * result + (type == null ? 0 : type.hashCode());
6464
return result;

reader/src/main/java/com/maxmind/db/CtrlData.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
final class CtrlData {
44
private final Type type;
55
private final int ctrlByte;
6-
private final int offset;
7-
private final int size;
6+
private final long offset;
7+
private final long size;
88

9-
CtrlData(Type type, int ctrlByte, int offset, int size) {
9+
CtrlData(Type type, int ctrlByte, long offset, long size) {
1010
this.type = type;
1111
this.ctrlByte = ctrlByte;
1212
this.offset = offset;
@@ -21,11 +21,11 @@ public int getCtrlByte() {
2121
return this.ctrlByte;
2222
}
2323

24-
public int getOffset() {
24+
public long getOffset() {
2525
return this.offset;
2626
}
2727

28-
public int getSize() {
28+
public long getSize() {
2929
return this.size;
3030
}
3131
}

0 commit comments

Comments
 (0)