1 /* 2 * Copyright 2014 Google Inc. All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.flatbuffers; 18 19 import static com.google.flatbuffers.Constants.*; 20 import java.nio.ByteBuffer; 21 import java.nio.ByteOrder; 22 import java.nio.CharBuffer; 23 import java.nio.charset.CharacterCodingException; 24 import java.nio.charset.Charset; 25 import java.nio.charset.CharsetDecoder; 26 import java.nio.charset.CoderResult; 27 28 /// @cond FLATBUFFERS_INTERNAL 29 30 /** 31 * All tables in the generated code derive from this class, and add their own accessors. 32 */ 33 public class Table { 34 private final static ThreadLocal<CharsetDecoder> UTF8_DECODER = new ThreadLocal<CharsetDecoder>() { 35 @Override 36 protected CharsetDecoder initialValue() { 37 return Charset.forName("UTF-8").newDecoder(); 38 } 39 }; 40 public final static ThreadLocal<Charset> UTF8_CHARSET = new ThreadLocal<Charset>() { 41 @Override 42 protected Charset initialValue() { 43 return Charset.forName("UTF-8"); 44 } 45 }; 46 private final static ThreadLocal<CharBuffer> CHAR_BUFFER = new ThreadLocal<CharBuffer>(); 47 /** Used to hold the position of the `bb` buffer. */ 48 protected int bb_pos; 49 /** The underlying ByteBuffer to hold the data of the Table. */ 50 protected ByteBuffer bb; 51 52 /** 53 * Get the underlying ByteBuffer. 54 * 55 * @return Returns the Table's ByteBuffer. 56 */ getByteBuffer()57 public ByteBuffer getByteBuffer() { return bb; } 58 59 /** 60 * Look up a field in the vtable. 61 * 62 * @param vtable_offset An `int` offset to the vtable in the Table's ByteBuffer. 63 * @return Returns an offset into the object, or `0` if the field is not present. 64 */ __offset(int vtable_offset)65 protected int __offset(int vtable_offset) { 66 int vtable = bb_pos - bb.getInt(bb_pos); 67 return vtable_offset < bb.getShort(vtable) ? bb.getShort(vtable + vtable_offset) : 0; 68 } 69 __offset(int vtable_offset, int offset, ByteBuffer bb)70 protected static int __offset(int vtable_offset, int offset, ByteBuffer bb) { 71 int vtable = bb.array().length - offset; 72 return bb.getShort(vtable + vtable_offset - bb.getInt(vtable)) + vtable; 73 } 74 75 /** 76 * Retrieve a relative offset. 77 * 78 * @param offset An `int` index into the Table's ByteBuffer containing the relative offset. 79 * @return Returns the relative offset stored at `offset`. 80 */ __indirect(int offset)81 protected int __indirect(int offset) { 82 return offset + bb.getInt(offset); 83 } 84 __indirect(int offset, ByteBuffer bb)85 protected static int __indirect(int offset, ByteBuffer bb) { 86 return offset + bb.getInt(offset); 87 } 88 89 /** 90 * Create a Java `String` from UTF-8 data stored inside the FlatBuffer. 91 * 92 * This allocates a new string and converts to wide chars upon each access, 93 * which is not very efficient. Instead, each FlatBuffer string also comes with an 94 * accessor based on __vector_as_bytebuffer below, which is much more efficient, 95 * assuming your Java program can handle UTF-8 data directly. 96 * 97 * @param offset An `int` index into the Table's ByteBuffer. 98 * @return Returns a `String` from the data stored inside the FlatBuffer at `offset`. 99 */ __string(int offset)100 protected String __string(int offset) { 101 CharsetDecoder decoder = UTF8_DECODER.get(); 102 decoder.reset(); 103 104 offset += bb.getInt(offset); 105 ByteBuffer src = bb.duplicate().order(ByteOrder.LITTLE_ENDIAN); 106 int length = src.getInt(offset); 107 src.position(offset + SIZEOF_INT); 108 src.limit(offset + SIZEOF_INT + length); 109 110 int required = (int)((float)length * decoder.maxCharsPerByte()); 111 CharBuffer dst = CHAR_BUFFER.get(); 112 if (dst == null || dst.capacity() < required) { 113 dst = CharBuffer.allocate(required); 114 CHAR_BUFFER.set(dst); 115 } 116 117 dst.clear(); 118 119 try { 120 CoderResult cr = decoder.decode(src, dst, true); 121 if (!cr.isUnderflow()) { 122 cr.throwException(); 123 } 124 } catch (CharacterCodingException x) { 125 throw new Error(x); 126 } 127 128 return dst.flip().toString(); 129 } 130 131 /** 132 * Get the length of a vector. 133 * 134 * @param offset An `int` index into the Table's ByteBuffer. 135 * @return Returns the length of the vector whose offset is stored at `offset`. 136 */ __vector_len(int offset)137 protected int __vector_len(int offset) { 138 offset += bb_pos; 139 offset += bb.getInt(offset); 140 return bb.getInt(offset); 141 } 142 143 /** 144 * Get the start data of a vector. 145 * 146 * @param offset An `int` index into the Table's ByteBuffer. 147 * @return Returns the start of the vector data whose offset is stored at `offset`. 148 */ __vector(int offset)149 protected int __vector(int offset) { 150 offset += bb_pos; 151 return offset + bb.getInt(offset) + SIZEOF_INT; // data starts after the length 152 } 153 154 /** 155 * Get a whole vector as a ByteBuffer. 156 * 157 * This is efficient, since it only allocates a new {@link ByteBuffer} object, 158 * but does not actually copy the data, it still refers to the same bytes 159 * as the original ByteBuffer. Also useful with nested FlatBuffers, etc. 160 * 161 * @param vector_offset The position of the vector in the byte buffer 162 * @param elem_size The size of each element in the array 163 * @return The {@link ByteBuffer} for the array 164 */ __vector_as_bytebuffer(int vector_offset, int elem_size)165 protected ByteBuffer __vector_as_bytebuffer(int vector_offset, int elem_size) { 166 int o = __offset(vector_offset); 167 if (o == 0) return null; 168 ByteBuffer bb = this.bb.duplicate().order(ByteOrder.LITTLE_ENDIAN); 169 int vectorstart = __vector(o); 170 bb.position(vectorstart); 171 bb.limit(vectorstart + __vector_len(o) * elem_size); 172 return bb; 173 } 174 175 /** 176 * Initialize any Table-derived type to point to the union at the given `offset`. 177 * 178 * @param t A `Table`-derived type that should point to the union at `offset`. 179 * @param offset An `int` index into the Table's ByteBuffer. 180 * @return Returns the Table that points to the union at `offset`. 181 */ __union(Table t, int offset)182 protected Table __union(Table t, int offset) { 183 offset += bb_pos; 184 t.bb_pos = offset + bb.getInt(offset); 185 t.bb = bb; 186 return t; 187 } 188 189 /** 190 * Check if a {@link ByteBuffer} contains a file identifier. 191 * 192 * @param bb A {@code ByteBuffer} to check if it contains the identifier 193 * `ident`. 194 * @param ident A `String` identifier of the FlatBuffer file. 195 * @return True if the buffer contains the file identifier 196 */ __has_identifier(ByteBuffer bb, String ident)197 protected static boolean __has_identifier(ByteBuffer bb, String ident) { 198 if (ident.length() != FILE_IDENTIFIER_LENGTH) 199 throw new AssertionError("FlatBuffers: file identifier must be length " + 200 FILE_IDENTIFIER_LENGTH); 201 for (int i = 0; i < FILE_IDENTIFIER_LENGTH; i++) { 202 if (ident.charAt(i) != (char)bb.get(bb.position() + SIZEOF_INT + i)) return false; 203 } 204 return true; 205 } 206 207 /** 208 * Sort tables by the key. 209 * 210 * @param offsets An 'int' indexes of the tables into the bb. 211 * @param bb A {@code ByteBuffer} to get the tables. 212 */ sortTables(int[] offsets, final ByteBuffer bb)213 protected void sortTables(int[] offsets, final ByteBuffer bb) { 214 Integer[] off = new Integer[offsets.length]; 215 for (int i = 0; i < offsets.length; i++) off[i] = offsets[i]; 216 java.util.Arrays.sort(off, new java.util.Comparator<Integer>() { 217 public int compare(Integer o1, Integer o2) { 218 return keysCompare(o1, o2, bb); 219 } 220 }); 221 for (int i = 0; i < offsets.length; i++) offsets[i] = off[i]; 222 } 223 224 /** 225 * Compare two tables by the key. 226 * 227 * @param o1 An 'Integer' index of the first key into the bb. 228 * @param o2 An 'Integer' index of the second key into the bb. 229 * @param bb A {@code ByteBuffer} to get the keys. 230 */ keysCompare(Integer o1, Integer o2, ByteBuffer bb)231 protected int keysCompare(Integer o1, Integer o2, ByteBuffer bb) { return 0; } 232 233 /** 234 * Compare two strings in the buffer. 235 * 236 * @param offset_1 An 'int' index of the first string into the bb. 237 * @param offset_2 An 'int' index of the second string into the bb. 238 * @param bb A {@code ByteBuffer} to get the strings. 239 */ compareStrings(int offset_1, int offset_2, ByteBuffer bb)240 protected static int compareStrings(int offset_1, int offset_2, ByteBuffer bb) { 241 offset_1 += bb.getInt(offset_1); 242 offset_2 += bb.getInt(offset_2); 243 int len_1 = bb.getInt(offset_1); 244 int len_2 = bb.getInt(offset_2); 245 int startPos_1 = offset_1 + SIZEOF_INT; 246 int startPos_2 = offset_2 + SIZEOF_INT; 247 int len = Math.min(len_1, len_2); 248 byte[] bbArray = bb.array(); 249 for(int i = 0; i < len; i++) { 250 if (bbArray[i + startPos_1] != bbArray[i + startPos_2]) 251 return bbArray[i + startPos_1] - bbArray[i + startPos_2]; 252 } 253 return len_1 - len_2; 254 } 255 256 /** 257 * Compare string from the buffer with the 'String' object. 258 * 259 * @param offset_1 An 'int' index of the first string into the bb. 260 * @param key Second string as a byte array. 261 * @param bb A {@code ByteBuffer} to get the first string. 262 */ compareStrings(int offset_1, byte[] key, ByteBuffer bb)263 protected static int compareStrings(int offset_1, byte[] key, ByteBuffer bb) { 264 offset_1 += bb.getInt(offset_1); 265 int len_1 = bb.getInt(offset_1); 266 int len_2 = key.length; 267 int startPos_1 = offset_1 + Constants.SIZEOF_INT; 268 int len = Math.min(len_1, len_2); 269 byte[] bbArray = bb.array(); 270 for (int i = 0; i < len; i++) { 271 if (bbArray[i + startPos_1] != key[i]) 272 return bbArray[i + startPos_1] - key[i]; 273 } 274 return len_1 - len_2; 275 } 276 } 277 278 /// @endcond 279