Statistics
| Branch: | Revision:

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
}