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