forked from fernomac/ion-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
reader.go
388 lines (332 loc) · 11.7 KB
/
reader.go
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
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
package ion
import (
"bufio"
"bytes"
"io"
"math"
"math/big"
"strings"
"time"
)
// A Reader reads a stream of Ion values.
//
// The Reader has a logical position within the stream of values, influencing the
// values returnedd from its methods. Initially, the Reader is positioned before the
// first value in the stream. A call to Next advances the Reader to the first value
// in the stream, with subsequent calls advancing to subsequent values. When a call to
// Next moves the Reader to the position after the final value in the stream, it returns
// false, making it easy to loop through the values in a stream.
//
// var r Reader
// for r.Next() {
// // ...
// }
//
// Next also returns false in case of error. This can be distinguished from a legitimate
// end-of-stream by calling Err after exiting the loop.
//
// When positioned on an Ion value, the type of the value can be retrieved by calling
// Type. If it has an associated field name (inside a struct) or annotations, they can
// be read by calling FieldName and Annotations respectively.
//
// For atomic values, an appropriate XxxValue method can be called to read the value.
// For lists, sexps, and structs, you should instead call StepIn to move the Reader in
// to the contained sequence of values. The Reader will initially be positioned before
// the first value in the container. Calling Next without calling StepIn will skip over
// the composite value and return the next value in the outer value stream.
//
// At any point while reading through a composite value, including when Next returns false
// to indicate the end of the contained values, you may call StepOut to move back to the
// outer sequence of values. The Reader will be positioned at the end of the composite value,
// such that a call to Next will move to the immediately-following value (if any).
//
// r := NewTextReaderStr("[foo, bar] [baz]")
// for r.Next() {
// if err := r.StepIn(); err != nil {
// return err
// }
// for r.Next() {
// fmt.Println(r.StringValue())
// }
// if err := r.StepOut(); err != nil {
// return err
// }
// }
// if err := r.Err(); err != nil {
// return err
// }
//
type Reader interface {
// SymbolTable returns the current symbol table, or nil if there isn't one.
// Text Readers do not, generally speaking, have an associated symbol table.
// Binary Readers do.
SymbolTable() SymbolTable
// Next advances the Reader to the next position in the current value stream.
// It returns true if this is the position of an Ion value, and false if it
// is not. On error, it returns false and sets Err.
Next() bool
// Err returns an error if a previous call call to Next has failed.
Err() error
// Type returns the type of the Ion value the Reader is currently positioned on.
// It returns NoType if the Reader is positioned before or after a value.
Type() Type
// IsNull returns true if the current value is an explicit null. This may be true
// even if the Type is not NullType (for example, null.struct has type Struct). Yes,
// that's a bit confusing.
IsNull() bool
// FieldName returns the field name associated with the current value. It returns
// the empty string if there is no current value or the current value has no field
// name.
FieldName() string
// Annotations returns the set of annotations associated with the current value.
// It returns nil if there is no current value or the current value has no annotations.
Annotations() []string
// StepIn steps in to the current value if it is a container. It returns an error if there
// is no current value or if the value is not a container. On success, the Reader is
// positioned before the first value in the container.
StepIn() error
// StepOut steps out of the current container value being read. It returns an error if
// this Reader is not currently stepped in to a container. On success, the Reader is
// positioned after the end of the container, but before any subsequent values in the
// stream.
StepOut() error
// BoolValue returns the current value as a boolean (if that makes sense). It returns
// an error if the current value is not an Ion bool.
BoolValue() (bool, error)
// IntSize returns the size of integer needed to losslessly represent the current value
// (if that makes sense). It returns an error if the current value is not an Ion int.
IntSize() (IntSize, error)
// IntValue returns the current value as a 32-bit integer (if that makes sense). It
// returns an error if the current value is not an Ion integer or requires more than
// 32 bits to represent losslessly.
IntValue() (int, error)
// Int64Value returns the current value as a 64-bit integer (if that makes sense). It
// returns an error if the current value is not an Ion integer or requires more than
// 64 bits to represent losslessly.
Int64Value() (int64, error)
// Uint64Value returns the current value as an unsigned 64-bit integer (if that makes
// sense). It returns an error if the current value is not an Ion integer, is negative,
// or requires more than 64 bits to represent losslessly.
Uint64Value() (uint64, error)
// BigIntValue returns the current value as a big.Integer (if that makes sense). It
// returns an error if the current value is not an Ion integer.
BigIntValue() (*big.Int, error)
// FloatValue returns the current value as a 64-bit floating point number (if that
// makes sense). It returns an error if the current value is not an Ion float.
FloatValue() (float64, error)
// DecimalValue returns the current value as an arbitrary-precision Decimal (if that
// makes sense). It returns an error if the current value is not an Ion decimal.
DecimalValue() (*Decimal, error)
// TimeValue returns the current value as a timestamp (if that makes sense). It returns
// an error if the current value is not an Ion timestamp.
TimeValue() (time.Time, error)
// StringValue returns the current value as a string (if that makes sense). It returns
// an error if the current value is not an Ion symbol or an Ion string.
StringValue() (string, error)
// ByteValue returns the current value as a byte slice (if that makes sense). It returns
// an error if the current value is not an Ion clob or an Ion blob.
ByteValue() ([]byte, error)
}
// NewReader creates a new Ion reader of the appropriate type by peeking
// at the first several bytes of input for a binary version marker.
func NewReader(in io.Reader) Reader {
return NewReaderCat(in, nil)
}
// NewReaderStr creates a new reader from a string.
func NewReaderStr(str string) Reader {
return NewReader(strings.NewReader(str))
}
// NewReaderBytes creates a new reader for the given bytes.
func NewReaderBytes(in []byte) Reader {
return NewReader(bytes.NewReader(in))
}
// NewReaderCat creates a new reader with the given catalog.
func NewReaderCat(in io.Reader, cat Catalog) Reader {
br := bufio.NewReader(in)
bs, err := br.Peek(4)
if err == nil && bs[0] == 0xE0 && bs[3] == 0xEA {
return newBinaryReaderBuf(br, cat)
}
return newTextReaderBuf(br)
}
// A reader holds common implementation stuff to both the text and binary readers.
type reader struct {
ctx ctxstack
eof bool
err error
fieldName string
annotations []string
valueType Type
value interface{}
}
// Err returns the current error.
func (r *reader) Err() error {
return r.err
}
// Type returns the current value's type.
func (r *reader) Type() Type {
return r.valueType
}
// IsNull returns true if the current value is null.
func (r *reader) IsNull() bool {
return r.valueType != NoType && r.value == nil
}
// FieldName returns the current value's field name.
func (r *reader) FieldName() string {
return r.fieldName
}
// Annotations returns the current value's annotations.
func (r *reader) Annotations() []string {
return r.annotations
}
// BoolValue returns the current value as a bool.
func (r *reader) BoolValue() (bool, error) {
if r.valueType != BoolType {
return false, &UsageError{"Reader.BoolValue", "value is not a bool"}
}
if r.value == nil {
return false, nil
}
return r.value.(bool), nil
}
// IntSize returns the size of the current int value.
func (r *reader) IntSize() (IntSize, error) {
if r.valueType != IntType {
return NullInt, &UsageError{"Reader.IntSize", "value is not a int"}
}
if r.value == nil {
return NullInt, nil
}
if i, ok := r.value.(int64); ok {
if i > math.MaxInt32 || i < math.MinInt32 {
return Int64, nil
}
return Int32, nil
}
i := r.value.(*big.Int)
if i.IsUint64() {
return Uint64, nil
}
return BigInt, nil
}
// IntValue returns the current value as an int.
func (r *reader) IntValue() (int, error) {
i, err := r.Int64Value()
if err != nil {
return 0, err
}
if i > math.MaxInt32 || i < math.MinInt32 {
return 0, &UsageError{"Reader.IntValue", "value too large for an int32"}
}
return int(i), nil
}
// Int64Value returns the current value as an int64.
func (r *reader) Int64Value() (int64, error) {
if r.valueType != IntType {
return 0, &UsageError{"Reader.Int64Value", "value is not an int"}
}
if r.value == nil {
return 0, nil
}
if i, ok := r.value.(int64); ok {
return i, nil
}
bi := r.value.(*big.Int)
if bi.IsInt64() {
return bi.Int64(), nil
}
return 0, &UsageError{"Reader.Int64Value", "value too large for an int64"}
}
// Uint64Value returns the current value as a uint64.
func (r *reader) Uint64Value() (uint64, error) {
if r.valueType != IntType {
return 0, &UsageError{"Reader.Uint64Value", "value is not an int"}
}
if r.value == nil {
return 0, nil
}
if i, ok := r.value.(int64); ok {
if i >= 0 {
return uint64(i), nil
}
return 0, &UsageError{"Reader.Uint64Value", "value is negative"}
}
bi := r.value.(*big.Int)
if bi.Sign() < 0 {
return 0, &UsageError{"Reader.Uint64Value", "value is negative"}
}
if !bi.IsUint64() {
return 0, &UsageError{"Reader.Uint64Value", "value too large for a uint64"}
}
return bi.Uint64(), nil
}
// BigIntValue returns the current value as a big int.
func (r *reader) BigIntValue() (*big.Int, error) {
if r.valueType != IntType {
return nil, &UsageError{"Reader.BigIntValue", "value is not an int"}
}
if r.value == nil {
return nil, nil
}
if i, ok := r.value.(int64); ok {
return big.NewInt(i), nil
}
return r.value.(*big.Int), nil
}
// FloatValue returns the current value as a float.
func (r *reader) FloatValue() (float64, error) {
if r.valueType != FloatType {
return 0, &UsageError{"Reader.FloatValue", "value is not a float"}
}
if r.value == nil {
return 0.0, nil
}
return r.value.(float64), nil
}
// DecimalValue returns the current value as a Decimal.
func (r *reader) DecimalValue() (*Decimal, error) {
if r.valueType != DecimalType {
return nil, &UsageError{"Reader.DecimalValue", "value is not a decimal"}
}
if r.value == nil {
return nil, nil
}
return r.value.(*Decimal), nil
}
// TimeValue returns the current value as a time.
func (r *reader) TimeValue() (time.Time, error) {
if r.valueType != TimestampType {
return time.Time{}, &UsageError{"Reader.TimestampValue", "value is not a timestamp"}
}
if r.value == nil {
return time.Time{}, nil
}
return r.value.(time.Time), nil
}
// StringValue returns the current value as a string.
func (r *reader) StringValue() (string, error) {
if r.valueType != StringType && r.valueType != SymbolType {
return "", &UsageError{"Reader.StringValue", "value is not a string"}
}
if r.value == nil {
return "", nil
}
return r.value.(string), nil
}
// ByteValue returns the current value as a byte slice.
func (r *reader) ByteValue() ([]byte, error) {
if r.valueType != BlobType && r.valueType != ClobType {
return nil, &UsageError{"Reader.ByteValue", "value is not a lob"}
}
if r.value == nil {
return nil, nil
}
return r.value.([]byte), nil
}
// Clear clears the current value from the reader.
func (r *reader) clear() {
r.fieldName = ""
r.annotations = nil
r.valueType = NoType
r.value = nil
}