1 module more.array; 2 3 import std.format : format; 4 5 template isArrayLike(T) 6 { 7 enum isArrayLike = 8 is(typeof(T.init.length)) 9 && is(typeof(T.init.ptr)) 10 && is(typeof(T.init[0])); 11 } 12 13 /** 14 TODO move this to mored 15 */ 16 pragma(inline) void copyFrom(T,U)(T dst, U src) 17 if (isArrayLike!T && isArrayLike!U && dst[0].sizeof == src[0].sizeof) 18 { 19 assert(dst.length >= src.length, "copyFrom source length larger than destination"); 20 import core.stdc..string : memcpy; 21 memcpy(dst.ptr, src.ptr, src.length); 22 } 23 24 pragma(inline) void setBytes(T,U)(T dst, U value) 25 if (isArrayLike!T && T.init[0].sizeof == 1 && value.sizeof == 1) 26 { 27 import core.stdc..string : memset; 28 memset(dst.ptr, cast(int)value, dst.length); 29 } 30 31 32 /** 33 TODO: move this to the mored repository 34 35 A LimitArray is like an array, except it contains 2 pointers, "ptr" and "limit", 36 instead of a "ptr" and "length". 37 38 The first pointer, "ptr", points to the beginning (like a normal array) and the 39 second pointer, "limit", points to 1 element past the last element in the array. 40 41 ``` 42 ------------------------------- 43 | first | second | ... | last | 44 ------------------------------- 45 ^ ^ 46 ptr limit 47 ```` 48 49 To get the length of the LimitArray, you can evaluate `limit - ptr`. 50 To check if a LimitArray is empty, you can check if `ptr == limit`. 51 52 The reason for the existense of the LimitArray structure is that some functionality 53 is more efficient when it uses this representation. A common example is when processing 54 or parsing an array of elements where the beginning is iteratively "sliced off" as 55 it is being processed, i.e. array = array[someCount .. $]; This operation is more efficient 56 when using a LimitArray because only the "ptr" field needs to be modified whereas a normal array 57 needs to modify the "ptr" field and the "length" each time. Note that other operations are more 58 efficiently done using a normal array, for example, if the length needs to be evaluated quite 59 often then it might make more sense to use a normal array. 60 61 In order to support "Element Type Modifiers" on a LimitArray's pointer types, the types are 62 defined using a template. Here is a table of LimitArray types with their equivalent normal array types. 63 64 | Normal Array | Limit Array | 65 |---------------------|-------------------------| 66 | `char[]` | `LimitArray!char.mutable` `LimitArray!(const(char)).mutable` `LimitArray!(immutable(char)).mutable` | 67 | `const(char)[]` | `LimitArray!char.const` `LimitArray!(const(char)).const` `LimitArray!(immutable(char)).const` | 68 | `immutable(char)[]` | `LimitArray!char.immutable` `LimitArray!(const(char)).immutable` `LimitArray!(immutable(char)).immutable` | 69 70 */ 71 template LimitArray(T) 72 { 73 static if( !is(T == Unqual!T) ) 74 { 75 alias LimitArray = LimitArray!(Unqual!T); 76 } 77 else 78 { 79 enum CommonMixin = q{ 80 pragma(inline) @property auto asArray() 81 { 82 return this.ptr[0 .. limit - ptr]; 83 } 84 pragma(inline) auto slice(size_t offset) 85 { 86 auto newPtr = ptr + offset; 87 assert(newPtr <= limit, "slice offset range violation"); 88 return typeof(this)(newPtr, limit); 89 } 90 pragma(inline) auto slice(size_t offset, size_t newLimit) 91 in { assert(newLimit >= offset, "slice offset range violation"); } do 92 { 93 auto newLimitPtr = ptr + newLimit; 94 assert(newLimitPtr <= limit, "slice limit range violation"); 95 return typeof(this)(ptr + offset, ptr + newLimit); 96 } 97 pragma(inline) auto ptrSlice(typeof(this.ptr) ptr) 98 { 99 auto copy = this; 100 copy.ptr = ptr; 101 return copy; 102 } 103 }; 104 105 struct mutable 106 { 107 union 108 { 109 struct 110 { 111 T* ptr; 112 T* limit; 113 } 114 const_ constVersion; 115 } 116 // mutable is implicitly convertible to const 117 alias constVersion this; 118 119 mixin(CommonMixin); 120 } 121 struct immutable_ 122 { 123 union 124 { 125 struct 126 { 127 immutable(T)* ptr; 128 immutable(T)* limit; 129 } 130 const_ constVersion; 131 } 132 // immutable is implicitly convertible to const 133 alias constVersion this; 134 135 mixin(CommonMixin); 136 } 137 struct const_ 138 { 139 const(T)* ptr; 140 const(T)* limit; 141 mixin(CommonMixin); 142 auto startsWith(const(T)[] check) const 143 { 144 return ptr + check.length <= limit && 145 0 == memcmp(ptr, check.ptr, check.length); 146 } 147 auto equals(const(T)[] check) const 148 { 149 return ptr + check.length == limit && 150 0 == memcmp(ptr, check.ptr, check.length); 151 } 152 } 153 } 154 } 155 156 pragma(inline) 157 @property auto asLimitArray(T)(T[] array) 158 { 159 static if( is(T == immutable) ) 160 { 161 return LimitArray!T.immutable_(array.ptr, array.ptr + array.length); 162 } 163 else static if( is(T == const) ) 164 { 165 return LimitArray!T.const_(array.ptr, array.ptr + array.length); 166 } 167 else 168 { 169 return LimitArray!T.mutable(array.ptr, array.ptr + array.length); 170 } 171 } 172 173 /** 174 TODO: move this to the mored repo 175 176 An array type that uses a custom type for the length. 177 */ 178 struct LengthArray(T, SizeType) 179 { 180 @property static typeof(this) nullValue() { return typeof(this)(null, 0); } 181 182 T* ptr; 183 SizeType length; 184 185 void nullify() { this.ptr = null; this.length = 0; } 186 bool isNull() const { return ptr is null; } 187 188 pragma(inline) @property auto ref last() const 189 in { assert(length > 0); } do { return ptr[length - 1]; } 190 191 pragma(inline) auto ref opIndex(SizeType index) inout 192 in { assert(index < length, format("range violation %s >= %s", index, length)); } do 193 { 194 return ptr[index]; 195 } 196 static if (size_t.sizeof != SizeType.sizeof) 197 { 198 pragma(inline) auto ref opIndex(size_t index) inout 199 in { assert(index < length, format("range violation %s >= %s", index, length)); } do 200 { 201 return ptr[index]; 202 } 203 } 204 pragma(inline) SizeType opDollar() const 205 { 206 return length; 207 } 208 /* 209 pragma(inline) auto ref opSlice(SizeType start, SizeType limit) inout 210 in { assert(limit >= start, "slice range violation"); } do 211 { 212 return inout LengthArray!(T,SizeType)(ptr + start, cast(SizeType)(limit - start)); 213 } 214 */ 215 pragma(inline) auto ref opSlice(SizeType start, SizeType limit) 216 in { assert(limit >= start, "slice range violation"); } do 217 { 218 return LengthArray!(T,SizeType)(ptr + start, cast(SizeType)(limit - start)); 219 } 220 221 pragma(inline) int opApply(scope int delegate(ref T element) dg) const 222 { 223 int result = 0; 224 for (SizeType i = 0; i < length; i++) 225 { 226 result = dg(*cast(T*)&ptr[i]); 227 if (result) 228 break; 229 } 230 return result; 231 } 232 pragma(inline) int opApply(scope int delegate(SizeType index, ref T element) dg) const 233 { 234 int result = 0; 235 for (SizeType i = 0; i < length; i++) 236 { 237 result = dg(i, *cast(T*)&ptr[i]); 238 if (result) 239 break; 240 } 241 return result; 242 } 243 244 @property auto asArray() { return ptr[0..length]; } 245 //alias toArray this; 246 247 /* 248 // range functions 249 @property bool empty() { return length == 0; } 250 @property auto front() { return *ptr; } 251 void popFront() { 252 ptr++; 253 length--; 254 } 255 */ 256 } 257 pragma(inline) LengthArray!(T, LengthType) asLengthArray(LengthType, T)(T[] array) 258 in { 259 static if (LengthType.sizeof < array.length.sizeof) 260 { 261 assert(array.length <= LengthType.max, 262 format("array length %s exceeded " ~ LengthType.stringof ~ ".max %s", array.length, LengthType.max)); 263 } 264 } do 265 { 266 return LengthArray!(T, LengthType)(array.ptr, cast(LengthType)array.length); 267 } 268 269 pragma(inline) LengthArray!(T, LengthType) asLengthArray(LengthType, T)(T* ptr, LengthType length) 270 { 271 return LengthArray!(T, LengthType)(ptr, length); 272 } 273 274