Former-commit-id:9f19e3f712
[formerly9f19e3f712
[formerly 64fa9254b946eae7e61bbc3f513b7c3696c4f54f]] Former-commit-id:06a8b51d6d
Former-commit-id:3360eb6c5f
982 lines
28 KiB
C#
982 lines
28 KiB
C#
/*
|
|
*
|
|
* 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;
|
|
|
|
namespace Apache.Qpid.Buffer
|
|
{
|
|
/// <summary>
|
|
/// Abstract class implementing a byte buffer
|
|
/// </summary>
|
|
public abstract class ByteBuffer
|
|
{
|
|
private int _position;
|
|
private int _limit;
|
|
private bool _isAutoExpand;
|
|
private static IByteBufferAllocator _allocator =
|
|
new SimpleByteBufferAllocator();
|
|
|
|
#region Properties
|
|
//
|
|
// Properties
|
|
//
|
|
|
|
/// <summary>
|
|
/// The maximum number of bytes the buffer can hold
|
|
/// </summary>
|
|
public abstract int Capacity
|
|
{
|
|
get;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return the backing array of this buffer
|
|
/// </summary>
|
|
public abstract byte[] Array
|
|
{
|
|
get;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The current position inside this buffer
|
|
/// </summary>
|
|
public int Position
|
|
{
|
|
get { return _position; }
|
|
set { Seek(value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Index of the first element that should not be read or written.
|
|
/// A buffer's limit is never negative and is never greater than the its capacity.
|
|
/// </summary>
|
|
public int Limit
|
|
{
|
|
get { return _limit; }
|
|
set { SetLimit(value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Number of bytes remaining in the buffer from the current position
|
|
/// </summary>
|
|
public int Remaining
|
|
{
|
|
get { return Limit - Position; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// True if there are bytes remaining in the buffer
|
|
/// </summary>
|
|
public bool HasRemaining
|
|
{
|
|
get { return Remaining > 0; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// If true, the buffer will be resized as necessary
|
|
/// to allow space for writing. By default is false.
|
|
/// </summary>
|
|
public bool IsAutoExpand
|
|
{
|
|
get { return _isAutoExpand; }
|
|
set { _isAutoExpand = value; }
|
|
}
|
|
|
|
#endregion // Properties
|
|
|
|
#region Buffer Manipulation
|
|
//
|
|
// Buffer Manipulation
|
|
//
|
|
|
|
/// <summary>
|
|
/// Move the buffer to Position 0
|
|
/// </summary>
|
|
/// <returns>This instance</returns>
|
|
public ByteBuffer Rewind()
|
|
{
|
|
Seek(0);
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Prepare the buffer to read back what's been written
|
|
/// </summary>
|
|
/// <returns>This instance</returns>
|
|
public ByteBuffer Flip()
|
|
{
|
|
Limit = Position;
|
|
Position = 0;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Compact this buffer.
|
|
/// </summary>
|
|
/// <returns>This instance</returns>
|
|
/// <remarks>
|
|
/// The bytes between the buffer's current position and its limit, if any,
|
|
/// are copied to the beginning of the buffer.
|
|
/// </remarks>
|
|
public ByteBuffer Compact()
|
|
{
|
|
DoCompact();
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clears this buffer. The position is set to zero, the limit is set to the capacity
|
|
/// </summary>
|
|
/// <returns>This instance</returns>
|
|
public ByteBuffer Clear()
|
|
{
|
|
Limit = Capacity;
|
|
Position = 0;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Expands this buffer's capacity so that
|
|
/// Remaining == expectedRemaining
|
|
/// </summary>
|
|
/// <param name="expectedRemaining">Number of bytes that should be accessable after resizing</param>
|
|
/// <returns>This instance</returns>
|
|
public ByteBuffer Expand(int expectedRemaining)
|
|
{
|
|
return Expand(Position, expectedRemaining);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Expands this buffer's capacity so that
|
|
/// Remaining == expectedRemaining
|
|
/// </summary>
|
|
/// <param name="position">Position from which to start the resize</param>
|
|
/// <param name="expectedRemaining">Number of bytes that should be accessable after resizing</param>
|
|
/// <returns>This instance</returns>
|
|
public ByteBuffer Expand(int position, int expectedRemaining)
|
|
{
|
|
if ( expectedRemaining <= 0 )
|
|
throw new ArgumentException("expectedRemaining must be greater than 0");
|
|
|
|
int end = position + expectedRemaining;
|
|
if ( end > Capacity )
|
|
{
|
|
DoResize(end);
|
|
}
|
|
if ( end > Limit )
|
|
Limit = end;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new byte buffer whose content is a shared
|
|
/// subsequence of this buffer's content.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The content of the new buffer will start at this buffer's current position.
|
|
/// Changes to this buffer's content will be visible in the new buffer,
|
|
/// and vice versa; the two buffers' position and limit values will be independent.
|
|
/// <para>
|
|
/// The new buffer's position will be zero, its capacity and its limit will
|
|
/// be the number of bytes remaining in this buffer.
|
|
/// </para>
|
|
/// </remarks>
|
|
/// <returns>A view on top of this instance</returns>
|
|
public ByteBuffer Slice()
|
|
{
|
|
return new SlicedByteBuffer(this);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Skip the specified number of bytes
|
|
/// </summary>
|
|
/// <param name="numBytes">Number of bytes to move forward by</param>
|
|
/// <returns>This instance</returns>
|
|
public ByteBuffer Skip(int numBytes)
|
|
{
|
|
Position += numBytes;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Acquire this buffer to keep it alive.
|
|
/// </summary>
|
|
public virtual void Acquire()
|
|
{
|
|
// override in subclass if supported
|
|
}
|
|
|
|
/// <summary>
|
|
/// Release this buffer instance
|
|
/// </summary>
|
|
public virtual void Release()
|
|
{
|
|
// override in subclass if supported
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return a string with a Hex Dump of this buffer's contents
|
|
/// </summary>
|
|
/// <returns>The hex dump</returns>
|
|
public string GetHexDump()
|
|
{
|
|
return ByteBufferHexDumper.GetHexDump(this);
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return GetHexDump();
|
|
}
|
|
#endregion // Buffer Manipulation
|
|
|
|
#region Static Operations
|
|
//
|
|
// Static Operations
|
|
//
|
|
/// <summary>
|
|
/// Replaces the default allocator with your own implementation
|
|
/// </summary>
|
|
/// <param name="allocator">New allocator</param>
|
|
public static void SetAllocator(IByteBufferAllocator allocator)
|
|
{
|
|
if ( allocator == null )
|
|
throw new ArgumentNullException("allocator");
|
|
_allocator = allocator;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Allocate a new buffer with the specified capacity
|
|
/// using the default allocator
|
|
/// </summary>
|
|
/// <param name="capacity">Desired capacity</param>
|
|
/// <returns>The new buffer</returns>
|
|
public static ByteBuffer Allocate(int capacity)
|
|
{
|
|
return _allocator.Allocate(capacity);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Wraps the specified arrat into a new buffer
|
|
/// </summary>
|
|
/// <param name="buffer"></param>
|
|
/// <returns></returns>
|
|
public static ByteBuffer Wrap(byte[] buffer)
|
|
{
|
|
return _allocator.Wrap(buffer);
|
|
}
|
|
#endregion // Static Operations
|
|
|
|
#region Data Accessors
|
|
//
|
|
// Data Accessors
|
|
//
|
|
|
|
// Byte Stuff
|
|
|
|
/// <summary>
|
|
/// Read the next byte in the buffer
|
|
/// </summary>
|
|
/// <returns>The next byte available</returns>
|
|
public byte GetByte()
|
|
{
|
|
byte value = GetByte(Position);
|
|
Position += 1;
|
|
return value;
|
|
}
|
|
/// <summary>
|
|
/// Read the byte at the specified position
|
|
/// </summary>
|
|
/// <param name="position">Position to read from</param>
|
|
/// <returns>The value at the position</returns>
|
|
public byte GetByte(int position)
|
|
{
|
|
CheckSpaceForReading(position, 1);
|
|
return ReadByte(position);
|
|
}
|
|
/// <summary>
|
|
/// Write a byte at the current position
|
|
/// </summary>
|
|
/// <param name="value">Value to write</param>
|
|
/// <returns>This instance</returns>
|
|
public ByteBuffer Put(byte value)
|
|
{
|
|
Put(Position, value);
|
|
Position++;
|
|
return this;
|
|
}
|
|
/// <summary>
|
|
/// Write a byte at the specified position
|
|
/// </summary>
|
|
/// <param name="position">Position to write to</param>
|
|
/// <param name="value">Value to write</param>
|
|
/// <returns>This instance</returns>
|
|
public ByteBuffer Put(int position, byte value)
|
|
{
|
|
CheckSpaceForWriting(position, 1);
|
|
Write(position, value);
|
|
return this;
|
|
}
|
|
|
|
// SByte Stuff
|
|
|
|
/// <summary>
|
|
/// Read the next signed byte in the buffer
|
|
/// </summary>
|
|
/// <returns>The next signed byte available</returns>
|
|
public sbyte GetSByte()
|
|
{
|
|
sbyte value = GetSByte(Position);
|
|
Position += 1;
|
|
return value;
|
|
}
|
|
/// <summary>
|
|
/// Read the signed byte at the specified position
|
|
/// </summary>
|
|
/// <param name="position">Position to read from</param>
|
|
/// <returns>The value at the position</returns>
|
|
public sbyte GetSByte(int position)
|
|
{
|
|
CheckSpaceForReading(position, 1);
|
|
return (sbyte)ReadByte(position);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write a signed byte at the current position
|
|
/// </summary>
|
|
/// <param name="value">Value to write</param>
|
|
/// <returns>This instance</returns>
|
|
public ByteBuffer Put(sbyte value)
|
|
{
|
|
Put(Position, value);
|
|
Position += 1;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write a signed byte at the specified position
|
|
/// </summary>
|
|
/// <param name="position">Position to write to</param>
|
|
/// <param name="value">Value to write</param>
|
|
/// <returns>This instance</returns>
|
|
public ByteBuffer Put(int position, sbyte value)
|
|
{
|
|
CheckSpaceForWriting(position, 1);
|
|
Write(position, (byte)value);
|
|
return this;
|
|
}
|
|
|
|
// UInt16 Stuff
|
|
|
|
/// <summary>
|
|
/// Read the next uint16 in the buffer
|
|
/// </summary>
|
|
/// <returns>The next uint16 available</returns>
|
|
public ushort GetUInt16()
|
|
{
|
|
ushort value = GetUInt16(Position);
|
|
Position += 2;
|
|
return value;
|
|
}
|
|
/// <summary>
|
|
/// Read the uint16 at the specified position
|
|
/// </summary>
|
|
/// <param name="position">Position to read from</param>
|
|
/// <returns>The value at the position</returns>
|
|
public ushort GetUInt16(int position)
|
|
{
|
|
CheckSpaceForReading(position, 2);
|
|
byte upper = ReadByte(position);
|
|
byte lower = ReadByte(position+1);
|
|
return (ushort)(((ushort)upper << 8) + lower);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write a uint16 at the current position
|
|
/// </summary>
|
|
/// <param name="value">Value to write</param>
|
|
/// <returns>This instance</returns>
|
|
public ByteBuffer Put(ushort value)
|
|
{
|
|
Put(Position, value);
|
|
Position += 2;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write a uint16 at the specified position
|
|
/// </summary>
|
|
/// <param name="position">Position to write to</param>
|
|
/// <param name="value">Value to write</param>
|
|
/// <returns>This instance</returns>
|
|
public ByteBuffer Put(int position, ushort value)
|
|
{
|
|
CheckSpaceForWriting(position, 2);
|
|
Write(position, (byte)(value >> 8));
|
|
Write(position+1, (byte)(value));
|
|
return this;
|
|
}
|
|
|
|
// Int16 Stuff
|
|
|
|
/// <summary>
|
|
/// Read the next int16 in the buffer
|
|
/// </summary>
|
|
/// <returns>The next int16 available</returns>
|
|
public short GetInt16()
|
|
{
|
|
return (short) GetUInt16();
|
|
}
|
|
/// <summary>
|
|
/// Read the int16 at the specified position
|
|
/// </summary>
|
|
/// <param name="position">Position to read from</param>
|
|
/// <returns>The value at the position</returns>
|
|
public short GetInt16(int position)
|
|
{
|
|
return (short)GetUInt16(position);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write a int16 at the current position
|
|
/// </summary>
|
|
/// <param name="value">Value to write</param>
|
|
/// <returns>This instance</returns>
|
|
public ByteBuffer Put(short value)
|
|
{
|
|
return Put((ushort) value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write a int16 at the specified position
|
|
/// </summary>
|
|
/// <param name="position">Position to write to</param>
|
|
/// <param name="value">Value to write</param>
|
|
/// <returns>This instance</returns>
|
|
public ByteBuffer Put(int position, short value)
|
|
{
|
|
return Put(position, (ushort)value);
|
|
}
|
|
|
|
|
|
// UInt32 Stuff
|
|
|
|
/// <summary>
|
|
/// Read the next uint32 in the buffer
|
|
/// </summary>
|
|
/// <returns>The next uint32 available</returns>
|
|
public uint GetUInt32()
|
|
{
|
|
uint value = GetUInt32(Position);
|
|
Position += 4;
|
|
return value;
|
|
}
|
|
/// <summary>
|
|
/// Read the uint32 at the specified position
|
|
/// </summary>
|
|
/// <param name="position">Position to read from</param>
|
|
/// <returns>The value at the position</returns>
|
|
public uint GetUInt32(int position)
|
|
{
|
|
CheckSpaceForReading(position, 4);
|
|
byte b1 = ReadByte(position);
|
|
byte b2 = ReadByte(position + 1);
|
|
byte b3 = ReadByte(position + 2);
|
|
byte b4 = ReadByte(position + 3);
|
|
return (uint)((b1 << 24) + (b2 << 16) + (b3 << 8) + b4);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write a uint32 at the current position
|
|
/// </summary>
|
|
/// <param name="value">Value to write</param>
|
|
/// <returns>This instance</returns>
|
|
public ByteBuffer Put(uint value)
|
|
{
|
|
Put(Position, value);
|
|
Position += 4;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write a uint32 at the specified position
|
|
/// </summary>
|
|
/// <param name="position">Position to write to</param>
|
|
/// <param name="value">Value to write</param>
|
|
/// <returns>This instance</returns>
|
|
public ByteBuffer Put(int position, uint value)
|
|
{
|
|
CheckSpaceForWriting(position, 4);
|
|
Write(position, (byte)(value >> 24));
|
|
Write(position + 1, (byte)(value >> 16));
|
|
Write(position + 2, (byte)(value >> 8));
|
|
Write(position + 3, (byte)(value));
|
|
return this;
|
|
}
|
|
|
|
// Int32 Stuff
|
|
|
|
/// <summary>
|
|
/// Read the next int32 in the buffer
|
|
/// </summary>
|
|
/// <returns>The next int32 available</returns>
|
|
public int GetInt32()
|
|
{
|
|
return (int)GetUInt32();
|
|
}
|
|
/// <summary>
|
|
/// Read the int32 at the specified position
|
|
/// </summary>
|
|
/// <param name="position">Position to read from</param>
|
|
/// <returns>The value at the position</returns>
|
|
public int GetInt32(int position)
|
|
{
|
|
return (int)GetUInt32(position);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write a int32 at the current position
|
|
/// </summary>
|
|
/// <param name="value">Value to write</param>
|
|
/// <returns>This instance</returns>
|
|
public ByteBuffer Put(int value)
|
|
{
|
|
return Put((uint)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write a int32 at the specified position
|
|
/// </summary>
|
|
/// <param name="position">Position to write to</param>
|
|
/// <param name="value">Value to write</param>
|
|
/// <returns>This instance</returns>
|
|
public ByteBuffer Put(int position, int value)
|
|
{
|
|
return Put(position, (uint)value);
|
|
}
|
|
|
|
// UInt64 Stuff
|
|
|
|
/// <summary>
|
|
/// Read the next uint64 in the buffer
|
|
/// </summary>
|
|
/// <returns>The next uint64 available</returns>
|
|
public ulong GetUInt64()
|
|
{
|
|
ulong value = GetUInt64(Position);
|
|
Position += 8;
|
|
return value;
|
|
}
|
|
/// <summary>
|
|
/// Read the uint64 at the specified position
|
|
/// </summary>
|
|
/// <param name="position">Position to read from</param>
|
|
/// <returns>The value at the position</returns>
|
|
public ulong GetUInt64(int position)
|
|
{
|
|
CheckSpaceForReading(position, 8);
|
|
byte b1 = ReadByte(position);
|
|
byte b2 = ReadByte(position + 1);
|
|
byte b3 = ReadByte(position + 2);
|
|
byte b4 = ReadByte(position + 3);
|
|
byte b5 = ReadByte(position + 4);
|
|
byte b6 = ReadByte(position + 5);
|
|
byte b7 = ReadByte(position + 6);
|
|
byte b8 = ReadByte(position + 7);
|
|
// all the casts necessary because otherwise each subexpression
|
|
// only gets promoted to uint and cause incorrect results
|
|
return (((ulong)b1 << 56) + ((ulong)b2 << 48) + ((ulong)b3 << 40) +
|
|
((ulong)b4 << 32) + ((ulong)b5 << 24) +
|
|
((ulong)b6 << 16) + ((ulong)b7 << 8) + b8);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write a uint64 at the current position
|
|
/// </summary>
|
|
/// <param name="value">Value to write</param>
|
|
/// <returns>This instance</returns>
|
|
public ByteBuffer Put(ulong value)
|
|
{
|
|
Put(Position, value);
|
|
Position += 8;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write a uint64 at the specified position
|
|
/// </summary>
|
|
/// <param name="position">Position to write to</param>
|
|
/// <param name="value">Value to write</param>
|
|
/// <returns>This instance</returns>
|
|
public ByteBuffer Put(int position, ulong value)
|
|
{
|
|
CheckSpaceForWriting(position, 8);
|
|
Write(position, (byte)(value >> 56));
|
|
Write(position + 1, (byte)(value >> 48));
|
|
Write(position + 2, (byte)(value >> 40));
|
|
Write(position + 3, (byte)(value >> 32));
|
|
Write(position + 4, (byte)(value >> 24));
|
|
Write(position + 5, (byte)(value >> 16));
|
|
Write(position + 6, (byte)(value >> 8));
|
|
Write(position + 7, (byte)(value));
|
|
return this;
|
|
}
|
|
|
|
// Int64 Stuff
|
|
|
|
/// <summary>
|
|
/// Read the next int64 in the buffer
|
|
/// </summary>
|
|
/// <returns>The next int64 available</returns>
|
|
public long GetInt64()
|
|
{
|
|
return (long)GetUInt64();
|
|
}
|
|
/// <summary>
|
|
/// Read the int64 at the specified position
|
|
/// </summary>
|
|
/// <param name="position">Position to read from</param>
|
|
/// <returns>The value at the position</returns>
|
|
public long GetInt64(int position)
|
|
{
|
|
return (long)GetUInt64(position);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write a int64 at the current position
|
|
/// </summary>
|
|
/// <param name="value">Value to write</param>
|
|
/// <returns>This instance</returns>
|
|
public ByteBuffer Put(long value)
|
|
{
|
|
return Put((ulong)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write a int64 at the specified position
|
|
/// </summary>
|
|
/// <param name="position">Position to write to</param>
|
|
/// <param name="value">Value to write</param>
|
|
/// <returns>This instance</returns>
|
|
public ByteBuffer Put(int position, long value)
|
|
{
|
|
return Put(position, (ulong)value);
|
|
}
|
|
|
|
|
|
// Float Stuff
|
|
|
|
/// <summary>
|
|
/// Read the next float in the buffer
|
|
/// </summary>
|
|
/// <returns>The next float available</returns>
|
|
public float GetFloat()
|
|
{
|
|
unsafe
|
|
{
|
|
uint val = GetUInt32();
|
|
return *((float*)&val);
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// Read the float at the specified position
|
|
/// </summary>
|
|
/// <param name="position">Position to read from</param>
|
|
/// <returns>The value at the position</returns>
|
|
public float GetFloat(int position)
|
|
{
|
|
unsafe
|
|
{
|
|
uint val = GetUInt32(position);
|
|
return *((float*)&val);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write a float at the current position
|
|
/// </summary>
|
|
/// <param name="value">Value to write</param>
|
|
/// <returns>This instance</returns>
|
|
public ByteBuffer Put(float value)
|
|
{
|
|
unsafe
|
|
{
|
|
uint val = *((uint*)&value);
|
|
return Put(val);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write a float at the specified position
|
|
/// </summary>
|
|
/// <param name="position">Position to write to</param>
|
|
/// <param name="value">Value to write</param>
|
|
/// <returns>This instance</returns>
|
|
public ByteBuffer Put(int position, float value)
|
|
{
|
|
unsafe
|
|
{
|
|
uint val = *((uint*)&value);
|
|
return Put(position, val);
|
|
}
|
|
}
|
|
|
|
// Double Stuff
|
|
|
|
/// <summary>
|
|
/// Read the next double in the buffer
|
|
/// </summary>
|
|
/// <returns>The next double available</returns>
|
|
public double GetDouble()
|
|
{
|
|
unsafe
|
|
{
|
|
ulong val = GetUInt64();
|
|
return *((double*)&val);
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// Read the double at the specified position
|
|
/// </summary>
|
|
/// <param name="position">Position to read from</param>
|
|
/// <returns>The value at the position</returns>
|
|
public double GetDouble(int position)
|
|
{
|
|
unsafe
|
|
{
|
|
ulong val = GetUInt64(position);
|
|
return *((double*)&val);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write a double at the current position
|
|
/// </summary>
|
|
/// <param name="value">Value to write</param>
|
|
/// <returns>This instance</returns>
|
|
public ByteBuffer Put(double value)
|
|
{
|
|
unsafe
|
|
{
|
|
ulong val = *((ulong*)&value);
|
|
return Put(val);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write a double at the specified position
|
|
/// </summary>
|
|
/// <param name="position">Position to write to</param>
|
|
/// <param name="value">Value to write</param>
|
|
/// <returns>This instance</returns>
|
|
public ByteBuffer Put(int position, double value)
|
|
{
|
|
unsafe
|
|
{
|
|
ulong val = *((ulong*)&value);
|
|
return Put(position, val);
|
|
}
|
|
}
|
|
|
|
// Char Stuff
|
|
|
|
/// <summary>
|
|
/// Read the next char in the buffer
|
|
/// </summary>
|
|
/// <returns>The next char available</returns>
|
|
public char GetChar()
|
|
{
|
|
return (char)GetUInt16();
|
|
}
|
|
/// <summary>
|
|
/// Read the char at the specified position
|
|
/// </summary>
|
|
/// <param name="position">Position to read from</param>
|
|
/// <returns>The value at the position</returns>
|
|
public char GetChar(int position)
|
|
{
|
|
return (char)GetUInt16(position);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write a char at the current position
|
|
/// </summary>
|
|
/// <param name="value">Value to write</param>
|
|
/// <returns>This instance</returns>
|
|
public ByteBuffer Put(char value)
|
|
{
|
|
return Put((ushort) value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write a char at the specified position
|
|
/// </summary>
|
|
/// <param name="position">Position to write to</param>
|
|
/// <param name="value">Value to write</param>
|
|
/// <returns>This instance</returns>
|
|
public ByteBuffer Put(int position, char value)
|
|
{
|
|
return Put(position, (ushort)value);
|
|
}
|
|
|
|
// Byte[] stuff
|
|
|
|
public void GetBytes(byte[] buffer)
|
|
{
|
|
GetBytes(buffer, 0, buffer.Length);
|
|
}
|
|
|
|
public void GetBytes(byte[] buffer, int offset, int length)
|
|
{
|
|
GetBytes(Position, buffer, offset, length);
|
|
Position += length;
|
|
}
|
|
public void GetBytes(int position, byte[] buffer, int offset, int length)
|
|
{
|
|
CheckSpaceForReading(position, length);
|
|
if ( offset + length > buffer.Length )
|
|
throw new ArgumentException("Invalid offset + length");
|
|
ReadBytes(position, buffer, offset, length);
|
|
}
|
|
|
|
public ByteBuffer Put(byte[] buffer)
|
|
{
|
|
return Put(buffer, 0, buffer.Length);
|
|
}
|
|
|
|
public ByteBuffer Put(byte[] buffer, int offset, int length)
|
|
{
|
|
Put(Position, buffer, offset, length);
|
|
Position += length;
|
|
return this;
|
|
}
|
|
|
|
public ByteBuffer Put(int position, byte[] buffer, int offset, int length)
|
|
{
|
|
CheckSpaceForWriting(position, length);
|
|
if ( offset + length > buffer.Length )
|
|
throw new ArgumentException("Invalid offset + length");
|
|
|
|
Write(position, buffer, offset, length);
|
|
return this;
|
|
}
|
|
|
|
public ByteBuffer Put(ByteBuffer data)
|
|
{
|
|
Put(Position, data);
|
|
Position += data.Remaining;
|
|
return this;
|
|
}
|
|
|
|
public ByteBuffer Put(int position, ByteBuffer data)
|
|
{
|
|
CheckSpaceForWriting(position, data.Remaining);
|
|
Write(position, data.Array, data.Position, data.Remaining);
|
|
return this;
|
|
}
|
|
|
|
#endregion // Data Accessors
|
|
|
|
#region Core Overrides
|
|
//
|
|
// Core Overrides
|
|
//
|
|
|
|
protected abstract void DoWrite(int position, byte value);
|
|
protected abstract void DoWrite(int position, byte[] src, int offset, int length);
|
|
protected abstract byte DoReadByte(int position);
|
|
protected abstract void DoReadBytes(int position, byte[] dest, int offset, int length);
|
|
protected abstract void DoCompact();
|
|
protected abstract void DoResize(int newSize);
|
|
|
|
#endregion // Core Overrides
|
|
|
|
#region Private Methods
|
|
//
|
|
// Private Methods
|
|
//
|
|
|
|
private void Seek(int offset)
|
|
{
|
|
if ( offset > Capacity )
|
|
throw new ArgumentException("Cannot position beyond end of buffer");
|
|
_position = offset;
|
|
AdjustLimit();
|
|
}
|
|
|
|
private void SetLimit(int newLimit)
|
|
{
|
|
if ( newLimit < 0 )
|
|
throw new ArgumentOutOfRangeException("The new limit must be a positive value");
|
|
if ( newLimit > Capacity )
|
|
throw new ArgumentOutOfRangeException("The new limit must not be greater than the capacity");
|
|
_limit = newLimit;
|
|
if ( _position > newLimit )
|
|
_position = newLimit;
|
|
}
|
|
|
|
private void AdjustLimit()
|
|
{
|
|
if ( _limit < _position )
|
|
_limit = _position;
|
|
}
|
|
|
|
private void CheckSpaceForReading(int position, int length)
|
|
{
|
|
if ( position + length > Limit )
|
|
{
|
|
throw new BufferUnderflowException("Attempt to read " + length + " byte(s) to buffer where position is " + position +
|
|
" and limit is " + Limit);
|
|
}
|
|
}
|
|
|
|
private void CheckSpaceForWriting(int position, int length)
|
|
{
|
|
if ( IsAutoExpand )
|
|
{
|
|
Expand(position, length);
|
|
}
|
|
if ( position + length > Limit )
|
|
{
|
|
throw new BufferOverflowException("Attempt to write " + length + " byte(s) to buffer where position is " + position +
|
|
" and limit is " + Limit);
|
|
}
|
|
}
|
|
|
|
private void Write(int position, byte value)
|
|
{
|
|
DoWrite(position, value);
|
|
}
|
|
private void Write(int position, byte[] src, int offset, int length)
|
|
{
|
|
DoWrite(position, src, offset, length);
|
|
}
|
|
private byte ReadByte(int position)
|
|
{
|
|
return DoReadByte(position);
|
|
}
|
|
private void ReadBytes(int position, byte[] dest, int offset, int length)
|
|
{
|
|
DoReadBytes(position, dest, offset, length);
|
|
}
|
|
|
|
#endregion // Private Methods
|
|
|
|
} // class ByteBuffer
|
|
}
|