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.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 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. opOpAssign!("~")). 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 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 public T[] opOpAssign ( string op : "~" ) ( in T[] arr ) 132 { 133 return cast(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 public T[] opOpAssign ( string op : "~" ) ( in T element ) 152 { 153 return this.opOpAssign!("~")((&element)[0 .. 1]); 154 } 155 } 156 157 /// 158 unittest 159 { 160 // Backing array. 161 void[] backing; 162 163 // Wrap the backing array for use as an S[]. 164 struct S 165 { 166 ubyte b; 167 hash_t h; 168 } 169 170 auto s_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 import ocean.core.Test; 186 187 align ( 1 ) struct S 188 { 189 ubyte b; 190 hash_t h; 191 } 192 } 193 194 unittest 195 { 196 void[] backing; 197 198 auto s_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 }