1 /** 2 * ae.sys.dataset 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <ae@cy.md> 12 */ 13 14 module ae.sys.dataset; 15 16 import std.algorithm.mutation : move; 17 import std.range.primitives : ElementType, front; 18 19 import ae.sys.data; 20 import ae.utils.array : asBytes, as; 21 import ae.utils.vec; 22 23 /// Copy a `Data` array's contents to a specified buffer. 24 T[] copyTo(R, T)(auto ref R data, T[] buffer) 25 if (is(ElementType!R == TData!T)) 26 { 27 size_t pos = 0; 28 foreach (ref d; data) 29 { 30 d.enter((scope contents) { 31 buffer[pos .. pos + contents.length] = contents[]; 32 pos += contents.length; 33 }); 34 } 35 assert(pos == buffer.length); 36 return buffer; 37 } 38 39 deprecated void[] copyTo(R)(auto ref R data, void[] buffer) 40 if (is(ElementType!R == Data)) 41 { 42 return data.copyTo(cast(ubyte[])buffer); 43 } 44 45 /// Join an array of Data to a single Data. 46 TData!(DataElementType!(ElementType!R)) joinData(R)(auto ref R data) 47 if (is(ElementType!R == TData!T, T)) 48 { 49 alias T = DataElementType!(ElementType!R); 50 51 if (data.length == 0) 52 return TData!T(); 53 else 54 if (data.length == 1) 55 return data[0]; 56 57 size_t size = 0; 58 foreach (ref d; data) 59 size += d.length; 60 TData!T result = TData!T(size); 61 result.enter((scope T[] contents) { 62 data.copyTo(contents); 63 }); 64 return result; 65 } 66 67 unittest 68 { 69 assert(([TData!int([1]), TData!int([2])].joinData().unsafeContents) == [1, 2]); 70 assert(cast(int[])([Data([1].asBytes), Data([2].asBytes)].joinData().unsafeContents) == [1, 2]); 71 } 72 73 /// Join an array of Data to a memory block on the managed heap. 74 DataElementType!(ElementType!R)[] joinToGC(R)(auto ref R data) 75 if (is(ElementType!R == TData!T, T)) 76 { 77 size_t size = 0; 78 foreach (ref d; data) 79 size += d.length; 80 auto result = new DataElementType!(ElementType!R)[size]; 81 data.copyTo(result); 82 return result; 83 } 84 85 unittest 86 { 87 assert(([TData!int([1]), TData!int([2])].joinToGC()) == [1, 2]); 88 assert(cast(int[])([Data([1].asBytes), Data([2].asBytes)].joinToGC()) == [1, 2]); 89 } 90 91 deprecated @property void[] joinToHeap(R)(auto ref R data) 92 if (is(ElementType!R == Data)) 93 { return data.joinToGC(); } 94 95 deprecated unittest 96 { 97 assert(cast(int[])([Data([1].asBytes), Data([2].asBytes)].joinToHeap()) == [1, 2]); 98 } 99 100 /// A vector of `Data` with deterministic lifetime. 101 alias DataVec = Vec!Data; 102 103 /// Remove and return the specified number of bytes from the given `Data` array. 104 DataVec shift(ref DataVec data, size_t amount) 105 { 106 auto bytes = data.bytes; 107 auto result = bytes[0..amount]; 108 data = bytes[amount..bytes.length]; 109 return result; 110 } 111 112 /// Return a type that's indexable to access individual bytes, 113 /// and sliceable to get an array of `Data` over the specified 114 /// byte range. No actual `Data` concatenation is done. 115 @property DataSetBytes bytes(Data[] data) { return DataSetBytes(data); } 116 @property DataSetBytes bytes(ref DataVec data) { return DataSetBytes(data[]); } /// ditto 117 118 /// ditto 119 struct DataSetBytes 120 { 121 Data[] data; /// Underlying `Data[]`. 122 123 ubyte opIndex(size_t offset) 124 { 125 size_t index = 0; 126 while (index < data.length && data[index].length <= offset) 127 { 128 offset -= data[index].length; 129 index++; 130 } 131 return data[index].asDataOf!ubyte[offset]; 132 } /// 133 134 DataVec opSlice() 135 { 136 return DataVec(data); 137 } /// 138 139 DataVec opSlice(size_t start, size_t end) 140 { 141 auto range = DataVec(data); 142 while (range.length && range[0].length <= start) 143 { 144 start -= range[0].length; 145 end -= range[0].length; 146 range.popFront(); 147 } 148 if (range.length==0) 149 { 150 assert(start==0, "Range error"); 151 return range; 152 } 153 154 size_t endIndex = 0; 155 while (endIndex < range.length && range[endIndex].length < end) 156 { 157 end -= range[endIndex].length; 158 endIndex++; 159 } 160 range.length = endIndex + 1; 161 range[$-1] = range[$-1][0..end]; 162 range[0 ] = range[0 ][start..range[0].length]; 163 return range; 164 } /// 165 166 @property 167 size_t length() 168 { 169 size_t result = 0; 170 foreach (ref d; data) 171 result += d.length; 172 return result; 173 } /// 174 175 size_t opDollar(size_t pos)() 176 { 177 static assert(pos == 0); 178 return length; 179 } /// 180 } 181 182 unittest 183 { 184 DataVec ds; 185 string s; 186 187 ds = DataVec( 188 Data("aaaaa".asBytes), 189 ); 190 s = ds.joinToGC().as!string; 191 assert(s == "aaaaa"); 192 s = ds.bytes[].joinToGC().as!string; 193 assert(s == "aaaaa"); 194 s = ds.bytes[1..4].joinToGC().as!string; 195 assert(s == "aaa"); 196 197 ds = DataVec( 198 Data("aaaaa".asBytes), 199 Data("bbbbb".asBytes), 200 Data("ccccc".asBytes), 201 ); 202 auto dsb = ds.bytes; 203 assert(dsb.length == 15); 204 assert(dsb.length == 15); 205 assert(dsb[ 4]=='a'); 206 assert(dsb[ 5]=='b'); 207 assert(dsb[ 9]=='b'); 208 assert(dsb[10]=='c'); 209 s = dsb[ 3..12].joinToGC().as!string; 210 assert(s == "aabbbbbcc"); 211 s = ds.joinToGC().as!string; 212 assert(s == "aaaaabbbbbccccc", s); 213 s = dsb[ 0.. 6].joinToGC().as!string; 214 assert(s == "aaaaab"); 215 s = dsb[ 9..15].joinToGC().as!string; 216 assert(s == "bccccc"); 217 s = dsb[ 0.. 0].joinToGC().as!string; 218 assert(s == ""); 219 s = dsb[15..15].joinToGC().as!string; 220 assert(s == ""); 221 }