summaryrefslogtreecommitdiff
path: root/dotnet/Qpid.Buffer/ByteBuffer.cs
blob: 70022407a5bafad2f4940030bbc3941014e5ad97 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 */
using System;
using System.Collections;
using System.Text;

namespace Qpid.Buffer
{
    /// <summary>
    /// A buffer that manages an underlying byte oriented stream, and writes and reads to and from it in
    /// BIG ENDIAN order.
    /// </summary>
    public abstract class ByteBuffer
    {
        protected const int MINIMUM_CAPACITY = 1;
        
        protected static Stack _containerStack = new Stack();
        
        protected static Stack[] _heapBufferStacks = new Stack[]
            {
                new Stack(), new Stack(), new Stack(), new Stack(), 
                new Stack(), new Stack(), new Stack(), new Stack(), 
                new Stack(), new Stack(), new Stack(), new Stack(), 
                new Stack(), new Stack(), new Stack(), new Stack(), 
                new Stack(), new Stack(), new Stack(), new Stack(), 
                new Stack(), new Stack(), new Stack(), new Stack(), 
                new Stack(), new Stack(), new Stack(), new Stack(), 
                new Stack(), new Stack(), new Stack(), new Stack()
            };

        /// <summary>
        /// Returns the direct or heap buffer which is capable of the specified size.
        /// Currently does not support direct buffers but this will be an option in future.
        /// </summary>
        /// <param name="capacity">The capacity.</param>
        /// <returns></returns>
        public static ByteBuffer Allocate(int capacity)
        {
            // for now, just allocate a heap buffer but in future could do an optimised "direct" buffer
            // that is implemented natively
            return Allocate(capacity, false);
        }
        
        public static ByteBuffer Allocate(int capacity, bool direct)
        {
            ByteBuffer buffer = Allocate0(capacity, direct);
            RefCountingByteBuffer buf = AllocateContainer();
            buf.Init(buffer);
            return buf;
        }
        
        private static RefCountingByteBuffer AllocateContainer()
        {
            RefCountingByteBuffer buf = null;
            lock (_containerStack)
            {
                if (_containerStack.Count > 0)
                {
                    buf = (RefCountingByteBuffer) _containerStack.Pop();
                }
            }
            
            if (buf == null)
            {
                buf = new RefCountingByteBuffer();                
            }
            return buf;
        }
        
        protected static ByteBuffer Allocate0(int capacity, bool direct)
        {
            if (direct)
            {
                throw new NotSupportedException("Direct buffers not currently implemented");
            }            
            int idx = GetBufferStackIndex(_heapBufferStacks, capacity);
            Stack stack = _heapBufferStacks[idx];
            ByteBuffer buf = null;
            lock (stack)
            {
                if (stack.Count > 0)
                {
                    buf = (ByteBuffer) stack.Pop();
                }
            }

            if (buf == null)
            {
                buf = new HeapByteBuffer(MINIMUM_CAPACITY << idx);
            }

            return buf;
        }
        
        protected static void Release0(ByteBuffer buf)
        {
            Stack stack = _heapBufferStacks[GetBufferStackIndex(_heapBufferStacks, buf.Capacity)];
            lock (stack)
            {
                stack.Push(buf);
            }            
        }
        
        private static int GetBufferStackIndex(Stack[] bufferStacks, int size)
        {
            int targetSize = MINIMUM_CAPACITY;
            int stackIdx = 0;
            // each bucket contains buffers that are double the size of the previous bucket
            while (size > targetSize)
            {
                targetSize <<= 1;
                stackIdx++;
                if (stackIdx >= bufferStacks.Length)
                {
                    throw new ArgumentOutOfRangeException("size", "Buffer size is too big: " + size);
                }
            }
            return stackIdx;
        }

        /// <summary>
        /// Increases the internal reference count of this buffer to defer automatic release. You have
        /// to invoke release() as many times as you invoked this method to release this buffer.
        /// </summary>
        public abstract void Acquire();

        /// <summary>
        /// Releases the specified buffer to the buffer pool.
        /// </summary>
        public abstract void Release();

        public abstract int Capacity
        {
            get;
        }

        public abstract bool IsAutoExpand
        {
            get;
            set;
        }

        /// <summary>
        /// Changes the capacity and limit of this buffer sot his buffer gets the specified
        /// expectedRemaining room from the current position. This method works even if you didn't set
        /// autoExpand to true.
        /// </summary>
        /// <param name="expectedRemaining">Room you want from the current position</param>        
        public abstract void Expand(int expectedRemaining);

        /// <summary>
        /// Changes the capacity and limit of this buffer sot his buffer gets the specified
        /// expectedRemaining room from the specified position.
        /// </summary>
        /// <param name="pos">The pos you want the room to be available from.</param>
        /// <param name="expectedRemaining">The expected room you want available.</param>        
        public abstract void Expand(int pos, int expectedRemaining);

        /// <summary>
        /// Returns true if and only if this buffer is returned back to the buffer pool when released.
        /// </summary>
        /// <value><c>true</c> if pooled; otherwise, <c>false</c>.</value>
        public abstract bool Pooled
        {
            get;
            set;
        }
        
