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(in size_t low, in 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     @property ref inout(ubyte[]) front() inout
87     {
88         assert (!_queue.empty, "ByteBuffer.front: Queue is empty");
89 
90         return _queue.front;
91     }
92 
93     void popFront()
94     {
95         assert (!_queue.empty, "ByteBuffer.popFront: Queue is empty");
96 
97         _size -= _queue.front.length;
98         _queue.removeFront();
99     }
100 
101     void popFront(size_t size)
102     {
103         assert (size >= 0 && size <= _size, "ByteBuffer.popFront: Invalid arguments size");
104 
105         if (size == 0)
106         {
107             return;
108         }
109 
110         if (size == _size)
111         {
112             _queue.clear();
113             _size = 0;
114 
115             return;
116         }
117 
118         size_t removed = 0, lack = size;
119 
120         size_t line = 0, count = 0, currline_len = 0;
121         foreach (a; _queue)
122         {
123             line++;
124             currline_len = a.length;
125             count       += currline_len;
126 
127             if (count > size)
128             {
129                 break;
130             }
131         }
132 
133         if (line > 1)
134         {
135             removed = count - currline_len;
136             lack    = size  - removed;
137             _queue.removeFront(line - 1);
138         }
139 
140         if (lack > 0)
141         {
142             _queue.front = _queue.front[lack .. $];
143         }
144 
145         _size -= size;
146     }
147 
148     void clear()
149     {
150         _queue.clear();
151         _size = 0;
152     }
153 
154     string toString()
155     {
156         return this[0 .. $].to!string();
157     }
158 
159     auto opCast(T)()
160     {
161         static if (isSomeString!T)
162         {
163             return toString();
164         }
165         else static if (is(T: const ubyte[]))
166         {
167             return this[0 .. $];
168         }
169         else
170         {
171             static assert(0, "ByteBuffer.opCast: Not support type.");
172         }
173     }
174 
175 private:
176 
177     DList!(ubyte[]) _queue;
178     size_t          _size;
179 }