using System; using System.IO; using System.Text; using System.Runtime.CompilerServices; namespace CompressSave.Wrapper; public unsafe class BufferWriter : BinaryWriter { private ByteSpan CurrentBuffer => _doubleBuffer.WriteBuffer; private readonly DoubleBuffer _doubleBuffer; private readonly Encoding _encoding; private readonly int _maxBytesPerChar; private byte[] Buffer => CurrentBuffer.Buffer; private long SuplusCapacity => _endPos - _curPos; private long _swapedBytes; public long WriteSum => _swapedBytes + _curPos - _startPos; public override Stream BaseStream => _baseStream; public override void Write(char[] chars, int index, int count) { if (chars == null) { throw new ArgumentNullException(nameof(chars)); } byte[] bytes = _encoding.GetBytes(chars, index, count); Write(bytes); } private byte* _curPos; private byte* _endPos; private byte* _startPos; private readonly Stream _baseStream; public BufferWriter(DoubleBuffer doubleBuffer, CompressionStream outStream) : this(doubleBuffer, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true), outStream) { } private BufferWriter(DoubleBuffer buffer , UTF8Encoding encoding, CompressionStream outStream) : base(Stream.Null, encoding) { _baseStream = outStream; _swapedBytes = 0; _doubleBuffer = buffer; RefreshStatus(); _encoding = encoding; _maxBytesPerChar = _encoding.GetMaxByteCount(1); } private void SwapBuffer() { CurrentBuffer.Position = 0; CurrentBuffer.Length = (int)(_curPos - _startPos); _swapedBytes += CurrentBuffer.Length; _doubleBuffer.SwapBuffer(); RefreshStatus(); } private void RefreshStatus() { _startPos = (byte*)Unsafe.AsPointer(ref Buffer[0]); _curPos = _startPos; _endPos = (byte*)Unsafe.AsPointer(ref Buffer[Buffer.Length - 1]) + 1; } private void CheckCapacityAndSwap(int requiredCapacity) { if (SuplusCapacity < requiredCapacity) { SwapBuffer(); } } public override void Write(byte value) { CheckCapacityAndSwap(1); *(_curPos++) = value; } public override void Write(bool value) => Write((byte)(value ? 1 : 0)); protected override void Dispose(bool disposing) { if (disposing) { SwapBuffer(); } base.Dispose(disposing); } public override void Close() { Dispose(disposing: true); } public override void Flush() { SwapBuffer(); } public override long Seek(int offset, SeekOrigin origin) { throw new NotImplementedException(); } public override void Write(sbyte value) => Write((byte)value); public override void Write(byte[] buffer) => Write(buffer, 0, buffer.Length); public override void Write(byte[] buffer, int index, int count) { if (buffer == null) { throw new ArgumentNullException(nameof(buffer)); } fixed (byte* start = buffer) { byte* srcPos = start + index; while (SuplusCapacity <= count) { int dstSuplus = (int)SuplusCapacity; //Array.Copy(_buffer, index + writed, Buffer, Position, SuplusCapacity); Unsafe.CopyBlock(_curPos, srcPos, (uint)dstSuplus); count -= dstSuplus; srcPos += dstSuplus; _curPos = _endPos; SwapBuffer(); } Unsafe.CopyBlock(_curPos, srcPos, (uint)count); _curPos += count; } } public override void Write(char ch) { if (char.IsSurrogate(ch)) { throw new ArgumentException("Arg_SurrogatesNotAllowedAsSingleChar"); } CheckCapacityAndSwap(_maxBytesPerChar); _curPos += _encoding.GetBytes(&ch, 1, _curPos, (int)SuplusCapacity); } //slow public override void Write(char[] chars) { if (chars == null) { throw new ArgumentNullException(nameof(chars)); } byte[] bytes = _encoding.GetBytes(chars, 0, chars.Length); Write(bytes); } public override void Write(double value) { CheckCapacityAndSwap(8); ulong num = (ulong)(*(long*)(&value)); *(_curPos++) = (byte)num; *(_curPos++) = (byte)(num >> 8); *(_curPos++) = (byte)(num >> 16); *(_curPos++) = (byte)(num >> 24); *(_curPos++) = (byte)(num >> 32); *(_curPos++) = (byte)(num >> 40); *(_curPos++) = (byte)(num >> 48); *(_curPos++) = (byte)(num >> 56); } //slow public override void Write(decimal d) { CheckCapacityAndSwap(16); int[] bits = decimal.GetBits(d); Write(bits[0]); Write(bits[1]); Write(bits[2]); Write(bits[3]); } public override void Write(short value) { CheckCapacityAndSwap(2); *(_curPos++) = (byte)value; *(_curPos++) = (byte)(value >> 8); } public override void Write(ushort value) { CheckCapacityAndSwap(2); *(_curPos++) = (byte)value; *(_curPos++) = (byte)(value >> 8); } public override void Write(int value) { if (SuplusCapacity < 4) { SwapBuffer(); } *(_curPos++) = (byte)value; *(_curPos++) = (byte)(value >> 8); *(_curPos++) = (byte)(value >> 16); *(_curPos++) = (byte)(value >> 24); } public override void Write(uint value) { CheckCapacityAndSwap(4); *(_curPos++) = (byte)value; *(_curPos++) = (byte)(value >> 8); *(_curPos++) = (byte)(value >> 16); *(_curPos++) = (byte)(value >> 24); } public override void Write(long value) { CheckCapacityAndSwap(8); *(_curPos++) = (byte)value; *(_curPos++) = (byte)(value >> 8); *(_curPos++) = (byte)(value >> 16); *(_curPos++) = (byte)(value >> 24); *(_curPos++) = (byte)(value >> 32); *(_curPos++) = (byte)(value >> 40); *(_curPos++) = (byte)(value >> 48); *(_curPos++) = (byte)(value >> 56); } public override void Write(ulong value) { CheckCapacityAndSwap(8); *(_curPos++) = (byte)value; *(_curPos++) = (byte)(value >> 8); *(_curPos++) = (byte)(value >> 16); *(_curPos++) = (byte)(value >> 24); *(_curPos++) = (byte)(value >> 32); *(_curPos++) = (byte)(value >> 40); *(_curPos++) = (byte)(value >> 48); *(_curPos++) = (byte)(value >> 56); } public override void Write(float value) { CheckCapacityAndSwap(4); uint num = *(uint*)(&value); *(_curPos++) = (byte)num; *(_curPos++) = (byte)(num >> 8); *(_curPos++) = (byte)(num >> 16); *(_curPos++) = (byte)(num >> 24); } // Just use same mechanisum from `Write(char[] chars, int index, int count)` public override void Write(string value) { if (value == null) { throw new ArgumentNullException(nameof(value)); } byte[] bytes = _encoding.GetBytes(value); Write7BitEncodedInt(bytes.Length); Write(bytes); } private new void Write7BitEncodedInt(int value) { uint num; for (num = (uint)value; num >= 128; num >>= 7) { Write((byte)(num | 0x80)); } Write((byte)num); } }