1 /**
2     Stream
3 
4     Copyright: (c) Enalye 2017
5     License: Zlib
6     Authors: Enalye
7 */
8 
9 module atelier.core.stream;
10 
11 import std.array;
12 import std.conv;
13 import std..string;
14 import std.bitmanip;
15 
16 /// Binary stream to write onto.
17 class OutStream {
18     private Appender!(const ubyte[]) _buffer;
19 
20     /// Ctor.
21     this() {
22         _buffer = appender!(const ubyte[])();
23     }
24 
25     @property {
26         /// Raw buffer.
27         const(ubyte)[] data() const {
28             return _buffer.data;
29         }
30         /// Total size of the buffer.
31         size_t length() const {
32             return _buffer.data.length;
33         }
34     }
35 
36     /// Append a string.
37     void write(string values) {
38         write!(char[])(cast(char[]) values);
39     }
40 
41     /// Append a wstring.
42     void write(wstring values) {
43         write!(wchar[])(cast(wchar[]) values);
44     }
45 
46     /// Append a dstring.
47     void write(dstring values) {
48         write!(dchar[])(cast(dchar[]) values);
49     }
50 
51     /// Append an array.
52     void write(T : T[])(T[] values) {
53         //Values size.
54         ubyte[ushort.sizeof] sizeValues = nativeToBigEndian!ushort(cast(ushort) values.length);
55         foreach (ubyte b; sizeValues)
56             _buffer.append!ubyte(b);
57         //Values array.
58         foreach (T v; values)
59             foreach (ubyte b; nativeToBigEndian(v))
60                 _buffer.append!ubyte(b);
61     }
62 
63     /// Append a value.
64     void write(T)(T value) {
65         //Value size.
66         ubyte[ushort.sizeof] sizeValues = nativeToBigEndian!ushort(1u);
67         foreach (ubyte b; sizeValues)
68             _buffer.append!ubyte(b);
69         //Value array.
70         foreach (ubyte b; nativeToBigEndian(value))
71             _buffer.append!ubyte(b);
72     }
73 }
74 
75 /// Binary stream to read from.
76 class InStream {
77     private ubyte[] _buffer;
78 
79     @property {
80         /// Raw buffer.
81         void data(ubyte[] buffer) {
82             _buffer = buffer;
83         }
84         /// Ditto
85         ref ubyte[] data() {
86             return _buffer;
87         }
88 
89         /// Total size of the buffer.
90         size_t length(size_t len) {
91             return _buffer.length = len;
92         }
93         /// Ditto
94         size_t length() const {
95             return _buffer.length;
96         }
97     }
98 
99     /// Sets a raw buffer.
100     void set(const ubyte[] buffer) {
101         _buffer = buffer.dup;
102     }
103 
104     /// Extract a string.
105     string read(T : string)() {
106         return read!(char[])();
107     }
108 
109     /// Extract a wstring.
110     wstring read(T : wstring)() {
111         return read!(wchar[])();
112     }
113 
114     /// Extract a dstring.
115     dstring read(T : dstring)() {
116         return read!(dchar[])();
117     }
118 
119     /// Extract an array.
120     T[] read(T : T[])() {
121         T[] values;
122         const ushort size = _buffer.read!ushort();
123         if (size == 0)
124             return values;
125         foreach (_; 0 .. size)
126             values ~= _buffer.read!T();
127         return values;
128     }
129 
130     /// Extract a value.
131     T read(T)() {
132         const ushort size = _buffer.read!ushort();
133         if (size != 1uL)
134             throw new Exception("Stream data does not match.");
135         return _buffer.read!T();
136     }
137 }
138 
139 unittest {
140     auto o = new OutStream;
141     auto i = new InStream;
142 
143     o.write!int(179);
144     o.write!(int[])([1, 2, 3, 0, 4, 5]);
145     o.write!string("hello");
146     o.write!bool(true);
147     i.set(o.data);
148 
149     assert(i.read!int() == 179, "Stream module failure");
150     assert(i.read!(int[])() == [1, 2, 3, 0, 4, 5], "Stream module failure");
151     assert(i.read!string() == "hello", "Stream module failure");
152     assert(i.read!bool() == true, "Stream module failure");
153 }