1 /******************************************************************************
2 3 Defines struct type that is guaranteed to be stored in a contiguous byte
4 buffer including all referenced arrays / pointers.
5 6 Copyright:
7 Copyright (c) 2009-2016 dunnhumby Germany GmbH.
8 All rights reserved.
9 10 License:
11 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
12 Alternatively, this file may be distributed under the terms of the Tango
13 3-Clause BSD License (see LICENSE_BSD.txt for details).
14 15 *******************************************************************************/16 17 moduleocean.util.serialize.contiguous.Contiguous;
18 19 20 importocean.meta.types.Qualifiers;
21 importocean.core.Verify;
22 importocean.meta.traits.Indirections;
23 importocean.core.Enforce;
24 25 version (unittest)
26 {
27 importocean.core.Test;
28 debug = ContiguousIntegrity;
29 }
30 31 32 /*******************************************************************************
33 34 "Tag" struct that wraps a void[] buffer with deserialized contents for
35 the struct of type S. Intended as type-safe tool to guarantee that any
36 operations on such structs preserve the contiguous data layout
37 38 Params:
39 S = type of the wrapped struct
40 41 *******************************************************************************/42 43 structContiguous( S )
44 {
45 /***************************************************************************
46 47 Data buffer that stores deserialized struct data together with all
48 referenced arrays in a single contiguous chunk
49 50 `package` protection is used so that contiguous (de)serializer can
51 access it directly
52 53 ***************************************************************************/54 55 packagevoid[] data;
56 57 /***************************************************************************
58 59 Used to work with `Contiguous!(S)` as if it was S*:
60 61 --------
62 struct S { int a; }
63 Contiguous!(S) s = getS();
64 s.ptr.a = 42;
65 --------
66 67 (replace it with "alias this" in D2)
68 69 NB! You may only modify value types accessed via .ptr or elements
70 of stored dynamic arrays. Modifying actual arrays (i.e. appending
71 new elements) is strictly prohibited and can result in very hard to
72 debug memory corruptions. When in doubt consult one of this module
73 developers.
74 75 Returns:
76 Pointer to stored data cast to struct type
77 78 /**************************************************************************/79 80 publicS* ptr ( )
81 {
82 verify((this.data.length == 0) || (this.data.length >= S.sizeof));
83 84 if (this.data.length == 0)
85 returnnull;
86 87 returncast(S*) this.data.ptr;
88 }
89 90 /***************************************************************************
91 92 Recursively iterates `this` and all referenced pointers / arrays and
93 verifies that data is indeed contiguous.
94 95 Throws:
96 Exception if assumption is not verified
97 98 ***************************************************************************/99 100 publicvoidenforceIntegrity()
101 {
102 if (this.data.ptr)
103 {
104 enforceContiguous(*cast(S*) this.data.ptr, this.data);
105 }
106 }
107 108 /***************************************************************************
109 110 Length getter.
111 112 Returns:
113 length of underlying data buffer
114 115 ***************************************************************************/116 117 publicsize_tlength()
118 {
119 returnthis.data.length;
120 }
121 122 /***************************************************************************
123 124 Resets length to 0 allowing same buffer to be used as null indicator
125 without creating new GC allocation later
126 127 ***************************************************************************/128 129 publicContiguous!(S) reset()
130 {
131 this.data.length = 0;
132 assumeSafeAppend(this.data);
133 returnthis;
134 }
135 136 debug(ContiguousIntegrity)
137 {
138 invariant()
139 {
140 // can't call this.enforceIntegrity because it will trigger141 // invariant recursively being a public method142 143 if (this.data.length)
144 {
145 enforceContiguous(*cast(S*) this.data.ptr, this.data);
146 }
147 }
148 }
149 }
150 151 unittest152 {
153 structS { intx; }
154 155 Contiguous!(S) instance;
156 instance.data = (cast(void*) newS)[0..S.sizeof];
157 instance.ptr.x = 42;
158 159 instance.enforceIntegrity();
160 161 test!("==")(
162 instance.data,
163 [ cast(ubyte)42, cast(ubyte) 0, cast(ubyte) 0, cast(ubyte) 0 ][]
164 );
165 166 test!("==")(instance.length, 4);
167 instance.reset();
168 test!("==")(instance.length, 0);
169 }
170 171 /*******************************************************************************
172 173 Iterates over S members recursively and verifies that it only refers
174 to data inside of contiguous data chunk
175 176 Params:
177 input = struct instance to verify
178 allowed_range = data buffer it must fit into
179 180 Throws:
181 Exception if assumption is not verified
182 183 *******************************************************************************/184 185 privatevoidenforceContiguous (S) ( refSinput, invoid[] allowed_range )
186 {
187 staticassert (
188 is(S == struct),
189 "can't verify integrity of non-struct types"190 );
191 192 foreach (i, refmember; input.tupleof)
193 {
194 aliastypeof(member) Member;
195 196 staticif (hasIndirections!(Member))
197 {
198 staticif (is(MemberU : U[]))
199 {
200 // static + dynamic arrays201 202 staticif (is(Unqual!(Member) == U[]))
203 {
204 if (member.ptr)
205 {
206 enforceRange(member, allowed_range);
207 }
208 }
209 210 staticif (is(U == struct))
211 {
212 foreach (refelement; member)
213 {
214 enforceContiguous(element, allowed_range);
215 }
216 }
217 }
218 elsestaticif (is(Member == struct))
219 {
220 // member structs221 222 enforceContiguous(member, allowed_range);
223 }
224 else225 {
226 aliasensureValueTypeMember!(S, i) evt;
227 }
228 }
229 }
230 }
231 232 /*******************************************************************************
233 234 Verifies that `slice` only refers to data inside `allowed_range`
235 236 Params:
237 slice = array slice to verify
238 allowed_range = data buffer it must fit into
239 240 Throws:
241 Exception if assumption is not verified
242 243 *******************************************************************************/244 245 privatevoidenforceRange(invoid[] slice, invoid[] allowed_range)
246 {
247 autoupper_limit = allowed_range.ptr + allowed_range.length;
248 enforce!(">=")(slice.ptr, allowed_range.ptr);
249 enforce!("<=")(slice.ptr, upper_limit);
250 enforce!("<=")(slice.ptr + slice.length, upper_limit);
251 }
252 253 /*******************************************************************************
254 255 Ensures that the type of the `i`th member of `S` (i.e. `S.tupleof[i]`) is a
256 value type; that is, it contains no references.
257 258 Params:
259 S = an aggregate type (usually a struct)
260 i = the index of the aggregate member to check
261 262 *******************************************************************************/263 264 packagetemplateensureValueTypeMember ( S, size_ti )
265 {
266 aliasensureValueTypeMember!(S, i, typeof(S.tupleof)[i]) ensureValueTypeMember;
267 }
268 269 /*******************************************************************************
270 271 Ensures that `T`, which is a the nested type of the type of the `i`th member
272 of `S` (i.e. `S.tupleof[i]`), is a value type; that is, it contains no
273 references.
274 275 Params:
276 S = an aggregate type (usually a struct), for the message
277 i = the index of the aggregate member to check, for the message
278 T = the type that is expected to be a value type
279 280 *******************************************************************************/281 282 packagetemplateensureValueTypeMember ( S, size_ti, T )
283 {
284 aliastypeof(S.tupleof)[i] M;
285 286 staticif (is (T == union))
287 {
288 staticassert (!containsDynamicArray!(T),
289 M.stringof ~ " " ~ S.tupleof[i].stringof ~
290 " - unions containing dynamic arrays are not " ~
291 "allowed, sorry");
292 }
293 294 staticassert(!hasIndirections!(T),
295 M.stringof ~ " " ~ S.tupleof[i].stringof ~
296 " is a or contains an unsupported reference type");
297 }
298 299 version (unittest)
300 {
301 importcore.stdc.string: memset;
302 importocean.meta.types.Typedef;
303 304 }
305 306 unittest307 {
308 mixin(Typedef!(int, "MyInt"));
309 310 // prepare structures311 staticstructS1312 {
313 void[] arr;
314 MyInt[2][2] static_arr;
315 }
316 317 staticstructS2318 {
319 inta, b, c;
320 321 union322 {
323 charx;
324 inty;
325 }
326 327 S1subs;
328 }
329 330 // prepare data331 void[] buffer = newvoid[100];
332 autotested = cast(S2*) buffer.ptr;
333 tested.subs.arr = (buffer.ptr + S2.sizeof)[0..2];
334 335 enforceContiguous(*tested, buffer);
336 337 tested.subs.arr = newvoid[2];
338 testThrown!(Exception)(enforceContiguous(*tested, buffer));
339 340 staticstructS4341 {
342 const(char[])[] str = ["Hello", "World"];
343 }
344 345 autotested2 = cast(S4*) memset(buffer.ptr, 0, buffer.length);
346 347 *tested2 = S4.init;
348 test!("==")(tested2.str.length, 2);
349 testThrown!(Exception)(enforceContiguous(*tested2, buffer));
350 }
351 352 unittest353 {
354 staticstructS { intx; }
355 Contiguous!(S) s;
356 s.data = newvoid[42];
357 s.data.length = 0;
358 test!("==")(s.ptr(), null);
359 }