root / trunk / hammock / src / net35 / ICSharpCode.SharpZipLib.Silverlight.Tests / TestSupport / RingBuffer.cs @ 0eea575a
History | View | Annotate | Download (13 kB)
1 |
// Define this to use simple synchronisation rather than events. |
---|---|
2 |
// They are about the same in terms of speed. |
3 |
#define SimpleSynch |
4 |
|
5 |
using System; |
6 |
using System.IO; |
7 |
using System.Threading; |
8 |
|
9 |
using NUnit.Framework; |
10 |
|
11 |
|
12 |
namespace ICSharpCode.SharpZipLib.Tests.TestSupport |
13 |
{ |
14 |
/// <summary> |
15 |
/// A fixed size buffer of bytes. Both reading and writing are supported. |
16 |
/// Reading from an empty buffer will wait until data is written. Writing to a full buffer |
17 |
/// will wait until data is read. |
18 |
/// </summary> |
19 |
public class ReadWriteRingBuffer |
20 |
{ |
21 |
#region Constructors |
22 |
/// <summary> |
23 |
/// Create a new RingBuffer with a specified size. |
24 |
/// </summary> |
25 |
/// <param name="size">The size of the ring buffer to create.</param> |
26 |
public ReadWriteRingBuffer( int size ) |
27 |
{ |
28 |
if ( size <= 0 ) { |
29 |
throw new ArgumentOutOfRangeException( "size" ); |
30 |
} |
31 |
|
32 |
array_ = new byte[size]; |
33 |
lockObject_ = new object(); |
34 |
|
35 |
#if SimpleSynch |
36 |
waitSpan_ = TimeSpan.FromMilliseconds(1); |
37 |
#else |
38 |
notEmptyEvent_ = new ManualResetEvent(false); |
39 |
notFullEvent_ = new ManualResetEvent(true); |
40 |
#endif |
41 |
} |
42 |
#endregion |
43 |
|
44 |
/// <summary> |
45 |
/// Clear the buffer contents. |
46 |
/// </summary> |
47 |
public void Clear() |
48 |
{ |
49 |
tail_ = 0; |
50 |
head_ = 0; |
51 |
count_ = 0; |
52 |
|
53 |
Array.Clear( array_, 0, array_.Length ); |
54 |
|
55 |
#if !SimpleSynch |
56 |
notFullEvent_.Set(); |
57 |
notEmptyEvent_.Reset(); |
58 |
#endif |
59 |
} |
60 |
|
61 |
/// <summary> |
62 |
/// Close the buffer for writing. |
63 |
/// </summary> |
64 |
/// <remarks>A Read when the buffer is closed and there is no data will return -1.</remarks> |
65 |
public void Close() |
66 |
{ |
67 |
isClosed_ = true; |
68 |
#if !SimpleSynch |
69 |
notEmptyEvent_.Set(); |
70 |
#endif |
71 |
} |
72 |
|
73 |
/// <summary> |
74 |
/// Write adds a byte to the head of the RingBuffer. |
75 |
/// </summary> |
76 |
/// <param name="value">The value to add.</param> |
77 |
public void WriteByte( byte value ) |
78 |
{ |
79 |
if (isClosed_) { |
80 |
throw new Exception("Buffer is closed"); |
81 |
} |
82 |
|
83 |
#if SimpleSynch |
84 |
while (IsFull) { |
85 |
Thread.Sleep(waitSpan_); |
86 |
} |
87 |
#else |
88 |
notFullEvent_.WaitOne(); |
89 |
#endif |
90 |
|
91 |
lock (lockObject_) { |
92 |
array_[head_] = value; |
93 |
head_ = (head_ + 1) % array_.Length; |
94 |
|
95 |
#if !SimpleSynch |
96 |
bool setEmpty = (count_ == 0); |
97 |
#endif |
98 |
|
99 |
count_ += 1; |
100 |
|
101 |
#if !SimpleSynch |
102 |
if (IsFull) |
103 |
{ |
104 |
notFullEvent_.Reset(); |
105 |
} |
106 |
|
107 |
if (setEmpty) |
108 |
{ |
109 |
notEmptyEvent_.Set(); |
110 |
} |
111 |
#endif |
112 |
} |
113 |
|
114 |
bytesWritten_++; |
115 |
} |
116 |
|
117 |
public void Write(byte[] buffer, int index, int count) |
118 |
{ |
119 |
if (isClosed_) |
120 |
{ |
121 |
throw new Exception("Buffer is closed"); |
122 |
} |
123 |
|
124 |
while ( count > 0 ) |
125 |
{ |
126 |
#if SimpleSynch |
127 |
while (IsFull) { |
128 |
Thread.Sleep(waitSpan_); |
129 |
} |
130 |
#else |
131 |
notFullEvent_.WaitOne(); |
132 |
#endif |
133 |
|
134 |
// Gauranteed to not be full at this point, however readers may sill read |
135 |
// from the buffer first. |
136 |
lock (lockObject_) |
137 |
{ |
138 |
int bytesToWrite = Length - Count; |
139 |
|
140 |
if (count < bytesToWrite) |
141 |
{ |
142 |
bytesToWrite = count; |
143 |
} |
144 |
#if !SimpleSynch |
145 |
bool setEmpty = (count_ == 0); |
146 |
#endif |
147 |
|
148 |
while (bytesToWrite > 0) |
149 |
{ |
150 |
array_[head_] = buffer[index]; |
151 |
index++; |
152 |
|
153 |
head_ = (head_ + 1) % array_.Length; |
154 |
|
155 |
bytesToWrite--; |
156 |
bytesWritten_++; |
157 |
count--; |
158 |
count_++; |
159 |
} |
160 |
|
161 |
#if !SimpleSynch |
162 |
if (IsFull) |
163 |
{ |
164 |
notFullEvent_.Reset(); |
165 |
} |
166 |
|
167 |
if (setEmpty) |
168 |
{ |
169 |
notEmptyEvent_.Set(); |
170 |
} |
171 |
#endif |
172 |
} |
173 |
} |
174 |
} |
175 |
|
176 |
/// <summary> |
177 |
/// Read a byte from the buffer. |
178 |
/// </summary> |
179 |
/// <returns></returns> |
180 |
public int ReadByte() |
181 |
{ |
182 |
int result = -1; |
183 |
|
184 |
#if SimpleSynch |
185 |
while (!isClosed_ && IsEmpty) { |
186 |
Thread.Sleep(waitSpan_); |
187 |
} |
188 |
#else |
189 |
notEmptyEvent_.WaitOne(); |
190 |
#endif |
191 |
|
192 |
if ( !IsEmpty ) { |
193 |
lock (lockObject_) { |
194 |
result = array_[tail_]; |
195 |
tail_ = (tail_ + 1) % array_.Length; |
196 |
#if !SimpleSynch |
197 |
bool setFull = IsFull; |
198 |
#endif |
199 |
count_ -= 1; |
200 |
#if !SimpleSynch |
201 |
if (!isClosed_ && (count_ == 0)) |
202 |
{ |
203 |
notEmptyEvent_.Reset(); |
204 |
} |
205 |
|
206 |
if (setFull) |
207 |
{ |
208 |
notFullEvent_.Set(); |
209 |
} |
210 |
#endif |
211 |
} |
212 |
} |
213 |
|
214 |
bytesRead_++; |
215 |
|
216 |
return result; |
217 |
} |
218 |
|
219 |
public int Read(byte[] buffer, int index, int count) |
220 |
{ |
221 |
int result = 0; |
222 |
|
223 |
while (count > 0) |
224 |
{ |
225 |
#if SimpleSynch |
226 |
while (!isClosed_ && IsEmpty) |
227 |
{ |
228 |
Thread.Sleep(waitSpan_); |
229 |
} |
230 |
#else |
231 |
notEmptyEvent_.WaitOne(); |
232 |
#endif |
233 |
|
234 |
if (IsEmpty) |
235 |
{ |
236 |
count = 0; |
237 |
} |
238 |
else |
239 |
{ |
240 |
lock (lockObject_) |
241 |
{ |
242 |
int toRead = Count; |
243 |
|
244 |
if (toRead > count) |
245 |
{ |
246 |
toRead = count; |
247 |
} |
248 |
|
249 |
result += toRead; |
250 |
|
251 |
#if !SimpleSynch |
252 |
bool setFull = IsFull; |
253 |
#endif |
254 |
|
255 |
while (toRead > 0) |
256 |
{ |
257 |
buffer[index] = array_[tail_]; |
258 |
index++; |
259 |
|
260 |
tail_ = (tail_ + 1) % array_.Length; |
261 |
count--; |
262 |
count_--; |
263 |
toRead--; |
264 |
bytesRead_++; |
265 |
} |
266 |
#if !SimpleSynch |
267 |
if (!isClosed_ && (count_ == 0)) |
268 |
{ |
269 |
notEmptyEvent_.Reset(); |
270 |
} |
271 |
|
272 |
if (setFull) |
273 |
{ |
274 |
notFullEvent_.Set(); |
275 |
} |
276 |
#endif |
277 |
} |
278 |
} |
279 |
} |
280 |
|
281 |
return result; |
282 |
} |
283 |
|
284 |
#region Properties |
285 |
|
286 |
/// <summary> |
287 |
/// Gets a value indicating wether the buffer is empty or not. |
288 |
/// </summary> |
289 |
public bool IsEmpty |
290 |
{ |
291 |
get { return count_ == 0; } |
292 |
} |
293 |
|
294 |
public bool IsFull |
295 |
{ |
296 |
get { |
297 |
return (count_ == array_.Length); |
298 |
} |
299 |
} |
300 |
|
301 |
public bool IsClosed |
302 |
{ |
303 |
get { return isClosed_; } |
304 |
} |
305 |
|
306 |
/// <summary> |
307 |
/// Gets the number of elements in the buffer. |
308 |
/// </summary> |
309 |
public int Count |
310 |
{ |
311 |
get { |
312 |
return count_; |
313 |
} |
314 |
} |
315 |
|
316 |
|
317 |
public int Length |
318 |
{ |
319 |
get { return array_.Length; } |
320 |
} |
321 |
|
322 |
public long BytesWritten |
323 |
{ |
324 |
get { return bytesWritten_; } |
325 |
} |
326 |
|
327 |
public long BytesRead |
328 |
{ |
329 |
get { return bytesRead_; } |
330 |
} |
331 |
|
332 |
/// <summary> |
333 |
/// Indexer - Get an element from the tail of the RingBuffer. |
334 |
/// </summary> |
335 |
public byte this[ int index ] |
336 |
{ |
337 |
get { |
338 |
if ( ( index < 0 ) || ( index >= array_.Length ) ) { |
339 |
throw new ArgumentOutOfRangeException( "index" ); |
340 |
} |
341 |
|
342 |
return array_[ ( tail_ + index ) % array_.Length ]; |
343 |
} |
344 |
} |
345 |
|
346 |
#endregion |
347 |
|
348 |
#region Instance Variables |
349 |
/// <summary> |
350 |
/// Flag indicating the buffer is closed. |
351 |
/// </summary> |
352 |
bool isClosed_; |
353 |
|
354 |
/// <summary> |
355 |
/// Index for the head of the buffer. |
356 |
/// </summary> |
357 |
/// <remarks>Its the index of the next byte to be <see cref="Write">written</see>.</remarks> |
358 |
int head_; |
359 |
|
360 |
/// <summary> |
361 |
/// Index for the tail of the buffer. |
362 |
/// </summary> |
363 |
/// <remarks>Its the index of the next byte to be <see cref="Read">written</see>.</remarks> |
364 |
int tail_; |
365 |
|
366 |
/// <summary> |
367 |
/// The total number of elements added to the buffer. |
368 |
/// </summary> |
369 |
int count_; |
370 |
|
371 |
/// <summary> |
372 |
/// Storage for the ring buffer contents. |
373 |
/// </summary> |
374 |
byte[] array_; |
375 |
|
376 |
long bytesWritten_; |
377 |
long bytesRead_; |
378 |
|
379 |
object lockObject_; |
380 |
|
381 |
TimeSpan waitSpan_; |
382 |
|
383 |
#if !SimpleSynch |
384 |
ManualResetEvent notEmptyEvent_; |
385 |
ManualResetEvent notFullEvent_; |
386 |
#endif |
387 |
#endregion |
388 |
} |
389 |
|
390 |
[TestFixture] |
391 |
public class ExerciseBuffer |
392 |
{ |
393 |
[Test] |
394 |
public void Basic() |
395 |
{ |
396 |
const int Size = 64; |
397 |
|
398 |
buffer_ = new ReadWriteRingBuffer(Size); |
399 |
|
400 |
Assert.IsFalse(buffer_.IsFull); |
401 |
Assert.IsTrue(buffer_.IsEmpty); |
402 |
|
403 |
buffer_.WriteByte(1); |
404 |
|
405 |
Assert.IsFalse(buffer_.IsFull); |
406 |
Assert.IsFalse(buffer_.IsEmpty); |
407 |
Assert.AreEqual(1, buffer_.Count); |
408 |
|
409 |
Assert.AreEqual(1, buffer_.ReadByte()); |
410 |
|
411 |
Assert.IsFalse(buffer_.IsFull); |
412 |
Assert.IsTrue(buffer_.IsEmpty); |
413 |
|
414 |
for (int i = 0; i < buffer_.Length; ++i) |
415 |
{ |
416 |
buffer_.WriteByte(unchecked((byte)(i & 0xff))); |
417 |
} |
418 |
|
419 |
Assert.IsTrue(buffer_.IsFull); |
420 |
Assert.IsFalse(buffer_.IsEmpty); |
421 |
|
422 |
buffer_.Close(); |
423 |
|
424 |
Assert.IsTrue(buffer_.IsClosed); |
425 |
|
426 |
bool caught = false; |
427 |
try |
428 |
{ |
429 |
buffer_.WriteByte(1); |
430 |
} |
431 |
catch |
432 |
{ |
433 |
caught = true; |
434 |
} |
435 |
|
436 |
Assert.IsTrue(caught); |
437 |
|
438 |
int count = Size; |
439 |
int expected = 0; |
440 |
|
441 |
while (count != 0) |
442 |
{ |
443 |
Assert.AreEqual(count, buffer_.Count); |
444 |
Assert.AreEqual(expected, buffer_.ReadByte()); |
445 |
count--; |
446 |
expected = (expected + 1) & 0xff; |
447 |
} |
448 |
|
449 |
Assert.IsTrue(buffer_.IsEmpty); |
450 |
Assert.AreEqual(-1, buffer_.ReadByte()); |
451 |
} |
452 |
|
453 |
[Test] |
454 |
public void Buffered() |
455 |
{ |
456 |
|
457 |
const int Size = 64; |
458 |
|
459 |
buffer_ = new ReadWriteRingBuffer(Size); |
460 |
|
461 |
byte[] writeBuffer = new byte[16]; |
462 |
for (int i = 0; i < 16; ++i) |
463 |
{ |
464 |
writeBuffer[i] = (byte)i; |
465 |
} |
466 |
|
467 |
buffer_.Write(writeBuffer, 0, 3); |
468 |
Assert.AreEqual(3, buffer_.Count); |
469 |
|
470 |
byte[] readBuffer = new byte[16]; |
471 |
Assert.AreEqual(3, buffer_.Read(readBuffer, 0, 3)); |
472 |
for (int i = 0; i < 3; ++i) |
473 |
{ |
474 |
Assert.AreEqual(i, readBuffer[i]); |
475 |
} |
476 |
|
477 |
} |
478 |
|
479 |
[Test] |
480 |
public void Threaded() |
481 |
{ |
482 |
buffer_ = new ReadWriteRingBuffer(8); |
483 |
readTarget_ = writeTarget_ = 16384; |
484 |
|
485 |
Thread reader = new Thread(Reader); |
486 |
reader.Start(); |
487 |
|
488 |
Thread writer = new Thread(Writer); |
489 |
writer.Start(); |
490 |
|
491 |
writer.Join(); |
492 |
reader.Join(); |
493 |
} |
494 |
|
495 |
void Reader() |
496 |
{ |
497 |
Random r = new Random(); |
498 |
byte nextValue = 0; |
499 |
|
500 |
while (readTarget_ > 0) |
501 |
{ |
502 |
int thisTime = r.Next(16); |
503 |
if (thisTime > readTarget_) |
504 |
{ |
505 |
thisTime = readTarget_; |
506 |
} |
507 |
|
508 |
while (thisTime > 0) |
509 |
{ |
510 |
int readValue = buffer_.ReadByte(); |
511 |
Assert.AreEqual(nextValue, readValue); |
512 |
nextValue = (byte)((nextValue + 1) & 0xff); |
513 |
thisTime--; |
514 |
readTarget_--; |
515 |
} |
516 |
|
517 |
Thread.Sleep(r.Next(10)); |
518 |
|
519 |
} |
520 |
|
521 |
int last = buffer_.ReadByte(); |
522 |
|
523 |
Assert.AreEqual(-1, last); |
524 |
Assert.IsTrue(buffer_.IsClosed); |
525 |
} |
526 |
|
527 |
void Writer() |
528 |
{ |
529 |
Random r = new Random(); |
530 |
|
531 |
byte nextValue = 0; |
532 |
while (writeTarget_ > 0) |
533 |
{ |
534 |
int thisTime = r.Next(16); |
535 |
if (thisTime > writeTarget_) |
536 |
{ |
537 |
thisTime = writeTarget_; |
538 |
} |
539 |
|
540 |
while (thisTime > 0) |
541 |
{ |
542 |
buffer_.WriteByte(nextValue); |
543 |
nextValue = (byte)((nextValue + 1) & 0xff); |
544 |
thisTime--; |
545 |
writeTarget_--; |
546 |
} |
547 |
Thread.Sleep(r.Next(10)); |
548 |
} |
549 |
buffer_.Close(); |
550 |
} |
551 |
|
552 |
int readTarget_; |
553 |
int writeTarget_; |
554 |
|
555 |
ReadWriteRingBuffer buffer_; |
556 |
|
557 |
} |
558 |
} |