FixAntenna/NetCore/Common/Utils/ByteBuffer.cs (421 lines of code) (raw):
// Copyright (c) 2021 EPAM Systems
//
// Licensed 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.Buffers.Binary;
using System.IO;
using Epam.FixAntenna.NetCore.Common.Pool;
namespace Epam.FixAntenna.NetCore.Common.Utils
{
/// <summary>
/// The byte buffer helper class.
/// </summary>
public sealed class ByteBuffer
{
private const int DefaultBufferSize = 1024;
private readonly BinaryReader _reader;
private readonly BinaryWriter _writer;
/// <summary>
/// Creates the <c>ByteBuffer</c> with allocated byte buffer.
/// The default buffer length if 1024 bytes.
/// </summary>
/// <param name="size">buffer size in bytes</param>
public ByteBuffer(int size = DefaultBufferSize)
{
Buffer = AllocateBuffer(size);
_writer = new BinaryWriter(Buffer);
_reader = new BinaryReader(Buffer);
}
private ByteBuffer(MemoryStream buffer)
{
Buffer = buffer;
}
private MemoryStream AllocateBuffer(int capacity)
{
var stream = new MemoryStream(capacity);
stream.SetLength(capacity);
return stream;
}
public ByteBuffer Clear()
{
Buffer.Position = 0;
return this;
}
public MemoryStream Buffer { get; }
public ByteBuffer AddLikeString(long value, int minLen = 1)
{
var isNegative = false;
long length = minLen;
if (value < 0)
{
isNegative = true;
value = -value;
length++;
}
var valueCopy = value;
var valueLength = 1;
while ((valueCopy /= 10L) > 0L)
{
valueLength++;
}
length = Math.Max(length, isNegative ? valueLength + 1 : valueLength);
CheckAndExtend(length);
if (isNegative)
{
Buffer.WriteByte((byte)'-');
length--;
}
var position = Buffer.Position;
var last = position + length;
for (var i = last - 1L; i >= position; i--)
{
Buffer.Position = i;
Buffer.WriteByte((byte)(value % 10L + '0'));
value /= 10L;
}
Buffer.Position = last;
return this;
}
public void CheckAndExtend(long length)
{
var size = Buffer.Position + length;
if (size > Buffer.Capacity)
{
IncreaseBuffer(size - Buffer.Capacity);
}
}
public ByteBuffer Add(string s)
{
CheckAndExtend(s.Length);
return Put(s);
}
public ByteBuffer Put(string s)
{
var length = s.Length;
for (var i = 0; i < length; i++)
{
_writer.Write((byte)s[i]);
}
return this;
}
/// <summary>
/// Appends the array argument to internal buffer.
/// </summary>
/// <returns> a reference to this object. </returns>
public ByteBuffer Add(byte[] array)
{
CheckAndExtend(array.Length);
_writer.Write(array);
return this;
}
/// <summary>
/// Adds byte to buffer.
/// </summary>
/// <param name="b"> a byte </param>
public ByteBuffer Add(byte b)
{
if (Buffer.Length == Buffer.Position)
{
IncreaseBuffer(64);
}
_writer.Write(b);
return this;
}
/// <summary>
/// Adds char to buffer.
/// </summary>
/// <param name="b"> a char </param>
public ByteBuffer Add(char b)
{
if (Buffer.Position == Buffer.Length)
{
IncreaseBuffer(64);
}
_writer.Write((byte)b);
return this;
}
/// <summary>
/// Appends the array argument to internal buffer.
/// </summary>
/// <returns> a reference to this object. </returns>
public ByteBuffer Add(byte[] array, int start, int len)
{
CheckAndExtend(len);
_writer.Write(array, start, len);
return this;
}
public ByteBuffer Add(ByteBuffer buffer)
{
return Add(buffer.Buffer);
}
public ByteBuffer Add(MemoryStream buffer)
{
Add(buffer.GetBuffer(), (int)buffer.Position, (int)(buffer.Length - buffer.Position));
return this;
}
/// <summary>
/// Checks if buffer has a numOfBytes.
/// </summary>
/// <param name="length"> the requested space </param>
/// <returns> boolean true if has </returns>
public bool IsAvailable(int length)
{
return Buffer.Position + length <= Buffer.Length;
}
/// <summary>
/// Gets or sets offset of buffer.
/// </summary>
/// <value> the new offset </value>
public int Offset
{
set
{
if (Buffer.Length < value)
{
Buffer.SetLength(value);
}
Buffer.Position = value;
}
get => Position;
}
/// <summary>
/// Increases the buffer.
/// </summary>
/// <param name="increase"> the number of bytes </param>
public void IncreaseBuffer(long increase)
{
if (increase <= 0)
{
throw new ArgumentException();
}
var targetCapacity = Buffer.Length + increase;
Buffer.SetLength(targetCapacity);
}
public byte[] ToArray()
{
return Buffer.ToArray();
}
/// <summary>
/// Gets internal byte buffer (buffer itself, NOT a copy)
/// </summary>
public byte[] GetByteArray()
{
//TODO make sure it returns own array
return Buffer.GetBuffer();
}
/// <summary>
/// Gets internal byte buffer.
/// </summary>
public byte[] GetByteArray(int position, int length)
{
var currentPosition = Buffer.Position;
Buffer.Position = position;
var result = new byte[length];
_reader.Read(result, 0, length);
Buffer.Position = currentPosition;
return result;
}
public void Release()
{
ByteBufferPool.Instance.Release(this);
}
public int Capacity()
{
return Buffer.Capacity;
}
public int Limit()
{
return (int)Buffer.Length;
}
public ByteBuffer Limit(long limit)
{
Buffer.SetLength(limit);
return this;
}
public int Position
{
get =>(int)Buffer.Position;
set => Buffer.Position = value;
}
public ByteBuffer Put(byte[] src)
{
_writer.Write(src);
return this;
}
public ByteBuffer Put(byte b)
{
_writer.Write(b);
return this;
}
public byte Get()
{
return _reader.ReadByte();
}
public byte Get(long index)
{
var position = Buffer.Position;
Buffer.Position = index;
var result = _reader.ReadByte();
Buffer.Position = position;
return result;
}
public ByteBuffer Put(long index, byte b)
{
var position = Buffer.Position;
Buffer.Position = index;
_writer.Write(b);
Buffer.Position = position;
return this;
}
public ByteBuffer Put(byte[] src, int offset, int length)
{
_writer.Write(src, offset, length);
return this;
}
public ByteBuffer Get(byte[] dst)
{
_reader.Read(dst, 0, dst.Length);
return this;
}
public ByteBuffer Get(byte[] dst, int offset, int length)
{
_reader.Read(dst, offset, length);
return this;
}
public ByteBuffer PutChar(char value)
{
_writer.Write(Convert.ToByte(value));
return this;
}
public ByteBuffer PutChar(long index, char value)
{
var position = Buffer.Position;
Buffer.Position = index;
_writer.Write(value);
Buffer.Position = position;
return this;
}
public char GetChar()
{
return _reader.ReadChar();
}
public char GetChar(long index)
{
var position = Buffer.Position;
Buffer.Position = index;
var result = _reader.ReadChar();
Buffer.Position = position;
return result;
}
public ByteBuffer PutInt(int value)
{
_writer.Write(value);
return this;
}
public ByteBuffer PutIntBe(int value)
{
_writer.Write(BinaryPrimitives.ReverseEndianness(value));
return this;
}
public ByteBuffer PutInt(long index, int value)
{
var position = Buffer.Position;
Buffer.Position = index;
_writer.Write(value);
Buffer.Position = position;
return this;
}
public int GetInt()
{
return _reader.ReadInt32();
}
public int GetIntBe()
{
return BinaryPrimitives.ReadInt32BigEndian(_reader.ReadBytes(4).AsSpan());
}
public int GetInt(long index)
{
var position = Buffer.Position;
Buffer.Position = index;
var result = _reader.ReadInt32();
Buffer.Position = position;
return result;
}
public ByteBuffer PutLong(long value)
{
_writer.Write(value);
return this;
}
public ByteBuffer PutLongBe(long value)
{
_writer.Write(BinaryPrimitives.ReverseEndianness(value));
return this;
}
public ByteBuffer PutLong(long index, long value)
{
var position = Buffer.Position;
Buffer.Position = index;
_writer.Write(value);
Buffer.Position = position;
return this;
}
public ByteBuffer PutLongBe(long index, long value)
{
var position = Buffer.Position;
Buffer.Position = index;
_writer.Write(BinaryPrimitives.ReverseEndianness(value));
Buffer.Position = position;
return this;
}
public long GetLong()
{
return _reader.ReadInt64();
}
public long GetLongBe()
{
return BinaryPrimitives.ReadInt64BigEndian(_reader.ReadBytes(8).AsSpan());
}
public long GetLong(long index)
{
var position = Buffer.Position;
Buffer.Position = index;
var result = _reader.ReadInt64();
Buffer.Position = position;
return result;
}
public long GetLongBe(long index)
{
var position = Buffer.Position;
Buffer.Position = index;
var result = BinaryPrimitives.ReadInt64BigEndian(_reader.ReadBytes(8).AsSpan());
Buffer.Position = position;
return result;
}
public ByteBuffer PutDouble(double value)
{
_writer.Write(value);
return this;
}
public ByteBuffer PutDouble(long index, double value)
{
var position = Buffer.Position;
Buffer.Position = index;
_writer.Write(value);
Buffer.Position = position;
return this;
}
public double GetDouble()
{
return _reader.ReadDouble();
}
public double GetDouble(long index)
{
var position = Buffer.Position;
Buffer.Position = index;
var result = _reader.ReadDouble();
Buffer.Position = position;
return result;
}
public ByteBuffer PutInAll(long start, long end, byte value)
{
var position = Buffer.Position;
Buffer.Position = start;
for (var i = start; i < end; i++)
{
_writer.Write(value);
}
Buffer.Position = position;
return this;
}
public int Remaining()
{
return (int)(Buffer.Length - Buffer.Position);
}
public ByteBuffer Flip()
{
Buffer.SetLength(Buffer.Position);
Buffer.Position = 0;
return this;
}
public static ByteBuffer Demand(int size)
{
return ByteBufferPool.Instance.Demand(size);
}
public static void Release(ByteBuffer buffer)
{
ByteBufferPool.Instance.Release(buffer);
}
/// <summary>
/// Resets the buffer.
/// </summary>
public void ResetBuffer()
{
Position = 0;
}
/// <summary>
/// Gets buffer length.
/// </summary>
public int Length => (int)Buffer.Length;
/// <summary>
/// Gets this ByteBuffer as byte array.
/// </summary>
/// <returns>Byte array of this ByteBuffer.</returns>
public byte[] GetBulk()
{
return GetByteArray(0, Position);
}
/// <summary>
/// Returns true if buffer is empty.
/// </summary>
public bool IsEmpty => Position == 0;
/// <summary>
/// Creates new ByteBuffer from byte[].
/// </summary>
/// <param name="buffer">Array of bytes to wrap.</param>
/// <returns>New instance of ByteByffer.</returns>
public static ByteBuffer WrapBuffer(byte[] buffer)
{
var wrap = new MemoryStream(buffer);
return new ByteBuffer(wrap);
}
/// <summary>
/// Get part of ByteBuffer as array of bytes.
/// </summary>
/// <param name="start">Offset of part.</param>
/// <param name="length">Length of part.</param>
/// <returns>Byte array with part of ByteBuffer.</returns>
public byte[] GetSubArray(in int start, in int length)
{
return GetByteArray(start, length);
}
}
}