1 /// Barebones memory stream implementation using `IStream` without any bounds checking.
2 module tern.stream.memory_stream;
3 
4 public import tern.stream.impl;
5 import tern.serialization;
6 import tern.traits;
7 
8 /// Memory stream implementation backed by a pointer, has no bounds checking.
9 public class MemoryStream : IStream
10 {
11 public:
12 final:
13     ubyte* data;
14     size_t position;
15 
16     this(T : U*, U)(T ptr)
17     {
18         data = cast(ubyte*)ptr;
19     }
20 
21     /**
22      * Always returns true.
23      */
24     bool mayRead(T)()
25     {
26         return true;
27     }
28 
29     /**
30      * Always returns true.
31      */
32     bool mayRead(size_t size = 1)
33     {
34         return true;
35     }
36 
37     /**
38      * Moves the position in the stream by the size of type T.
39      *
40      * Params:
41      *   T = The size of type to move the position by.
42      */
43     void step(T)(size_t count = 1)
44     {
45         position += T.sizeof * count;
46     }
47 
48     /** 
49      * Moves the position in the stream forward by one until `val` is peeked.
50      *
51      * Params:
52      *  val = The value to be peeked.
53      */
54     void stepUntil(T)(T val)
55     {
56         static if (isSomeString!T)
57         {
58             while (peekString!(ElementType!T) != val)
59                 position++;
60         }
61         else
62         {
63             while (peek!T != val)
64                 position++;
65         }
66     }
67 
68     /**
69      * Seeks to a new position in the stream based on the provided offset and seek direction.
70      *
71      * Params:
72      *  SEEK = The direction of the seek operation (Start, Current, or End).
73      *  offset = The offset from the seek direction to be set.
74      */
75     void seek(Seek SEEK)(size_t offset)
76     {
77         static if (SEEK = Seek.Current)
78             position += offset;
79         else static if (SEEK = Seek.Start)
80             position = offset;
81         else
82             position = data.length - offset;
83     }
84 
85     /**
86      * Reads the next value from the stream of type T.
87      *
88      * Params:
89      *   T = The type of data to be read.
90      *
91      * Returns:
92      *  The value read from the stream.
93      */
94     T read(T)()
95         if (!isDynamicArray!T)
96     {
97         return *cast(T*)data[position..(position += T.sizeof)].ptr;
98     }
99 
100     /**
101      * Peeks at the next value from the stream of type T without advancing the stream position.
102      *
103      * Params:
104      *  T = The type of data to peek.
105      *
106      * Returns:
107      *  The value peeked from the stream.
108      */
109     T peek(T)()
110         if (!isDynamicArray!T)
111     {
112         return *cast(T*)data[position..(position + T.sizeof)].ptr;
113     }
114 
115     /**
116      * Reads multiple values of type T from the stream.
117      *
118      * Params:
119      *  T = The type of data to be read.
120      *  count = The number of values to read from the stream.
121      *
122      * Returns:
123      *  An array of values read from the stream.
124      */
125     T[] read(T)(size_t count)
126         if (!isDynamicArray!T)
127     {
128         T[] arr;
129         foreach (i; 0..count)
130             arr ~= read!T;
131         return arr;
132     }
133 
134     /**
135      * Peeks at multiple values of type T from the stream without advancing the stream position.
136      *
137      * Params:
138      *  T = The type of data to peek.
139      *  count = The number of values to peek from the stream.
140      *
141      * Returns:
142      *  An array of values peeked from the stream.
143      */
144     T[] peek(T)(size_t count)
145         if (!isDynamicArray!T)
146     {
147         auto _position = position;
148         scope (exit) position = _position;
149         T[] arr;
150         foreach (i; 0..count)
151             arr ~= read!T;
152         return arr;
153     }
154 
155     /**
156      * Reads an array of type T from the stream.
157      *
158      * Params:
159      *  T = The type of data to be read.
160      *
161      * Returns:
162      *  An array read from the stream.
163      */
164     T read(T : U[], U)()
165         if (isDynamicArray!T)
166     {
167         return read!(ElementType!T)(cast(size_t)read7EncodedInt());
168     }
169 
170     /**
171      * Peeks an array of type T from the stream without advancing the stream position.
172      *
173      * Params:
174      *  T = The type of data to peek.
175      *
176      * Returns:
177      *  An array peeked from the stream.
178      */
179     T peek(T : U[], U)()
180         if (isDynamicArray!T)
181     {
182         return peek!(ElementType!T)(cast(size_t)read7EncodedInt());
183     }
184 
185     /**
186      * Writes the provided value to the stream.
187      *
188      * Params:
189      *   T = The type of data to be written.
190      *   val = The value to be written to the stream.
191      */
192     void write(T)(T val)
193     {    
194         data[position..(position += T.sizeof)] = (cast(ubyte*)&val)[0..T.sizeof];
195     }
196 
197     /**
198      * Writes the provided value to the stream without advancing the stream position.
199      *
200      * Params:
201      *   T = The type of data to be written.
202      *   val = The value to be written to the stream.
203      */
204     void put(T)(T val)
205     {
206         data[position..(position + T.sizeof)] = (cast(ubyte*)&val)[0..T.sizeof];
207     }
208 
209     /**
210      * Writes multiple values of type T to the stream.
211      *
212      * Params:
213      *   T = The type of data to be written.
214      *   items = An array of values to be written to the stream.
215      */
216     void write(T, bool PREFIXED = true)(T val)
217         if (isArray!T)
218     {
219         static if (PREFIXED)
220             write7EncodedInt(cast(uint)(val.length));
221 
222         foreach (u; val)
223             write(u);
224     }
225 
226     /**
227      * Writes multiple values of type T to the stream without advancing the stream position.
228      *
229      * Params:
230      *   T = The type of data to be written.
231      *   items = An array of values to be written to the stream.
232      */
233     void put(T, bool PREFIXED = true)(T val)
234         if (isArray!T)
235     {
236         auto _position = position;
237         scope (exit) position = _position;
238         static if (PREFIXED)
239             write7EncodedInt(cast(uint)(val.length));
240 
241         foreach (u; val)
242             write(u);
243     }
244 
245     /**
246      * Reads a string from the stream considering the character width and prefixing.
247      *
248      * Params:
249      *   CHAR = The character type used for reading the string (char, wchar, or dchar).
250      *   PREFIXED = Indicates whether the string is prefixed. Default is false.
251      *
252      * Returns:
253      *  The read string from the stream.
254      */
255     immutable(CHAR)[] readString(CHAR, bool PREFIXED = false)()
256     {
257         static if (PREFIXED)
258             return cast(immutable(CHAR)[])read!(immutable(CHAR)[]);
259         else
260         {
261             immutable(CHAR)[] ret;
262             while (peek!CHAR != '\0')
263                 ret ~= read!CHAR;
264             return ret;
265         }
266     }
267 
268     /**
269      * Reads a string from the stream considering the character width and prefixing without advancing the stream position.
270      *
271      * Params:
272      *   CHAR = The character type used for reading the string (char, wchar, or dchar).
273      *   PREFIXED = Indicates whether the string is prefixed. Default is false.
274      *
275      * Returns:
276      *  The read string from the stream.
277      */
278     immutable(CHAR)[] peekString(CHAR, bool PREFIXED = false)()
279     {
280         auto _position = position;
281         scope (exit) position = _position;
282         static if (PREFIXED)
283             return cast(immutable(CHAR)[])read!(immutable(CHAR)[]);
284         else
285         {
286             immutable(CHAR)[] ret;
287             while (peek!CHAR != '\0')
288                 ret ~= read!CHAR;
289             return ret;
290         }
291     }
292 
293     /**
294      * Writes a string to the stream considering the character width and prefixing.
295      *
296      * Params:
297      *   CHAR = The character type used for writing the string (char, wchar, or dchar).
298      *   PREFIXED = Indicates whether the string is prefixed. Default is false.
299      *   val = The string to be written to the stream.
300      */
301     void writeString(CHAR, bool PREFIXED = false)(immutable(CHAR)[] val)
302     {
303         static if (!PREFIXED)
304             val ~= '\0';
305 
306         write!(immutable(CHAR)[], PREFIXED)(val);
307     }
308 
309     /**
310      * Writes a string into the stream considering the character width and prefixing without advancing the stream position.
311      *
312      * Params:
313      *   CHAR = The character type used for writing the string (char, wchar, or dchar).
314      *   PREFIXED = Indicates whether the string is prefixed. Default is false.
315      *   val = The string to be put into the stream.
316      */
317     void putString(CHAR, bool PREFIXED = false)(immutable(CHAR)[] val)
318     {
319         static if (!PREFIXED)
320             val ~= '\0';
321 
322         put!(immutable(CHAR)[], PREFIXED)(val);
323     }
324 
325     /**
326      * Reads an integer value encoded in 7 bits from the stream.
327      *
328      * Returns:
329      *  The integer value read from the stream.
330      */
331     uint read7EncodedInt()
332     {
333         uint result = 0;
334         uint shift = 0;
335 
336         foreach (i; 0..5)
337         {
338             ubyte b = read!ubyte;
339             result |= cast(uint)(b & 0x7F) << shift;
340             if ((b & 0x80) == 0)
341                 return result;
342             shift += 7;
343         }
344         
345         return result;
346     }
347 
348     /**
349      * Writes an integer value encoded in 7 bits to the stream.
350      *
351      * Params:
352      *   val = The integer value to be written to the stream.
353      */
354     void write7EncodedInt(uint val)
355     {
356         foreach (i; 0..5)
357         {
358             byte b = cast(byte)(val & 0x7F);
359             val >>= 7;
360             if (val != 0)
361                 b |= 0x80;
362             write(b);
363             if (val == 0)
364                 return;
365         }
366     }
367 
368     /**
369      * Reads data from a byte stream into a structured type based on specified field names and read kinds.  
370      * Designed specifically for better control reading string and array fields.
371      *
372      * Params:
373      *   T = The type representing the structure to read into.
374      *   ARGS = Variadic template parameter representing field names and read kinds.
375      *
376      * Returns:
377      *  Returns an instance of type T with fields populated based on the specified read operations.
378      */
379     T read(T, ARGS...)()
380     {
381         T val;
382         foreach (field; Fields!T)
383         {
384             alias M = TypeOf!(val, field);
385             bool cread;
386             static foreach (i, ARG; ARGS)
387             {
388                 static if (i % 3 == 0)
389                 {
390                     static assert(is(typeof(ARG) == string),
391                         "Field name expected, found " ~ ARG.stringof);  
392                 }
393                 else static if (i % 3 == 1)
394                 {
395                     static assert(is(typeof(ARG) == ReadKind),
396                         "Read kind expected, found " ~ ARG.stringof);  
397                 }
398                 else
399                 {
400                     static if (field == ARGS[i - 2] || ARGS[i - 2] == "")
401                     {
402                         static if (!isStaticArray!M && is(M == string))
403                         {
404                             cread = true;
405                             static if (ARGS[i - 1] == ReadKind.Field)
406                             {
407                                 __traits(getMember, val, field) = read!char(__traits(getMember, val, ARG)).to!string;
408                             }
409                             else static if  (ARGS[i - 1] == ReadKind.Fixed)
410                             {
411                                 __traits(getMember, val, field) =  read!char(ARG).to!string;
412                             }
413                             else
414                             {
415                                 __traits(getMember, val, field) = readString!(char, ARG);
416                             }
417                         }
418                         else static if (!isStaticArray!M && is(M == wstring))
419                         {
420                             cread = true;
421                             static if (ARGS[i - 1] == ReadKind.Field)
422                             {
423                                 __traits(getMember, val, field) = read!wchar(__traits(getMember, val, ARG)).to!string;
424                             }
425                             else static if  (ARGS[i - 1] == ReadKind.Fixed)
426                             {
427                                 __traits(getMember, val, field) =  read!wchar(ARG).to!string;
428                             }
429                             else
430                             {
431                                 __traits(getMember, val, field) = readString!(wchar, ARG);
432                             }
433                         }
434                         static if (!isStaticArray!M && is(M == dstring))
435                         {
436                             cread = true;
437                             static if (ARGS[i - 1] == ReadKind.Field)
438                             {
439                                 __traits(getMember, val, field) = read!dchar(__traits(getMember, val, ARG)).to!string;
440                             }
441                             else static if  (ARGS[i - 1] == ReadKind.Fixed)
442                             {
443                                 __traits(getMember, val, field) =  read!dchar(ARG).to!string;
444                             }
445                             else
446                             {
447                                 __traits(getMember, val, field) = readString!(dchar, ARG);
448                             }
449                         }
450                         else static if (isDynamicArray!M)
451                         {
452                             cread = true;
453                             static if (ARGS[i - 1] == ReadKind.Field)
454                             {
455                                 __traits(getMember, val, field) = read!(ElementType!M)(__traits(getMember, val, ARG));
456                             }
457                             else static if  (ARGS[i - 1] == ReadKind.Fixed)
458                             {
459                                 __traits(getMember, val, field) = read!(ElementType!M)(ARG);
460                             }
461                             else
462                             {
463                                 __traits(getMember, val, field) = read!M;
464                             }
465                         }
466                     }
467                 }
468             }
469             if (!cread)
470                 __traits(getMember, val, field) = read!M;
471         }
472         return val;
473     }
474 
475     /// ditto
476     T[] read(T, ARGS...)(size_t count)
477     {
478         T[] items;
479         foreach (i; 0..count)
480             items ~= read!(T, ARGS);
481         return items;
482     }
483 
484     /**
485      * Reads a type from the stream using optional fields.
486      *
487      * Params:
488      *   T = The type to be read from the stream.
489      *   ARGS... = The arguments for optional fields.
490      *
491      * Returns:
492      *  The read type read from the stream.
493      */
494     T readPlasticized(T, ARGS...)()
495         if (ARGS.length % 3 == 0)
496     {
497         T val;
498         foreach (field; Fields!T)
499         {
500             bool cread = true;
501             static foreach (i, ARG; ARGS)
502             {
503                 static if (i % 3 == 0)
504                 {
505                     static assert(is(typeof(ARG) == string),
506                         "Field name expected, found " ~ ARG.stringof);  
507                 }
508                 else static if (i % 3 == 1)
509                 {
510                     static assert(is(typeof(ARG) == string),
511                         "Conditional field name expected, found " ~ ARG.stringof);
512                 }
513                 else
514                 {
515                     if (field == ARGS[i - 2] && __traits(getMember, val, ARGS[i - 1]) != ARG)
516                         cread = false;
517                 }
518             }
519             if (cread)
520                 __traits(getMember, val, field) = read!(TypeOf!(val, field));
521         }
522         return val;
523     }
524 }