Squashed 'third_party/flatbuffers/' content from commit acc9990ab

Change-Id: I48550d40d78fea996ebe74e9723a5d1f910de491
git-subtree-dir: third_party/flatbuffers
git-subtree-split: acc9990abd2206491480291b0f85f925110102ea
diff --git a/java/com/google/flatbuffers/ByteBufferUtil.java b/java/com/google/flatbuffers/ByteBufferUtil.java
new file mode 100644
index 0000000..624dc4e
--- /dev/null
+++ b/java/com/google/flatbuffers/ByteBufferUtil.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2017 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.flatbuffers;
+
+import static com.google.flatbuffers.Constants.*;
+
+import java.nio.ByteBuffer;
+
+/// @file
+/// @addtogroup flatbuffers_java_api
+/// @{
+
+/**
+ * Class that collects utility functions around `ByteBuffer`.
+ */
+public class ByteBufferUtil {
+
+	/**
+     * Extract the size prefix from a `ByteBuffer`.
+     * 
+     * @param bb a size-prefixed buffer
+     * @return the size prefix
+     */
+    public static int getSizePrefix(ByteBuffer bb) {
+        return bb.getInt(bb.position());
+    }
+
+	/**
+     * Create a duplicate of a size-prefixed `ByteBuffer` that has its position
+     * advanced just past the size prefix.
+     * 
+     * @param bb a size-prefixed buffer
+     * @return a new buffer on the same underlying data that has skipped the
+     *         size prefix
+     */
+    public static ByteBuffer removeSizePrefix(ByteBuffer bb) {
+        ByteBuffer s = bb.duplicate();
+        s.position(s.position() + SIZE_PREFIX_LENGTH);
+        return s;
+    }
+
+}
+
+/// @}
diff --git a/java/com/google/flatbuffers/Constants.java b/java/com/google/flatbuffers/Constants.java
new file mode 100644
index 0000000..e974a80
--- /dev/null
+++ b/java/com/google/flatbuffers/Constants.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.flatbuffers;
+
+/// @cond FLATBUFFERS_INTERNAL
+
+/**
+ * Class that holds shared constants
+ */
+public class Constants {
+    // Java doesn't seem to have these.
+    /** The number of bytes in an `byte`. */
+    static final int SIZEOF_BYTE = 1;
+    /** The number of bytes in a `short`. */
+    static final int SIZEOF_SHORT = 2;
+    /** The number of bytes in an `int`. */
+    static final int SIZEOF_INT = 4;
+    /** The number of bytes in an `float`. */
+    static final int SIZEOF_FLOAT = 4;
+    /** The number of bytes in an `long`. */
+    static final int SIZEOF_LONG = 8;
+    /** The number of bytes in an `double`. */
+    static final int SIZEOF_DOUBLE = 8;
+    /** The number of bytes in a file identifier. */
+    static final int FILE_IDENTIFIER_LENGTH = 4;
+    /** The number of bytes in a size prefix. */
+    public static final int SIZE_PREFIX_LENGTH = 4;
+    /** A version identifier to force a compile error if someone
+    accidentally tries to build generated code with a runtime of
+    two mismatched version. Versions need to always match, as
+    the runtime and generated code are modified in sync.
+    Changes to the Java implementation need to be sure to change
+    the version here and in the code generator on every possible
+    incompatible change */
+    public static void FLATBUFFERS_1_11_1() {}
+}
+
+/// @endcond
diff --git a/java/com/google/flatbuffers/FlatBufferBuilder.java b/java/com/google/flatbuffers/FlatBufferBuilder.java
new file mode 100644
index 0000000..f224610
--- /dev/null
+++ b/java/com/google/flatbuffers/FlatBufferBuilder.java
@@ -0,0 +1,1012 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.flatbuffers;
+
+import static com.google.flatbuffers.Constants.*;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.*;
+import java.util.Arrays;
+
+/// @file
+/// @addtogroup flatbuffers_java_api
+/// @{
+
+/**
+ * Class that helps you build a FlatBuffer.  See the section
+ * "Use in Java/C#" in the main FlatBuffers documentation.
+ */
+public class FlatBufferBuilder {
+    /// @cond FLATBUFFERS_INTERNAL
+    ByteBuffer bb;                  // Where we construct the FlatBuffer.
+    int space;                      // Remaining space in the ByteBuffer.
+    int minalign = 1;               // Minimum alignment encountered so far.
+    int[] vtable = null;            // The vtable for the current table.
+    int vtable_in_use = 0;          // The amount of fields we're actually using.
+    boolean nested = false;         // Whether we are currently serializing a table.
+    boolean finished = false;       // Whether the buffer is finished.
+    int object_start;               // Starting offset of the current struct/table.
+    int[] vtables = new int[16];    // List of offsets of all vtables.
+    int num_vtables = 0;            // Number of entries in `vtables` in use.
+    int vector_num_elems = 0;       // For the current vector being built.
+    boolean force_defaults = false; // False omits default values from the serialized data.
+    ByteBufferFactory bb_factory;   // Factory for allocating the internal buffer
+    final Utf8 utf8;                // UTF-8 encoder to use
+    /// @endcond
+
+    /**
+     * Start with a buffer of size `initial_size`, then grow as required.
+     *
+     * @param initial_size The initial size of the internal buffer to use.
+     * @param bb_factory The factory to be used for allocating the internal buffer
+     */
+    public FlatBufferBuilder(int initial_size, ByteBufferFactory bb_factory) {
+        this(initial_size, bb_factory, null, Utf8.getDefault());
+    }
+
+    /**
+     * Start with a buffer of size `initial_size`, then grow as required.
+     *
+     * @param initial_size The initial size of the internal buffer to use.
+     * @param bb_factory The factory to be used for allocating the internal buffer
+     * @param existing_bb The byte buffer to reuse.
+     * @param utf8 The Utf8 codec
+     */
+    public FlatBufferBuilder(int initial_size, ByteBufferFactory bb_factory,
+                             ByteBuffer existing_bb, Utf8 utf8) {
+        if (initial_size <= 0) {
+          initial_size = 1;
+        }
+        space = initial_size;
+        this.bb_factory = bb_factory;
+        if (existing_bb != null) {
+          bb = existing_bb;
+          bb.clear();
+          bb.order(ByteOrder.LITTLE_ENDIAN);
+        } else {
+          bb = bb_factory.newByteBuffer(initial_size);
+        }
+        this.utf8 = utf8;
+    }
+
+   /**
+    * Start with a buffer of size `initial_size`, then grow as required.
+    *
+    * @param initial_size The initial size of the internal buffer to use.
+    */
+    public FlatBufferBuilder(int initial_size) {
+        this(initial_size, HeapByteBufferFactory.INSTANCE, null, Utf8.getDefault());
+    }
+
+    /**
+     * Start with a buffer of 1KiB, then grow as required.
+     */
+    public FlatBufferBuilder() {
+        this(1024);
+    }
+
+    /**
+     * Alternative constructor allowing reuse of {@link ByteBuffer}s.  The builder
+     * can still grow the buffer as necessary.  User classes should make sure
+     * to call {@link #dataBuffer()} to obtain the resulting encoded message.
+     *
+     * @param existing_bb The byte buffer to reuse.
+     * @param bb_factory The factory to be used for allocating a new internal buffer if
+     *                   the existing buffer needs to grow
+     */
+    public FlatBufferBuilder(ByteBuffer existing_bb, ByteBufferFactory bb_factory) {
+        this(existing_bb.capacity(), bb_factory, existing_bb, Utf8.getDefault());
+    }
+
+    /**
+     * Alternative constructor allowing reuse of {@link ByteBuffer}s.  The builder
+     * can still grow the buffer as necessary.  User classes should make sure
+     * to call {@link #dataBuffer()} to obtain the resulting encoded message.
+     *
+     * @param existing_bb The byte buffer to reuse.
+     */
+    public FlatBufferBuilder(ByteBuffer existing_bb) {
+        this(existing_bb, new HeapByteBufferFactory());
+    }
+
+    /**
+     * Alternative initializer that allows reusing this object on an existing
+     * `ByteBuffer`. This method resets the builder's internal state, but keeps
+     * objects that have been allocated for temporary storage.
+     *
+     * @param existing_bb The byte buffer to reuse.
+     * @param bb_factory The factory to be used for allocating a new internal buffer if
+     *                   the existing buffer needs to grow
+     * @return Returns `this`.
+     */
+    public FlatBufferBuilder init(ByteBuffer existing_bb, ByteBufferFactory bb_factory){
+        this.bb_factory = bb_factory;
+        bb = existing_bb;
+        bb.clear();
+        bb.order(ByteOrder.LITTLE_ENDIAN);
+        minalign = 1;
+        space = bb.capacity();
+        vtable_in_use = 0;
+        nested = false;
+        finished = false;
+        object_start = 0;
+        num_vtables = 0;
+        vector_num_elems = 0;
+        return this;
+    }
+
+    /**
+     * An interface that provides a user of the FlatBufferBuilder class the ability to specify
+     * the method in which the internal buffer gets allocated. This allows for alternatives
+     * to the default behavior, which is to allocate memory for a new byte-array
+     * backed `ByteBuffer` array inside the JVM.
+     *
+     * The FlatBufferBuilder class contains the HeapByteBufferFactory class to
+     * preserve the default behavior in the event that the user does not provide
+     * their own implementation of this interface.
+     */
+    public static abstract class ByteBufferFactory {
+        /**
+         * Create a `ByteBuffer` with a given capacity.
+         * The returned ByteBuf must have a ByteOrder.LITTLE_ENDIAN ByteOrder.
+         *
+         * @param capacity The size of the `ByteBuffer` to allocate.
+         * @return Returns the new `ByteBuffer` that was allocated.
+         */
+        public abstract ByteBuffer newByteBuffer(int capacity);
+
+        /**
+         * Release a ByteBuffer. Current {@link FlatBufferBuilder}
+         * released any reference to it, so it is safe to dispose the buffer
+         * or return it to a pool.
+         * It is not guaranteed that the buffer has been created
+         * with {@link #newByteBuffer(int) }.
+         *
+         * @param bb the buffer to release
+         */
+        public void releaseByteBuffer(ByteBuffer bb) {
+        }
+    }
+
+    /**
+     * An implementation of the ByteBufferFactory interface that is used when
+     * one is not provided by the user.
+     *
+     * Allocate memory for a new byte-array backed `ByteBuffer` array inside the JVM.
+     */
+    public static final class HeapByteBufferFactory extends ByteBufferFactory {
+
+        public static final HeapByteBufferFactory INSTANCE = new HeapByteBufferFactory();
+
+        @Override
+        public ByteBuffer newByteBuffer(int capacity) {
+            return ByteBuffer.allocate(capacity).order(ByteOrder.LITTLE_ENDIAN);
+        }
+    }
+
+    /**
+     * Reset the FlatBufferBuilder by purging all data that it holds.
+     */
+    public void clear(){
+        space = bb.capacity();
+        bb.clear();
+        minalign = 1;
+        while(vtable_in_use > 0) vtable[--vtable_in_use] = 0;
+        vtable_in_use = 0;
+        nested = false;
+        finished = false;
+        object_start = 0;
+        num_vtables = 0;
+        vector_num_elems = 0;
+    }
+
+    /**
+     * Doubles the size of the backing {@link ByteBuffer} and copies the old data towards the
+     * end of the new buffer (since we build the buffer backwards).
+     *
+     * @param bb The current buffer with the existing data.
+     * @param bb_factory The factory to be used for allocating the new internal buffer
+     * @return A new byte buffer with the old data copied copied to it.  The data is
+     * located at the end of the buffer.
+     */
+    static ByteBuffer growByteBuffer(ByteBuffer bb, ByteBufferFactory bb_factory) {
+        int old_buf_size = bb.capacity();
+        if ((old_buf_size & 0xC0000000) != 0)  // Ensure we don't grow beyond what fits in an int.
+            throw new AssertionError("FlatBuffers: cannot grow buffer beyond 2 gigabytes.");
+        int new_buf_size = old_buf_size == 0 ? 1 : old_buf_size << 1;
+        bb.position(0);
+        ByteBuffer nbb = bb_factory.newByteBuffer(new_buf_size);
+        nbb.position(new_buf_size - old_buf_size);
+        nbb.put(bb);
+        return nbb;
+    }
+
+   /**
+    * Offset relative to the end of the buffer.
+    *
+    * @return Offset relative to the end of the buffer.
+    */
+    public int offset() {
+        return bb.capacity() - space;
+    }
+
+   /**
+    * Add zero valued bytes to prepare a new entry to be added.
+    *
+    * @param byte_size Number of bytes to add.
+    */
+    public void pad(int byte_size) {
+        for (int i = 0; i < byte_size; i++) bb.put(--space, (byte)0);
+    }
+
+   /**
+    * Prepare to write an element of `size` after `additional_bytes`
+    * have been written, e.g. if you write a string, you need to align such
+    * the int length field is aligned to {@link com.google.flatbuffers.Constants#SIZEOF_INT}, and
+    * the string data follows it directly.  If all you need to do is alignment, `additional_bytes`
+    * will be 0.
+    *
+    * @param size This is the of the new element to write.
+    * @param additional_bytes The padding size.
+    */
+    public void prep(int size, int additional_bytes) {
+        // Track the biggest thing we've ever aligned to.
+        if (size > minalign) minalign = size;
+        // Find the amount of alignment needed such that `size` is properly
+        // aligned after `additional_bytes`
+        int align_size = ((~(bb.capacity() - space + additional_bytes)) + 1) & (size - 1);
+        // Reallocate the buffer if needed.
+        while (space < align_size + size + additional_bytes) {
+            int old_buf_size = bb.capacity();
+            ByteBuffer old = bb;
+            bb = growByteBuffer(old, bb_factory);
+            if (old != bb) {
+                bb_factory.releaseByteBuffer(old);
+            }
+            space += bb.capacity() - old_buf_size;
+        }
+        pad(align_size);
+    }
+
+    /**
+     * Add a `boolean` to the buffer, backwards from the current location. Doesn't align nor
+     * check for space.
+     *
+     * @param x A `boolean` to put into the buffer.
+     */
+    public void putBoolean(boolean x) { bb.put      (space -= Constants.SIZEOF_BYTE, (byte)(x ? 1 : 0)); }
+
+    /**
+     * Add a `byte` to the buffer, backwards from the current location. Doesn't align nor
+     * check for space.
+     *
+     * @param x A `byte` to put into the buffer.
+     */
+    public void putByte   (byte    x) { bb.put      (space -= Constants.SIZEOF_BYTE, x); }
+
+    /**
+     * Add a `short` to the buffer, backwards from the current location. Doesn't align nor
+     * check for space.
+     *
+     * @param x A `short` to put into the buffer.
+     */
+    public void putShort  (short   x) { bb.putShort (space -= Constants.SIZEOF_SHORT, x); }
+
+    /**
+     * Add an `int` to the buffer, backwards from the current location. Doesn't align nor
+     * check for space.
+     *
+     * @param x An `int` to put into the buffer.
+     */
+    public void putInt    (int     x) { bb.putInt   (space -= Constants.SIZEOF_INT, x); }
+
+    /**
+     * Add a `long` to the buffer, backwards from the current location. Doesn't align nor
+     * check for space.
+     *
+     * @param x A `long` to put into the buffer.
+     */
+    public void putLong   (long    x) { bb.putLong  (space -= Constants.SIZEOF_LONG, x); }
+
+    /**
+     * Add a `float` to the buffer, backwards from the current location. Doesn't align nor
+     * check for space.
+     *
+     * @param x A `float` to put into the buffer.
+     */
+    public void putFloat  (float   x) { bb.putFloat (space -= Constants.SIZEOF_FLOAT, x); }
+
+    /**
+     * Add a `double` to the buffer, backwards from the current location. Doesn't align nor
+     * check for space.
+     *
+     * @param x A `double` to put into the buffer.
+     */
+    public void putDouble (double  x) { bb.putDouble(space -= Constants.SIZEOF_DOUBLE, x); }
+    /// @endcond
+
+    /**
+     * Add a `boolean` to the buffer, properly aligned, and grows the buffer (if necessary).
+     *
+     * @param x A `boolean` to put into the buffer.
+     */
+    public void addBoolean(boolean x) { prep(Constants.SIZEOF_BYTE, 0); putBoolean(x); }
+
+    /**
+     * Add a `byte` to the buffer, properly aligned, and grows the buffer (if necessary).
+     *
+     * @param x A `byte` to put into the buffer.
+     */
+    public void addByte   (byte    x) { prep(Constants.SIZEOF_BYTE, 0); putByte   (x); }
+
+    /**
+     * Add a `short` to the buffer, properly aligned, and grows the buffer (if necessary).
+     *
+     * @param x A `short` to put into the buffer.
+     */
+    public void addShort  (short   x) { prep(Constants.SIZEOF_SHORT, 0); putShort  (x); }
+
+    /**
+     * Add an `int` to the buffer, properly aligned, and grows the buffer (if necessary).
+     *
+     * @param x An `int` to put into the buffer.
+     */
+    public void addInt    (int     x) { prep(Constants.SIZEOF_INT, 0); putInt    (x); }
+
+    /**
+     * Add a `long` to the buffer, properly aligned, and grows the buffer (if necessary).
+     *
+     * @param x A `long` to put into the buffer.
+     */
+    public void addLong   (long    x) { prep(Constants.SIZEOF_LONG, 0); putLong   (x); }
+
+    /**
+     * Add a `float` to the buffer, properly aligned, and grows the buffer (if necessary).
+     *
+     * @param x A `float` to put into the buffer.
+     */
+    public void addFloat  (float   x) { prep(Constants.SIZEOF_FLOAT, 0); putFloat  (x); }
+
+    /**
+     * Add a `double` to the buffer, properly aligned, and grows the buffer (if necessary).
+     *
+     * @param x A `double` to put into the buffer.
+     */
+    public void addDouble (double  x) { prep(Constants.SIZEOF_DOUBLE, 0); putDouble (x); }
+
+   /**
+    * Adds on offset, relative to where it will be written.
+    *
+    * @param off The offset to add.
+    */
+    public void addOffset(int off) {
+        prep(SIZEOF_INT, 0);  // Ensure alignment is already done.
+        assert off <= offset();
+        off = offset() - off + SIZEOF_INT;
+        putInt(off);
+    }
+
+   /// @cond FLATBUFFERS_INTERNAL
+   /**
+    * Start a new array/vector of objects.  Users usually will not call
+    * this directly.  The `FlatBuffers` compiler will create a start/end
+    * method for vector types in generated code.
+    * <p>
+    * The expected sequence of calls is:
+    * <ol>
+    * <li>Start the array using this method.</li>
+    * <li>Call {@link #addOffset(int)} `num_elems` number of times to set
+    * the offset of each element in the array.</li>
+    * <li>Call {@link #endVector()} to retrieve the offset of the array.</li>
+    * </ol>
+    * <p>
+    * For example, to create an array of strings, do:
+    * <pre>{@code
+    * // Need 10 strings
+    * FlatBufferBuilder builder = new FlatBufferBuilder(existingBuffer);
+    * int[] offsets = new int[10];
+    *
+    * for (int i = 0; i < 10; i++) {
+    *   offsets[i] = fbb.createString(" " + i);
+    * }
+    *
+    * // Have the strings in the buffer, but don't have a vector.
+    * // Add a vector that references the newly created strings:
+    * builder.startVector(4, offsets.length, 4);
+    *
+    * // Add each string to the newly created vector
+    * // The strings are added in reverse order since the buffer
+    * // is filled in back to front
+    * for (int i = offsets.length - 1; i >= 0; i--) {
+    *   builder.addOffset(offsets[i]);
+    * }
+    *
+    * // Finish off the vector
+    * int offsetOfTheVector = fbb.endVector();
+    * }</pre>
+    *
+    * @param elem_size The size of each element in the array.
+    * @param num_elems The number of elements in the array.
+    * @param alignment The alignment of the array.
+    */
+    public void startVector(int elem_size, int num_elems, int alignment) {
+        notNested();
+        vector_num_elems = num_elems;
+        prep(SIZEOF_INT, elem_size * num_elems);
+        prep(alignment, elem_size * num_elems); // Just in case alignment > int.
+        nested = true;
+    }
+
+   /**
+    * Finish off the creation of an array and all its elements.  The array
+    * must be created with {@link #startVector(int, int, int)}.
+    *
+    * @return The offset at which the newly created array starts.
+    * @see #startVector(int, int, int)
+    */
+    public int endVector() {
+        if (!nested)
+            throw new AssertionError("FlatBuffers: endVector called without startVector");
+        nested = false;
+        putInt(vector_num_elems);
+        return offset();
+    }
+    /// @endcond
+
+    /**
+     * Create a new array/vector and return a ByteBuffer to be filled later.
+     * Call {@link #endVector} after this method to get an offset to the beginning
+     * of vector.
+     *
+     * @param elem_size the size of each element in bytes.
+     * @param num_elems number of elements in the vector.
+     * @param alignment byte alignment.
+     * @return ByteBuffer with position and limit set to the space allocated for the array.
+     */
+    public ByteBuffer createUnintializedVector(int elem_size, int num_elems, int alignment) {
+        int length = elem_size * num_elems;
+        startVector(elem_size, num_elems, alignment);
+
+        bb.position(space -= length);
+
+        // Slice and limit the copy vector to point to the 'array'
+        ByteBuffer copy = bb.slice().order(ByteOrder.LITTLE_ENDIAN);
+        copy.limit(length);
+        return copy;
+    }
+
+   /**
+     * Create a vector of tables.
+     *
+     * @param offsets Offsets of the tables.
+     * @return Returns offset of the vector.
+     */
+    public int createVectorOfTables(int[] offsets) {
+        notNested();
+        startVector(Constants.SIZEOF_INT, offsets.length, Constants.SIZEOF_INT);
+        for(int i = offsets.length - 1; i >= 0; i--) addOffset(offsets[i]);
+        return endVector();
+    }
+
+    /**
+     * Create a vector of sorted by the key tables.
+     *
+     * @param obj Instance of the table subclass.
+     * @param offsets Offsets of the tables.
+     * @return Returns offset of the sorted vector.
+     */
+    public <T extends Table> int createSortedVectorOfTables(T obj, int[] offsets) {
+        obj.sortTables(offsets, bb);
+        return createVectorOfTables(offsets);
+    }
+
+   /**
+    * Encode the string `s` in the buffer using UTF-8.  If {@code s} is
+    * already a {@link CharBuffer}, this method is allocation free.
+    *
+    * @param s The string to encode.
+    * @return The offset in the buffer where the encoded string starts.
+    */
+    public int createString(CharSequence s) {
+        int length = utf8.encodedLength(s);
+        addByte((byte)0);
+        startVector(1, length, 1);
+        bb.position(space -= length);
+        utf8.encodeUtf8(s, bb);
+        return endVector();
+    }
+
+   /**
+    * Create a string in the buffer from an already encoded UTF-8 string in a ByteBuffer.
+    *
+    * @param s An already encoded UTF-8 string as a `ByteBuffer`.
+    * @return The offset in the buffer where the encoded string starts.
+    */
+    public int createString(ByteBuffer s) {
+        int length = s.remaining();
+        addByte((byte)0);
+        startVector(1, length, 1);
+        bb.position(space -= length);
+        bb.put(s);
+        return endVector();
+    }
+
+    /**
+     * Create a byte array in the buffer.
+     *
+     * @param arr A source array with data
+     * @return The offset in the buffer where the encoded array starts.
+     */
+    public int createByteVector(byte[] arr) {
+        int length = arr.length;
+        startVector(1, length, 1);
+        bb.position(space -= length);
+        bb.put(arr);
+        return endVector();
+    }
+
+   /// @cond FLATBUFFERS_INTERNAL
+   /**
+    * Should not be accessing the final buffer before it is finished.
+    */
+    public void finished() {
+        if (!finished)
+            throw new AssertionError(
+                "FlatBuffers: you can only access the serialized buffer after it has been" +
+                " finished by FlatBufferBuilder.finish().");
+    }
+
+   /**
+    * Should not be creating any other object, string or vector
+    * while an object is being constructed.
+    */
+    public void notNested() {
+        if (nested)
+            throw new AssertionError("FlatBuffers: object serialization must not be nested.");
+    }
+
+   /**
+    * Structures are always stored inline, they need to be created right
+    * where they're used.  You'll get this assertion failure if you
+    * created it elsewhere.
+    *
+    * @param obj The offset of the created object.
+    */
+    public void Nested(int obj) {
+        if (obj != offset())
+            throw new AssertionError("FlatBuffers: struct must be serialized inline.");
+    }
+
+   /**
+    * Start encoding a new object in the buffer.  Users will not usually need to
+    * call this directly. The `FlatBuffers` compiler will generate helper methods
+    * that call this method internally.
+    * <p>
+    * For example, using the "Monster" code found on the "landing page". An
+    * object of type `Monster` can be created using the following code:
+    *
+    * <pre>{@code
+    * int testArrayOfString = Monster.createTestarrayofstringVector(fbb, new int[] {
+    *   fbb.createString("test1"),
+    *   fbb.createString("test2")
+    * });
+    *
+    * Monster.startMonster(fbb);
+    * Monster.addPos(fbb, Vec3.createVec3(fbb, 1.0f, 2.0f, 3.0f, 3.0,
+    *   Color.Green, (short)5, (byte)6));
+    * Monster.addHp(fbb, (short)80);
+    * Monster.addName(fbb, str);
+    * Monster.addInventory(fbb, inv);
+    * Monster.addTestType(fbb, (byte)Any.Monster);
+    * Monster.addTest(fbb, mon2);
+    * Monster.addTest4(fbb, test4);
+    * Monster.addTestarrayofstring(fbb, testArrayOfString);
+    * int mon = Monster.endMonster(fbb);
+    * }</pre>
+    * <p>
+    * Here:
+    * <ul>
+    * <li>The call to `Monster#startMonster(FlatBufferBuilder)` will call this
+    * method with the right number of fields set.</li>
+    * <li>`Monster#endMonster(FlatBufferBuilder)` will ensure {@link #endObject()} is called.</li>
+    * </ul>
+    * <p>
+    * It's not recommended to call this method directly.  If it's called manually, you must ensure
+    * to audit all calls to it whenever fields are added or removed from your schema.  This is
+    * automatically done by the code generated by the `FlatBuffers` compiler.
+    *
+    * @param numfields The number of fields found in this object.
+    */
+    public void startTable(int numfields) {
+        notNested();
+        if (vtable == null || vtable.length < numfields) vtable = new int[numfields];
+        vtable_in_use = numfields;
+        Arrays.fill(vtable, 0, vtable_in_use, 0);
+        nested = true;
+        object_start = offset();
+    }
+
+    /**
+     * Add a `boolean` to a table at `o` into its vtable, with value `x` and default `d`.
+     *
+     * @param o The index into the vtable.
+     * @param x A `boolean` to put into the buffer, depending on how defaults are handled. If
+     * `force_defaults` is `false`, compare `x` against the default value `d`. If `x` contains the
+     * default value, it can be skipped.
+     * @param d A `boolean` default value to compare against when `force_defaults` is `false`.
+     */
+    public void addBoolean(int o, boolean x, boolean d) { if(force_defaults || x != d) { addBoolean(x); slot(o); } }
+
+    /**
+     * Add a `byte` to a table at `o` into its vtable, with value `x` and default `d`.
+     *
+     * @param o The index into the vtable.
+     * @param x A `byte` to put into the buffer, depending on how defaults are handled. If
+     * `force_defaults` is `false`, compare `x` against the default value `d`. If `x` contains the
+     * default value, it can be skipped.
+     * @param d A `byte` default value to compare against when `force_defaults` is `false`.
+     */
+    public void addByte   (int o, byte    x, int     d) { if(force_defaults || x != d) { addByte   (x); slot(o); } }
+
+    /**
+     * Add a `short` to a table at `o` into its vtable, with value `x` and default `d`.
+     *
+     * @param o The index into the vtable.
+     * @param x A `short` to put into the buffer, depending on how defaults are handled. If
+     * `force_defaults` is `false`, compare `x` against the default value `d`. If `x` contains the
+     * default value, it can be skipped.
+     * @param d A `short` default value to compare against when `force_defaults` is `false`.
+     */
+    public void addShort  (int o, short   x, int     d) { if(force_defaults || x != d) { addShort  (x); slot(o); } }
+
+    /**
+     * Add an `int` to a table at `o` into its vtable, with value `x` and default `d`.
+     *
+     * @param o The index into the vtable.
+     * @param x An `int` to put into the buffer, depending on how defaults are handled. If
+     * `force_defaults` is `false`, compare `x` against the default value `d`. If `x` contains the
+     * default value, it can be skipped.
+     * @param d An `int` default value to compare against when `force_defaults` is `false`.
+     */
+    public void addInt    (int o, int     x, int     d) { if(force_defaults || x != d) { addInt    (x); slot(o); } }
+
+    /**
+     * Add a `long` to a table at `o` into its vtable, with value `x` and default `d`.
+     *
+     * @param o The index into the vtable.
+     * @param x A `long` to put into the buffer, depending on how defaults are handled. If
+     * `force_defaults` is `false`, compare `x` against the default value `d`. If `x` contains the
+     * default value, it can be skipped.
+     * @param d A `long` default value to compare against when `force_defaults` is `false`.
+     */
+    public void addLong   (int o, long    x, long    d) { if(force_defaults || x != d) { addLong   (x); slot(o); } }
+
+    /**
+     * Add a `float` to a table at `o` into its vtable, with value `x` and default `d`.
+     *
+     * @param o The index into the vtable.
+     * @param x A `float` to put into the buffer, depending on how defaults are handled. If
+     * `force_defaults` is `false`, compare `x` against the default value `d`. If `x` contains the
+     * default value, it can be skipped.
+     * @param d A `float` default value to compare against when `force_defaults` is `false`.
+     */
+    public void addFloat  (int o, float   x, double  d) { if(force_defaults || x != d) { addFloat  (x); slot(o); } }
+
+    /**
+     * Add a `double` to a table at `o` into its vtable, with value `x` and default `d`.
+     *
+     * @param o The index into the vtable.
+     * @param x A `double` to put into the buffer, depending on how defaults are handled. If
+     * `force_defaults` is `false`, compare `x` against the default value `d`. If `x` contains the
+     * default value, it can be skipped.
+     * @param d A `double` default value to compare against when `force_defaults` is `false`.
+     */
+    public void addDouble (int o, double  x, double  d) { if(force_defaults || x != d) { addDouble (x); slot(o); } }
+
+    /**
+     * Add an `offset` to a table at `o` into its vtable, with value `x` and default `d`.
+     *
+     * @param o The index into the vtable.
+     * @param x An `offset` to put into the buffer, depending on how defaults are handled. If
+     * `force_defaults` is `false`, compare `x` against the default value `d`. If `x` contains the
+     * default value, it can be skipped.
+     * @param d An `offset` default value to compare against when `force_defaults` is `false`.
+     */
+    public void addOffset (int o, int     x, int     d) { if(force_defaults || x != d) { addOffset (x); slot(o); } }
+
+    /**
+     * Add a struct to the table. Structs are stored inline, so nothing additional is being added.
+     *
+     * @param voffset The index into the vtable.
+     * @param x The offset of the created struct.
+     * @param d The default value is always `0`.
+     */
+    public void addStruct(int voffset, int x, int d) {
+        if(x != d) {
+            Nested(x);
+            slot(voffset);
+        }
+    }
+
+    /**
+     * Set the current vtable at `voffset` to the current location in the buffer.
+     *
+     * @param voffset The index into the vtable to store the offset relative to the end of the
+     * buffer.
+     */
+    public void slot(int voffset) {
+        vtable[voffset] = offset();
+    }
+
+   /**
+    * Finish off writing the object that is under construction.
+    *
+    * @return The offset to the object inside {@link #dataBuffer()}.
+    * @see #startTable(int)
+    */
+    public int endTable() {
+        if (vtable == null || !nested)
+            throw new AssertionError("FlatBuffers: endTable called without startTable");
+        addInt(0);
+        int vtableloc = offset();
+        // Write out the current vtable.
+        int i = vtable_in_use - 1;
+        // Trim trailing zeroes.
+        for (; i >= 0 && vtable[i] == 0; i--) {}
+        int trimmed_size = i + 1;
+        for (; i >= 0 ; i--) {
+            // Offset relative to the start of the table.
+            short off = (short)(vtable[i] != 0 ? vtableloc - vtable[i] : 0);
+            addShort(off);
+        }
+
+        final int standard_fields = 2; // The fields below:
+        addShort((short)(vtableloc - object_start));
+        addShort((short)((trimmed_size + standard_fields) * SIZEOF_SHORT));
+
+        // Search for an existing vtable that matches the current one.
+        int existing_vtable = 0;
+        outer_loop:
+        for (i = 0; i < num_vtables; i++) {
+            int vt1 = bb.capacity() - vtables[i];
+            int vt2 = space;
+            short len = bb.getShort(vt1);
+            if (len == bb.getShort(vt2)) {
+                for (int j = SIZEOF_SHORT; j < len; j += SIZEOF_SHORT) {
+                    if (bb.getShort(vt1 + j) != bb.getShort(vt2 + j)) {
+                        continue outer_loop;
+                    }
+                }
+                existing_vtable = vtables[i];
+                break outer_loop;
+            }
+        }
+
+        if (existing_vtable != 0) {
+            // Found a match:
+            // Remove the current vtable.
+            space = bb.capacity() - vtableloc;
+            // Point table to existing vtable.
+            bb.putInt(space, existing_vtable - vtableloc);
+        } else {
+            // No match:
+            // Add the location of the current vtable to the list of vtables.
+            if (num_vtables == vtables.length) vtables = Arrays.copyOf(vtables, num_vtables * 2);
+            vtables[num_vtables++] = offset();
+            // Point table to current vtable.
+            bb.putInt(bb.capacity() - vtableloc, offset() - vtableloc);
+        }
+
+        nested = false;
+        return vtableloc;
+    }
+
+    /**
+     * Checks that a required field has been set in a given table that has
+     * just been constructed.
+     *
+     * @param table The offset to the start of the table from the `ByteBuffer` capacity.
+     * @param field The offset to the field in the vtable.
+     */
+    public void required(int table, int field) {
+        int table_start = bb.capacity() - table;
+        int vtable_start = table_start - bb.getInt(table_start);
+        boolean ok = bb.getShort(vtable_start + field) != 0;
+        // If this fails, the caller will show what field needs to be set.
+        if (!ok)
+            throw new AssertionError("FlatBuffers: field " + field + " must be set");
+    }
+    /// @endcond
+
+    /**
+     * Finalize a buffer, pointing to the given `root_table`.
+     *
+     * @param root_table An offset to be added to the buffer.
+     * @param size_prefix Whether to prefix the size to the buffer.
+     */
+    protected void finish(int root_table, boolean size_prefix) {
+        prep(minalign, SIZEOF_INT + (size_prefix ? SIZEOF_INT : 0));
+        addOffset(root_table);
+        if (size_prefix) {
+            addInt(bb.capacity() - space);
+        }
+        bb.position(space);
+        finished = true;
+    }
+
+    /**
+     * Finalize a buffer, pointing to the given `root_table`.
+     *
+     * @param root_table An offset to be added to the buffer.
+     */
+    public void finish(int root_table) {
+        finish(root_table, false);
+    }
+
+    /**
+     * Finalize a buffer, pointing to the given `root_table`, with the size prefixed.
+     *
+     * @param root_table An offset to be added to the buffer.
+     */
+    public void finishSizePrefixed(int root_table) {
+        finish(root_table, true);
+    }
+
+    /**
+     * Finalize a buffer, pointing to the given `root_table`.
+     *
+     * @param root_table An offset to be added to the buffer.
+     * @param file_identifier A FlatBuffer file identifier to be added to the buffer before
+     * `root_table`.
+     * @param size_prefix Whether to prefix the size to the buffer.
+     */
+    protected void finish(int root_table, String file_identifier, boolean size_prefix) {
+        prep(minalign, SIZEOF_INT + FILE_IDENTIFIER_LENGTH + (size_prefix ? SIZEOF_INT : 0));
+        if (file_identifier.length() != FILE_IDENTIFIER_LENGTH)
+            throw new AssertionError("FlatBuffers: file identifier must be length " +
+                                     FILE_IDENTIFIER_LENGTH);
+        for (int i = FILE_IDENTIFIER_LENGTH - 1; i >= 0; i--) {
+            addByte((byte)file_identifier.charAt(i));
+        }
+        finish(root_table, size_prefix);
+    }
+
+    /**
+     * Finalize a buffer, pointing to the given `root_table`.
+     *
+     * @param root_table An offset to be added to the buffer.
+     * @param file_identifier A FlatBuffer file identifier to be added to the buffer before
+     * `root_table`.
+     */
+    public void finish(int root_table, String file_identifier) {
+        finish(root_table, file_identifier, false);
+    }
+
+    /**
+     * Finalize a buffer, pointing to the given `root_table`, with the size prefixed.
+     *
+     * @param root_table An offset to be added to the buffer.
+     * @param file_identifier A FlatBuffer file identifier to be added to the buffer before
+     * `root_table`.
+     */
+    public void finishSizePrefixed(int root_table, String file_identifier) {
+        finish(root_table, file_identifier, true);
+    }
+
+    /**
+     * In order to save space, fields that are set to their default value
+     * don't get serialized into the buffer. Forcing defaults provides a
+     * way to manually disable this optimization.
+     *
+     * @param forceDefaults When set to `true`, always serializes default values.
+     * @return Returns `this`.
+     */
+    public FlatBufferBuilder forceDefaults(boolean forceDefaults){
+        this.force_defaults = forceDefaults;
+        return this;
+    }
+
+    /**
+     * Get the ByteBuffer representing the FlatBuffer. Only call this after you've
+     * called `finish()`. The actual data starts at the ByteBuffer's current position,
+     * not necessarily at `0`.
+     *
+     * @return The {@link ByteBuffer} representing the FlatBuffer
+     */
+    public ByteBuffer dataBuffer() {
+        finished();
+        return bb;
+    }
+
+   /**
+    * The FlatBuffer data doesn't start at offset 0 in the {@link ByteBuffer}, but
+    * now the {@code ByteBuffer}'s position is set to that location upon {@link #finish(int)}.
+    *
+    * @return The {@link ByteBuffer#position() position} the data starts in {@link #dataBuffer()}
+    * @deprecated This method should not be needed anymore, but is left
+    * here for the moment to document this API change. It will be removed in the future.
+    */
+    @Deprecated
+    private int dataStart() {
+        finished();
+        return space;
+    }
+
+   /**
+    * A utility function to copy and return the ByteBuffer data from `start` to
+    * `start` + `length` as a `byte[]`.
+    *
+    * @param start Start copying at this offset.
+    * @param length How many bytes to copy.
+    * @return A range copy of the {@link #dataBuffer() data buffer}.
+    * @throws IndexOutOfBoundsException If the range of bytes is ouf of bound.
+    */
+    public byte[] sizedByteArray(int start, int length){
+        finished();
+        byte[] array = new byte[length];
+        bb.position(start);
+        bb.get(array);
+        return array;
+    }
+
+   /**
+    * A utility function to copy and return the ByteBuffer data as a `byte[]`.
+    *
+    * @return A full copy of the {@link #dataBuffer() data buffer}.
+    */
+    public byte[] sizedByteArray() {
+        return sizedByteArray(space, bb.capacity() - space);
+    }
+
+    /**
+     * A utility function to return an InputStream to the ByteBuffer data
+     *
+     * @return An InputStream that starts at the beginning of the ByteBuffer data
+     *         and can read to the end of it.
+     */
+    public InputStream sizedInputStream() {
+        finished();
+        ByteBuffer duplicate = bb.duplicate();
+        duplicate.position(space);
+        duplicate.limit(bb.capacity());
+        return new ByteBufferBackedInputStream(duplicate);
+    }
+
+    /**
+     * A class that allows a user to create an InputStream from a ByteBuffer.
+     */
+    static class ByteBufferBackedInputStream extends InputStream {
+
+        ByteBuffer buf;
+
+        public ByteBufferBackedInputStream(ByteBuffer buf) {
+            this.buf = buf;
+        }
+
+        public int read() throws IOException {
+            try {
+                return buf.get() & 0xFF;
+            } catch(BufferUnderflowException e) {
+                return -1;
+            }
+        }
+    }
+
+}
+
+/// @}
diff --git a/java/com/google/flatbuffers/Struct.java b/java/com/google/flatbuffers/Struct.java
new file mode 100644
index 0000000..c92164f
--- /dev/null
+++ b/java/com/google/flatbuffers/Struct.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.flatbuffers;
+
+import java.nio.ByteBuffer;
+
+/// @cond FLATBUFFERS_INTERNAL
+
+/**
+ * All structs in the generated code derive from this class, and add their own accessors.
+ */
+public class Struct {
+  /** Used to hold the position of the `bb` buffer. */
+  protected int bb_pos;
+  /** The underlying ByteBuffer to hold the data of the Struct. */
+  protected ByteBuffer bb;
+
+  /**
+   * Re-init the internal state with an external buffer {@code ByteBuffer} and an offset within.
+   *
+   * This method exists primarily to allow recycling Table instances without risking memory leaks
+   * due to {@code ByteBuffer} references.
+   */
+  protected void __reset(int _i, ByteBuffer _bb) { 
+    bb = _bb;
+    if (bb != null) {
+      bb_pos = _i;
+    } else {
+      bb_pos = 0;
+    }
+  }
+
+  /**
+   * Resets internal state with a null {@code ByteBuffer} and a zero position.
+   *
+   * This method exists primarily to allow recycling Struct instances without risking memory leaks
+   * due to {@code ByteBuffer} references. The instance will be unusable until it is assigned
+   * again to a {@code ByteBuffer}.
+   *
+   * @param struct the instance to reset to initial state
+   */
+  public void __reset() {
+    __reset(0, null);
+  }
+}
+
+/// @endcond
diff --git a/java/com/google/flatbuffers/Table.java b/java/com/google/flatbuffers/Table.java
new file mode 100644
index 0000000..ff069f2
--- /dev/null
+++ b/java/com/google/flatbuffers/Table.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.flatbuffers;
+
+import static com.google.flatbuffers.Constants.*;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.Charset;
+
+/// @cond FLATBUFFERS_INTERNAL
+
+/**
+ * All tables in the generated code derive from this class, and add their own accessors.
+ */
+public class Table {
+  public final static ThreadLocal<Charset> UTF8_CHARSET = new ThreadLocal<Charset>() {
+    @Override
+    protected Charset initialValue() {
+      return Charset.forName("UTF-8");
+    }
+  };
+  /** Used to hold the position of the `bb` buffer. */
+  protected int bb_pos;
+  /** The underlying ByteBuffer to hold the data of the Table. */
+  protected ByteBuffer bb;
+  /** Used to hold the vtable position. */
+  private int vtable_start;
+  /** Used to hold the vtable size. */
+  private int vtable_size;
+  Utf8 utf8 = Utf8.getDefault();
+
+  /**
+   * Get the underlying ByteBuffer.
+   *
+   * @return Returns the Table's ByteBuffer.
+   */
+  public ByteBuffer getByteBuffer() { return bb; }
+
+  /**
+   * Look up a field in the vtable.
+   *
+   * @param vtable_offset An `int` offset to the vtable in the Table's ByteBuffer.
+   * @return Returns an offset into the object, or `0` if the field is not present.
+   */
+  protected int __offset(int vtable_offset) {
+    return vtable_offset < vtable_size ? bb.getShort(vtable_start + vtable_offset) : 0;
+  }
+
+  protected static int __offset(int vtable_offset, int offset, ByteBuffer bb) {
+    int vtable = bb.capacity() - offset;
+    return bb.getShort(vtable + vtable_offset - bb.getInt(vtable)) + vtable;
+  }
+
+  /**
+   * Retrieve a relative offset.
+   *
+   * @param offset An `int` index into the Table's ByteBuffer containing the relative offset.
+   * @return Returns the relative offset stored at `offset`.
+   */
+  protected int __indirect(int offset) {
+    return offset + bb.getInt(offset);
+  }
+
+  protected static int __indirect(int offset, ByteBuffer bb) {
+    return offset + bb.getInt(offset);
+  }
+
+  /**
+   * Create a Java `String` from UTF-8 data stored inside the FlatBuffer.
+   *
+   * This allocates a new string and converts to wide chars upon each access,
+   * which is not very efficient. Instead, each FlatBuffer string also comes with an
+   * accessor based on __vector_as_bytebuffer below, which is much more efficient,
+   * assuming your Java program can handle UTF-8 data directly.
+   *
+   * @param offset An `int` index into the Table's ByteBuffer.
+   * @return Returns a `String` from the data stored inside the FlatBuffer at `offset`.
+   */
+  protected String __string(int offset) {
+    offset += bb.getInt(offset);
+    int length = bb.getInt(offset);
+    return utf8.decodeUtf8(bb, offset + SIZEOF_INT, length);
+  }
+
+  /**
+   * Get the length of a vector.
+   *
+   * @param offset An `int` index into the Table's ByteBuffer.
+   * @return Returns the length of the vector whose offset is stored at `offset`.
+   */
+  protected int __vector_len(int offset) {
+    offset += bb_pos;
+    offset += bb.getInt(offset);
+    return bb.getInt(offset);
+  }
+
+  /**
+   * Get the start data of a vector.
+   *
+   * @param offset An `int` index into the Table's ByteBuffer.
+   * @return Returns the start of the vector data whose offset is stored at `offset`.
+   */
+  protected int __vector(int offset) {
+    offset += bb_pos;
+    return offset + bb.getInt(offset) + SIZEOF_INT;  // data starts after the length
+  }
+
+  /**
+   * Get a whole vector as a ByteBuffer.
+   *
+   * This is efficient, since it only allocates a new {@link ByteBuffer} object,
+   * but does not actually copy the data, it still refers to the same bytes
+   * as the original ByteBuffer. Also useful with nested FlatBuffers, etc.
+   *
+   * @param vector_offset The position of the vector in the byte buffer
+   * @param elem_size The size of each element in the array
+   * @return The {@link ByteBuffer} for the array
+   */
+  protected ByteBuffer __vector_as_bytebuffer(int vector_offset, int elem_size) {
+    int o = __offset(vector_offset);
+    if (o == 0) return null;
+    ByteBuffer bb = this.bb.duplicate().order(ByteOrder.LITTLE_ENDIAN);
+    int vectorstart = __vector(o);
+    bb.position(vectorstart);
+    bb.limit(vectorstart + __vector_len(o) * elem_size);
+    return bb;
+  }
+
+  /**
+   * Initialize vector as a ByteBuffer.
+   *
+   * This is more efficient than using duplicate, since it doesn't copy the data
+   * nor allocattes a new {@link ByteBuffer}, creating no garbage to be collected.
+   *
+   * @param bb The {@link ByteBuffer} for the array
+   * @param vector_offset The position of the vector in the byte buffer
+   * @param elem_size The size of each element in the array
+   * @return The {@link ByteBuffer} for the array
+   */
+  protected ByteBuffer __vector_in_bytebuffer(ByteBuffer bb, int vector_offset, int elem_size) {
+    int o = this.__offset(vector_offset);
+    if (o == 0) return null;
+    int vectorstart = __vector(o);
+    bb.rewind();
+    bb.limit(vectorstart + __vector_len(o) * elem_size);
+    bb.position(vectorstart);
+    return bb;
+  }
+
+  /**
+   * Initialize any Table-derived type to point to the union at the given `offset`.
+   *
+   * @param t A `Table`-derived type that should point to the union at `offset`.
+   * @param offset An `int` index into the Table's ByteBuffer.
+   * @return Returns the Table that points to the union at `offset`.
+   */
+  protected Table __union(Table t, int offset) {
+    offset += bb_pos;
+    t.bb_pos = offset + bb.getInt(offset);
+    t.bb = bb;
+    t.vtable_start = t.bb_pos - bb.getInt(t.bb_pos);
+    t.vtable_size = bb.getShort(t.vtable_start);
+    return t;
+  }
+
+  /**
+   * Check if a {@link ByteBuffer} contains a file identifier.
+   *
+   * @param bb A {@code ByteBuffer} to check if it contains the identifier
+   * `ident`.
+   * @param ident A `String` identifier of the FlatBuffer file.
+   * @return True if the buffer contains the file identifier
+   */
+  protected static boolean __has_identifier(ByteBuffer bb, String ident) {
+    if (ident.length() != FILE_IDENTIFIER_LENGTH)
+        throw new AssertionError("FlatBuffers: file identifier must be length " +
+                                 FILE_IDENTIFIER_LENGTH);
+    for (int i = 0; i < FILE_IDENTIFIER_LENGTH; i++) {
+      if (ident.charAt(i) != (char)bb.get(bb.position() + SIZEOF_INT + i)) return false;
+    }
+    return true;
+  }
+
+  /**
+   * Sort tables by the key.
+   *
+   * @param offsets An 'int' indexes of the tables into the bb.
+   * @param bb A {@code ByteBuffer} to get the tables.
+   */
+  protected void sortTables(int[] offsets, final ByteBuffer bb) {
+    Integer[] off = new Integer[offsets.length];
+    for (int i = 0; i < offsets.length; i++) off[i] = offsets[i];
+    java.util.Arrays.sort(off, new java.util.Comparator<Integer>() {
+      public int compare(Integer o1, Integer o2) {
+        return keysCompare(o1, o2, bb);
+      }
+    });
+    for (int i = 0; i < offsets.length; i++) offsets[i] = off[i];
+  }
+
+  /**
+   * Compare two tables by the key.
+   *
+   * @param o1 An 'Integer' index of the first key into the bb.
+   * @param o2 An 'Integer' index of the second key into the bb.
+   * @param bb A {@code ByteBuffer} to get the keys.
+   */
+  protected int keysCompare(Integer o1, Integer o2, ByteBuffer bb) { return 0; }
+
+  /**
+   * Compare two strings in the buffer.
+   *
+   * @param offset_1 An 'int' index of the first string into the bb.
+   * @param offset_2 An 'int' index of the second string into the bb.
+   * @param bb A {@code ByteBuffer} to get the strings.
+   */
+  protected static int compareStrings(int offset_1, int offset_2, ByteBuffer bb) {
+    offset_1 += bb.getInt(offset_1);
+    offset_2 += bb.getInt(offset_2);
+    int len_1 = bb.getInt(offset_1);
+    int len_2 = bb.getInt(offset_2);
+    int startPos_1 = offset_1 + SIZEOF_INT;
+    int startPos_2 = offset_2 + SIZEOF_INT;
+    int len = Math.min(len_1, len_2);
+    for(int i = 0; i < len; i++) {
+      if (bb.get(i + startPos_1) != bb.get(i + startPos_2))
+        return bb.get(i + startPos_1) - bb.get(i + startPos_2);
+    }
+    return len_1 - len_2;
+  }
+
+  /**
+   * Compare string from the buffer with the 'String' object.
+   *
+   * @param offset_1 An 'int' index of the first string into the bb.
+   * @param key Second string as a byte array.
+   * @param bb A {@code ByteBuffer} to get the first string.
+   */
+  protected static int compareStrings(int offset_1, byte[] key, ByteBuffer bb) {
+    offset_1 += bb.getInt(offset_1);
+    int len_1 = bb.getInt(offset_1);
+    int len_2 = key.length;
+    int startPos_1 = offset_1 + Constants.SIZEOF_INT;
+    int len = Math.min(len_1, len_2);
+    for (int i = 0; i < len; i++) {
+      if (bb.get(i + startPos_1) != key[i])
+        return bb.get(i + startPos_1) - key[i];
+    }
+    return len_1 - len_2;
+  }
+
+  /**
+   * Re-init the internal state with an external buffer {@code ByteBuffer} and an offset within.
+   *
+   * This method exists primarily to allow recycling Table instances without risking memory leaks
+   * due to {@code ByteBuffer} references.
+   */
+  protected void __reset(int _i, ByteBuffer _bb) { 
+    bb = _bb;
+    if (bb != null) {
+      bb_pos = _i;
+      vtable_start = bb_pos - bb.getInt(bb_pos);
+      vtable_size = bb.getShort(vtable_start);
+    } else {
+      bb_pos = 0;
+      vtable_start = 0;
+      vtable_size = 0;
+    }
+  }
+
+  /**
+   * Resets the internal state with a null {@code ByteBuffer} and a zero position.
+   *
+   * This method exists primarily to allow recycling Table instances without risking memory leaks
+   * due to {@code ByteBuffer} references. The instance will be unusable until it is assigned
+   * again to a {@code ByteBuffer}.
+   */
+  public void __reset() {
+    __reset(0, null);
+  }
+}
+
+/// @endcond
diff --git a/java/com/google/flatbuffers/Utf8.java b/java/com/google/flatbuffers/Utf8.java
new file mode 100644
index 0000000..efb6811
--- /dev/null
+++ b/java/com/google/flatbuffers/Utf8.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.flatbuffers;
+
+import java.nio.ByteBuffer;
+
+import static java.lang.Character.MIN_HIGH_SURROGATE;
+import static java.lang.Character.MIN_LOW_SURROGATE;
+import static java.lang.Character.MIN_SUPPLEMENTARY_CODE_POINT;
+
+public abstract class Utf8 {
+
+  /**
+   * Returns the number of bytes in the UTF-8-encoded form of {@code sequence}. For a string,
+   * this method is equivalent to {@code string.getBytes(UTF_8).length}, but is more efficient in
+   * both time and space.
+   *
+   * @throws IllegalArgumentException if {@code sequence} contains ill-formed UTF-16 (unpaired
+   *     surrogates)
+   */
+  public abstract int encodedLength(CharSequence sequence);
+
+  /**
+   * Encodes the given characters to the target {@link ByteBuffer} using UTF-8 encoding.
+   *
+   * <p>Selects an optimal algorithm based on the type of {@link ByteBuffer} (i.e. heap or direct)
+   * and the capabilities of the platform.
+   *
+   * @param in the source string to be encoded
+   * @param out the target buffer to receive the encoded string.
+   */
+  public abstract void encodeUtf8(CharSequence in, ByteBuffer out);
+
+  /**
+   * Decodes the given UTF-8 portion of the {@link ByteBuffer} into a {@link String}.
+   *
+   * @throws IllegalArgumentException if the input is not valid UTF-8.
+   */
+  public abstract String decodeUtf8(ByteBuffer buffer, int offset, int length);
+
+  private static Utf8 DEFAULT;
+
+  /**
+   * Get the default UTF-8 processor.
+   * @return the default processor
+   */
+  public static Utf8 getDefault() {
+    if (DEFAULT == null) {
+      DEFAULT = new Utf8Safe();
+    }
+    return DEFAULT;
+  }
+
+  /**
+   * Set the default instance of the UTF-8 processor.
+   * @param instance the new instance to use
+   */
+  public static void setDefault(Utf8 instance) {
+    DEFAULT = instance;
+  }
+
+  /**
+   * Utility methods for decoding bytes into {@link String}. Callers are responsible for extracting
+   * bytes (possibly using Unsafe methods), and checking remaining bytes. All other UTF-8 validity
+   * checks and codepoint conversion happen in this class.
+   */
+  static class DecodeUtil {
+
+    /**
+     * Returns whether this is a single-byte codepoint (i.e., ASCII) with the form '0XXXXXXX'.
+     */
+    static boolean isOneByte(byte b) {
+      return b >= 0;
+    }
+
+    /**
+     * Returns whether this is a two-byte codepoint with the form '10XXXXXX'.
+     */
+    static boolean isTwoBytes(byte b) {
+      return b < (byte) 0xE0;
+    }
+
+    /**
+     * Returns whether this is a three-byte codepoint with the form '110XXXXX'.
+     */
+    static boolean isThreeBytes(byte b) {
+      return b < (byte) 0xF0;
+    }
+
+    static void handleOneByte(byte byte1, char[] resultArr, int resultPos) {
+      resultArr[resultPos] = (char) byte1;
+    }
+
+    static void handleTwoBytes(
+        byte byte1, byte byte2, char[] resultArr, int resultPos)
+        throws IllegalArgumentException {
+      // Simultaneously checks for illegal trailing-byte in leading position (<= '11000000') and
+      // overlong 2-byte, '11000001'.
+      if (byte1 < (byte) 0xC2) {
+        throw new IllegalArgumentException("Invalid UTF-8: Illegal leading byte in 2 bytes utf");
+      }
+      if (isNotTrailingByte(byte2)) {
+        throw new IllegalArgumentException("Invalid UTF-8: Illegal trailing byte in 2 bytes utf");
+      }
+      resultArr[resultPos] = (char) (((byte1 & 0x1F) << 6) | trailingByteValue(byte2));
+    }
+
+    static void handleThreeBytes(
+        byte byte1, byte byte2, byte byte3, char[] resultArr, int resultPos)
+        throws IllegalArgumentException {
+      if (isNotTrailingByte(byte2)
+              // overlong? 5 most significant bits must not all be zero
+              || (byte1 == (byte) 0xE0 && byte2 < (byte) 0xA0)
+              // check for illegal surrogate codepoints
+              || (byte1 == (byte) 0xED && byte2 >= (byte) 0xA0)
+              || isNotTrailingByte(byte3)) {
+        throw new IllegalArgumentException("Invalid UTF-8");
+      }
+      resultArr[resultPos] = (char)
+                                 (((byte1 & 0x0F) << 12) | (trailingByteValue(byte2) << 6) | trailingByteValue(byte3));
+    }
+
+    static void handleFourBytes(
+        byte byte1, byte byte2, byte byte3, byte byte4, char[] resultArr, int resultPos)
+        throws IllegalArgumentException{
+      if (isNotTrailingByte(byte2)
+              // Check that 1 <= plane <= 16.  Tricky optimized form of:
+              //   valid 4-byte leading byte?
+              // if (byte1 > (byte) 0xF4 ||
+              //   overlong? 4 most significant bits must not all be zero
+              //     byte1 == (byte) 0xF0 && byte2 < (byte) 0x90 ||
+              //   codepoint larger than the highest code point (U+10FFFF)?
+              //     byte1 == (byte) 0xF4 && byte2 > (byte) 0x8F)
+              || (((byte1 << 28) + (byte2 - (byte) 0x90)) >> 30) != 0
+              || isNotTrailingByte(byte3)
+              || isNotTrailingByte(byte4)) {
+        throw new IllegalArgumentException("Invalid UTF-8");
+      }
+      int codepoint = ((byte1 & 0x07) << 18)
+                          | (trailingByteValue(byte2) << 12)
+                          | (trailingByteValue(byte3) << 6)
+                          | trailingByteValue(byte4);
+      resultArr[resultPos] = DecodeUtil.highSurrogate(codepoint);
+      resultArr[resultPos + 1] = DecodeUtil.lowSurrogate(codepoint);
+    }
+
+    /**
+     * Returns whether the byte is not a valid continuation of the form '10XXXXXX'.
+     */
+    private static boolean isNotTrailingByte(byte b) {
+      return b > (byte) 0xBF;
+    }
+
+    /**
+     * Returns the actual value of the trailing byte (removes the prefix '10') for composition.
+     */
+    private static int trailingByteValue(byte b) {
+      return b & 0x3F;
+    }
+
+    private static char highSurrogate(int codePoint) {
+      return (char) ((MIN_HIGH_SURROGATE - (MIN_SUPPLEMENTARY_CODE_POINT >>> 10))
+                         + (codePoint >>> 10));
+    }
+
+    private static char lowSurrogate(int codePoint) {
+      return (char) (MIN_LOW_SURROGATE + (codePoint & 0x3ff));
+    }
+  }
+
+  // These UTF-8 handling methods are copied from Guava's Utf8Unsafe class with a modification to throw
+  // a protocol buffer local exception. This exception is then caught in CodedOutputStream so it can
+  // fallback to more lenient behavior.
+  static class UnpairedSurrogateException extends IllegalArgumentException {
+    UnpairedSurrogateException(int index, int length) {
+      super("Unpaired surrogate at index " + index + " of " + length);
+    }
+  }
+}
diff --git a/java/com/google/flatbuffers/Utf8Old.java b/java/com/google/flatbuffers/Utf8Old.java
new file mode 100644
index 0000000..3dac714
--- /dev/null
+++ b/java/com/google/flatbuffers/Utf8Old.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.flatbuffers;
+
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CoderResult;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * This class implements the Utf8 API using the Java Utf8 encoder. Use
+ * Utf8.setDefault(new Utf8Old()); to use it.
+ */
+public class Utf8Old extends Utf8 {
+
+  private static class Cache {
+    final CharsetEncoder encoder;
+    final CharsetDecoder decoder;
+    CharSequence lastInput = null;
+    ByteBuffer lastOutput = null;
+
+    Cache() {
+      encoder = StandardCharsets.UTF_8.newEncoder();
+      decoder = StandardCharsets.UTF_8.newDecoder();
+    }
+  }
+
+  private static final ThreadLocal<Cache> CACHE =
+      ThreadLocal.withInitial(() -> new Cache());
+
+  // Play some games so that the old encoder doesn't pay twice for computing
+  // the length of the encoded string.
+
+  @Override
+  public int encodedLength(CharSequence in) {
+    final Cache cache = CACHE.get();
+    int estimated = (int) (in.length() * cache.encoder.maxBytesPerChar());
+    if (cache.lastOutput == null || cache.lastOutput.capacity() < estimated) {
+      cache.lastOutput = ByteBuffer.allocate(Math.max(128, estimated));
+    }
+    cache.lastOutput.clear();
+    cache.lastInput = in;
+    CharBuffer wrap = (in instanceof CharBuffer) ?
+                          (CharBuffer) in : CharBuffer.wrap(in);
+    CoderResult result = cache.encoder.encode(wrap, cache.lastOutput, true);
+    if (result.isError()) {
+      try {
+        result.throwException();
+      } catch (CharacterCodingException e) {
+        throw new IllegalArgumentException("bad character encoding", e);
+      }
+    }
+    cache.lastOutput.flip();
+    return cache.lastOutput.remaining();
+  }
+
+  @Override
+  public void encodeUtf8(CharSequence in, ByteBuffer out) {
+    final Cache cache = CACHE.get();
+    if (cache.lastInput != in) {
+      // Update the lastOutput to match our input, although flatbuffer should
+      // never take this branch.
+      encodedLength(in);
+    }
+    out.put(cache.lastOutput);
+  }
+
+  @Override
+  public String decodeUtf8(ByteBuffer buffer, int offset, int length) {
+    CharsetDecoder decoder = CACHE.get().decoder;
+    decoder.reset();
+    buffer = buffer.duplicate();
+    buffer.position(offset);
+    buffer.limit(offset + length);
+    try {
+      CharBuffer result = decoder.decode(buffer);
+      return result.toString();
+    } catch (CharacterCodingException e) {
+      throw new IllegalArgumentException("Bad encoding", e);
+    }
+  }
+}
diff --git a/java/com/google/flatbuffers/Utf8Safe.java b/java/com/google/flatbuffers/Utf8Safe.java
new file mode 100644
index 0000000..06ea420
--- /dev/null
+++ b/java/com/google/flatbuffers/Utf8Safe.java
@@ -0,0 +1,451 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.  All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package com.google.flatbuffers;
+
+import java.nio.ByteBuffer;
+import static java.lang.Character.MAX_SURROGATE;
+import static java.lang.Character.MIN_SUPPLEMENTARY_CODE_POINT;
+import static java.lang.Character.MIN_SURROGATE;
+import static java.lang.Character.isSurrogatePair;
+import static java.lang.Character.toCodePoint;
+
+/**
+ * A set of low-level, high-performance static utility methods related
+ * to the UTF-8 character encoding.  This class has no dependencies
+ * outside of the core JDK libraries.
+ *
+ * <p>There are several variants of UTF-8.  The one implemented by
+ * this class is the restricted definition of UTF-8 introduced in
+ * Unicode 3.1, which mandates the rejection of "overlong" byte
+ * sequences as well as rejection of 3-byte surrogate codepoint byte
+ * sequences.  Note that the UTF-8 decoder included in Oracle's JDK
+ * has been modified to also reject "overlong" byte sequences, but (as
+ * of 2011) still accepts 3-byte surrogate codepoint byte sequences.
+ *
+ * <p>The byte sequences considered valid by this class are exactly
+ * those that can be roundtrip converted to Strings and back to bytes
+ * using the UTF-8 charset, without loss: <pre> {@code
+ * Arrays.equals(bytes, new String(bytes, Internal.UTF_8).getBytes(Internal.UTF_8))
+ * }</pre>
+ *
+ * <p>See the Unicode Standard,</br>
+ * Table 3-6. <em>UTF-8 Bit Distribution</em>,</br>
+ * Table 3-7. <em>Well Formed UTF-8 Byte Sequences</em>.
+ */
+final public class Utf8Safe extends Utf8 {
+
+  /**
+   * Returns the number of bytes in the UTF-8-encoded form of {@code sequence}. For a string,
+   * this method is equivalent to {@code string.getBytes(UTF_8).length}, but is more efficient in
+   * both time and space.
+   *
+   * @throws IllegalArgumentException if {@code sequence} contains ill-formed UTF-16 (unpaired
+   *     surrogates)
+   */
+  private static int computeEncodedLength(CharSequence sequence) {
+    // Warning to maintainers: this implementation is highly optimized.
+    int utf16Length = sequence.length();
+    int utf8Length = utf16Length;
+    int i = 0;
+
+    // This loop optimizes for pure ASCII.
+    while (i < utf16Length && sequence.charAt(i) < 0x80) {
+      i++;
+    }
+
+    // This loop optimizes for chars less than 0x800.
+    for (; i < utf16Length; i++) {
+      char c = sequence.charAt(i);
+      if (c < 0x800) {
+        utf8Length += ((0x7f - c) >>> 31);  // branch free!
+      } else {
+        utf8Length += encodedLengthGeneral(sequence, i);
+        break;
+      }
+    }
+
+    if (utf8Length < utf16Length) {
+      // Necessary and sufficient condition for overflow because of maximum 3x expansion
+      throw new IllegalArgumentException("UTF-8 length does not fit in int: "
+                                             + (utf8Length + (1L << 32)));
+    }
+    return utf8Length;
+  }
+
+  private static int encodedLengthGeneral(CharSequence sequence, int start) {
+    int utf16Length = sequence.length();
+    int utf8Length = 0;
+    for (int i = start; i < utf16Length; i++) {
+      char c = sequence.charAt(i);
+      if (c < 0x800) {
+        utf8Length += (0x7f - c) >>> 31; // branch free!
+      } else {
+        utf8Length += 2;
+        // jdk7+: if (Character.isSurrogate(c)) {
+        if (Character.MIN_SURROGATE <= c && c <= Character.MAX_SURROGATE) {
+          // Check that we have a well-formed surrogate pair.
+          int cp = Character.codePointAt(sequence, i);
+          if (cp < MIN_SUPPLEMENTARY_CODE_POINT) {
+            throw new Utf8Safe.UnpairedSurrogateException(i, utf16Length);
+          }
+          i++;
+        }
+      }
+    }
+    return utf8Length;
+  }
+
+  private 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(
+          String.format("buffer length=%d, index=%d, size=%d", bytes.length, index, size));
+    }
+
+    int offset = index;
+    final int limit = offset + size;
+
+    // The longest possible resulting String is the same as the number of input bytes, when it is
+    // all ASCII. For other cases, this over-allocates and we will truncate in the end.
+    char[] resultArr = new char[size];
+    int resultPos = 0;
+
+    // Optimize for 100% ASCII (Hotspot loves small simple top-level loops like this).
+    // This simple loop stops when we encounter a byte >= 0x80 (i.e. non-ASCII).
+    while (offset < limit) {
+      byte b = bytes[offset];
+      if (!DecodeUtil.isOneByte(b)) {
+        break;
+      }
+      offset++;
+      DecodeUtil.handleOneByte(b, resultArr, resultPos++);
+    }
+
+    while (offset < limit) {
+      byte byte1 = bytes[offset++];
+      if (DecodeUtil.isOneByte(byte1)) {
+        DecodeUtil.handleOneByte(byte1, resultArr, resultPos++);
+        // It's common for there to be multiple ASCII characters in a run mixed in, so add an
+        // extra optimized loop to take care of these runs.
+        while (offset < limit) {
+          byte b = bytes[offset];
+          if (!DecodeUtil.isOneByte(b)) {
+            break;
+          }
+          offset++;
+          DecodeUtil.handleOneByte(b, resultArr, resultPos++);
+        }
+      } else if (DecodeUtil.isTwoBytes(byte1)) {
+        if (offset >= limit) {
+          throw new IllegalArgumentException("Invalid UTF-8");
+        }
+        DecodeUtil.handleTwoBytes(byte1, /* byte2 */ bytes[offset++], resultArr, resultPos++);
+      } else if (DecodeUtil.isThreeBytes(byte1)) {
+        if (offset >= limit - 1) {
+          throw new IllegalArgumentException("Invalid UTF-8");
+        }
+        DecodeUtil.handleThreeBytes(
+            byte1,
+            /* byte2 */ bytes[offset++],
+            /* byte3 */ bytes[offset++],
+            resultArr,
+            resultPos++);
+      } else {
+        if (offset >= limit - 2) {
+          throw new IllegalArgumentException("Invalid UTF-8");
+        }
+        DecodeUtil.handleFourBytes(
+            byte1,
+            /* byte2 */ bytes[offset++],
+            /* byte3 */ bytes[offset++],
+            /* byte4 */ bytes[offset++],
+            resultArr,
+            resultPos++);
+        // 4-byte case requires two chars.
+        resultPos++;
+      }
+    }
+
+    return new String(resultArr, 0, resultPos);
+  }
+
+  private 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) {
+      throw new ArrayIndexOutOfBoundsException(
+          String.format("buffer limit=%d, index=%d, limit=%d", buffer.limit(),
+              offset, length));
+    }
+
+    final int limit = offset + length;
+
+    // The longest possible resulting String is the same as the number of input bytes, when it is
+    // all ASCII. For other cases, this over-allocates and we will truncate in the end.
+    char[] resultArr = new char[length];
+    int resultPos = 0;
+
+    // Optimize for 100% ASCII (Hotspot loves small simple top-level loops like this).
+    // This simple loop stops when we encounter a byte >= 0x80 (i.e. non-ASCII).
+    while (offset < limit) {
+      byte b = buffer.get(offset);
+      if (!DecodeUtil.isOneByte(b)) {
+        break;
+      }
+      offset++;
+      DecodeUtil.handleOneByte(b, resultArr, resultPos++);
+    }
+
+    while (offset < limit) {
+      byte byte1 = buffer.get(offset++);
+      if (DecodeUtil.isOneByte(byte1)) {
+        DecodeUtil.handleOneByte(byte1, resultArr, resultPos++);
+        // It's common for there to be multiple ASCII characters in a run mixed in, so add an
+        // extra optimized loop to take care of these runs.
+        while (offset < limit) {
+          byte b = buffer.get(offset);
+          if (!DecodeUtil.isOneByte(b)) {
+            break;
+          }
+          offset++;
+          DecodeUtil.handleOneByte(b, resultArr, resultPos++);
+        }
+      } else if (DecodeUtil.isTwoBytes(byte1)) {
+        if (offset >= limit) {
+          throw new IllegalArgumentException("Invalid UTF-8");
+        }
+        DecodeUtil.handleTwoBytes(
+            byte1, /* byte2 */ buffer.get(offset++), resultArr, resultPos++);
+      } else if (DecodeUtil.isThreeBytes(byte1)) {
+        if (offset >= limit - 1) {
+          throw new IllegalArgumentException("Invalid UTF-8");
+        }
+        DecodeUtil.handleThreeBytes(
+            byte1,
+            /* byte2 */ buffer.get(offset++),
+            /* byte3 */ buffer.get(offset++),
+            resultArr,
+            resultPos++);
+      } else {
+        if (offset >= limit - 2) {
+          throw new IllegalArgumentException("Invalid UTF-8");
+        }
+        DecodeUtil.handleFourBytes(
+            byte1,
+            /* byte2 */ buffer.get(offset++),
+            /* byte3 */ buffer.get(offset++),
+            /* byte4 */ buffer.get(offset++),
+            resultArr,
+            resultPos++);
+        // 4-byte case requires two chars.
+        resultPos++;
+      }
+    }
+
+    return new String(resultArr, 0, resultPos);
+  }
+
+  @Override
+  public int encodedLength(CharSequence in) {
+    return computeEncodedLength(in);
+  }
+
+  /**
+   * Decodes the given UTF-8 portion of the {@link ByteBuffer} into a {@link String}.
+   *
+   * @throws IllegalArgumentException if the input is not valid UTF-8.
+   */
+  @Override
+  public String decodeUtf8(ByteBuffer buffer, int offset, int length)
+      throws IllegalArgumentException {
+    if (buffer.hasArray()) {
+      return decodeUtf8Array(buffer.array(), buffer.arrayOffset() + offset, length);
+    } else {
+      return decodeUtf8Buffer(buffer, offset, length);
+    }
+  }
+
+
+  private static void encodeUtf8Buffer(CharSequence in, ByteBuffer out) {
+    final int inLength = in.length();
+    int outIx = out.position();
+    int inIx = 0;
+
+    // Since ByteBuffer.putXXX() already checks boundaries for us, no need to explicitly check
+    // access. Assume the buffer is big enough and let it handle the out of bounds exception
+    // if it occurs.
+    try {
+      // Designed to take advantage of
+      // https://wikis.oracle.com/display/HotSpotInternals/RangeCheckElimination
+      for (char c; inIx < inLength && (c = in.charAt(inIx)) < 0x80; ++inIx) {
+        out.put(outIx + inIx, (byte) c);
+      }
+      if (inIx == inLength) {
+        // Successfully encoded the entire string.
+        out.position(outIx + inIx);
+        return;
+      }
+
+      outIx += inIx;
+      for (char c; inIx < inLength; ++inIx, ++outIx) {
+        c = in.charAt(inIx);
+        if (c < 0x80) {
+          // One byte (0xxx xxxx)
+          out.put(outIx, (byte) c);
+        } else if (c < 0x800) {
+          // Two bytes (110x xxxx 10xx xxxx)
+
+          // Benchmarks show put performs better than putShort here (for HotSpot).
+          out.put(outIx++, (byte) (0xC0 | (c >>> 6)));
+          out.put(outIx, (byte) (0x80 | (0x3F & c)));
+        } else if (c < MIN_SURROGATE || MAX_SURROGATE < c) {
+          // Three bytes (1110 xxxx 10xx xxxx 10xx xxxx)
+          // Maximum single-char code point is 0xFFFF, 16 bits.
+
+          // Benchmarks show put performs better than putShort here (for HotSpot).
+          out.put(outIx++, (byte) (0xE0 | (c >>> 12)));
+          out.put(outIx++, (byte) (0x80 | (0x3F & (c >>> 6))));
+          out.put(outIx, (byte) (0x80 | (0x3F & c)));
+        } else {
+          // Four bytes (1111 xxxx 10xx xxxx 10xx xxxx 10xx xxxx)
+
+          // Minimum code point represented by a surrogate pair is 0x10000, 17 bits, four UTF-8
+          // bytes
+          final char low;
+          if (inIx + 1 == inLength || !isSurrogatePair(c, (low = in.charAt(++inIx)))) {
+            throw new UnpairedSurrogateException(inIx, inLength);
+          }
+          // TODO(nathanmittler): Consider using putInt() to improve performance.
+          int codePoint = toCodePoint(c, low);
+          out.put(outIx++, (byte) ((0xF << 4) | (codePoint >>> 18)));
+          out.put(outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 12))));
+          out.put(outIx++, (byte) (0x80 | (0x3F & (codePoint >>> 6))));
+          out.put(outIx, (byte) (0x80 | (0x3F & codePoint)));
+        }
+      }
+
+      // Successfully encoded the entire string.
+      out.position(outIx);
+    } catch (IndexOutOfBoundsException e) {
+      // TODO(nathanmittler): Consider making the API throw IndexOutOfBoundsException instead.
+
+      // If we failed in the outer ASCII loop, outIx will not have been updated. In this case,
+      // use inIx to determine the bad write index.
+      int badWriteIndex = out.position() + Math.max(inIx, outIx - out.position() + 1);
+      throw new ArrayIndexOutOfBoundsException(
+          "Failed writing " + in.charAt(inIx) + " at index " + badWriteIndex);
+    }
+  }
+
+  private static int encodeUtf8Array(CharSequence in, byte[] out,
+                                     int offset, int length) {
+    int utf16Length = in.length();
+    int j = offset;
+    int i = 0;
+    int limit = offset + length;
+    // Designed to take advantage of
+    // https://wikis.oracle.com/display/HotSpotInternals/RangeCheckElimination
+    for (char c; i < utf16Length && i + j < limit && (c = in.charAt(i)) < 0x80; i++) {
+      out[j + i] = (byte) c;
+    }
+    if (i == utf16Length) {
+      return j + utf16Length;
+    }
+    j += i;
+    for (char c; i < utf16Length; i++) {
+      c = in.charAt(i);
+      if (c < 0x80 && j < limit) {
+        out[j++] = (byte) c;
+      } else if (c < 0x800 && j <= limit - 2) { // 11 bits, two UTF-8 bytes
+        out[j++] = (byte) ((0xF << 6) | (c >>> 6));
+        out[j++] = (byte) (0x80 | (0x3F & c));
+      } else if ((c < Character.MIN_SURROGATE || Character.MAX_SURROGATE < c) && j <= limit - 3) {
+        // Maximum single-char code point is 0xFFFF, 16 bits, three UTF-8 bytes
+        out[j++] = (byte) ((0xF << 5) | (c >>> 12));
+        out[j++] = (byte) (0x80 | (0x3F & (c >>> 6)));
+        out[j++] = (byte) (0x80 | (0x3F & c));
+      } else if (j <= limit - 4) {
+        // Minimum code point represented by a surrogate pair is 0x10000, 17 bits,
+        // four UTF-8 bytes
+        final char low;
+        if (i + 1 == in.length()
+                || !Character.isSurrogatePair(c, (low = in.charAt(++i)))) {
+          throw new UnpairedSurrogateException((i - 1), utf16Length);
+        }
+        int codePoint = Character.toCodePoint(c, low);
+        out[j++] = (byte) ((0xF << 4) | (codePoint >>> 18));
+        out[j++] = (byte) (0x80 | (0x3F & (codePoint >>> 12)));
+        out[j++] = (byte) (0x80 | (0x3F & (codePoint >>> 6)));
+        out[j++] = (byte) (0x80 | (0x3F & codePoint));
+      } else {
+        // If we are surrogates and we're not a surrogate pair, always throw an
+        // UnpairedSurrogateException instead of an ArrayOutOfBoundsException.
+        if ((Character.MIN_SURROGATE <= c && c <= Character.MAX_SURROGATE)
+                && (i + 1 == in.length()
+                        || !Character.isSurrogatePair(c, in.charAt(i + 1)))) {
+          throw new UnpairedSurrogateException(i, utf16Length);
+        }
+        throw new ArrayIndexOutOfBoundsException("Failed writing " + c + " at index " + j);
+      }
+    }
+    return j;
+  }
+
+  /**
+   * Encodes the given characters to the target {@link ByteBuffer} using UTF-8 encoding.
+   *
+   * <p>Selects an optimal algorithm based on the type of {@link ByteBuffer} (i.e. heap or direct)
+   * and the capabilities of the platform.
+   *
+   * @param in the source string to be encoded
+   * @param out the target buffer to receive the encoded string.
+   */
+  @Override
+  public void encodeUtf8(CharSequence in, ByteBuffer out) {
+    if (out.hasArray()) {
+      int start = out.arrayOffset();
+      int end = encodeUtf8Array(in, out.array(), start + out.position(),
+          out.remaining());
+      out.position(end - start);
+    } else {
+      encodeUtf8Buffer(in, out);
+    }
+  }
+
+  // These UTF-8 handling methods are copied from Guava's Utf8Unsafe class with
+  // a modification to throw a local exception. This exception can be caught
+  // to fallback to more lenient behavior.
+  static class UnpairedSurrogateException extends IllegalArgumentException {
+    UnpairedSurrogateException(int index, int length) {
+      super("Unpaired surrogate at index " + index + " of " + length);
+    }
+  }
+}