3 using System.Collections.Generic;
5 using Microsoft.Xna.Framework;
80 public readonly record
struct Segment<T>(T Identifier, int Pointer) : INetSerializableStruct where T : struct;
84 private readonly IWriteMessage message;
85 private readonly List<Segment<T>> segments;
89 this.message = message;
90 this.PointerLocation = pointerLocation;
91 this.segments =
new List<Segment<T>>();
101 private void ThrowOnInvalidState()
103 if (segments.Count >= UInt16.MaxValue)
105 throw new InvalidOperationException($
"Too many segments in SegmentTable<{typeof(T).Name}>");
111 ThrowOnInvalidState();
112 segments.Add(
new Segment<T>(value, message.BitPosition - PointerLocation));
117 ThrowOnInvalidState();
118 int tablePosition = message.BitPosition;
122 message.WriteInt32(tablePosition - PointerLocation);
125 message.BitPosition = tablePosition;
126 message.WriteUInt16((UInt16)segments.Count);
127 foreach (var segment
in segments)
129 message.WriteNetSerializableStruct(segment);
136 private class SegmentReadMsg : IReadMessage
138 private readonly IReadMessage underlyingMsg;
139 private readonly IReadOnlyList<Segment<T>> segments;
140 private readonly
int segmentIndex;
141 private readonly
int offset;
142 private readonly
int lengthBits;
143 public SegmentReadMsg(IReadMessage underlyingMsg, IReadOnlyList<Segment<T>> segments,
int segmentIndex,
int offset,
int lengthBits)
145 this.underlyingMsg = underlyingMsg;
146 this.segments = segments;
147 this.segmentIndex = segmentIndex;
148 this.offset = offset;
149 this.lengthBits = lengthBits;
151 if (offset + lengthBits >= underlyingMsg.LengthBits)
154 $
"Segment table is corrupt, segment length is invalid: {offset} + {lengthBits} >= {underlyingMsg.LengthBits}");
160 if (BitPosition > lengthBits)
162 throw new Exception($
"Tried to read too much data from segment.");
166 private TRead Check<TRead>(TRead v)
172 public bool ReadBoolean() => Check(underlyingMsg.ReadBoolean());
174 public void ReadPadBits()
176 Check(); underlyingMsg.ReadPadBits();
179 public byte ReadByte() => Check(underlyingMsg.ReadByte());
181 public byte PeekByte() => Check(underlyingMsg.PeekByte());
183 public ushort ReadUInt16() => Check(underlyingMsg.ReadUInt16());
185 public short ReadInt16() => Check(underlyingMsg.ReadInt16());
187 public uint ReadUInt32() => Check(underlyingMsg.ReadUInt32());
189 public int ReadInt32() => Check(underlyingMsg.ReadInt32());
191 public ulong ReadUInt64() => Check(underlyingMsg.ReadUInt64());
193 public long ReadInt64() => Check(underlyingMsg.ReadInt64());
195 public float ReadSingle() => Check(underlyingMsg.ReadSingle());
197 public double ReadDouble() => Check(underlyingMsg.ReadDouble());
199 public uint ReadVariableUInt32() => Check(underlyingMsg.ReadVariableUInt32());
201 public string ReadString() => Check(underlyingMsg.ReadString());
203 public Identifier ReadIdentifier() => Check(underlyingMsg.ReadIdentifier());
205 public Color ReadColorR8G8B8() => Check(underlyingMsg.ReadColorR8G8B8());
207 public Color ReadColorR8G8B8A8() => Check(underlyingMsg.ReadColorR8G8B8A8());
209 public int ReadRangedInteger(
int min,
int max) => Check(underlyingMsg.ReadRangedInteger(min, max));
211 public float ReadRangedSingle(
float min,
float max,
int bitCount) => Check(underlyingMsg.ReadRangedSingle(min, max, bitCount));
213 public byte[] ReadBytes(
int numberOfBytes) => Check(underlyingMsg.ReadBytes(numberOfBytes));
215 public int BitPosition
217 get => underlyingMsg.BitPosition - offset;
218 set => Check(underlyingMsg.BitPosition = value + offset);
221 public int BytePosition => BitPosition / 8;
223 public byte[] Buffer => underlyingMsg.Buffer;
225 public int LengthBits
228 set =>
throw new InvalidOperationException($
"Cannot resize {nameof(SegmentReadMsg)}");
231 public int LengthBytes => lengthBits / 8;
233 public NetworkConnection Sender => underlyingMsg.Sender;
236 private readonly IReadMessage message;
237 private readonly List<Segment<T>> segments;
238 private readonly
int exitLocation;
240 private SegmentTableReader(IReadMessage message, List<Segment<T>> segments,
int pointerLocation,
int exitLocation)
242 this.message = message;
243 this.segments = segments;
244 this.PointerLocation = pointerLocation;
245 this.exitLocation = exitLocation;
248 public IReadOnlyList<Segment<T>>
Segments => segments;
250 public enum BreakSegmentReading
258 IReadMessage incMsg);
261 Segment<T> segmentWithError,
262 Segment<T>[] previousSegments,
263 Exception exceptionThrown);
270 int pointerLocation = msg.BitPosition;
271 int tablePointer = msg.ReadInt32();
272 int tableLocation = pointerLocation + tablePointer;
274 int returnPosition = msg.BitPosition;
277 var segments =
new List<Segment<T>>();
278 msg.BitPosition = tableLocation;
279 int numSegments = msg.ReadUInt16();
280 for (
int i = 0; i < numSegments; i++)
282 segments.Add(INetSerializableStruct.Read<Segment<T>>(msg));
286 int exitLocation = msg.BitPosition;
287 msg.BitPosition = returnPosition;
290 for (
int i = 0; i < segmentTable.Segments.Count; i++)
292 var segment = segmentTable.Segments[i];
293 msg.BitPosition = segmentTable.PointerLocation + segment.Pointer;
296 if (segmentDataReader(segment.Identifier,
new SegmentReadMsg(
300 offset: segmentTable.PointerLocation + segment.Pointer,
301 lengthBits: (i < segmentTable.Segments.Count - 1 ? segments[i + 1].Pointer : tablePointer) -
303 is BreakSegmentReading.Yes)
310 var prevSegments = segments.Take(i).ToArray();
311 if (exceptionHandler is not
null)
313 exceptionHandler(segment, prevSegments, e);
318 $
"Exception thrown while reading segment {segment.Identifier} at position {segment.Pointer}." +
319 (prevSegments.Any() ? $
" Previous segments: {string.Join(",
", prevSegments)}." :
""),
328 message.BitPosition = exitLocation;
static void Read(IReadMessage msg, SegmentDataReader segmentDataReader, ExceptionHandler? exceptionHandler=null)
readonly int PointerLocation
delegate BreakSegmentReading SegmentDataReader(T segmentHeader, IReadMessage incMsg)
delegate void ExceptionHandler(Segment< T > segmentWithError, Segment< T >[] previousSegments, Exception exceptionThrown)
IReadOnlyList< Segment< T > > Segments
void StartNewSegment(T value)
static SegmentTableWriter< T > StartWriting(IWriteMessage msg)
readonly int PointerLocation