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