ModernUO/Projects/Server/Serialization/IGenericReader.cs
Kamron Batman e1e30998ba
fix: Adds ReadType/Write(Type) and improves type referencing (#1172)
## Changes
* Improves type hashing by introducing xxHash3 (64bit)
* Removes individual `tdb` files in favor of a single `SerializedTypes.db` file. This file is only used to identify a type that is being deserialized, which doesn't exist.
* Adds duplicate type alias detection
* Adds `AssemblyHandler.FindTypeByHash`

View changed files whitespaces: https://github.com/modernuo/ModernUO/pull/1172/files?diff=split&w=1

## SerializedTypes.db
The serialized types file is used to get back the original name of a type in case it no longer exists in code. This can easily be necessary if a class is renamed in code and no `TypeAlias` is provided.

### Format
byte[4] - version
byte[4] - count
--array--
byte[8] - xxHash
byte[1] - flag, 0 - null, 1 - not null
byte[n] - Full class name in UTF8

### Example
<img width="472" alt="SerializedTypes_Example" src="https://user-images.githubusercontent.com/3953314/195255429-31d24293-6bd1-419e-811b-07874dd0f78d.png">

## Benchmarks
Serialized 500 Type fields. The 8192bytes comes from the _ConcurrentQueue_ that would later be used for SerializedTypes.
Note that the queue is never cleared, so it's size grew considerably.
```cs
|               Method |     Mean |    Error |   StdDev | Allocated |
|--------------------- |---------:|---------:|---------:|----------:|
|      BenchmarkXXHash | 18.44 us | 0.278 us | 0.260 us |    8192 B |
| BenchmarkTypeStrings | 25.09 us | 0.292 us | 0.259 us |         - |
```

TODO:
* Add support in the Serialization Generator for `ReadType()` and `Write(Type)`
* Remove `SetTypeRef` from Serialization Generator
2022-10-11 22:17:22 -07:00

124 lines
3.8 KiB
C#

/*************************************************************************
* ModernUO *
* Copyright 2019-2022 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: IGenericReader.cs *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.IO;
using System.Net;
using Server.Collections;
namespace Server;
public interface IGenericReader
{
// Used to determine valid Entity deserialization
DateTime LastSerialized { get; init; }
string ReadString(bool intern = false);
long ReadLong();
ulong ReadULong();
int ReadInt();
uint ReadUInt();
short ReadShort();
ushort ReadUShort();
double ReadDouble();
float ReadFloat();
byte ReadByte();
sbyte ReadSByte();
bool ReadBool();
Serial ReadSerial();
Type ReadType();
DateTime ReadDateTime() => new(ReadLong(), DateTimeKind.Utc);
TimeSpan ReadTimeSpan() => new(ReadLong());
DateTime ReadDeltaTime()
{
return ReadLong() switch
{
long.MinValue => DateTime.MinValue,
long.MaxValue => DateTime.MaxValue,
var delta => new DateTime(delta + DateTime.UtcNow.Ticks, DateTimeKind.Utc)
};
}
decimal ReadDecimal() => new(stackalloc int[4] { ReadInt(), ReadInt(), ReadInt(), ReadInt() });
int ReadEncodedInt()
{
int v = 0, shift = 0;
byte b;
do
{
b = ReadByte();
v |= (b & 0x7F) << shift;
shift += 7;
}
while (b >= 0x80);
return v;
}
IPAddress ReadIPAddress()
{
byte length = ReadByte();
// Either 2 ushorts, or 8 ushorts
Span<byte> integer = stackalloc byte[length];
Read(integer);
return Utility.Intern(new IPAddress(integer));
}
Point3D ReadPoint3D() => new(ReadInt(), ReadInt(), ReadInt());
Point2D ReadPoint2D() => new(ReadInt(), ReadInt());
Rectangle2D ReadRect2D() => new(ReadPoint2D(), ReadPoint2D());
Rectangle3D ReadRect3D() => new(ReadPoint3D(), ReadPoint3D());
Map ReadMap() => Map.Maps[ReadByte()];
Race ReadRace() => Race.Races[ReadByte()];
int Read(Span<byte> buffer);
unsafe T ReadEnum<T>() where T : unmanaged, Enum
{
switch (sizeof(T))
{
case 1:
{
var num = ReadByte();
return *(T*)&num;
}
case 2:
{
var num = ReadShort();
return *(T*)&num;
}
case 4:
{
var num = ReadEncodedInt();
return *(T*)&num;
}
case 8:
{
var num = ReadLong();
return *(T*)&num;
}
}
return default;
}
Guid ReadGuid()
{
Span<byte> bytes = stackalloc byte[16];
Read(bytes);
return new Guid(bytes);
}
BitArray ReadBitArray();
long Seek(long offset, SeekOrigin origin);
}