3 * Dynamic array implementation.
5 * Copyright: Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved
6 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
7 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
8 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/root/array.d, root/_array.d)
9 * Documentation: https://dlang.org/phobos/dmd_root_array.html
10 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/root/array.d
13 module dmd
.root
.array
;
15 import core
.stdc
.stdlib
: _compare_fp_t
;
16 import core
.stdc
.string
;
19 import dmd
.root
.string
;
21 // `qsort` is only `nothrow` since 2.081.0
22 private extern(C
) void qsort(scope void* base
, size_t nmemb
, size_t size
, _compare_fp_t compar
) nothrow @nogc;
27 debug = stomp
; // flush out dangling pointer problems by stomping on unused memory
30 extern (C
++) struct Array(T
)
36 enum SMALLARRAYCAP
= 1;
37 T
[SMALLARRAYCAP
] smallarray
; // inline storage for small arrays
42 * dim = initial length of array
44 this(size_t dim
) pure nothrow scope
54 debug (stomp
) memset(data
.ptr
, 0xFF, data
.length
);
55 if (data
.ptr
!= &smallarray
[0])
58 ///returns elements comma separated in []
59 extern(D
) const(char)[] toString() const
61 static const(char)[] toStringImpl(alias toStringFunc
, Array
)(Array
* a
, bool quoted
= false)
63 const(char)[][] buf
= (cast(const(char)[]*)mem
.xcalloc((char[]).sizeof
, a
.length
))[0 .. a
.length
];
64 size_t len
= 2; // [ and ]
65 const seplen
= quoted ?
3 : 1; // ',' or null terminator and optionally '"'
67 len
+= 1; // null terminator
70 foreach (u
; 0 .. a
.length
)
72 static if (is(typeof(a
.data
[u
] is null)))
74 if (a
.data
[u
] is null)
77 buf
[u
] = toStringFunc(a
.data
[u
]);
81 buf
[u
] = toStringFunc(a
.data
[u
]);
84 len
+= buf
[u
].length
+ seplen
;
87 char[] str = (cast(char*)mem
.xmalloc_noscan(len
))[0..len
];
90 char* p
= str.ptr
+ 1;
91 foreach (u
; 0 .. a
.length
)
97 memcpy(p
, buf
[u
].ptr
, buf
[u
].length
);
104 assert(p
- str.ptr
== str.length
- 1); // null terminator
106 return str[0 .. $-1];
109 static if (is(typeof(T
.init
.toString())))
111 return toStringImpl
!(a
=> a
.toString
)(&this);
113 else static if (is(typeof(T
.init
.toDString())))
115 return toStringImpl
!(a
=> a
.toDString
)(&this, true);
123 const(char)* toChars() const
128 ref Array
push(T ptr
) return pure nothrow
131 data
[length
++] = ptr
;
135 extern (D
) ref Array
pushSlice(T
[] a
) return pure nothrow
137 const oldLength
= length
;
138 setDim(oldLength
+ a
.length
);
139 memcpy(data
.ptr
+ oldLength
, a
.ptr
, a
.length
* T
.sizeof
);
143 ref Array
append(typeof(this)* a
) return pure nothrow
149 void reserve(size_t nentries
) pure nothrow
151 //printf("Array::reserve: length = %d, data.length = %d, nentries = %d\n", cast(int)length, cast(int)data.length, cast(int)nentries);
154 void enlarge(size_t nentries
)
156 pragma(inline
, false); // never inline cold path
157 if (data
.length
== 0)
159 // Not properly initialized, someone memset it to zero
160 if (nentries
<= SMALLARRAYCAP
)
162 data
= SMALLARRAYCAP ? smallarray
[] : null;
166 auto p
= cast(T
*)mem
.xmalloc(nentries
* T
.sizeof
);
167 data
= p
[0 .. nentries
];
170 else if (data
.length
== SMALLARRAYCAP
)
172 const allocdim
= length
+ nentries
;
173 auto p
= cast(T
*)mem
.xmalloc(allocdim
* T
.sizeof
);
174 memcpy(p
, smallarray
.ptr
, length
* T
.sizeof
);
175 data
= p
[0 .. allocdim
];
179 /* Increase size by 1.5x to avoid excessive memory fragmentation
181 auto increment
= length
/ 2;
182 if (nentries
> increment
) // if 1.5 is not enough
183 increment
= nentries
;
184 const allocdim
= length
+ increment
;
187 // always move using allocate-copy-stomp-free
188 auto p
= cast(T
*)mem
.xmalloc(allocdim
* T
.sizeof
);
189 memcpy(p
, data
.ptr
, length
* T
.sizeof
);
190 memset(data
.ptr
, 0xFF, data
.length
* T
.sizeof
);
194 auto p
= cast(T
*)mem
.xrealloc(data
.ptr
, allocdim
* T
.sizeof
);
195 data
= p
[0 .. allocdim
];
200 if (length
< data
.length
)
201 memset(data
.ptr
+ length
, 0xFF, (data
.length
- length
) * T
.sizeof
);
206 if (length
< data
.length
)
207 memset(data
.ptr
+ length
, 0xFF, (data
.length
- length
) * T
.sizeof
);
211 if (data
.length
- length
< nentries
) // false means hot path
215 void remove(size_t i
) pure nothrow @nogc
218 memmove(data
.ptr
+ i
, data
.ptr
+ i
+ 1, (length
- i
- 1) * T
.sizeof
);
220 debug (stomp
) memset(data
.ptr
+ length
, 0xFF, T
.sizeof
);
223 void insert(size_t index
, typeof(this)* a
) pure nothrow
230 memmove(data
.ptr
+ index
+ d
, data
.ptr
+ index
, (length
- index
) * T
.sizeof
);
231 memcpy(data
.ptr
+ index
, a
.data
.ptr
, d
* T
.sizeof
);
236 extern (D
) void insert(size_t index
, T
[] a
) pure nothrow
241 memmove(data
.ptr
+ index
+ d
, data
.ptr
+ index
, (length
- index
) * T
.sizeof
);
242 memcpy(data
.ptr
+ index
, a
.ptr
, d
* T
.sizeof
);
246 void insert(size_t index
, T ptr
) pure nothrow
249 memmove(data
.ptr
+ index
+ 1, data
.ptr
+ index
, (length
- index
) * T
.sizeof
);
254 /// Insert 'count' copies of 'value' at 'index' position
255 void insert(size_t index
, size_t count
, T value
) pure nothrow
261 memmove(data
.ptr
+ index
+ count
, data
.ptr
+ index
, (length
- index
) * T
.sizeof
);
262 data
[index
.. index
+ count
] = value
;
266 void setDim(size_t newdim
) pure nothrow
270 reserve(newdim
- length
);
275 size_t
find(T ptr
) const nothrow pure
277 foreach (i
; 0 .. length
)
283 bool contains(T ptr
) const nothrow pure
285 return find(ptr
) != size_t
.max
;
288 ref inout(T
) opIndex(size_t i
) inout nothrow pure
291 // This is called so often the array bounds become expensive
297 inout(T
)* tdata() inout pure nothrow @nogc @trusted
302 Array
!T
* copy() const pure nothrow
304 auto a
= new Array
!T();
306 memcpy(a
.data
.ptr
, data
.ptr
, length
* T
.sizeof
);
310 void shift(T ptr
) pure nothrow
313 memmove(data
.ptr
+ 1, data
.ptr
, length
* T
.sizeof
);
318 void zero() nothrow pure @nogc
320 data
[0 .. length
] = T
.init
;
323 T
pop() nothrow pure @nogc
328 auto result
= data
[length
- 1];
333 return data
[--length
];
336 extern (D
) inout(T
)[] opSlice() inout nothrow pure @nogc
338 return data
[0 .. length
];
341 extern (D
) inout(T
)[] opSlice(size_t a
, size_t b
) inout nothrow pure @nogc
343 assert(a
<= b
&& b
<= length
);
348 * Sort the elements of an array
350 * This function relies on `qsort`.
353 * pred = Predicate to use. Should be a function of type
354 * `int function(scope const T* e1, scope const T* e2) nothrow`.
355 * The return value of this function should follow the
356 * usual C rule: `e1 >= e2 ? (e1 > e2) : -1`.
357 * The function can have D linkage.
360 * A reference to this, for easy chaining.
362 extern(D
) ref typeof(this) sort (alias pred
) () nothrow
366 qsort(this.data
.ptr
, this.length
, T
.sizeof
, &arraySortWrapper
!(T
, pred
));
370 /// Ditto, but use `opCmp` by default
371 extern(D
) ref typeof(this) sort () () nothrow
372 if (is(typeof(this.data
[0].opCmp(this.data
[1])) : int))
374 return this.sort
!(function (scope const(T
)* pe1
, scope const(T
)* pe2
) => pe1
.opCmp(*pe2
));
377 alias opDollar
= length
;
379 deprecated("use `.length` instead")
380 extern(D
) size_t
dim() const @nogc nothrow pure @safe { return length
; }
385 // Test for objects implementing toString()
389 string
toString() const
394 auto array
= Array
!S(4);
395 assert(array
.toString() == "[S,S,S,S]");
397 assert(array
.toString() == "[]");
399 // Test for toDString()
400 auto strarray
= Array
!(const(char)*)(2);
401 strarray
[0] = "hello";
402 strarray
[1] = "world";
403 auto str = strarray
.toString();
404 assert(str == `["hello","world"]`);
405 // Test presence of null terminator.
406 assert(str.ptr
[str.length
] == '\0');
408 // Test printing an array of classes, which can be null
411 override string
toString() const
416 auto nullarray
= Array
!C(2);
417 nullarray
[0] = new C();
419 assert(nullarray
.toString() == `[x,null]`);
424 auto array
= Array
!double(4);
428 assert(array
[0] == 10);
429 assert(array
.find(10) == 0);
430 assert(array
.find(20) == 5);
431 assert(!array
.contains(99));
433 assert(array
.length
== 5);
434 assert(array
[1] == 15);
435 assert(array
.pop() == 20);
436 assert(array
.length
== 4);
438 assert(array
[1] == 30);
439 assert(array
[2] == 15);
444 auto arrayA
= Array
!int(0);
445 int[3] buf
= [10, 15, 20];
446 arrayA
.pushSlice(buf
);
447 assert(arrayA
[] == buf
[]);
448 auto arrayPtr
= arrayA
.copy();
450 assert((*arrayPtr
)[] == arrayA
[]);
451 assert(arrayPtr
.tdata
!= arrayA
.tdata
);
454 int[2] buf2
= [100, 200];
455 arrayPtr
.pushSlice(buf2
);
457 arrayA
.append(arrayPtr
);
458 assert(arrayA
[3..$] == buf2
[]);
459 arrayA
.insert(0, arrayPtr
);
460 assert(arrayA
[] == [100, 200, 10, 15, 20, 100, 200]);
467 arrayA
.pushSlice([5, 6]);
468 arrayA
.insert(1, [1, 2]);
469 assert(arrayA
[] == [5, 1, 2, 6]);
470 arrayA
.insert(0, [7, 8]);
471 arrayA
.insert(arrayA
.length
, [0, 9]);
472 assert(arrayA
[] == [7, 8, 5, 1, 2, 6, 0, 9]);
473 arrayA
.insert(4, 3, 8);
474 assert(arrayA
[] == [7, 8, 5, 1, 8, 8, 8, 2, 6, 0, 9]);
475 arrayA
.insert(0, 3, 8);
476 assert(arrayA
[] == [8, 8, 8, 7, 8, 5, 1, 8, 8, 8, 2, 6, 0, 9]);
477 arrayA
.insert(arrayA
.length
, 3, 8);
478 assert(arrayA
[] == [8, 8, 8, 7, 8, 5, 1, 8, 8, 8, 2, 6, 0, 9, 8, 8, 8]);
482 * Exposes the given root Array as a standard D array.
484 * array = the array to expose.
486 * The given array exposed to a standard D array.
488 @property inout(T
)[] peekSlice(T
)(inout(Array
!T
)* array
) pure nothrow @nogc
490 return array ?
(*array
)[] : null;
494 * Splits the array at $(D index) and expands it to make room for $(D length)
495 * elements by shifting everything past $(D index) to the right.
497 * array = the array to split.
498 * index = the index to split the array from.
499 * length = the number of elements to make room for starting at $(D index).
501 void split(T
)(ref Array
!T array
, size_t index
, size_t length
) pure nothrow
505 auto previousDim
= array
.length
;
506 array
.setDim(array
.length
+ length
);
507 for (size_t i
= previousDim
; i
> index
;)
510 array
[i
+ length
] = array
[i
];
516 auto array
= Array
!int();
518 assert([] == array
[]);
519 array
.push(1).push(3);
522 assert([1, 2, 3] == array
[]);
527 assert([1, 2, 8, 20, 4, 3] == array
[]);
529 assert([1, 2, 8, 20, 4, 3] == array
[]);
532 assert([123, 1, 2, 8, 20, 4, 3] == array
[]);
537 assert([123, 421, 910, 123, 1, 2, 8, 20, 4, 3] == (&array
).peekSlice());
541 * Reverse an array in-place.
547 T
[] reverse(T
)(T
[] a
) pure nothrow @nogc @safe
551 const mid
= (a
.length
+ 1) >> 1;
552 foreach (i
; 0 .. mid
)
565 assert(reverse(a1
) == []);
567 assert(reverse(a2
) == [2]);
569 assert(reverse(a3
) == [3,2]);
571 assert(reverse(a4
) == [4,3,2]);
572 int[] a5
= [2,3,4,5];
573 assert(reverse(a5
) == [5,4,3,2]);
578 //test toString/toChars. Identifier is a simple object that has a usable .toString
579 import dmd
.identifier
: Identifier
;
580 import core
.stdc
.string
: strcmp
;
582 auto array
= Array
!Identifier();
583 array
.push(new Identifier("id1"));
584 array
.push(new Identifier("id2"));
586 string expected
= "[id1,id2]";
587 assert(array
.toString
== expected
);
588 assert(strcmp(array
.toChars
, expected
.ptr
) == 0);
591 /// Predicate to wrap a D function passed to `qsort`
592 private template arraySortWrapper(T
, alias fn
)
594 pragma(mangle
, "arraySortWrapper_" ~ T
.mangleof
~ "_" ~ fn
.mangleof
)
595 extern(C
) int arraySortWrapper(scope const void* e1
, scope const void* e2
)
597 return fn(cast(const(T
*))e1
, cast(const(T
*))e2
);
604 Array
!(const(char)*) strings
;
605 strings
.push("World");
607 strings
.push("baguette");
608 strings
.push("Avocado");
609 strings
.push("Hello");
610 // Newer frontend versions will work with (e1, e2) and infer the type
611 strings
.sort
!(function (scope const char** e1
, scope const char** e2
) => strcmp(*e1
, *e2
));
612 assert(strings
[0] == "Avocado");
613 assert(strings
[1] == "Foo");
614 assert(strings
[2] == "Hello");
615 assert(strings
[3] == "World");
616 assert(strings
[4] == "baguette");
618 /// opCmp automatically supported
619 static struct MyStruct
623 int opCmp(const ref MyStruct other
) const nothrow
626 return other
.a
- this.a
;
631 arr1
.push(MyStruct(2));
632 arr1
.push(MyStruct(4));
633 arr1
.push(MyStruct(256));
634 arr1
.push(MyStruct(42));
636 assert(arr1
[0].a
== 256);
637 assert(arr1
[1].a
== 42);
638 assert(arr1
[2].a
== 4);
639 assert(arr1
[3].a
== 2);
641 /// But only if user defined
642 static struct OtherStruct
646 static int pred (scope const OtherStruct
* pe1
, scope const OtherStruct
* pe2
)
647 nothrow @nogc pure @safe
649 return pe1
.a
- pe2
.a
;
653 static assert (!is(typeof(Array
!(OtherStruct
).init
.sort())));
654 static assert (!is(typeof(Array
!(OtherStruct
).init
.sort
!pred
)));
658 * Iterates the given array and calls the given callable for each element.
660 * Use this instead of `foreach` when the array may expand during iteration.
663 * callable = the callable to call for each element
664 * array = the array to iterate
666 * See_Also: $(REF foreachDsymbol, dmd, dsymbol)
668 template each(alias callable
, T
)
669 if (is(ReturnType
!(typeof((T t
) => callable(t
))) == void))
671 void each(ref Array
!T array
)
673 // Do not use foreach, as the size of the array may expand during iteration
674 for (size_t i
= 0; i
< array
.length
; ++i
)
678 void each(Array
!T
* array
)
681 each
!callable(*array
);
686 @
("iterate over an Array") unittest
688 static immutable expected
= [2, 3, 4, 5];
692 foreach (e
; expected
)
700 assert(result
== expected
);
703 @
("iterate over a pointer to an Array") unittest
705 static immutable expected
= [2, 3, 4, 5];
707 auto array
= new Array
!int;
709 foreach (e
; expected
)
717 assert(result
== expected
);
720 @
("iterate while appending to the array being iterated") unittest
722 static immutable expected
= [2, 3, 4, 5];
726 foreach (e
; expected
[0 .. $ - 1])
738 assert(array
[] == expected
);
739 assert(result
== expected
);
743 * Iterates the given array and calls the given callable for each element.
745 * If `callable` returns `!= 0`, it will stop the iteration and return that
746 * value, otherwise it will return 0.
748 * Use this instead of `foreach` when the array may expand during iteration.
751 * callable = the callable to call for each element
752 * array = the array to iterate
754 * Returns: the last value returned by `callable`
755 * See_Also: $(REF foreachDsymbol, dmd, dsymbol)
757 template each(alias callable
, T
)
758 if (is(ReturnType
!(typeof((T t
) => callable(t
))) == int))
760 int each(ref Array
!T array
)
762 // Do not use foreach, as the size of the array may expand during iteration
763 for (size_t i
= 0; i
< array
.length
; ++i
)
765 if (const result
= callable(array
[i
]))
772 int each(Array
!T
* array
)
774 return array ? each
!callable(*array
) : 0;
779 @
("iterate over an Array and stop the iteration") unittest
783 foreach (e
; [2, 3, 4, 5])
787 const returnValue
= array
.each
!((e
) {
796 assert(result
== [2, 3]);
797 assert(returnValue
== 8);
800 @
("iterate over an Array") unittest
802 static immutable expected
= [2, 3, 4, 5];
806 foreach (e
; expected
)
810 const returnValue
= array
.each
!((e
) {
815 assert(result
== expected
);
816 assert(returnValue
== 0);
819 @
("iterate over a pointer to an Array and stop the iteration") unittest
821 auto array
= new Array
!int;
823 foreach (e
; [2, 3, 4, 5])
827 const returnValue
= array
.each
!((e
) {
836 assert(result
== [2, 3]);
837 assert(returnValue
== 9);
840 @
("iterate while appending to the array being iterated and stop the iteration") unittest
849 const returnValue
= array
.each
!((e
) {
861 static immutable expected
= [2, 3, 1];
863 assert(array
[] == expected
);
864 assert(result
== expected
);
865 assert(returnValue
== 7);
868 /// Returns: A static array constructed from `array`.
869 pragma(inline
, true) T
[n
] staticArray(T
, size_t n
)(auto ref T
[n
] array
)
875 pure nothrow @safe @nogc unittest
877 enum a
= [0, 1].staticArray
;
878 static assert(is(typeof(a
) == int[2]));
879 static assert(a
== [0, 1]);
882 /// Returns: `true` if the two given ranges are equal
883 bool equal(Range1
, Range2
)(Range1 range1
, Range2 range2
)
887 static if (is(T U
: U
[]))
891 enum isArray
= false;
894 static if (isArray
!Range1
&& isArray
!Range2
&& is(typeof(range1
== range2
)))
895 return range1
== range2
;
899 static if (hasLength
!Range1
&& hasLength
!Range2
&& is(typeof(r1
.length
== r2
.length
)))
901 if (range1
.length
!= range2
.length
)
905 for (; !range1
.empty
; range1
.popFront(), range2
.popFront())
910 if (range1
.front
!= range2
.front
)
919 pure nothrow @nogc @safe unittest
921 enum a
= [ 1, 2, 4, 3 ].staticArray
;
922 static assert(!equal(a
[], a
[1..$]));
923 static assert(equal(a
[], a
[]));
926 enum b
= [ 1.0, 2, 4, 3].staticArray
;
927 static assert(!equal(a
[], b
[1..$]));
928 static assert(equal(a
[], b
[]));
931 pure nothrow @safe unittest
933 static assert(equal([1, 2, 3].map
!(x
=> x
* 2), [1, 2, 3].map
!(x
=> x
* 2)));
935 static assert(!equal([1, 2].map
!(x
=> x
* 2), [1, 2, 3].map
!(x
=> x
* 2)));
939 * Lazily filters the given range based on the given predicate.
941 * Returns: a range containing only elements for which the predicate returns
944 auto filter(alias predicate
, Range
)(Range range
)
945 if (isInputRange
!(Unqual
!Range
) && isPredicateOf
!(predicate
, ElementType
!Range
))
947 return Filter
!(predicate
, Range
)(range
);
951 pure nothrow @safe @nogc unittest
953 enum a
= [1, 2, 3, 4].staticArray
;
954 enum result
= a
[].filter
!(e
=> e
> 2);
956 enum expected
= [3, 4].staticArray
;
957 static assert(result
.equal(expected
[]));
960 private struct Filter(alias predicate
, Range
)
970 while (!range
.empty
&& !predicate(range
.front
))
976 @property bool empty()
982 @property auto front()
984 assert(!range
.empty
);
991 assert(!range
.empty
);
997 } while (!range
.empty
&& !predicate(range
.front
));
1007 * Lazily iterates the given range and calls the given callable for each element.
1009 * Returns: a range containing the result of each call to `callable`
1011 auto map(alias callable
, Range
)(Range range
)
1012 if (isInputRange
!(Unqual
!Range
) && isCallableWith
!(callable
, ElementType
!Range
))
1014 return Map
!(callable
, Range
)(range
);
1018 pure nothrow @safe @nogc unittest
1020 enum a
= [1, 2, 3, 4].staticArray
;
1021 enum expected
= [2, 4, 6, 8].staticArray
;
1023 enum result
= a
[].map
!(e
=> e
* 2);
1024 static assert(result
.equal(expected
[]));
1027 private struct Map(alias callable
, Range
)
1029 private Range range
;
1031 @property bool empty()
1036 @property auto front()
1038 assert(!range
.empty
);
1039 return callable(range
.front
);
1044 assert(!range
.empty
);
1048 static if (hasLength
!Range
)
1050 @property auto length()
1052 return range
.length
;
1055 alias opDollar
= length
;
1059 /// Returns: the length of the given range.
1060 auto walkLength(Range
)(Range range
)
1061 if (isInputRange
!Range
)
1063 static if (hasLength
!Range
)
1064 return range
.length
;
1068 for (; !range
.empty
; range
.popFront())
1075 pure nothrow @safe @nogc unittest
1077 enum a
= [1, 2, 3, 4].staticArray
;
1078 static assert(a
[].walkLength
== 4);
1080 enum c
= a
[].filter
!(e
=> e
> 2);
1081 static assert(c
.walkLength
== 2);
1084 /// Evaluates to the element type of `R`.
1085 template ElementType(R
)
1087 static if (is(typeof(R
.init
.front
.init
) T
))
1088 alias ElementType
= T
;
1090 alias ElementType
= void;
1093 /// Evaluates to `true` if the given type satisfy the input range interface.
1094 enum isInputRange(R
) =
1095 is(typeof(R
.init
) == R
)
1096 && is(ReturnType
!(typeof((R r
) => r
.empty
)) == bool)
1097 && is(typeof((return ref R r
) => r
.front
))
1098 && !is(ReturnType
!(typeof((R r
) => r
.front
)) == void)
1099 && is(typeof((R r
) => r
.popFront
));
1101 /// Evaluates to `true` if `func` can be called with a value of `T` and returns
1102 /// a value that is convertible to `bool`.
1103 enum isPredicateOf(alias func
, T
) = is(typeof((T t
) => !func(t
)));
1105 /// Evaluates to `true` if `func` be called withl a value of `T`.
1106 enum isCallableWith(alias func
, T
) =
1107 __traits(compiles
, { auto _
= (T t
) => func(t
); });
1111 template ReturnType(T
)
1113 static if (is(T R
== return))
1114 alias ReturnType
= R
;
1116 static assert(false, "argument is not a function");
1119 alias Unqual(T
) = ReturnType
!(typeof((T t
) => cast() t
));
1121 template hasLength(Range
)
1123 static if (is(typeof(((Range
* r
) => r
.length
)(null)) Length
))
1124 enum hasLength
= is(Length
== size_t
);
1126 enum hasLength
= false;
1129 /// Implements the range interface primitive `front` for built-in arrays.
1130 @property ref inout(T
) front(T
)(return scope inout(T
)[] a
) pure nothrow @nogc @safe
1132 assert(a
.length
, "Attempting to fetch the front of an empty array of " ~ T
.stringof
);
1137 pure nothrow @nogc @safe unittest
1139 enum a
= [1, 2, 3].staticArray
;
1140 static assert(a
[].front
== 1);
1143 /// Implements the range interface primitive `empty` for types that obey $(LREF hasLength) property
1144 @property bool empty(T
)(auto ref scope T a
)
1145 if (is(typeof(a
.length
) : size_t
))
1151 pure nothrow @nogc @safe unittest
1153 enum a
= [1, 2, 3].staticArray
;
1155 static assert(!a
.empty
);
1156 static assert(a
[3 .. $].empty
);
1159 pure nothrow @safe unittest
1167 /// Implements the range interface primitive `popFront` for built-in arrays.
1168 void popFront(T
)(/*scope*/ ref inout(T
)[] array
) pure nothrow @nogc @safe
1169 { // does not compile with GDC 9 if this is `scope`
1170 assert(array
.length
, "Attempting to popFront() past the end of an array of " ~ T
.stringof
);
1171 array
= array
[1 .. $];
1175 pure nothrow @nogc @safe unittest
1177 auto a
= [1, 2, 3].staticArray
;
1179 auto expected
= [2, 3].staticArray
;
1182 assert(b
== expected
[]);