1 /*
2  * Kiss - A refined core library for D programming language.
3  *
4  * Copyright (C) 2015-2018  Shanghai Putao Technology Co., Ltd
5  *
6  * Developer: HuntLabs.cn
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11  
12 module kiss.container.ByteBuffer;
13 
14 import core.stdc.string;
15 import std.bitmanip;
16 import kiss.util.traits;
17 
18 
19 interface WriteBuffer
20 {
21 	size_t write(in ubyte[] data);
22 
23 	size_t set(size_t pos, in ubyte[] data);
24 
25 	@property size_t length() const;
26 }
27 
28 interface ReadBuffer
29 {
30 	@property bool eof() const;
31 
32 	size_t read(size_t size, scope void delegate(in ubyte[]) cback);
33 
34 	void rest(size_t size = 0);
35 
36 	size_t readPos();
37 
38 	@property size_t length() const;
39 }
40 
41 interface Buffer : WriteBuffer, ReadBuffer
42 {
43 	size_t readLine(scope void delegate(in ubyte[]) cback);
44 
45 	size_t readAll(scope void delegate(in ubyte[]) cback);
46 
47 	size_t readUtil(in ubyte[] data, scope void delegate(in ubyte[]) cback);
48 }
49 
50 
51 /**
52 */
53 final class ByteBuffer(Alloc) : Buffer
54 {
55 	import kiss.container.ByteBuffer;
56 	import kiss.container.Vector;
57 	import std.experimental.allocator.common;
58 
59 	alias BufferStore = Vector!(ubyte, Alloc);
60 
61 	static if (stateSize!(Alloc) != 0)
62 	{
63 		this(Alloc alloc)
64 		{
65 			_store = BufferStore(1024, alloc);
66 		}
67 
68 		@property allocator()
69 		{
70 			return _store.allocator;
71 		}
72 
73 	}
74 	else
75 	{
76 		this()
77 		{
78 			_store = BufferStore(1024);
79 		}
80 	}
81 
82 	~this()
83 	{
84 		destroy(_store);
85 	}
86 
87 	pragma(inline, true) void reserve(size_t elements)
88 	{
89 		_store.reserve(elements);
90 	}
91 
92 	pragma(inline, true) void clear()
93 	{
94 		_rsize = 0;
95 		_store.clear();
96 	}
97 
98 	override @property bool eof() const
99 	{
100 		return (_rsize >= _store.length);
101 	}
102 
103 	override size_t read(size_t size, scope void delegate(in ubyte[]) cback)
104 	{
105 		size_t len = _store.length - _rsize;
106 		len = size < len ? size : len;
107 		auto _data = _store.data();
108 		size = _rsize;
109 		_rsize += len;
110 		if (len > 0)
111 			cback(_data[size .. _rsize]);
112 
113 		return len;
114 	}
115 
116 	override size_t write(in ubyte[] dt)
117 	{
118 		size_t len = _store.length;
119 		_store.insertBack(cast(ubyte[]) dt);
120 		return _store.length - len;
121 	}
122 
123 	override size_t set(size_t pos, in ubyte[] data)
124 	{
125 		import core.stdc.string : memcpy;
126 
127 		if (pos >= _store.length || data.length == 0)
128 			return 0;
129 		size_t len = _store.length - pos;
130 		len = len > data.length ? data.length : len;
131 		ubyte* ptr = cast(ubyte*)(_store.ptr + pos);
132 		memcpy(ptr, data.ptr, len);
133 		return len;
134 	}
135 
136 	override void rest(size_t size = 0)
137 	{
138 		_rsize = size;
139 	}
140 
141 	override size_t readPos()
142 	{
143 		return _rsize;
144 	}
145 
146 	BufferStore allData()
147 	{
148 		return _store;
149 	}
150 
151 	override @property size_t length() const
152 	{
153 		return _store.length;
154 	}
155 
156 	override size_t readLine(scope void delegate(in ubyte[]) cback)
157 	{
158 		if (eof())
159 			return 0;
160 		auto _data = _store.data();
161 		auto tdata = _data[_rsize .. $];
162 		size_t size = _rsize;
163 		ptrdiff_t index = findCharByte(tdata, cast(ubyte) '\n');
164 		if (index < 0)
165 		{
166 			_rsize += tdata.length;
167 			cback(tdata);
168 		}
169 		else
170 		{
171 			_rsize += (index + 1);
172 			size += 1;
173 			if (index > 0)
174 			{
175 				size_t ts = index - 1;
176 				if (tdata[ts] == cast(ubyte) '\r')
177 				{
178 					index = ts;
179 				}
180 			}
181 			cback(tdata[0 .. index]);
182 		}
183 
184 		return _rsize - size;
185 	}
186 
187 	override size_t readAll(scope void delegate(in ubyte[]) cback)
188 	{
189 		if (eof())
190 			return 0;
191 		auto _data = _store.data();
192 		auto tdata = _data[_rsize .. $];
193 		_rsize = _store.length;
194 		cback(tdata);
195 		return tdata.length;
196 	}
197 
198 	override size_t readUtil(in ubyte[] chs, scope void delegate(in ubyte[]) cback)
199 	{
200 		if (eof())
201 			return 0;
202 		auto _data = _store.data();
203 		auto tdata = _data[_rsize .. $];
204 		size_t size = _rsize;
205 		ptrdiff_t index = findCharBytes(tdata, chs);
206 		if (index < 0)
207 		{
208 			_rsize += tdata.length;
209 			cback(tdata);
210 		}
211 		else
212 		{
213 			_rsize += (index + chs.length);
214 			size += chs.length;
215 			cback(tdata[0 .. index]);
216 		}
217 		return _rsize - size;
218 	}
219 
220 private:
221 	BufferStore _store;
222 	size_t _rsize = 0;
223 }
224 
225 
226 
227 ptrdiff_t findCharByte(T)(in T[] data, in T ch) if (isCharByte!(T)) {
228     if (data.length == 0)
229         return -1;
230     ptrdiff_t index = -1;
231     auto ptr = memchr(data.ptr, ch, data.length);
232     if (ptr !is null) {
233         index = cast(ptrdiff_t)((cast(T*) ptr) - data.ptr);
234     }
235 
236     return index;
237 }
238 
239 ptrdiff_t findCharBytes(T)(in T[] data, in T[] chs) if (isCharByte!(T)) {
240     if (data.length < chs.length || data.length == 0 || chs.length == 0)
241         return -1;
242     ptrdiff_t index = -1;
243     size_t rsize = 0;
244     while (rsize < data.length) {
245         auto tdata = data[rsize .. $];
246         auto ptr = memchr(tdata.ptr, chs[0], tdata.length);
247         if (ptr is null)
248             break;
249 
250         size_t fistindex = (cast(T*) ptr) - tdata.ptr;
251         if (tdata.length - fistindex < chs.length)
252             break;
253 
254         size_t i = 1;
255         size_t j = fistindex + 1;
256         while (i < chs.length && j < tdata.length) {
257             if (chs[i] != tdata[j]) {
258                 rsize += fistindex + 1;
259                 goto next;
260             }
261             ++i;
262             ++j;
263         }
264         index = cast(ptrdiff_t)(rsize + fistindex);
265         break;
266     next:
267         continue;
268     }
269     return index;
270 }
271 
272 template endianToNative(bool litte, T) {
273     static if (litte)
274         alias endianToNative = littleEndianToNative!(T, T.sizeof);
275     else
276         alias endianToNative = bigEndianToNative!(T, T.sizeof);
277 }
278 
279 template nativeToEndian(bool litte, T) {
280     static if (litte)
281         alias nativeToEndian = nativeToLittleEndian!(T);
282     else
283         alias nativeToEndian = nativeToBigEndian!(T);
284 
285 }
286 
287 unittest {
288     string hello = "hell worlf\r\nnext";
289     assert(findCharByte(hello, 'l') == 2);
290     assert(findCharBytes(hello, "worlf") == 5);
291 }