xref: /aosp_15_r20/external/flatbuffers/ts/flexbuffers/reference.ts (revision 890232f25432b36107d06881e0a25aaa6b473652)
1*890232f2SAndroid Build Coastguard Workerimport { fromByteWidth } from './bit-width-util.js'
2*890232f2SAndroid Build Coastguard Workerimport { ValueType } from './value-type.js'
3*890232f2SAndroid Build Coastguard Workerimport { isNumber, isIndirectNumber, isAVector, fixedTypedVectorElementSize, isFixedTypedVector, isTypedVector, typedVectorElementType, packedType, fixedTypedVectorElementType } from './value-type-util.js'
4*890232f2SAndroid Build Coastguard Workerimport { indirect, keyForIndex, keyIndex, readFloat, readInt, readUInt } from './reference-util.js'
5*890232f2SAndroid Build Coastguard Workerimport { fromUTF8Array } from './flexbuffers-util.js';
6*890232f2SAndroid Build Coastguard Workerimport { BitWidth } from './bit-width.js';
7*890232f2SAndroid Build Coastguard Worker
8*890232f2SAndroid Build Coastguard Workerexport function toReference(buffer: ArrayBuffer): Reference {
9*890232f2SAndroid Build Coastguard Worker  const len = buffer.byteLength;
10*890232f2SAndroid Build Coastguard Worker
11*890232f2SAndroid Build Coastguard Worker  if (len < 3) {
12*890232f2SAndroid Build Coastguard Worker    throw "Buffer needs to be bigger than 3";
13*890232f2SAndroid Build Coastguard Worker  }
14*890232f2SAndroid Build Coastguard Worker
15*890232f2SAndroid Build Coastguard Worker  const dataView = new DataView(buffer);
16*890232f2SAndroid Build Coastguard Worker  const byteWidth = dataView.getUint8(len - 1);
17*890232f2SAndroid Build Coastguard Worker  const packedType = dataView.getUint8(len - 2);
18*890232f2SAndroid Build Coastguard Worker  const parentWidth = fromByteWidth(byteWidth);
19*890232f2SAndroid Build Coastguard Worker  const offset = len - byteWidth - 2;
20*890232f2SAndroid Build Coastguard Worker
21*890232f2SAndroid Build Coastguard Worker  return new Reference(dataView, offset, parentWidth, packedType, "/")
22*890232f2SAndroid Build Coastguard Worker}
23*890232f2SAndroid Build Coastguard Worker
24*890232f2SAndroid Build Coastguard Workerfunction valueForIndexWithKey(index: number, key: string, dataView: DataView, offset: number, parentWidth: number, byteWidth: number, length: number, path: string): Reference {
25*890232f2SAndroid Build Coastguard Worker  const _indirect = indirect(dataView, offset, parentWidth);
26*890232f2SAndroid Build Coastguard Worker  const elementOffset = _indirect + index * byteWidth;
27*890232f2SAndroid Build Coastguard Worker  const packedType = dataView.getUint8(_indirect + length * byteWidth + index);
28*890232f2SAndroid Build Coastguard Worker  return new Reference(dataView, elementOffset, fromByteWidth(byteWidth), packedType, `${path}/${key}`)
29*890232f2SAndroid Build Coastguard Worker}
30*890232f2SAndroid Build Coastguard Worker
31*890232f2SAndroid Build Coastguard Workerexport class Reference {
32*890232f2SAndroid Build Coastguard Worker  private readonly byteWidth: number
33*890232f2SAndroid Build Coastguard Worker  private readonly valueType: ValueType
34*890232f2SAndroid Build Coastguard Worker  private _length = -1
35*890232f2SAndroid Build Coastguard Worker  constructor(private dataView: DataView, private offset: number, private parentWidth: number, private packedType: ValueType, private path: string) {
36*890232f2SAndroid Build Coastguard Worker    this.byteWidth = 1 << (packedType & 3)
37*890232f2SAndroid Build Coastguard Worker    this.valueType = packedType >> 2
38*890232f2SAndroid Build Coastguard Worker  }
39*890232f2SAndroid Build Coastguard Worker
40*890232f2SAndroid Build Coastguard Worker  isNull(): boolean { return this.valueType === ValueType.NULL; }
41*890232f2SAndroid Build Coastguard Worker  isNumber(): boolean { return isNumber(this.valueType) || isIndirectNumber(this.valueType); }
42*890232f2SAndroid Build Coastguard Worker  isFloat(): boolean { return ValueType.FLOAT === this.valueType || ValueType.INDIRECT_FLOAT === this.valueType; }
43*890232f2SAndroid Build Coastguard Worker  isInt(): boolean { return this.isNumber() && !this.isFloat(); }
44*890232f2SAndroid Build Coastguard Worker  isString(): boolean { return ValueType.STRING === this.valueType || ValueType.KEY === this.valueType; }
45*890232f2SAndroid Build Coastguard Worker  isBool(): boolean { return ValueType.BOOL === this.valueType; }
46*890232f2SAndroid Build Coastguard Worker  isBlob(): boolean { return ValueType.BLOB === this.valueType; }
47*890232f2SAndroid Build Coastguard Worker  isVector(): boolean { return isAVector(this.valueType); }
48*890232f2SAndroid Build Coastguard Worker  isMap(): boolean { return ValueType.MAP === this.valueType; }
49*890232f2SAndroid Build Coastguard Worker
50*890232f2SAndroid Build Coastguard Worker  boolValue(): boolean | null {
51*890232f2SAndroid Build Coastguard Worker    if (this.isBool()) {
52*890232f2SAndroid Build Coastguard Worker      return readInt(this.dataView, this.offset, this.parentWidth) > 0;
53*890232f2SAndroid Build Coastguard Worker    }
54*890232f2SAndroid Build Coastguard Worker    return null;
55*890232f2SAndroid Build Coastguard Worker  }
56*890232f2SAndroid Build Coastguard Worker
57*890232f2SAndroid Build Coastguard Worker  intValue(): number | bigint | null {
58*890232f2SAndroid Build Coastguard Worker    if (this.valueType === ValueType.INT) {
59*890232f2SAndroid Build Coastguard Worker      return readInt(this.dataView, this.offset, this.parentWidth);
60*890232f2SAndroid Build Coastguard Worker    }
61*890232f2SAndroid Build Coastguard Worker    if (this.valueType === ValueType.UINT) {
62*890232f2SAndroid Build Coastguard Worker      return readUInt(this.dataView, this.offset, this.parentWidth);
63*890232f2SAndroid Build Coastguard Worker    }
64*890232f2SAndroid Build Coastguard Worker    if (this.valueType === ValueType.INDIRECT_INT) {
65*890232f2SAndroid Build Coastguard Worker      return readInt(this.dataView, indirect(this.dataView, this.offset, this.parentWidth), fromByteWidth(this.byteWidth));
66*890232f2SAndroid Build Coastguard Worker    }
67*890232f2SAndroid Build Coastguard Worker    if (this.valueType === ValueType.INDIRECT_UINT) {
68*890232f2SAndroid Build Coastguard Worker      return readUInt(this.dataView, indirect(this.dataView, this.offset, this.parentWidth), fromByteWidth(this.byteWidth));
69*890232f2SAndroid Build Coastguard Worker    }
70*890232f2SAndroid Build Coastguard Worker    return null;
71*890232f2SAndroid Build Coastguard Worker  }
72*890232f2SAndroid Build Coastguard Worker
73*890232f2SAndroid Build Coastguard Worker  floatValue(): number | null {
74*890232f2SAndroid Build Coastguard Worker    if (this.valueType === ValueType.FLOAT) {
75*890232f2SAndroid Build Coastguard Worker      return readFloat(this.dataView, this.offset, this.parentWidth);
76*890232f2SAndroid Build Coastguard Worker    }
77*890232f2SAndroid Build Coastguard Worker    if (this.valueType === ValueType.INDIRECT_FLOAT) {
78*890232f2SAndroid Build Coastguard Worker      return readFloat(this.dataView, indirect(this.dataView, this.offset, this.parentWidth), fromByteWidth(this.byteWidth));
79*890232f2SAndroid Build Coastguard Worker    }
80*890232f2SAndroid Build Coastguard Worker    return null;
81*890232f2SAndroid Build Coastguard Worker  }
82*890232f2SAndroid Build Coastguard Worker
83*890232f2SAndroid Build Coastguard Worker  numericValue(): number | bigint | null { return this.floatValue() || this.intValue()}
84*890232f2SAndroid Build Coastguard Worker
85*890232f2SAndroid Build Coastguard Worker  stringValue(): string | null {
86*890232f2SAndroid Build Coastguard Worker    if (this.valueType === ValueType.STRING || this.valueType === ValueType.KEY) {
87*890232f2SAndroid Build Coastguard Worker      const begin = indirect(this.dataView, this.offset, this.parentWidth);
88*890232f2SAndroid Build Coastguard Worker      return fromUTF8Array(new Uint8Array(this.dataView.buffer, begin, this.length()));
89*890232f2SAndroid Build Coastguard Worker    }
90*890232f2SAndroid Build Coastguard Worker    return null;
91*890232f2SAndroid Build Coastguard Worker  }
92*890232f2SAndroid Build Coastguard Worker
93*890232f2SAndroid Build Coastguard Worker  blobValue(): Uint8Array | null {
94*890232f2SAndroid Build Coastguard Worker    if (this.isBlob()) {
95*890232f2SAndroid Build Coastguard Worker      const begin = indirect(this.dataView, this.offset, this.parentWidth);
96*890232f2SAndroid Build Coastguard Worker      return new Uint8Array(this.dataView.buffer, begin, this.length());
97*890232f2SAndroid Build Coastguard Worker    }
98*890232f2SAndroid Build Coastguard Worker    return null;
99*890232f2SAndroid Build Coastguard Worker  }
100*890232f2SAndroid Build Coastguard Worker
101*890232f2SAndroid Build Coastguard Worker  get(key: number): Reference {
102*890232f2SAndroid Build Coastguard Worker    const length = this.length();
103*890232f2SAndroid Build Coastguard Worker    if (Number.isInteger(key) && isAVector(this.valueType)) {
104*890232f2SAndroid Build Coastguard Worker      if (key >= length || key < 0) {
105*890232f2SAndroid Build Coastguard Worker        throw `Key: [${key}] is not applicable on ${this.path} of ${this.valueType} length: ${length}`;
106*890232f2SAndroid Build Coastguard Worker      }
107*890232f2SAndroid Build Coastguard Worker      const _indirect = indirect(this.dataView, this.offset, this.parentWidth);
108*890232f2SAndroid Build Coastguard Worker      const elementOffset = _indirect + key * this.byteWidth;
109*890232f2SAndroid Build Coastguard Worker      let _packedType = this.dataView.getUint8(_indirect + length * this.byteWidth + key);
110*890232f2SAndroid Build Coastguard Worker      if (isTypedVector(this.valueType)) {
111*890232f2SAndroid Build Coastguard Worker        const _valueType = typedVectorElementType(this.valueType);
112*890232f2SAndroid Build Coastguard Worker        _packedType = packedType(_valueType, BitWidth.WIDTH8);
113*890232f2SAndroid Build Coastguard Worker      } else if (isFixedTypedVector(this.valueType)) {
114*890232f2SAndroid Build Coastguard Worker        const _valueType = fixedTypedVectorElementType(this.valueType);
115*890232f2SAndroid Build Coastguard Worker        _packedType = packedType(_valueType, BitWidth.WIDTH8);
116*890232f2SAndroid Build Coastguard Worker      }
117*890232f2SAndroid Build Coastguard Worker      return new Reference(this.dataView, elementOffset, fromByteWidth(this.byteWidth), _packedType, `${this.path}[${key}]`);
118*890232f2SAndroid Build Coastguard Worker    }
119*890232f2SAndroid Build Coastguard Worker    if (typeof key === 'string') {
120*890232f2SAndroid Build Coastguard Worker      const index = keyIndex(key, this.dataView, this.offset, this.parentWidth, this.byteWidth, length);
121*890232f2SAndroid Build Coastguard Worker      if (index !== null) {
122*890232f2SAndroid Build Coastguard Worker        return valueForIndexWithKey(index, key, this.dataView, this.offset, this.parentWidth, this.byteWidth, length, this.path)
123*890232f2SAndroid Build Coastguard Worker      }
124*890232f2SAndroid Build Coastguard Worker    }
125*890232f2SAndroid Build Coastguard Worker    throw `Key [${key}] is not applicable on ${this.path} of ${this.valueType}`;
126*890232f2SAndroid Build Coastguard Worker  }
127*890232f2SAndroid Build Coastguard Worker
128*890232f2SAndroid Build Coastguard Worker  length(): number {
129*890232f2SAndroid Build Coastguard Worker    let size;
130*890232f2SAndroid Build Coastguard Worker    if (this._length > -1) {
131*890232f2SAndroid Build Coastguard Worker      return this._length;
132*890232f2SAndroid Build Coastguard Worker    }
133*890232f2SAndroid Build Coastguard Worker    if (isFixedTypedVector(this.valueType)) {
134*890232f2SAndroid Build Coastguard Worker      this._length = fixedTypedVectorElementSize(this.valueType);
135*890232f2SAndroid Build Coastguard Worker    } else if (this.valueType === ValueType.BLOB
136*890232f2SAndroid Build Coastguard Worker      || this.valueType === ValueType.MAP
137*890232f2SAndroid Build Coastguard Worker      || isAVector(this.valueType)) {
138*890232f2SAndroid Build Coastguard Worker      this._length = readUInt(this.dataView, indirect(this.dataView, this.offset, this.parentWidth) - this.byteWidth, fromByteWidth(this.byteWidth)) as number
139*890232f2SAndroid Build Coastguard Worker    } else if (this.valueType === ValueType.NULL) {
140*890232f2SAndroid Build Coastguard Worker      this._length = 0;
141*890232f2SAndroid Build Coastguard Worker    } else if (this.valueType === ValueType.STRING) {
142*890232f2SAndroid Build Coastguard Worker      const _indirect = indirect(this.dataView, this.offset, this.parentWidth);
143*890232f2SAndroid Build Coastguard Worker      let sizeByteWidth = this.byteWidth;
144*890232f2SAndroid Build Coastguard Worker      size = readUInt(this.dataView, _indirect - sizeByteWidth, fromByteWidth(this.byteWidth));
145*890232f2SAndroid Build Coastguard Worker      while (this.dataView.getInt8(_indirect + (size as number)) !== 0) {
146*890232f2SAndroid Build Coastguard Worker        sizeByteWidth <<= 1;
147*890232f2SAndroid Build Coastguard Worker        size = readUInt(this.dataView, _indirect - sizeByteWidth, fromByteWidth(this.byteWidth));
148*890232f2SAndroid Build Coastguard Worker      }
149*890232f2SAndroid Build Coastguard Worker      this._length = size as number;
150*890232f2SAndroid Build Coastguard Worker    } else if (this.valueType === ValueType.KEY) {
151*890232f2SAndroid Build Coastguard Worker      const _indirect = indirect(this.dataView, this.offset, this.parentWidth);
152*890232f2SAndroid Build Coastguard Worker      size = 1;
153*890232f2SAndroid Build Coastguard Worker      while (this.dataView.getInt8(_indirect + size) !== 0) {
154*890232f2SAndroid Build Coastguard Worker        size++;
155*890232f2SAndroid Build Coastguard Worker      }
156*890232f2SAndroid Build Coastguard Worker      this._length = size;
157*890232f2SAndroid Build Coastguard Worker    } else {
158*890232f2SAndroid Build Coastguard Worker      this._length = 1;
159*890232f2SAndroid Build Coastguard Worker    }
160*890232f2SAndroid Build Coastguard Worker    return Number(this._length);
161*890232f2SAndroid Build Coastguard Worker  }
162*890232f2SAndroid Build Coastguard Worker
163*890232f2SAndroid Build Coastguard Worker  toObject(): unknown {
164*890232f2SAndroid Build Coastguard Worker    const length = this.length();
165*890232f2SAndroid Build Coastguard Worker    if (this.isVector()) {
166*890232f2SAndroid Build Coastguard Worker      const result = [];
167*890232f2SAndroid Build Coastguard Worker      for (let i = 0; i < length; i++) {
168*890232f2SAndroid Build Coastguard Worker        result.push(this.get(i).toObject());
169*890232f2SAndroid Build Coastguard Worker      }
170*890232f2SAndroid Build Coastguard Worker      return result;
171*890232f2SAndroid Build Coastguard Worker    }
172*890232f2SAndroid Build Coastguard Worker    if (this.isMap()) {
173*890232f2SAndroid Build Coastguard Worker      const result: Record<string, unknown> = {};
174*890232f2SAndroid Build Coastguard Worker      for (let i = 0; i < length; i++) {
175*890232f2SAndroid Build Coastguard Worker        const key = keyForIndex(i, this.dataView, this.offset, this.parentWidth, this.byteWidth);
176*890232f2SAndroid Build Coastguard Worker        result[key] = valueForIndexWithKey(i, key, this.dataView, this.offset, this.parentWidth, this.byteWidth, length, this.path).toObject();
177*890232f2SAndroid Build Coastguard Worker      }
178*890232f2SAndroid Build Coastguard Worker      return result;
179*890232f2SAndroid Build Coastguard Worker    }
180*890232f2SAndroid Build Coastguard Worker    if (this.isNull()) {
181*890232f2SAndroid Build Coastguard Worker      return null;
182*890232f2SAndroid Build Coastguard Worker    }
183*890232f2SAndroid Build Coastguard Worker    if (this.isBool()) {
184*890232f2SAndroid Build Coastguard Worker      return this.boolValue();
185*890232f2SAndroid Build Coastguard Worker    }
186*890232f2SAndroid Build Coastguard Worker    if (this.isNumber()) {
187*890232f2SAndroid Build Coastguard Worker      return this.numericValue();
188*890232f2SAndroid Build Coastguard Worker    }
189*890232f2SAndroid Build Coastguard Worker    return this.blobValue() || this.stringValue();
190*890232f2SAndroid Build Coastguard Worker  }
191*890232f2SAndroid Build Coastguard Worker}
192