1 /** Deep duplication.
2 
3     Move to Phobos somewhere.
4 
5     TODO: Support inout relevant for const?
6     See: http://forum.dlang.org/thread/hojk95$4mk$1@digitalmars.com
7  */
8 module nxt.deepdup;
9 
10 import core.internal.traits : Unqual;
11 import std.typecons : tuple, Tuple;
12 import std.meta : staticMap;
13 import std.traits : isDynamicArray, isStaticArray, isArray, isPointer;
14 import std.range.primitives : ElementType;
15 
16 alias TypeofDeepdup(T) = typeof(deepdup(T.init));
17 
18 ref Unqual!T deepdup(T)(T t)
19 if (is(T == struct) &&
20     !is(T.Types))
21 {
22     staticMap!(TypeofDeepdup, typeof(t.tupleof)) tup;
23     foreach (const i, Type; tup)
24         tup[i] = t.tupleof[i].deepdup;
25     return Unqual!T(tup);
26 }
27 
28 Tuple!(staticMap!(TypeofDeepdup, T.Types)) deepdup(T)(T t)
29 if (is(T.Types))
30 {
31     staticMap!(TypeofDeepdup, T.Types) tup;
32     foreach (const i, Type; tup)
33         tup[i] = t.field[i].deepdup;
34     return tuple(tup);
35 }
36 
37 Unqual!T deepdup(T)(T t)
38 if (is(T == class))
39 {
40     staticMap!(TypeofDeepdup, typeof(t.tupleof)) tup;
41     foreach (const i, Type; tup)
42         tup[i] = t.tupleof[i].deepdup;
43     return new Unqual!T(tup);
44 }
45 
46 TypeofDeepdup!(ElementType!T)[] deepdup(T)(T t)
47 if (isDynamicArray!T)
48 {
49     auto result = new TypeofDeepdup!(ElementType!T)[](t.length);
50     foreach (const i, elem; t)
51         result[i] = elem.deepdup;
52     return result;
53 }
54 
55 TypeofDeepdup!(ElementType!T)[T.length] deepdup(T)(T t)
56 if (isStaticArray!T)
57 {
58     TypeofDeepdup!(ElementType!T)[T.length] result = t;
59     foreach (ref elem; result)
60         elem = elem.deepdup;
61     return result;
62 }
63 
64 TypeofDeepdup!T* deepdup(T)(T* t) => &deepdup(*t);
65 
66 Unqual!T deepdup(T)(T t) if (!is(T == struct) &&
67                              !is(T == class) &&
68                              !isArray!T &&
69                              !is(T.Types) &&
70                              !isPointer!T)
71 	=> cast(Unqual!T)t;
72 
73 @safe pure nothrow:
74 
75 ///
76 unittest
77 {
78     auto x = [1, 2, 3];
79     assert(x == x.dup);
80     auto y = x;
81     assert(&x[0] == &y[0]);
82     assert(&x[0] != &x.dup[0]);
83 }
84 
85 ///
86 unittest
87 {
88     auto x = [[1], [2], [3]];
89     auto y = x.dup;
90     x[0][0] = 11;
91     assert(x[0][0] == 11);
92     assert(y[0][0] == 11);
93 }
94 
95 ///
96 unittest
97 {
98     auto x = [[1], [2], [3]];
99     auto y = x.deepdup;
100     x[0][0] = 11;
101     assert(x[0][0] == 11);
102     assert(y[0][0] == 1);
103 }
104 
105 ///
106 unittest
107 {
108     auto x = [[1], [2], [3]];
109     auto y = x.deepdup;
110     x[0][0] = 11;
111     assert(x[0][0] == 11);
112     assert(y[0][0] == 1);
113 }
114 
115 ///
116 unittest
117 {
118     auto x = [[[1]], [[2]], [[3]]];
119     auto y = x.deepdup;
120     x[0][0][0] = 11;
121     assert(x[0][0][0] == 11);
122     assert(y[0][0][0] == 1);
123 }
124 
125 /// dup of static array
126 unittest
127 {
128     int[3] x = [1, 2, 3];
129     auto y = x.dup;
130     x[0] = 11;
131     assert(x[0] == 11);
132     assert(y[0] == 1);
133 }
134 
135 /// dup of static array of dynamic arrays
136 unittest
137 {
138     int[][3] x = [[1], [2], [3]];
139     auto y = x.dup;
140     x[0][0] = 11;
141     assert(x[0][0] == 11);
142     assert(y[0][0] == 11);
143 }
144 
145 /// deepdup of static array of dynamic arrays
146 unittest
147 {
148     int[][3] x = [[1], [2], [3]];
149     auto y = x.deepdup;
150     x[0][0] = 11;
151     assert(x[0][0] == 11);
152     assert(y[0][0] == 1);
153 }