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