fix(houses): Converts house packets (1 of 2) (#502)

- [X] Adds `ToSpan()` for SpanWriter.
- [X] Moves house files to UOContent/Multis/Houses
- [X] Adds house packets

### SpanWriter.ToSpan()
`ToSpan()` returns an `SpanOwner` struct which allows access to the buffer as a Span by calling `Span`.

Example:
```cs
public SpanOwner CreatePacket()
{
  var writer = new SpanWriter(0x400);
  // write some stuff
  return writer.ToSpan();
}
```
In this example, we are creating a packet and returning the `SpanOwner` for caching. This means in the future we can do something like this:
```cs
ns.Send(_cachedPacket.Span);
```
Where `_cachedPacket` is the `SpanOwner`

Notes:
Do not use a SpanWriter, or attempt to dispose of it after calling `ToSpan()`. Doing so will probably cause an NPE.
This commit is contained in:
Kamron Batman 2021-02-13 09:18:12 -08:00 committed by GitHub
parent 952a3e0265
commit d070efeab3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 267 additions and 73 deletions

View file

@ -20,6 +20,7 @@ using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Toolkit.HighPerformance.Extensions;
using Server.Network;
using Server.Text;
@ -54,6 +55,45 @@ namespace System.Buffers
public Span<byte> RawBuffer => _buffer;
/**
* Converts the writer to a Span<byte> using a SpanOwner.
* If the buffer was stackalloc, it will be copied to a rented buffer.
* Otherwise the existing rented buffer is used.
*
* Note:
* Do not use the SpanWriter after calling this method.
* This method will effectively dispose of the SpanWriter and is therefore considered terminal.
*/
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public SpanOwner ToSpan()
{
var toReturn = _arrayToReturnToPool;
SpanOwner apo;
if (_position == 0)
{
apo = new SpanOwner(_position, Array.Empty<byte>());
if (toReturn != null)
{
ArrayPool<byte>.Shared.Return(toReturn);
}
}
else if (toReturn != null)
{
apo = new SpanOwner(_position, toReturn);
}
else
{
var buffer = ArrayPool<byte>.Shared.Rent(_position);
_buffer.CopyTo(buffer);
apo = new SpanOwner(_position, buffer);
}
this = default; // Don't allow two references to the same buffer
return apo;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public SpanWriter(Span<byte> initialBuffer, bool resize = false)
{
_resize = resize;
@ -63,6 +103,7 @@ namespace System.Buffers
_arrayToReturnToPool = null;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public SpanWriter(int initialCapacity, bool resize = false)
{
_resize = resize;
@ -218,6 +259,7 @@ namespace System.Buffers
Position += 8;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Write(ReadOnlySpan<byte> buffer)
{
var count = buffer.Length;
@ -331,6 +373,7 @@ namespace System.Buffers
Debug.Assert(
origin != SeekOrigin.End || offset >= -_buffer.Length,
"Attempting to seek to a negative position using SeekOrigin.End"
);
@ -379,5 +422,35 @@ namespace System.Buffers
ArrayPool<byte>.Shared.Return(toReturn);
}
}
public struct SpanOwner : IDisposable
{
private readonly int _length;
private readonly byte[] _arrayToReturnToPool;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal SpanOwner(int length, byte[] buffer)
{
_length = length;
_arrayToReturnToPool = buffer;
}
public Span<byte> Span
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => MemoryMarshal.CreateSpan(ref _arrayToReturnToPool.DangerousGetReference(), _length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
byte[] toReturn = _arrayToReturnToPool;
this = default;
if (_length > 0)
{
ArrayPool<byte>.Shared.Return(toReturn);
}
}
}
}
}