WuhuIslandTesting/Library/PackageCache/com.unity.addressables@1.21.12/Runtime/ResourceManager/Util/BinaryStorageBuffer.cs

856 lines
33 KiB
C#
Raw Normal View History

2025-01-07 02:06:59 +01:00
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
namespace UnityEngine.ResourceManagement.Util
{
internal class BinaryStorageBuffer
{
class BuiltinTypesSerializer :
ISerializationAdapter<int>,
ISerializationAdapter<bool>,
ISerializationAdapter<long>,
ISerializationAdapter<string>,
ISerializationAdapter<Hash128>
{
public IEnumerable<ISerializationAdapter> Dependencies => null;
struct ObjectToStringRemap
{
public uint stringId;
public char separator;
}
public object Deserialize(Reader reader, Type t, uint offset)
{
if (offset == uint.MaxValue)
return null;
if (t == typeof(int)) return reader.ReadValue<int>(offset);
if (t == typeof(bool)) return reader.ReadValue<bool>(offset);
if (t == typeof(long)) return reader.ReadValue<long>(offset);
if (t == typeof(Hash128)) return reader.ReadValue<Hash128>(offset);
if (t == typeof(string))
{
var remap = reader.ReadValue<ObjectToStringRemap>(offset);
return reader.ReadString(remap.stringId, remap.separator, false);
}
return null;
}
char FindBestSeparator(string str, params char[] seps)
{
int bestCount = 0;
char bestSep = (char)0;
foreach (var s in seps)
{
var sepCount = str.Count(c => c == s);
if (sepCount > bestCount)
{
bestCount = sepCount;
bestSep = s;
}
}
var parts = str.Split(bestSep);
int validParts = 0;
foreach (var p in parts)
if (p.Length > 8)
validParts++;
if (validParts < 2)
bestSep = (char)0;
return bestSep;
}
public uint Serialize(Writer writer, object val)
{
if (val == null)
return uint.MaxValue;
var t = val.GetType();
if (t == typeof(int)) return writer.Write((int)val);
if (t == typeof(bool)) return writer.Write((bool)val);
if (t == typeof(long)) return writer.Write((long)val);
if (t == typeof(Hash128)) return writer.Write((Hash128)val);
if (t == typeof(string))
{
var str = val as string;
var bestSep = FindBestSeparator(str, '/', '.', '-', '_', '\\', ',');
return writer.Write(new ObjectToStringRemap { stringId = writer.WriteString((string)val), separator = bestSep });
}
return uint.MaxValue;
}
}
class TypeSerializer :
ISerializationAdapter<Type>
{
struct Data
{
public uint assemblyId;
public uint classId;
}
public IEnumerable<BinaryStorageBuffer.ISerializationAdapter> Dependencies => null;
public object Deserialize(Reader reader, Type type, uint offset)
{
try
{
var d = reader.ReadValue<Data>(offset);
var assemblyName = reader.ReadString(d.assemblyId, '.');
var className = reader.ReadString(d.classId, '.');
var assembly = Assembly.Load(assemblyName);
return assembly == null ? null : assembly.GetType(className);
}
catch (Exception e)
{
Debug.LogException(e);
return null;
}
}
public uint Serialize(Writer writer, object val)
{
if (val == null)
return uint.MaxValue;
var t = val as Type;
return writer.Write(new Data
{
assemblyId = writer.WriteString(t.Assembly.FullName, '.'),
classId = writer.WriteString(t.FullName, '.')
});
}
}
private unsafe static void ComputeHash(void* pData, ulong size, Hash128* hash)
{
if (pData == null || size == 0)
{
*hash = default;
return;
}
HashUnsafeUtilities.ComputeHash128(pData, size, hash);
}
static void AddSerializationAdapter(Dictionary<Type, ISerializationAdapter> serializationAdapters, ISerializationAdapter adapter, bool forceOverride = false)
{
bool added = false;
foreach (var i in adapter.GetType().GetInterfaces())
{
if (i.IsGenericType && typeof(ISerializationAdapter).IsAssignableFrom(i))
{
var aType = i.GenericTypeArguments[0];
if (serializationAdapters.ContainsKey(aType))
{
if (forceOverride)
{
var prevAdapter = serializationAdapters[aType];
serializationAdapters.Remove(aType);
// serializationAdapters.Add(aType, adapter);
serializationAdapters[aType] = adapter;
added = true;
Debug.Log($"Replacing adapter for type {aType}: {prevAdapter} -> {adapter}");
}
else
{
Debug.Log($"Failed to register adapter for type {aType}: {adapter}, {serializationAdapters[aType]} is already registered.");
}
}
else
{
serializationAdapters[aType] = adapter;
added = true;
}
}
}
if (added)
{
var deps = adapter.Dependencies;
if (deps != null)
foreach (var d in deps)
AddSerializationAdapter(serializationAdapters, d);
}
}
static bool GetSerializationAdapter(Dictionary<Type, ISerializationAdapter> serializationAdapters, Type t, out ISerializationAdapter adapter)
{
if (!serializationAdapters.TryGetValue(t, out adapter))
{
foreach (var k in serializationAdapters)
if (k.Key.IsAssignableFrom(t))
return (adapter = k.Value) != null;
Debug.LogError($"Unable to find serialization adapter for type {t}.");
}
return adapter != null;
}
const uint kUnicodeStringFlag = 0x80000000;
const uint kDynamicStringFlag = 0x40000000;
const uint kClearFlagsMask = 0x3fffffff;
struct DynamicString
{
public uint stringId;
public uint nextId;
}
struct ObjectTypeData
{
public uint typeId;
public uint objectId;
}
public interface ISerializationAdapter
{
IEnumerable<ISerializationAdapter> Dependencies { get; }
uint Serialize(Writer writer, object val);
object Deserialize(Reader reader, Type t, uint offset);
}
public interface ISerializationAdapter<T> : ISerializationAdapter
{
}
public unsafe class Reader
{
byte[] m_Buffer;
Dictionary<Type, ISerializationAdapter> m_Adapters;
LRUCache<uint, object> m_Cache;
StringBuilder stringBuilder;
private void Init(byte[] data, int maxCachedObjects, params ISerializationAdapter[] adapters)
{
m_Buffer = data;
stringBuilder = new StringBuilder(1024);
m_Cache = new LRUCache<uint, object>(maxCachedObjects);
m_Adapters = new Dictionary<Type, ISerializationAdapter>();
foreach (var a in adapters)
BinaryStorageBuffer.AddSerializationAdapter(m_Adapters, a);
BinaryStorageBuffer.AddSerializationAdapter(m_Adapters, new TypeSerializer());
BinaryStorageBuffer.AddSerializationAdapter(m_Adapters, new BuiltinTypesSerializer());
}
public void AddSerializationAdapter(ISerializationAdapter a)
{
BinaryStorageBuffer.AddSerializationAdapter(m_Adapters, a);
}
public Reader(byte[] data, int maxCachedObjects = 1024, params ISerializationAdapter[] adapters)
{
Init(data, maxCachedObjects, adapters);
}
public Reader(Stream inputStream, uint bufferSize, int maxCachedObjects, params ISerializationAdapter[] adapters)
{
var data = new byte[bufferSize == 0 ? inputStream.Length : bufferSize];
inputStream.Read(data, 0, data.Length);
Init(data, maxCachedObjects, adapters);
}
bool TryGetCachedValue<T>(uint offset, out T val)
{
if(m_Cache.TryGet(offset, out var obj))
{
val = (T)obj;
return true;
}
val = default;
return false;
}
public object[] ReadObjectArray(uint id, bool cacheValues = true)
{
if (id == uint.MaxValue)
return null;
var ids = ReadValueArray<uint>(id, cacheValues);
var objs = new object[ids.Length];
for (int i = 0; i < ids.Length; i++)
objs[i] = ReadObject(ids[i], cacheValues);
return objs;
}
public object[] ReadObjectArray(Type t, uint id, bool cacheValues = true)
{
if (id == uint.MaxValue)
return null;
var ids = ReadValueArray<uint>(id, cacheValues);
var objs = new object[ids.Length];
for (int i = 0; i < ids.Length; i++)
objs[i] = ReadObject(t, ids[i], cacheValues);
return objs;
}
public T[] ReadObjectArray<T>(uint id, bool cacheValues = true)
{
if (id == uint.MaxValue)
return null;
var ids = ReadValueArray<uint>(id, cacheValues);
var objs = new T[ids.Length];
for (int i = 0; i < ids.Length; i++)
objs[i] = ReadObject<T>(ids[i], cacheValues);
return objs;
}
public object ReadObject(uint id, bool cacheValue = true)
{
if (id == uint.MaxValue)
return null;
var td = ReadValue<ObjectTypeData>(id);
var type = ReadObject<Type>(td.typeId);
return ReadObject(type, td.objectId, cacheValue);
}
public T ReadObject<T>(uint offset, bool cacheValue = true) => (T)ReadObject(typeof(T), offset, cacheValue);
public object ReadObject(Type t, uint id, bool cacheValue = true)
{
if (id == uint.MaxValue)
return null;
if (TryGetCachedValue<object>(id, out var val))
return val;
if (!GetSerializationAdapter(m_Adapters, t, out var adapter))
return null;
object res = default;
try
{
res = adapter.Deserialize(this, t, id);
}
catch (Exception e)
{
Debug.LogException(e);
return null;
}
if (cacheValue && res != null)
m_Cache.TryAdd(id, res);
return res;
}
public T[] ReadValueArray<T>(uint id, bool cacheValue = true) where T : unmanaged
{
if (id == uint.MaxValue)
return null;
if (id - sizeof(uint) >= m_Buffer.Length)
throw new Exception($"Data offset {id} is out of bounds of buffer with length of {m_Buffer.Length}.");
fixed (byte* pData = &m_Buffer[id - sizeof(uint)])
{
if (TryGetCachedValue<T[]>(id, out var vals))
return vals;
uint size = 0;
UnsafeUtility.MemCpy(&size, pData, sizeof(uint));
if((id + size) > m_Buffer.Length)
throw new Exception($"Data size {size} is out of bounds of buffer with length of {m_Buffer.Length}.");
var elCount = size / sizeof(T);
var valsT = new T[elCount];
fixed (T* pVals = valsT)
UnsafeUtility.MemCpy(pVals, &pData[sizeof(uint)], size);
if(cacheValue)
m_Cache.TryAdd(id, valsT);
return valsT;
}
}
public T ReadValue<T>(uint id) where T : unmanaged
{
if (id == uint.MaxValue)
return default;
if (id >= m_Buffer.Length)
throw new Exception($"Data offset {id} is out of bounds of buffer with length of {m_Buffer.Length}.");
fixed (byte *pData = m_Buffer)
return *(T*)&pData[id];
}
public string ReadString(uint id, char sep = (char)0, bool cacheValue = true)
{
if (id == uint.MaxValue)
return null;
if (sep == (char)0)
return ReadAutoEncodedString(id, cacheValue);
return ReadDynamicString(id, sep, cacheValue);
}
string ReadStringInternal(uint offset, Encoding enc, bool cacheValue = true)
{
if (offset - sizeof(uint) >= m_Buffer.Length)
throw new Exception($"Data offset {offset} is out of bounds of buffer with length of {m_Buffer.Length}.");
if (TryGetCachedValue<string>(offset, out var val))
return val;
fixed (byte* pData = m_Buffer)
{
var strDataLength = *(uint*)&pData[offset - sizeof(uint)];
if (offset + strDataLength > m_Buffer.Length)
throw new Exception($"Data offset {offset}, len {strDataLength} is out of bounds of buffer with length of {m_Buffer.Length}.");
var valStr = enc.GetString(&pData[offset], (int)strDataLength);
if(cacheValue)
m_Cache.TryAdd(offset, valStr);
return valStr;
}
}
string ReadAutoEncodedString(uint id, bool cacheValue)
{
if ((id & kUnicodeStringFlag) == kUnicodeStringFlag)
return ReadStringInternal((uint)(id & kClearFlagsMask), Encoding.Unicode, cacheValue);
return ReadStringInternal(id, Encoding.ASCII, cacheValue);
}
string ReadDynamicString(uint id, char sep, bool cacheValue)
{
if ((id & kDynamicStringFlag) == kDynamicStringFlag)
{
if (!TryGetCachedValue<string>(id, out var str))
{
var ds = ReadValue<DynamicString>((uint)(id & kClearFlagsMask));
stringBuilder.Append(ReadAutoEncodedString(ds.stringId, cacheValue));
while (ds.nextId != uint.MaxValue)
{
ds = ReadValue<DynamicString>(ds.nextId);
stringBuilder.Append(sep);
stringBuilder.Append(ReadAutoEncodedString(ds.stringId, cacheValue));
}
str = stringBuilder.ToString();
stringBuilder.Clear();
if (cacheValue)
m_Cache.TryAdd(id, str);
}
return str;
}
else
{
return ReadAutoEncodedString(id, cacheValue);
}
}
}
public unsafe class Writer
{
class Chunk
{
public uint position;
public byte[] data;
}
uint totalBytes;
uint defaulChunkSize;
List<Chunk> chunks;
Dictionary<Hash128, uint> existingValues;
Dictionary<Type, ISerializationAdapter> serializationAdapters;
public uint Length => totalBytes;
public Writer(int chunkSize = 1024*1024, params ISerializationAdapter[] adapters)
{
defaulChunkSize = (uint)(chunkSize > 0 ? chunkSize : 1024 * 1024);
existingValues = new Dictionary<Hash128, uint>();
chunks = new List<Chunk>(10);
chunks.Add(new Chunk { position = 0 });
serializationAdapters = new Dictionary<Type, ISerializationAdapter>();
AddSerializationAdapter(serializationAdapters, new TypeSerializer());
AddSerializationAdapter(serializationAdapters, new BuiltinTypesSerializer());
foreach (var a in adapters)
AddSerializationAdapter(serializationAdapters, a, true);
}
Chunk FindChunkWithSpace(uint length)
{
var chunk = chunks[chunks.Count - 1];
if (chunk.data == null)
chunk.data = new byte[length > defaulChunkSize ? length : defaulChunkSize];
if (length > chunk.data.Length - chunk.position)
{
chunk = new Chunk { position = 0, data = new byte[length > defaulChunkSize ? length : defaulChunkSize] };
chunks.Add(chunk);
}
return chunk;
}
uint WriteInternal(void* pData, uint dataSize, bool prefixSize)
{
Hash128 hash;
ComputeHash(pData, (ulong)dataSize, &hash);
if (existingValues.TryGetValue(hash, out var existingOffset))
return existingOffset;
var addedBytes = prefixSize ? dataSize + sizeof(uint) : dataSize;
var chunk = FindChunkWithSpace(addedBytes);
fixed (byte* pChunkData = &chunk.data[chunk.position])
{
var id = totalBytes;
if (prefixSize)
{
UnsafeUtility.MemCpy(pChunkData, &dataSize, sizeof(uint));
if(dataSize > 0)
UnsafeUtility.MemCpy(&pChunkData[sizeof(uint)], pData, dataSize);
id += sizeof(uint);
}
else
{
if (dataSize == 0)
return uint.MaxValue;
UnsafeUtility.MemCpy(pChunkData, pData, dataSize);
}
totalBytes += addedBytes;
chunk.position += addedBytes;
existingValues[hash] = id;
return id;
}
}
uint ReserveInternal(uint dataSize, bool prefixSize)
{
//reserved data cannot reuse previously hashed values, but it can be reused for future writes
var addedBytes = prefixSize ? dataSize + sizeof(uint) : dataSize;
var chunk = FindChunkWithSpace(addedBytes);
totalBytes += addedBytes;
chunk.position += addedBytes;
return totalBytes - dataSize;
}
void WriteInternal(uint id, void* pData, uint dataSize, bool prefixSize)
{
//reserved data cannot reuse previously hashed values, but it can be reused for future writes
var addedBytes = prefixSize ? dataSize + sizeof(uint) : dataSize;
Hash128 hash;
ComputeHash(pData, (ulong)dataSize, &hash);
existingValues[hash] = id;
var chunkOffset = id;
foreach (var c in chunks)
{
if (chunkOffset < c.position)
{
fixed (byte* pChunkData = c.data)
{
if (prefixSize)
UnsafeUtility.MemCpy(&pChunkData[chunkOffset - sizeof(uint)], &dataSize, sizeof(uint));
UnsafeUtility.MemCpy(&pChunkData[chunkOffset], pData, dataSize);
return;
}
}
chunkOffset -= c.position;
}
}
public uint Reserve<T>() where T : unmanaged => ReserveInternal((uint)sizeof(T), false);
public uint Write<T>(in T val) where T : unmanaged
{
fixed (T* pData = &val)
return WriteInternal(pData, (uint)sizeof(T), false);
}
public uint Write<T>(T val) where T : unmanaged
{
return WriteInternal(&val, (uint)sizeof(T), false);
}
public uint Write<T>(uint offset, in T val) where T : unmanaged
{
fixed (T* pData = &val)
WriteInternal(offset, pData, (uint)sizeof(T), false);
return offset;
}
public uint Write<T>(uint offset, T val) where T : unmanaged
{
WriteInternal(offset, &val, (uint)sizeof(T), false);
return offset;
}
public uint Reserve<T>(uint count) where T : unmanaged => ReserveInternal((uint)sizeof(T) * count, true);
public uint Write<T>(T[] values, bool hashElements = true) where T : unmanaged
{
fixed (T* pData = values)
{
uint size = (uint)(values.Length * sizeof(T));
Hash128 hash;
ComputeHash(pData, (ulong)size, &hash);
if (existingValues.TryGetValue(hash, out var existingOffset))
return existingOffset;
var chunk = FindChunkWithSpace(size + sizeof(uint));
fixed (byte* pChunkData = &chunk.data[chunk.position])
{
var id = totalBytes + sizeof(uint);
UnsafeUtility.MemCpy(pChunkData, &size, sizeof(uint));
UnsafeUtility.MemCpy(&pChunkData[sizeof(uint)], pData, size);
var addedBytes = size + sizeof(uint);
totalBytes += addedBytes;
chunk.position += addedBytes;
existingValues[hash] = id;
if (hashElements && sizeof(T) > sizeof(uint))
{
for (int i = 0; i < values.Length; i++)
{
hash = default;
ComputeHash(&pData[i], (ulong)sizeof(T), &hash);
existingValues[hash] = id + (uint)(i * sizeof(T));
}
}
return id;
}
}
}
public uint Write<T>(uint offset, T[] values, bool hashElements = true) where T : unmanaged
{
var dataSize = (uint)(values.Length * sizeof(T));
var chunkOffset = offset;
fixed (T* pValues = values)
{
foreach (var c in chunks)
{
if (chunkOffset < c.position)
{
fixed (byte* pChunkData = c.data)
{
UnsafeUtility.MemCpy(&pChunkData[chunkOffset - sizeof(uint)], &dataSize, sizeof(uint));
UnsafeUtility.MemCpy(&pChunkData[chunkOffset], pValues, dataSize);
if (hashElements && sizeof(T) > sizeof(uint))
{
for (int i = 0; i < values.Length; i++)
{
Hash128 hash;
var v = values[i];
ComputeHash(&v, (ulong)sizeof(T), &hash);
existingValues[hash] = offset + (uint)i * (uint)sizeof(T);
}
}
return offset;
}
}
chunkOffset -= c.position;
}
}
return uint.MaxValue;
}
public uint WriteObjects<T>(IEnumerable<T> objs, bool serizalizeTypeData)
{
if (objs == null)
return uint.MaxValue;
var count = objs.Count();
var ids = new uint[count];
var index = 0;
foreach (var o in objs)
ids[index++] = WriteObject(o, serizalizeTypeData);
return Write(ids);
}
public uint WriteObject(object obj, bool serializeTypeData)
{
if (obj == null)
return uint.MaxValue;
var objType = obj.GetType();
if (!GetSerializationAdapter(serializationAdapters, objType, out var adapter))
return uint.MaxValue;
var id = adapter.Serialize(this, obj);
if (serializeTypeData)
id = Write(new ObjectTypeData { typeId = WriteObject(objType, false), objectId = id });
return id;
}
public uint WriteString(string str, char sep = (char)0)
{
if (str == null)
return uint.MaxValue;
return sep == (char)0 ? WriteAutoEncodedString(str) : WriteDynamicString(str, sep);
}
uint WriteStringInternal(string val, Encoding enc)
{
if (val == null)
return uint.MaxValue;
byte[] tmp = enc.GetBytes(val);
fixed (byte* pBytes = tmp)
return WriteInternal(pBytes, (uint)tmp.Length, true);
}
public byte[] SerializeToByteArray()
{
var data = new byte[totalBytes];
fixed (byte* pData = data)
{
uint offset = 0;
foreach (var c in chunks)
{
fixed (byte* pChunk = c.data)
UnsafeUtility.MemCpy(&pData[offset], pChunk, c.position);
offset += c.position;
}
}
return data;
}
public uint SerializeToStream(Stream str)
{
foreach (var c in chunks)
str.Write(c.data, 0, (int)c.position);
return totalBytes;
}
static bool IsUnicode(string str)
{
for (int i = 0; i < str.Length; i++)
if (str[i] > 255)
return true;
return false;
}
uint WriteAutoEncodedString(string str)
{
if (str == null)
return uint.MaxValue;
if (IsUnicode(str))
return WriteUnicodeString(str);
else
return WriteStringInternal(str, Encoding.ASCII);
}
uint WriteUnicodeString(string str)
{
var id = WriteStringInternal(str, Encoding.Unicode);
return (kUnicodeStringFlag | id);
}
static uint ComputeStringSize(string str, out bool isUnicode)
{
if (isUnicode = IsUnicode(str))
return (uint)Encoding.Unicode.GetByteCount(str);
return (uint)Encoding.ASCII.GetByteCount(str);
}
uint RecurseDynamicStringParts(StringParts[] parts, int index, char sep, uint minSize)
{
while (index < parts.Length - 1)
{
var currPartSize = parts[index].dataSize;
var nextPartSize = parts[index + 1].dataSize;
if (currPartSize < minSize || nextPartSize < minSize)
{
parts[index + 1].str = $"{parts[index].str}{sep}{parts[index + 1].str}";
index++;
}
else
{
break;
}
}
var strId = parts[index].isUnicode ? WriteUnicodeString(parts[index].str) : WriteStringInternal(parts[index].str, Encoding.ASCII);
var nxtId = (index < parts.Length - 1 ? RecurseDynamicStringParts(parts, index + 1, sep, minSize) : uint.MaxValue);
var id = Write(new DynamicString { stringId = strId, nextId = nxtId });
return id;
}
struct StringParts
{
public string str;
public uint dataSize;
public bool isUnicode;
}
unsafe uint WriteDynamicString(string str, char sep)
{
if (str == null)
return uint.MaxValue;
var minSize = (uint)sizeof(DynamicString);
var split = str.Split(sep);
var parts = new StringParts[split.Length];
for (int i = 0; i < parts.Length; i++)
{
var partSize = ComputeStringSize(split[i], out var isUnicode);
parts[i] = new StringParts { str = split[i], dataSize = partSize, isUnicode = isUnicode };
}
if (parts.Length < 2 || (parts.Length == 2 && (parts[0].dataSize + parts[1].dataSize) < minSize))
{
return WriteAutoEncodedString(str);
}
else
{
return (kDynamicStringFlag | RecurseDynamicStringParts(parts, 0, sep, minSize));
}
}
}
}
internal struct LRUCache<TKey, TValue> where TKey : IEquatable<TKey>
{
public struct Entry : IEquatable<Entry>
{
public LinkedListNode<TKey> lruNode;
public TValue Value;
public bool Equals(Entry other) => Value.Equals(other);
public override int GetHashCode() => Value.GetHashCode();
}
int entryLimit;
Dictionary<TKey, Entry> cache;
LinkedList<TKey> lru;
public LRUCache(int limit)
{
entryLimit = limit;
cache = new Dictionary<TKey, Entry>();
lru = new LinkedList<TKey>();
}
public bool TryAdd(TKey id, TValue obj)
{
if (obj == null || entryLimit <= 0)
return false;
cache.Add(id, new Entry { Value = obj, lruNode = lru.AddFirst(id) });
while (lru.Count > entryLimit)
{
cache.Remove(lru.Last.Value);
lru.RemoveLast();
}
return true;
}
public bool TryGet(TKey offset, out TValue val)
{
if (cache.TryGetValue(offset, out var entry))
{
val = entry.Value;
if (entry.lruNode.Previous != null)
{
lru.Remove(entry.lruNode);
lru.AddFirst(entry.lruNode);
}
return true;
}
val = default;
return false;
}
}
}