1,237 changes: 759 additions & 478 deletions std/conv.d

Large diffs are not rendered by default.

250 changes: 0 additions & 250 deletions std/cstream.d

This file was deleted.

172 changes: 90 additions & 82 deletions std/csv.d

Large diffs are not rendered by default.

6,523 changes: 3,749 additions & 2,774 deletions std/datetime.d

Large diffs are not rendered by default.

7 changes: 2 additions & 5 deletions std/demangle.d
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,9 @@
/**
* Demangle D mangled names.
*
* Macros:
* WIKI = Phobos/StdDemangle
*
* Copyright: Copyright Digital Mars 2000 - 2009.
* License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: $(WEB digitalmars.com, Walter Bright),
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: $(HTTP digitalmars.com, Walter Bright),
* Thomas K$(UUML)hne, Frits van Bommel
* Source: $(PHOBOSSRC std/_demangle.d)
*/
Expand Down
19 changes: 8 additions & 11 deletions std/digest/crc.d
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ $(TR $(TDNW Helpers) $(TD $(MYREF crcHexString) $(MYREF crc32Of))
*
* Note:
* CRCs are usually printed with the MSB first. When using
* $(XREF_PACK digest,digest,toHexString) the result will be in an unexpected
* order. Use $(XREF_PACK digest,digest,toHexString)'s optional order parameter
* $(REF toHexString, std,digest,digest) the result will be in an unexpected
* order. Use $(REF toHexString, std,digest,digest)'s optional order parameter
* to specify decreasing order for the correct result. The $(LREF crcHexString)
* alias can also be used for this purpose.
*
* License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
*
* Authors: Pavel "EvilOne" Minayev, Alex Rønne Petersen, Johannes Pfau
*
Expand All @@ -40,9 +40,6 @@ $(TR $(TDNW Helpers) $(TD $(MYREF crcHexString) $(MYREF crc32Of))
*
* Source: $(PHOBOSSRC std/digest/_crc.d)
*
* Macros:
* WIKI = Phobos/StdUtilDigestCRC32
*
* Standards:
* Implements the 'common' IEEE CRC32 variant
* (LSB-first order, Initial value uint.max, complement result)
Expand Down Expand Up @@ -157,7 +154,7 @@ struct CRC32
public:
/**
* Use this to feed the digest with data.
* Also implements the $(XREF_PACK range,primitives,isOutputRange)
* Also implements the $(REF isOutputRange, std,range,primitives)
* interface for $(D ubyte) and $(D const(ubyte)[]).
*/
void put(scope const(ubyte)[] data...) @trusted pure nothrow @nogc
Expand Down Expand Up @@ -252,7 +249,7 @@ unittest
{
//Let's use the template features:
//Note: When passing a CRC32 to a function, it must be passed by reference!
void doSomething(T)(ref T hash) if(isDigest!T)
void doSomething(T)(ref T hash) if (isDigest!T)
{
hash.put(cast(ubyte)0);
}
Expand Down Expand Up @@ -307,7 +304,7 @@ unittest
}

/**
* This is a convenience alias for $(XREF_PACK digest,digest,digest) using the
* This is a convenience alias for $(REF digest, std,digest,digest) using the
* CRC32 implementation.
*
* Params:
Expand Down Expand Up @@ -342,7 +339,7 @@ unittest
}

/**
* This is a convenience alias for $(XREF_PACK digest,digest,toHexString)
* This is a convenience alias for $(REF toHexString, std,digest,digest)
* producing the usual CRC32 string output.
*/
public alias crcHexString = toHexString!(Order.decreasing);
Expand All @@ -354,7 +351,7 @@ public alias crcHexString = toHexString!(Order.decreasing, 16);
* OOP API CRC32 implementation.
* See $(D std.digest.digest) for differences between template and OOP API.
*
* This is an alias for $(D $(XREF_PACK digest,digest,WrapperDigest)!CRC32), see
* This is an alias for $(D $(REF WrapperDigest, std,digest,digest)!CRC32), see
* there for more information.
*/
alias CRC32Digest = WrapperDigest!CRC32;
Expand Down
85 changes: 49 additions & 36 deletions std/digest/digest.d
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ $(TR $(TDNW Implementation helpers) $(TD $(MYREF digestLength) $(MYREF WrapperDi
* In this simplest case, the template API can even be used without templates: Just use the "$(B x)" structs
* directly.
*
* License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors:
* Johannes Pfau
*
Expand Down Expand Up @@ -92,7 +92,7 @@ unittest
import std.stdio;

// Digests a file and prints the result.
void digestFile(Hash)(string filename) if(isDigest!Hash)
void digestFile(Hash)(string filename) if (isDigest!Hash)
{
auto file = File(filename);
auto result = digest!Hash(file.byChunk(4096 * 1024));
Expand All @@ -116,7 +116,7 @@ unittest
import std.digest.crc, std.digest.sha, std.digest.md;
import std.stdio;
// Digests a file and prints the result.
void digestFile(Hash)(ref Hash hash, string filename) if(isDigest!Hash)
void digestFile(Hash)(ref Hash hash, string filename) if (isDigest!Hash)
{
File file = File(filename);

Expand Down Expand Up @@ -204,7 +204,7 @@ version(ExampleDigest)
public:
/**
* Use this to feed the digest with data.
* Also implements the $(XREF_PACK range,primitives,isOutputRange)
* Also implements the $(REF isOutputRange, std,range,primitives)
* interface for $(D ubyte) and $(D const(ubyte)[]).
* The following usages of $(D put) must work for any type which
* passes $(LREF isDigest):
Expand Down Expand Up @@ -256,7 +256,7 @@ version(ExampleDigest)
unittest
{
//Using the OutputRange feature
import std.algorithm : copy;
import std.algorithm.mutation : copy;
import std.range : repeat;
import std.digest.md;

Expand Down Expand Up @@ -303,7 +303,7 @@ unittest
unittest
{
import std.digest.crc;
void myFunction(T)() if(isDigest!T)
void myFunction(T)() if (isDigest!T)
{
T dig;
dig.start();
Expand All @@ -317,7 +317,7 @@ unittest
*/
template DigestType(T)
{
static if(isDigest!T)
static if (isDigest!T)
{
alias DigestType =
ReturnType!(typeof(
Expand Down Expand Up @@ -377,7 +377,7 @@ unittest
unittest
{
import std.digest.crc;
void myFunction(T)() if(hasPeek!T)
void myFunction(T)() if (hasPeek!T)
{
T dig;
dig.start();
Expand All @@ -388,7 +388,7 @@ unittest

/**
* Checks whether the digest has a $(D blockSize) member, which contains the
* digest's internal block size in bits. It is primarily used by $(XREF digest.hmac, HMAC).
* digest's internal block size in bits. It is primarily used by $(REF HMAC, std,digest.hmac).
*/

template hasBlockSize(T)
Expand Down Expand Up @@ -424,10 +424,10 @@ package template isDigestibleRange(Range)
* Params:
* range= an $(D InputRange) with $(D ElementType) $(D ubyte), $(D ubyte[]) or $(D ubyte[num])
*/
DigestType!Hash digest(Hash, Range)(auto ref Range range) if(!isArray!Range
DigestType!Hash digest(Hash, Range)(auto ref Range range) if (!isArray!Range
&& isDigestibleRange!Range)
{
import std.algorithm : copy;
import std.algorithm.mutation : copy;
Hash hash;
hash.start();
copy(range, &hash);
Expand All @@ -449,11 +449,11 @@ unittest
* Params:
* data= one or more arrays of any type
*/
DigestType!Hash digest(Hash, T...)(scope const T data) if(allSatisfy!(isArray, typeof(data)))
DigestType!Hash digest(Hash, T...)(scope const T data) if (allSatisfy!(isArray, typeof(data)))
{
Hash hash;
hash.start();
foreach(datum; data)
foreach (datum; data)
hash.put(cast(const(ubyte[]))datum);
return hash.finish();
}
Expand Down Expand Up @@ -486,7 +486,7 @@ unittest
* range= an $(D InputRange) with $(D ElementType) $(D ubyte), $(D ubyte[]) or $(D ubyte[num])
*/
char[digestLength!(Hash)*2] hexDigest(Hash, Order order = Order.increasing, Range)(ref Range range)
if(!isArray!Range && isDigestibleRange!Range)
if (!isArray!Range && isDigestibleRange!Range)
{
return toHexString!order(digest!Hash(range));
}
Expand All @@ -508,7 +508,7 @@ unittest
* data= one or more arrays of any type
*/
char[digestLength!(Hash)*2] hexDigest(Hash, Order order = Order.increasing, T...)(scope const T data)
if(allSatisfy!(isArray, typeof(data)))
if (allSatisfy!(isArray, typeof(data)))
{
return toHexString!order(digest!Hash(data));
}
Expand Down Expand Up @@ -562,7 +562,7 @@ interface Digest
public:
/**
* Use this to feed the digest with data.
* Also implements the $(XREF_PACK range,primitives,isOutputRange)
* Also implements the $(REF isOutputRange, std,range,primitives)
* interface for $(D ubyte) and $(D const(ubyte)[]).
*
* Example:
Expand Down Expand Up @@ -598,7 +598,7 @@ interface Digest
*/
@trusted nothrow ubyte[] finish();
///ditto
nothrow ubyte[] finish(scope ubyte[] buf);
nothrow ubyte[] finish(ubyte[] buf);
//@@@BUG@@@ http://d.puremagic.com/issues/show_bug.cgi?id=6549
/*in
{
Expand All @@ -611,7 +611,7 @@ interface Digest
final @trusted nothrow ubyte[] digest(scope const(void[])[] data...)
{
this.reset();
foreach(datum; data)
foreach (datum; data)
this.put(cast(ubyte[])datum);
return this.finish();
}
Expand All @@ -621,7 +621,7 @@ interface Digest
unittest
{
//Using the OutputRange feature
import std.algorithm : copy;
import std.algorithm.mutation : copy;
import std.range : repeat;
import std.digest.md;

Expand Down Expand Up @@ -713,9 +713,9 @@ char[num*2] toHexString(Order order = Order.increasing, size_t num, LetterCase l
char[num*2] result;
size_t i;

static if(order == Order.increasing)
static if (order == Order.increasing)
{
foreach(u; digest)
foreach (u; digest)
{
result[i++] = hexDigits[u >> 4];
result[i++] = hexDigits[u & 15];
Expand All @@ -724,7 +724,7 @@ char[num*2] toHexString(Order order = Order.increasing, size_t num, LetterCase l
else
{
size_t j = num - 1;
while(i < num*2)
while (i < num*2)
{
result[i++] = hexDigits[digest[j] >> 4];
result[i++] = hexDigits[digest[j] & 15];
Expand Down Expand Up @@ -756,9 +756,9 @@ string toHexString(Order order = Order.increasing, LetterCase letterCase = Lette
auto result = new char[digest.length*2];
size_t i;

static if(order == Order.increasing)
static if (order == Order.increasing)
{
foreach(u; digest)
foreach (u; digest)
{
result[i++] = hexDigits[u >> 4];
result[i++] = hexDigits[u & 15];
Expand All @@ -767,14 +767,15 @@ string toHexString(Order order = Order.increasing, LetterCase letterCase = Lette
else
{
import std.range : retro;
foreach(u; retro(digest))
foreach (u; retro(digest))
{
result[i++] = hexDigits[u >> 4];
result[i++] = hexDigits[u & 15];
}
}
import std.exception : assumeUnique;
return assumeUnique(result);
// memory was just created, so casting to immutable is safe
return () @trusted { return assumeUnique(result); }();
}

///ditto
Expand All @@ -786,7 +787,7 @@ string toHexString(LetterCase letterCase, Order order = Order.increasing)(in uby
//For more example unittests, see Digest.digest, digest

///
unittest
@safe unittest
{
import std.digest.crc;
//Test with template API:
Expand All @@ -799,7 +800,7 @@ unittest
}

///
unittest
@safe unittest
{
import std.digest.crc;
// With OOP API
Expand All @@ -808,7 +809,7 @@ unittest
assert(toHexString!(Order.decreasing)(crc32) == "414FA339");
}

unittest
@safe unittest
{
ubyte[16] data;
assert(toHexString(data) == "00000000000000000000000000000000");
Expand Down Expand Up @@ -838,7 +839,7 @@ ref T[N] asArray(size_t N, T)(ref T[] source, string errorMsg = "")
* useful for other purposes as well. It returns the length (in bytes) of the hash value
* produced by T.
*/
template digestLength(T) if(isDigest!T)
template digestLength(T) if (isDigest!T)
{
enum size_t digestLength = (ReturnType!(T.finish)).length;
}
Expand All @@ -848,7 +849,7 @@ template digestLength(T) if(isDigest!T)
* Modules providing digest implementations will usually provide
* an alias for this template (e.g. MD5Digest, SHA1Digest, ...).
*/
class WrapperDigest(T) if(isDigest!T) : Digest
class WrapperDigest(T) if (isDigest!T) : Digest
{
protected:
T _digest;
Expand All @@ -864,7 +865,7 @@ class WrapperDigest(T) if(isDigest!T) : Digest

/**
* Use this to feed the digest with data.
* Also implements the $(XREF_PACK range,primitives,isOutputRange)
* Also implements the $(REF isOutputRange, std,range,primitives)
* interface for $(D ubyte) and $(D const(ubyte)[]).
*/
@trusted nothrow void put(scope const(ubyte)[] data...)
Expand Down Expand Up @@ -909,7 +910,7 @@ class WrapperDigest(T) if(isDigest!T) : Digest
* //length
* --------
*/
nothrow ubyte[] finish(scope ubyte[] buf)
nothrow ubyte[] finish(ubyte[] buf)
in
{
assert(buf.length >= this.length);
Expand Down Expand Up @@ -939,13 +940,13 @@ class WrapperDigest(T) if(isDigest!T) : Digest
*
* These functions are only available if $(D hasPeek!T) is true.
*/
@trusted ubyte[] peek(scope ubyte[] buf) const;
@trusted ubyte[] peek(ubyte[] buf) const;
///ditto
@trusted ubyte[] peek() const;
}
else static if(hasPeek!T)
else static if (hasPeek!T)
{
@trusted ubyte[] peek(scope ubyte[] buf) const
@trusted ubyte[] peek(ubyte[] buf) const
in
{
assert(buf.length >= this.length);
Expand Down Expand Up @@ -991,3 +992,15 @@ unittest
//necessary, result will have the correct length, but buf will still have it's original
//length
}

@safe unittest
{
// Test peek & length
import std.digest.crc;
auto hash = new WrapperDigest!CRC32();
assert(hash.length == 4);
hash.put(cast(const(ubyte[]))"The quick brown fox jumps over the lazy dog");
assert(hash.peek().toHexString() == "39A34F41");
ubyte[5] buf;
assert(hash.peek(buf).toHexString() == "39A34F41");
}
48 changes: 32 additions & 16 deletions std/digest/hmac.d
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,14 @@

/**
This package implements the hash-based message authentication code (_HMAC)
algorithm as defined in $(WEB tools.ietf.org/html/rfc2104, RFC2104). See also
the corresponding $(WEB en.wikipedia.org/wiki/Hash-based_message_authentication_code, Wikipedia article).
algorithm as defined in $(HTTP tools.ietf.org/html/rfc2104, RFC2104). See also
the corresponding $(HTTP en.wikipedia.org/wiki/Hash-based_message_authentication_code, Wikipedia article).
$(SCRIPT inhibitQuickIndex = 1;)
Macros:
WIKI = Phobos/StdDigestHMAC
SUBMODULE = $(LINK2 std_digest_$1.html, std.digest.$1)
SUBREF = $(LINK2 std_digest_$1.html#.$2, $(TT $2))$(NBSP)
License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
Source: $(PHOBOSSRC std/digest/_hmac.d)
*/
Expand All @@ -29,7 +26,7 @@ import std.meta : allSatisfy;
* information about the block size, it can be supplied explicitly using
* the second overload.
*
* This type conforms to $(XREF digest.digest, isDigest).
* This type conforms to $(REF isDigest, std,digest.digest).
*/

version(StdDdoc)
Expand Down Expand Up @@ -96,7 +93,10 @@ if (hashBlockSize % 8 == 0)
import std.string : representation;
auto hmac = HMAC!SHA1("My s3cR3T keY".representation);
hmac.put("Hello, world".representation);
static immutable expected = [130, 32, 235, 44, 208, 141, 150, 232, 211, 214, 162, 195, 188, 127, 52, 89, 100, 68, 90, 216];
static immutable expected = [
130, 32, 235, 44, 208, 141,
150, 232, 211, 214, 162, 195,
188, 127, 52, 89, 100, 68, 90, 216];
assert(hmac.finish() == expected);
}

Expand Down Expand Up @@ -133,13 +133,16 @@ if (hashBlockSize % 8 == 0)
hmac.put(data1.representation);
hmac.start(); // reset digest
hmac.put(data2.representation); // start over
static immutable expected = [122, 151, 232, 240, 249, 80, 19, 178, 186, 77, 110, 23, 208, 52, 11, 88, 34, 151, 192, 255];
static immutable expected = [
122, 151, 232, 240, 249, 80,
19, 178, 186, 77, 110, 23, 208,
52, 11, 88, 34, 151, 192, 255];
assert(hmac.finish() == expected);
}

/**
* Feeds a piece of data into the hash computation. This method allows the
* type to be used as an $(XREF range, OutputRange).
* type to be used as an $(REF OutputRange, std,range).
*
* Returns:
* A reference to the digest for convenient chaining.
Expand All @@ -160,7 +163,10 @@ if (hashBlockSize % 8 == 0)
auto hmac = HMAC!SHA1("My s3cR3T keY".representation);
hmac.put(data1.representation)
.put(data2.representation);
static immutable expected = [197, 57, 52, 3, 13, 194, 13, 36, 117, 228, 8, 11, 111, 51, 165, 3, 123, 31, 251, 113];
static immutable expected = [
197, 57, 52, 3, 13, 194, 13,
36, 117, 228, 8, 11, 111, 51,
165, 3, 123, 31, 251, 113];
assert(hmac.finish() == expected);
}

Expand Down Expand Up @@ -194,7 +200,10 @@ if (hashBlockSize % 8 == 0)
auto digest = hmac.put(data1.representation)
.put(data2.representation)
.finish();
static immutable expected = [197, 57, 52, 3, 13, 194, 13, 36, 117, 228, 8, 11, 111, 51, 165, 3, 123, 31, 251, 113];
static immutable expected = [
197, 57, 52, 3, 13, 194, 13,
36, 117, 228, 8, 11, 111, 51,
165, 3, 123, 31, 251, 113];
assert(digest == expected);
}
}
Expand Down Expand Up @@ -231,7 +240,10 @@ if (isDigest!H)
.put(data1.representation)
.put(data2.representation)
.finish();
static immutable expected = [197, 57, 52, 3, 13, 194, 13, 36, 117, 228, 8, 11, 111, 51, 165, 3, 123, 31, 251, 113];
static immutable expected = [
197, 57, 52, 3, 13, 194, 13, 36,
117, 228, 8, 11, 111, 51, 165,
3, 123, 31, 251, 113];
assert(digest == expected);
}

Expand All @@ -258,13 +270,16 @@ if (isDigest!H)
{
import std.digest.sha, std.digest.hmac;
import std.string : representation;
import std.algorithm : map;
import std.algorithm.iteration : map;
string data = "Hello, world";
auto digest = data.representation
.map!(a => cast(ubyte)(a+1))
.hmac!SHA1("My s3cR3T keY".representation);
static assert(is(typeof(digest) == ubyte[20]));
static immutable expected = [163, 208, 118, 179, 216, 93, 17, 10, 84, 200, 87, 104, 244, 111, 136, 214, 167, 210, 58, 10];
static immutable expected = [
163, 208, 118, 179, 216, 93,
17, 10, 84, 200, 87, 104, 244,
111, 136, 214, 167, 210, 58, 10];
assert(digest == expected);
}
}
Expand Down Expand Up @@ -298,7 +313,8 @@ unittest

import std.string : representation;
auto key = "key".representation,
long_key = "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789".representation,
long_key = ("012345678901234567890123456789012345678901"
~"234567890123456789012345678901234567890123456789").representation,
data1 = "The quick brown fox ".representation,
data2 = "jumps over the lazy dog".representation,
data = data1 ~ data2;
Expand Down
16 changes: 7 additions & 9 deletions std/digest/md.d
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ $(TR $(TDNW Helpers) $(TD $(MYREF md5Of))
* This module publicly imports $(D std.digest.digest) and can be used as a stand-alone
* module.
*
* License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
*
* CTFE:
* Digests do not work in CTFE
Expand All @@ -38,8 +38,6 @@ $(TR $(TDNW Helpers) $(TD $(MYREF md5Of))
*
* Source: $(PHOBOSSRC std/digest/_md.d)
*
* Macros:
* WIKI = Phobos/StdMd5
*/

/* md5.d - RSA Data Security, Inc., MD5 message-digest algorithm
Expand Down Expand Up @@ -194,7 +192,7 @@ struct MD5
{
import std.bitmanip : littleEndianToNative;

for(size_t i = 0; i < 16; i++)
for (size_t i = 0; i < 16; i++)
{
x[i] = littleEndianToNative!uint(*cast(ubyte[4]*)&(*block)[i*4]);
}
Expand Down Expand Up @@ -290,7 +288,7 @@ struct MD5

/**
* Use this to feed the digest with data.
* Also implements the $(XREF_PACK range,primitives,isOutputRange)
* Also implements the $(REF isOutputRange, std,range,primitives)
* interface for $(D ubyte) and $(D const(ubyte)[]).
*
* Example:
Expand Down Expand Up @@ -321,7 +319,7 @@ struct MD5
(&_buffer[index])[0 .. partLen] = data.ptr[0 .. partLen];
transform(&_buffer);

for(i = partLen; i + 63 < inputLen; i += 64)
for (i = partLen; i + 63 < inputLen; i += 64)
{
transform(cast(const(ubyte[64])*)(data[i .. i + 64].ptr));
}
Expand Down Expand Up @@ -428,7 +426,7 @@ unittest
unittest
{
//Let's use the template features:
void doSomething(T)(ref T hash) if(isDigest!T)
void doSomething(T)(ref T hash) if (isDigest!T)
{
hash.put(cast(ubyte)0);
}
Expand Down Expand Up @@ -494,7 +492,7 @@ unittest
}

/**
* This is a convenience alias for $(XREF_PACK digest,digest,digest) using the
* This is a convenience alias for $(REF digest, std,digest,digest) using the
* MD5 implementation.
*/
//simple alias doesn't work here, hope this gets inlined...
Expand All @@ -514,7 +512,7 @@ unittest
* OOP API MD5 implementation.
* See $(D std.digest.digest) for differences between template and OOP API.
*
* This is an alias for $(D $(XREF_PACK digest,digest,WrapperDigest)!MD5), see
* This is an alias for $(D $(REF WrapperDigest, std,digest,digest)!MD5), see
* there for more information.
*/
alias MD5Digest = WrapperDigest!MD5;
Expand Down
751 changes: 751 additions & 0 deletions std/digest/murmurhash.d

Large diffs are not rendered by default.

16 changes: 7 additions & 9 deletions std/digest/ripemd.d
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ $(TR $(TDNW Helpers) $(TD $(MYREF ripemd160Of))
* This module publicly imports $(D std.digest.digest) and can be used as a stand-alone
* module.
*
* License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
*
* CTFE:
* Digests do not work in CTFE
Expand All @@ -42,8 +42,6 @@ $(TR $(TDNW Helpers) $(TD $(MYREF ripemd160Of))
*
* Source: $(PHOBOSSRC std/digest/_ripemd.d)
*
* Macros:
* WIKI = Phobos/StdRipemd
*/

module std.digest.ripemd;
Expand Down Expand Up @@ -239,7 +237,7 @@ struct RIPEMD160
{
import std.bitmanip : littleEndianToNative;

for(size_t i = 0; i < 16; i++)
for (size_t i = 0; i < 16; i++)
{
x[i] = littleEndianToNative!uint(*cast(ubyte[4]*)&(*block)[i*4]);
}
Expand Down Expand Up @@ -446,7 +444,7 @@ struct RIPEMD160

/**
* Use this to feed the digest with data.
* Also implements the $(XREF_PACK range,primitives,isOutputRange)
* Also implements the $(REF isOutputRange, std,range,primitives)
* interface for $(D ubyte) and $(D const(ubyte)[]).
*
* Example:
Expand Down Expand Up @@ -477,7 +475,7 @@ struct RIPEMD160
(&_buffer[index])[0 .. partLen] = data.ptr[0 .. partLen];
transform(&_buffer);

for(i = partLen; i + 63 < inputLen; i += 64)
for (i = partLen; i + 63 < inputLen; i += 64)
{
transform(cast(const(ubyte[64])*)(data[i .. i + 64].ptr));
}
Expand Down Expand Up @@ -586,7 +584,7 @@ unittest
unittest
{
//Let's use the template features:
void doSomething(T)(ref T hash) if(isDigest!T)
void doSomething(T)(ref T hash) if (isDigest!T)
{
hash.put(cast(ubyte)0);
}
Expand Down Expand Up @@ -663,7 +661,7 @@ unittest
}

/**
* This is a convenience alias for $(XREF_PACK digest,digest,digest) using the
* This is a convenience alias for $(REF digest, std,digest,digest) using the
* RIPEMD160 implementation.
*/
//simple alias doesn't work here, hope this gets inlined...
Expand All @@ -683,7 +681,7 @@ unittest
* OOP API RIPEMD160 implementation.
* See $(D std.digest.digest) for differences between template and OOP API.
*
* This is an alias for $(D $(XREF_PACK digest,digest,WrapperDigest)!RIPEMD160),
* This is an alias for $(D $(REF WrapperDigest, std,digest,digest)!RIPEMD160),
* see there for more information.
*/
alias RIPEMD160Digest = WrapperDigest!RIPEMD160;
Expand Down
115 changes: 68 additions & 47 deletions std/digest/sha.d

Large diffs are not rendered by default.

666 changes: 430 additions & 236 deletions std/encoding.d

Large diffs are not rendered by default.

207 changes: 97 additions & 110 deletions std/exception.d

Large diffs are not rendered by default.

112 changes: 96 additions & 16 deletions std/experimental/allocator/building_blocks/affix_allocator.d
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
///
module std.experimental.allocator.building_blocks.affix_allocator;

/**
Expand All @@ -18,8 +19,13 @@ The following methods are defined if $(D Allocator) defines them, and forward to
*/
struct AffixAllocator(Allocator, Prefix, Suffix = void)
{
import std.conv, std.experimental.allocator.common, std.traits;
import std.algorithm : min;
import std.conv : emplace;
import std.experimental.allocator.common : stateSize, forwardToMember,
roundUpToMultipleOf, alignedAt, alignDownTo, roundUpToMultipleOf;
import std.traits : hasMember;
import std.algorithm.comparison : min;
import std.typecons : Ternary;
import std.math : isPowerOf2;

static assert(
!stateSize!Prefix || Allocator.alignment >= Prefix.alignof,
Expand Down Expand Up @@ -48,6 +54,7 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void)
{
size_t goodAllocSize(size_t s)
{
import std.experimental.allocator.common : goodAllocSize;
auto a = actualAllocationSize(s);
return roundUpToMultipleOf(parent.goodAllocSize(a)
- stateSize!Prefix - stateSize!Suffix,
Expand Down Expand Up @@ -145,11 +152,7 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void)
static if (!stateSize!Suffix && hasMember!(Allocator, "expand"))
bool expand(ref void[] b, size_t delta)
{
if (!b.ptr)
{
b = allocate(delta);
return b.length == delta;
}
if (!b.ptr) return delta == 0;
auto t = actualAllocation(b);
const result = parent.expand(t, delta);
if (!result) return false;
Expand Down Expand Up @@ -184,21 +187,40 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void)
mixin(forwardToMember("parent",
"deallocateAll", "empty"));

// Computes suffix type given buffer type
private template Payload2Affix(Payload, Affix)
{
static if (is(Payload[] : void[]))
alias Payload2Affix = Affix;
else static if (is(Payload[] : shared(void)[]))
alias Payload2Affix = shared Affix;
else static if (is(Payload[] : immutable(void)[]))
alias Payload2Affix = shared Affix;
else static if (is(Payload[] : const(shared(void))[]))
alias Payload2Affix = shared Affix;
else static if (is(Payload[] : const(void)[]))
alias Payload2Affix = const Affix;
else
static assert(0, "Internal error for type " ~ Payload.stringof);
}

// Extra functions
static if (stateSize!Prefix)
static ref Prefix prefix(void[] b)
{
static auto ref prefix(T)(T[] b)
{
assert(b.ptr && b.ptr.alignedAt(Prefix.alignof));
return (cast(Prefix*)b.ptr)[-1];
return (cast(Payload2Affix!(T, Prefix)*) b.ptr)[-1];
}
}
static if (stateSize!Suffix)
ref Suffix suffix(void[] b)
auto ref suffix(T)(T[] b)
{
assert(b.ptr);
auto p = b.ptr - stateSize!Prefix
+ actualAllocationSize(b.length);
assert(p && p.alignedAt(Suffix.alignof));
return (cast(Suffix*) p)[-1];
return (cast(Payload2Affix!(T, Suffix)*) p)[-1];
}
}

Expand Down Expand Up @@ -227,18 +249,59 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void)
Ternary empty();

/**
The `instance` singleton is defined if and only if the parent allocator has no state and defines its own `it` object.
The `instance` singleton is defined if and only if the parent allocator
has no state and defines its own `it` object.
*/
static AffixAllocator instance;

/**
Affix access functions offering mutable references to the affixes of a block previously allocated with this allocator. $(D b) may not be null. They are defined if and only if the corresponding affix is not $(D void).
Affix access functions offering references to the affixes of a
block `b` previously allocated with this allocator. `b` may not be null.
They are defined if and only if the corresponding affix is not `void`.
The qualifiers of the affix are not always the same as the qualifiers
of the argument. This is because the affixes are not part of the data
itself, but instead are just $(I associated) with the data and known
to the allocator. The table below documents the type of `preffix(b)` and
`affix(b)` depending on the type of `b`.
Precondition: $(D b !is null)
$(BOOKTABLE Result of `prefix`/`suffix` depending on argument (`U` is
any unqualified type, `Affix` is `Prefix` or `Suffix`),
$(TR $(TH Argument$(NBSP)Type) $(TH Return) $(TH Comments))
$(TR $(TD `shared(U)[]`) $(TD `ref shared Affix`)
$(TD Data is shared across threads and the affix follows suit.))
$(TR $(TD `immutable(U)[]`) $(TD `ref shared Affix`)
$(TD Although the data is immutable, the allocator "knows" the
underlying memory is mutable, so `immutable` is elided for the affix
which is independent from the data itself. However, the result is
`shared` because `immutable` is implicitly shareable so multiple
threads may access and manipulate the affix for the same data.))
$(TR $(TD `const(shared(U))[]`) $(TD `ref shared Affix`)
$(TD The data is always shareable across threads. Even if the data
is `const`, the affix is modifiable by the same reasoning as for
`immutable`.))
$(TR $(TD `const(U)[]`) $(TD `ref const Affix`)
$(TD The input may have originated from `U[]` or `immutable(U)[]`,
so it may be actually shared or not. Returning an unqualified affix
may result in race conditions, whereas returning a `shared` affix
may result in inadvertent sharing of mutable thread-local data
across multiple threads. So the returned type is conservatively
`ref const`.))
$(TR $(TD `U[]`) $(TD `ref Affix`)
$(TD Unqualified data has unqualified affixes.))
)
Precondition: `b !is null` and `b` must have been allocated with
this allocator.
*/
static ref Prefix prefix(void[] b);
static ref auto prefix(T)(T[] b);
/// Ditto
static ref Suffix suffix(void[] b);
ref auto suffix(T)(T[] b);
}
else static if (is(typeof(Allocator.instance) == shared))
{
Expand Down Expand Up @@ -292,3 +355,20 @@ unittest
b = B.instance.allocate(100);
assert(b is null);
}

unittest
{
import std.experimental.allocator.gc_allocator;
import std.experimental.allocator;
alias MyAllocator = AffixAllocator!(GCAllocator, uint);
auto a = MyAllocator.instance.makeArray!(shared int)(100);
static assert(is(typeof(&MyAllocator.instance.prefix(a)) == shared(uint)*));
auto b = MyAllocator.instance.makeArray!(shared const int)(100);
static assert(is(typeof(&MyAllocator.instance.prefix(b)) == shared(uint)*));
auto c = MyAllocator.instance.makeArray!(immutable int)(100);
static assert(is(typeof(&MyAllocator.instance.prefix(c)) == shared(uint)*));
auto d = MyAllocator.instance.makeArray!(int)(100);
static assert(is(typeof(&MyAllocator.instance.prefix(d)) == uint*));
auto e = MyAllocator.instance.makeArray!(const int)(100);
static assert(is(typeof(&MyAllocator.instance.prefix(e)) == const(uint)*));
}
47 changes: 32 additions & 15 deletions std/experimental/allocator/building_blocks/allocator_list.d
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
///
module std.experimental.allocator.building_blocks.allocator_list;

import std.experimental.allocator.common;
Expand Down Expand Up @@ -64,7 +65,7 @@ struct AllocatorList(Factory, BookkeepingAllocator = GCAllocator)
{
import std.traits : hasMember;
import std.conv : emplace;
import std.algorithm : min, move;
import std.typecons : Ternary;
import std.experimental.allocator.building_blocks.stats_collector
: StatsCollector, Options;

Expand Down Expand Up @@ -102,7 +103,7 @@ struct AllocatorList(Factory, BookkeepingAllocator = GCAllocator)
// State is stored in an array, but it has a list threaded through it by
// means of "nextIdx".

// state {
// state
static if (!ouroboros)
{
static if (stateSize!BookkeepingAllocator) BookkeepingAllocator bkalloc;
Expand All @@ -114,7 +115,6 @@ struct AllocatorList(Factory, BookkeepingAllocator = GCAllocator)
}
private Node[] allocators;
private Node* root;
// }

static if (stateSize!Factory)
{
Expand Down Expand Up @@ -173,8 +173,8 @@ struct AllocatorList(Factory, BookkeepingAllocator = GCAllocator)
*p = n.next;
n.next = root;
root = n;
return result;
}
return result;
}
// Can't allocate from the current pool. Check if we just added a new
// allocator, in that case it won't do any good to add yet another.
Expand Down Expand Up @@ -228,10 +228,9 @@ struct AllocatorList(Factory, BookkeepingAllocator = GCAllocator)
}
auto toFree = allocators;

// Change state {
// Change state
root = newAllocators.ptr + (root - allocators.ptr);
allocators = newAllocators;
// }

// Free the olden buffer
static if (ouroboros)
Expand Down Expand Up @@ -381,11 +380,7 @@ struct AllocatorList(Factory, BookkeepingAllocator = GCAllocator)
&& hasMember!(Allocator, "owns"))
bool expand(ref void[] b, size_t delta)
{
if (!b.ptr)
{
b = allocate(delta);
return b.length == delta;
}
if (!b.ptr) return delta == 0;
for (auto p = &root, n = *p; n; p = &n.next, n = *p)
{
if (n.owns(b) == Ternary.yes) return n.expand(b, delta);
Expand Down Expand Up @@ -539,7 +534,7 @@ template AllocatorList(alias factoryFunction,
///
version(Posix) unittest
{
import std.algorithm : max;
import std.algorithm.comparison : max;
import std.experimental.allocator.building_blocks.region : Region;
import std.experimental.allocator.mmap_allocator : MmapAllocator;
import std.experimental.allocator.building_blocks.segregator : Segregator;
Expand Down Expand Up @@ -582,7 +577,7 @@ version(Posix) unittest
unittest
{
// Create an allocator based upon 4MB regions, fetched from the GC heap.
import std.algorithm : max;
import std.algorithm.comparison : max;
import std.experimental.allocator.building_blocks.region : Region;
AllocatorList!((n) => Region!GCAllocator(new void[max(n, 1024 * 4096)]),
NullAllocator) a;
Expand All @@ -596,7 +591,7 @@ unittest
unittest
{
// Create an allocator based upon 4MB regions, fetched from the GC heap.
import std.algorithm : max;
import std.algorithm.comparison : max;
import std.experimental.allocator.building_blocks.region : Region;
AllocatorList!((n) => Region!()(new void[max(n, 1024 * 4096)])) a;
auto b1 = a.allocate(1024 * 8192);
Expand All @@ -608,8 +603,9 @@ unittest

unittest
{
import std.algorithm : max;
import std.algorithm.comparison : max;
import std.experimental.allocator.building_blocks.region : Region;
import std.typecons : Ternary;
AllocatorList!((n) => Region!()(new void[max(n, 1024 * 4096)])) a;
auto b1 = a.allocate(1024 * 8192);
assert(b1 !is null);
Expand All @@ -619,3 +615,24 @@ unittest
a.deallocateAll();
assert(a.empty == Ternary.yes);
}

unittest
{
import std.experimental.allocator.building_blocks.region : Region;
enum bs = GCAllocator.alignment;
AllocatorList!((n) => Region!GCAllocator(256 * bs)) a;
auto b1 = a.allocate(192 * bs);
assert(b1.length == 192 * bs);
assert(a.allocators.length == 1);
auto b2 = a.allocate(64 * bs);
assert(b2.length == 64 * bs);
assert(a.allocators.length == 1);
auto b3 = a.allocate(192 * bs);
assert(b3.length == 192 * bs);
assert(a.allocators.length == 2);
a.deallocate(b1);
b1 = a.allocate(64 * bs);
assert(b1.length == 64 * bs);
assert(a.allocators.length == 2);
a.deallocateAll();
}
52 changes: 11 additions & 41 deletions std/experimental/allocator/building_blocks/bitmapped_block.d
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
///
module std.experimental.allocator.building_blocks.bitmapped_block;

import std.experimental.allocator.common;
Expand Down Expand Up @@ -44,15 +45,15 @@ block size to the constructor.
struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment,
ParentAllocator = NullAllocator)
{
import std.typecons;
import std.typecons : tuple, Tuple;
import std.traits : hasMember;
version(unittest) import std.stdio;
import std.conv : text;
import std.typecons : Ternary;

unittest
{
import std.experimental.allocator.mallocator : AlignedMallocator;
import std.algorithm : max;
import std.algorithm.comparison : max;
auto m = AlignedMallocator.instance.alignedAllocate(1024 * 64,
max(theAlignment, cast(uint) size_t.sizeof));
scope(exit) AlignedMallocator.instance.deallocate(m);
Expand Down Expand Up @@ -126,7 +127,7 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment
{
auto blocks = capacity.divideRoundUp(blockSize);
auto leadingUlongs = blocks.divideRoundUp(64);
import std.algorithm : min;
import std.algorithm.comparison : min;
immutable initialAlignment = min(parentAlignment,
1U << trailingZeros(leadingUlongs * 8));
auto maxSlack = alignment <= initialAlignment
Expand Down Expand Up @@ -259,7 +260,6 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment
@trusted void[] allocate(const size_t s)
{
const blocks = s.divideRoundUp(blockSize);
//writefln("Allocating %s blocks each of size %s", blocks, blockSize);
void[] result = void;

switcharoo:
Expand Down Expand Up @@ -304,6 +304,7 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment
*/
void[] alignedAllocate(size_t n, uint a)
{
import std.math : isPowerOf2;
assert(a.isPowerOf2);
if (a <= alignment) return allocate(n);

Expand Down Expand Up @@ -467,22 +468,6 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment
_control[i .. i + blocks] = 1;
return _payload[cast(size_t) (i * blockSize)
.. cast(size_t) ((i + blocks) * blockSize)];
//void[] result;
//auto pos = tuple(_startIdx, 0);
//for (;;)
//{
// if (pos[0] >= _control.rep.length)
// {
// // No more memory
// return null;
// }
// pos = allocateAt(pos[0], pos[1], blocks, result);
// if (pos[0] == size_t.max)
// {
// // Found and allocated
// return result;
// }
//}
}

// Rounds sizeInBytes to a multiple of blockSize.
Expand Down Expand Up @@ -510,11 +495,7 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment
{
// Dispose with trivial corner cases
if (delta == 0) return true;
if (b is null)
{
b = allocate(delta);
return b !is null;
}
if (b is null) return false;

/* To simplify matters, refuse to expand buffers that don't start at a block start (this may be the case for blocks allocated with alignedAllocate).
*/
Expand All @@ -534,7 +515,6 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment
assert((b.ptr - _payload.ptr) % blockSize == 0);
const blockIdx = (b.ptr - _payload.ptr) / blockSize;
const blockIdxAfter = blockIdx + blocksOld;
//writefln("blockIdx: %s, blockIdxAfter: %s", blockIdx, blockIdxAfter);

// Try the maximum
const wordIdx = blockIdxAfter / 64,
Expand Down Expand Up @@ -601,8 +581,6 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment
bool deallocate(void[] b)
{
if (b is null) return true;
// Adjust pointer, might be inside a block after alignedAllocate
//auto p = (b.ptr - _payload.ptr) / blockSize

// Locate position
immutable pos = b.ptr - _payload.ptr;
Expand Down Expand Up @@ -677,7 +655,7 @@ struct BitmappedBlock(size_t theBlockSize, uint theAlignment = platformAlignment

void dump()
{
import std.stdio;
import std.stdio : writefln, writeln;
writefln("%s @ %s {", typeid(this), cast(void*) _control._rep.ptr);
scope(exit) writeln("}");
assert(_payload.length == blockSize * _blocks);
Expand Down Expand Up @@ -734,7 +712,7 @@ unittest
{
static void testAllocateAll(size_t bs)(uint blocks, uint blocksAtATime)
{
import std.algorithm : min;
import std.algorithm.comparison : min;
assert(bs);
import std.experimental.allocator.gc_allocator : GCAllocator;
auto a = BitmappedBlock!(bs, min(bs, platformAlignment))(
Expand All @@ -753,8 +731,6 @@ unittest
text(x.ptr, " ", x.length, " ", a));
a.deallocateAll();

//writeln("Control words: ", a._control.length);
//writeln("Payload bytes: ", a._payload.length);
bool twice = true;

begin:
Expand Down Expand Up @@ -852,7 +828,6 @@ unittest
BitmappedBlock!(8, 8, NullAllocator) h1;
assert(h1.totalAllocation(1) >= 8);
assert(h1.totalAllocation(64) >= 64);
//writeln(h1.totalAllocation(8 * 64));
assert(h1.totalAllocation(8 * 64) >= 8 * 64);
assert(h1.totalAllocation(8 * 63) >= 8 * 63);
assert(h1.totalAllocation(8 * 64 + 1) >= 8 * 65);
Expand Down Expand Up @@ -884,6 +859,7 @@ struct BitmappedBlockWithInternalPointers(
ParentAllocator = NullAllocator)
{
import std.conv : text;
import std.typecons : Ternary;
unittest
{
import std.experimental.allocator.mallocator : AlignedMallocator;
Expand Down Expand Up @@ -989,11 +965,7 @@ struct BitmappedBlockWithInternalPointers(
bool expand(ref void[] b, size_t bytes)
{
if (!bytes) return true;
if (b is null)
{
b = allocate(bytes);
return b !is null;
}
if (b is null) return false;
immutable oldBlocks =
(b.length + _heap.blockSize - 1) / _heap.blockSize;
assert(oldBlocks);
Expand Down Expand Up @@ -1034,8 +1006,6 @@ struct BitmappedBlockWithInternalPointers(
// Find block start
auto block = (p - _heap._payload.ptr) / _heap.blockSize;
if (block >= _allocStart.length) return null;
// This may happen during marking, so comment it out.
// if (!_heap._control[block]) return null;
// Within an allocation, must find the 1 just to the left of it
auto i = _allocStart.find1Backward(block);
if (i == i.max) return null;
Expand Down
27 changes: 8 additions & 19 deletions std/experimental/allocator/building_blocks/bucketizer.d
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
///
module std.experimental.allocator.building_blocks.bucketizer;

/**
Expand All @@ -17,27 +18,18 @@ for $(D Bucketizer). To handle them separately, $(D Segregator) may be of use.
struct Bucketizer(Allocator, size_t min, size_t max, size_t step)
{
import std.traits : hasMember;
import common = std.experimental.allocator.common : roundUpToMultipleOf,
reallocate,
Ternary;
import common = std.experimental.allocator.common : roundUpToMultipleOf;
import std.typecons : Ternary;

static assert((max - (min - 1)) % step == 0,
"Invalid limits when instantiating " ~ Bucketizer.stringof);

//static if (min == chooseAtRuntime) size_t _min;
//else alias _min = min;
//static if (max == chooseAtRuntime) size_t _max;
//else alias _max = max;
//static if (step == chooseAtRuntime) size_t _step;
//else alias _step = step;

// state {
// state
/**
The array of allocators is publicly available for e.g. initialization and
inspection.
*/
Allocator[(max + 1 - min) / step] buckets;
// }

private Allocator* allocatorFor(size_t n)
{
Expand Down Expand Up @@ -100,11 +92,7 @@ struct Bucketizer(Allocator, size_t min, size_t max, size_t step)
*/
bool expand(ref void[] b, size_t delta)
{
if (!b.ptr)
{
b = allocate(delta);
return b.length == delta;
}
if (!b.ptr) return delta == 0;
assert(b.length >= min && b.length <= max);
const available = goodAllocSize(b.length);
const desired = b.length + delta;
Expand Down Expand Up @@ -235,8 +223,9 @@ unittest
import std.experimental.allocator.building_blocks.free_list : FreeList;
import std.experimental.allocator.building_blocks.region : Region;
import std.experimental.allocator.mallocator : Mallocator;
import std.experimental.allocator.common : unbounded, Ternary;
import std.algorithm : max;
import std.experimental.allocator.common : unbounded;
import std.typecons : Ternary;
import std.algorithm.comparison : max;
Bucketizer!(
FreeList!(
AllocatorList!(
Expand Down
13 changes: 6 additions & 7 deletions std/experimental/allocator/building_blocks/fallback_allocator.d
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
///
module std.experimental.allocator.building_blocks.fallback_allocator;

import std.experimental.allocator.common;
Expand All @@ -21,6 +22,7 @@ struct FallbackAllocator(Primary, Fallback)
{
import std.algorithm.comparison : min;
import std.traits : hasMember;
import std.typecons : Ternary;

unittest
{
Expand Down Expand Up @@ -95,11 +97,7 @@ struct FallbackAllocator(Primary, Fallback)
bool expand(ref void[] b, size_t delta)
{
if (!delta) return true;
if (!b.ptr)
{
b = allocate(delta);
return b.length == delta;
}
if (!b.ptr) return false;
if (primary.owns(b) == Ternary.yes)
{
static if (hasMember!(Primary, "expand"))
Expand Down Expand Up @@ -262,6 +260,7 @@ unittest
import std.experimental.allocator.building_blocks.region : InSituRegion;
import std.experimental.allocator.gc_allocator : GCAllocator;
import std.conv : text;
import std.typecons : Ternary;
FallbackAllocator!(InSituRegion!16_384, GCAllocator) a;
// This allocation uses the stack
auto b1 = a.allocate(1024);
Expand All @@ -285,7 +284,7 @@ private auto ref forward(alias arg)()
}
else
{
import std.algorithm : move;
import std.algorithm.mutation : move;
return move(arg);
}
}
Expand Down Expand Up @@ -328,7 +327,6 @@ FallbackAllocator!(Primary, Fallback)
fallbackAllocator(Primary, Fallback)(auto ref Primary p, auto ref Fallback f)
{
alias R = FallbackAllocator!(Primary, Fallback);
import std.algorithm : move;

static if (stateSize!Primary)
static if (stateSize!Fallback)
Expand All @@ -347,6 +345,7 @@ unittest
{
import std.experimental.allocator.building_blocks.region : Region;
import std.experimental.allocator.gc_allocator : GCAllocator;
import std.typecons : Ternary;
auto a = fallbackAllocator(Region!GCAllocator(1024), GCAllocator.instance);
auto b1 = a.allocate(1020);
assert(b1.length == 1020);
Expand Down
19 changes: 10 additions & 9 deletions std/experimental/allocator/building_blocks/free_list.d
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
///
module std.experimental.allocator.building_blocks.free_list;

import std.experimental.allocator.common;
import std.typecons : Flag, Yes, No;

/**
$(WEB en.wikipedia.org/wiki/Free_list, Free list allocator), stackable on top of
$(HTTP en.wikipedia.org/wiki/Free_list, Free list allocator), stackable on top of
another allocator. Allocation requests between $(D min) and $(D max) bytes are
rounded up to $(D max) and served from a singly-linked list of buffers
deallocated in the past. All other allocations are directed to $(D
Expand All @@ -30,6 +31,7 @@ struct FreeList(ParentAllocator,
import std.conv : text;
import std.exception : enforce;
import std.traits : hasMember;
import std.typecons : Ternary;

static assert(minSize != unbounded, "Use minSize = 0 for no low bound.");
static assert(maxSize >= (void*).sizeof,
Expand Down Expand Up @@ -165,7 +167,7 @@ struct FreeList(ParentAllocator,
return (cast(void*) p)[0 .. max];
}

// statistics {
// statistics
static if (adaptive == Yes.adaptive)
{
private enum double windowLength = 1000.0;
Expand Down Expand Up @@ -198,12 +200,11 @@ struct FreeList(ParentAllocator,
}
}
}
// } statistics

private struct Node { Node* next; }
static assert(ParentAllocator.alignment >= Node.alignof);

// state {
// state
/**
The parent allocator. Depending on whether $(D ParentAllocator) holds state
or not, this is a member variable or an alias for
Expand All @@ -214,7 +215,6 @@ struct FreeList(ParentAllocator,
private Node* root;
static if (minSize == chooseAtRuntime) private size_t _min = chooseAtRuntime;
static if (maxSize == chooseAtRuntime) private size_t _max = chooseAtRuntime;
// }

/**
Alignment offered.
Expand Down Expand Up @@ -392,7 +392,6 @@ unittest
FreeList!(GCAllocator, 0, 8) fl;
assert(fl.root is null);
auto b1 = fl.allocate(7);
//assert(fl._root !is null);
fl.allocate(8);
assert(fl.root is null);
fl.deallocate(b1);
Expand Down Expand Up @@ -429,14 +428,15 @@ struct ContiguousFreeList(ParentAllocator,
import std.experimental.allocator.building_blocks.stats_collector
: StatsCollector, Options;
import std.traits : hasMember;
import std.typecons : Ternary;

alias Impl = FreeList!(NullAllocator, minSize, maxSize);
enum unchecked = minSize == 0 && maxSize == unbounded;
alias Node = Impl.Node;

alias SParent = StatsCollector!(ParentAllocator, Options.bytesUsed);

// state {
// state
/**
The parent allocator. Depending on whether $(D ParentAllocator) holds state
or not, this is a member variable or an alias for
Expand All @@ -446,7 +446,6 @@ struct ContiguousFreeList(ParentAllocator,
FreeList!(NullAllocator, minSize, maxSize) fl;
void[] support;
size_t allocated;
// }

/// Alignment offered.
enum uint alignment = (void*).alignof;
Expand Down Expand Up @@ -684,6 +683,7 @@ unittest
{
import std.experimental.allocator.building_blocks.null_allocator
: NullAllocator;
import std.typecons : Ternary;
alias A = ContiguousFreeList!(NullAllocator, 0, 64);
auto a = A(new void[1024]);

Expand All @@ -708,6 +708,7 @@ unittest
{
import std.experimental.allocator.building_blocks.region : Region;
import std.experimental.allocator.gc_allocator : GCAllocator;
import std.typecons : Ternary;
alias A = ContiguousFreeList!(Region!GCAllocator, 0, 64);
auto a = A(Region!GCAllocator(1024 * 4), 1024);

Expand Down Expand Up @@ -758,7 +759,7 @@ struct SharedFreeList(ParentAllocator,
static assert(maxSize >= (void*).sizeof,
"Maximum size must accommodate a pointer.");

private import core.atomic;
import core.atomic : atomicOp, cas;

static if (minSize != chooseAtRuntime)
{
Expand Down
95 changes: 90 additions & 5 deletions std/experimental/allocator/building_blocks/free_tree.d
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
///
module std.experimental.allocator.building_blocks.free_tree;

import std.experimental.allocator.common;
Expand Down Expand Up @@ -51,14 +52,14 @@ struct FreeTree(ParentAllocator)
static assert(ParentAllocator.alignment % size_t.alignof == 0,
"FreeTree must be on top of a word-aligned allocator");

import std.algorithm : min, max, swap;
import std.algorithm.comparison : min, max;
import std.algorithm.mutation : swap;
import std.traits : hasMember;

// State {
// State
static if (stateSize!ParentAllocator) private ParentAllocator parent;
else private alias parent = ParentAllocator.instance;
private Node* root; // that's the entire added state
// }

private struct Node
{
Expand Down Expand Up @@ -287,14 +288,26 @@ struct FreeTree(ParentAllocator)
void[] allocate(size_t n)
{
assertValid;
auto result = findAndRemove(root, goodAllocSize(n));
if (n == 0) return null;

immutable s = goodAllocSize(n);

// Consult the free tree.
auto result = findAndRemove(root, s);
if (result.ptr) return result.ptr[0 .. n];

// No block found, try the parent allocator.
result = parent.allocate(s);
if (result.ptr) return result.ptr[0 .. n];

// Parent ran out of juice, desperation mode on
static if (hasMember!(ParentAllocator, "deallocate"))
{
clear;
return parent.allocate(n);
// Try parent allocator again.
result = parent.allocate(s);
if (result.ptr) return result.ptr[0 .. n];
return null;
}
else
{
Expand Down Expand Up @@ -400,3 +413,75 @@ unittest
import std.experimental.allocator.gc_allocator;
testAllocator!(() => FreeTree!GCAllocator());
}

unittest // issue 16506
{
import std.experimental.allocator.gc_allocator: GCAllocator;
import std.experimental.allocator.mallocator: Mallocator;

static void f(ParentAllocator)(size_t sz)
{
static FreeTree!ParentAllocator myAlloc;
byte[] _payload = cast(byte[]) myAlloc.allocate(sz);
assert(_payload, "_payload is null");
_payload[] = 0;
myAlloc.deallocate(_payload);
}

f!Mallocator(33);
f!Mallocator(43);
f!GCAllocator(1);
}

unittest // issue 16507
{
static struct MyAllocator
{
byte dummy;
static bool alive = true;
void[] allocate(size_t s) { return new byte[](s); }
bool deallocate(void[] ) { if (alive) assert(false); return true; }
enum alignment = size_t.sizeof;
}

FreeTree!MyAllocator ft;
void[] x = ft.allocate(1);
ft.deallocate(x);
ft.allocate(1000);
MyAllocator.alive = false;
}

unittest // "desperation mode"
{
uint myDeallocCounter = 0;

struct MyAllocator
{
byte[] allocation;
void[] allocate(size_t s)
{
if (allocation.ptr) return null;
allocation = new byte[](s);
return allocation;
}
bool deallocate(void[] )
{
++myDeallocCounter;
allocation = null;
return true;
}
enum alignment = size_t.sizeof;
}

FreeTree!MyAllocator ft;
void[] x = ft.allocate(1);
ft.deallocate(x);
assert(myDeallocCounter == 0);
x = ft.allocate(1000); // Triggers "desperation mode".
assert(myDeallocCounter == 1);
assert(x.ptr);
void[] y = ft.allocate(1000); /* Triggers "desperation mode" but there's
nothing to deallocate so MyAllocator can't deliver. */
assert(myDeallocCounter == 1);
assert(y.ptr is null);
}
35 changes: 17 additions & 18 deletions std/experimental/allocator/building_blocks/kernighan_ritchie.d
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
///
module std.experimental.allocator.building_blocks.kernighan_ritchie;
import std.experimental.allocator.building_blocks.null_allocator;

Expand All @@ -7,11 +8,11 @@ version(unittest) import std.conv : text;

// KRRegion
/**
$(D KRRegion) draws inspiration from the $(LINK2
std_experimental_allocator_region.html, region allocation strategy) and also the
$(WEB stackoverflow.com/questions/13159564/explain-this-implementation-of-malloc-from-the-kr-book,
$(D KRRegion) draws inspiration from the $(MREF_ALTTEXT region allocation
strategy, std,experimental,allocator,building_blocks,region) and also the
$(HTTP stackoverflow.com/questions/13159564/explain-this-implementation-of-malloc-from-the-kr-book,
famed allocator) described by Brian Kernighan and Dennis Ritchie in section 8.7
of the book $(WEB amazon.com/exec/obidos/ASIN/0131103628/classicempire, "The C
of the book $(HTTP amazon.com/exec/obidos/ASIN/0131103628/classicempire, "The C
Programming Language"), Second Edition, Prentice Hall, 1988.
$(H4 `KRRegion` = `Region` + Kernighan-Ritchie Allocator)
Expand Down Expand Up @@ -94,8 +95,9 @@ information is available in client code at deallocation time.)
*/
struct KRRegion(ParentAllocator = NullAllocator)
{
import std.experimental.allocator.common : stateSize, alignedAt, Ternary;
import std.experimental.allocator.common : stateSize, alignedAt;
import std.traits : hasMember;
import std.typecons : Ternary;

private static struct Node
{
Expand Down Expand Up @@ -149,7 +151,7 @@ struct KRRegion(ParentAllocator = NullAllocator)
}
}

// state {
// state
/**
If $(D ParentAllocator) holds state, $(D parent) is a public member of type
$(D KRRegion). Otherwise, $(D parent) is an $(D alias) for
Expand All @@ -160,7 +162,6 @@ struct KRRegion(ParentAllocator = NullAllocator)
private void[] payload;
private Node* root;
private bool regionMode = true;
// }

auto byNodePtr()
{
Expand Down Expand Up @@ -518,8 +519,6 @@ struct KRRegion(ParentAllocator = NullAllocator)
*/
void[] allocateAll()
{
//debug(KRRegion) assertValid("allocateAll");
//debug(KRRegion) scope(exit) assertValid("allocateAll");
if (regionMode) switchToFreeList;
if (root && root.next == root)
return allocate(root.size);
Expand Down Expand Up @@ -600,7 +599,7 @@ unittest
import std.experimental.allocator.gc_allocator : GCAllocator;
import std.experimental.allocator.building_blocks.fallback_allocator
: fallbackAllocator;
import std.experimental.allocator.common : Ternary;
import std.typecons : Ternary;
// KRRegion fronting a general-purpose allocator
ubyte[1024 * 128] buf;
auto alloc = fallbackAllocator(KRRegion!()(buf), GCAllocator.instance);
Expand All @@ -621,7 +620,7 @@ it actually returns memory to the operating system when possible.
*/
unittest
{
import std.algorithm : max;
import std.algorithm.comparison : max;
import std.experimental.allocator.gc_allocator : GCAllocator;
import std.experimental.allocator.mmap_allocator : MmapAllocator;
import std.experimental.allocator.building_blocks.allocator_list
Expand All @@ -631,9 +630,9 @@ unittest

unittest
{
import std.algorithm : max;
import std.algorithm.comparison : max;
import std.experimental.allocator.gc_allocator : GCAllocator;
import std.experimental.allocator.common : Ternary;
import std.typecons : Ternary;
import std.experimental.allocator.mallocator : Mallocator;
import std.experimental.allocator.building_blocks.allocator_list
: AllocatorList;
Expand Down Expand Up @@ -664,9 +663,9 @@ unittest

unittest
{
import std.algorithm : max;
import std.algorithm.comparison : max;
import std.experimental.allocator.gc_allocator : GCAllocator;
import std.experimental.allocator.common : Ternary;
import std.typecons : Ternary;
import std.experimental.allocator.mmap_allocator : MmapAllocator;
import std.experimental.allocator.building_blocks.allocator_list
: AllocatorList;
Expand Down Expand Up @@ -705,7 +704,7 @@ unittest
import std.experimental.allocator.gc_allocator : GCAllocator;
import std.experimental.allocator.building_blocks.allocator_list
: AllocatorList;
import std.algorithm : max;
import std.algorithm.comparison : max;
import std.experimental.allocator.common : testAllocator;
testAllocator!(() => AllocatorList!(
n => KRRegion!GCAllocator(max(n * 16, 1024 * 1024)))());
Expand All @@ -732,12 +731,12 @@ unittest
unittest
{
import std.experimental.allocator.gc_allocator : GCAllocator;
import std.experimental.allocator.common : Ternary;
import std.typecons : Ternary;
auto alloc = KRRegion!()(GCAllocator.instance.allocate(1024 * 1024));
const store = alloc.allocate(KRRegion!().sizeof);
auto p = cast(KRRegion!()* ) store.ptr;
import std.conv : emplace;
import std.algorithm : move;
import std.algorithm.mutation : move;
import core.stdc.string : memcpy;

memcpy(p, &alloc, alloc.sizeof);
Expand Down
20 changes: 6 additions & 14 deletions std/experimental/allocator/building_blocks/null_allocator.d
Original file line number Diff line number Diff line change
@@ -1,30 +1,22 @@
///
module std.experimental.allocator.building_blocks.null_allocator;

/*
_ _ _ _ _ _ _
| \ | | | | | /\ | | | | |
| \| |_ _| | | / \ | | | ___ ___ __ _| |_ ___ _ __
| . ` | | | | | | / /\ \ | | |/ _ \ / __/ _` | __/ _ \| '__|
| |\ | |_| | | |/ ____ \| | | (_) | (_| (_| | || (_) | |
|_| \_|\__,_|_|_/_/ \_\_|_|\___/ \___\__,_|\__\___/|_|
*/

/**
$(D NullAllocator) is an emphatically empty implementation of the allocator
interface. Although it has no direct use, it is useful as a "terminator" in
composite allocators.
*/
struct NullAllocator
{
import std.experimental.allocator.common : Ternary;
import std.typecons : Ternary;
/**
$(D NullAllocator) advertises a relatively large _alignment equal to 64 KB.
This is because $(D NullAllocator) never actually needs to honor this
alignment and because composite allocators using $(D NullAllocator)
shouldn't be unnecessarily constrained.
*/
enum uint alignment = 64 * 1024;
/// Returns $(D n).
// /// Returns $(D n).
//size_t goodAllocSize(size_t n) shared const
//{ return .goodAllocSize(this, n); }
/// Always returns $(D null).
Expand All @@ -38,8 +30,8 @@ struct NullAllocator
Precondition: $(D b is null). This is because there is no other possible
legitimate input.
*/
bool expand(ref void[] b, size_t) shared
{ assert(b is null); return false; }
bool expand(ref void[] b, size_t s) shared
{ assert(b is null); return s == 0; }
/// Ditto
bool reallocate(ref void[] b, size_t) shared
{ assert(b is null); return false; }
Expand Down Expand Up @@ -77,6 +69,6 @@ unittest
assert(b is null);
NullAllocator.instance.deallocate(b);
NullAllocator.instance.deallocateAll();
import std.experimental.allocator.common : Ternary;
import std.typecons : Ternary;
assert(NullAllocator.instance.owns(null) == Ternary.no);
}
24 changes: 11 additions & 13 deletions std/experimental/allocator/building_blocks/package.d
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ allocated. Put simply, the client must pass the allocated size upon
deallocation. Storing the size in the _allocator has significant negative
performance implications, and is virtually always redundant because client code
needs knowledge of the allocated size in order to avoid buffer overruns. (See
more discussion in a $(WEB open-
more discussion in a $(HTTP open-
std.org/JTC1/SC22/WG21/docs/papers/2013/n3536.html, proposal) for sized
deallocation in C++.) For this reason, allocators herein traffic in $(D void[])
as opposed to $(D void*).)
Expand Down Expand Up @@ -42,7 +42,7 @@ alignedReallocate) APIs.))
$(TR $(TDC size_t goodAllocSize(size_t n);, $(POST $(RES) >= n)) $(TD Allocators
customarily allocate memory in discretely-sized chunks. Therefore, a request for
$(D n) bytes may result in a larger allocation. The extra memory allocated goes
unused and adds to the so-called $(WEB goo.gl/YoKffF,internal fragmentation).
unused and adds to the so-called $(HTTP goo.gl/YoKffF,internal fragmentation).
The function $(D goodAllocSize(n)) returns the actual number of bytes that would
be allocated upon a request for $(D n) bytes. This module defines a default
implementation that returns $(D n) rounded up to a multiple of the allocator's
Expand Down Expand Up @@ -70,8 +70,8 @@ reallocated or deallocated with the usual primitives, if defined).))
$(TR $(TDC bool expand(ref void[] b, size_t delta);, $(POST !$(RES) || b.length
== $(I old)(b).length + delta)) $(TD Expands $(D b) by $(D delta) bytes. If $(D
delta == 0), succeeds without changing $(D b). If $(D b is null), the call
evaluates $(D b = allocate(delta)) and returns $(D b !is null). Otherwise, $(D
delta == 0), succeeds without changing $(D b). If $(D b is null), returns
`false` (the null pointer cannot be expanded in place). Otherwise, $(D
b) must be a buffer previously allocated with the same allocator. If expansion
was successful, $(D expand) changes $(D b)'s length to $(D b.length + delta) and
returns $(D true). Upon failure, the call effects no change upon the allocator
Expand Down Expand Up @@ -135,7 +135,7 @@ thread-safe or not, this instance may be $(D shared).))
$(H2 Sample Assembly)
The example below features an _allocator modeled after $(WEB goo.gl/m7329l,
The example below features an _allocator modeled after $(HTTP goo.gl/m7329l,
jemalloc), which uses a battery of free-list allocators spaced so as to keep
internal fragmentation to a minimum. The $(D FList) definitions specify no
bounds for the freelist because the $(D Segregator) does all size selection in
Expand Down Expand Up @@ -201,8 +201,7 @@ or `import` `std.experimental.building_blocks`, which imports them all
`public`ly. The building blocks can be assembled in unbounded ways and also
combined with your own. For a collection of typical and useful preassembled
allocators and for inspiration in defining more such assemblies, refer to
$(LINK2 std_experimental_allocator_showcase.html,
`std.experimental.allocator.building_blocks.showcase`).)
$(MREF std,experimental,allocator,showcase).)
$(BOOKTABLE,
$(TR $(TH Allocator$(BR)) $(TH Description))
Expand All @@ -220,8 +219,8 @@ to leak.))
$(TR $(TDC3 AlignedMallocator, mallocator) $(TD Interface to OS-specific _allocators that
support specifying alignment:
$(WEB man7.org/linux/man-pages/man3/posix_memalign.3.html, $(D posix_memalign))
on Posix and $(WEB msdn.microsoft.com/en-us/library/fs9stz4e(v=vs.80).aspx,
$(HTTP man7.org/linux/man-pages/man3/posix_memalign.3.html, $(D posix_memalign))
on Posix and $(HTTP msdn.microsoft.com/en-us/library/fs9stz4e(v=vs.80).aspx,
$(D __aligned_xxx)) on Windows.))
$(TR $(TDC2 AffixAllocator, affix_allocator) $(TD Allocator that allows and manages allocating
Expand All @@ -236,7 +235,7 @@ $(TR $(TDC2 FallbackAllocator, fallback_allocator) $(TD Allocator that combines
upon failure are passed to the fallback. Useful for small and fast allocators
fronting general-purpose ones.))
$(TR $(TDC2 FreeList, free_list) $(TD Allocator that implements a $(WEB
$(TR $(TDC2 FreeList, free_list) $(TD Allocator that implements a $(HTTP
wikipedia.org/wiki/Free_list, free list) on top of any other allocator. The
preferred size, tolerance, and maximum elements are configurable at compile- and
run time.))
Expand Down Expand Up @@ -280,9 +279,8 @@ pointers on top of another allocator.)))
)
Macros:
MYREF = $(LINK2 std_experimental_allocator_building_blocks_$2.html, $1)&nbsp;
MYREF2 = $(LINK2 std_experimental_allocator_building_blocks_$2.html#$1, $1)&nbsp;
MYREF3 = $(LINK2 std_experimental_allocator_$2.html#$1, $1)&nbsp;
MYREF2 = $(REF_SHORT $1, std,experimental,allocator,building_blocks,$2)
MYREF3 = $(REF_SHORT $1, std,experimental,allocator,$2)
TDC = $(TDNW $(D $1)$+)
TDC2 = $(TDNW $(D $(MYREF2 $1,$+))$(BR)$(SMALL
$(D std.experimental.allocator.building_blocks.$2)))
Expand Down
7 changes: 2 additions & 5 deletions std/experimental/allocator/building_blocks/quantizer.d
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
///
module std.experimental.allocator.building_blocks.quantizer;

import std.experimental.allocator.common;
Expand Down Expand Up @@ -98,11 +99,7 @@ struct Quantizer(ParentAllocator, alias roundingFunction)
*/
bool expand(ref void[] b, size_t delta)
{
if (!b.ptr)
{
b = allocate(delta);
return b.length == delta;
}
if (!b.ptr) return delta == 0;
immutable allocated = goodAllocSize(b.length),
needed = b.length + delta,
neededAllocation = goodAllocSize(needed);
Expand Down
47 changes: 13 additions & 34 deletions std/experimental/allocator/building_blocks/region.d
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
///
module std.experimental.allocator.building_blocks.region;

import std.experimental.allocator.common;
Expand Down Expand Up @@ -33,8 +34,9 @@ struct Region(ParentAllocator = NullAllocator,
static assert(ParentAllocator.alignment >= minAlign);

import std.traits : hasMember;
import std.typecons : Ternary;

// state {
// state
/**
The _parent allocator. Depending on whether $(D ParentAllocator) holds state
or not, this is a member variable or an alias for
Expand All @@ -49,7 +51,6 @@ struct Region(ParentAllocator = NullAllocator,
alias parent = ParentAllocator.instance;
}
private void* _current, _begin, _end;
// }

/**
Constructs a region backed by a user-provided store. Assumes $(D store) is
Expand Down Expand Up @@ -88,10 +89,9 @@ struct Region(ParentAllocator = NullAllocator,
}

/*
TODO: The postblit of $(D BasicRegion) is disabled because such objects
TODO: The postblit of $(D BasicRegion) should be disabled because such objects
should not be copied around naively.
*/
//@disable this(this);

/**
If `ParentAllocator` is not `NullAllocator` and defines `deallocate`, the region defines a destructor that uses `ParentAllocator.delete` to free the
Expand Down Expand Up @@ -164,6 +164,7 @@ struct Region(ParentAllocator = NullAllocator,
*/
void[] alignedAllocate(size_t n, uint a)
{
import std.math : isPowerOf2;
assert(a.isPowerOf2);
static if (growDownwards)
{
Expand Down Expand Up @@ -219,11 +220,7 @@ struct Region(ParentAllocator = NullAllocator,
{
assert(owns(b) == Ternary.yes || b.ptr is null);
assert(b.ptr + b.length <= _current || b.ptr is null);
if (!b.ptr)
{
b = allocate(delta);
return b.length == delta;
}
if (!b.ptr) return delta == 0;
auto newLength = b.length + delta;
if (_current < b.ptr + b.length + alignment)
{
Expand Down Expand Up @@ -335,7 +332,7 @@ unittest
import std.experimental.allocator.mallocator : Mallocator;
import std.experimental.allocator.building_blocks.allocator_list
: AllocatorList;
import std.algorithm : max;
import std.algorithm.comparison : max;
// Create a scalable list of regions. Each gets at least 1MB at a time by
// using malloc.
auto batchAllocator = AllocatorList!(
Expand Down Expand Up @@ -378,9 +375,10 @@ hot memory is used first.
*/
struct InSituRegion(size_t size, size_t minAlign = platformAlignment)
{
import std.algorithm : max;
import std.algorithm.comparison : max;
import std.conv : to;
import std.traits : hasMember;
import std.typecons : Ternary;

static assert(minAlign.isGoodStaticAlignment);
static assert(size >= minAlign);
Expand Down Expand Up @@ -425,12 +423,6 @@ struct InSituRegion(size_t size, size_t minAlign = platformAlignment)
private void lazyInit()
{
assert(!_impl._current);
//static if (alignment > double.alignof)
//{
// auto p = _store.ptr.alignUpTo(alignment);
//}
//else
// auto p = _store.ptr;
_impl = typeof(_impl)(_store);
assert(_impl._current.alignedAt(alignment));
}
Expand Down Expand Up @@ -590,7 +582,7 @@ private extern(C) int brk(shared void*);
/**
Allocator backed by $(D $(LUCKY sbrk)) for Posix systems. Due to the fact that
$(D sbrk) is not thread-safe $(WEB lifecs.likai.org/2010/02/sbrk-is-not-thread-
$(D sbrk) is not thread-safe $(HTTP lifecs.likai.org/2010/02/sbrk-is-not-thread-
safe.html, by design), $(D SbrkRegion) uses a mutex internally. This implies
that uncontrolled calls to $(D brk) and $(D sbrk) may affect the workings of $(D
SbrkRegion) adversely.
Expand All @@ -602,21 +594,7 @@ version(Posix) struct SbrkRegion(uint minAlign = platformAlignment)
pthread_mutex_t, pthread_mutex_lock, pthread_mutex_unlock,
PTHREAD_MUTEX_INITIALIZER;
private static shared pthread_mutex_t sbrkMutex = PTHREAD_MUTEX_INITIALIZER;

// workaround for https://issues.dlang.org/show_bug.cgi?id=14617
version(OSX)
{
shared static this()
{
pthread_mutex_init(cast(pthread_mutex_t*) &sbrkMutex, null) == 0
|| assert(0);
}
shared static ~this()
{
pthread_mutex_destroy(cast(pthread_mutex_t*) &sbrkMutex) == 0
|| assert(0);
}
}
import std.typecons : Ternary;

static assert(minAlign.isGoodStaticAlignment);
static assert(size_t.sizeof == (void*).sizeof);
Expand Down Expand Up @@ -699,7 +677,7 @@ version(Posix) struct SbrkRegion(uint minAlign = platformAlignment)
*/
bool expand(ref void[] b, size_t delta) shared
{
if (b is null) return (b = allocate(delta)) !is null;
if (b is null) return delta == 0;
assert(_brkInitial && _brkCurrent); // otherwise where did b come from?
pthread_mutex_lock(cast(pthread_mutex_t*) &sbrkMutex) == 0 || assert(0);
scope(exit) pthread_mutex_unlock(cast(pthread_mutex_t*) &sbrkMutex) == 0
Expand Down Expand Up @@ -788,6 +766,7 @@ version(Posix) unittest

version(Posix) unittest
{
import std.typecons : Ternary;
alias alloc = SbrkRegion!(8).instance;
auto a = alloc.alignedAllocate(2001, 4096);
assert(a.length == 2001);
Expand Down
21 changes: 19 additions & 2 deletions std/experimental/allocator/building_blocks/scoped_allocator.d
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
///
module std.experimental.allocator.building_blocks.scoped_allocator;

import std.experimental.allocator.common;
Expand All @@ -24,6 +25,7 @@ struct ScopedAllocator(ParentAllocator)
private import std.experimental.allocator.building_blocks.affix_allocator
: AffixAllocator;
private import std.traits : hasMember;
import std.typecons : Ternary;

private struct Node
{
Expand All @@ -34,7 +36,7 @@ struct ScopedAllocator(ParentAllocator)

alias Allocator = AffixAllocator!(ParentAllocator, Node);

// state {
// state
/**
If $(D ParentAllocator) is stateful, $(D parent) is a property giving access
to an $(D AffixAllocator!ParentAllocator). Otherwise, $(D parent) is an alias for `AffixAllocator!ParentAllocator.instance`.
Expand All @@ -48,7 +50,6 @@ struct ScopedAllocator(ParentAllocator)
alias parent = Allocator.instance;
}
private Node* root;
// }

/**
$(D ScopedAllocator) is not copyable.
Expand Down Expand Up @@ -88,6 +89,8 @@ struct ScopedAllocator(ParentAllocator)
toInsert.prev = null;
toInsert.next = root;
toInsert.length = n;
assert(!root || !root.prev);
if (root) root.prev = toInsert;
root = toInsert;
return b;
}
Expand Down Expand Up @@ -127,6 +130,7 @@ struct ScopedAllocator(ParentAllocator)
n.prev = null;
n.next = root;
n.length = s;
if (root) root.prev = n;
root = n;
}
return result;
Expand Down Expand Up @@ -190,6 +194,7 @@ struct ScopedAllocator(ParentAllocator)
unittest
{
import std.experimental.allocator.mallocator : Mallocator;
import std.typecons : Ternary;
ScopedAllocator!Mallocator alloc;
assert(alloc.empty == Ternary.yes);
const b = alloc.allocate(10);
Expand All @@ -202,3 +207,15 @@ unittest
import std.experimental.allocator.gc_allocator : GCAllocator;
testAllocator!(() => ScopedAllocator!GCAllocator());
}

unittest // https://issues.dlang.org/show_bug.cgi?id=16046
{
import std.exception;
import std.experimental.allocator;
import std.experimental.allocator.mallocator;
ScopedAllocator!Mallocator alloc;
auto foo = alloc.make!int(1).enforce;
auto bar = alloc.make!int(2).enforce;
alloc.dispose(foo);
alloc.dispose(bar); // segfault here
}
12 changes: 4 additions & 8 deletions std/experimental/allocator/building_blocks/segregator.d
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
///
module std.experimental.allocator.building_blocks.segregator;

import std.experimental.allocator.common;
Expand All @@ -13,8 +14,9 @@ shared) methods.
*/
struct Segregator(size_t threshold, SmallAllocator, LargeAllocator)
{
import std.algorithm : min;
import std.algorithm.comparison : min;
import std.traits : hasMember;
import std.typecons : Ternary;

static if (stateSize!SmallAllocator) private SmallAllocator _small;
else private alias _small = SmallAllocator.instance;
Expand Down Expand Up @@ -150,6 +152,7 @@ struct Segregator(size_t threshold, SmallAllocator, LargeAllocator)
|| hasMember!(LargeAllocator, "expand"))
bool expand(ref void[] b, size_t delta)
{
if (!delta) return true;
if (b.length + delta <= threshold)
{
// Old and new allocations handled by _small
Expand Down Expand Up @@ -256,7 +259,6 @@ struct Segregator(size_t threshold, SmallAllocator, LargeAllocator)
&& !stateSize!LargeAllocator
&& is(typeof(SmallAllocator.instance) == shared)
&& is(typeof(LargeAllocator.instance) == shared);
//pragma(msg, sharedMethods);

static if (sharedMethods)
{
Expand Down Expand Up @@ -336,12 +338,6 @@ template Segregator(Args...) if (Args.length > 3)
.Segregator!(Args[2 .. $])
);
}

// Linear search
//alias Segregator = .Segregator!(
// Args[0], Args[1],
// .Segregator!(Args[2 .. $])
//);
}

///
Expand Down
7 changes: 2 additions & 5 deletions std/experimental/allocator/building_blocks/stats_collector.d
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ enum Options : ulong
bytesNotMoved = 1u << 15,
/**
Measures the sum of extra bytes allocated beyond the bytes requested, i.e.
the $(WEB goo.gl/YoKffF, internal fragmentation). This is the current
the $(HTTP goo.gl/YoKffF, internal fragmentation). This is the current
effective number of slack bytes, and it goes up and down with time.
*/
bytesSlack = 1u << 16,
Expand Down Expand Up @@ -159,6 +159,7 @@ struct StatsCollector(Allocator, ulong flags = Options.all,
{
private:
import std.traits : hasMember, Signed;
import std.typecons : Ternary;

static string define(string type, string[] names...)
{
Expand Down Expand Up @@ -702,10 +703,6 @@ unittest
assert(a.numDeallocate == 3);
assert(a.numAllocate == a.numDeallocate);
assert(a.bytesUsed == 0);

//import std.stdio;
//Allocator.reportPerCallStatistics(stdout);
//a.reportStatistics(stdout);
}

import std.experimental.allocator.gc_allocator : GCAllocator;
Expand Down
223 changes: 17 additions & 206 deletions std/experimental/allocator/common.d
Original file line number Diff line number Diff line change
Expand Up @@ -3,157 +3,10 @@ Utility and ancillary artifacts of `std.experimental.allocator`. This module
shouldn't be used directly; its functionality will be migrated into more
appropriate parts of `std`.
Authors: $(WEB erdani.com, Andrei Alexandrescu), Timon Gehr (`Ternary`)
Authors: $(HTTP erdani.com, Andrei Alexandrescu), Timon Gehr (`Ternary`)
*/
module std.experimental.allocator.common;
import std.algorithm, std.traits;

/**
Ternary type with three thruth values.
*/
struct Ternary
{
@safe @nogc nothrow pure:

package ubyte value = 6;
package static Ternary make(ubyte b)
{
Ternary r = void;
r.value = b;
return r;
}

/**
In addition to `false` and `true`, `Ternary` offers `unknown`.
*/
enum no = make(0);
/// ditto
enum yes = make(2);
/// ditto
enum unknown = make(6);

/**
Construct and assign from a `bool`, receiving `no` for `false` and `yes`
for `true`.
*/
this(bool b) { value = b << 1; }

/// ditto
void opAssign(bool b) { value = b << 1; }

/**
Construct a ternary value from another ternary value
*/
this(const Ternary b) { value = b.value; }

/**
$(TABLE Truth table for logical operations,
$(TR $(TH `a`) $(TH `b`) $(TH `$(TILDE)a`) $(TH `a | b`) $(TH `a & b`) $(TH `a ^ b`))
$(TR $(TD `no`) $(TD `no`) $(TD `yes`) $(TD `no`) $(TD `no`) $(TD `no`))
$(TR $(TD `no`) $(TD `yes`) $(TD) $(TD `yes`) $(TD `no`) $(TD `yes`))
$(TR $(TD `no`) $(TD `unknown`) $(TD) $(TD `unknown`) $(TD `no`) $(TD `unknown`))
$(TR $(TD `yes`) $(TD `no`) $(TD `no`) $(TD `yes`) $(TD `no`) $(TD `yes`))
$(TR $(TD `yes`) $(TD `yes`) $(TD) $(TD `yes`) $(TD `yes`) $(TD `no`))
$(TR $(TD `yes`) $(TD `unknown`) $(TD) $(TD `yes`) $(TD `unknown`) $(TD `unknown`))
$(TR $(TD `unknown`) $(TD `no`) $(TD `unknown`) $(TD `unknown`) $(TD `no`) $(TD `unknown`))
$(TR $(TD `unknown`) $(TD `yes`) $(TD) $(TD `yes`) $(TD `unknown`) $(TD `unknown`))
$(TR $(TD `unknown`) $(TD `unknown`) $(TD) $(TD `unknown`) $(TD `unknown`) $(TD `unknown`))
)
*/
Ternary opUnary(string s)() if (s == "~")
{
return make(386 >> value & 6);
}

/// ditto
Ternary opBinary(string s)(Ternary rhs) if (s == "|")
{
return make(25_512 >> value + rhs.value & 6);
}

/// ditto
Ternary opBinary(string s)(Ternary rhs) if (s == "&")
{
return make(26_144 >> value + rhs.value & 6);
}

/// ditto
Ternary opBinary(string s)(Ternary rhs) if (s == "^")
{
return make(26_504 >> value + rhs.value & 6);
}
}

@safe @nogc nothrow pure
unittest
{
alias f = Ternary.no, t = Ternary.yes, u = Ternary.unknown;
Ternary[27] truthTableAnd =
[
t, t, t,
t, u, u,
t, f, f,
u, t, u,
u, u, u,
u, f, f,
f, t, f,
f, u, f,
f, f, f,
];

Ternary[27] truthTableOr =
[
t, t, t,
t, u, t,
t, f, t,
u, t, t,
u, u, u,
u, f, u,
f, t, t,
f, u, u,
f, f, f,
];

Ternary[27] truthTableXor =
[
t, t, f,
t, u, u,
t, f, t,
u, t, u,
u, u, u,
u, f, u,
f, t, t,
f, u, u,
f, f, f,
];

for (auto i = 0; i != truthTableAnd.length; i += 3)
{
assert((truthTableAnd[i] & truthTableAnd[i + 1])
== truthTableAnd[i + 2]);
assert((truthTableOr[i] | truthTableOr[i + 1])
== truthTableOr[i + 2]);
assert((truthTableXor[i] ^ truthTableXor[i + 1])
== truthTableXor[i + 2]);
}

Ternary a;
assert(a == Ternary.unknown);
static assert(!is(typeof({ if (a) {} })));
assert(!is(typeof({ auto b = Ternary(3); })));
a = true;
assert(a == Ternary.yes);
a = false;
assert(a == Ternary.no);
a = Ternary.unknown;
assert(a == Ternary.unknown);
Ternary b;
b = a;
assert(b == a);
assert(~Ternary.yes == Ternary.no);
assert(~Ternary.no == Ternary.yes);
assert(~Ternary.unknown == Ternary.unknown);
}
import std.algorithm.comparison, std.traits;

/**
Returns the size in bytes of the state that needs to be allocated to hold an
Expand Down Expand Up @@ -214,7 +67,7 @@ enum unbounded = size_t.max;
The alignment that is guaranteed to accommodate any D object allocation on the
current platform.
*/
enum uint platformAlignment = std.algorithm.max(double.alignof, real.alignof);
enum uint platformAlignment = std.algorithm.comparison.max(double.alignof, real.alignof);

/**
The default good size allocation is deduced as $(D n) rounded up to the
Expand Down Expand Up @@ -251,6 +104,7 @@ Returns `n` rounded up to a multiple of alignment, which must be a power of 2.
@safe @nogc nothrow pure
package size_t roundUpToAlignment(size_t n, uint alignment)
{
import std.math : isPowerOf2;
assert(alignment.isPowerOf2);
immutable uint slack = cast(uint) n & (alignment - 1);
const result = slack
Expand All @@ -275,6 +129,7 @@ Returns `n` rounded down to a multiple of alignment, which must be a power of 2.
@safe @nogc nothrow pure
package size_t roundDownToAlignment(size_t n, uint alignment)
{
import std.math : isPowerOf2;
assert(alignment.isPowerOf2);
return n & ~size_t(alignment - 1);
}
Expand Down Expand Up @@ -409,7 +264,7 @@ unittest
Returns `true` if `ptr` is aligned at `alignment`.
*/
@nogc nothrow pure
package bool alignedAt(void* ptr, uint alignment)
package bool alignedAt(T)(T* ptr, uint alignment)
{
return cast(size_t) ptr % alignment == 0;
}
Expand Down Expand Up @@ -438,6 +293,7 @@ than or equal to the given pointer.
@nogc nothrow pure
package void* alignDownTo(void* ptr, uint alignment)
{
import std.math : isPowerOf2;
assert(alignment.isPowerOf2);
return cast(void*) (cast(size_t) ptr & ~(alignment - 1UL));
}
Expand All @@ -449,76 +305,26 @@ than or equal to the given pointer.
@nogc nothrow pure
package void* alignUpTo(void* ptr, uint alignment)
{
import std.math : isPowerOf2;
assert(alignment.isPowerOf2);
immutable uint slack = cast(size_t) ptr & (alignment - 1U);
return slack ? ptr + alignment - slack : ptr;
}

// Credit: Matthias Bentrup
/**
Returns `true` if `x` is a nonzero power of two.
*/
@safe @nogc nothrow pure
package bool isPowerOf2(uint x)
{
return (x & -x) > (x - 1);
}

@safe @nogc nothrow pure
unittest
{
assert(!isPowerOf2(0));
assert(isPowerOf2(1));
assert(isPowerOf2(2));
assert(!isPowerOf2(3));
assert(isPowerOf2(4));
assert(!isPowerOf2(5));
assert(!isPowerOf2(6));
assert(!isPowerOf2(7));
assert(isPowerOf2(8));
assert(!isPowerOf2(9));
assert(!isPowerOf2(10));
assert(isPowerOf2(1UL << 31));
}

@safe @nogc nothrow pure
package bool isGoodStaticAlignment(uint x)
{
import std.math : isPowerOf2;
return x.isPowerOf2;
}

@safe @nogc nothrow pure
package bool isGoodDynamicAlignment(uint x)
{
import std.math : isPowerOf2;
return x.isPowerOf2 && x >= (void*).sizeof;
}

/*
If $(D b.length + delta <= a.goodAllocSize(b.length)), $(D expand) just adjusts
$(D b) and returns $(D true). Otherwise, returns $(D false).
$(D expand) does not attempt to use $(D Allocator.reallocate) even if
defined. This is deliberate so allocators may use it internally within their own
implementation of $(D expand).
*/
//bool expand(Allocator)(ref Allocator a, ref void[] b, size_t delta)
//{
// if (!b.ptr)
// {
// b = a.allocate(delta);
// return b.length == delta;
// }
// if (delta == 0) return true;
// immutable length = b.length + delta;
// if (length <= a.goodAllocSize(b.length))
// {
// b = b.ptr[0 .. length];
// return true;
// }
// return false;
//}

/**
The default $(D reallocate) function first attempts to use $(D expand). If $(D
Allocator.expand) is not defined or returns $(D false), $(D reallocate)
Expand Down Expand Up @@ -601,10 +407,13 @@ Forwards each of the methods in `funs` (if defined) to `member`.
return result;
}

version(unittest)
package void testAllocator(alias make)()
{
import std.conv : text;
import std.stdio : writeln, stderr;
import std.math : isPowerOf2;
import std.typecons : Ternary;
alias A = typeof(make());
scope(failure) stderr.writeln("testAllocator failed for ", A.stringof);

Expand Down Expand Up @@ -675,15 +484,17 @@ package void testAllocator(alias make)()
void[] b5 = null;
assert(aa.expand(b5, 0));
assert(b5 is null);
assert(aa.expand(b5, 1));
assert(b5.length == 1);
assert(!aa.expand(b5, 1));
assert(b5.length == 0);
}}

void[] b6 = null;
assert(a.reallocate(b6, 0));
assert(b6.length == 0);
assert(a.reallocate(b6, 1));
assert(b6.length == 1, text(b6.length));
assert(a.reallocate(b6, 2));
assert(b6.length == 2);

// Test owns
static if (hasMember!(A, "owns"))
Expand Down
29 changes: 13 additions & 16 deletions std/experimental/allocator/gc_allocator.d
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
///
module std.experimental.allocator.gc_allocator;
import std.experimental.allocator.common;

Expand All @@ -20,7 +21,7 @@ struct GCAllocator
deallocate) and $(D reallocate) methods are $(D @system) because they may
move memory around, leaving dangling pointers in user code.
*/
@trusted void[] allocate(size_t bytes) shared
pure nothrow @trusted void[] allocate(size_t bytes) shared
{
if (!bytes) return null;
auto p = GC.malloc(bytes);
Expand All @@ -31,15 +32,11 @@ struct GCAllocator
@system bool expand(ref void[] b, size_t delta) shared
{
if (delta == 0) return true;
if (b is null)
{
b = allocate(delta);
return b.ptr != null; // we assume allocate will achieve the correct size.
}
if (b is null) return false;
immutable curLength = GC.sizeOf(b.ptr);
assert(curLength != 0); // we have a valid GC pointer here
immutable desired = b.length + delta;
if(desired > curLength) // check to see if the current block can't hold the data
if (desired > curLength) // check to see if the current block can't hold the data
{
immutable sizeRequest = desired - curLength;
immutable newSize = GC.extend(b.ptr, sizeRequest, sizeRequest);
Expand All @@ -55,7 +52,7 @@ struct GCAllocator
}

/// Ditto
@system bool reallocate(ref void[] b, size_t newSize) shared
pure nothrow @system bool reallocate(ref void[] b, size_t newSize) shared
{
import core.exception : OutOfMemoryError;
try
Expand All @@ -72,15 +69,15 @@ struct GCAllocator
}

/// Ditto
void[] resolveInternalPointer(void* p) shared
pure nothrow void[] resolveInternalPointer(void* p) shared
{
auto r = GC.addrOf(p);
if (!r) return null;
return r[0 .. GC.sizeOf(r)];
}

/// Ditto
@system bool deallocate(void[] b) shared
pure nothrow @system bool deallocate(void[] b) shared
{
GC.free(b.ptr);
return true;
Expand All @@ -89,12 +86,12 @@ struct GCAllocator
/// Ditto
size_t goodAllocSize(size_t n) shared
{
if(n == 0)
if (n == 0)
return 0;
if(n <= 16)
if (n <= 16)
return 16;

import core.bitop: bsr;
import core.bitop : bsr;

auto largestBit = bsr(n-1) + 1;
if (largestBit <= 12) // 4096 or less
Expand All @@ -113,7 +110,7 @@ struct GCAllocator
static shared GCAllocator instance;

// Leave it undocummented for now.
@trusted void collect() shared
nothrow @trusted void collect() shared
{
GC.collect();
}
Expand All @@ -136,11 +133,11 @@ unittest

unittest
{
import core.memory: GC;
import core.memory : GC;

// test allocation sizes
assert(GCAllocator.instance.goodAllocSize(1) == 16);
for(size_t s = 16; s <= 8192; s *= 2)
for (size_t s = 16; s <= 8192; s *= 2)
{
assert(GCAllocator.instance.goodAllocSize(s) == s);
assert(GCAllocator.instance.goodAllocSize(s - (s / 2) + 1) == s);
Expand Down
25 changes: 14 additions & 11 deletions std/experimental/allocator/mallocator.d
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
///
module std.experimental.allocator.mallocator;
import std.experimental.allocator.common;

Expand Down Expand Up @@ -132,7 +133,7 @@ version (Windows)
@nogc nothrow
private void* _aligned_malloc(size_t size, size_t alignment)
{
import std.c.stdlib: malloc;
import std.c.stdlib : malloc;
size_t offset = alignment + size_t.sizeof * 2 - 1;

// unaligned chunk
Expand All @@ -154,10 +155,10 @@ version (Windows)
@nogc nothrow
private void* _aligned_realloc(void* ptr, size_t size, size_t alignment)
{
import std.c.stdlib: free;
import std.c.string: memcpy;
import std.c.stdlib : free;
import std.c.string : memcpy;

if(!ptr) return _aligned_malloc(size, alignment);
if (!ptr) return _aligned_malloc(size, alignment);

// gets the header from the exising pointer
AlignInfo* head = AlignInfo(ptr);
Expand All @@ -181,7 +182,7 @@ version (Windows)
@nogc nothrow
private void _aligned_free(void *ptr)
{
import std.c.stdlib: free;
import std.c.stdlib : free;
if (!ptr) return;
AlignInfo* head = AlignInfo(ptr);
free(head.basePtr);
Expand Down Expand Up @@ -220,9 +221,9 @@ struct AlignedMallocator
}

/**
Uses $(WEB man7.org/linux/man-pages/man3/posix_memalign.3.html,
Uses $(HTTP man7.org/linux/man-pages/man3/posix_memalign.3.html,
$(D posix_memalign)) on Posix and
$(WEB msdn.microsoft.com/en-us/library/8z34s9c6(v=vs.80).aspx,
$(HTTP msdn.microsoft.com/en-us/library/8z34s9c6(v=vs.80).aspx,
$(D __aligned_malloc)) on Windows.
*/
version(Posix)
Expand All @@ -237,8 +238,10 @@ struct AlignedMallocator
return null;

else if (code == EINVAL)
assert (0, "AlignedMallocator.alignment is not a power of two multiple of (void*).sizeof, according to posix_memalign!");

{
assert(0, "AlignedMallocator.alignment is not a power of two "
~"multiple of (void*).sizeof, according to posix_memalign!");
}
else if (code != 0)
assert (0, "posix_memalign returned an unknown code!");

Expand All @@ -256,7 +259,7 @@ struct AlignedMallocator

/**
Calls $(D free(b.ptr)) on Posix and
$(WEB msdn.microsoft.com/en-US/library/17b5h8td(v=vs.80).aspx,
$(HTTP msdn.microsoft.com/en-US/library/17b5h8td(v=vs.80).aspx,
$(D __aligned_free(b.ptr))) on Windows.
*/
version (Posix)
Expand Down Expand Up @@ -296,7 +299,7 @@ struct AlignedMallocator
/**
On Posix, uses $(D alignedAllocate) and copies data around because there is
no realloc for aligned memory. On Windows, calls
$(WEB msdn.microsoft.com/en-US/library/y69db7sx(v=vs.80).aspx,
$(HTTP msdn.microsoft.com/en-US/library/y69db7sx(v=vs.80).aspx,
$(D __aligned_realloc(b.ptr, newSize, a))).
*/
version (Windows)
Expand Down
20 changes: 12 additions & 8 deletions std/experimental/allocator/mmap_allocator.d
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
///
module std.experimental.allocator.mmap_allocator;

// MmapAllocator
/**
Allocator (currently defined only for Posix) using $(D $(LUCKY mmap)) and $(D
$(LUCKY munmap)) directly. There is no additional structure: each call to $(D
allocate(s)) issues a call to $(D mmap(null, s, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)), and each call to $(D deallocate(b)) issues
$(D munmap(b.ptr, b.length)). So $(D MmapAllocator) is usually intended for
allocating large chunks to be managed by fine-granular allocators.
Allocator (currently defined only for Posix and Windows) using $(D $(LUCKY mmap))
and $(D $(LUCKY munmap)) directly (or their Windows equivalents). There is no
additional structure: each call to $(D allocate(s)) issues a call to
$(D mmap(null, s, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)),
and each call to $(D deallocate(b)) issues $(D munmap(b.ptr, b.length)).
So $(D MmapAllocator) is usually intended for allocating large chunks to be
managed by fine-granular allocators.
*/
struct MmapAllocator
Expand All @@ -27,7 +29,8 @@ struct MmapAllocator
/// Allocator API.
void[] allocate(size_t bytes) shared
{
import core.sys.posix.sys.mman;
import core.sys.posix.sys.mman : mmap, MAP_ANON, PROT_READ,
PROT_WRITE, MAP_PRIVATE, MAP_FAILED;
if (!bytes) return null;
auto p = mmap(null, bytes, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON, -1, 0);
Expand All @@ -45,7 +48,8 @@ struct MmapAllocator
}
else version(Windows)
{
import core.sys.windows.windows;
import core.sys.windows.windows : VirtualAlloc, VirtualFree, MEM_COMMIT,
PAGE_READWRITE, MEM_RELEASE;

/// Allocator API.
void[] allocate(size_t bytes) shared
Expand Down
Loading