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;
18 
19 import ae.sys.data;
20 import ae.utils.vec;
21 
22 /// Copy a `Data` array's contents to a specified buffer.
23 void[] copyTo(R)(auto ref R data, void[] buffer)
24 if (is(ElementType!R == Data))
25 {
26 	size_t pos = 0;
27 	foreach (ref d; data)
28 	{
29 		buffer[pos .. pos + d.length] = d.contents[];
30 		pos += d.length;
31 	}
32 	assert(pos == buffer.length);
33 	return buffer;
34 }
35 
36 /// Join an array of Data to a single Data.
37 Data joinData(R)(auto ref R data)
38 if (is(ElementType!R == Data))
39 {
40 	if (data.length == 0)
41 		return Data();
42 	else
43 	if (data.length == 1)
44 		return data[0];
45 
46 	size_t size = 0;
47 	foreach (ref d; data)
48 		size += d.length;
49 	Data result = Data(size);
50 	data.copyTo(result.mcontents);
51 	return result;
52 }
53 
54 unittest
55 {
56 	assert(cast(int[])([Data([1]), Data([2])].joinData().contents) == [1, 2]);
57 }
58 
59 /// Join an array of Data to a memory block on the managed heap.
60 @property
61 void[] joinToHeap(R)(auto ref R data)
62 if (is(ElementType!R == Data))
63 {
64 	size_t size = 0;
65 	foreach (ref d; data)
66 		size += d.length;
67 	auto result = new void[size];
68 	data.copyTo(result);
69 	return result;
70 }
71 
72 unittest
73 {
74 	assert(cast(int[])([Data([1]), Data([2])].joinToHeap()) == [1, 2]);
75 }
76 
77 
78 /// A vector of `Data` with deterministic lifetime.
79 alias DataVec = Vec!Data;
80 
81 /// Remove and return the specified number of bytes from the given `Data` array.
82 DataVec shift(ref DataVec data, size_t amount)
83 {
84 	auto bytes = data.bytes;
85 	auto result = bytes[0..amount];
86 	data = bytes[amount..bytes.length];
87 	return result;
88 }
89 
90 /// Return a type that's indexable to access individual bytes,
91 /// and sliceable to get an array of `Data` over the specified
92 /// byte range. No actual `Data` concatenation is done.
93 @property DataSetBytes bytes(Data[] data) { return DataSetBytes(data); }
94 @property DataSetBytes bytes(ref DataVec data) { return DataSetBytes(data[]); } /// ditto
95 
96 /// ditto
97 struct DataSetBytes
98 {
99 	Data[] data; /// Underlying `Data[]`.
100 
101 	ubyte opIndex(size_t offset)
102 	{
103 		size_t index = 0;
104 		while (index < data.length && data[index].length <= offset)
105 		{
106 			offset -= data[index].length;
107 			index++;
108 		}
109 		return (cast(ubyte[])data[index].contents)[offset];
110 	} ///
111 
112 	DataVec opSlice()
113 	{
114 		return DataVec(data);
115 	} ///
116 
117 	DataVec opSlice(size_t start, size_t end)
118 	{
119 		auto range = DataVec(data);
120 		while (range.length && range[0].length <= start)
121 		{
122 			start -= range[0].length;
123 			end   -= range[0].length;
124 			range.popFront();
125 		}
126 		if (range.length==0)
127 		{
128 			assert(start==0, "Range error");
129 			return range;
130 		}
131 
132 		size_t endIndex = 0;
133 		while (endIndex < range.length && range[endIndex].length < end)
134 		{
135 			end -= range[endIndex].length;
136 			endIndex++;
137 		}
138 		range.length = endIndex + 1;
139 		range[$-1] = range[$-1][0..end];
140 		range[0  ] = range[0  ][start..range[0].length];
141 		return range;
142 	} ///
143 
144 	@property
145 	size_t length()
146 	{
147 		size_t result = 0;
148 		foreach (ref d; data)
149 			result += d.length;
150 		return result;
151 	} ///
152 
153 	size_t opDollar(size_t pos)()
154 	{
155 		static assert(pos == 0);
156 		return length;
157 	} ///
158 }
159 
160 unittest
161 {
162 	DataVec ds;
163 	string s;
164 
165 	ds = DataVec(
166 		Data("aaaaa"),
167 	);
168 	s = cast(string)(ds.joinToHeap);
169 	assert(s == "aaaaa");
170 	s = cast(string)(ds.bytes[].joinToHeap);
171 	assert(s == "aaaaa");
172 	s = cast(string)(ds.bytes[1..4].joinToHeap);
173 	assert(s == "aaa");
174 
175 	ds = DataVec(
176 		Data("aaaaa"),
177 		Data("bbbbb"),
178 		Data("ccccc"),
179 	);
180 	auto dsb = ds.bytes;
181 	assert(dsb.length == 15);
182 	assert(dsb.length == 15);
183 	assert(dsb[ 4]=='a');
184 	assert(dsb[ 5]=='b');
185 	assert(dsb[ 9]=='b');
186 	assert(dsb[10]=='c');
187 	s = cast(string)(dsb[ 3..12].joinToHeap);
188 	assert(s == "aabbbbbcc");
189 	s = cast(string)(ds.joinToHeap);
190 	assert(s == "aaaaabbbbbccccc", s);
191 	s = cast(string)(dsb[ 0.. 6].joinToHeap);
192 	assert(s == "aaaaab");
193 	s = cast(string)(dsb[ 9..15].joinToHeap);
194 	assert(s == "bccccc");
195 	s = cast(string)(dsb[ 0.. 0].joinToHeap);
196 	assert(s == "");
197 	s = cast(string)(dsb[15..15].joinToHeap);
198 	assert(s == "");
199 }