-
Notifications
You must be signed in to change notification settings - Fork 48
/
Copy pathIonBinaryReader.ts
300 lines (272 loc) · 7.7 KB
/
IonBinaryReader.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
/*!
* Copyright 2012 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
// Binary reader. This is a user reader built on top of
// the IonParserBinaryRaw parser.
//
// Handles system symbols, and conversion from the parsed
// input byte array to the desired Javascript value (scalar or
// object, such as IonValue).
import IntSize from "./IntSize";
import { Catalog } from "./IonCatalog";
import { IVM } from "./IonConstants";
import { Decimal } from "./IonDecimal";
import {
defaultLocalSymbolTable,
LocalSymbolTable,
} from "./IonLocalSymbolTable";
import { ParserBinaryRaw } from "./IonParserBinaryRaw";
import { Reader } from "./IonReader";
import { BinarySpan } from "./IonSpan";
import { ion_symbol_table_sid, makeSymbolTable } from "./IonSymbols";
import { Timestamp } from "./IonTimestamp";
import { IonType } from "./IonType";
import { IonTypes } from "./IonTypes";
import { isSafeInteger } from "./util";
const BOC = -2; // beginning of container?
const EOF = -1;
const TB_NULL = 0;
const TB_BOOL = 1;
const TB_INT = 2;
const TB_NEG_INT = 3;
const TB_FLOAT = 4;
const TB_DECIMAL = 5;
const TB_TIMESTAMP = 6;
const TB_SYMBOL = 7;
const TB_STRING = 8;
const TB_CLOB = 9;
const TB_BLOB = 10;
const TB_LIST = 11;
const TB_SEXP = 12;
const TB_STRUCT = 13;
function get_ion_type(t: number): IonType | null {
switch (t) {
case TB_NULL:
return IonTypes.NULL;
case TB_BOOL:
return IonTypes.BOOL;
case TB_INT:
return IonTypes.INT;
case TB_NEG_INT:
return IonTypes.INT;
case TB_FLOAT:
return IonTypes.FLOAT;
case TB_DECIMAL:
return IonTypes.DECIMAL;
case TB_TIMESTAMP:
return IonTypes.TIMESTAMP;
case TB_SYMBOL:
return IonTypes.SYMBOL;
case TB_STRING:
return IonTypes.STRING;
case TB_CLOB:
return IonTypes.CLOB;
case TB_BLOB:
return IonTypes.BLOB;
case TB_LIST:
return IonTypes.LIST;
case TB_SEXP:
return IonTypes.SEXP;
case TB_STRUCT:
return IonTypes.STRUCT;
default:
return null;
}
}
export class BinaryReader implements Reader {
private _parser: ParserBinaryRaw;
private readonly _cat: Catalog;
private _symtab: LocalSymbolTable;
private _raw_type: number;
private _annotations: string[] | null = null;
constructor(source: BinarySpan, catalog?: Catalog) {
this._parser = new ParserBinaryRaw(source);
this._cat = catalog ? catalog : new Catalog();
this._symtab = defaultLocalSymbolTable();
this._raw_type = BOC;
}
position(): number {
return this._parser.source().position();
}
next(): IonType | null {
this._annotations = null;
if (this._raw_type === EOF) {
return null;
}
for (
this._raw_type = this._parser.next();
this.depth() === 0;
this._raw_type = this._parser.next()
) {
if (this._raw_type === TB_SYMBOL) {
const raw: number | null = this._parser._getSid();
if (raw !== IVM.sid) {
break;
}
this._symtab = defaultLocalSymbolTable();
} else if (this._raw_type === TB_STRUCT) {
if (!this._parser.hasAnnotations()) {
break;
}
if (this._parser.getAnnotation(0) !== ion_symbol_table_sid) {
break;
}
this._symtab = makeSymbolTable(this._cat, this, this._symtab);
} else {
break;
}
}
return get_ion_type(this._raw_type);
}
stepIn(): void {
if (!get_ion_type(this._raw_type)!.isContainer) {
throw new Error("Can't step in to a scalar value");
}
this._parser.stepIn();
this._raw_type = BOC;
}
stepOut(): void {
this._parser.stepOut();
this._raw_type = BOC;
}
type(): IonType | null {
return get_ion_type(this._raw_type);
}
depth(): number {
return this._parser.depth();
}
fieldName(): string | null {
return this.getSymbolString(this._parser.getFieldId());
}
hasAnnotations(): boolean {
return this._parser.hasAnnotations();
}
annotations(): string[] {
this._loadAnnotations();
return this._annotations !== null ? this._annotations : [];
}
getAnnotation(index: number): string {
this._loadAnnotations();
return this._annotations![index];
}
isNull(): boolean {
return this._raw_type === TB_NULL || this._parser.isNull();
}
uInt8ArrayValue(): Uint8Array | null {
return this._parser.uInt8ArrayValue();
}
booleanValue(): boolean | null {
return this._parser.booleanValue();
}
decimalValue(): Decimal | null {
return this._parser.decimalValue();
}
bigIntValue(): bigint | null {
return this._parser.bigIntValue();
}
intSize(): IntSize {
if (isSafeInteger(this.bigIntValue()!)) {
return IntSize.Number;
}
return IntSize.BigInt;
}
numberValue(): number | null {
return this._parser.numberValue();
}
stringValue(): string | null {
const t: BinaryReader = this;
const p = t._parser;
switch (get_ion_type(t._raw_type)) {
case IonTypes.NULL:
return null;
case IonTypes.STRING:
if (this.isNull()) {
return null;
}
return p.stringValue();
case IonTypes.SYMBOL:
if (this.isNull()) {
return null;
}
const sid = p._getSid();
if (sid !== null) {
return this.getSymbolString(sid);
}
}
throw new Error("Current value is not a string or symbol.");
}
timestampValue(): Timestamp | null {
return this._parser.timestampValue();
}
value(): any {
const type = this.type();
if (type && type.isContainer) {
if (this.isNull()) {
return null;
}
throw new Error(
"Unable to provide a value for " + type.name + " containers."
);
}
switch (type) {
case IonTypes.NULL:
return null;
case IonTypes.BLOB:
case IonTypes.CLOB:
return this.uInt8ArrayValue();
case IonTypes.BOOL:
return this.booleanValue();
case IonTypes.DECIMAL:
return this.decimalValue();
case IonTypes.INT:
return this.bigIntValue();
case IonTypes.FLOAT:
return this.numberValue();
case IonTypes.STRING:
case IonTypes.SYMBOL:
return this.stringValue();
case IonTypes.TIMESTAMP:
return this.timestampValue();
default:
throw new Error("There is no current value.");
}
}
private _loadAnnotations(): void {
if (this._annotations === null) {
this._annotations = [];
this._parser.getAnnotations().forEach((id) => {
this._annotations!.push(this.getSymbolString(id)!);
});
}
}
private getSymbolString(symbolId: number | null): string | null {
let s: string | null = null;
if (symbolId === null) {
return null;
}
if (symbolId > 0) {
s = this._symtab.getSymbolText(symbolId);
if (s === undefined) {
throw new Error("symbol is unresolvable");
// s = "$" + symbolId.toString();
// todo turn this back on once symbol table imports are supported and lst context transfer is supported.
}
} else if (symbolId === 0) {
throw new Error("Symbol ID zero is unsupported");
} else if (symbolId < 0) {
throw new Error("Negative symbol ID: " + symbolId + " is illegal.");
}
return s;
}
}