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