1 /** 2 * Betterc: Frequently used primitives suitable for use in the BetterC D subset. 3 * 4 * Copyright: Maxim Freck, 2018. 5 * Authors: Maxim Freck <maxim@freck.pp.ru> 6 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 7 */ 8 module betterc.ds.vector; 9 10 /******* 11 * Dynamic Vector without Garbage Collection 12 */ 13 struct Vector(T) 14 { 15 private import core.stdc.stdlib: malloc, realloc, free; 16 private import core.stdc.string: memcpy, memmove; 17 18 private struct Payload { 19 T* arr; 20 size_t length; 21 size_t capacity; 22 size_t initCap; 23 24 size_t count; 25 @disable this(this); // not copyable 26 } 27 28 private Payload* payload; 29 30 /******* 31 * Constructor 32 * 33 * Params: 34 * n = Initial vector capacity 35 */ 36 this (size_t n) nothrow @nogc 37 { 38 payload = (cast(Payload*)malloc(Payload.sizeof)); 39 payload.length = 0; 40 payload.capacity = n; 41 payload.initCap = n; 42 payload.arr = (cast(T*)malloc(T.sizeof*n)); 43 payload.count = 1; 44 } 45 46 /******* 47 * Constructor 48 * 49 * Params: 50 * src = Initial vector values 51 * n = Initial vector capacity 52 */ 53 this(T[] src, size_t n = 0) nothrow @nogc 54 { 55 56 payload = (cast(Payload*)malloc(Payload.sizeof)); 57 payload.length = 0; 58 payload.initCap = n == 0 ? src.length : n; 59 payload.capacity = payload.initCap; 60 payload.arr = (cast(T*)malloc(T.sizeof*payload.capacity)); 61 payload.count = 1; 62 63 push(src); 64 } 65 66 ///ditto 67 private this(Payload* src, size_t a, size_t b) nothrow @nogc 68 { 69 70 payload = (cast(Payload*)malloc(Payload.sizeof)); 71 payload.length = b - a; 72 payload.initCap = src.initCap; 73 payload.capacity = src.capacity; 74 payload.arr = (cast(T*)malloc(T.sizeof*payload.length)); 75 payload.count = 1; 76 memcpy(payload.arr, src.arr+a, payload.length); 77 } 78 79 ///ditto 80 this(this) nothrow @nogc 81 { 82 if (payload !is null) 83 payload.count++; 84 } 85 86 ///Ref. counting during structure assignment 87 ref typeof(this) opAssign(ref typeof(this) rhs) 88 { 89 this.payload = rhs.payload; 90 if (payload !is null) 91 payload.count++; 92 93 return this; 94 } 95 96 /******* 97 * Destructor: frees all memory 98 */ 99 ~this() nothrow @nogc 100 { 101 //TODO: call elements destructors if present 102 if (payload !is null && --payload.count == 0) { 103 free(payload.arr); 104 free(payload); 105 } 106 } 107 108 /******* 109 * Shrinks vector 110 * 111 * Params: 112 * len = New vector size 113 */ 114 public ref typeof(this) shrink(size_t len = 0) nothrow @nogc 115 { 116 if (len < payload.length) payload.length = len; 117 if (payload.length == 0 || payload.capacity/payload.length > 1) shrink_to_fit(); 118 return this; 119 } 120 121 /******* 122 * Requests the removal of unused capacity. 123 * 124 */ 125 public ref typeof(this) shrink_to_fit() nothrow @nogc 126 { 127 payload.capacity = payload.length == 0 ? payload.initCap : (payload.length * 3) / 2; 128 payload.arr = (cast(T*)realloc(payload.arr, T.sizeof*payload.capacity)); 129 return this; 130 } 131 132 /******* 133 * Clears entire vector 134 * 135 */ 136 pragma(inline) 137 public ref typeof(this) clear() nothrow @nogc 138 { 139 return shrink(0); 140 } 141 142 /******* 143 * Erases nth vector element 144 * 145 * Params: 146 * n = Element number 147 */ 148 public ref typeof(this) erase(in size_t n) nothrow @nogc 149 { 150 if (n >= payload.length) return this; 151 152 if (n == payload.length - 1) { 153 payload.length--; 154 return this; 155 } 156 157 payload.length--; 158 memmove(payload.arr+n, payload.arr+n+1, payload.length - n); 159 160 return this; 161 } 162 163 /******* 164 * Trims the capacity of the vector to be the vector's current size. 165 */ 166 public ref typeof(this) trim() 167 { 168 payload.capacity = payload.length; 169 payload.arr = (cast(T*)realloc(payload.arr, T.sizeof*payload.capacity)); 170 171 return this; 172 } 173 174 /******* 175 * Returns: the current length of the vector 176 */ 177 public size_t length() const pure nothrow @nogc 178 { 179 return payload.length; 180 } 181 182 /******* 183 * Returns: the current capacity of the vector 184 */ 185 public size_t capacity() pure nothrow @nogc 186 { 187 return payload.capacity; 188 } 189 190 /******* 191 * The [] operator 192 */ 193 public auto ref T opIndex(in size_t id) pure nothrow @nogc 194 { 195 if (payload.length == 0) return T.init; 196 return payload.arr[id>=payload.length ? payload.length - 1 : id]; 197 } 198 199 /******* 200 * Appends a value to the vector 201 * 202 * Params: 203 * rhs = The value to add 204 */ 205 public ref typeof(this) push(in T rhs) nothrow @nogc 206 { 207 payload.length++; 208 if (payload.length > payload.capacity) grow(); 209 payload.arr[payload.length - 1] = rhs; 210 211 return this; 212 } 213 214 ///ditto 215 public ref typeof(this) push(in T[] rhs) nothrow @nogc 216 { 217 foreach (v; rhs) push(v); 218 return this; 219 } 220 221 ///ditto 222 public ref typeof(this) push(typeof(this) rhs) nothrow @nogc 223 { 224 foreach (v; rhs) push(v); 225 return this; 226 } 227 228 private void grow() nothrow @nogc 229 { 230 payload.capacity = (payload.capacity * 3) / 2 + 1; 231 payload.arr = (cast(T*)realloc(payload.arr, T.sizeof*payload.capacity)); 232 } 233 234 /******* 235 * Removes the last element from the vector 236 */ 237 public void pop() nothrow @nogc 238 { 239 if (payload.length > 0) payload.length--; 240 } 241 242 /******* 243 * Appends a value to the vector using << operator 244 * 245 * Params: 246 * rhs = The value to add 247 */ 248 pragma(inline) 249 public ref typeof(this) opBinary(string op)(in T rhs) nothrow @nogc if (op == "<<") 250 { 251 return push(rhs); 252 } 253 254 ///ditto 255 pragma(inline) 256 public ref typeof(this) opBinary(string op)(in T[] rhs) nothrow @nogc if (op == "<<") 257 { 258 return push(rhs); 259 } 260 261 ///ditto 262 pragma(inline) 263 public ref typeof(this) opBinary(string op)(typeof(this) rhs) nothrow @nogc if (op == "<<") 264 { 265 return push(rhs); 266 } 267 268 269 /******* 270 * The $ operator returns the length of the vector 271 */ 272 size_t opDollar() nothrow @nogc 273 { 274 return payload.length; 275 } 276 277 /******* 278 * The [a..b] operator returns the slice of the vector 279 * 280 * Returns: a new vector containing values from a to b 281 */ 282 public typeof(this) opSlice(size_t a, size_t b) nothrow @nogc 283 { 284 if (a > b) { 285 immutable c = b; 286 b = a; 287 a = c; 288 } 289 if (a > payload.length) a = payload.length; 290 if (b > payload.length) b = payload.length; 291 292 return typeof(this)(payload, a, b); 293 } 294 295 296 /******* 297 * This method is used in foreach() 298 */ 299 int opApply(scope int delegate(ref T) nothrow @nogc dg) nothrow @nogc 300 { 301 int result = 0; 302 303 foreach (size_t i; 0 .. payload.length) { 304 result = dg(payload.arr[i]); 305 if (result) break; 306 } 307 308 return result; 309 } 310 311 ///ditto 312 int opApply(scope int delegate(ref size_t, ref T) nothrow @nogc dg) nothrow @nogc 313 { 314 int result; 315 316 foreach (size_t i; 0 .. payload.length) { 317 result = dg(i, payload.arr[i]); 318 if (result) break; 319 } 320 321 return result; 322 } 323 324 /******* 325 * Returns: the pointer to the underlying vector 326 */ 327 T* ptr() pure nothrow @nogc 328 { 329 return payload.arr; 330 } 331 }