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 core.exception : onOutOfMemoryError;
16 import core.stdc.string : memcpy;
17 import memutils.utils;
18 import std.algorithm : min,max;
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 core.stdc.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 core.stdc.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 core.stdc.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 		import std.algorithm : max;
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, 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 	static 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!(typeof(this))();
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 	do
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 	do {
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 			import std.algorithm : min;
390 			nwrite = cast(size_t) min(buf.available, len);
391 			if (nwrite == 0) {
392 				rv = allocChain();
393 				if (rv != 0) {
394 					return rv;
395 				}
396 				continue;
397 			}
398 			memcpy(buf.last, p, nwrite);
399 			buf.last += nwrite;
400 			p += nwrite;
401 			len -= nwrite;
402 		}
403 		
404 		return ErrorCode.OK;
405 	}
406 
407 	/*
408 	 * Appends a single byte |b| to the Buffers. The write starts at
409 	 * cur.buf.last. A new buffers will be allocated to store all
410 	 * data.
411 	 *
412 	 * This function returns 0 if it succeeds, or one of the following
413 	 * negative error codes:
414 	 *
415 	 * ErrorCode.BUFFER_ERROR
416 	 *     Out of buffer space.
417 	 */
418 	ErrorCode add(ubyte b)
419 	{
420 		ErrorCode rv;
421 		
422 		rv = ensureAddByte();
423 		if (rv != 0) 
424 			return rv;
425 
426 		
427 		*cur.buf.last++ = b;
428 		
429 		return ErrorCode.OK;
430 	}
431 
432 	/*
433 	 * Behaves like addb(), but this does not update
434 	 * buf.last pointer.
435 	 */
436 	ErrorCode addHold(ubyte b)
437 	{
438 		ErrorCode rv;
439 		
440 		rv = ensureAddByte();
441 		if (rv != 0) {
442 			return rv;
443 		}
444 		
445 		*cur.buf.last = b;
446 		
447 		return ErrorCode.OK;
448 	}
449 
450 	void fastAdd(ubyte b) {
451 		assert(cur.buf.last+1 <= cur.buf.end);
452 		*cur.buf.last++ = b;
453 	}
454 
455 	void fastAddHold(ubyte b) {
456 		*cur.buf.last = b;
457 	}
458 		
459 	/*
460 	 * Performs bitwise-OR of |b| at cur.buf.last. A new buffers
461 	 * will be allocated if necessary.
462 	 *
463 	 * This function returns 0 if it succeeds, or one of the following
464 	 * negative error codes:
465 	 *
466 	 * ErrorCode.NOMEM
467 	 *     Out of memory.
468 	 * ErrorCode.BUFFER_ERROR
469 	 *     Out of buffer space.
470 	 */
471 	ErrorCode or(ubyte b)
472 	{
473 		ErrorCode rv;
474 		
475 		rv = ensureAddByte();
476 		if (rv != 0) {
477 			return rv;
478 		}
479 		
480 		*cur.buf.last++ |= b;
481 		
482 		return ErrorCode.OK;
483 	}
484 
485 	/*
486 	 * Behaves like orb(), but does not update buf.last
487 	 * pointer.
488 	 */
489 	ErrorCode orHold(ubyte b)
490 	{
491 		ErrorCode rv;
492 		
493 		rv = ensureAddByte();
494 		if (rv != 0) {
495 			return rv;
496 		}
497 		
498 		*cur.buf.last |= b;
499 		
500 		return ErrorCode.OK;
501 	}
502 
503 	void fastOr(ubyte b) {
504 		assert(cur.buf.last+1 <= cur.buf.end);
505 		*cur.buf.last++ |= b;
506 	}
507 
508 	void fastOrHold(ubyte b) {
509 		*cur.buf.last |= b;
510 	}
511 
512 	/*
513 	 * Copies all data stored in Buffers to the contagious buffer.  This
514 	 * function allocates the contagious memory to store all data in
515 	 * Buffers and returns it.
516 	 */
517 	ubyte[] remove()
518 	{
519 		size_t len;
520 		Chain chain;
521 		Buffer* buf;
522 		ubyte[] res;
523 		Buffer resbuf;
524 		len = 0;
525 		
526 		for (chain = head; chain; chain = chain.next) {
527 			len += chain.buf.length;
528 		}
529 		if (!len) 
530 			res = null;
531 		else {
532 			res = Mem.alloc!(ubyte[])(len);
533 		}
534 		resbuf = Buffer(res);
535 		
536 		for (chain = head; chain; chain = chain.next) {
537 			buf = &chain.buf;
538 			
539 			if (resbuf.last) {
540 				assert(resbuf.available >= buf.length);
541 				memcpy(resbuf.last, buf.pos, buf.length);
542 				resbuf.last += buf.length;
543 			}
544 			
545 			buf.reset();
546 			chain.buf.shiftRight(offset);
547 		}
548 		
549 		cur = head;
550 		return res;
551 	}
552 
553 	/// Fills dst with a slice of the head chain's buffer, and frees the chain if it becomes empty
554 	/// chunk_keep must be 1 for buffers to be emptied this way.
555 	ubyte[] removeOne(ubyte[] dst) 
556 	in { 
557 		assert(chunk_keep <= 1, "Cannot use removeOne with a custom keep amount set"); 
558 	}
559 	do {
560 		Chain chain = head;
561 		import std.algorithm : min;
562 		size_t len = min(chain.buf.length, dst.length);
563 		ubyte[] data = chain.buf.pos[0 .. len];
564 		memcpy(dst.ptr, data.ptr, len);
565 
566 		chain.buf.pos += len;
567 
568 		if (chain.buf.available == 0 && chain.buf.length == 0) 
569 		{
570 			if (chain.next) {
571 				head = chain.next;
572 				chain.free();
573 				Mem.free(chain);
574 				chunk_used--;
575 			} else {
576 				chain.buf.reset();
577 				chain.buf.shiftRight(offset);
578 				cur = head = chain;
579 			}
580 		}
581 
582 		if (dst.length == len) 
583 			return dst;
584 		return dst[0 .. len];
585 	}
586 
587 	// The head buffer was already read, just remove it
588 	void removeOne()
589 	in { 
590 		assert(chunk_keep <= 1, "Cannot use removeOne with a custom keep amount set"); 
591 	}
592 	do {
593 		Chain chain = head;
594 
595 		if (chain.next) {
596 			head = chain.next;
597 			chain.free();
598 			Mem.free(chain);
599 			chunk_used--;
600 		} else {
601 			chain.buf.reset();
602 			chain.buf.shiftRight(offset);
603 			cur = head = chain;
604 		}
605 	}
606 
607 	/*
608 	 * Resets Buffers and makes the buffers empty.
609 	 */
610 	void reset()
611 	{
612 		Chain chain;
613 		Chain ci;
614 		size_t k;
615 		
616 		k = chunk_keep;
617 		for (ci = head; ci; ci = ci.next) {
618 			ci.buf.reset();
619 			ci.buf.shiftRight(offset);
620 			
621 			if (--k == 0) {
622 				break;
623 			}
624 		}
625 		
626 		if (ci) {
627 			chain = ci.next;
628 			ci.next = null;
629 			
630 			for (ci = chain; ci;) {
631 				chain = ci.next;
632 				
633 				ci.free();
634 				Mem.free(ci);
635 				ci = chain;
636 			}
637 			
638 			chunk_used = chunk_keep;
639 		}
640 		
641 		cur = head;
642 	}
643 
644 	/*
645 	 * Moves cur to cur.next.  If resulting cur is
646 	 * null, this function allocates new buffers and cur points to
647 	 * it.
648 	 *
649 	 * This function returns 0 if it succeeds, or one of the following
650 	 * negative error codes:
651 	 *
652 	 * ErrorCode.NOMEM
653 	 *     Out of memory
654 	 * ErrorCode.BUFFER_ERROR
655 	 *     Out of buffer space.
656 	 */
657 	ErrorCode advance() { return allocChain(); }
658 
659 	/* Sets cur to head */
660 	void rewind() {
661 		cur = head;
662 	}
663 
664 	
665 	/*
666 	 * Move cur, from the current position, using next member, to
667 	 * the last buf which has length > 0 without seeing buf
668 	 * which satisfies length == 0.  If cur.buf.length == 0 or cur.next is null,
669 	 * cur is unchanged.
670 	 */
671 	void seekLastPresent()
672 	{
673 		Chain ci;
674 		
675 		for (ci = cur; ci; ci = ci.next) {
676 			if (ci.buf.length == 0) {
677 				return;
678 			} else {
679 				cur = ci;
680 			}
681 		}
682 	}
683 
684 	/*
685 	 * Returns true if cur.next is not empty.
686 	 */
687 	bool nextPresent()
688 	{
689 		Chain chain;
690 		
691 		chain = cur.next;
692 		
693 		return chain && chain.buf.length;
694 	}
695 
696 	int curAvailable() {
697 		return cur.buf.available();
698 	}
699 
700 	@property int length()
701 	{
702 		Chain ci;
703 		int len;
704 		
705 		len = 0;
706 		for (ci = head; ci; ci = ci.next) {
707 			len += ci.buf.length;
708 		}
709 		
710 		return len;
711 	}
712 
713 	@property int available() {
714 		return cast(int)(cur.buf.available + (chunk_length - offset) * (max_chunk - chunk_used));
715 
716 	}
717 
718 private:
719 	ErrorCode ensureAddByte()
720 	{
721 		ErrorCode rv;
722 		Buffer* buf;
723 		
724 		buf = &cur.buf;
725 		
726 		if (buf.available >= 1) {
727 			return ErrorCode.OK;
728 		}
729 		
730 		rv = allocChain();
731 		if (rv != 0)
732 			return rv;		
733 		return ErrorCode.OK;
734 	}
735 
736 	ErrorCode allocChain() {
737 		Chain chain;
738 		
739 		if (cur.next) {
740 			cur = cur.next;
741 			return ErrorCode.OK;
742 		}
743 		
744 		if (max_chunk == chunk_used)
745 			return ErrorCode.BUFFER_ERROR;
746 
747 		chain = Chain(chunk_length, use_secure_mem, zeroize_on_free);
748 		
749 		LOGF("new buffer %d bytes allocated for bufs %s, used %d", chunk_length, this, chunk_used);
750 		
751 		++chunk_used;
752 		
753 		cur.next = chain;
754 		cur = chain;		
755 		cur.buf.shiftRight(offset);
756 		
757 		return ErrorCode.OK;
758 	}
759 
760 }