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 ref Unqual!T deepdup(T)(T t)
19 if (is(T == struct) &&
20 	!is(T.Types)) {
21 	staticMap!(TypeofDeepdup, typeof(t.tupleof)) tup;
22 	foreach (const i, Type; tup)
23 		tup[i] = t.tupleof[i].deepdup;
24 	return Unqual!T(tup);
25 }
26 
27 Tuple!(staticMap!(TypeofDeepdup, T.Types)) deepdup(T)(T t)
28 if (is(T.Types)) {
29 	staticMap!(TypeofDeepdup, T.Types) tup;
30 	foreach (const i, Type; tup)
31 		tup[i] = t.field[i].deepdup;
32 	return tuple(tup);
33 }
34 
35 Unqual!T deepdup(T)(T t)
36 if (is(T == class)) {
37 	staticMap!(TypeofDeepdup, typeof(t.tupleof)) tup;
38 	foreach (const i, Type; tup)
39 		tup[i] = t.tupleof[i].deepdup;
40 	return new Unqual!T(tup);
41 }
42 
43 TypeofDeepdup!(ElementType!T)[] deepdup(T)(T t)
44 if (isDynamicArray!T) {
45 	auto result = new TypeofDeepdup!(ElementType!T)[](t.length);
46 	foreach (const i, elem; t)
47 		result[i] = elem.deepdup;
48 	return result;
49 }
50 
51 TypeofDeepdup!(ElementType!T)[T.length] deepdup(T)(T t)
52 if (__traits(isStaticArray, T)) {
53 	TypeofDeepdup!(ElementType!T)[T.length] result = t;
54 	foreach (ref elem; result)
55 		elem = elem.deepdup;
56 	return result;
57 }
58 
59 TypeofDeepdup!T* deepdup(T)(T* t) => &deepdup(*t);
60 
61 Unqual!T deepdup(T)(T t) if (!is(T == struct) &&
62 							 !is(T == class) &&
63 							 !isArray!T &&
64 							 !is(T.Types) &&
65 							 !isPointer!T)
66 	=> cast(Unqual!T)t;
67 
68 @safe pure nothrow:
69 
70 ///
71 unittest {
72 	auto x = [1, 2, 3];
73 	assert(x == x.dup);
74 	auto y = x;
75 	assert(&x[0] == &y[0]);
76 	assert(&x[0] != &x.dup[0]);
77 }
78 
79 ///
80 unittest {
81 	auto x = [[1], [2], [3]];
82 	auto y = x.dup;
83 	x[0][0] = 11;
84 	assert(x[0][0] == 11);
85 	assert(y[0][0] == 11);
86 }
87 
88 ///
89 unittest {
90 	auto x = [[1], [2], [3]];
91 	auto y = x.deepdup;
92 	x[0][0] = 11;
93 	assert(x[0][0] == 11);
94 	assert(y[0][0] == 1);
95 }
96 
97 ///
98 unittest {
99 	auto x = [[1], [2], [3]];
100 	auto y = x.deepdup;
101 	x[0][0] = 11;
102 	assert(x[0][0] == 11);
103 	assert(y[0][0] == 1);
104 }
105 
106 ///
107 unittest {
108 	auto x = [[[1]], [[2]], [[3]]];
109 	auto y = x.deepdup;
110 	x[0][0][0] = 11;
111 	assert(x[0][0][0] == 11);
112 	assert(y[0][0][0] == 1);
113 }
114 
115 /// dup of static array
116 unittest {
117 	int[3] x = [1, 2, 3];
118 	auto y = x.dup;
119 	x[0] = 11;
120 	assert(x[0] == 11);
121 	assert(y[0] == 1);
122 }
123 
124 /// dup of static array of dynamic arrays
125 unittest {
126 	int[][3] x = [[1], [2], [3]];
127 	auto y = x.dup;
128 	x[0][0] = 11;
129 	assert(x[0][0] == 11);
130 	assert(y[0][0] == 11);
131 }
132 
133 /// deepdup of static array of dynamic arrays
134 unittest {
135 	int[][3] x = [[1], [2], [3]];
136 	auto y = x.deepdup;
137 	x[0][0] = 11;
138 	assert(x[0][0] == 11);
139 	assert(y[0][0] == 1);
140 }