1 /**
2  * Buffers
3  * 
4  * Copyright:
5  * (C) 2012-2015 Tatsuhiro Tsujikawa
6  * (C) 2014-2015 Etienne Cimon
7  *
8  * License: 
9  * Distributed under the terms of the MIT license with an additional section 1.2 of the curl/libcurl project. 
10  * Consult the provided LICENSE.md file for details
11  */
12 module libhttp2.buffers;
13 
14 import libhttp2.types;
15 import std.algorithm : max, min;
16 import core.exception : onOutOfMemoryError;
17 import std.c.string : memcpy;
18 import memutils.utils;
19 
20 struct Buffer
21 {
22 	/// This points to the beginning of the buffer. The effective range of buffer is [begin, end).
23 	ubyte* begin;
24 	/// This points to the memory one byte beyond the end of the buffer.
25 	ubyte* end;
26 	/// The position indicator for effective start of the buffer. pos <= last must be hold.
27 	ubyte* pos;
28 	/// The position indicator for effective one beyond of the end of the buffer. last <= end must be hold.
29 	ubyte* last;
30 	/// Mark arbitrary position in buffer [begin, end)
31 	ubyte* mark;
32 	/// true if the allocator must be secure
33 	bool use_secure_mem;
34 	/// true if the memory much be manually freed. use_secure_mem can be false if this is true
35 	bool zeroize_on_free;
36 
37 	ubyte[] opSlice() { return pos[0 .. length]; }
38 
39 	@property int length() {
40 		assert(last-pos >= 0);
41 		return cast(int)(last - pos);
42 	}
43 
44 	@property int available() {
45 		return cast(int)(end - last);
46 	}
47 
48 	@property int markAvailable() {
49 		return cast(int)(mark - last);
50 	}
51 
52 	@property int capacity() {
53 		return cast(int)(end - begin);
54 	}
55 
56 	@property int posOffset() {
57 		return cast(int)(pos - begin);
58 	}
59 	@property int offset() {
60 		return cast(int) (last - begin);
61 	}
62 
63 	void shiftRight(size_t amount) {
64 		pos += amount;
65 		last += amount;
66 	}
67 
68 	void shiftLeft(size_t amount) {
69 		pos -= amount;
70 		last -= amount;
71 	}
72 
73 	void free()
74 	{
75 		if (!begin) return;
76 		static if (__VERSION__ >= 2067)
77 		{
78 			if (use_secure_mem) SecureMem.free(begin[0 .. end - begin]);
79 			else 
80 			{
81 				if (zeroize_on_free)
82 				{
83 					import std.c.string : memset;
84 					memset(begin, 0, end-begin);
85 				}
86 				Mem.free(begin[0 .. end - begin]);
87 			}
88 		}
89 		else {
90 			if (zeroize_on_free || use_secure_mem)
91 			{
92 				import std.c.string : memset;
93 				memset(begin, 0, end-begin);
94 			}
95 			Mem.free(begin[0 .. end - begin]);
96 		}
97 		begin = null;
98 	}
99 	
100 	/*
101 	 * Extends buffer so that capacity() returns at least
102 	 * |new_cap|. If extensions took place, buffer pointers in |buf| will
103 	 * change.
104 	 */
105 	void reserve(size_t new_cap)
106 	{
107 		if (!new_cap) return;
108 
109 		ubyte[] new_buf;
110 		size_t cap;
111 		
112 		cap = capacity;
113 		
114 		if (cap >= new_cap) {
115 			return;
116 		}
117 		
118 		new_cap = max(new_cap, cap * 2);
119 
120 		static if (__VERSION__ >= 2067)
121 		{
122 			if (use_secure_mem) {
123 				if (begin)
124 					new_buf = SecureMem.realloc(begin[0 .. end - begin], new_cap);
125 				else
126 					new_buf = SecureMem.alloc!(ubyte[])(new_cap);
127 			}
128 			else {
129 				if (begin) {
130 					new_buf = Mem.realloc(begin[0 .. end - begin], new_cap);
131 					if (zeroize_on_free && (new_buf.ptr > end || new_buf.ptr + new_cap < begin))
132 					{
133 						import std.c.string : memset;
134 						memset(begin, 0, end - begin);
135 					}
136 				}
137 				else
138 					new_buf = Mem.alloc!(ubyte[])(new_cap);
139 			}
140 		}
141 		else {
142 			if (begin) {
143 				new_buf = Mem.realloc(begin[0 .. end - begin], new_cap);
144 				if ((zeroize_on_free || use_secure_mem) && (new_buf.ptr > end || new_buf.ptr + new_cap < begin))
145 				{
146 					import std.c.string : memset;
147 					memset(begin, 0, end - begin);
148 				}
149 			}
150 			else
151 				new_buf = Mem.alloc!(ubyte[])(new_cap);
152 		}
153 		ubyte* ptr = new_buf.ptr;
154 		
155 		pos = ptr + (pos - begin);
156 		last = ptr + (last - begin);
157 		mark = ptr + (mark - begin);
158 		begin = ptr;
159 		end = ptr + new_cap;
160 		
161 		return;
162 	}
163 	
164 	/// Resets pos, last, mark member of |buf| to buf.begin.
165 	void reset()
166 	{
167 		pos = last = mark = begin;
168 	}
169 	
170 	/*
171 	 * Initializes Buffer using supplied buffer |buf|. Semantically, 
172 	 * the application should not call *_reserve() or free() functions.
173 	 */
174 	this(ubyte[] buf) {
175 		begin = pos = last = mark = buf.ptr;
176 		end = begin + buf.length;
177 	}		
178 	
179 	/*
180 	 * Initializes the |buf| and allocates at least |initial| bytes of
181 	 * memory.
182 	 */
183 	this(size_t initial = 0) {
184 		begin = null;
185 		end = null;
186 		pos = null;
187 		last = null;
188 		mark = null;
189 		reserve(initial);
190 	}
191 
192 }
193 
194 class Buffers {
195 
196 	class Chain {
197 		Buffer buf;
198 		Chain next;
199 		static Chain opCall(size_t chunk_length = 0, bool use_secure_mem = false, bool zeroize_on_free = false)
200 		{
201 			Chain chain;
202 			chain = Mem.alloc!(Buffers.Chain)();
203 			scope(failure) Mem.free(chain);
204 			chain.next = null;
205 			chain.buf = Buffer(chunk_length);
206 			chain.buf.use_secure_mem = use_secure_mem;
207 			chain.buf.zeroize_on_free = zeroize_on_free;
208 			return chain;
209 		}
210 
211 		void free() 
212 		{
213 			buf.free();
214 		}
215 	}
216 
217 	/// Points to the first buffer
218 	Chain head;
219 	/// Buffer pointer where write occurs.
220 	Chain cur;
221 	/// The buffer capacity of each Buffer
222 	size_t chunk_length;
223 	/// The maximum number of `Chain`s
224 	size_t max_chunk;
225 	/// The number of `Chain`s allocated
226 	size_t chunk_used;
227 	/// The number of `Chain`s to keep on reset
228 	size_t chunk_keep;
229 	/// pos offset from begin in each buffers. On initialization and reset, buf.pos and buf.last are positioned at buf.begin + offset.
230 	size_t offset;
231 
232 	/// true if the buffers were initialized with a pre-allocated ubyte[] which mustn't be freed
233 	bool dont_free;
234 	/// true if we should secure the Buffer allocations
235 	bool use_secure_mem;
236 	/// true if the Buffer allocations should be zeroized
237 	bool zeroize_on_free;
238 
239 	/// This is the same as calling init2 with the given arguments and offset = 0.
240 	this(size_t _chunk_length, size_t _max_chunk, bool _use_secure_mem = false, bool _zeroize_on_free = false) {
241 		this(_chunk_length, _max_chunk, _max_chunk, 0, _use_secure_mem, _zeroize_on_free);
242 	}
243 
244 	/// This is the same as calling init3 with the given arguments and chunk_keep = max_chunk.
245 	this(size_t _chunk_length, size_t _max_chunk, size_t _offset, bool _use_secure_mem = false, bool _zeroize_on_free = false)
246 	{
247 		this(_chunk_length, _max_chunk, _max_chunk, _offset, _use_secure_mem, _zeroize_on_free);
248 	}
249 
250 	/**
251 	 * Initializes Buffers. Each buffer size is given in the
252 	 * |chunk_length|.  The maximum number of buffers is given in the
253 	 * |max_chunk|.  On reset, first |chunk_keep| buffers are kept and
254 	 * remaining buffers are deleted.  Each buffer will have bufs.pos and
255 	 * bufs.last shifted to left by |offset| bytes on creation and reset.
256 	 *
257 	 * This function allocates first buffer.  bufs.head and bufs.cur
258 	 * will point to the first buffer after this call.
259 	 */
260 	this(size_t _chunk_length, size_t _max_chunk, size_t _chunk_keep, size_t _offset, bool _use_secure_mem = false, bool _zeroize_on_free = false) 
261 	in { assert(!(_chunk_keep == 0 || _max_chunk < _chunk_keep || _chunk_length < _offset), "Invalid Arguments"); }
262 	body
263 	{
264 		Chain chain = Chain(_chunk_length, _use_secure_mem, _zeroize_on_free);
265 
266 		offset = _offset;
267 		
268 		head = chain;
269 		cur = head;
270 		
271 		cur.buf.shiftRight(offset);
272 		
273 		chunk_length = _chunk_length;
274 		chunk_used = 1;
275 		max_chunk = _max_chunk;
276 		chunk_keep = _chunk_keep;
277 	}
278 
279 	/*
280 	 * Initializes Buffers using supplied buffer |buf|.
281 	 * The first buffer bufs.head uses buffer |begin|.  The buffer size
282 	 * is fixed and no allocate extra chunk buffer is allocated.  In other
283 	 * words, max_chunk = chunk_keep = 1. 
284 	 */
285 	this(ubyte[] buf)
286 	{
287 		Chain chain = Chain();
288 
289 		chain.next = null;		
290 		chain.buf = Buffer(buf);
291 
292 		dont_free = true;
293 		offset = 0;		
294 		head = chain;
295 		cur = head;		
296 		chunk_length = buf.length;
297 		chunk_used = 1;
298 		max_chunk = 1;
299 		chunk_keep = 1;
300 	}
301 
302 	/// Frees any related resources
303 	void free()
304 	{
305 		Chain chain;
306 		Chain next_chain;
307 		
308 		if (!head) return;
309 
310 		if (dont_free) {
311 			Mem.free(head);
312 			head = null;
313 			return;
314 		}
315 		
316 		for (chain = head; chain;) {
317 			next_chain = chain.next;			
318 			chain.free();
319 			Mem.free(chain);
320 			chain = next_chain;
321 		}
322 		
323 		head = null;
324 	}	
325 
326 	/*
327 	 * Reallocates internal buffer using |chunk_length|.  The max_chunk,
328 	 * chunk_keep and offset do not change.  After successful allocation
329 	 * of new buffer, previous buffers are deallocated without copying
330 	 * anything into new buffers.  chunk_used is reset to 1.
331 	 *
332 	 * This function returns 0 if it succeeds, or one of the following
333 	 * negative error codes:
334 	 *
335 	 * ErrorCode.NOMEM
336 	 *     Out of memory.
337 	 * ErrorCode.INVALID_ARGUMENT
338 	 *     chunk_length < offset
339 	 */
340 	void realloc(size_t _chunk_length)
341 	in { assert(_chunk_length >= offset, "Invalid Arguments"); }
342 	body {
343 		Chain chain = Chain(_chunk_length, use_secure_mem, zeroize_on_free);
344 		
345 		free();
346 		
347 		head = chain;
348 		cur = head;
349 		
350 		cur.buf.shiftRight(offset);
351 		
352 		chunk_length = _chunk_length;
353 		chunk_used = 1;
354 	}
355 
356 	/*
357 	 * Appends the |data| of length |len| to the |bufs|. The write starts
358 	 * at bufs.cur.buf.last. A new buffers will be allocated to store
359 	 * all data.
360 	 *
361 	 * This function returns 0 if it succeeds, or one of the following
362 	 * negative error codes:
363 	 *
364 	 * ErrorCode.NOMEM
365 	 *     Out of memory.
366 	 * ErrorCode.BUFFER_ERROR
367 	 *     Out of buffer space.
368 	 */
369 	ErrorCode add(in string data)
370 	{
371 		ErrorCode rv;
372 		size_t nwrite;
373 		Buffer* buf;
374 		const(ubyte)* p;
375 		int len = cast(int)data.length;
376 		if (available < len) {
377 			return ErrorCode.BUFFER_ERROR;
378 		}
379 		
380 		p = cast(const(ubyte)*)data.ptr;
381 		
382 		while (len > 0) {
383 			buf = &cur.buf;
384 			
385 			nwrite = cast(size_t) min(buf.available, len);
386 			if (nwrite == 0) {
387 				rv = allocChain();
388 				if (rv != 0) {
389 					return rv;
390 				}
391 				continue;
392 			}
393 			memcpy(buf.last, p, nwrite);
394 			buf.last += nwrite;
395 			p += len;
396 			len -= nwrite;
397 		}
398 		
399 		return ErrorCode.OK;
400 	}
401 
402 	/*
403 	 * Appends a single byte |b| to the Buffers. The write starts at
404 	 * cur.buf.last. A new buffers will be allocated to store all
405 	 * data.
406 	 *
407 	 * This function returns 0 if it succeeds, or one of the following
408 	 * negative error codes:
409 	 *
410 	 * ErrorCode.BUFFER_ERROR
411 	 *     Out of buffer space.
412 	 */
413 	ErrorCode add(ubyte b)
414 	{
415 		ErrorCode rv;
416 		
417 		rv = ensureAddByte();
418 		if (rv != 0) 
419 			return rv;
420 
421 		
422 		*cur.buf.last++ = b;
423 		
424 		return ErrorCode.OK;
425 	}
426 
427 	/*
428 	 * Behaves like addb(), but this does not update
429 	 * buf.last pointer.
430 	 */
431 	ErrorCode addHold(ubyte b)
432 	{
433 		ErrorCode rv;
434 		
435 		rv = ensureAddByte();
436 		if (rv != 0) {
437 			return rv;
438 		}
439 		
440 		*cur.buf.last = b;
441 		
442 		return ErrorCode.OK;
443 	}
444 
445 	void fastAdd(ubyte b) {
446 		assert(cur.buf.last+1 <= cur.buf.end);
447 		*cur.buf.last++ = b;
448 	}
449 
450 	void fastAddHold(ubyte b) {
451 		*cur.buf.last = b;
452 	}
453 		
454 	/*
455 	 * Performs bitwise-OR of |b| at cur.buf.last. A new buffers
456 	 * will be allocated if necessary.
457 	 *
458 	 * This function returns 0 if it succeeds, or one of the following
459 	 * negative error codes:
460 	 *
461 	 * ErrorCode.NOMEM
462 	 *     Out of memory.
463 	 * ErrorCode.BUFFER_ERROR
464 	 *     Out of buffer space.
465 	 */
466 	ErrorCode or(ubyte b)
467 	{
468 		ErrorCode rv;
469 		
470 		rv = ensureAddByte();
471 		if (rv != 0) {
472 			return rv;
473 		}
474 		
475 		*cur.buf.last++ |= b;
476 		
477 		return ErrorCode.OK;
478 	}
479 
480 	/*
481 	 * Behaves like orb(), but does not update buf.last
482 	 * pointer.
483 	 */
484 	ErrorCode orHold(ubyte b)
485 	{
486 		ErrorCode rv;
487 		
488 		rv = ensureAddByte();
489 		if (rv != 0) {
490 			return rv;
491 		}
492 		
493 		*cur.buf.last |= b;
494 		
495 		return ErrorCode.OK;
496 	}
497 
498 	void fastOr(ubyte b) {
499 		assert(cur.buf.last+1 <= cur.buf.end);
500 		*cur.buf.last++ |= b;
501 	}
502 
503 	void fastOrHold(ubyte b) {
504 		*cur.buf.last |= b;
505 	}
506 
507 	/*
508 	 * Copies all data stored in Buffers to the contagious buffer.  This
509 	 * function allocates the contagious memory to store all data in
510 	 * Buffers and returns it.
511 	 */
512 	ubyte[] remove()
513 	{
514 		size_t len;
515 		Chain chain;
516 		Buffer* buf;
517 		ubyte[] res;
518 		Buffer resbuf;
519 		len = 0;
520 		
521 		for (chain = head; chain; chain = chain.next) {
522 			len += chain.buf.length;
523 		}
524 		if (!len) 
525 			res = null;
526 		else {
527 			res = Mem.alloc!(ubyte[])(len);
528 		}
529 		resbuf = Buffer(res);
530 		
531 		for (chain = head; chain; chain = chain.next) {
532 			buf = &chain.buf;
533 			
534 			if (resbuf.last) {
535 				assert(resbuf.available >= buf.length);
536 				memcpy(resbuf.last, buf.pos, buf.length);
537 				resbuf.last += buf.length;
538 			}
539 			
540 			buf.reset();
541 			chain.buf.shiftRight(offset);
542 		}
543 		
544 		cur = head;
545 		return res;
546 	}
547 
548 	/// Fills dst with a slice of the head chain's buffer, and frees the chain if it becomes empty
549 	/// chunk_keep must be 1 for buffers to be emptied this way.
550 	ubyte[] removeOne(ubyte[] dst) 
551 	in { 
552 		assert(chunk_keep <= 1, "Cannot use removeOne with a custom keep amount set"); 
553 	}
554 	body {
555 		Chain chain = head;
556 		size_t len = min(chain.buf.length, dst.length);
557 		ubyte[] data = chain.buf.pos[0 .. len];
558 		memcpy(dst.ptr, data.ptr, len);
559 
560 		chain.buf.pos += len;
561 
562 		if (chain.buf.length > 0)
563 			return dst;
564 
565 		if (chain.next) {
566 			head = chain.next;
567 			chain.free();
568 			chunk_used--;
569 		} else {
570 			chain.buf.reset();
571 			chain.buf.shiftRight(offset);
572 			cur = head = chain;
573 		}
574 		return dst[0 .. len];
575 	}
576 
577 	// The head buffer was already read, just remove it
578 	void removeOne()
579 	in { 
580 		assert(chunk_keep <= 1, "Cannot use removeOne with a custom keep amount set"); 
581 	}
582 	body {
583 		Chain chain = head;
584 
585 		if (chain.next) {
586 			head = chain.next;
587 			chain.free();
588 			chunk_used--;
589 		} else {
590 			chain.buf.reset();
591 			chain.buf.shiftRight(offset);
592 			cur = head = chain;
593 		}
594 	}
595 
596 	/*
597 	 * Resets Buffers and makes the buffers empty.
598 	 */
599 	void reset()
600 	{
601 		Chain chain;
602 		Chain ci;
603 		size_t k;
604 		
605 		k = chunk_keep;
606 		for (ci = head; ci; ci = ci.next) {
607 			ci.buf.reset();
608 			ci.buf.shiftRight(offset);
609 			
610 			if (--k == 0) {
611 				break;
612 			}
613 		}
614 		
615 		if (ci) {
616 			chain = ci.next;
617 			ci.next = null;
618 			
619 			for (ci = chain; ci;) {
620 				chain = ci.next;
621 				
622 				ci.free();
623 				Mem.free(ci);
624 				ci = chain;
625 			}
626 			
627 			chunk_used = chunk_keep;
628 		}
629 		
630 		cur = head;
631 	}
632 
633 	/*
634 	 * Moves cur to cur.next.  If resulting cur is
635 	 * null, this function allocates new buffers and cur points to
636 	 * it.
637 	 *
638 	 * This function returns 0 if it succeeds, or one of the following
639 	 * negative error codes:
640 	 *
641 	 * ErrorCode.NOMEM
642 	 *     Out of memory
643 	 * ErrorCode.BUFFER_ERROR
644 	 *     Out of buffer space.
645 	 */
646 	ErrorCode advance() { return allocChain(); }
647 
648 	/* Sets cur to head */
649 	void rewind() {
650 		cur = head;
651 	}
652 
653 	
654 	/*
655 	 * Move cur, from the current position, using next member, to
656 	 * the last buf which has length > 0 without seeing buf
657 	 * which satisfies length == 0.  If cur.buf.length == 0 or cur.next is null,
658 	 * cur is unchanged.
659 	 */
660 	void seekLastPresent()
661 	{
662 		Chain ci;
663 		
664 		for (ci = cur; ci; ci = ci.next) {
665 			if (ci.buf.length == 0) {
666 				return;
667 			} else {
668 				cur = ci;
669 			}
670 		}
671 	}
672 
673 	/*
674 	 * Returns true if cur.next is not empty.
675 	 */
676 	bool nextPresent()
677 	{
678 		Chain chain;
679 		
680 		chain = cur.next;
681 		
682 		return chain && chain.buf.length;
683 	}
684 
685 	int curAvailable() {
686 		return cur.buf.available();
687 	}
688 
689 	@property int length()
690 	{
691 		Chain ci;
692 		int len;
693 		
694 		len = 0;
695 		for (ci = head; ci; ci = ci.next) {
696 			len += ci.buf.length;
697 		}
698 		
699 		return len;
700 	}
701 
702 private:
703 	@property int available() {
704 		return cast(int)(cur.buf.available + (chunk_length - offset) * (max_chunk - chunk_used));
705 
706 	}
707 
708 	ErrorCode ensureAddByte()
709 	{
710 		ErrorCode rv;
711 		Buffer* buf;
712 		
713 		buf = &cur.buf;
714 		
715 		if (buf.available >= 1) {
716 			return ErrorCode.OK;
717 		}
718 		
719 		rv = allocChain();
720 		if (rv != 0)
721 			return rv;		
722 		return ErrorCode.OK;
723 	}
724 
725 	ErrorCode allocChain() {
726 		Chain chain;
727 		
728 		if (cur.next) {
729 			cur = cur.next;
730 			return ErrorCode.OK;
731 		}
732 		
733 		if (max_chunk == chunk_used)
734 			return ErrorCode.BUFFER_ERROR;
735 
736 		chain = Chain(chunk_length, use_secure_mem, zeroize_on_free);
737 		
738 		LOGF("new buffer %d bytes allocated for bufs %s, used %d", chunk_length, this, chunk_used);
739 		
740 		++chunk_used;
741 		
742 		cur.next = chain;
743 		cur = chain;		
744 		cur.buf.shiftRight(offset);
745 		
746 		return ErrorCode.OK;
747 	}
748 
749 }