Home | History | Annotate | Download | only in flatbuffers
      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    */
     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    */
     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 
     70   protected static int __offset(int vtable_offset, int offset, ByteBuffer bb) {
     71     int vtable = bb.capacity() - 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    */
     81   protected int __indirect(int offset) {
     82     return offset + bb.getInt(offset);
     83   }
     84 
     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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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    */
    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     for(int i = 0; i < len; i++) {
    249       if (bb.get(i + startPos_1) != bb.get(i + startPos_2))
    250         return bb.get(i + startPos_1) - bb.get(i + startPos_2);
    251     }
    252     return len_1 - len_2;
    253   }
    254 
    255   /**
    256    * Compare string from the buffer with the 'String' object.
    257    *
    258    * @param offset_1 An 'int' index of the first string into the bb.
    259    * @param key Second string as a byte array.
    260    * @param bb A {@code ByteBuffer} to get the first string.
    261    */
    262   protected static int compareStrings(int offset_1, byte[] key, ByteBuffer bb) {
    263     offset_1 += bb.getInt(offset_1);
    264     int len_1 = bb.getInt(offset_1);
    265     int len_2 = key.length;
    266     int startPos_1 = offset_1 + Constants.SIZEOF_INT;
    267     int len = Math.min(len_1, len_2);
    268     for (int i = 0; i < len; i++) {
    269       if (bb.get(i + startPos_1) != key[i])
    270         return bb.get(i + startPos_1) - key[i];
    271     }
    272     return len_1 - len_2;
    273   }
    274 }
    275 
    276 /// @endcond
    277