Terraria v1.4.4.9
Terraria source code documentation
Loading...
Searching...
No Matches
WebSocketInflater.cs
Go to the documentation of this file.
4
6
7internal sealed class WebSocketInflater : IDisposable
8{
9 private readonly int _windowBits;
10
12
13 private readonly bool _persisted;
14
15 private byte? _remainingByte;
16
17 private bool _endOfMessage;
18
19 private byte[] _buffer;
20
21 private int _position;
22
23 private int _available;
24
25 internal static ReadOnlySpan<byte> FlushMarker => new byte[4] { 0, 0, 255, 255 };
26
28
30
31 internal WebSocketInflater(int windowBits, bool persisted)
32 {
33 _windowBits = -windowBits;
34 _persisted = persisted;
35 }
36
37 public void Dispose()
38 {
39 if (_stream != null)
40 {
41 _stream.Dispose();
42 _stream = null;
43 }
45 }
46
47 public void Prepare(long payloadLength, int userBufferLength)
48 {
49 if (_buffer != null)
50 {
51 _buffer.AsSpan(_position, _available).CopyTo(_buffer);
52 _position = 0;
53 }
54 else
55 {
56 _buffer = ArrayPool<byte>.Shared.Rent((int)Math.Min(userBufferLength, payloadLength));
57 }
58 }
59
60 public void AddBytes(int totalBytesReceived, bool endOfMessage)
61 {
62 _available += totalBytesReceived;
63 _endOfMessage = endOfMessage;
64 if (!endOfMessage)
65 {
66 return;
67 }
68 if (_buffer == null)
69 {
71 _available = 4;
72 FlushMarker.CopyTo(_buffer);
73 return;
74 }
75 if (_buffer.Length < _available + 4)
76 {
77 byte[] array = ArrayPool<byte>.Shared.Rent(_available + 4);
78 _buffer.AsSpan(0, _available).CopyTo(array);
79 byte[] buffer = _buffer;
80 _buffer = array;
82 }
83 FlushMarker.CopyTo(_buffer.AsSpan(_available));
84 _available += 4;
85 }
86
87 public unsafe bool Inflate(Span<byte> output, out int written)
88 {
89 if (_stream == null)
90 {
92 }
93 if (_available > 0 && output.Length > 0)
94 {
95 int num;
96 fixed (byte* ptr = _buffer)
97 {
98 _stream.NextIn = (IntPtr)(ptr + _position);
99 _stream.AvailIn = (uint)_available;
100 written = Inflate(_stream, output, ZLibNative.FlushCode.NoFlush);
101 num = _available - (int)_stream.AvailIn;
102 }
103 _position += num;
104 _available -= num;
105 }
106 else
107 {
108 written = 0;
109 }
110 if (_available == 0)
111 {
113 if (!_endOfMessage)
114 {
115 return true;
116 }
117 return Finish(output, ref written);
118 }
119 return false;
120 }
121
122 private bool Finish(Span<byte> output, ref int written)
123 {
124 byte? remainingByte = _remainingByte;
125 if (remainingByte.HasValue)
126 {
127 if (output.Length == written)
128 {
129 return false;
130 }
131 output[written] = _remainingByte.GetValueOrDefault();
132 _remainingByte = null;
133 written++;
134 }
135 if (output.Length > written)
136 {
137 int num = written;
139 Span<byte> span = output;
140 written = num + Inflate(stream, span[written..], ZLibNative.FlushCode.SyncFlush);
141 }
142 if (written < output.Length || IsFinished(_stream, out _remainingByte))
143 {
144 if (!_persisted)
145 {
146 _stream.Dispose();
147 _stream = null;
148 }
149 return true;
150 }
151 return false;
152 }
153
154 private void ReleaseBuffer()
155 {
156 byte[] buffer = _buffer;
157 if (buffer != null)
158 {
159 _buffer = null;
160 _available = 0;
161 _position = 0;
163 }
164 }
165
166 private unsafe static bool IsFinished(ZLibNative.ZLibStreamHandle stream, out byte? remainingByte)
167 {
168 Unsafe.SkipInit(out byte value);
169 if (Inflate(stream, new Span<byte>(&value, 1), ZLibNative.FlushCode.SyncFlush) == 0)
170 {
171 remainingByte = null;
172 return true;
173 }
174 remainingByte = value;
175 return false;
176 }
177
179 {
180 ZLibNative.ErrorCode errorCode;
181 fixed (byte* ptr = destination)
182 {
183 stream.NextOut = (IntPtr)ptr;
184 stream.AvailOut = (uint)destination.Length;
185 errorCode = stream.Inflate(flushCode);
186 if (errorCode == ZLibNative.ErrorCode.Ok || errorCode == ZLibNative.ErrorCode.StreamEnd || errorCode == ZLibNative.ErrorCode.BufError)
187 {
188 return destination.Length - (int)stream.AvailOut;
189 }
190 }
191 throw new WebSocketException(errorCode switch
192 {
193 ZLibNative.ErrorCode.MemError => System.SR.ZLibErrorNotEnoughMemory,
194 ZLibNative.ErrorCode.DataError => System.SR.ZLibUnsupportedCompression,
195 ZLibNative.ErrorCode.StreamError => System.SR.ZLibErrorInconsistentStream,
196 _ => string.Format(System.SR.ZLibErrorUnexpected, (int)errorCode),
197 });
198 }
199
201 {
202 ZLibNative.ErrorCode errorCode;
203 ZLibNative.ZLibStreamHandle zLibStreamHandle;
204 try
205 {
206 errorCode = ZLibNative.CreateZLibStreamForInflate(out zLibStreamHandle, _windowBits);
207 }
208 catch (Exception innerException)
209 {
210 throw new WebSocketException(System.SR.ZLibErrorDLLLoadError, innerException);
211 }
212 if (errorCode == ZLibNative.ErrorCode.Ok)
213 {
214 return zLibStreamHandle;
215 }
216 zLibStreamHandle.Dispose();
217 string message = ((errorCode == ZLibNative.ErrorCode.MemError) ? System.SR.ZLibErrorNotEnoughMemory : string.Format(System.SR.ZLibErrorUnexpected, (int)errorCode));
218 throw new WebSocketException(message);
219 }
220}
static ArrayPool< T > Shared
Definition ArrayPool.cs:7
static ErrorCode CreateZLibStreamForInflate(out ZLibStreamHandle zLibStreamHandle, int windowBits)
static byte Min(byte val1, byte val2)
Definition Math.cs:912
void AddBytes(int totalBytesReceived, bool endOfMessage)
static unsafe int Inflate(ZLibNative.ZLibStreamHandle stream, Span< byte > destination, ZLibNative.FlushCode flushCode)
unsafe bool Inflate(Span< byte > output, out int written)
void Prepare(long payloadLength, int userBufferLength)
bool Finish(Span< byte > output, ref int written)
static unsafe bool IsFinished(ZLibNative.ZLibStreamHandle stream, out byte? remainingByte)
static string ZLibErrorNotEnoughMemory
Definition SR.cs:46
static string ZLibUnsupportedCompression
Definition SR.cs:78
static string ZLibErrorDLLLoadError
Definition SR.cs:40
static string ZLibErrorInconsistentStream
Definition SR.cs:42
static string ZLibErrorUnexpected
Definition SR.cs:50
Definition SR.cs:7
int Length
Definition Span.cs:70