1 /** 2 Utility functions for array processing 3 4 Copyright: © 2012 Sönke Ludwig 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 vibe.utils.array; 9 10 import vibe.internal.utilallocator; 11 12 import std.algorithm; 13 import std.range : isInputRange, isOutputRange; 14 import std.traits; 15 static import std.utf; 16 17 18 enum AppenderResetMode { 19 keepData, 20 freeData, 21 reuseData 22 } 23 24 struct AllocAppender(ArrayType : E[], E) { 25 alias ElemType = Unqual!E; 26 27 static assert(!hasIndirections!E && !hasElaborateDestructor!E); 28 29 private { 30 ElemType[] m_data; 31 ElemType[] m_remaining; 32 IAllocator m_alloc; 33 bool m_allocatedBuffer = false; 34 } 35 36 this(IAllocator alloc, ElemType[] initial_buffer = null) 37 { 38 m_alloc = alloc; 39 m_data = initial_buffer; 40 m_remaining = initial_buffer; 41 } 42 43 @disable this(this); 44 45 @property ArrayType data() { return cast(ArrayType)m_data[0 .. m_data.length - m_remaining.length]; } 46 47 void reset(AppenderResetMode reset_mode = AppenderResetMode.keepData) 48 { 49 if (reset_mode == AppenderResetMode.keepData) m_data = null; 50 else if (reset_mode == AppenderResetMode.freeData) { if (m_allocatedBuffer) m_alloc.deallocate(m_data); m_data = null; } 51 m_remaining = m_data; 52 } 53 54 /** Grows the capacity of the internal buffer so that it can hold a minumum amount of elements. 55 56 Params: 57 amount = The minimum amount of elements that shall be appendable without 58 triggering a re-allocation. 59 60 */ 61 void reserve(size_t amount) 62 @trusted { 63 size_t nelems = m_data.length - m_remaining.length; 64 if (!m_data.length) { 65 m_data = cast(ElemType[])m_alloc.allocate(amount*E.sizeof); 66 m_remaining = m_data; 67 m_allocatedBuffer = true; 68 } 69 if (m_remaining.length < amount) { 70 if (m_allocatedBuffer) { 71 void[] vdata = m_data; 72 m_alloc.reallocate(vdata, (nelems+amount)*E.sizeof); 73 m_data = () @trusted { return cast(ElemType[])vdata; } (); 74 } else { 75 auto newdata = cast(ElemType[])m_alloc.allocate((nelems+amount)*E.sizeof); 76 newdata[0 .. nelems] = m_data[0 .. nelems]; 77 m_data = newdata; 78 m_allocatedBuffer = true; 79 } 80 } 81 m_remaining = m_data[nelems .. m_data.length]; 82 } 83 84 void put(E el) 85 @safe { 86 if( m_remaining.length == 0 ) grow(1); 87 m_remaining[0] = el; 88 m_remaining = m_remaining[1 .. $]; 89 } 90 91 void put(ArrayType arr) 92 @safe { 93 if (m_remaining.length < arr.length) grow(arr.length); 94 m_remaining[0 .. arr.length] = arr[]; 95 m_remaining = m_remaining[arr.length .. $]; 96 } 97 98 static if( !hasAliasing!E ){ 99 void put(in ElemType[] arr) 100 @trusted 101 { 102 put(cast(ArrayType)arr); 103 } 104 } 105 106 static if( is(ElemType == char) ){ 107 void put(dchar el) 108 @safe 109 { 110 if( el < 128 ) put(cast(char)el); 111 else { 112 char[4] buf; 113 auto len = std.utf.encode(buf, el); 114 put(() @trusted { return cast(ArrayType)buf[0 .. len]; }()); 115 } 116 } 117 } 118 119 static if( is(ElemType == wchar) ){ 120 void put(dchar el) 121 @safe 122 { 123 if( el < 128 ) put(cast(wchar)el); 124 else { 125 wchar[3] buf; 126 auto len = std.utf.encode(buf, el); 127 put(() @trusted { return cast(ArrayType)buf[0 .. len]; } ()); 128 } 129 } 130 } 131 132 static if (!is(E == immutable) || !hasAliasing!E) { 133 /** Appends a number of bytes in-place. 134 135 The delegate will get the memory slice of the memory that follows 136 the already written data. Use `reserve` to ensure that this slice 137 has enough room. The delegate should overwrite as much of the 138 slice as desired and then has to return the number of elements 139 that should be appended (counting from the start of the slice). 140 */ 141 void append(scope size_t delegate(scope ElemType[] dst) @safe del) 142 { 143 auto n = del(m_remaining); 144 assert(n <= m_remaining.length); 145 m_remaining = m_remaining[n .. $]; 146 } 147 } 148 149 void grow(size_t min_free) 150 { 151 if( !m_data.length && min_free < 16 ) min_free = 16; 152 153 auto min_size = m_data.length + min_free - m_remaining.length; 154 auto new_size = max(m_data.length, 16); 155 while( new_size < min_size ) 156 new_size = (new_size * 3) / 2; 157 reserve(new_size - m_data.length + m_remaining.length); 158 } 159 } 160 161 unittest { 162 auto a = AllocAppender!string(theAllocator()); 163 a.put("Hello"); 164 a.put(' '); 165 a.put("World"); 166 assert(a.data == "Hello World"); 167 a.reset(); 168 assert(a.data == ""); 169 } 170 171 unittest { 172 char[4] buf; 173 auto a = AllocAppender!string(theAllocator(), buf); 174 a.put("He"); 175 assert(a.data == "He"); 176 assert(a.data.ptr == buf.ptr); 177 a.put("ll"); 178 assert(a.data == "Hell"); 179 assert(a.data.ptr == buf.ptr); 180 a.put('o'); 181 assert(a.data == "Hello"); 182 assert(a.data.ptr != buf.ptr); 183 } 184 185 unittest { 186 char[4] buf; 187 auto a = AllocAppender!string(theAllocator(), buf); 188 a.put("Hello"); 189 assert(a.data == "Hello"); 190 assert(a.data.ptr != buf.ptr); 191 } 192 193 unittest { 194 auto app = AllocAppender!(int[])(theAllocator); 195 app.reserve(2); 196 app.append((scope mem) { 197 assert(mem.length >= 2); 198 mem[0] = 1; 199 mem[1] = 2; 200 return size_t(2); 201 }); 202 assert(app.data == [1, 2]); 203 } 204 205 unittest { 206 auto app = AllocAppender!string(theAllocator); 207 app.reserve(3); 208 app.append((scope mem) { 209 assert(mem.length >= 3); 210 mem[0] = 'f'; 211 mem[1] = 'o'; 212 mem[2] = 'o'; 213 return size_t(3); 214 }); 215 assert(app.data == "foo"); 216 } 217 218 219 struct FixedAppender(ArrayType : E[], size_t NELEM, E) { 220 alias ElemType = Unqual!E; 221 private { 222 ElemType[NELEM] m_data; 223 size_t m_fill; 224 } 225 226 void clear() 227 { 228 m_fill = 0; 229 } 230 231 void put(E el) 232 { 233 m_data[m_fill++] = el; 234 } 235 236 static if( is(ElemType == char) ){ 237 void put(dchar el) 238 { 239 if( el < 128 ) put(cast(char)el); 240 else { 241 char[4] buf; 242 auto len = std.utf.encode(buf, el); 243 put(cast(ArrayType)buf[0 .. len]); 244 } 245 } 246 } 247 248 static if( is(ElemType == wchar) ){ 249 void put(dchar el) 250 { 251 if( el < 128 ) put(cast(wchar)el); 252 else { 253 wchar[3] buf; 254 auto len = std.utf.encode(buf, el); 255 put(cast(ArrayType)buf[0 .. len]); 256 } 257 } 258 } 259 260 void put(ArrayType arr) 261 { 262 m_data[m_fill .. m_fill+arr.length] = arr[]; 263 m_fill += arr.length; 264 } 265 266 @property ArrayType data() { return cast(ArrayType)m_data[0 .. m_fill]; } 267 268 static if (!is(E == immutable)) { 269 void reset() { m_fill = 0; } 270 } 271 } 272 273 274 /** 275 TODO: clear ring buffer fields upon removal (to run struct destructors, if T is a struct) 276 */ 277 struct FixedRingBuffer(T, size_t N = 0, bool INITIALIZE = true) { 278 private { 279 static if( N > 0 ) { 280 static if (INITIALIZE) T[N] m_buffer; 281 else T[N] m_buffer = void; 282 } else T[] m_buffer; 283 size_t m_start = 0; 284 size_t m_fill = 0; 285 } 286 287 static if( N == 0 ){ 288 this(size_t capacity) { m_buffer = new T[capacity]; } 289 } 290 291 @property bool empty() const { return m_fill == 0; } 292 293 @property bool full() const { return m_fill == m_buffer.length; } 294 295 @property size_t length() const { return m_fill; } 296 297 @property size_t freeSpace() const { return m_buffer.length - m_fill; } 298 299 @property size_t capacity() const { return m_buffer.length; } 300 301 static if( N == 0 ){ 302 /// Resets the capacity to zero and explicitly frees the memory for the buffer. 303 void dispose() 304 { 305 import core.memory : __delete; 306 __delete(m_buffer); 307 m_buffer = null; 308 m_start = m_fill = 0; 309 } 310 311 @property void capacity(size_t new_size) 312 { 313 if( m_buffer.length ){ 314 auto newbuffer = new T[new_size]; 315 auto dst = newbuffer; 316 auto newfill = min(m_fill, new_size); 317 read(dst[0 .. newfill]); 318 m_buffer = newbuffer; 319 m_start = 0; 320 m_fill = newfill; 321 } else { 322 m_buffer = new T[new_size]; 323 } 324 } 325 } 326 327 @property ref inout(T) front() inout { assert(!empty); return m_buffer[m_start]; } 328 329 @property ref inout(T) back() inout { assert(!empty); return m_buffer[mod(m_start+m_fill-1)]; } 330 331 void clear() 332 { 333 popFrontN(length); 334 assert(m_fill == 0); 335 m_start = 0; 336 } 337 338 void putBack()(T itm) { assert(m_fill < m_buffer.length); m_buffer[mod(m_start + m_fill++)] = itm; } 339 void putBack(TC : T)(TC[] itms) 340 { 341 if( !itms.length ) return; 342 assert(m_fill+itms.length <= m_buffer.length); 343 if( mod(m_start+m_fill) >= mod(m_start+m_fill+itms.length) ){ 344 size_t chunk1 = m_buffer.length - (m_start+m_fill); 345 size_t chunk2 = itms.length - chunk1; 346 m_buffer[m_start+m_fill .. m_buffer.length] = itms[0 .. chunk1]; 347 m_buffer[0 .. chunk2] = itms[chunk1 .. $]; 348 } else { 349 m_buffer[mod(m_start+m_fill) .. mod(m_start+m_fill)+itms.length] = itms[]; 350 } 351 m_fill += itms.length; 352 } 353 void putBackN(size_t n) { assert(m_fill+n <= m_buffer.length); m_fill += n; } 354 355 alias put = putBack; 356 alias putN = putBackN; 357 358 void putFront(T itm) 359 { 360 assert(m_fill < m_buffer.length); 361 m_start = mod(m_start + m_buffer.length - 1); 362 m_fill++; 363 m_buffer[m_start] = itm; 364 } 365 366 void popFront() { assert(!empty); m_start = mod(m_start+1); m_fill--; } 367 void popFrontN(size_t n) { assert(length >= n); m_start = mod(m_start + n); m_fill -= n; } 368 369 void popBack() { assert(!empty); m_fill--; } 370 void popBackN(size_t n) { assert(length >= n); m_fill -= n; } 371 372 void removeAt(Range r) 373 { 374 assert(r.m_buffer is m_buffer); 375 if (r.m_start == m_start) { popFront(); return; } 376 if( m_start + m_fill > m_buffer.length ){ 377 assert(r.m_start > m_start && r.m_start < m_buffer.length || r.m_start < mod(m_start+m_fill)); 378 if( r.m_start > m_start ){ 379 foreach(i; r.m_start .. m_buffer.length-1) 380 m_buffer[i] = m_buffer[i+1]; 381 m_buffer[$-1] = m_buffer[0]; 382 foreach(i; 0 .. mod(m_start + m_fill - 1)) 383 m_buffer[i] = m_buffer[i+1]; 384 } else { 385 foreach(i; r.m_start .. mod(m_start + m_fill - 1)) 386 m_buffer[i] = m_buffer[i+1]; 387 } 388 } else { 389 assert(r.m_start >= m_start && r.m_start < m_start+m_fill); 390 foreach(i; r.m_start .. m_start+m_fill-1) 391 m_buffer[i] = m_buffer[i+1]; 392 } 393 m_fill--; 394 destroy(m_buffer[mod(m_start+m_fill)]); // TODO: only call destroy for non-POD T 395 } 396 397 inout(T)[] peek() inout { return m_buffer[m_start .. min(m_start+m_fill, m_buffer.length)]; } 398 T[] peekDst() { 399 if( m_start + m_fill < m_buffer.length ) return m_buffer[m_start+m_fill .. $]; 400 else return m_buffer[mod(m_start+m_fill) .. m_start]; 401 } 402 403 void read(T[] dst) 404 { 405 assert(dst.length <= length); 406 if( !dst.length ) return; 407 if( mod(m_start) >= mod(m_start+dst.length) ){ 408 size_t chunk1 = m_buffer.length - m_start; 409 size_t chunk2 = dst.length - chunk1; 410 dst[0 .. chunk1] = m_buffer[m_start .. $]; 411 dst[chunk1 .. $] = m_buffer[0 .. chunk2]; 412 } else { 413 dst[] = m_buffer[m_start .. m_start+dst.length]; 414 } 415 popFrontN(dst.length); 416 } 417 418 int opApply(scope int delegate(ref T itm) @safe del) 419 { 420 if( m_start+m_fill > m_buffer.length ){ 421 foreach(i; m_start .. m_buffer.length) 422 if( auto ret = del(m_buffer[i]) ) 423 return ret; 424 foreach(i; 0 .. mod(m_start+m_fill)) 425 if( auto ret = del(m_buffer[i]) ) 426 return ret; 427 } else { 428 foreach(i; m_start .. m_start+m_fill) 429 if( auto ret = del(m_buffer[i]) ) 430 return ret; 431 } 432 return 0; 433 } 434 435 /// iterate through elements with index 436 int opApply(scope int delegate(size_t i, ref T itm) @safe del) 437 { 438 if( m_start+m_fill > m_buffer.length ){ 439 foreach(i; m_start .. m_buffer.length) 440 if( auto ret = del(i - m_start, m_buffer[i]) ) 441 return ret; 442 foreach(i; 0 .. mod(m_start+m_fill)) 443 if( auto ret = del(i + m_buffer.length - m_start, m_buffer[i]) ) 444 return ret; 445 } else { 446 foreach(i; m_start .. m_start+m_fill) 447 if( auto ret = del(i - m_start, m_buffer[i]) ) 448 return ret; 449 } 450 return 0; 451 } 452 453 ref inout(T) opIndex(size_t idx) inout { assert(idx < length); return m_buffer[mod(m_start+idx)]; } 454 455 Range opSlice() { return Range(m_buffer, m_start, m_fill); } 456 457 Range opSlice(size_t from, size_t to) 458 { 459 assert(from <= to); 460 assert(to <= m_fill); 461 return Range(m_buffer, mod(m_start+from), to-from); 462 } 463 464 size_t opDollar(size_t dim)() const if(dim == 0) { return length; } 465 466 private size_t mod(size_t n) 467 const { 468 static if( N == 0 ){ 469 /*static if(PotOnly){ 470 return x & (m_buffer.length-1); 471 } else {*/ 472 return n % m_buffer.length; 473 //} 474 } else static if( ((N - 1) & N) == 0 ){ 475 return n & (N - 1); 476 } else return n % N; 477 } 478 479 static struct Range { 480 private { 481 T[] m_buffer; 482 size_t m_start; 483 size_t m_length; 484 } 485 486 private this(T[] buffer, size_t start, size_t length) 487 { 488 m_buffer = buffer; 489 m_start = start; 490 m_length = length; 491 } 492 493 @property bool empty() const { return m_length == 0; } 494 495 @property ref inout(T) front() inout { assert(!empty); return m_buffer[m_start]; } 496 497 void popFront() 498 { 499 assert(!empty); 500 m_start++; 501 m_length--; 502 if( m_start >= m_buffer.length ) 503 m_start = 0; 504 } 505 } 506 } 507 508 unittest { 509 import std.range : only; 510 511 static assert(isInputRange!(FixedRingBuffer!int) && isOutputRange!(FixedRingBuffer!int, int)); 512 513 FixedRingBuffer!(int, 5) buf; 514 assert(buf.length == 0 && buf.freeSpace == 5); buf.put(1); // |1 . . . . 515 assert(buf.length == 1 && buf.freeSpace == 4); buf.put(2); // |1 2 . . . 516 assert(buf.length == 2 && buf.freeSpace == 3); buf.put(3); // |1 2 3 . . 517 assert(buf.length == 3 && buf.freeSpace == 2); buf.put(4); // |1 2 3 4 . 518 assert(buf.length == 4 && buf.freeSpace == 1); buf.put(5); // |1 2 3 4 5 519 assert(buf.length == 5 && buf.freeSpace == 0); 520 assert(buf.front == 1); 521 buf.popFront(); // .|2 3 4 5 522 assert(buf.front == 2); 523 buf.popFrontN(2); // . . .|4 5 524 assert(buf.front == 4); 525 assert(buf.length == 2 && buf.freeSpace == 3); 526 buf.put([6, 7, 8]); // 6 7 8|4 5 527 assert(buf.length == 5 && buf.freeSpace == 0); 528 int[5] dst; 529 buf.read(dst); // . . .|. . 530 assert(dst == [4, 5, 6, 7, 8]); 531 assert(buf.length == 0 && buf.freeSpace == 5); 532 buf.put([1, 2]); // . . .|1 2 533 assert(buf.length == 2 && buf.freeSpace == 3); 534 buf.read(dst[0 .. 2]); //|. . . . . 535 assert(dst[0 .. 2] == [1, 2]); 536 537 buf.put([0, 0, 0, 1, 2]); //|0 0 0 1 2 538 buf.popFrontN(2); //. .|0 1 2 539 buf.put([3, 4]); // 3 4|0 1 2 540 foreach(i, item; buf) 541 { 542 assert(i == item); 543 } 544 545 assert(buf.front == 0); 546 assert(buf.full); 547 buf.removeAt(buf[0..1]); //4 .|1 2 3 548 foreach(i, item; buf) 549 { 550 assert(i == item - 1); 551 } 552 553 buf.put(5); // 4 5|1 2 3 554 buf.removeAt(buf[3..4]); // 5 .|1 2 3 555 assert(buf.front == 1); buf.popFront(); 556 assert(buf.front == 2); buf.popFront(); 557 assert(buf.front == 3); buf.popFront(); 558 assert(buf.front == 5); buf.popFront(); 559 assert(buf.empty); 560 561 buf.put(1); 562 assert(buf.front == 1); 563 buf.front = 2; 564 assert(buf.front == 2); 565 buf[].front = 3; 566 assert(buf.front == 3); 567 568 buf.putFront(4); 569 assert(buf.length == 2); 570 assert(buf[].equal(only(4, 3))); 571 } 572 573 574 struct ArraySet(Key) 575 { 576 import stdx.allocator : makeArray, expandArray, dispose; 577 import stdx.allocator.building_blocks.affix_allocator : AffixAllocator; 578 579 private { 580 IAllocator AW(IAllocator a) { return a; } 581 alias AllocatorType = AffixAllocator!(IAllocator, int); 582 Key[4] m_staticEntries; 583 Key[] m_entries; 584 AllocatorType m_allocator; 585 } 586 587 ~this() 588 @trusted { 589 if (m_entries.ptr) { 590 if (--allocator.prefix(m_entries) <= 0) { 591 try allocator.dispose(m_entries); 592 catch (Exception e) assert(false, e.msg); // should never happen 593 } 594 } 595 } 596 597 this(this) 598 @trusted { 599 if (m_entries.ptr) { 600 allocator.prefix(m_entries)++; 601 } 602 } 603 604 @property ArraySet dup() 605 { 606 ArraySet ret; 607 ret.m_staticEntries = m_staticEntries; 608 ret.m_allocator = m_allocator; 609 610 if (m_entries.length) { 611 Key[] duped; 612 () @trusted { 613 try duped = allocator.makeArray!(Key)(m_entries.length); 614 catch (Exception e) assert(false, e.msg); 615 if (!duped.length) 616 assert(false, "Failed to allocate memory for duplicated "~ArraySet.stringof); 617 allocator.prefix(duped) = 1; 618 } (); 619 duped[] = m_entries[]; 620 ret.m_entries = duped; 621 } 622 623 return ret; 624 } 625 626 void setAllocator(IAllocator allocator) 627 in { assert(m_entries.ptr is null, "Cannot set allocator after elements have been inserted."); } 628 do { 629 m_allocator = AllocatorType(AW(allocator)); 630 } 631 632 bool opBinaryRight(string op)(Key key) if (op == "in") { return contains(key); } 633 634 int opApply(int delegate(ref Key) @safe del) 635 { 636 foreach (ref k; m_staticEntries) 637 if (k != Key.init) 638 if (auto ret = del(k)) 639 return ret; 640 foreach (ref k; m_entries) 641 if (k != Key.init) 642 if (auto ret = del(k)) 643 return ret; 644 return 0; 645 } 646 647 bool contains(Key key) 648 const { 649 foreach (ref k; m_staticEntries) if (k == key) return true; 650 foreach (ref k; m_entries) if (k == key) return true; 651 return false; 652 } 653 654 void insert(Key key) 655 { 656 if (contains(key)) return; 657 658 foreach (ref k; m_staticEntries) 659 if (k == Key.init) { 660 k = key; 661 return; 662 } 663 664 foreach (ref k; m_entries) 665 if (k == Key.init) { 666 k = key; 667 return; 668 } 669 670 size_t oldlen = m_entries.length; 671 672 () @trusted { 673 try { 674 if (!oldlen) { 675 m_entries = allocator.makeArray!Key(64); 676 assert(m_entries.length, "Failed to allocate memory for "~ArraySet.stringof); 677 allocator.prefix(m_entries) = 1; 678 } else { 679 int oldrc = allocator.prefix(m_entries); 680 if (!allocator.expandArray(m_entries, max(64, oldlen * 3 / 4))) 681 assert(false, "Failed to allocate memory for "~ArraySet.stringof); 682 allocator.prefix(m_entries) = oldrc; 683 } 684 } catch (Exception e) assert(false, e.msg); 685 } (); 686 687 m_entries[oldlen] = key; 688 } 689 690 void remove(Key key) 691 { 692 foreach (ref k; m_staticEntries) if (k == key) { k = Key.init; return; } 693 foreach (ref k; m_entries) if (k == key) { k = Key.init; return; } 694 } 695 696 ref allocator() 697 nothrow @trusted { 698 try { 699 auto palloc = m_allocator._parent; 700 if (!palloc) { 701 assert(vibeThreadAllocator !is null, "No theAllocator set!?"); 702 m_allocator = AllocatorType(AW(vibeThreadAllocator)); 703 } 704 } catch (Exception e) assert(false, e.msg); // should never throw 705 return m_allocator; 706 } 707 } 708 709 @safe nothrow unittest { 710 import stdx.allocator : allocatorObject; 711 import stdx.allocator.mallocator : Mallocator; 712 713 ArraySet!int s; 714 s.setAllocator(() @trusted { return Mallocator.instance.allocatorObject; } ()); 715 716 ArraySet!int t; 717 t = s; 718 719 s.insert(1); 720 s.insert(2); 721 s.insert(3); 722 s.insert(4); 723 assert(s.contains(1)); 724 assert(s.contains(2)); 725 assert(s.contains(3)); 726 assert(s.contains(4)); 727 assert(!t.contains(1)); 728 729 s.insert(5); 730 assert(s.contains(5)); 731 732 t = s; 733 assert(t.contains(5)); 734 assert(t.contains(1)); 735 736 s.insert(6); 737 assert(s.contains(6)); 738 assert(t.contains(6)); 739 740 s = ArraySet!int.init; 741 assert(!s.contains(1)); 742 assert(t.contains(1)); 743 assert(t.contains(6)); 744 745 s = t.dup; 746 assert(s.contains(1)); 747 assert(s.contains(6)); 748 749 t.remove(1); 750 assert(!t.contains(1)); 751 assert(s.contains(1)); 752 assert(t.contains(2)); 753 assert(t.contains(6)); 754 755 t.remove(6); 756 assert(!t.contains(6)); 757 assert(s.contains(6)); 758 assert(t.contains(5)); 759 }