134 lines
4.1 KiB
C#
134 lines
4.1 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Text;
|
|
|
|
using Best.HTTP.Shared.PlatformSupport.Memory;
|
|
|
|
namespace Best.HTTP.Shared.Databases.Utils
|
|
{
|
|
public static class StreamUtil
|
|
{
|
|
public static void WriteLengthPrefixedString(this Stream stream, string str)
|
|
{
|
|
if (str != null)
|
|
{
|
|
var byteCount = Encoding.UTF8.GetByteCount(str);
|
|
|
|
if (byteCount >= 1 << 16)
|
|
throw new ArgumentException($"byteCount({byteCount})");
|
|
|
|
stream.EncodeUnsignedVariableByteInteger((ulong)byteCount);
|
|
|
|
byte[] tmp = BufferPool.Get(byteCount, true);
|
|
|
|
Encoding.UTF8.GetBytes(str, 0, str.Length, tmp, 0);
|
|
stream.Write(tmp, 0, byteCount);
|
|
|
|
BufferPool.Release(tmp);
|
|
}
|
|
else
|
|
{
|
|
stream.WriteByte(0);
|
|
}
|
|
}
|
|
|
|
public static string ReadLengthPrefixedString(this Stream stream)
|
|
{
|
|
int strLength = (int)stream.DecodeUnsignedVariableByteInteger();
|
|
string result = null;
|
|
|
|
if (strLength != 0)
|
|
{
|
|
byte[] buffer = BufferPool.Get(strLength, true);
|
|
|
|
stream.Read(buffer, 0, strLength);
|
|
result = System.Text.Encoding.UTF8.GetString(buffer, 0, strLength);
|
|
|
|
BufferPool.Release(buffer);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public static void EncodeUnsignedVariableByteInteger(this Stream encodeTo, ulong value)
|
|
{
|
|
if (value < 0)
|
|
throw new NotSupportedException($"Can't encode negative value({value:N0})!");
|
|
|
|
byte encodedByte;
|
|
do
|
|
{
|
|
encodedByte = (byte)(value % 128);
|
|
value /= 128;
|
|
// if there are more data to encode, set the top bit of this byte
|
|
if (value > 0)
|
|
encodedByte = (byte)(encodedByte | 128);
|
|
|
|
encodeTo.WriteByte(encodedByte);
|
|
}
|
|
while (value > 0);
|
|
}
|
|
|
|
public static ulong DecodeUnsignedVariableByteInteger(this Stream decodeFrom)
|
|
{
|
|
ulong multiplier = 1;
|
|
ulong value = 0;
|
|
byte encodedByte = 0;
|
|
do
|
|
{
|
|
encodedByte = (byte)decodeFrom.ReadByte();
|
|
value += (ulong)((ulong)(encodedByte & 127) * multiplier);
|
|
multiplier *= 128;
|
|
} while ((encodedByte & 128) != 0);
|
|
|
|
return value;
|
|
}
|
|
|
|
public static void EncodeSignedVariableByteInteger(this Stream encodeTo, long value)
|
|
{
|
|
bool more = true;
|
|
|
|
while (more)
|
|
{
|
|
byte chunk = (byte)(value & 0x7fL); // extract a 7-bit chunk
|
|
value >>= 7;
|
|
|
|
bool signBitSet = (chunk & 0x40) != 0; // sign bit is the msb of a 7-bit byte, so 0x40
|
|
more = !((value == 0 && !signBitSet) || (value == -1 && signBitSet));
|
|
if (more) { chunk |= 0x80; } // set msb marker that more bytes are coming
|
|
|
|
encodeTo.WriteByte(chunk);
|
|
};
|
|
}
|
|
|
|
public static long DecodeSignedVariableByteInteger(this Stream stream)
|
|
{
|
|
long value = 0;
|
|
int shift = 0;
|
|
bool more = true;
|
|
bool signBitSet = false;
|
|
|
|
while (more)
|
|
{
|
|
var next = stream.ReadByte();
|
|
if (next < 0)
|
|
throw new InvalidOperationException("Unexpected end of stream");
|
|
|
|
byte b = (byte)next;
|
|
|
|
more = (b & 0x80) != 0; // extract msb
|
|
signBitSet = (b & 0x40) != 0; // sign bit is the msb of a 7-bit byte, so 0x40
|
|
|
|
long chunk = b & 0x7fL; // extract lower 7 bits
|
|
value |= chunk << shift;
|
|
shift += 7;
|
|
};
|
|
|
|
// extend the sign of shorter negative numbers
|
|
if (shift < (sizeof(long) * 8) && signBitSet) { value |= -1L << shift; }
|
|
|
|
return value;
|
|
}
|
|
}
|
|
}
|