From 935d4ada9aeaaf5f105b75b84ca6ef72a053d962 Mon Sep 17 00:00:00 2001 From: RazvanN7 Date: Wed, 21 Dec 2016 16:31:44 +0200 Subject: [PATCH 1/4] Issue 16824 - std.experimental.allocator.dispose leaks memory for arrays of more than 1 dimension --- std/experimental/allocator/package.d | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/std/experimental/allocator/package.d b/std/experimental/allocator/package.d index 03123733334..1d4b198da32 100644 --- a/std/experimental/allocator/package.d +++ b/std/experimental/allocator/package.d @@ -1538,6 +1538,17 @@ if (is(T == class) || is(T == interface)) /// Ditto void dispose(A, T)(auto ref A alloc, T[] array) { + + import std.traits : isArray; + import std.range.primitives : ElementType; + import std.stdio; + + static if (isArray!T) + { + foreach (ref elem; array) + dispose(alloc, elem); + } + static if (hasElaborateDestructor!(typeof(array[0]))) { foreach (ref e; array) From 936a802b50d6dd096f2504595d18436eeceed8e5 Mon Sep 17 00:00:00 2001 From: RazvanN7 Date: Wed, 21 Dec 2016 16:34:49 +0200 Subject: [PATCH 2/4] Issue 16824 - std.experimental.allocator.dispose leaks memory for arrays of more than 1 dimension --- std/experimental/allocator/package.d | 63 +++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/std/experimental/allocator/package.d b/std/experimental/allocator/package.d index 1d4b198da32..8194819e09d 100644 --- a/std/experimental/allocator/package.d +++ b/std/experimental/allocator/package.d @@ -1540,8 +1540,6 @@ void dispose(A, T)(auto ref A alloc, T[] array) { import std.traits : isArray; - import std.range.primitives : ElementType; - import std.stdio; static if (isArray!T) { @@ -1611,6 +1609,67 @@ unittest //bugzilla 15721 Mallocator.instance.dispose(foo); } +unittest //bugzilla 16824 +{ + struct TestAllocator + { + import std.experimental.allocator.common: platformAlignment; + import std.experimental.allocator.mallocator: Mallocator; + + alias allocator = Mallocator.instance; + + private static struct ByteRange + { + void* ptr; + size_t length; + } + + private ByteRange[] _allocations; + + enum uint alignment = platformAlignment; + + void[] allocate(size_t numBytes) + { + auto ret = allocator.allocate(numBytes); + _allocations ~= ByteRange(ret.ptr, ret.length); + return ret; + } + + bool deallocate(void[] bytes) + { + import std.algorithm: remove, canFind; + import std.conv: text; + + bool pred(ByteRange other) + { return other.ptr == bytes.ptr && other.length == bytes.length; } + + assert(_allocations.canFind!pred); + + _allocations = _allocations.remove!pred; + return allocator.deallocate(bytes); + } + + ~this() + { + assert(!_allocations.length); + } + } + + import std.experimental.allocator: dispose, makeArray; + + TestAllocator allocator; + + auto ints2d = allocator.makeArray!(int[][])(2); + foreach (ref ints1d; ints2d) + ints1d = allocator.makeArray!(int[])(3); + + foreach (ref ints1d; ints2d) + foreach(ref ints0d; ints1d) + ints0d = allocator.makeArray!(int)(4); + + allocator.dispose(ints2d); +} + /** Returns a dynamically-typed $(D CAllocator) built around a given statically- From 098e52ba210e1356bc88c488b603b129d7481fe4 Mon Sep 17 00:00:00 2001 From: RazvanN7 Date: Thu, 12 Jan 2017 19:47:37 +0200 Subject: [PATCH 3/4] Created the make/disposeMultidimensionalArray functions + unittest --- std/experimental/allocator/package.d | 95 +++++++++++++++++++++------- 1 file changed, 72 insertions(+), 23 deletions(-) diff --git a/std/experimental/allocator/package.d b/std/experimental/allocator/package.d index 8194819e09d..e52736ab2e1 100644 --- a/std/experimental/allocator/package.d +++ b/std/experimental/allocator/package.d @@ -1538,15 +1538,6 @@ if (is(T == class) || is(T == interface)) /// Ditto void dispose(A, T)(auto ref A alloc, T[] array) { - - import std.traits : isArray; - - static if (isArray!T) - { - foreach (ref elem; array) - dispose(alloc, elem); - } - static if (hasElaborateDestructor!(typeof(array[0]))) { foreach (ref e; array) @@ -1609,12 +1600,78 @@ unittest //bugzilla 15721 Mallocator.instance.dispose(foo); } -unittest //bugzilla 16824 +/** +Allocates a multidimensional array of elements of type T. + +Params: +N = number of dimensions +T = element type of an element of the multidimensional arrat +alloc = the allocator used for getting memory +lengths = static array containing the size of each dimension + +Returns: +An N-dimensional array with individual elements of type T. +*/ +auto makeMultidimensionalArray(int N, T, Allocator)(auto ref Allocator alloc, size_t[N] lengths) +{ + static if (N == 1) + { + return makeArray!T(alloc, lengths[0]); + } + else + { + alias E = typeof(makeMultidimensionalArray!(N-1, T)(alloc, lengths[1..$])); + auto ret = makeArray!E(alloc, lengths[0]); + foreach (ref e; ret) + e = makeMultidimensionalArray!(N-1, T)(alloc, lengths[1..$]); + return ret; + } +} + +/// +unittest +{ + import std.experimental.allocator.mallocator : Mallocator; + + size_t[3] dimArray = [2, 3, 6]; + auto mArray = Mallocator.instance.makeMultidimensionalArray!(dimArray.length, int)(dimArray); + + assert(mArray.length == 2); + foreach (lvl2Array; mArray) + { + assert(lvl2Array.length == 3); + foreach (lvl3Array; lvl2Array) + assert(lvl3Array.length == 6); + } +} + +/** +Destroys and then deallocates a multidimensional array, assuming it was +created with makeMultidimensionalArray and with the same allocator. + +Params: +T = element type of an element of the multidimensional array +alloc = the allocator used for getting memory +array = the multidimensional array that is to be deallocated +*/ +void disposeMultidimensionalArray(Allocator, T)(auto ref Allocator alloc, T[] array) +{ + static if(isArray!T) + { + foreach (ref e; array) + disposeMultidimensionalArray(alloc, e); + } + + dispose(alloc, array); +} + +/// +unittest { struct TestAllocator { - import std.experimental.allocator.common: platformAlignment; - import std.experimental.allocator.mallocator: Mallocator; + import std.experimental.allocator.common : platformAlignment; + import std.experimental.allocator.mallocator : Mallocator; alias allocator = Mallocator.instance; @@ -1638,7 +1695,6 @@ unittest //bugzilla 16824 bool deallocate(void[] bytes) { import std.algorithm: remove, canFind; - import std.conv: text; bool pred(ByteRange other) { return other.ptr == bytes.ptr && other.length == bytes.length; } @@ -1655,19 +1711,12 @@ unittest //bugzilla 16824 } } - import std.experimental.allocator: dispose, makeArray; - TestAllocator allocator; - auto ints2d = allocator.makeArray!(int[][])(2); - foreach (ref ints1d; ints2d) - ints1d = allocator.makeArray!(int[])(3); - - foreach (ref ints1d; ints2d) - foreach(ref ints0d; ints1d) - ints0d = allocator.makeArray!(int)(4); + size_t[5] a = [2, 3, 6, 7, 2]; + auto mArray = allocator.makeMultidimensionalArray!(a.length, int)(a); - allocator.dispose(ints2d); + allocator.disposeMultidimensionalArray(mArray); } /** From 23fcc1cc25f463741402cfeb08feef7d2e9a7600 Mon Sep 17 00:00:00 2001 From: RazvanN7 Date: Sat, 14 Jan 2017 16:22:19 +0200 Subject: [PATCH 4/4] Style changes --- std/experimental/allocator/package.d | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/std/experimental/allocator/package.d b/std/experimental/allocator/package.d index e52736ab2e1..75e9c56925a 100644 --- a/std/experimental/allocator/package.d +++ b/std/experimental/allocator/package.d @@ -1612,18 +1612,18 @@ lengths = static array containing the size of each dimension Returns: An N-dimensional array with individual elements of type T. */ -auto makeMultidimensionalArray(int N, T, Allocator)(auto ref Allocator alloc, size_t[N] lengths) +auto makeMultidimensionalArray(uint n, T, Allocator)(auto ref Allocator alloc, size_t[n] lengths) { - static if (N == 1) + static if (n == 1) { return makeArray!T(alloc, lengths[0]); } else { - alias E = typeof(makeMultidimensionalArray!(N-1, T)(alloc, lengths[1..$])); + alias E = typeof(makeMultidimensionalArray!(n - 1, T)(alloc, lengths[1..$])); auto ret = makeArray!E(alloc, lengths[0]); foreach (ref e; ret) - e = makeMultidimensionalArray!(N-1, T)(alloc, lengths[1..$]); + e = makeMultidimensionalArray!(n - 1, T)(alloc, lengths[1..$]); return ret; } } @@ -1636,6 +1636,12 @@ unittest size_t[3] dimArray = [2, 3, 6]; auto mArray = Mallocator.instance.makeMultidimensionalArray!(dimArray.length, int)(dimArray); + // deallocate when exiting scope + scope(exit) + { + Mallocator.instance.disposeMultidimensionalArray(mArray); + } + assert(mArray.length == 2); foreach (lvl2Array; mArray) { @@ -1647,7 +1653,7 @@ unittest /** Destroys and then deallocates a multidimensional array, assuming it was -created with makeMultidimensionalArray and with the same allocator. +created with makeMultidimensionalArray and the same allocator was used. Params: T = element type of an element of the multidimensional array @@ -1656,7 +1662,7 @@ array = the multidimensional array that is to be deallocated */ void disposeMultidimensionalArray(Allocator, T)(auto ref Allocator alloc, T[] array) { - static if(isArray!T) + static if (isArray!T) { foreach (ref e; array) disposeMultidimensionalArray(alloc, e); @@ -1694,7 +1700,8 @@ unittest bool deallocate(void[] bytes) { - import std.algorithm: remove, canFind; + import std.algorithm.mutation : remove; + import std.algorithm.searching : canFind; bool pred(ByteRange other) { return other.ptr == bytes.ptr && other.length == bytes.length; }