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, isArray, isPointer;
14 import std.range.primitives : ElementType;
15 
16 alias TypeofDeepdup(T) = typeof(deepdup(T.init));
17 
18 /++ Returns: Copy of `x` where each members `m` is duplicated via `m.dup` if
19     possible. +/
20 T dupMembers(T)(T x) @safe pure nothrow if (is(T == struct)) {
21     typeof(return)ret;
22     foreach (const i, member; x.tupleof) {
23         static if(__traits(compiles, member.dup))
24             ret.tupleof[i] = member.dup;
25         else
26             ret.tupleof[i] = member;
27     }
28     return ret;
29 }
30 
31 ///
32 @safe pure nothrow unittest {
33 	struct S {
34 		int ia;
35 		int ib;
36 		string s; // @gc
37 	}
38 	S x = { ia: 42, ib: 43, s: "abc" };
39 	auto y = x.dupMembers;
40 	assert(x == y);
41 	assert(x.s !is y.s);
42 }
43 
44 ref Unqual!T deepdup(T)(T t)
45 if (is(T == struct) &&
46 	!is(T.Types)) {
47 	staticMap!(TypeofDeepdup, typeof(t.tupleof)) tup;
48 	foreach (const i, Type; tup)
49 		tup[i] = t.tupleof[i].deepdup;
50 	return Unqual!T(tup);
51 }
52 
53 Tuple!(staticMap!(TypeofDeepdup, T.Types)) deepdup(T)(T t)
54 if (is(T.Types)) {
55 	staticMap!(TypeofDeepdup, T.Types) tup;
56 	foreach (const i, Type; tup)
57 		tup[i] = t.field[i].deepdup;
58 	return tuple(tup);
59 }
60 
61 Unqual!T deepdup(T)(T t)
62 if (is(T == class)) {
63 	staticMap!(TypeofDeepdup, typeof(t.tupleof)) tup;
64 	foreach (const i, Type; tup)
65 		tup[i] = t.tupleof[i].deepdup;
66 	return new Unqual!T(tup);
67 }
68 
69 TypeofDeepdup!(ElementType!T)[] deepdup(T)(T t)
70 if (isDynamicArray!T) {
71 	auto result = new TypeofDeepdup!(ElementType!T)[](t.length);
72 	foreach (const i, elem; t)
73 		result[i] = elem.deepdup;
74 	return result;
75 }
76 
77 TypeofDeepdup!(ElementType!T)[T.length] deepdup(T)(T t)
78 if (__traits(isStaticArray, T)) {
79 	TypeofDeepdup!(ElementType!T)[T.length] result = t;
80 	foreach (ref elem; result)
81 		elem = elem.deepdup;
82 	return result;
83 }
84 
85 TypeofDeepdup!T* deepdup(T)(T* t) => &deepdup(*t);
86 
87 Unqual!T deepdup(T)(T t) if (!is(T == struct) &&
88 							 !is(T == class) &&
89 							 !isArray!T &&
90 							 !is(T.Types) &&
91 							 !isPointer!T)
92 	=> cast(Unqual!T)t;
93 
94 @safe pure nothrow:
95 
96 ///
97 unittest {
98 	auto x = [1, 2, 3];
99 	assert(x == x.dup);
100 	auto y = x;
101 	assert(&x[0] == &y[0]);
102 	assert(&x[0] != &x.dup[0]);
103 }
104 
105 ///
106 unittest {
107 	auto x = [[1], [2], [3]];
108 	auto y = x.dup;
109 	x[0][0] = 11;
110 	assert(x[0][0] == 11);
111 	assert(y[0][0] == 11);
112 }
113 
114 ///
115 unittest {
116 	auto x = [[1], [2], [3]];
117 	auto y = x.deepdup;
118 	x[0][0] = 11;
119 	assert(x[0][0] == 11);
120 	assert(y[0][0] == 1);
121 }
122 
123 ///
124 unittest {
125 	auto x = [[1], [2], [3]];
126 	auto y = x.deepdup;
127 	x[0][0] = 11;
128 	assert(x[0][0] == 11);
129 	assert(y[0][0] == 1);
130 }
131 
132 ///
133 unittest {
134 	auto x = [[[1]], [[2]], [[3]]];
135 	auto y = x.deepdup;
136 	x[0][0][0] = 11;
137 	assert(x[0][0][0] == 11);
138 	assert(y[0][0][0] == 1);
139 }
140 
141 /// dup of static array
142 unittest {
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 	int[][3] x = [[1], [2], [3]];
153 	auto y = x.dup;
154 	x[0][0] = 11;
155 	assert(x[0][0] == 11);
156 	assert(y[0][0] == 11);
157 }
158 
159 /// deepdup of static array of dynamic arrays
160 unittest {
161 	int[][3] x = [[1], [2], [3]];
162 	auto y = x.deepdup;
163 	x[0][0] = 11;
164 	assert(x[0][0] == 11);
165 	assert(y[0][0] == 1);
166 }