diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c74af84 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +# Created by .ignore support plugin (hsz.mobi) +*.class +.mtj.tmp/ +*.jar +*.war +*.ear +hs_err_pid* +.gradle +build/ +gradle-app.setting +!gradle-wrapper.jar +.gradletasknamecache +.idea \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..04d784b --- /dev/null +++ b/build.gradle @@ -0,0 +1,45 @@ +group 'com.github.xxxifan' +version '1.4.0' + +apply plugin: 'java' +apply plugin: 'maven' + +sourceCompatibility = 1.7 + +repositories { + mavenCentral() +} + +dependencies { + testCompile group: 'junit', name: 'junit', version: '4.11' +} + +task sourcesJar(type: Jar, dependsOn: classes) { + classifier = 'sources' + from sourceSets.main.allSource +} + +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir +} + +artifacts { + archives sourcesJar + archives javadocJar +} + +// To specify a license in the pom: +install { + repositories.mavenInstaller { + pom.project { + licenses { + license { + name 'The Apache Software License, Version 2.0' + url 'http://www.apache.org/licenses/LICENSE-2.0.txt' + distribution 'repo' + } + } + } + } +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..ca78035 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..4a23047 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sun Sep 11 22:41:25 CST 2016 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.13-bin.zip diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..27309d9 --- /dev/null +++ b/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..832fdb6 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..1fd56ce --- /dev/null +++ b/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'flatbuffers-java' + diff --git a/src/main/java/com/google/flatbuffers/Constants.java b/src/main/java/com/google/flatbuffers/Constants.java new file mode 100644 index 0000000..f590631 --- /dev/null +++ b/src/main/java/com/google/flatbuffers/Constants.java @@ -0,0 +1,42 @@ +/* + * 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; +} + +/// @endcond diff --git a/src/main/java/com/google/flatbuffers/FlatBufferBuilder.java b/src/main/java/com/google/flatbuffers/FlatBufferBuilder.java new file mode 100644 index 0000000..f2b261e --- /dev/null +++ b/src/main/java/com/google/flatbuffers/FlatBufferBuilder.java @@ -0,0 +1,781 @@ +/* + * 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.CharBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; +import java.util.Arrays; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.Charset; + +/// @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. + static final Charset utf8charset = Charset.forName("UTF-8"); // The UTF-8 character set used by FlatBuffers. + 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. + CharsetEncoder encoder = utf8charset.newEncoder(); + ByteBuffer dst; + /// @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. + */ + public FlatBufferBuilder(int initial_size) { + if (initial_size <= 0) initial_size = 1; + space = initial_size; + bb = newByteBuffer(initial_size); + } + + /** + * 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. + */ + public FlatBufferBuilder(ByteBuffer existing_bb) { + init(existing_bb); + } + + /** + * 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. + * @return Returns `this`. + */ + public FlatBufferBuilder init(ByteBuffer existing_bb){ + 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; + } + + /// @cond FLATBUFFERS_INTERNAL + /** + * Create a `ByteBuffer` with a given capacity. + * + * @param capacity The size of the `ByteBuffer` to allocate. + * @return Returns the new `ByteBuffer` that was allocated. + */ + static ByteBuffer newByteBuffer(int capacity) { + ByteBuffer newbb = ByteBuffer.allocate(capacity); + newbb.order(ByteOrder.LITTLE_ENDIAN); + return newbb; + } + + /** + * 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. + * @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) { + 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 << 1; + bb.position(0); + ByteBuffer nbb = 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 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(); + bb = growByteBuffer(bb); + 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. + *
+ * The expected sequence of calls is: + *
+ * For example, to create an array of strings, do: + *
{@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(); + * }+ * + * @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 + + /** + * 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 = s.length(); + int estimatedDstCapacity = (int) (length * encoder.maxBytesPerChar()); + if (dst == null || dst.capacity() < estimatedDstCapacity) { + dst = ByteBuffer.allocate(Math.max(128, estimatedDstCapacity)); + } + + dst.clear(); + + CharBuffer src = s instanceof CharBuffer ? (CharBuffer) s : + CharBuffer.wrap(s); + CoderResult result = encoder.encode(src, dst, true); + if (result.isError()) { + try { + result.throwException(); + } catch (CharacterCodingException x) { + throw new Error(x); + } + } + + dst.flip(); + return createString(dst); + } + + /** + * 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(); + } + + /// @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. + *
+ * For example, using the "Monster" code found on the "landing page". An + * object of type `Monster` can be created using the following code: + * + *
{@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); + * }+ *
+ * Here: + *
+ * 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 startObject(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 #startObject(int)
+ */
+ public int endObject() {
+ if (vtable == null || !nested)
+ throw new AssertionError("FlatBuffers: endObject called without startObject");
+ addInt(0);
+ int vtableloc = offset();
+ // Write out the current vtable.
+ for (int i = vtable_in_use - 1; 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)((vtable_in_use + standard_fields) * SIZEOF_SHORT));
+
+ // Search for an existing vtable that matches the current one.
+ int existing_vtable = 0;
+ outer_loop:
+ for (int 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.
+ */
+ public void finish(int root_table) {
+ prep(minalign, SIZEOF_INT);
+ addOffset(root_table);
+ 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.
+ * @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) {
+ prep(minalign, SIZEOF_INT + FILE_IDENTIFIER_LENGTH);
+ 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);
+ }
+
+ /**
+ * 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);
+ }
+}
+
+/// @}
diff --git a/src/main/java/com/google/flatbuffers/Struct.java b/src/main/java/com/google/flatbuffers/Struct.java
new file mode 100644
index 0000000..ae31553
--- /dev/null
+++ b/src/main/java/com/google/flatbuffers/Struct.java
@@ -0,0 +1,33 @@
+/*
+ * 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;
+}
+
+/// @endcond
diff --git a/src/main/java/com/google/flatbuffers/Table.java b/src/main/java/com/google/flatbuffers/Table.java
new file mode 100644
index 0000000..4087654
--- /dev/null
+++ b/src/main/java/com/google/flatbuffers/Table.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 static com.google.flatbuffers.Constants.*;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.CharBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CoderResult;
+
+/// @cond FLATBUFFERS_INTERNAL
+
+/**
+ * All tables in the generated code derive from this class, and add their own accessors.
+ */
+public class Table {
+ private final static ThreadLocal