1 /**
2 Miscellaneous memory routines.
3 
4 Copyright: Denis Shelomovskij 2013
5 
6 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
7 
8 Authors: Denis Shelomovskij
9 */
10 module unstd.memory.misc;
11 
12 
13 import core.stdc..string;
14 
15 import unstd.array;
16 import unstd.math;
17 
18 
19 /**
20 This struct provides a static buffer of maximum size $(D maxBytes) which is
21 aligned as requested by $(D alignment).
22 
23 The whole struct may be misaligned and moved in memory.
24 */
25 @trusted struct RawAutoalignedBuff(size_t alignment, size_t maxBytes)
26 {
27 	private
28 	{
29 		version(assert) enum _debug = true, _stamp = 0x90EA7FB;
30 		else enum _debug = false;
31 
32 		void[alignUp!alignment(maxBytes + size_t.sizeof * (2 + _debug)) + alignment] _data = void;
33 	}
34 
35 	/**
36 	Initializes the struct.
37 
38 	$(D bytes) specifies buffer size.
39 
40 	If $(D zeroFill) is true the memory will be zero-filled.
41 
42 	Preconditions:
43 	$(D bytes <= maxBytes)
44 	*/
45 	void initialize(in size_t bytes = maxBytes, in bool zeroFill = true) pure nothrow @nogc
46 	in { assert(bytes <= maxBytes); }
47 	body
48 	{
49 		version(assert) *cast(size_t*) &_data[$ - size_t.sizeof * 3] = _stamp;
50 		*cast(size_t*) &_data[$ - size_t.sizeof * 2] = -1;
51 		*cast(size_t*) &_data[$ - size_t.sizeof] = bytes;
52 		if(zeroFill)
53 			memset(_data.ptr, 0, bytes + alignment - 1);
54 	}
55 
56 	/**
57 	Returnes the buffer. Moves the buffer in memory if it is misaligned.
58 
59 	Preconditions:
60 	$(D RawAutoalignedBuff) is initialized.
61 	*/
62 	@property void[] buff() pure nothrow @nogc
63 	in
64 	{
65 		version(assert) assert(*cast(size_t*) &_data[$ - size_t.sizeof * 3] == _stamp,
66 			RawAutoalignedBuff.stringof ~ " is uninitialized. You have to call `initialize` before retrieving the buffer.");
67 	}
68 	out(res)
69 	{ assert(isAligned!alignment(cast(size_t) res.ptr)); }
70 	body
71 	{
72 		void* alignedPtr = cast(void*) alignUp!alignment(cast(size_t) _data.ptr);
73 		size_t* dPtr = cast(size_t*) &_data[$ - size_t.sizeof * 2];
74 		const size_t bytes = *cast(size_t*) &_data[$ - size_t.sizeof];
75 		const size_t d = alignedPtr - _data.ptr, prevD = *dPtr;
76 		if(prevD != d)
77 		{
78 			if(prevD != -1)
79 				rawCopy(_data.ptr + prevD, alignedPtr, bytes);
80 			*dPtr = d;
81 		}
82 		return alignedPtr[0 .. bytes];
83 	}
84 
85 	alias buff this;
86 }
87 
88 ///
89 pure nothrow unittest
90 {
91 	import unstd.math;
92 
93 	alias Buff = RawAutoalignedBuff!(64, 16);
94 
95 	void[1024] sbuff = void;
96 	auto mbuff = cast(Buff*) sbuff.ptr;
97 	mbuff.initialize();
98 
99 	assert(isAligned!64(cast(size_t) mbuff.buff.ptr));
100 	assert(mbuff.buff == [0, 0, 0, 0]);
101 	mbuff.buff[] = [1, 2, 3, 4];
102 
103 	rawCopy(sbuff.ptr, sbuff.ptr + 1, Buff.sizeof);
104 	mbuff = cast(Buff*) (sbuff.ptr + 1);
105 
106 	assert(isAligned!64(cast(size_t) mbuff.buff.ptr)); 
107 	assert(mbuff.buff == [1, 2, 3, 4]);
108 }
109 
110 pure nothrow @nogc unittest
111 {
112 	import unstd.generictuple;
113 
114 	void[1024] sbuff = void;
115 
116 	foreach(alignment; expressionTuple!(1, 2, 4, 16, 64))
117 	{
118 		alias Buff = RawAutoalignedBuff!(alignment, 8);
119 		static assert(Buff.sizeof % alignment == 0);
120 
121 		foreach(i; 0 .. alignment + 1)
122 		{
123 			void* ptr = sbuff.ptr + i;
124 			auto mbuff = cast(Buff*) ptr;
125 			mbuff.initialize(8);
126 			void[] buff = mbuff.buff;
127 
128 			immutable int[2] data = [0xABCDEF01, 0x12345678];
129 			buff[] = (cast(void[]) data)[];
130 			foreach(d; 1 .. alignment + 1)
131 			{
132 				void* newPtr = ptr + d;
133 				rawCopy(ptr, newPtr, Buff.sizeof);
134 				assert((cast(Buff*) newPtr).buff == data);
135 				rawCopy(newPtr, ptr, Buff.sizeof);
136 				assert(mbuff.buff == data);
137 			}
138 		}
139 	}
140 }