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() {