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