Skip to content

Commit 557b744

Browse files
authored
Merge pull request #4683 from wilzbach/allocator_safe_6
Add attributes to makeArray - part 3 (input range)
2 parents a5c8440 + 9c7bd0e commit 557b744

File tree

1 file changed

+166
-21
lines changed

1 file changed

+166
-21
lines changed

std/experimental/allocator/package.d

Lines changed: 166 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -827,30 +827,37 @@ if (isInputRange!R && !isInfinite!R)
827827
if (!length) return null;
828828
auto m = alloc.allocate(T.sizeof * length);
829829
if (!m.ptr) return null;
830-
auto result = cast(T[]) m;
830+
auto result = () @trusted { return cast(T[]) m; } ();
831831

832832
size_t i = 0;
833833
scope (failure)
834834
{
835835
foreach (j; 0 .. i)
836836
{
837-
destroy(*cast(Unqual!T*) (result.ptr + j));
837+
auto p = () @trusted { return cast(Unqual!T*) &result[j]; }();
838+
destroy(p);
838839
}
839-
alloc.deallocate(m);
840+
841+
static if (canSafelyDeallocPostRewind!T)
842+
() @trusted { alloc.deallocate(m); } ();
843+
else
844+
alloc.deallocate(m);
840845
}
841846

842-
import std.conv : emplace;
847+
import std.conv : emplaceRef;
843848
static if (isNarrowString!R || isRandomAccessRange!R)
844849
{
845850
foreach (j; 0 .. range.length)
846851
{
847-
cast(void) emplace!T(result.ptr + i++, range[j]);
852+
emplaceRef!T(result[i++], range[j]);
848853
}
849854
}
850855
else
851856
{
852857
for (; !range.empty; range.popFront, ++i)
853-
cast(void) emplace!T(result.ptr + i, range.front);
858+
{
859+
emplaceRef!T(result[i], range.front);
860+
}
854861
}
855862

856863
return result;
@@ -861,16 +868,20 @@ if (isInputRange!R && !isInfinite!R)
861868
size_t estimated = 8;
862869
auto m = alloc.allocate(T.sizeof * estimated);
863870
if (!m.ptr) return null;
864-
auto result = cast(T[]) m;
871+
auto result = () @trusted { return cast(T[]) m; } ();
865872

866873
size_t initialized = 0;
867874
void bailout()
868875
{
869-
foreach (i; 0 .. initialized)
876+
foreach (i; 0 .. initialized + 1)
870877
{
871878
destroy(result[i]);
872879
}
873-
alloc.deallocate(m);
880+
881+
static if (canSafelyDeallocPostRewind!T)
882+
() @trusted { alloc.deallocate(m); } ();
883+
else
884+
alloc.deallocate(m);
874885
}
875886
scope (failure) bailout;
876887

@@ -879,22 +890,30 @@ if (isInputRange!R && !isInfinite!R)
879890
if (initialized == estimated)
880891
{
881892
// Need to reallocate
882-
if (!alloc.reallocate(m, T.sizeof * (estimated *= 2)))
893+
static if (hasPurePostblit!T)
894+
auto success = () @trusted { return alloc.reallocate(m, T.sizeof * (estimated *= 2)); } ();
895+
else
896+
auto success = alloc.reallocate(m, T.sizeof * (estimated *= 2));
897+
if (!success)
883898
{
884899
bailout;
885900
return null;
886901
}
887-
result = cast(T[]) m;
902+
result = () @trusted { return cast(T[]) m; } ();
888903
}
889-
import std.conv : emplace;
890-
emplace!T(result.ptr + initialized, range.front);
904+
import std.conv : emplaceRef;
905+
emplaceRef(result[initialized], range.front);
891906
}
892907

893-
// Try to shrink memory, no harm if not possible
894-
if (initialized < estimated
895-
&& alloc.reallocate(m, T.sizeof * initialized))
908+
if (initialized < estimated)
896909
{
897-
result = cast(T[]) m;
910+
// Try to shrink memory, no harm if not possible
911+
static if (hasPurePostblit!T)
912+
auto success = () @trusted { return alloc.reallocate(m, T.sizeof * initialized); } ();
913+
else
914+
auto success = alloc.reallocate(m, T.sizeof * initialized);
915+
if (success)
916+
result = () @trusted { return cast(T[]) m; } ();
898917
}
899918

900919
return result[0 .. initialized];
@@ -917,7 +936,7 @@ unittest
917936
assert(b == [4.0, 2.0]);
918937
}
919938
import std.experimental.allocator.gc_allocator : GCAllocator;
920-
test(GCAllocator.instance);
939+
(alloc) pure nothrow @safe { test(alloc); } (GCAllocator.instance);
921940
test(theAllocator);
922941
}
923942

@@ -940,15 +959,141 @@ unittest
940959
}
941960

