1 module cybuf;
2 
3 import std.algorithm;
4 import std.range;
5 import std.stdio;
6 
7 struct Cybuf(T){
8     alias Handler = void delegate(T value);
9     private T[] buf;
10     private size_t place;
11     private size_t size;
12     private Handler handler;
13 
14     @disable
15     public this();
16 
17     public this(size_t length, Handler handler = null){
18         this.buf.length = length;
19         place = 0;
20         size = 0;
21         this.handler = handler;
22     }
23 
24     public this(T[] buf, size_t place, size_t size){
25         this.buf = buf;
26         this.place = place;
27         this.size = size;
28     }
29 
30     @property
31     public T back(){
32         return this[$-1];
33     }
34 
35     @property
36     public bool empty(){
37         return (size == 0);
38     }
39 
40     @property
41     public T front(){
42         return this[0];
43     }
44 
45     @property
46     public size_t length(){
47         return size;
48     }
49 
50     public T opIndex(size_t index)
51     in{
52         assert(index < size);
53     }
54     body{
55         if(place+index >= buf.length){
56             return buf[index - (buf.length - place)];
57         }
58         else{
59             return buf[place + index];
60         }
61     }
62 
63     public size_t opDollar(){
64         return size;
65     }
66 
67     public void popBack(){
68         size--;
69     }
70 
71     public void popFront(){
72         place = (place+1 == buf.length) ? 0 : place+1;
73         size--;
74     }
75 
76     public void put(T elem){
77         if(size == buf.length && (handler !is null)){
78             handler(this[0]);
79         }
80 
81         if(place+size < buf.length){
82             buf[place+size] = elem;
83         }
84         else{
85             buf[size - (buf.length - place)] = elem;
86         }
87 
88         if(size == buf.length){
89             place = (place+1 == buf.length) ? 0 : place+1;
90         }
91         else{
92             size++;
93         }
94     }
95 
96     public void put(T[] elems){
97         foreach(e; elems){
98             this.put(e);
99         }
100     }
101 
102     public T[] rawBuf(){
103         return this.buf;
104     }
105 
106     public void setHandler(Handler h){
107         this.handler = h;
108     }
109 
110     @property 
111     public Cybuf!T save() const
112     {
113         return Cybuf!T(buf.dup, place, size);
114     }
115 }
116 
117 //Static checks about the status of the range.
118 unittest {
119     static assert(isInputRange!(Cybuf!int));
120     static assert(isOutputRange!(Cybuf!int, int));
121     static assert(isForwardRange!(Cybuf!int));
122     static assert(isBidirectionalRange!(Cybuf!int));
123     static assert(isRandomAccessRange!(Cybuf!int));
124 }
125 
126 //Testing Input/Output
127 unittest {
128     auto cb = Cybuf!int(4);
129 
130     cb.put([1,2,3]);
131 
132     assert(equal(cb.rawBuf(),[1,2,3,0][]));
133 
134     cb.put([4,5]);
135 
136     assert(equal(cb.rawBuf(),[5,2,3,4][]));
137 }
138 
139 //Testing save
140 unittest {
141     auto cb = Cybuf!string(4);
142 
143     cb.put(["faa", "fbb", "fcc", "fdd"]);
144 
145     auto cb_save = cb.save;
146 
147     cb.put(["fee"]);
148 
149     assert(!equal(cb.rawBuf(),cb_save.rawBuf()));
150 }
151 
152 //Testing backward iterating
153 unittest {
154     auto cb = Cybuf!int(6);
155 
156     cb.put([1,2,3,4,5,6,7,8]);
157 
158     auto daplop = retro(cb);
159 
160     int[] test;
161 
162     foreach(i; daplop){
163         test ~= i;
164     }
165 
166     assert(equal(test, [8,7,6,5,4,3]));
167 }
168 
169 //Testing index access
170 unittest {
171     auto cb = Cybuf!int(4);
172 
173     cb.put([1,2,3,4,5,6]);
174 
175     assert(cb[0] == 3);
176     assert(cb[$-1] == 6);
177 }
178 
179 //Example of how to use a handler
180 //Disabled because it is not a real unittest
181 /*
182 unittest {
183 
184     void printValue(int value){
185         writefln("I received the value %d", value);
186     }
187 
188     auto cb = Cybuf!int(4, &printValue);
189 
190     cb.put([1,2,3,4]);
191 
192     cb.put([5, 6]);
193 
194     cb = Cybuf!int(4);
195 
196     cb.setHandler(&printValue);
197 
198     cb.put([3,4,5,6,7,8]);
199 }
200 */