1 /*******************************************************************************
2 
3     Utility to recursively reset fields of struct to their .init value while
4     preserving array pointers (their length is set to 0 but memory is kept
5     available for further reusage)
6 
7     Copyright:
8         Copyright (c) 2009-2016 dunnhumby Germany GmbH.
9         All rights reserved.
10 
11     License:
12         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
13         Alternatively, this file may be distributed under the terms of the Tango
14         3-Clause BSD License (see LICENSE_BSD.txt for details).
15 
16 *******************************************************************************/
17 
18 module ocean.util.DeepReset;
19 
20 
21 import ocean.meta.types.Qualifiers;
22 import ocean.core.Array;
23 import ocean.meta.traits.Basic /* isArrayType */;
24 
25 version (unittest)
26 {
27     import ocean.core.Test;
28 }
29 
30 /*******************************************************************************
31 
32     Template to determine the correct DeepReset function to call dependent on
33     the type given.
34 
35     Params:
36         T = type to deep reset
37 
38     Evaluates to:
39         aliases function appropriate to T
40 
41 *******************************************************************************/
42 
43 public template DeepReset ( T )
44 {
45     static if ( is(T == class) )
46     {
47         alias ClassDeepReset DeepReset;
48     }
49     else static if ( is(T == struct) )
50     {
51         alias StructDeepReset DeepReset;
52     }
53     else static if ( isArrayType!(T) == ArrayKind.Associative )
54     {
55         // TODO: reset associative arrays
56         pragma(msg, "Warning: deep reset of associative arrays not yet implemented");
57         alias nothing DeepReset;
58     }
59     else static if ( isArrayType!(T) == ArrayKind.Dynamic )
60     {
61         alias DynamicArrayDeepReset DeepReset;
62     }
63     else static if ( isArrayType!(T) == ArrayKind.Static )
64     {
65         alias StaticArrayDeepReset DeepReset;
66     }
67     else
68     {
69         pragma(msg, "Warning: DeepReset template could not expand for type " ~ T.stringof);
70         alias nothing DeepReset;
71     }
72 }
73 
74 
75 
76 /*******************************************************************************
77 
78     Deep reset function for dynamic arrays. To reset a dynamic array set the
79     length to 0.
80 
81     Params:
82         T = type of array to deep copy
83         dst = destination array
84 
85 *******************************************************************************/
86 
87 public void DynamicArrayDeepReset ( T ) ( ref T[] dst )
88 {
89     ArrayDeepReset(dst);
90     dst.length = 0;
91     assumeSafeAppend(dst);
92 }
93 
94 
95 
96 /*******************************************************************************
97 
98     Deep reset function for static arrays. To reset a static array go through
99     the whole array and set the items to the init values for the type of the
100     array.
101 
102     Params:
103         T = type of array to deep copy
104         dst = destination array
105 
106 *******************************************************************************/
107 
108 public void StaticArrayDeepReset ( T ) ( T[] dst )
109 {
110     ArrayDeepReset(dst);
111 }
112 
113 
114 
115 /*******************************************************************************
116 
117     Deep reset function for arrays.
118 
119     Params:
120         T = type of array to deep copy
121         dst = destination array
122 
123 *******************************************************************************/
124 
125 private void ArrayDeepReset ( T ) ( ref T[] dst )
126 {
127     static if ( isArrayType!(T) == ArrayKind.Associative )
128     {
129         // TODO: copy associative arrays
130         pragma(msg, "Warning: deep reset of associative arrays not yet implemented");
131     }
132     else static if ( isArrayType!(T) )
133     {
134         foreach ( i, e; dst )
135         {
136             static if ( isArrayType!(T) == ArrayKind.Dynamic )
137             {
138                 DynamicArrayDeepReset(dst[i]);
139             }
140             else
141             {
142                 StaticArrayDeepReset(dst[i]);
143             }
144         }
145     }
146     else static if ( is(T == struct) )
147     {
148         foreach ( i, e; dst )
149         {
150             StructDeepReset(dst[i]);
151         }
152     }
153     else static if ( is(T == class) )
154     {
155         foreach ( i, e; dst )
156         {
157             ClassDeepReset(dst[i]);
158         }
159     }
160     else
161     {
162         // TODO this probably does not need to be done for a dynamic array
163         foreach ( ref item; dst )
164         {
165             item = item.init;
166         }
167     }
168 }
169 
170 
171 
172 /*******************************************************************************
173 
174     Deep reset function for structs.
175 
176     Params:
177         T = type of struct to deep copy
178         dst = destination struct
179 
180 *******************************************************************************/
181 
182 // TODO: struct & class both share basically the same body, could be shared?
183 
184 public void StructDeepReset ( T ) ( ref T dst )
185 {
186     static if ( !is(T == struct) )
187     {
188         static assert(false, "StructDeepReset: " ~ T.stringof ~ " is not a struct");
189     }
190 
191     foreach ( i, member; dst.tupleof )
192     {
193         static if ( isArrayType!(typeof(member)) == ArrayKind.Associative )
194         {
195             // TODO: copy associative arrays
196             pragma(msg, "Warning: deep reset of associative arrays not yet implemented");
197         }
198         else static if ( isArrayType!(typeof(member)) == ArrayKind.Dynamic )
199         {
200             DynamicArrayDeepReset(dst.tupleof[i]);
201         }
202         else static if ( isArrayType!(typeof(member)) == ArrayKind.Static )
203         {
204             StaticArrayDeepReset(dst.tupleof[i]);
205         }
206         else static if ( is(typeof(member) == class) )
207         {
208             ClassDeepReset(dst.tupleof[i]);
209         }
210         else static if ( is(typeof(member) == struct) )
211         {
212             StructDeepReset(dst.tupleof[i]);
213         }
214         else
215         {
216             dst.tupleof[i] = dst.tupleof[i].init;
217         }
218     }
219 }
220 
221 
222 
223 /*******************************************************************************
224 
225     Deep reset function for dynamic class instances.
226 
227     Params:
228         T = type of class to deep copy
229         dst = destination instance
230 
231 *******************************************************************************/
232 
233 public void ClassDeepReset ( T ) ( ref T dst )
234 {
235     static if ( !is(T == class) )
236     {
237         static assert(false, "ClassDeepReset: " ~ T.stringof ~ " is not a class");
238     }
239 
240     foreach ( i, member; dst.tupleof )
241     {
242         static if ( isArrayType!(typeof(member)) == ArrayKind.Associative )
243         {
244             // TODO: copy associative arrays
245             pragma(msg, "Warning: deep reset of associative arrays not yet implemented");
246         }
247         else static if ( isArrayType!(typeof(member)) )
248         {
249             static if ( isArrayType!(typeof(member)) == ArrayKind.Dynamic )
250             {
251                 DynamicArrayDeepReset(dst.tupleof[i]);
252             }
253             else
254             {
255                 StaticArrayDeepReset(dst.tupleof[i]);
256             }
257         }
258         else static if ( is(typeof(member) == class) )
259         {
260             ClassDeepReset(dst.tupleof[i]);
261         }
262         else static if ( is(typeof(member) == struct) )
263         {
264             StructDeepReset(dst.tupleof[i]);
265         }
266         else
267         {
268             dst.tupleof[i] = dst.tupleof[i].init;
269         }
270     }
271 
272     // Recurse into super any classes
273     static if ( is(T S == super ) )
274     {
275         foreach ( V; S )
276         {
277             static if ( !is(V == Object) )
278             {
279                 ClassDeepReset(cast(V)dst);
280             }
281         }
282     }
283 }
284 
285 
286 
287 /*******************************************************************************
288 
289     unit test for the DeepReset method. Makes a test structure and fills it
290     with data before calling reset and making sure it is cleared.
291 
292     We first build a basic struct that has both a single sub struct and a
293     dynamic array of sub structs. Both of these are then filled along with
294     the fursther sub sub struct.
295 
296     The DeepReset method is then called. The struct is then confirmed to
297     have had it's members reset to the correct values
298 
299     TODO Adjust the unit test so it also deals with struct being
300     re-initialised to make sure they are not full of old data (~=)
301 
302 *******************************************************************************/
303 
304 
305 unittest
306 {
307     struct TestStruct
308     {
309         int a;
310         char[] b;
311         int[7] c;
312 
313         public struct SubStruct
314         {
315             int d;
316             char[] e;
317             char[][] f;
318             int[7] g;
319 
320             public struct SubSubStruct
321             {
322                 int h;
323                 char[] i;
324                 char[][] j;
325                 int[7] k;
326 
327                 void InitStructure()
328                 {
329                     this.h = -52;
330                     this.i.copy("even even more test text");
331                     this.j.length = 3;
332                     this.j[0].copy("abc");
333                     this.j[1].copy("def");
334                     this.j[2].copy("ghi");
335                     foreach ( ref item; this.k )
336                     {
337                         item = 120000;
338                     }
339                 }
340             }
341 
342             void InitStructure()
343             {
344                 this.d = 32;
345                 this.e.copy("even more test text");
346 
347                 this.f.length = 1;
348                 this.f[0].copy("abc");
349                 foreach ( ref item; this.g )
350                 {
351                     item = 32400;
352                 }
353             }
354 
355             SubSubStruct[] sub_sub_struct;
356         }
357 
358         SubStruct sub_struct;
359 
360         SubStruct[] sub_struct_array;
361     }
362 
363     TestStruct test_struct;
364     test_struct.a = 7;
365     test_struct.b.copy("some test");
366     foreach ( i, ref item; test_struct.c )
367     {
368         item = 64800;
369     }
370 
371     TestStruct.SubStruct sub_struct;
372     sub_struct.InitStructure;
373     test_struct.sub_struct = sub_struct;
374     test_struct.sub_struct_array ~= sub_struct;
375     test_struct.sub_struct_array ~= sub_struct;
376 
377 
378     TestStruct.SubStruct.SubSubStruct sub_sub_struct;
379     sub_sub_struct.InitStructure;
380     test_struct.sub_struct_array[0].sub_sub_struct ~= sub_sub_struct;
381     test_struct.sub_struct_array[1].sub_sub_struct ~= sub_sub_struct;
382     test_struct.sub_struct_array[1].sub_sub_struct ~= sub_sub_struct;
383     test_struct.sub_struct.sub_sub_struct ~= sub_sub_struct;
384     test_struct.sub_struct.sub_sub_struct ~= sub_sub_struct;
385 
386     DeepReset!(TestStruct)(test_struct);
387 
388     test!("==")(test_struct.a, 0);
389     test!("==")(test_struct.b, ""[]);
390     foreach ( item; test_struct.c )
391     {
392         test!("==")(item, 0);
393     }
394 
395     test!("==")(test_struct.sub_struct_array.length, 0);
396 
397     test!("==")(test_struct.sub_struct.d, 0);
398     test!("==")(test_struct.sub_struct.e, ""[]);
399     test!("==")(test_struct.sub_struct.f.length, 0);
400     foreach ( item; test_struct.sub_struct.g )
401     {
402         test!("==")(item, 0);
403     }
404 
405     test!("==")(test_struct.sub_struct.sub_sub_struct.length, 0);
406 
407     //Test nested classes.
408     class TestClass
409     {
410         int a;
411         char[] b;
412         int[2] c;
413 
414         public class SubClass
415         {
416             int d;
417             char[] e;
418         }
419 
420         SubClass s;
421     }
422 
423     TestClass test_class = new TestClass;
424     test_class.s =  test_class..new SubClass;
425     test_class.a = 7;
426     test_class.b = [];
427     test_class.b ~= 't';
428     test_class.c[1] = 1;
429     test_class.s.d = 5;
430     test_class.s.e = [];
431     test_class.s.e ~= 'q';
432 
433     DeepReset!(TestClass)(test_class);
434     test!("==")(test_class.a, 0);
435     test!("==")(test_class.b.length, 0);
436     test!("==")(test_class.c[1], 0);
437     test!("!is")(cast(void*)test_class.s, null);
438     test!("==")(test_class.s.d, 0);
439     test!("==")(test_class.s.e.length, 0);
440 }