1 /*
2 Copyright (c) 2017-2018 Timur Gafarov
3 
4 Boost Software License - Version 1.0 - August 17th, 2003
5 Permission is hereby granted, free of charge, to any person or organization
6 obtaining a copy of the software and accompanying documentation covered by
7 this license (the "Software") to use, reproduce, display, distribute,
8 execute, and transmit the Software, and to prepare derivative works of the
9 Software, and to permit third-parties to whom the Software is furnished to
10 do so, all subject to the following:
11 
12 The copyright notices in the Software and this entire statement, including
13 the above license grant, this restriction and the following disclaimer,
14 must be included in all copies of the Software, in whole or in part, and
15 all derivative works of the Software, unless such copies or derivative
16 works are solely in the form of machine-executable object code generated by
17 a source language processor.
18 
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
22 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
23 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
24 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 DEALINGS IN THE SOFTWARE.
26 */
27 
28 module dagon.resource.serialization;
29 
30 import std.traits;
31 import std.bitmanip;
32 
33 import dlib.core.stream;
34 
35 // Stream-based serialization and deserialization
36 
37 struct Series(T, bool fixedSize = false)
38 {
39     union
40     {
41         T _value;
42         static if (isDynamicArray!T)
43             ubyte[] _bytes;
44         else
45             ubyte[T.sizeof] _bytes;
46     }
47 
48     this(T val)
49     {
50         value = val;
51     }
52 
53     T opAssign(T val)
54     {
55         return (value = val);
56     }
57 
58     this(InputStream istrm)
59     {
60         readFrom(istrm);
61     }
62 
63     @property T value(T v)
64     {
65         static if (isIntegral!T)
66         {
67             _bytes = nativeToLittleEndian!T(v);
68         }
69         else
70             _value = v;
71         return _value;
72     }
73 
74     @property T value()
75     {
76         T res;
77         static if (isIntegral!T)
78         {
79             res = littleEndianToNative!T(_bytes);
80         }
81         else
82             res = _value;
83         return res;
84     }
85 
86     size_t writeTo(OutputStream ostrm)
87     {
88         size_t n = 0;
89         static if (isDynamicArray!T)
90         {
91             n += Series!(uint)(cast(uint)_value.length).writeTo(ostrm);
92             foreach(v; _value)
93                 n += Series!(Unqual!(typeof(v)))(v).writeTo(ostrm);
94             return n;
95         }
96         else
97         static if (is(T == struct) || is(T == class))
98         {
99             static if (is(T == class))
100                 if (_value is null)
101                     throw new Exception("null reference in input");
102 
103             // TODO: make automatic check
104             static if (is(T == struct) && fixedSize)
105             {
106                 n = ostrm.writeBytes(_bytes.ptr, _bytes.length);
107             }
108             else
109             {
110                 foreach(v; _value.tupleof)
111                     n += Series!(typeof(v))(v).writeTo(ostrm);
112             }
113             return n;
114         }
115         else
116         {
117             return ostrm.writeBytes(_bytes.ptr, _bytes.length);
118         }
119     }
120 
121     size_t readFrom(InputStream istrm)
122     {
123         static if (isSomeString!T)
124         {
125             uint len = Series!(uint)(istrm).value;
126             size_t pos = 4;
127             ubyte[] buff = new ubyte[len];
128             istrm.fillArray(buff);
129             T str = cast(T)buff;
130             _value = str;
131             pos += len;
132             return pos;
133         }
134         else
135         static if (isDynamicArray!T)
136         {
137             uint len = Series!(uint)(istrm).value;
138             size_t pos = 4;
139             alias FT = ForeachType!T;
140             if (len == 0)
141                 return pos;
142 
143             _value = new FT[len];
144 
145             foreach(ref v; _value)
146             {
147                 Series!(FT) se;
148                 size_t s = se.readFrom(istrm);
149                 v = se.value;
150                 pos += s;
151             }
152 
153             return pos;
154         }
155         else
156         static if (is(T == struct) || is(T == class))
157         {
158             size_t pos = 0;
159             static if (is(T == class))
160                 if (_value is null)
161                     throw new Exception("null reference in output");
162 
163             static if (is(T == struct) && fixedSize)
164             {
165                 pos += istrm.readBytes(_bytes.ptr, T.sizeof);
166             }
167             else
168             foreach(ref v; _value.tupleof)
169             {
170                 Series!(typeof(v)) se;
171                 static if (is(typeof(v) == class))
172                 {
173                     if (v is null)
174                         throw new Exception("null reference in output");
175                     se._value = v;
176                 }
177                 size_t s = se.readFrom(istrm);
178                 v = se.value;
179                 pos += s;
180             }
181 
182             return pos;
183         }
184         else
185         {
186             return istrm.readBytes(_bytes.ptr, T.sizeof);
187         }
188     }
189 }
190 
191 T read(T, bool fixedSize = false)(InputStream istrm)
192 {
193     auto s = Series!(T, fixedSize)(istrm);
194     return s.value;
195 }
196 
197 size_t write(T, bool fixedSize = false)(InputStream istrm, T val)
198 {
199     auto s = Series!(T, fixedSize)(val);
200     return s.writeTo(istrm);
201 }
202