1 /*******************************************************************************
2 3 Struct template which wraps a void[] with an API allowing it to be safely
4 used as an array of another type.
5 6 It is, of course, possible to simply cast a void[] to another type of array
7 and use it directly. However, care must be taken when casting to an array
8 type with a different element size. Experience has shown that this is likely
9 to hit undefined behaviour. For example, casting the array then sizing it
10 has been observed to cause segfaults, e.g.:
11 12 ---
13 void[]* void_array; // acquired from somewhere
14 15 struct S { int i; hash_t h; }
16 auto s_array = cast(S[]*)void_array;
17 s_array.length = 23;
18 ---
19 20 The exact reason for the segfaults is not known, but this usage appears to
21 lead to corruption of internal GC data (possibly type metadata associated
22 with the array's pointer).
23 24 Sizing the array first, then casting is fine, e.g.:
25 26 ---
27 void[]* void_array; // acquired from somewhere
28 29 struct S { int i; hash_t h; }
30 (*void_array).length = 23 * S.sizeof;
31 auto s_array = cast(S[])*void_array;
32 ---
33 34 The helper VoidBufferAsArrayOf simplifies this procedure and removes the
35 risk of undefined behaviour by always handling the void[] as a void[]
36 internally.
37 38 Copyright:
39 Copyright (c) 2017-2018 dunnhumby Germany GmbH.
40 All rights reserved
41 42 License:
43 Boost Software License Version 1.0. See LICENSE.txt for details.
44 45 *******************************************************************************/46 47 moduleocean.util.container.VoidBufferAsArrayOf;
48 49 importocean.meta.types.Qualifiers;
50 51 /*******************************************************************************
52 53 Struct template which wraps a void[] with an API allowing it to be safely
54 used as an array of another type.
55 56 Params:
57 T = element type of array which the API mimics
58 59 *******************************************************************************/60 61 publicstructVoidBufferAsArrayOf ( T )
62 {
63 // T == void is not only pointless but is also invalid: it's illegal to pass64 // a void argument to a function (e.g. opOpAssign!("~")).65 staticassert(!is(T == void));
66 67 /// Pointer to the underlying void buffer. Must be set before use by struct68 /// construction.69 privatevoid[]* buffer;
70 71 // The length of the buffer must always be an even multiple of T.sizeof.72 invariant ( )
73 {
74 assert(this.buffer.length % T.sizeof == 0);
75 }
76 77 /***************************************************************************
78 79 Returns:
80 a slice of this array
81 82 ***************************************************************************/83 84 publicT[] array ( )
85 {
86 returncast(T[])(*this.buffer);
87 }
88 89 /***************************************************************************
90 91 Returns:
92 the number of T elements in the array
93 94 ***************************************************************************/95 96 publicsize_tlength ( )
97 {
98 returnthis.buffer.length / T.sizeof;
99 }
100 101 /***************************************************************************
102 103 Sets the length of the array.
104 105 Params:
106 len = new length of the array, in terms of the number of T elements
107 108 ***************************************************************************/109 110 publicvoidlength ( size_tlen )
111 {
112 this.buffer.length = len * T.sizeof;
113 assumeSafeAppend(*this.buffer);
114 }
115 116 /***************************************************************************
117 118 Appends an array of elements.
119 120 Note that mutable copies of appended elements are made internally, but
121 to access them from the outside, the constness of T applies.
122 123 Params:
124 arr = elements to append
125 126 Returns:
127 a slice of this array, now with the specified elements appended
128 129 ***************************************************************************/130 131 publicT[] opOpAssign ( stringop : "~" ) ( inT[] arr )
132 {
133 returncast(T[])((*this.buffer) ~= cast(void[])arr);
134 }
135 136 /***************************************************************************
137 138 Appends an element.
139 140 Note that a mutable copy of the appended element is made internally, but
141 to access it from the outside, the constness of T applies.
142 143 Params:
144 element = element to append
145 146 Returns:
147 a slice of this array, now with the specified element appended
148 149 ***************************************************************************/150 151 publicT[] opOpAssign ( stringop : "~" ) ( inTelement )
152 {
153 returnthis.opOpAssign!("~")((&element)[0 .. 1]);
154 }
155 }
156 157 ///158 unittest159 {
160 // Backing array.161 void[] backing;
162 163 // Wrap the backing array for use as an S[].164 structS165 {
166 ubyteb;
167 hash_th;
168 }
169 170 autos_array = VoidBufferAsArrayOf!(S)(&backing);
171 172 // Append some elements.173 s_array ~= S();
174 s_array ~= [S(), S(), S()];
175 176 // Resize the array.177 s_array.length = 2;
178 179 // Iterate over the elements.180 foreach ( e; s_array.array() ) { }
181 }
182 183 version (unittest)
184 {
185 importocean.core.Test;
186 187 align ( 1 ) structS188 {
189 ubyteb;
190 hash_th;
191 }
192 }
193 194 unittest195 {
196 void[] backing;
197 198 autos_array = VoidBufferAsArrayOf!(S)(&backing);
199 200 test!("==")(s_array.length, 0);
201 test!("==")(s_array.buffer.length, 0);
202 203 s_array ~= S(0, 0);
204 test!("==")(s_array.array(), [S(0, 0)]);
205 test(s_array.length == 1);
206 test(s_array.buffer.length == S.sizeof);
207 208 s_array ~= [S(1, 1), S(2, 2), S(3, 3)];
209 test!("==")(s_array.array(), [S(0, 0), S(1, 1), S(2, 2), S(3, 3)]);
210 test(s_array.length == 4);
211 test(s_array.buffer.length == S.sizeof * 4);
212 213 s_array.length = 2;
214 test!("==")(s_array.array(), [S(0, 0), S(1, 1)]);
215 test(s_array.length == 2);
216 test(s_array.buffer.length == S.sizeof * 2);
217 }