1 /**
2  * Deserialization into an existing D variable.
3  *
4  * License:
5  *   This Source Code Form is subject to the terms of
6  *   the Mozilla Public License, v. 2.0. If a copy of
7  *   the MPL was not distributed with this file, You
8  *   can obtain one at http://mozilla.org/MPL/2.0/.
9  *
10  * Authors:
11  *   Vladimir Panteleev <vladimir@thecybershadow.net>
12  */
13 
14 module ae.utils.sd.serialization.deserializer;
15 
16 import std.algorithm.comparison;
17 import std.algorithm.iteration;
18 import std.traits : Unqual;
19 
20 /// Sink for handling array items up to a given length
21 private struct MaxLengthArrayHandler(
22 	/// Array element type
23 	T,
24 	/// Maximum number of elements to receive and store
25 	size_t maxLength,
26 	/// Object receiving the result, or handling overflow
27 	ResultHandler,
28 )
29 {
30 	ResultHandler handler;
31 
32 private:
33 	T[maxLength] buf;
34 	size_t pos;
35 
36 public: // Handler interface
37 	enum canHandleSlice(U) = is(Unqual!U == T);
38 	void handleSlice(U)(U[] slice)
39 	if (canHandleSlice!U)
40 	{
41 		auto end = pos + slice.length;
42 		if (end > maxLength)
43 			handler.handleOverflow(data, slice);
44 		buf[pos .. end] = slice[];
45 		pos = end;
46 	}
47 
48 	private struct ElementHandler
49 	{
50 		MaxLengthArrayHandler* arrayHandler;
51 
52 		enum bool canHandleValue(U) = is(Unqual!U == T);
53 		void handleValue(U)(auto ref U value)
54 		if (canHandleValue!U)
55 		{
56 			if (arrayHandler.pos == maxLength)
57 				arrayHandler.handler.handleOverflow(arrayHandler.data, (&value)[0..1]);
58 			arrayHandler.buf[arrayHandler.pos++] = value;
59 		}
60 	}
61 
62 	void handleElement(Reader)(Reader reader)
63 	{
64 		reader.read(ElementHandler(&this));
65 	}
66 
67 	void handleEnd()
68 	{
69 		handler.handleResult(data);
70 	}
71 
72 public: // Caller API
73 	/// Get elements received so far
74 	T[] data() { return buf[0 .. pos]; }
75 }
76 
77 /// Sink for deserializing data into a variable of type `T`.
78 struct Deserializer(T)
79 {
80 	T* target;
81 
82 	// Implements the top-level context handler
83 
84 	enum bool canHandleValue(U) = is(U : T);
85 	void handleValue(U)(auto ref U value)
86 	if (canHandleValue!U)
87 	{
88 		*target = value;
89 	}
90 
91 	static if (is(T : real))
92 	{
93 		void handleNumeric(Reader)(Reader reader)
94 		{
95 			struct ResultHandler
96 			{
97 				T* target;
98 
99 				void handleResult(char[] data)
100 				{
101 					import std.conv : to;
102 					*target = data.to!T;
103 				}
104 
105 				void handleOverflow(in char[] /*data*/, in char[] /*overflow*/)
106 				{
107 					throw new Exception("Numeric value is too long");
108 				}
109 			}
110 
111 			// The longest number we can reasonably expect in the
112 			// input which would still reasonably be parsed into a D
113 			// numeric type.  Integers already are bounded by a hard
114 			// length limit, however, floating-point numbers can occur
115 			// in inputs with arbitrary precision. Go with 64 total
116 			// characters, which is more than double the amount of
117 			// information that could be contained in the biggest D
118 			// numeric type when expressed as a string.
119 			enum maxLength = 64;
120 			alias NumericArrayHandler = MaxLengthArrayHandler!(
121 				char,
122 				maxLength,
123 				ResultHandler,
124 			);
125 
126 			auto handler = NumericArrayHandler(ResultHandler(target));
127 			reader.read(&handler);
128 		}
129 	}
130 
131 	static if (is(T == struct))
132 	{
133 		static immutable string[] fieldNames = {
134 			string[] result;
135 			foreach (i, field; T.init.tupleof)
136 				result ~= __traits(identifier, T.tupleof[i]);
137 			return result;
138 		}();
139 
140 		enum maxLength = fieldNames.map!((string name) => name.length).fold!max(size_t(0));
141 
142 		struct FieldNameHandler
143 		{
144 			struct ResultHandler
145 			{
146 				T* target;
147 
148 				void handleResult(char[] /*data*/)
149 				{
150 				}
151 
152 				void handleOverflow(in char[] data, in char[] overflow)
153 				{
154 					throw new Exception("No field with prefix " ~ cast(string)data ~ cast(string)overflow);
155 				}
156 			}
157 
158 			alias FieldNameArrayHandler = MaxLengthArrayHandler!(char, maxLength, ResultHandler);
159 			FieldNameArrayHandler arrayHandler;
160 
161 			void handleArray(Reader)(Reader reader)
162 			{
163 				reader.read(&arrayHandler);
164 			}
165 		}
166 
167 		struct FieldHandler
168 		{
169 			T* target;
170 
171 			FieldNameHandler nameHandler;
172 
173 			void handlePairKey(Reader)(Reader reader)
174 			{
175 				reader.read(&nameHandler);
176 			}
177 
178 			void handlePairValue(Reader)(Reader reader)
179 			{
180 				auto name = nameHandler.arrayHandler.data;
181 				switch (name)
182 				{
183 					static foreach (i, fieldName; fieldNames)
184 						case fieldName:
185 							return reader.read(Deserializer!(typeof(T.tupleof[i]))(&target.tupleof[i]));
186 					default:
187 						throw new Exception("No field with prefix " ~ name.idup);
188 				}
189 			}
190 
191 			void handleEnd() {}
192 		}
193 
194 		struct StructHandler
195 		{
196 			T* target;
197 
198 			void handlePair(Reader)(Reader reader)
199 			{
200 				reader.read(FieldHandler(target));
201 			}
202 
203 			void handleEnd() {}
204 		}
205 
206 		void handleMap(Reader)(Reader reader)
207 		{
208 			reader.read(StructHandler(target));
209 		}
210 	}
211 	else
212 	static if (is(T E : E[]))
213 	{
214 		struct ArrayHandler
215 		{
216 			T* target;
217 
218 			// TODO handleSlice
219 
220 			void handleElement(Reader)(Reader reader)
221 			{
222 				target.length++;
223 				alias U = Unqual!E;
224 				reader.read(.Deserializer!U(cast(U*)&(*target)[$ - 1]));
225 			}
226 
227 			void handleEnd() {}
228 		}
229 
230 		void handleArray(Reader)(Reader reader)
231 		{
232 			reader.read(ArrayHandler(target));
233 		}
234 	}
235 }
236 
237 /// Accept a data source and absorb received data into the given variable.
238 void deserializeInto(Source, T)(Source source, ref T target)
239 {
240 	source.read(Deserializer!T(&target));
241 }
242 
243 /// Accept a data source and absorb received data into a new variable of type `T`.
244 T deserializeNew(T, Source)(Source source)
245 {
246 	T target;
247 	source.read(Deserializer!T(&target));
248 	return target;
249 }