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 }