        public abstract int Position
        {
            get;
            set;
        }

        public abstract int Limit
        {
            get;
            set;
        }

        //public abstract void Mark();

        //public abstract void Reset();

        public abstract void Clear();

        /// <summary>
        /// Clears this buffer and fills its content with NULL. The position is set to zero, the limit is set to
        /// capacity and the mark is discarded.
        /// </summary>
        public void Sweep()
        {
            Clear();
            FillAndReset(Remaining);
        }
        
        public void Sweep(byte value)
        {
            Clear();
            FillAndReset(value, Remaining);
        }

        public abstract void Flip();

        public abstract void Rewind();

        public abstract int Remaining
        {
            get;
        }
        
        public bool HasRemaining()
        {
            return Remaining > 0;
        }

        public abstract byte Get();

        public abstract byte Get(int index);
        
        public abstract void Get(byte[] destination);

        public abstract ushort GetUnsignedShort();

        public abstract uint GetUnsignedInt();

        public abstract ulong GetUnsignedLong();

        public abstract string GetString(uint length, Encoding encoder);

        public abstract void Put(byte data);

        public abstract void Put(byte[] data);
        public abstract void Put(byte[] data, int offset, int size);

        public abstract void Put(ushort data);

        public abstract void Put(uint data);

        public abstract void Put(ulong data);

        public abstract void Put(ByteBuffer buf);
        
        public abstract void Compact();

        public abstract byte[] ToByteArray();
        
        public override string ToString()
        {
            StringBuilder buf = new StringBuilder();
            buf.Append("HeapBuffer");
            buf.AppendFormat("[pos={0} lim={1} cap={2} : {3}]", Position, Limit, Capacity, HexDump);
            return buf.ToString();
        }

        public override int GetHashCode()
        {
            int h = 1;
            int p = Position;
            for (int i = Limit - 1; i >= p; i--)
            {
                h = 31 * h + Get(i);
            }

            return h;
        }

        public override bool Equals(object obj)
        {
            if (!(obj is ByteBuffer))
            {
                return false;
            }
            ByteBuffer that = (ByteBuffer) obj;
            
            if (Remaining != that.Remaining)
            {
                return false;
            }
            int p = Position;
            for (int i = Limit - 1, j = that.Limit - 1; i >= p; i--, j--)
            {
                byte v1 = this.Get(i);
                byte v2 = that.Get(j);
                if (v1 != v2)
                {
                    return false;
                }
            }
            return true;
        }
        
        public string HexDump
        {
            get
            {
                return ByteBufferHexDumper.GetHexDump(this);
            }
        }

        /// <summary>
        /// Fills the buffer with the specified specified value. This method moves the buffer position forward.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <param name="size">The size.</param>
        public void Fill(byte value, int size)
        {
            AutoExpand(size);
            int q = size >> 3;
            int r = size & 7;
                        
            if (q > 0)
            {
                int intValue = value | (value << 8) | (value << 16) | (value << 24);
                long longValue = intValue;
                longValue <<= 32;
                longValue |= (ushort)intValue;
                
                for (int i = q; i > 0; i--)
                {
                    Put((ulong)longValue);
                }
            }

            q = r >> 2;
            r = r & 3;
            
            if (q > 0)
            {
                int intValue = value | (value << 8) | (value << 16) | (value << 24);
                Put((uint)intValue);
            }

            q = r >> 1;
            r = r & 1;
            
            if (q > 0)
            {
                short shortValue = (short) (value | (value << 8));
                Put((ushort) shortValue);
            }
            if (r > 0)
            {
                Put(value);
            }
        }
        
        public void FillAndReset(byte value, int size)
        {
            AutoExpand(size);
            int pos = Position;
            try
            {
                Fill(value, size);
            }
            finally
            {
                Position = pos;
            }            
        }
        
        public void Fill(int size)
        {
            AutoExpand(size);
            int q = size >> 3;
            int r = size & 7;

            for (int i = q; i > 0; i--)
            {
                Put(0L);
            }

            q = r >> 2;
            r = r & 3;

            if (q > 0)
            {
                Put(0);
            }

            q = r >> 1;
            r = r & 1;

            if(q > 0)
            {
                Put((ushort) 0);
            }

            if (r > 0)
            {
                Put((byte) 0);
            }
        }
        
        public void FillAndReset(int size)
        {
            AutoExpand(size);
            int pos = Position;
            try
            {
                Fill(size);
            }
            finally
            {
                Position = pos;
            }
        }
        
        public void Skip(int size)
        {
            AutoExpand(size);
            Position = Position + size;
        }
        
        protected void AutoExpand(int expectedRemaining)
        {
            if (IsAutoExpand)
            {
                Expand(expectedRemaining);
            }
        }
        
        protected void AutoExpand(int pos, int expectedRemaining)
        {
            if (IsAutoExpand)
            {
                Expand(pos, expectedRemaining);
            }
        }                
    }
}