1 /**
2   This module provide support for aligned memory.
3  */
4 module gfm.core.memory;
5 
6 import std.c.stdlib : malloc, free, realloc;
7 
8 // TODO: make this module disappear when std.allocator is out.
9 
10 static if( __VERSION__ < 2066 ) private enum nogc = 1;
11 
12 /// Returns: next pointer aligned with alignment bytes.
13 @nogc void* nextAlignedPointer(void* start, size_t alignment) pure nothrow
14 {
15     return cast(void*)nextMultipleOf(cast(size_t)(start), alignment);
16 }
17 
18 /// Allocates an aligned memory chunk.
19 /// Functionally equivalent to Visual C++ _aligned_malloc.
20 @nogc void* alignedMalloc(size_t size, size_t alignment) nothrow
21 {
22     if (size == 0)
23         return null;
24 
25     void* raw = malloc(requestedSize(size, alignment));
26 
27     return storeRawPointerAndReturnAligned(raw, alignment);
28 }
29 
30 /// Frees aligned memory allocated by alignedMalloc or alignedRealloc.
31 /// Functionally equivalent to Visual C++ _aligned_free.
32 @nogc void alignedFree(void* aligned) nothrow
33 {
34     // support for free(NULL)
35     if (aligned is null)
36         return;
37 
38     void** rawLocation = cast(void**)(cast(char*)aligned - size_t.sizeof);
39     free(*rawLocation);
40 }
41 
42 /// Reallocates an aligned memory chunk allocated by alignedMalloc or alignedRealloc.
43 /// Functionally equivalent to Visual C++ _aligned_realloc.
44 @nogc void* alignedRealloc(void* aligned, size_t size, size_t alignment) nothrow
45 {
46     if (aligned is null)
47         return alignedMalloc(size, alignment);
48 
49     if (size == 0)
50     {
51         alignedFree(aligned);
52         return null;
53     }
54 
55     void* raw = *cast(void**)(cast(char*)aligned - size_t.sizeof);
56     void* newRaw = realloc(raw, requestedSize(size, alignment));
57 
58     // if newRaw is raw, nothing to do
59     if (raw is newRaw)
60         return aligned;
61 
62     // else write raw at the new location
63     return storeRawPointerAndReturnAligned(newRaw, alignment);
64 }
65 
66 private
67 {
68     // Returns number of bytes to actually allocate when asking
69     // for a particular alignement
70     @nogc size_t requestedSize(size_t askedSize, size_t alignment) pure nothrow
71     {
72         enum size_t pointerSize = size_t.sizeof;
73         return askedSize + alignment - 1 + pointerSize;
74     }
75 
76     @nogc void* storeRawPointerAndReturnAligned(void* raw, size_t alignment) nothrow
77     {
78         enum size_t pointerSize = size_t.sizeof;
79         char* start = cast(char*)raw + pointerSize;
80         void* aligned = nextAlignedPointer(start, alignment);
81         void** rawLocation = cast(void**)(cast(char*)aligned - pointerSize);
82         *rawLocation = raw;
83         return aligned;
84     }
85 
86     // Returns: x, multiple of powerOfTwo, so that x >= n.
87     @nogc size_t nextMultipleOf(size_t n, size_t powerOfTwo) pure nothrow
88     {
89         // check power-of-two
90         assert( (powerOfTwo != 0) && ((powerOfTwo & (powerOfTwo - 1)) == 0));
91 
92         size_t mask = ~(powerOfTwo - 1);
93         return (n + powerOfTwo - 1) & mask;
94     }
95 }
96 
97 unittest
98 {
99     {
100         void* p = alignedMalloc(23, 16);
101         assert(p !is null);
102         assert(((cast(size_t)p) & 0xf) == 0);
103 
104         alignedFree(p);
105     }
106 
107     assert(alignedMalloc(0, 16) == null);
108     alignedFree(null);
109 
110     {
111         void* p = alignedRealloc(null, 100, 16);
112         p = alignedRealloc(p, 200, 16);
113         p = alignedRealloc(p, 0, 16);
114     }
115 }