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