1# Copyright 2014 Google Inc. All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15from . import encode
16from . import number_types as N
17
18
19class Table(object):
20    """Table wraps a byte slice and provides read access to its data.
21
22    The variable `Pos` indicates the root of the FlatBuffers object therein."""
23
24    __slots__ = ("Bytes", "Pos")
25
26    def __init__(self, buf, pos):
27        N.enforce_number(pos, N.UOffsetTFlags)
28
29        self.Bytes = buf
30        self.Pos = pos
31
32    def Offset(self, vtableOffset):
33        """Offset provides access into the Table's vtable.
34
35        Deprecated fields are ignored by checking the vtable's length."""
36
37        vtable = self.Pos - self.Get(N.SOffsetTFlags, self.Pos)
38        vtableEnd = self.Get(N.VOffsetTFlags, vtable)
39        if vtableOffset < vtableEnd:
40            return self.Get(N.VOffsetTFlags, vtable + vtableOffset)
41        return 0
42
43    def Indirect(self, off):
44        """Indirect retrieves the relative offset stored at `offset`."""
45        N.enforce_number(off, N.UOffsetTFlags)
46        return off + encode.Get(N.UOffsetTFlags.packer_type, self.Bytes, off)
47
48    def String(self, off):
49        """String gets a string from data stored inside the flatbuffer."""
50        N.enforce_number(off, N.UOffsetTFlags)
51        off += encode.Get(N.UOffsetTFlags.packer_type, self.Bytes, off)
52        start = off + N.UOffsetTFlags.bytewidth
53        length = encode.Get(N.UOffsetTFlags.packer_type, self.Bytes, off)
54        return bytes(self.Bytes[start:start+length])
55
56    def VectorLen(self, off):
57        """VectorLen retrieves the length of the vector whose offset is stored
58           at "off" in this object."""
59        N.enforce_number(off, N.UOffsetTFlags)
60
61        off += self.Pos
62        off += encode.Get(N.UOffsetTFlags.packer_type, self.Bytes, off)
63        ret = encode.Get(N.UOffsetTFlags.packer_type, self.Bytes, off)
64        return ret
65
66    def Vector(self, off):
67        """Vector retrieves the start of data of the vector whose offset is
68           stored at "off" in this object."""
69        N.enforce_number(off, N.UOffsetTFlags)
70
71        off += self.Pos
72        x = off + self.Get(N.UOffsetTFlags, off)
73        # data starts after metadata containing the vector length
74        x += N.UOffsetTFlags.bytewidth
75        return x
76
77    def Union(self, t2, off):
78        """Union initializes any Table-derived type to point to the union at
79           the given offset."""
80        assert type(t2) is Table
81        N.enforce_number(off, N.UOffsetTFlags)
82
83        off += self.Pos
84        t2.Pos = off + self.Get(N.UOffsetTFlags, off)
85        t2.Bytes = self.Bytes
86
87    def Get(self, flags, off):
88        """
89        Get retrieves a value of the type specified by `flags`  at the
90        given offset.
91        """
92        N.enforce_number(off, N.UOffsetTFlags)
93        return flags.py_type(encode.Get(flags.packer_type, self.Bytes, off))
94
95    def GetSlot(self, slot, d, validator_flags):
96        N.enforce_number(slot, N.VOffsetTFlags)
97        if validator_flags is not None:
98            N.enforce_number(d, validator_flags)
99        off = self.Offset(slot)
100        if off == 0:
101            return d
102        return self.Get(validator_flags, self.Pos + off)
103
104    def GetVOffsetTSlot(self, slot, d):
105        """
106        GetVOffsetTSlot retrieves the VOffsetT that the given vtable location
107        points to. If the vtable value is zero, the default value `d`
108        will be returned.
109        """
110
111        N.enforce_number(slot, N.VOffsetTFlags)
112        N.enforce_number(d, N.VOffsetTFlags)
113
114        off = self.Offset(slot)
115        if off == 0:
116                return d
117        return off
118