1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /** 6 * Algorithms that modify its arguments. 7 * 8 * Copyright: Eugene Wissner 2017-2020. 9 * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, 10 * Mozilla Public License, v. 2.0). 11 * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) 12 * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/algorithm/mutation.d, 13 * tanya/algorithm/mutation.d) 14 */ 15 module tanya.algorithm.mutation; 16 17 static import tanya.memory.lifetime; 18 static import tanya.memory.op; 19 import tanya.meta.trait; 20 import tanya.meta.transform; 21 import tanya.range; 22 23 /** 24 * Copies the $(D_PARAM source) range into the $(D_PARAM target) range. 25 * 26 * Params: 27 * Source = Input range type. 28 * Target = Output range type. 29 * source = Source input range. 30 * target = Target output range. 31 * 32 * Returns: $(D_PARAM target) range, whose front element is the one past the 33 * last element copied. 34 * 35 * Precondition: $(D_PARAM target) should be large enough to accept all 36 * $(D_PARAM source) elements. 37 */ 38 Target copy(Source, Target)(Source source, Target target) 39 if (isInputRange!Source && isOutputRange!(Target, ElementType!Source)) 40 in 41 { 42 static if (hasLength!Source && hasLength!Target) 43 { 44 assert(target.length >= source.length); 45 } 46 } 47 do 48 { 49 alias E = ElementType!Source; 50 static if (isDynamicArray!Source 51 && is(Unqual!E == ElementType!Target) 52 && !hasElaborateCopyConstructor!E 53 && !hasElaborateAssign!E 54 && !hasElaborateDestructor!E) 55 { 56 if (source.ptr < target.ptr 57 && (() @trusted => (target.ptr - source.ptr) < source.length)()) 58 { 59 tanya.memory.op.copyBackward(source, target); 60 } 61 else if (source.ptr !is target.ptr) 62 { 63 tanya.memory.op.copy(source, target); 64 } 65 return target[source.length .. $]; 66 } 67 else 68 { 69 for (; !source.empty; source.popFront()) 70 { 71 put(target, source.front); 72 } 73 return target; 74 } 75 } 76 77 /// 78 @nogc nothrow pure @safe unittest 79 { 80 import std.algorithm.comparison : equal; 81 82 const int[2] source = [1, 2]; 83 int[2] target = [3, 4]; 84 85 copy(source[], target[]); 86 assert(equal(source[], target[])); 87 } 88 89 /** 90 * Fills $(D_PARAM range) with $(D_PARAM value). 91 * 92 * Params: 93 * Range = Input range type. 94 * Value = Filler type. 95 * range = Input range. 96 * value = Filler. 97 */ 98 void fill(Range, Value)(Range range, auto ref Value value) 99 if (isInputRange!Range && isAssignable!(ElementType!Range, Value)) 100 { 101 static if (!isDynamicArray!Range && is(typeof(range[] = value))) 102 { 103 range[] = value; 104 } 105 else 106 { 107 for (; !range.empty; range.popFront()) 108 { 109 range.front = value; 110 } 111 } 112 } 113 114 /// 115 @nogc nothrow pure @safe unittest 116 { 117 import std.algorithm.comparison : equal; 118 119 int[6] actual; 120 const int[6] expected = [1, 1, 1, 1, 1, 1]; 121 122 fill(actual[], 1); 123 assert(equal(actual[], expected[])); 124 } 125 126 /** 127 * Fills $(D_PARAM range) with $(D_PARAM value) assuming the elements of the 128 * $(D_PARAM range) aren't initialized. 129 * 130 * Params: 131 * Range = Input range type. 132 * Value = Initializer type. 133 * range = Input range. 134 * value = Initializer. 135 */ 136 void uninitializedFill(Range, Value)(Range range, auto ref Value value) 137 if (isInputRange!Range && hasLvalueElements!Range 138 && isAssignable!(ElementType!Range, Value)) 139 { 140 static if (hasElaborateDestructor!(ElementType!Range)) 141 { 142 for (; !range.empty; range.popFront()) 143 { 144 ElementType!Range* p = &range.front; 145 tanya.memory.lifetime.emplace!(ElementType!Range)(cast(void[]) (p[0 .. 1]), value); 146 } 147 } 148 else 149 { 150 fill(range, value); 151 } 152 } 153 154 /// 155 @nogc nothrow pure @safe unittest 156 { 157 import std.algorithm.comparison : equal; 158 159 int[6] actual = void; 160 const int[6] expected = [1, 1, 1, 1, 1, 1]; 161 162 uninitializedFill(actual[], 1); 163 assert(equal(actual[], expected[])); 164 } 165 166 /** 167 * Initializes all elements of the $(D_PARAM range) assuming that they are 168 * uninitialized. 169 * 170 * Params: 171 * Range = Input range type 172 * range = Input range. 173 */ 174 void initializeAll(Range)(Range range) @trusted 175 if (isInputRange!Range && hasLvalueElements!Range) 176 { 177 import tanya.memory.op : copy, fill; 178 alias T = ElementType!Range; 179 180 static if (__VERSION__ >= 2083 181 && isDynamicArray!Range 182 && __traits(isZeroInit, T)) 183 { 184 fill!0(range); 185 } 186 else 187 { 188 static immutable init = T.init; 189 for (; !range.empty; range.popFront()) 190 { 191 copy((&init)[0 .. 1], (&range.front)[0 .. 1]); 192 } 193 } 194 } 195 196 /// 197 @nogc nothrow pure @safe unittest 198 { 199 import std.algorithm.comparison : equal; 200 201 int[2] actual = void; 202 const int[2] expected = [0, 0]; 203 204 initializeAll(actual[]); 205 assert(equal(actual[], expected[])); 206 } 207 208 /** 209 * Destroys all elements in the $(D_PARAM range). 210 * 211 * This function has effect only if the element type of $(D_PARAM Range) has 212 * an elaborate destructor, i.e. it is a $(D_PSYMBOL struct) with an explicit 213 * or generated by the compiler destructor. 214 * 215 * Params: 216 * Range = Input range type. 217 * range = Input range. 218 */ 219 void destroyAll(Range)(Range range) 220 if (isInputRange!Range && hasLvalueElements!Range) 221 { 222 tanya.memory.lifetime.destroyAllImpl!(Range, ElementType!Range)(range); 223 } 224 225 /// 226 @nogc nothrow pure @trusted unittest 227 { 228 static struct WithDtor 229 { 230 private size_t* counter; 231 ~this() @nogc nothrow pure 232 { 233 if (this.counter !is null) 234 { 235 ++(*this.counter); 236 } 237 } 238 } 239 240 size_t counter; 241 WithDtor[2] withDtor = [WithDtor(&counter), WithDtor(&counter)]; 242 243 destroyAll(withDtor[]); 244 245 assert(counter == 2); 246 }