1 /++
2 +/
3 module mir.ion.stream;
4 
5 import mir.ion.exception;
6 import mir.ion.type_code;
7 import mir.ion.value;
8 import mir.utility: _expect;
9 
10 /++
11 Ion Value Stream
12 
13 Note: this implementation of value stream doesn't support shared symbol tables.
14 +/
15 struct IonValueStream
16 {
17     /// data view.
18     const(ubyte)[] data;
19 
20     private alias DG = int delegate(IonErrorCode error, scope const(char[])[] symbolTable, scope IonDescribedValue value) @safe pure nothrow @nogc;
21     private alias EDG = int delegate(scope const(char[])[] symbolTable, scope IonDescribedValue value) @safe pure @nogc;
22 
23 const:
24 
25     //
26     void toString(W)(scope ref W w) scope
27     {
28         import mir.ser.json: serializeJson;
29         return serializeJson(w, this);
30     }
31 
32     version (D_Exceptions)
33     {
34         /++
35         +/
36         @safe pure @nogc
37         scope int opApply(scope int delegate(scope const(char[])[] symbolTable, scope IonDescribedValue value) @safe pure @nogc dg)
38         {
39             return opApply((IonErrorCode error, scope const(char[])[] symbolTable, scope IonDescribedValue value) {
40                 if (_expect(error, false))
41                     throw error.ionException;
42                 return dg(symbolTable, value);
43             });
44         }
45 
46         /// ditto
47         @trusted @nogc
48         scope int opApply(scope int delegate(scope const(char[])[] symbolTable, scope IonDescribedValue value)
49         @safe @nogc dg) { return opApply(cast(EDG) dg); }
50 
51         /// ditto
52         @trusted pure
53         scope int opApply(scope int delegate(scope const(char[])[] symbolTable, scope IonDescribedValue value)
54         @safe pure dg) { return opApply(cast(EDG) dg); }
55 
56         /// ditto
57         @trusted
58         scope int opApply(scope int delegate(scope const(char[])[] symbolTable, scope IonDescribedValue value)
59         @safe dg) { return opApply(cast(EDG) dg); }
60 
61         /// ditto
62         @system pure @nogc
63         scope int opApply(scope int delegate(scope const(char[])[] symbolTable, scope IonDescribedValue value)
64         @system pure @nogc dg) { return opApply(cast(EDG) dg); }
65 
66         /// ditto
67         @system @nogc
68         scope int opApply(scope int delegate(scope const(char[])[] symbolTable, scope IonDescribedValue value)
69         @system @nogc dg) { return opApply(cast(EDG) dg); }
70 
71         /// ditto
72         @system pure
73         scope int opApply(scope int delegate(scope const(char[])[] symbolTable, scope IonDescribedValue value)
74         @system pure dg) { return opApply(cast(EDG) dg); }
75 
76         /// ditto
77         @system
78         scope int opApply(scope int delegate(scope const(char[])[] symbolTable, scope IonDescribedValue value)
79         @system dg) { return opApply(cast(EDG) dg); }
80     }
81 
82     /++
83     +/
84     @trusted pure nothrow @nogc
85     scope int opApply(scope int delegate(IonErrorCode error, scope const(char[])[] symbolTable, scope IonDescribedValue value) @safe pure nothrow @nogc dg)
86     {
87         import mir.appender: ScopedBuffer;
88         import mir.ion.symbol_table;
89 
90         ScopedBuffer!(const(char)[]) symbolTableBuffer = void;
91         symbolTableBuffer.initialize;
92 
93         const(ubyte)[] d = data;
94 
95         void resetSymbolTable()
96         {
97             symbolTableBuffer.reset;
98             symbolTableBuffer.put(IonSystemSymbolTable_v1);
99         }
100 
101         while (d.length)
102         {
103             IonErrorCode error;
104             IonVersionMarker versionMarker;
105             IonDescribedValue describedValue;
106             error = d.parseVersion(versionMarker);
107             if (!error)
108             {
109                 if (versionMarker != IonVersionMarker(1, 0))
110                 {
111                     error = IonErrorCode.unexpectedVersionMarker;
112                     goto C;
113                 }
114                 resetSymbolTable();
115                 continue;
116             }
117             describedValue = d.parseValue(error);
118             if (error == IonErrorCode.nop)
119                 continue;
120             // check if describedValue is symbol table
121             if (describedValue.descriptor.type == IonTypeCode.annotations)
122             {
123                 auto annotationWrapper = describedValue.get!IonAnnotationWrapper(error);
124                 IonAnnotations annotations = annotationWrapper.annotations;
125                 IonDescribedValue symbolTableValue = annotationWrapper.value;
126                 if (!error && !annotations.empty)
127                 {
128                     // check first annotation is $ion_symbol_table
129                     {
130                         bool nextAnnotation;
131                         foreach (IonErrorCode annotationError, size_t annotationId; annotations)
132                         {
133                             error = annotationError;
134                             if (error)
135                                 goto C;
136                             if (nextAnnotation)
137                                 continue;
138                             nextAnnotation = true;
139                             if (annotationId != IonSystemSymbol.ion_symbol_table)
140                                 goto C;
141                         }
142                     }
143                     IonStruct symbolTableStruct;
144                     if (symbolTableValue.descriptor.type != IonTypeCode.struct_)
145                     {
146                         error = IonErrorCode.expectedStructValue;
147                         goto C;
148                     }
149                     if (symbolTableValue != null)
150                     {
151                         symbolTableStruct = symbolTableValue.trustedGet!IonStruct;
152                     }
153 
154                     {
155                         bool preserveCurrentSymbols;
156                         IonList symbols;
157 
158                         foreach (IonErrorCode symbolTableError, size_t symbolTableKeyId, scope IonDescribedValue elementValue; symbolTableStruct)
159                         {
160                             error = symbolTableError;
161                             if (error)
162                                 goto C;
163                             switch (symbolTableKeyId)
164                             {
165                                 case IonSystemSymbol.imports:
166                                 {
167                                     if (preserveCurrentSymbols || (elementValue.descriptor.type != IonTypeCode.symbol && elementValue.descriptor.type != IonTypeCode.list))
168                                     {
169                                         error = IonErrorCode.invalidLocalSymbolTable;
170                                         goto C;
171                                     }
172                                     if (elementValue.descriptor.type == IonTypeCode.list)
173                                     {
174                                         error = IonErrorCode.sharedSymbolTablesAreUnsupported;
175                                         goto C;
176                                     }
177                                     size_t id;
178                                     error = elementValue.trustedGet!IonSymbolID.get(id);
179                                     if (error)
180                                         goto C;
181                                     if (id != IonSystemSymbol.ion_symbol_table)
182                                     {
183                                         error = IonErrorCode.invalidLocalSymbolTable;
184                                         goto C;
185                                     }
186                                     preserveCurrentSymbols = true;
187                                     break;
188                                 }
189                                 case IonSystemSymbol.symbols:
190                                 {
191                                     if (symbols != symbols.init || elementValue.descriptor.type != IonTypeCode.list)
192                                     {
193                                         error = IonErrorCode.invalidLocalSymbolTable;
194                                         goto C;
195                                     }
196                                     if (elementValue != null)
197                                     {
198                                         symbols = elementValue.trustedGet!IonList;
199                                     }
200                                     if (error)
201                                         goto C;
202                                     break;
203                                 }
204                                 default:
205                                 {
206                                     //CHECK: should other symbols be ignored?
207                                     continue;
208                                 }
209                             }
210                         }
211 
212                         if (!preserveCurrentSymbols)
213                         {
214                             resetSymbolTable();
215                         }
216 
217                         foreach (IonErrorCode symbolsError, scope IonDescribedValue symbolValue; symbols)
218                         {
219                             error = symbolsError;
220                             if (error)
221                                 goto C;
222                             auto symbol = symbolValue.get!(const(char)[])(error);
223                             if (error)
224                                 goto C;
225                             symbolTableBuffer.put(symbol);
226                         }
227                         continue;
228                     }
229                 }
230                 // TODO: continue work
231             }
232         C:
233             if (auto ret = dg(error, symbolTableBuffer.data, describedValue))
234                 return ret;
235         }
236         return 0;
237     }
238 
239     /// ditto
240     @trusted nothrow @nogc
241     scope int opApply(scope int delegate(IonErrorCode error, scope const(char[])[] symbolTable, scope IonDescribedValue value)
242     @safe nothrow @nogc dg) { return opApply(cast(DG) dg); }
243 
244     /// ditto
245     @trusted pure @nogc
246     scope int opApply(scope int delegate(IonErrorCode error, scope const(char[])[] symbolTable, scope IonDescribedValue value)
247     @safe pure @nogc dg) { return opApply(cast(DG) dg); }
248 
249     /// ditto
250     @trusted pure nothrow
251     scope int opApply(scope int delegate(IonErrorCode error, scope const(char[])[] symbolTable, scope IonDescribedValue value)
252     @safe pure nothrow dg) { return opApply(cast(DG) dg); }
253 
254     /// ditto
255     @trusted @nogc
256     scope int opApply(scope int delegate(IonErrorCode error, scope const(char[])[] symbolTable, scope IonDescribedValue value)
257     @safe @nogc dg) { return opApply(cast(DG) dg); }
258 
259     /// ditto
260     @trusted pure
261     scope int opApply(scope int delegate(IonErrorCode error, scope const(char[])[] symbolTable, scope IonDescribedValue value)
262     @safe pure dg) { return opApply(cast(DG) dg); }
263 
264     /// ditto
265     @trusted nothrow
266     scope int opApply(scope int delegate(IonErrorCode error, scope const(char[])[] symbolTable, scope IonDescribedValue value)
267     @safe nothrow dg) { return opApply(cast(DG) dg); }
268 
269     /// ditto
270     @trusted
271     scope int opApply(scope int delegate(IonErrorCode error, scope const(char[])[] symbolTable, scope IonDescribedValue value)
272     @safe dg) { return opApply(cast(DG) dg); }
273 
274     /// ditto
275     @system pure nothrow @nogc
276     scope int opApply(scope int delegate(IonErrorCode error, scope const(char[])[] symbolTable, scope IonDescribedValue value)
277     @system pure nothrow @nogc dg) { return opApply(cast(DG) dg); }
278 
279     /// ditto
280     @system nothrow @nogc
281     scope int opApply(scope int delegate(IonErrorCode error, scope const(char[])[] symbolTable, scope IonDescribedValue value)
282     @system nothrow @nogc dg) { return opApply(cast(DG) dg); }
283 
284     /// ditto
285     @system pure @nogc
286     scope int opApply(scope int delegate(IonErrorCode error, scope const(char[])[] symbolTable, scope IonDescribedValue value)
287     @system pure @nogc dg) { return opApply(cast(DG) dg); }
288 
289     /// ditto
290     @system pure nothrow
291     scope int opApply(scope int delegate(IonErrorCode error, scope const(char[])[] symbolTable, scope IonDescribedValue value)
292     @system pure nothrow dg) { return opApply(cast(DG) dg); }
293 
294     /// ditto
295     @system @nogc
296     scope int opApply(scope int delegate(IonErrorCode error, scope const(char[])[] symbolTable, scope IonDescribedValue value)
297     @system @nogc dg) { return opApply(cast(DG) dg); }
298 
299     /// ditto
300     @system pure
301     scope int opApply(scope int delegate(IonErrorCode error, scope const(char[])[] symbolTable, scope IonDescribedValue value)
302     @system pure dg) { return opApply(cast(DG) dg); }
303 
304     /// ditto
305     @system nothrow
306     scope int opApply(scope int delegate(IonErrorCode error, scope const(char[])[] symbolTable, scope IonDescribedValue value)
307     @system nothrow dg) { return opApply(cast(DG) dg); }
308 
309     /// ditto
310     @system
311     scope int opApply(scope int delegate(IonErrorCode error, scope const(char[])[] symbolTable, scope IonDescribedValue value)
312     @system dg) { return opApply(cast(DG) dg); }
313 
314     /++
315     Params:
316         serializer = serializer
317     +/
318     void serialize(S)(scope ref S serializer) scope const @safe
319     {
320         bool following;
321         foreach (scope symbolTable, scope value; this)
322         {
323             if (following)
324                 serializer.nextTopLevelValue;
325             following = true;
326             // static if (__traits(hasMember, serializer, "putKeyId"))
327             // {
328             //     alias unwrappedSerializer = serializer;
329             // }
330             // else
331             // {
332                 import mir.ser.unwrap_ids;
333                 auto unwrappedSerializer = unwrapSymbolIds((()@trusted => &serializer)(), symbolTable);
334             // }
335             value.serialize(unwrappedSerializer);
336         }
337     }
338 
339     ///
340     @safe pure
341     unittest
342     {
343         import mir.ser.json;
344         const ubyte[] data = [0xe0, 0x01, 0x00, 0xea, 0xe9, 0x81, 0x83, 0xd6, 0x87, 0xb4, 0x81, 0x61, 0x81, 0x62, 0xd6, 0x8a, 0x21, 0x01, 0x8b, 0x21, 0x02];
345         auto json = data.IonValueStream.serializeJson;
346         assert(json == `{"a":1,"b":2}`);
347     }
348 }