1 /** 2 Utility functions for array processing 3 4 Copyright: © 2012 RejectedSoftware e.K. 5 License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. 6 Authors: Sönke Ludwig 7 */ 8 module vutil.array; 9 10 import vutil.memory; 11 12 import std.algorithm; 13 import std.range : isInputRange, isOutputRange; 14 import std.traits; 15 static import std.utf; 16 17 18 void removeFromArray(T)(ref T[] array, T item) 19 { 20 foreach( i; 0 .. array.length ) 21 if( array[i] is item ){ 22 removeFromArrayIdx(array, i); 23 return; 24 } 25 } 26 27 void removeFromArrayIdx(T)(ref T[] array, size_t idx) 28 { 29 foreach( j; idx+1 .. array.length) 30 array[j-1] = array[j]; 31 array.length = array.length-1; 32 } 33 34 enum AppenderResetMode { 35 keepData, 36 freeData, 37 reuseData 38 } 39 40 struct AllocAppender(ArrayType : E[], E) { 41 alias ElemType = Unqual!E; 42 43 static assert(!hasIndirections!E && !hasElaborateDestructor!E); 44 45 private { 46 ElemType[] m_data; 47 ElemType[] m_remaining; 48 Allocator m_alloc; 49 bool m_allocatedBuffer = false; 50 } 51 52 this(Allocator alloc, ElemType[] initial_buffer = null) 53 { 54 m_alloc = alloc; 55 m_data = initial_buffer; 56 m_remaining = initial_buffer; 57 } 58 59 @disable this(this); 60 61 @property ArrayType data() { return cast(ArrayType)m_data[0 .. m_data.length - m_remaining.length]; } 62 63 void reset(AppenderResetMode reset_mode = AppenderResetMode.keepData) 64 { 65 if (reset_mode == AppenderResetMode.keepData) m_data = null; 66 else if (reset_mode == AppenderResetMode.freeData) { if (m_allocatedBuffer) m_alloc.free(m_data); m_data = null; } 67 m_remaining = m_data; 68 } 69 70 void reserve(size_t amt) 71 { 72 size_t nelems = m_data.length - m_remaining.length; 73 if (!m_data.length) { 74 m_data = cast(ElemType[])m_alloc.alloc(amt*E.sizeof); 75 m_remaining = m_data; 76 m_allocatedBuffer = true; 77 } 78 if (m_remaining.length < amt) { 79 debug { 80 import std.digest.crc; 81 auto checksum = crc32Of(m_data[0 .. nelems]); 82 } 83 if (m_allocatedBuffer) m_data = cast(ElemType[])m_alloc.realloc(m_data, (nelems+amt)*E.sizeof); 84 else { 85 auto newdata = cast(ElemType[])m_alloc.alloc((nelems+amt)*E.sizeof); 86 newdata[0 .. nelems] = m_data[0 .. nelems]; 87 m_data = newdata; 88 m_allocatedBuffer = true; 89 } 90 debug assert(crc32Of(m_data[0 .. nelems]) == checksum); 91 } 92 m_remaining = m_data[nelems .. m_data.length]; 93 } 94 95 void put(E el) 96 { 97 if( m_remaining.length == 0 ) grow(1); 98 m_remaining[0] = el; 99 m_remaining = m_remaining[1 .. $]; 100 } 101 102 void put(ArrayType arr) 103 { 104 if (m_remaining.length < arr.length) grow(arr.length); 105 m_remaining[0 .. arr.length] = arr[]; 106 m_remaining = m_remaining[arr.length .. $]; 107 } 108 109 static if( !hasAliasing!E ){ 110 void put(in ElemType[] arr){ 111 put(cast(ArrayType)arr); 112 } 113 } 114 115 static if( is(ElemType == char) ){ 116 void put(dchar el) 117 { 118 if( el < 128 ) put(cast(char)el); 119 else { 120 char[4] buf; 121 auto len = std.utf.encode(buf, el); 122 put(cast(ArrayType)buf[0 .. len]); 123 } 124 } 125 } 126 127 static if( is(ElemType == wchar) ){ 128 void put(dchar el) 129 { 130 if( el < 128 ) put(cast(wchar)el); 131 else { 132 wchar[3] buf; 133 auto len = std.utf.encode(buf, el); 134 put(cast(ArrayType)buf[0 .. len]); 135 } 136 } 137 } 138 139 void grow(size_t min_free) 140 { 141 if( !m_data.length && min_free < 16 ) min_free = 16; 142 143 auto min_size = m_data.length + min_free - m_remaining.length; 144 auto new_size = max(m_data.length, 16); 145 while( new_size < min_size ) 146 new_size = (new_size * 3) / 2; 147 reserve(new_size - m_data.length + m_remaining.length); 148 } 149 } 150 151 unittest { 152 auto a = AllocAppender!string(defaultAllocator()); 153 a.put("Hello"); 154 a.put(' '); 155 a.put("World"); 156 assert(a.data == "Hello World"); 157 a.reset(); 158 assert(a.data == ""); 159 } 160 161 unittest { 162 char[4] buf; 163 auto a = AllocAppender!string(defaultAllocator(), buf); 164 a.put("He"); 165 assert(a.data == "He"); 166 assert(a.data.ptr == buf.ptr); 167 a.put("ll"); 168 assert(a.data == "Hell"); 169 assert(a.data.ptr == buf.ptr); 170 a.put('o'); 171 assert(a.data == "Hello"); 172 assert(a.data.ptr != buf.ptr); 173 } 174 175 unittest { 176 char[4] buf; 177 auto a = AllocAppender!string(defaultAllocator(), buf); 178 a.put("Hello"); 179 assert(a.data == "Hello"); 180 assert(a.data.ptr != buf.ptr); 181 } 182 183 struct FixedAppender(ArrayType : E[], size_t NELEM, E) { 184 alias ElemType = Unqual!E; 185 private { 186 ElemType[NELEM] m_data; 187 size_t m_fill; 188 } 189 190 void clear() 191 { 192 m_fill = 0; 193 } 194 195 void put(E el) 196 { 197 m_data[m_fill++] = el; 198 } 199 200 static if( is(ElemType == char) ){ 201 void put(dchar el) 202 { 203 if( el < 128 ) put(cast(char)el); 204 else { 205 char[4] buf; 206 auto len = std.utf.encode(buf, el); 207 put(cast(ArrayType)buf[0 .. len]); 208 } 209 } 210 } 211 212 static if( is(ElemType == wchar) ){ 213 void put(dchar el) 214 { 215 if( el < 128 ) put(cast(wchar)el); 216 else { 217 wchar[3] buf; 218 auto len = std.utf.encode(buf, el); 219 put(cast(ArrayType)buf[0 .. len]); 220 } 221 } 222 } 223 224 void put(ArrayType arr) 225 { 226 m_data[m_fill .. m_fill+arr.length] = (cast(ElemType[])arr)[]; 227 m_fill += arr.length; 228 } 229 230 @property ArrayType data() { return cast(ArrayType)m_data[0 .. m_fill]; } 231 232 static if (!is(E == immutable)) { 233 void reset() { m_fill = 0; } 234 } 235 } 236 237 238 /** 239 TODO: clear ring buffer fields upon removal (to run struct destructors, if T is a struct) 240 */ 241 struct FixedRingBuffer(T, size_t N = 0) { 242 private { 243 static if( N > 0 ) T[N] m_buffer; 244 else T[] m_buffer; 245 size_t m_start = 0; 246 size_t m_fill = 0; 247 } 248 249 static if( N == 0 ){ 250 bool m_freeOnDestruct; 251 this(size_t capacity) { m_buffer = new T[capacity]; } 252 ~this() { if (m_freeOnDestruct && m_buffer.length > 0) delete m_buffer; } 253 } 254 255 @property bool empty() const { return m_fill == 0; } 256 257 @property bool full() const { return m_fill == m_buffer.length; } 258 259 @property size_t length() const { return m_fill; } 260 261 @property size_t freeSpace() const { return m_buffer.length - m_fill; } 262 263 @property size_t capacity() const { return m_buffer.length; } 264 265 static if( N == 0 ){ 266 @property void freeOnDestruct(bool b) { m_freeOnDestruct = b; } 267 @property void capacity(size_t new_size) 268 { 269 if( m_buffer.length ){ 270 auto newbuffer = new T[new_size]; 271 auto dst = newbuffer; 272 auto newfill = min(m_fill, new_size); 273 read(dst[0 .. newfill]); 274 if (m_freeOnDestruct && m_buffer.length > 0) delete m_buffer; 275 m_buffer = newbuffer; 276 m_start = 0; 277 m_fill = newfill; 278 } else { 279 if (m_freeOnDestruct && m_buffer.length > 0) delete m_buffer; 280 m_buffer = new T[new_size]; 281 } 282 } 283 } 284 285 @property ref inout(T) front() inout { assert(!empty); return m_buffer[m_start]; } 286 287 @property ref inout(T) back() inout { assert(!empty); return m_buffer[mod(m_start+m_fill-1)]; } 288 289 void clear() 290 { 291 popFrontN(length); 292 assert(m_fill == 0); 293 m_start = 0; 294 } 295 296 void put()(T itm) { assert(m_fill < m_buffer.length); m_buffer[mod(m_start + m_fill++)] = itm; } 297 void put(TC : T)(TC[] itms) 298 { 299 if( !itms.length ) return; 300 assert(m_fill+itms.length <= m_buffer.length); 301 if( mod(m_start+m_fill) >= mod(m_start+m_fill+itms.length) ){ 302 size_t chunk1 = m_buffer.length - (m_start+m_fill); 303 size_t chunk2 = itms.length - chunk1; 304 m_buffer[m_start+m_fill .. m_buffer.length] = itms[0 .. chunk1]; 305 m_buffer[0 .. chunk2] = itms[chunk1 .. $]; 306 } else { 307 m_buffer[mod(m_start+m_fill) .. mod(m_start+m_fill)+itms.length] = itms[]; 308 } 309 m_fill += itms.length; 310 } 311 void putN(size_t n) { assert(m_fill+n <= m_buffer.length); m_fill += n; } 312 313 void popFront() { assert(!empty); m_start = mod(m_start+1); m_fill--; } 314 void popFrontN(size_t n) { assert(length >= n); m_start = mod(m_start + n); m_fill -= n; } 315 316 void popBack() { assert(!empty); m_fill--; } 317 void popBackN(size_t n) { assert(length >= n); m_fill -= n; } 318 319 void removeAt(Range r) 320 { 321 assert(r.m_buffer is m_buffer); 322 if( m_start + m_fill > m_buffer.length ){ 323 assert(r.m_start >= m_start && r.m_start < m_buffer.length || r.m_start < mod(m_start+m_fill)); 324 if( r.m_start > m_start ){ 325 foreach(i; r.m_start .. m_buffer.length-1) 326 m_buffer[i] = m_buffer[i+1]; 327 m_buffer[$-1] = m_buffer[0]; 328 foreach(i; 0 .. mod(m_start + m_fill - 1)) 329 m_buffer[i] = m_buffer[i+1]; 330 } else { 331 foreach(i; r.m_start .. mod(m_start + m_fill - 1)) 332 m_buffer[i] = m_buffer[i+1]; 333 } 334 } else { 335 assert(r.m_start >= m_start && r.m_start < m_start+m_fill); 336 foreach(i; r.m_start .. m_start+m_fill-1) 337 m_buffer[i] = m_buffer[i+1]; 338 } 339 m_fill--; 340 destroy(m_buffer[mod(m_start+m_fill)]); // TODO: only call destroy for non-POD T 341 } 342 343 inout(T)[] peek() inout { return m_buffer[m_start .. min(m_start+m_fill, m_buffer.length)]; } 344 T[] peekDst() { 345 if( m_start + m_fill < m_buffer.length ) return m_buffer[m_start+m_fill .. $]; 346 else return m_buffer[mod(m_start+m_fill) .. m_start]; 347 } 348 349 void read(T[] dst) 350 { 351 assert(dst.length <= length); 352 if( !dst.length ) return; 353 if( mod(m_start) >= mod(m_start+dst.length) ){ 354 size_t chunk1 = m_buffer.length - m_start; 355 size_t chunk2 = dst.length - chunk1; 356 dst[0 .. chunk1] = m_buffer[m_start .. $]; 357 dst[chunk1 .. $] = m_buffer[0 .. chunk2]; 358 } else { 359 dst[] = m_buffer[m_start .. m_start+dst.length]; 360 } 361 popFrontN(dst.length); 362 } 363 364 int opApply(scope int delegate(ref T itm) del) 365 { 366 if( m_start+m_fill > m_buffer.length ){ 367 foreach(i; m_start .. m_buffer.length) 368 if( auto ret = del(m_buffer[i]) ) 369 return ret; 370 foreach(i; 0 .. mod(m_start+m_fill)) 371 if( auto ret = del(m_buffer[i]) ) 372 return ret; 373 } else { 374 foreach(i; m_start .. m_start+m_fill) 375 if( auto ret = del(m_buffer[i]) ) 376 return ret; 377 } 378 return 0; 379 } 380 381 ref inout(T) opIndex(size_t idx) inout { assert(idx < length); return m_buffer[mod(m_start+idx)]; } 382 383 Range opSlice() { return Range(m_buffer, m_start, m_fill); } 384 385 Range opSlice(size_t from, size_t to) 386 { 387 assert(from <= to); 388 assert(to <= m_fill); 389 return Range(m_buffer, mod(m_start+from), to-from); 390 } 391 392 size_t opDollar(size_t dim)() const if(dim == 0) { return length; } 393 394 private size_t mod(size_t n) 395 const { 396 static if( N == 0 ){ 397 /*static if(PotOnly){ 398 return x & (m_buffer.length-1); 399 } else {*/ 400 return n % m_buffer.length; 401 //} 402 } else static if( ((N - 1) & N) == 0 ){ 403 return n & (N - 1); 404 } else return n % N; 405 } 406 407 static struct Range { 408 private { 409 T[] m_buffer; 410 size_t m_start; 411 size_t m_length; 412 } 413 414 private this(T[] buffer, size_t start, size_t length) 415 { 416 m_buffer = buffer; 417 m_start = start; 418 m_length = length; 419 } 420 421 @property bool empty() const { return m_length == 0; } 422 423 @property inout(T) front() inout { assert(!empty); return m_buffer[m_start]; } 424 425 void popFront() 426 { 427 assert(!empty); 428 m_start++; 429 m_length--; 430 if( m_start >= m_buffer.length ) 431 m_start = 0; 432 } 433 } 434 } 435 436 unittest { 437 static assert(isInputRange!(FixedRingBuffer!int) && isOutputRange!(FixedRingBuffer!int, int)); 438 439 FixedRingBuffer!(int, 5) buf; 440 assert(buf.length == 0 && buf.freeSpace == 5); buf.put(1); // |1 . . . . 441 assert(buf.length == 1 && buf.freeSpace == 4); buf.put(2); // |1 2 . . . 442 assert(buf.length == 2 && buf.freeSpace == 3); buf.put(3); // |1 2 3 . . 443 assert(buf.length == 3 && buf.freeSpace == 2); buf.put(4); // |1 2 3 4 . 444 assert(buf.length == 4 && buf.freeSpace == 1); buf.put(5); // |1 2 3 4 5 445 assert(buf.length == 5 && buf.freeSpace == 0); 446 assert(buf.front == 1); 447 buf.popFront(); // .|2 3 4 5 448 assert(buf.front == 2); 449 buf.popFrontN(2); // . . .|4 5 450 assert(buf.front == 4); 451 assert(buf.length == 2 && buf.freeSpace == 3); 452 buf.put([6, 7, 8]); // 6 7 8|4 5 453 assert(buf.length == 5 && buf.freeSpace == 0); 454 int[5] dst; 455 buf.read(dst); // . . .|. . 456 assert(dst == [4, 5, 6, 7, 8]); 457 assert(buf.length == 0 && buf.freeSpace == 5); 458 buf.put([1, 2]); // . . .|1 2 459 assert(buf.length == 2 && buf.freeSpace == 3); 460 buf.read(dst[0 .. 2]); //|. . . . . 461 assert(dst[0 .. 2] == [1, 2]); 462 } 463 464 465 struct ArraySet(Key) 466 { 467 private { 468 Key[4] m_staticEntries; 469 Key[] m_entries; 470 } 471 472 @property ArraySet dup() 473 { 474 return ArraySet(m_staticEntries, m_entries.dup); 475 } 476 477 bool opBinaryRight(string op)(Key key) if (op == "in") { return contains(key); } 478 479 int opApply(int delegate(ref Key) del) 480 { 481 foreach (ref k; m_staticEntries) 482 if (k != Key.init) 483 if (auto ret = del(k)) 484 return ret; 485 foreach (ref k; m_entries) 486 if (k != Key.init) 487 if (auto ret = del(k)) 488 return ret; 489 return 0; 490 } 491 492 bool contains(Key key) 493 const { 494 foreach (ref k; m_staticEntries) if (k == key) return true; 495 foreach (ref k; m_entries) if (k == key) return true; 496 return false; 497 } 498 499 void insert(Key key) 500 { 501 if (contains(key)) return; 502 foreach (ref k; m_staticEntries) 503 if (k == Key.init) { 504 k = key; 505 return; 506 } 507 foreach (ref k; m_entries) 508 if (k == Key.init) { 509 k = key; 510 return; 511 } 512 m_entries ~= key; 513 } 514 515 void remove(Key key) 516 { 517 foreach (ref k; m_staticEntries) if (k == key) { k = Key.init; return; } 518 foreach (ref k; m_entries) if (k == key) { k = Key.init; return; } 519 } 520 }