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 }