diff --git a/java/com/google/flatbuffers/ArrayReadWriteBuf.java b/java/com/google/flatbuffers/ArrayReadWriteBuf.java new file mode 100644 index 000000000..00517747c --- /dev/null +++ b/java/com/google/flatbuffers/ArrayReadWriteBuf.java @@ -0,0 +1,241 @@ +package com.google.flatbuffers; + +import java.util.Arrays; + +/** + * Implements {@code ReadBuf} using an array of bytes + * as a backing storage. Using array of bytes are + * usually faster than {@code ByteBuffer}. + * + * This class is not thread-safe, meaning that + * it must operate on a single thread. Operating from + * multiple thread leads into a undefined behavior + */ +public class ArrayReadWriteBuf implements ReadWriteBuf { + + private byte[] buffer; + private int writePos; + + public ArrayReadWriteBuf() { + this(10); + } + + public ArrayReadWriteBuf(int initialCapacity) { + this(new byte[initialCapacity]); + } + + public ArrayReadWriteBuf(byte[] buffer) { + this.buffer = buffer; + this.writePos = 0; + } + + public ArrayReadWriteBuf(byte[] buffer, int startPos) { + this.buffer = buffer; + this.writePos = startPos; + } + + @Override + public boolean getBoolean(int index) { + return buffer[index] != 0; + } + + @Override + public byte get(int index) { + return buffer[index]; + } + + @Override + public short getShort(int index) { + return (short) ((buffer[index+ 1] << 8) | (buffer[index] & 0xff)); + } + + @Override + public int getInt(int index) { + return (((buffer[index + 3]) << 24) | + ((buffer[index + 2] & 0xff) << 16) | + ((buffer[index + 1] & 0xff) << 8) | + ((buffer[index] & 0xff))); + } + + @Override + public long getLong(int index) { + return ((((long) buffer[index++] & 0xff)) | + (((long) buffer[index++] & 0xff) << 8) | + (((long) buffer[index++] & 0xff) << 16) | + (((long) buffer[index++] & 0xff) << 24) | + (((long) buffer[index++] & 0xff) << 32) | + (((long) buffer[index++] & 0xff) << 40) | + (((long) buffer[index++] & 0xff) << 48) | + (((long) buffer[index]) << 56)); + } + + @Override + public float getFloat(int index) { + return Float.intBitsToFloat(getInt(index)); + } + + @Override + public double getDouble(int index) { + return Double.longBitsToDouble(getLong(index)); + } + + @Override + public String getString(int start, int size) { + return Utf8Safe.decodeUtf8Array(buffer, start, size); + } + + @Override + public byte[] data() { + return buffer; + } + + + @Override + public void putBoolean(boolean value) { + setBoolean(writePos, value); + writePos++; + } + + @Override + public void put(byte[] value, int start, int length) { + set(writePos, value, start, length); + writePos+=length; + } + + @Override + public void put(byte value) { + set(writePos, value); + writePos++; + } + + @Override + public void putShort(short value) { + setShort(writePos, value); + writePos +=2; + } + + @Override + public void putInt(int value) { + setInt(writePos, value); + writePos +=4; + } + + @Override + public void putLong(long value) { + setLong(writePos, value); + writePos +=8; + } + + @Override + public void putFloat(float value) { + setFloat(writePos, value); + writePos +=4; + } + + @Override + public void putDouble(double value) { + setDouble(writePos, value); + writePos +=8; + } + + @Override + public void setBoolean(int index, boolean value) { + set(index, value ? (byte)1 : (byte)0); + } + + @Override + public void set(int index, byte value) { + requestCapacity(index + 1); + buffer[index] = value; + } + + @Override + public void set(int index, byte[] toCopy, int start, int length) { + requestCapacity(index + (length - start)); + System.arraycopy(toCopy, start, buffer, index, length); + } + + @Override + public void setShort(int index, short value) { + requestCapacity(index + 2); + + buffer[index++] = (byte) ((value) & 0xff); + buffer[index ] = (byte) ((value >> 8) & 0xff); + } + + @Override + public void setInt(int index, int value) { + requestCapacity(index + 4); + + buffer[index++] = (byte) ((value) & 0xff); + buffer[index++] = (byte) ((value >> 8) & 0xff); + buffer[index++] = (byte) ((value >> 16) & 0xff); + buffer[index ] = (byte) ((value >> 24) & 0xff); + } + + @Override + public void setLong(int index, long value) { + requestCapacity(index + 8); + + int i = (int) value; + buffer[index++] = (byte) ((i) & 0xff); + buffer[index++] = (byte) ((i >> 8) & 0xff); + buffer[index++] = (byte) ((i >> 16) & 0xff); + buffer[index++] = (byte) ((i >> 24) & 0xff); + i = (int) (value >> 32); + buffer[index++] = (byte) ((i) & 0xff); + buffer[index++] = (byte) ((i >> 8) & 0xff); + buffer[index++] = (byte) ((i >> 16) & 0xff); + buffer[index ] = (byte) ((i >> 24) & 0xff); + } + + @Override + public void setFloat(int index, float value) { + requestCapacity(index + 4); + + int iValue = Float.floatToRawIntBits(value); + buffer[index++] = (byte) ((iValue) & 0xff); + buffer[index++] = (byte) ((iValue >> 8) & 0xff); + buffer[index++] = (byte) ((iValue >> 16) & 0xff); + buffer[index ] = (byte) ((iValue >> 24) & 0xff); + } + + @Override + public void setDouble(int index, double value) { + requestCapacity(index + 8); + + long lValue = Double.doubleToRawLongBits(value); + int i = (int) lValue; + buffer[index++] = (byte) ((i) & 0xff); + buffer[index++] = (byte) ((i >> 8) & 0xff); + buffer[index++] = (byte) ((i >> 16) & 0xff); + buffer[index++] = (byte) ((i >> 24) & 0xff); + i = (int) (lValue >> 32); + buffer[index++] = (byte) ((i) & 0xff); + buffer[index++] = (byte) ((i >> 8) & 0xff); + buffer[index++] = (byte) ((i >> 16) & 0xff); + buffer[index ] = (byte) ((i >> 24) & 0xff); + } + + @Override + public int limit() { + return writePos; + } + + @Override + public int writePosition() { + return writePos; + } + + @Override + public boolean requestCapacity(int capacity) { + if (buffer.length > capacity) { + return true; + } + // implemented in the same growing fashion as ArrayList + int oldCapacity = buffer.length; + int newCapacity = oldCapacity + (oldCapacity >> 1); + buffer = Arrays.copyOf(buffer, newCapacity); + return true; + } +} diff --git a/java/com/google/flatbuffers/ByteBufferReadWriteBuf.java b/java/com/google/flatbuffers/ByteBufferReadWriteBuf.java new file mode 100644 index 000000000..79ad7bbb4 --- /dev/null +++ b/java/com/google/flatbuffers/ByteBufferReadWriteBuf.java @@ -0,0 +1,165 @@ +package com.google.flatbuffers; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +public class ByteBufferReadWriteBuf implements ReadWriteBuf { + + private final ByteBuffer buffer; + + public ByteBufferReadWriteBuf(ByteBuffer bb) { + this.buffer = bb; + this.buffer.order(ByteOrder.LITTLE_ENDIAN); + } + + @Override + public boolean getBoolean(int index) { + return get(index) != 0; + } + + @Override + public byte get(int index) { + return buffer.get(index); + } + + @Override + public short getShort(int index) { + return buffer.getShort(index); + } + + @Override + public int getInt(int index) { + return buffer.getInt(index); + } + + @Override + public long getLong(int index) { + return buffer.getLong(index); + } + + @Override + public float getFloat(int index) { + return buffer.getFloat(index); + } + + @Override + public double getDouble(int index) { + return buffer.getDouble(index); + } + + @Override + public String getString(int start, int size) { + return Utf8Safe.decodeUtf8Buffer(buffer, start, size); + } + + @Override + public byte[] data() { + return buffer.array(); + } + + @Override + public void putBoolean(boolean value) { + buffer.put(value ? (byte)1 : (byte)0); + } + + @Override + public void put(byte[] value, int start, int length) { + buffer.put(value, start, length); + } + + @Override + public void put(byte value) { + buffer.put(value); + } + + @Override + public void putShort(short value) { + buffer.putShort(value); + } + + @Override + public void putInt(int value) { + buffer.putInt(value); + } + + @Override + public void putLong(long value) { + buffer.putLong(value); + } + + @Override + public void putFloat(float value) { + buffer.putFloat(value); + } + + @Override + public void putDouble(double value) { + buffer.putDouble(value); + } + + @Override + public void setBoolean(int index, boolean value) { + set(index, value ? (byte)1 : (byte)0); + } + + @Override + public void set(int index, byte value) { + requestCapacity(index + 1); + buffer.put(index, value); + } + + @Override + public void set(int index, byte[] value, int start, int length) { + requestCapacity(index + (length - start)); + int curPos = buffer.position(); + buffer.position(index); + buffer.put(value, start, length); + buffer.position(curPos); + } + + @Override + public void setShort(int index, short value) { + requestCapacity(index + 2); + buffer.putShort(index, value); + } + + @Override + public void setInt(int index, int value) { + requestCapacity(index + 4); + buffer.putInt(index, value); + } + + @Override + public void setLong(int index, long value) { + requestCapacity(index + 8); + buffer.putLong(index, value); + } + + @Override + public void setFloat(int index, float value) { + requestCapacity(index + 4); + buffer.putFloat(index, value); + } + + @Override + public void setDouble(int index, double value) { + requestCapacity(index + 8); + buffer.putDouble(index, value); + } + + @Override + public int writePosition() { + return buffer.position(); + } + + @Override + public int limit() { + return buffer.limit(); + } + + @Override + public boolean requestCapacity(int capacity) { + return capacity <= buffer.limit(); + } + +} diff --git a/java/com/google/flatbuffers/FlexBuffers.java b/java/com/google/flatbuffers/FlexBuffers.java index ff8ea8d83..d7df08c1e 100644 --- a/java/com/google/flatbuffers/FlexBuffers.java +++ b/java/com/google/flatbuffers/FlexBuffers.java @@ -36,7 +36,7 @@ import java.nio.charset.StandardCharsets; *
* Example of usage: *
- * ByteBuffer bb = ... // load message from file or network
+ * ReadBuf bb = ... // load message from file or network
* FlexBuffers.Reference r = FlexBuffers.getRoot(bb); // Reads the root element
* FlexBuffers.Map map = r.asMap(); // We assumed root object is a map
* System.out.println(map.get("name").asString()); // prints element with key "name"
@@ -100,7 +100,7 @@ public class FlexBuffers {
/** Represent a vector of booleans type */
public static final int FBT_VECTOR_BOOL = 36; // To Allow the same type of conversion of type to vector type
- private static final ByteBuffer EMPTY_BB = ByteBuffer.allocate(1).asReadOnlyBuffer();
+ private static final ReadBuf EMPTY_BB = new ArrayReadWriteBuf(new byte[] {0}, 1);
/**
* Checks where a type is a typed vector
@@ -151,13 +151,13 @@ public class FlexBuffers {
}
// return position of the element that the offset is pointing to
- private static int indirect(ByteBuffer bb, int offset, int byteWidth) {
- // we assume all offset fits on a int, since ByteBuffer operates with that assumption
+ private static int indirect(ReadBuf bb, int offset, int byteWidth) {
+ // we assume all offset fits on a int, since ReadBuf operates with that assumption
return (int) (offset - readUInt(bb, offset, byteWidth));
}
// read unsigned int with size byteWidth and return as a 64-bit integer
- private static long readUInt(ByteBuffer buff, int end, int byteWidth) {
+ private static long readUInt(ReadBuf buff, int end, int byteWidth) {
switch (byteWidth) {
case 1: return byteToUnsignedInt(buff.get(end));
case 2: return shortToUnsignedInt(buff.getShort(end));
@@ -168,12 +168,12 @@ public class FlexBuffers {
}
// read signed int of size byteWidth and return as 32-bit int
- private static int readInt(ByteBuffer buff, int end, int byteWidth) {
+ private static int readInt(ReadBuf buff, int end, int byteWidth) {
return (int) readLong(buff, end, byteWidth);
}
// read signed int of size byteWidth and return as 64-bit int
- private static long readLong(ByteBuffer buff, int end, int byteWidth) {
+ private static long readLong(ReadBuf buff, int end, int byteWidth) {
switch (byteWidth) {
case 1: return buff.get(end);
case 2: return buff.getShort(end);
@@ -183,7 +183,7 @@ public class FlexBuffers {
}
}
- private static double readDouble(ByteBuffer buff, int end, int byteWidth) {
+ private static double readDouble(ReadBuf buff, int end, int byteWidth) {
switch (byteWidth) {
case 4: return buff.getFloat(end);
case 8: return buff.getDouble(end);
@@ -192,12 +192,23 @@ public class FlexBuffers {
}
/**
- * Reads a FlexBuffer message in ByteBuffer and returns {@link Reference} to
+ * Reads a FlexBuffer message in ReadBuf and returns {@link Reference} to
* the root element.
- * @param buffer ByteBuffer containing FlexBuffer message
+ * @param buffer ReadBuf containing FlexBuffer message
* @return {@link Reference} to the root object
*/
+ @Deprecated
public static Reference getRoot(ByteBuffer buffer) {
+ return getRoot( buffer.hasArray() ? new ArrayReadWriteBuf(buffer.array(), buffer.limit()) : new ByteBufferReadWriteBuf(buffer));
+ }
+
+ /**
+ * Reads a FlexBuffer message in ReadBuf and returns {@link Reference} to
+ * the root element.
+ * @param buffer ReadBuf containing FlexBuffer message
+ * @return {@link Reference} to the root object
+ */
+ public static Reference getRoot(ReadBuf buffer) {
// See Finish() below for the serialization counterpart of this.
// The root ends at the end of the buffer, so we parse backwards from there.
int end = buffer.limit();
@@ -213,17 +224,17 @@ public class FlexBuffers {
public static class Reference {
private static final Reference NULL_REFERENCE = new Reference(EMPTY_BB, 0, 1, 0);
- private ByteBuffer bb;
+ private ReadBuf bb;
private int end;
private int parentWidth;
private int byteWidth;
private int type;
- Reference(ByteBuffer bb, int end, int parentWidth, int packedType) {
+ Reference(ReadBuf bb, int end, int parentWidth, int packedType) {
this(bb, end, parentWidth, (1 << (packedType & 3)), packedType >> 2);
}
- Reference(ByteBuffer bb, int end, int parentWidth, int byteWidth, int type) {
+ Reference(ReadBuf bb, int end, int parentWidth, int byteWidth, int type) {
this.bb = bb;
this.end = end;
this.parentWidth = parentWidth;
@@ -484,13 +495,13 @@ public class FlexBuffers {
if (isString()) {
int start = indirect(bb, end, parentWidth);
int size = (int) readUInt(bb, start - byteWidth, byteWidth);
- return Utf8.getDefault().decodeUtf8(bb, start, size);
+ return bb.getString(start, size);
}
else if (isKey()){
int start = indirect(bb, end, byteWidth);
for (int i = start; ; i++) {
if (bb.get(i) == 0) {
- return Utf8.getDefault().decodeUtf8(bb, start, i - start);
+ return bb.getString(start, i - start);
}
}
} else {
@@ -619,11 +630,11 @@ public class FlexBuffers {
* Points into the data buffer and allows access to one type.
*/
private static abstract class Object {
- ByteBuffer bb;
+ ReadBuf bb;
int end;
int byteWidth;
- Object(ByteBuffer buff, int end, int byteWidth) {
+ Object(ReadBuf buff, int end, int byteWidth) {
this.bb = buff;
this.end = end;
this.byteWidth = byteWidth;
@@ -640,9 +651,9 @@ public class FlexBuffers {
// Stores size in `byte_width_` bytes before end position.
private static abstract class Sized extends Object {
- private final int size;
+ protected final int size;
- Sized(ByteBuffer buff, int end, int byteWidth) {
+ Sized(ReadBuf buff, int end, int byteWidth) {
super(buff, end, byteWidth);
size = readInt(bb, end - byteWidth, byteWidth);
}
@@ -655,14 +666,14 @@ public class FlexBuffers {
/**
* Represents a array of bytes element in the buffer
*
- * It can be converted to `ByteBuffer` using {@link data()},
+ *
It can be converted to `ReadBuf` using {@link data()},
* copied into a byte[] using {@link getBytes()} or
* have individual bytes accessed individually using {@link get(int)}
*/
public static class Blob extends Sized {
static final Blob EMPTY = new Blob(EMPTY_BB, 1, 1);
- Blob(ByteBuffer buff, int end, int byteWidth) {
+ Blob(ReadBuf buff, int end, int byteWidth) {
super(buff, end, byteWidth);
}
@@ -672,11 +683,11 @@ public class FlexBuffers {
}
/**
- * Return {@link Blob} as `ByteBuffer`
- * @return blob as `ByteBuffer`
+ * Return {@link Blob} as `ReadBuf`
+ * @return blob as `ReadBuf`
*/
public ByteBuffer data() {
- ByteBuffer dup = bb.duplicate();
+ ByteBuffer dup = ByteBuffer.wrap(bb.data());
dup.position(end);
dup.limit(end + size());
return dup.asReadOnlyBuffer().slice();
@@ -709,7 +720,7 @@ public class FlexBuffers {
*/
@Override
public String toString() {
- return Utf8.getDefault().decodeUtf8(bb, end, size());
+ return bb.getString(end, size());
}
/**
@@ -718,7 +729,7 @@ public class FlexBuffers {
@Override
public StringBuilder toString(StringBuilder sb) {
sb.append('"');
- sb.append(Utf8.getDefault().decodeUtf8(bb, end, size()));
+ sb.append(bb.getString(end, size()));
return sb.append('"');
}
}
@@ -731,7 +742,7 @@ public class FlexBuffers {
private static final Key EMPTY = new Key(EMPTY_BB, 0, 0);
- Key(ByteBuffer buff, int end, int byteWidth) {
+ Key(ReadBuf buff, int end, int byteWidth) {
super(buff, end, byteWidth);
}
@@ -760,7 +771,7 @@ public class FlexBuffers {
break;
}
}
- return Utf8.getDefault().decodeUtf8(bb, end, size);
+ return bb.getString(end, size);
}
int compareTo(byte[] other) {
@@ -808,7 +819,7 @@ public class FlexBuffers {
public static class Map extends Vector {
private static final Map EMPTY_MAP = new Map(EMPTY_BB, 1, 1);
- Map(ByteBuffer bb, int end, int byteWidth) {
+ Map(ReadBuf bb, int end, int byteWidth) {
super(bb, end, byteWidth);
}
@@ -913,7 +924,7 @@ public class FlexBuffers {
private static final Vector EMPTY_VECTOR = new Vector(EMPTY_BB, 1, 1);
- Vector(ByteBuffer bb, int end, int byteWidth) {
+ Vector(ReadBuf bb, int end, int byteWidth) {
super(bb, end, byteWidth);
}
@@ -976,7 +987,7 @@ public class FlexBuffers {
private final int elemType;
- TypedVector(ByteBuffer bb, int end, int byteWidth, int elemType) {
+ TypedVector(ReadBuf bb, int end, int byteWidth, int elemType) {
super(bb, end, byteWidth);
this.elemType = elemType;
}
diff --git a/java/com/google/flatbuffers/FlexBuffersBuilder.java b/java/com/google/flatbuffers/FlexBuffersBuilder.java
index c8438bba9..cb44492a1 100644
--- a/java/com/google/flatbuffers/FlexBuffersBuilder.java
+++ b/java/com/google/flatbuffers/FlexBuffersBuilder.java
@@ -83,7 +83,7 @@ public class FlexBuffersBuilder {
private static final int WIDTH_16 = 1;
private static final int WIDTH_32 = 2;
private static final int WIDTH_64 = 3;
- private final ByteBuffer bb;
+ private final ReadWriteBuf bb;
private final ArrayList stack = new ArrayList<>();
private final HashMap keyPool = new HashMap<>();
private final HashMap stringPool = new HashMap<>();
@@ -116,7 +116,7 @@ public class FlexBuffersBuilder {
* @param bufSize size of buffer in bytes.
*/
public FlexBuffersBuilder(int bufSize) {
- this(ByteBuffer.allocate(bufSize), BUILDER_FLAG_SHARE_KEYS);
+ this(new ArrayReadWriteBuf(bufSize), BUILDER_FLAG_SHARE_KEYS);
}
/**
@@ -132,11 +132,14 @@ public class FlexBuffersBuilder {
* @param bb `ByteBuffer` that will hold the message
* @param flags Share flags
*/
+ @Deprecated
public FlexBuffersBuilder(ByteBuffer bb, int flags) {
+ this(new ArrayReadWriteBuf(bb.array()), flags);
+ }
+
+ public FlexBuffersBuilder(ReadWriteBuf bb, int flags) {
this.bb = bb;
this.flags = flags;
- bb.order(ByteOrder.LITTLE_ENDIAN);
- bb.position(0);
}
/**
@@ -154,7 +157,7 @@ public class FlexBuffersBuilder {
*
* @return `ByteBuffer` with finished message
*/
- public ByteBuffer getBuffer() {
+ public ReadWriteBuf getBuffer() {
assert (finished);
return bb;
}
@@ -180,17 +183,20 @@ public class FlexBuffersBuilder {
if (key == null) {
return -1;
}
- int pos = bb.position();
+ int pos = bb.writePosition();
if ((flags & BUILDER_FLAG_SHARE_KEYS) != 0) {
- if (keyPool.get(key) == null) {
- bb.put(key.getBytes(StandardCharsets.UTF_8));
+ Integer keyFromPool = keyPool.get(key);
+ if (keyFromPool == null) {
+ byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
+ bb.put(keyBytes, 0, keyBytes.length);
bb.put((byte) 0);
keyPool.put(key, pos);
} else {
- pos = keyPool.get(key);
+ pos = keyFromPool;
}
} else {
- bb.put(key.getBytes(StandardCharsets.UTF_8));
+ byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
+ bb.put(keyBytes, 0, keyBytes.length);
bb.put((byte) 0);
keyPool.put(key, pos);
}
@@ -373,8 +379,8 @@ public class FlexBuffersBuilder {
int bitWidth = widthUInBits(blob.length);
int byteWidth = align(bitWidth);
writeInt(blob.length, byteWidth);
- int sloc = bb.position();
- bb.put(blob);
+ int sloc = bb.writePosition();
+ bb.put(blob, 0, blob.length);
if (trailing) {
bb.put((byte) 0);
}
@@ -384,7 +390,7 @@ public class FlexBuffersBuilder {
// Align to prepare for writing a scalar with a certain size.
private int align(int alignment) {
int byteWidth = 1 << alignment;
- int padBytes = Value.paddingBytes(bb.position(), byteWidth);
+ int padBytes = Value.paddingBytes(bb.writePosition(), byteWidth);
while (padBytes-- != 0) {
bb.put((byte) 0);
}
@@ -463,15 +469,14 @@ public class FlexBuffersBuilder {
// some other object.
assert (stack.size() == 1);
// Write root value.
- int byteWidth = align(stack.get(0).elemWidth(bb.position(), 0));
+ int byteWidth = align(stack.get(0).elemWidth(bb.writePosition(), 0));
writeAny(stack.get(0), byteWidth);
// Write root type.
bb.put(stack.get(0).storedPackedType());
// Write root size. Normally determined by parent, but root has no parent :)
bb.put((byte) byteWidth);
- bb.limit(bb.position());
this.finished = true;
- return bb;
+ return ByteBuffer.wrap(bb.data(), 0, bb.writePosition());
}
/*
@@ -493,13 +498,13 @@ public class FlexBuffersBuilder {
if (keys != null) {
// If this vector is part of a map, we will pre-fix an offset to the keys
// to this vector.
- bitWidth = Math.max(bitWidth, keys.elemWidth(bb.position(), 0));
+ bitWidth = Math.max(bitWidth, keys.elemWidth(bb.writePosition(), 0));
prefixElems += 2;
}
int vectorType = FBT_KEY;
// Check bit widths and types for all elements.
for (int i = start; i < stack.size(); i++) {
- int elemWidth = stack.get(i).elemWidth(bb.position(), i + prefixElems);
+ int elemWidth = stack.get(i).elemWidth(bb.writePosition(), i + prefixElems);
bitWidth = Math.max(bitWidth, elemWidth);
if (typed) {
if (i == start) {
@@ -528,7 +533,7 @@ public class FlexBuffersBuilder {
writeInt(length, byteWidth);
}
// Then the actual data.
- int vloc = bb.position();
+ int vloc = bb.writePosition();
for (int i = start; i < stack.size(); i++) {
writeAny(stack.get(i), byteWidth);
}
@@ -544,7 +549,7 @@ public class FlexBuffersBuilder {
}
private void writeOffset(long val, int byteWidth) {
- int reloff = (int) (bb.position() - val);
+ int reloff = (int) (bb.writePosition() - val);
assert (byteWidth == 8 || reloff < 1L << (byteWidth * 8));
writeInt(reloff, byteWidth);
}
@@ -610,7 +615,7 @@ public class FlexBuffersBuilder {
int prefixElems = 1;
// Check bit widths and types for all elements.
for (int i = start; i < stack.size(); i++) {
- int elemWidth = Value.elemWidth(FBT_KEY, WIDTH_8, stack.get(i).key, bb.position(), i + prefixElems);
+ int elemWidth = Value.elemWidth(FBT_KEY, WIDTH_8, stack.get(i).key, bb.writePosition(), i + prefixElems);
bitWidth = Math.max(bitWidth, elemWidth);
}
@@ -618,7 +623,7 @@ public class FlexBuffersBuilder {
// Write vector. First the keys width/offset if available, and size.
writeInt(length, byteWidth);
// Then the actual data.
- int vloc = bb.position();
+ int vloc = bb.writePosition();
for (int i = start; i < stack.size(); i++) {
int pos = stack.get(i).key;
assert(pos != -1);
diff --git a/java/com/google/flatbuffers/ReadBuf.java b/java/com/google/flatbuffers/ReadBuf.java
new file mode 100644
index 000000000..dbb4e733a
--- /dev/null
+++ b/java/com/google/flatbuffers/ReadBuf.java
@@ -0,0 +1,81 @@
+package com.google.flatbuffers;
+
+/**
+ * Represent a chunk of data, where FlexBuffers will read from.
+ */
+interface ReadBuf {
+
+ /**
+ * Read boolean from data. Booleans as stored as single byte
+ * @param index position of the element in ReadBuf
+ * @return boolean element
+ */
+ boolean getBoolean(int index);
+
+ /**
+ * Read a byte from data.
+ * @param index position of the element in ReadBuf
+ * @return a byte
+ */
+ byte get(int index);
+
+ /**
+ * Read a short from data.
+ * @param index position of the element in ReadBuf
+ * @return a short
+ */
+ short getShort(int index);
+
+ /**
+ * Read a 32-bit int from data.
+ * @param index position of the element in ReadBuf
+ * @return an int
+ */
+ int getInt(int index);
+
+ /**
+ * Read a 64-bit long from data.
+ * @param index position of the element in ReadBuf
+ * @return a long
+ */
+ long getLong(int index);
+
+ /**
+ * Read a 32-bit float from data.
+ * @param index position of the element in ReadBuf
+ * @return a float
+ */
+ float getFloat(int index);
+
+ /**
+ * Read a 64-bit float from data.
+ * @param index position of the element in ReadBuf
+ * @return a double
+ */
+ double getDouble(int index);
+
+ /**
+ * Read an UTF-8 string from data.
+ * @param start initial element of the string
+ * @param size size of the string in bytes.
+ * @return a {@code String}
+ */
+ String getString(int start, int size);
+
+ /**
+ * Expose ReadBuf as an array of bytes.
+ * This method is meant to be as efficient as possible, so for a array-backed ReadBuf, it should
+ * return its own internal data. In case access to internal data is not possible,
+ * a copy of the data into an array of bytes might occur.
+ * @return ReadBuf as an array of bytes
+ */
+ byte[] data();
+
+ /**
+ * Defines the size of the message in the buffer. It also determines last position that buffer
+ * can be read. Last byte to be accessed is in position {@code limit() -1}.
+ * @return indicate last position
+ */
+ int limit();
+
+}
diff --git a/java/com/google/flatbuffers/ReadWriteBuf.java b/java/com/google/flatbuffers/ReadWriteBuf.java
new file mode 100644
index 000000000..df672fbcc
--- /dev/null
+++ b/java/com/google/flatbuffers/ReadWriteBuf.java
@@ -0,0 +1,135 @@
+package com.google.flatbuffers;
+
+/**
+ * Interface to represent a read-write buffer. This interface will be used to access and write
+ * FlexBuffers message.
+ */
+interface ReadWriteBuf extends ReadBuf {
+ /**
+ * Put a boolean into the buffer at {@code writePosition()} . Booleans as stored as single
+ * byte. Write position will be incremented.
+ * @return boolean element
+ */
+ void putBoolean(boolean value);
+
+ /**
+ * Put an array of bytes into the buffer at {@code writePosition()}. Write position will be
+ * incremented.
+ * @param value the data to be copied
+ * @param start initial position on value to be copied
+ * @param length amount of bytes to be copied
+ */
+ void put (byte[] value, int start, int length);
+
+ /**
+ * Write a byte into the buffer at {@code writePosition()}. Write position will be
+ * incremented.
+ */
+ void put(byte value);
+
+ /**
+ * Write a 16-bit into in the buffer at {@code writePosition()}. Write position will be
+ * incremented.
+ */
+ void putShort(short value);
+
+ /**
+ * Write a 32-bit into in the buffer at {@code writePosition()}. Write position will be
+ * incremented.
+ */
+ void putInt(int value);
+
+ /**
+ * Write a 64-bit into in the buffer at {@code writePosition()}. Write position will be
+ * incremented.
+ */
+ void putLong(long value);
+
+ /**
+ * Write a 32-bit float into the buffer at {@code writePosition()}. Write position will be
+ * incremented.
+ */
+ void putFloat(float value);
+
+ /**
+ * Write a 64-bit float into the buffer at {@code writePosition()}. Write position will be
+ * incremented.
+ */
+ void putDouble(double value);
+
+ /**
+ * Write boolean into a given position on the buffer. Booleans as stored as single byte.
+ * @param index position of the element in buffer
+ */
+ void setBoolean(int index, boolean value);
+
+ /**
+ * Read a byte from data.
+ * @param index position of the element in the buffer
+ * @return a byte
+ */
+ void set(int index, byte value);
+
+ /**
+ * Write an array of bytes into the buffer.
+ * @param index initial position of the buffer to be written
+ * @param value the data to be copied
+ * @param start initial position on value to be copied
+ * @param length amount of bytes to be copied
+ */
+ void set(int index, byte[] value, int start, int length);
+
+ /**
+ * Read a short from data.
+ * @param index position of the element in ReadBuf
+ * @return a short
+ */
+ void setShort(int index, short value);
+
+ /**
+ * Read a 32-bit int from data.
+ * @param index position of the element in ReadBuf
+ * @return an int
+ */
+ void setInt(int index, int value);
+
+ /**
+ * Read a 64-bit long from data.
+ * @param index position of the element in ReadBuf
+ * @return a long
+ */
+ void setLong(int index, long value);
+
+ /**
+ * Read a 32-bit float from data.
+ * @param index position of the element in ReadBuf
+ * @return a float
+ */
+ void setFloat(int index, float value);
+
+ /**
+ * Read a 64-bit float from data.
+ * @param index position of the element in ReadBuf
+ * @return a double
+ */
+ void setDouble(int index, double value);
+
+
+ int writePosition();
+ /**
+ * Defines the size of the message in the buffer. It also determines last position that buffer
+ * can be read or write. Last byte to be accessed is in position {@code limit() -1}.
+ * @return indicate last position
+ */
+ int limit();
+
+ /**
+ * Request capacity of the buffer. In case buffer is already larger
+ * than the requested, this method will just return true. Otherwise
+ * It might try to resize the buffer.
+ *
+ * @return true if buffer is able to offer
+ * the requested capacity
+ */
+ boolean requestCapacity(int capacity);
+}
diff --git a/java/com/google/flatbuffers/Utf8Safe.java b/java/com/google/flatbuffers/Utf8Safe.java
index 06ea420bb..523e3f1b4 100644
--- a/java/com/google/flatbuffers/Utf8Safe.java
+++ b/java/com/google/flatbuffers/Utf8Safe.java
@@ -123,7 +123,7 @@ final public class Utf8Safe extends Utf8 {
return utf8Length;
}
- private static String decodeUtf8Array(byte[] bytes, int index, int size) {
+ public static String decodeUtf8Array(byte[] bytes, int index, int size) {
// Bitwise OR combines the sign bits so any negative value fails the check.
if ((index | size | bytes.length - index - size) < 0) {
throw new ArrayIndexOutOfBoundsException(
@@ -197,7 +197,7 @@ final public class Utf8Safe extends Utf8 {
return new String(resultArr, 0, resultPos);
}
- private static String decodeUtf8Buffer(ByteBuffer buffer, int offset,
+ public static String decodeUtf8Buffer(ByteBuffer buffer, int offset,
int length) {
// Bitwise OR combines the sign bits so any negative value fails the check.
if ((offset | length | buffer.limit() - offset - length) < 0) {
diff --git a/tests/JavaTest.java b/tests/JavaTest.java
index 766d35255..5ea9baad8 100644
--- a/tests/JavaTest.java
+++ b/tests/JavaTest.java
@@ -15,6 +15,7 @@ import com.google.flatbuffers.UnionVector;
import com.google.flatbuffers.FlexBuffers.FlexBufferException;
import com.google.flatbuffers.FlexBuffers.Reference;
import com.google.flatbuffers.FlexBuffers.Vector;
+import com.google.flatbuffers.ArrayReadWriteBuf;
import java.io.*;
import java.math.BigInteger;
@@ -1007,6 +1008,23 @@ class JavaTest {
TestEq(source, result);
}
+ public static void testBuilderGrowth() {
+ FlexBuffersBuilder builder = new FlexBuffersBuilder();
+ builder.putString("This is a small string");
+ ByteBuffer b = builder.finish();
+ TestEq("This is a small string", FlexBuffers.getRoot(b).asString());
+
+ FlexBuffersBuilder failBuilder = new FlexBuffersBuilder(ByteBuffer.allocate(1));
+ try {
+ failBuilder.putString("This is a small string");
+ // This should never be reached, it should throw an exception
+ // since ByteBuffers do not grow
+ assert(false);
+ } catch (java.lang.ArrayIndexOutOfBoundsException exception) {
+ // It should throw exception
+ }
+ }
+
public static void TestFlexBuffers() {
testSingleElementByte();
testSingleElementShort();
@@ -1028,6 +1046,7 @@ class JavaTest {
testFlexBuferEmpty();
testFlexBufferVectorStrings();
testDeprecatedTypedVectorString();
+ testBuilderGrowth();
}
static void TestVectorOfBytes() {