942961
import std.experimental.allocator.gc_allocator : GCAllocator;
943-
test(GCAllocator.instance);
962+
(alloc) pure nothrow @safe { test(alloc); } (GCAllocator.instance);
944963
test(theAllocator);
945964
}
946965

966+
/*pure*/ nothrow @safe unittest
967+
{
968+
import std.algorithm.comparison : equal;
969+
import std.internal.test.dummyrange;
970+
import std.experimental.allocator.gc_allocator : GCAllocator;
971+
import std.range : iota;
972+
foreach (DummyType; AllDummyRanges)
973+
{
974+
(alloc) pure nothrow @safe
975+
{
976+
DummyType d;
977+
auto arr = alloc.makeArray(d);
978+
assert(arr.length == 10);
979+
assert(arr.equal(iota(1, 11)));
980+
} (GCAllocator.instance);
981+
}
982+
}
983+
984+
// test failure with a pure, failing struct
985+
@safe unittest
986+
{
987+
import std.exception : assertThrown, enforce;
988+
989+
struct NoCopy
990+
{
991+
int b;
992+
993+
@disable this();
994+
995+
this(int b)
996+
{
997+
this.b = b;
998+
}
999+
1000+
// can't be copied
1001+
this(this)
1002+
{
1003+
enforce(b < 3, "there can only be three elements");
1004+
}
1005+
}
1006+
import std.experimental.allocator.mallocator : Mallocator;
1007+
auto arr = [NoCopy(1), NoCopy(2), NoCopy(3)];
1008+
assertThrown(makeArray!NoCopy(Mallocator.instance, arr));
1009+
1010+
struct NoCopyRange
1011+
{
1012+
static j = 0;
1013+
bool empty()
1014+
{
1015+
return j > 5;
1016+
}
1017+
1018+
auto front()
1019+
{
1020+
return NoCopy(j);
1021+
}
1022+
1023+
void popFront()
1024+
{
1025+
j++;
1026+
}
1027+
}
1028+
assertThrown(makeArray!NoCopy(Mallocator.instance, NoCopyRange()));
1029+
}
1030+
1031+
// test failure with an impure, failing struct
1032+
@system unittest
1033+
{
1034+
import std.exception : assertThrown, enforce;
1035+
1036+
static i = 0;
1037+
static maxElements = 2;
1038+
struct NoCopy
1039+
{
1040+
int val;
1041+
@disable this();
1042+
1043+
this(int b){
1044+
this.val = i++;
1045+
}
1046+
1047+
// can't be copied
1048+
this(this)
1049+
{
1050+
enforce(i++ < maxElements, "there can only be four elements");
1051+
}
1052+
}
1053+
1054+
import std.experimental.allocator.mallocator : Mallocator;
1055+
auto arr = [NoCopy(1), NoCopy(2)];
1056+
assertThrown(makeArray!NoCopy(Mallocator.instance, arr));
1057+
1058+
// allow more copies and thus force reallocation
1059+
i = 0;
1060+
maxElements = 30;
1061+
static j = 0;
1062+
1063+
struct NoCopyRange
1064+
{
1065+
bool empty()
1066+
{
1067+
return j > 100;
1068+
}
1069+
1070+
auto front()
1071+
{
1072+
return NoCopy(1);
1073+
}
1074+
1075+
void popFront()
1076+
{
1077+
j++;
1078+
}
1079+
}
1080+
assertThrown(makeArray!NoCopy(Mallocator.instance, NoCopyRange()));
1081+
1082+
maxElements = 300;
1083+
auto arr2 = makeArray!NoCopy(Mallocator.instance, NoCopyRange());
1084+
1085+
import std.algorithm.comparison : equal;
1086+
import std.algorithm.iteration : map;
1087+
import std.range : iota;
1088+
assert(arr2.map!`a.val`.equal(iota(32, 204, 2)));
1089+
}
1090+
9471091
version(unittest)
9481092
{
9491093
private struct ForcedInputRange
9501094
{
9511095
int[]* array;
1096+
pure nothrow @safe @nogc:
9521097
bool empty() { return !array || (*array).empty; }
9531098
ref int front() { return (*array)[0]; }
9541099
void popFront() { *array = (*array)[1 .. $]; }
@@ -967,13 +1112,13 @@ unittest
9671112
long[] a = alloc.makeArray!long(r);
9681113
assert(a.length == 0 && a.ptr is null);
9691114
auto arr2 = arr;
970-
r.array = &arr2;
1115+
r.array = () @trusted { return &arr2; } ();
9711116
a = alloc.makeArray!long(r);
9721117
assert(a.length == 10);
9731118
assert(a == iota(10).array);
9741119
}
9751120
import std.experimental.allocator.gc_allocator : GCAllocator;
976-
test(GCAllocator.instance);
1121+
(alloc) pure nothrow @safe { test(alloc); } (GCAllocator.instance);
9771122
test(theAllocator);
9781123
}
9791124

0 commit comments

Comments
 (0)