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 module ocean.util.container.VoidBufferAsArrayOf; 48 49 import ocean.transition; 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 public struct VoidBufferAsArrayOf ( T ) 62 { 63 // T == void is not only pointless but is also invalid: it's illegal to pass 64 // a void argument to a function (e.g. opCatAssign). 65 static assert(!is(T == void)); 66 67 /// Pointer to the underlying void buffer. Must be set before use by struct 68 /// construction. 69 private void[]* 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 public T[] array ( ) 85 { 86 return cast(T[])(*(&this).buffer); 87 } 88 89 /*************************************************************************** 90 91 Returns: 92 the number of T elements in the array 93 94 ***************************************************************************/ 95 96 public size_t length ( ) 97 { 98 return (&this).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 public void length ( size_t len ) 111 { 112 (&this).buffer.length = len * T.sizeof; 113 enableStomping(*(&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 but 124 are inaccessible from the outside. 125 126 Params: 127 arr = elements to append 128 129 Returns: 130 a slice of this array, now with the specified elements appended 131 132 ***************************************************************************/ 133 134 public T[] opCatAssign ( in T[] arr ) 135 { 136 return cast(T[])((*(&this).buffer) ~= cast(void[])arr); 137 } 138 139 /*************************************************************************** 140 141 Appends an element. 142 143 Note that a mutable copy of the appended element is made internally, but 144 to access it from the outside, the constness of T applies. 145 146 Params: 147 element = element to append 148 149 Returns: 150 a slice of this array, now with the specified element appended 151 152 ***************************************************************************/ 153 154 public T[] opCatAssign ( in T element ) 155 { 156 return (&this).opCatAssign((&element)[0 .. 1]); 157 } 158 } 159 160 /// 161 unittest 162 { 163 // Backing array. 164 void[] backing; 165 166 // Wrap the backing array for use as an S[]. 167 struct S 168 { 169 ubyte b; 170 hash_t h; 171 } 172 173 auto s_array = VoidBufferAsArrayOf!(S)(&backing); 174 175 // Append some elements. 176 s_array ~= S(); 177 s_array ~= [S(), S(), S()]; 178 179 // Resize the array. 180 s_array.length = 2; 181 182 // Iterate over the elements. 183 foreach ( e; s_array.array() ) { } 184 } 185 186 version ( UnitTest ) 187 { 188 import ocean.core.Test; 189 190 align ( 1 ) struct S 191 { 192 ubyte b; 193 hash_t h; 194 } 195 } 196 197 unittest 198 { 199 void[] backing; 200 201 auto s_array = VoidBufferAsArrayOf!(S)(&backing); 202 203 test!("==")(s_array.length, 0); 204 test!("==")(s_array.buffer.length, 0); 205 206 s_array ~= S(0, 0); 207 test!("==")(s_array.array(), [S(0, 0)]); 208 test(s_array.length == 1); 209 test(s_array.buffer.length == S.sizeof); 210 211 s_array ~= [S(1, 1), S(2, 2), S(3, 3)]; 212 test!("==")(s_array.array(), [S(0, 0), S(1, 1), S(2, 2), S(3, 3)]); 213 test(s_array.length == 4); 214 test(s_array.buffer.length == S.sizeof * 4); 215 216 s_array.length = 2; 217 test!("==")(s_array.array(), [S(0, 0), S(1, 1)]); 218 test(s_array.length == 2); 219 test(s_array.buffer.length == S.sizeof * 2); 220 }