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 (!end || !begin) return;
76 		assert(end >= begin);
77 		size_t memlen = cast(size_t)((cast(void*)end) - (cast(void*)begin));
78 		static if (__VERSION__ >= 2067)
79 		{
80 			if (use_secure_mem) { 
81 				import std.c.string : memset;
82 				// optimization, skips freeing the entire buffer in memutils for light buffer uses
83 				if (capacity > 1024 && offset < capacity - capacity/128)
84 					memset(begin, 0, offset);
85 				SecureMem.free(begin[0 .. memlen]);
86 			}
87 			else 
88 			{
89 				if (zeroize_on_free)
90 				{
91 					import std.c.string : memset;
92 					memset(begin, 0, memlen);
93 				}
94 				Mem.free(begin[0 .. memlen]);
95 			}
96 		}
97 		else {
98 			if (zeroize_on_free || use_secure_mem)
99 			{
100 				import std.c.string : memset;
101 				memset(begin, 0, memlen);
102 			}
103 			Mem.free(begin[0 .. memlen]);
104 		}
105 		begin = null;
106 		end = null;
107 	}
108 	
109 	/*
110 	 * Extends buffer so that capacity() returns at least
111 	 * |new_cap|. If extensions took place, buffer pointers in |buf| will
112 	 * change.
113 	 */
114 	void reserve(size_t new_cap)
115 	{
116 		if (!new_cap) return;
117 
118 		ubyte[] new_buf;
119 		size_t cap;
120 		
121 		cap = capacity;
122 		
123 		if (cap >= new_cap) {
124 			return;
125 		}
126 		
127 		new_cap = max(new_cap, cap * 2);
128 
129 		static if (__VERSION__ >= 2067)
130 		{
131 			if (use_secure_mem) {
132 				if (begin)
133 					new_buf = SecureMem.realloc(begin[0 .. end - begin], new_cap);
134 				else
135 					new_buf = SecureMem.alloc!(ubyte[])(new_cap);
136 			}
137 			else {
138 				if (begin) {
139 					new_buf = Mem.realloc(begin[0 .. end - begin], new_cap);
140 				}
141 				else
142 					new_buf = Mem.alloc!(ubyte[])(new_cap);
143 			}
144 		}
145 		else {
146 			if (begin) {
147 				new_buf = Mem.realloc(begin[0 .. end - begin], new_cap);
148 			}
149 			else
150 				new_buf = Mem.alloc!(ubyte[])(new_cap);
151 		}
152 		ubyte* ptr = new_buf.ptr;
153 		
154 		pos = ptr + (pos - begin);
155 		last = ptr + (last - begin);
156 		mark = ptr + (mark - begin);
157 		begin = ptr;
158 		end = ptr + new_cap;
159 		
160 		return;
161 	}
162 	
163 	/// Resets pos, last, mark member of |buf| to buf.begin.
164 	void reset()
165 	{
166 		pos = last = mark = begin;
167 	}
168 	
169 	/*
170 	 * Initializes Buffer using supplied buffer |buf|. Semantically, 
171 	 * the application should not call *_reserve() or free() functions.
172 	 */
173 	this(ubyte[] buf) {
174 		begin = pos = last = mark = buf.ptr;
175 		end = begin + buf.length;
176 	}		
177 	
178 	/*
179 	 * Initializes the |buf| and allocates at least |initial| bytes of
180 	 * memory.
181 	 */
182 	this(size_t initial = 0, bool _use_secure_mem = false, bool _zeroise_on_free = false) {
183 		use_secure_mem = _use_secure_mem;
184 		zeroize_on_free = _zeroise_on_free;
185 		begin = null;
186 		end = null;
187 		pos = null;
188 		last = null;
189 		mark = null;
190 		reserve(initial);
191 	}
192 
193 }
194 
195 class Buffers {
196 
197 	class Chain {
198 		Buffer buf;
199 		Chain next;
200 		static Chain opCall(size_t chunk_length = 0, bool _use_secure_mem = false, bool _zeroize_on_free = false)
201 		{
202 			Chain chain;
203 			chain = Mem.alloc!(Buffers.Chain)();
204 			scope(failure) Mem.free(chain);
205 			chain.next = null;
206 			chain.buf = Buffer(chunk_length, _use_secure_mem, _zeroize_on_free);
207 			return chain;
208 		}
209 
210 		void free() 
211 		{
212 			buf.free();
213 			buf.destroy();
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 		use_secure_mem = _use_secure_mem;
265 		zeroize_on_free = _zeroize_on_free;
266 		Chain chain = Chain(_chunk_length, _use_secure_mem, _zeroize_on_free);
267 
268 		offset = _offset;
269 		
270 		head = chain;
271 		cur = head;
272 		
273 		cur.buf.shiftRight(offset);
274 		
275 		chunk_length = _chunk_length;
276 		chunk_used = 1;
277 		max_chunk = _max_chunk;
278 		chunk_keep = _chunk_keep;
279 	}
280 
281 	/*
282 	 * Initializes Buffers using supplied buffer |buf|.
283 	 * The first buffer bufs.head uses buffer |begin|.  The buffer size
284 	 * is fixed and no allocate extra chunk buffer is allocated.  In other
285 	 * words, max_chunk = chunk_keep = 1. 
286 	 */
287 	this(ubyte[] buf)
288 	{
289 		Chain chain = Chain();
290 
291 		chain.next = null;
292 		chain.buf = Buffer(buf);
293 
294 		dont_free = true;
295 		offset = 0;
296 		head = chain;
297 		cur = head;
298 		chunk_length = buf.length;
299 		chunk_used = 1;
300 		max_chunk = 1;
301 		chunk_keep = 1;
302 	}
303 
304 	~this() { if (head) free(); }
305 
306 	/// Frees any related resources
307 	void free()
308 	{
309 		Chain chain;
310 		Chain next_chain;
311 		
312 		if (!head) return;
313 
314 		if (dont_free) {
315 			Mem.free(head);
316 			head = null;
317 			return;
318 		}
319 		
320 		for (chain = head; chain;) {
321 			next_chain = chain.next;
322 			chain.free();
323 			Mem.free(chain);
324 			chain = next_chain;
325 		}
326 		
327 		head = null;
328 	}	
329 
330 	/*
331 	 * Reallocates internal buffer using |chunk_length|.  The max_chunk,
332 	 * chunk_keep and offset do not change.  After successful allocation
333 	 * of new buffer, previous buffers are deallocated without copying
334 	 * anything into new buffers.  chunk_used is reset to 1.
335 	 *
336 	 * This function returns 0 if it succeeds, or one of the following
337 	 * negative error codes:
338 	 *
339 	 * ErrorCode.NOMEM
340 	 *     Out of memory.
341 	 * ErrorCode.INVALID_ARGUMENT
342 	 *     chunk_length < offset
343 	 */
344 	void realloc(size_t _chunk_length)
345 	in { assert(_chunk_length >= offset, "Invalid Arguments"); }
346 	body {
347 		Chain chain = Chain(_chunk_length, use_secure_mem, zeroize_on_free);
348 		
349 		free();
350 		
351 		head = chain;
352 		cur = head;
353 		
354 		cur.buf.shiftRight(offset);
355 		
356 		chunk_length = _chunk_length;
357 		chunk_used = 1;
358 	}
359 
360 	/*
361 	 * Appends the |data| of length |len| to the |bufs|. The write starts
362 	 * at bufs.cur.buf.last. A new buffers will be allocated to store
363 	 * all data.
364 	 *
365 	 * This function returns 0 if it succeeds, or one of the following
366 	 * negative error codes:
367 	 *
368 	 * ErrorCode.NOMEM
369 	 *     Out of memory.
370 	 * ErrorCode.BUFFER_ERROR
371 	 *     Out of buffer space.
372 	 */
373 	ErrorCode add(in string data)
374 	{
375 		ErrorCode rv;
376 		size_t nwrite;
377 		Buffer* buf;
378 		const(ubyte)* p;
379 		int len = cast(int)data.length;
380 		if (available < len) {
381 			return ErrorCode.BUFFER_ERROR;
382 		}
383 		
384 		p = cast(const(ubyte)*)data.ptr;
385 		
386 		while (len > 0) {
387 			buf = &cur.buf;
388 			
389 			nwrite = cast(size_t) min(buf.available, len);
390 			if (nwrite == 0) {
391 				rv = allocChain();
392 				if (rv != 0) {
393 					return rv;
394 				}
395 				continue;
396 			}
397 			memcpy(buf.last, p, nwrite);
398 			buf.last += nwrite;
399 			p += nwrite;
400 			len -= nwrite;
401 		}
402 		
403 		return ErrorCode.OK;
404 	}
405 
406 	/*
407 	 * Appends a single byte |b| to the Buffers. The write starts at
408 	 * cur.buf.last. A new buffers will be allocated to store all
409 	 * data.
410 	 *
411 	 * This function returns 0 if it succeeds, or one of the following
412 	 * negative error codes:
413 	 *
414 	 * ErrorCode.BUFFER_ERROR
415 	 *     Out of buffer space.
416 	 */
417 	ErrorCode add(ubyte b)
418 	{
419 		ErrorCode rv;
420 		
421 		rv = ensureAddByte();
422 		if (rv != 0) 
423 			return rv;
424 
425 		
426 		*cur.buf.last++ = b;
427 		
428 		return ErrorCode.OK;
429 	}
430 
431 	/*
432 	 * Behaves like addb(), but this does not update
433 	 * buf.last pointer.
434 	 */
435 	ErrorCode addHold(ubyte b)
436 	{
437 		ErrorCode rv;
438 		
439 		rv = ensureAddByte();
440 		if (rv != 0) {
441 			return rv;
442 		}
443 		
444 		*cur.buf.last = b;
445 		
446 		return ErrorCode.OK;
447 	}
448 
449 	void fastAdd(ubyte b) {
450 		assert(cur.buf.last+1 <= cur.buf.end);
451 		*cur.buf.last++ = b;
452 	}
453 
454 	void fastAddHold(ubyte b) {
455 		*cur.buf.last = b;
456 	}
457 		
458 	/*
459 	 * Performs bitwise-OR of |b| at cur.buf.last. A new buffers
460 	 * will be allocated if necessary.
461 	 *
462 	 * This function returns 0 if it succeeds, or one of the following
463 	 * negative error codes:
464 	 *
465 	 * ErrorCode.NOMEM
466 	 *     Out of memory.
467 	 * ErrorCode.BUFFER_ERROR
468 	 *     Out of buffer space.
469 	 */
470 	ErrorCode or(ubyte b)
471 	{
472 		ErrorCode rv;
473 		
474 		rv = ensureAddByte();
475 		if (rv != 0) {
476 			return rv;
477 		}
478 		
479 		*cur.buf.last++ |= b;
480 		
481 		return ErrorCode.OK;
482 	}
483 
484 	/*
485 	 * Behaves like orb(), but does not update buf.last
486 	 * pointer.
487 	 */
488 	ErrorCode orHold(ubyte b)
489 	{
490 		ErrorCode rv;
491 		
492 		rv = ensureAddByte();
493 		if (rv != 0) {
494 			return rv;
495 		}
496 		
497 		*cur.buf.last |= b;
498 		
499 		return ErrorCode.OK;
500 	}
501 
502 	void fastOr(ubyte b) {
503 		assert(cur.buf.last+1 <= cur.buf.end);
504 		*cur.buf.last++ |= b;
505 	}
506 
507 	void fastOrHold(ubyte b) {
508 		*cur.buf.last |= b;
509 	}
510 
511 	/*
512 	 * Copies all data stored in Buffers to the contagious buffer.  This
513 	 * function allocates the contagious memory to store all data in
514 	 * Buffers and returns it.
515 	 */
516 	ubyte[] remove()
517 	{
518 		size_t len;
519 		Chain chain;
520 		Buffer* buf;
521 		ubyte[] res;
522 		Buffer resbuf;
523 		len = 0;
524 		
525 		for (chain = head; chain; chain = chain.next) {
526 			len += chain.buf.length;
527 		}
528 		if (!len) 
529 			res = null;
530 		else {
531 			res = Mem.alloc!(ubyte[])(len);
532 		}
533 		resbuf = Buffer(res);
534 		
535 		for (chain = head; chain; chain = chain.next) {
536 			buf = &chain.buf;
537 			
538 			if (resbuf.last) {
539 				assert(resbuf.available >= buf.length);
540 				memcpy(resbuf.last, buf.pos, buf.length);
541 				resbuf.last += buf.length;
542 			}
543 			
544 			buf.reset();
545 			chain.buf.shiftRight(offset);
546 		}
547 		
548 		cur = head;
549 		return res;
550 	}
551 
552 	/// Fills dst with a slice of the head chain's buffer, and frees the chain if it becomes empty
553 	/// chunk_keep must be 1 for buffers to be emptied this way.
554 	ubyte[] removeOne(ubyte[] dst) 
555 	in { 
556 		assert(chunk_keep <= 1, "Cannot use removeOne with a custom keep amount set"); 
557 	}
558 	body {
559 		Chain chain = head;
560 		size_t len = min(chain.buf.length, dst.length);
561 		ubyte[] data = chain.buf.pos[0 .. len];
562 		memcpy(dst.ptr, data.ptr, len);
563 
564 		chain.buf.pos += len;
565 
566 		if (chain.buf.available == 0 && chain.buf.length == 0) 
567 		{
568 			if (chain.next) {
569 				head = chain.next;
570 				chain.free();
571 				Mem.free(chain);
572 				chunk_used--;
573 			} else {
574 				chain.buf.reset();
575 				chain.buf.shiftRight(offset);
576 				cur = head = chain;
577 			}
578 		}
579 
580 		if (dst.length == len) 
581 			return dst;
582 		return dst[0 .. len];
583 	}
584 
585 	// The head buffer was already read, just remove it
586 	void removeOne()
587 	in { 
588 		assert(chunk_keep <= 1, "Cannot use removeOne with a custom keep amount set"); 
589 	}
590 	body {
591 		Chain chain = head;
592 
593 		if (chain.next) {
594 			head = chain.next;
595 			chain.free();
596 			Mem.free(chain);
597 			chunk_used--;
598 		} else {
599 			chain.buf.reset();
600 			chain.buf.shiftRight(offset);
601 			cur = head = chain;
602 		}
603 	}
604 
605 	/*
606 	 * Resets Buffers and makes the buffers empty.
607 	 */
608 	void reset()
609 	{
610 		Chain chain;
611 		Chain ci;
612 		size_t k;
613 		
614 		k = chunk_keep;
615 		for (ci = head; ci; ci = ci.next) {
616 			ci.buf.reset();
617 			ci.buf.shiftRight(offset);
618 			
619 			if (--k == 0) {
620 				break;
621 			}
622 		}
623 		
624 		if (ci) {
625 			chain = ci.next;
626 			ci.next = null;
627 			
628 			for (ci = chain; ci;) {
629 				chain = ci.next;
630 				
631 				ci.free();
632 				Mem.free(ci);
633 				ci = chain;
634 			}
635 			
636 			chunk_used = chunk_keep;
637 		}
638 		
639 		cur = head;
640 	}
641 
642 	/*
643 	 * Moves cur to cur.next.  If resulting cur is
644 	 * null, this function allocates new buffers and cur points to
645 	 * it.
646 	 *
647 	 * This function returns 0 if it succeeds, or one of the following
648 	 * negative error codes:
649 	 *
650 	 * ErrorCode.NOMEM
651 	 *     Out of memory
652 	 * ErrorCode.BUFFER_ERROR
653 	 *     Out of buffer space.
654 	 */
655 	ErrorCode advance() { return allocChain(); }
656 
657 	/* Sets cur to head */
658 	void rewind() {
659 		cur = head;
660 	}
661 
662 	
663 	/*
664 	 * Move cur, from the current position, using next member, to
665 	 * the last buf which has length > 0 without seeing buf
666 	 * which satisfies length == 0.  If cur.buf.length == 0 or cur.next is null,
667 	 * cur is unchanged.
668 	 */
669 	void seekLastPresent()
670 	{
671 		Chain ci;
672 		
673 		for (ci = cur; ci; ci = ci.next) {
674 			if (ci.buf.length == 0) {
675 				return;
676 			} else {
677 				cur = ci;
678 			}
679 		}
680 	}
681 
682 	/*
683 	 * Returns true if cur.next is not empty.
684 	 */
685 	bool nextPresent()
686 	{
687 		Chain chain;
688 		
689 		chain = cur.next;
690 		
691 		return chain && chain.buf.length;
692 	}
693 
694 	int curAvailable() {
695 		return cur.buf.available();
696 	}
697 
698 	@property int length()
699 	{
700 		Chain ci;
701 		int len;
702 		
703 		len = 0;
704 		for (ci = head; ci; ci = ci.next) {
705 			len += ci.buf.length;
706 		}
707 		
708 		return len;
709 	}
710 
711 	@property int available() {
712 		return cast(int)(cur.buf.available + (chunk_length - offset) * (max_chunk - chunk_used));
713 
714 	}
715 
716 private:
717 	ErrorCode ensureAddByte()
718 	{
719 		ErrorCode rv;
720 		Buffer* buf;
721 		
722 		buf = &cur.buf;
723 		
724 		if (buf.available >= 1) {
725 			return ErrorCode.OK;
726 		}
727 		
728 		rv = allocChain();
729 		if (rv != 0)
730 			return rv;		
731 		return ErrorCode.OK;
732 	}
733 
734 	ErrorCode allocChain() {
735 		Chain chain;
736 		
737 		if (cur.next) {
738 			cur = cur.next;
739 			return ErrorCode.OK;
740 		}
741 		
742 		if (max_chunk == chunk_used)
743 			return ErrorCode.BUFFER_ERROR;
744 
745 		chain = Chain(chunk_length, use_secure_mem, zeroize_on_free);
746 		
747 		LOGF("new buffer %d bytes allocated for bufs %s, used %d", chunk_length, this, chunk_used);
748 		
749 		++chunk_used;
750 		
751 		cur.next = chain;
752 		cur = chain;		
753 		cur.buf.shiftRight(offset);
754 		
755 		return ErrorCode.OK;
756 	}
757 
758 }