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 <vladimir@thecybershadow.net>
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 Data[] popFront(ref Data[] data, size_t amount)
58 {
59 	auto result = data.bytes[0..amount];
60 	data = data.bytes[amount..data.bytes.length];
61 	return result;
62 }
63 
64 /// Return a type that's indexable to access individual bytes,
65 /// and sliceable to get an array of Data over the specified
66 /// byte range. No actual Data concatenation is done.
67 @property
68 DataSetBytes bytes(Data[] data)
69 {
70 	return DataSetBytes(data);
71 }
72 
73 struct DataSetBytes
74 {
75 	Data[] data;
76 
77 	ubyte opIndex(size_t offset)
78 	{
79 		size_t index = 0;
80 		while (index < data.length && data[index].length <= offset)
81 		{
82 			offset -= data[index].length;
83 			index++;
84 		}
85 		return (cast(ubyte[])data[index].contents)[offset];
86 	}
87 
88 	Data[] opSlice()
89 	{
90 		return data;
91 	}
92 
93 	Data[] opSlice(size_t start, size_t end)
94 	{
95 		Data[] range = data;
96 		while (range.length && range[0].length <= start)
97 		{
98 			start -= range[0].length;
99 			end   -= range[0].length;
100 			range = range[1..$];
101 		}
102 		if (range.length==0)
103 		{
104 			assert(start==0, "Range error");
105 			return null;
106 		}
107 
108 		size_t endIndex = 0;
109 		while (endIndex < range.length && range[endIndex].length < end)
110 		{
111 			end -= range[endIndex].length;
112 			endIndex++;
113 		}
114 		range = range[0..endIndex+1];
115 		range = range.dup;
116 		range[$-1] = range[$-1][0..end];
117 		range[0  ] = range[0  ][start..range[0].length];
118 		return range;
119 	}
120 
121 	@property
122 	size_t length()
123 	{
124 		size_t result = 0;
125 		foreach (ref d; data)
126 			result += d.length;
127 		return result;
128 	}
129 
130 	size_t opDollar(size_t pos)()
131 	{
132 		static assert(pos == 0);
133 		return length;
134 	}
135 }
136 
137 unittest
138 {
139 	Data[] ds;
140 	string s;
141 
142 	ds = [
143 		Data("aaaaa"),
144 	];
145 	s = cast(string)(ds.joinToHeap);
146 	assert(s == "aaaaa");
147 	s = cast(string)(ds.bytes[].joinToHeap);
148 	assert(s == "aaaaa");
149 	s = cast(string)(ds.bytes[1..4].joinToHeap);
150 	assert(s == "aaa");
151 
152 	ds = [
153 		Data("aaaaa"),
154 		Data("bbbbb"),
155 		Data("ccccc"),
156 	];
157 	assert(ds.bytes[ 4]=='a');
158 	assert(ds.bytes[ 5]=='b');
159 	assert(ds.bytes[ 9]=='b');
160 	assert(ds.bytes[10]=='c');
161 	s = cast(string)(ds.bytes[ 3..12].joinToHeap);
162 	assert(s == "aabbbbbcc");
163 	s = cast(string)(ds.joinToHeap);
164 	assert(s == "aaaaabbbbbccccc");
165 	s = cast(string)(ds.bytes[ 0.. 6].joinToHeap);
166 	assert(s == "aaaaab");
167 	s = cast(string)(ds.bytes[ 9..15].joinToHeap);
168 	assert(s == "bccccc");
169 	s = cast(string)(ds.bytes[ 0.. 0].joinToHeap);
170 	assert(s == "");
171 	s = cast(string)(ds.bytes[15..15].joinToHeap);
172 	assert(s == "");
173 }