1 module async.container.bytebuffer; 2 3 import std.container.dlist; 4 import std.conv; 5 6 struct ByteBuffer 7 { 8 @property bool empty() 9 { 10 return (_size == 0); 11 } 12 13 @property size_t length() 14 { 15 return _size; 16 } 17 18 ref typeof(this) opBinary(string op, Stuff)(auto ref Stuff rhs) 19 if ((is(Stuff: const ubyte[])) && op == "~") 20 { 21 if (rhs is null) 22 { 23 return this; 24 } 25 26 _queue.insertBack(cast(ubyte[])rhs); 27 _size += rhs.length; 28 29 return this; 30 } 31 32 void opOpAssign(string op, Stuff)(auto ref Stuff rhs) 33 if ((is(Stuff: const ubyte[])) && op == "~") 34 { 35 if (rhs is null) 36 { 37 return; 38 } 39 40 _queue.insertBack(cast(ubyte[])rhs); 41 _size += rhs.length; 42 } 43 44 ubyte[] opSlice(const size_t low, const size_t high) @trusted 45 in 46 { 47 assert((low <= high) && (high <= _size), "ByteBuffer.opSlice: Invalid arguments low, high"); 48 } 49 body 50 { 51 ubyte[] ret; 52 53 if (low == high) 54 { 55 return ret; 56 } 57 58 size_t count = 0, lack = high - low; 59 foreach (a; _queue) 60 { 61 count += a.length; 62 63 if (count < low) 64 { 65 continue; 66 } 67 68 size_t start = low + ret.length - (count - a.length); 69 ret ~= a[start .. ($ - start) >= lack ? start + lack : $]; 70 lack = high - low - ret.length; 71 72 if (lack == 0) 73 { 74 break; 75 } 76 } 77 78 return ret; 79 } 80 81 size_t opDollar() nothrow const 82 { 83 return _size; 84 } 85 86 ref ubyte opIndex(const size_t index) @trusted 87 in 88 { 89 assert((index < _size), "ByteBuffer.opIndex: Invalid arguments index"); 90 } 91 body 92 { 93 size_t size, start; 94 foreach (a; _queue) 95 { 96 start = size; 97 size += a.length; 98 99 if (index < size) 100 { 101 return a[index - start]; 102 } 103 } 104 105 assert(0); 106 } 107 108 @property ref inout(ubyte[]) front() inout 109 { 110 assert(!_queue.empty, "ByteBuffer.front: Queue is empty"); 111 112 return _queue.front; 113 } 114 115 void popFront() 116 { 117 assert(!_queue.empty, "ByteBuffer.popFront: Queue is empty"); 118 119 _size -= _queue.front.length; 120 _queue.removeFront(); 121 } 122 123 void popFront(const size_t size) 124 { 125 assert(size >= 0 && size <= _size, "ByteBuffer.popFront: Invalid arguments size"); 126 127 if (size == 0) 128 { 129 return; 130 } 131 132 if (size == _size) 133 { 134 _queue.clear(); 135 _size = 0; 136 137 return; 138 } 139 140 size_t removed = 0, lack = size; 141 142 size_t line = 0, count = 0, currline_len = 0; 143 foreach (a; _queue) 144 { 145 line++; 146 currline_len = a.length; 147 count += currline_len; 148 149 if (count > size) 150 { 151 break; 152 } 153 } 154 155 if (line > 1) 156 { 157 removed = count - currline_len; 158 lack = size - removed; 159 _queue.removeFront(line - 1); 160 } 161 162 if (lack > 0) 163 { 164 _queue.front = _queue.front[lack .. $]; 165 } 166 167 _size -= size; 168 } 169 170 void clear() 171 { 172 _queue.clear(); 173 _size = 0; 174 } 175 176 string toString() 177 { 178 return this[0 .. $].to!string(); 179 } 180 181 auto opCast(T)() 182 { 183 static if (isSomeString!T) 184 { 185 return toString(); 186 } 187 else static if (is(T: const ubyte[])) 188 { 189 return this[0 .. $]; 190 } 191 else 192 { 193 static assert(0, "ByteBuffer.opCast: Not support type."); 194 } 195 } 196 197 private: 198 199 DList!(ubyte[]) _queue; 200 size_t _size; 201 }