WuhuIslandTesting/Library/PackageCache/com.unity.addressables@1.21.12/Tests/Editor/BinaryStorageBufferTests.cs
2025-01-07 02:06:59 +01:00

474 lines
21 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#if UNITY_2020_1_OR_NEWER
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.AddressableAssets.ResourceLocators;
using UnityEngine.ResourceManagement.Util;
using Debug = UnityEngine.Debug;
namespace UnityEditor.AddressableAssets.Tests
{
public class BinaryStorageBufferTests
{
[StructLayout(LayoutKind.Sequential)]
struct SimpleStruct : IEquatable<SimpleStruct>
{
public int intVal;
public float floatVal;
public byte byteVal;
public short shortVal;
public long longVal;
public char charVal;
public SimpleStruct(int multiple)
{
intVal = 8 * multiple;
floatVal = 3.14f * multiple;
byteVal = (byte)(16 * multiple);
shortVal = (short)(125 * multiple);
longVal = 10000000 * multiple;
charVal = 'u';
}
public bool Equals(SimpleStruct other)
{
return intVal.Equals(other.intVal) && floatVal.Equals(other.floatVal) && byteVal.Equals(other.byteVal) && shortVal.Equals(other.shortVal) && longVal.Equals(other.longVal) && charVal.Equals(other.charVal);
}
}
[Test]
public void TestValueTypes([Values(1024, 1024 * 1024)]int chunkSize, [Values(0, 10, 1024)]int cacheSize)
{
var wr = new BinaryStorageBuffer.Writer(chunkSize);
var intID = wr.Write(5);
var floatId = wr.Write(3.14f);
var boolId = wr.Write(true);
var structId1 = wr.Write(new SimpleStruct(1));
var structId2 = wr.Write(new SimpleStruct(2));
var bytes = wr.SerializeToByteArray();
var re = new BinaryStorageBuffer.Reader(bytes, cacheSize);
Assert.AreEqual(5, re.ReadValue<int>(intID));
Assert.AreEqual(true, re.ReadValue<bool>(boolId));
Assert.AreEqual(new SimpleStruct(1), re.ReadValue<SimpleStruct>(structId1));
Assert.AreEqual(new SimpleStruct(2), re.ReadValue<SimpleStruct>(structId2));
}
[Test]
public void TestValueTypesWithReserve([Values(1024, 1024 * 1024)]int chunkSize, [Values(0, 10, 1024)]int cacheSize)
{
var wr = new BinaryStorageBuffer.Writer(chunkSize);
var intID = wr.Write(wr.Reserve<int>(), 5);
var floatId = wr.Write(wr.Reserve<float>(), 3.14f);
var boolId = wr.Write(wr.Reserve<bool>(), true);
var structId1 = wr.Write(wr.Reserve<SimpleStruct>(), new SimpleStruct(1));
var structId2 = wr.Write(wr.Reserve<SimpleStruct>(), new SimpleStruct(2));
var bytes = wr.SerializeToByteArray();
var re = new BinaryStorageBuffer.Reader(bytes, cacheSize);
Assert.AreEqual(5, re.ReadValue<int>(intID));
Assert.AreEqual(true, re.ReadValue<bool>(boolId));
Assert.AreEqual(new SimpleStruct(1), re.ReadValue<SimpleStruct>(structId1));
Assert.AreEqual(new SimpleStruct(2), re.ReadValue<SimpleStruct>(structId2));
}
[Test]
public void TestValueTypesWithUnorderedReserve([Values(1024, 1024 * 1024)]int chunkSize, [Values(0, 10, 1024)]int cacheSize)
{
var wr = new BinaryStorageBuffer.Writer(chunkSize);
var intID = wr.Reserve<int>();
var floatId = wr.Reserve<float>();
var boolId = wr.Write(wr.Reserve<bool>(), true);
var structId1 = wr.Reserve<SimpleStruct>();
var structId2 = wr.Write(wr.Reserve<SimpleStruct>(), new SimpleStruct(2));
wr.Write(floatId, 3.14f);
wr.Write(intID, 5);
wr.Write(structId1, new SimpleStruct(1));
wr.Write(structId1, new SimpleStruct(1));
var bytes = wr.SerializeToByteArray();
var re = new BinaryStorageBuffer.Reader(bytes, cacheSize);
Assert.AreEqual(5, re.ReadValue<int>(intID));
Assert.AreEqual(true, re.ReadValue<bool>(boolId));
Assert.AreEqual(new SimpleStruct(1), re.ReadValue<SimpleStruct>(structId1));
Assert.AreEqual(new SimpleStruct(2), re.ReadValue<SimpleStruct>(structId2));
}
[Test]
public void TestValueTypeArrays([Values(1024, 1024 * 1024)]int chunkSize, [Values(0, 1, 32, 256, 1024)]int count, [Values(0, 10, 1024)]int cacheSize)
{
var array = new SimpleStruct[count];
var array2 = new SimpleStruct[count];
for (int i = 0; i < array.Length; i++)
{
array2[i] = new SimpleStruct(array2.Length - i);
array[i] = new SimpleStruct(i);
}
var wr = new BinaryStorageBuffer.Writer(chunkSize);
var arrayId2 = wr.Reserve<SimpleStruct>((uint)count);
var arrayId = wr.Write(array);
wr.Write(arrayId2, array2);
var re = new BinaryStorageBuffer.Reader(wr.SerializeToByteArray(), cacheSize);
var a1 = re.ReadValueArray<SimpleStruct>(arrayId);
var a2 = re.ReadValueArray<SimpleStruct>(arrayId2);
for (int i = 0; i < array.Length; i++)
{
Assert.AreEqual(array[i], a1[i]);
Assert.AreEqual(array2[i], a2[i]);
}
}
[Test]
public void TestValueTypeDeduplication([Values(1024, 1024 * 1024)]int chunkSize)
{
var wr = new BinaryStorageBuffer.Writer(chunkSize);
wr.Write(new SimpleStruct(1));
wr.Write(new SimpleStruct(2));
wr.Write(new SimpleStruct(3));
wr.Write(new SimpleStruct(4));
wr.Write(new SimpleStruct(5));
var size = wr.Length;
wr.Write(new SimpleStruct(5));
wr.Write(new SimpleStruct(4));
wr.Write(new SimpleStruct(3));
wr.Write(new SimpleStruct(2));
wr.Write(new SimpleStruct(1));
Assert.AreEqual(size, wr.Length);
}
[Test]
public void TestValueArrayTypeDeduplication([Values(1024, 1024 * 1024)]int chunkSize, [Values(0, 1, 32, 256, 1024)]int count)
{
var array = new SimpleStruct[count];
for (int i = 0; i < array.Length; i++)
array[i] = new SimpleStruct(i);
var wr = new BinaryStorageBuffer.Writer(chunkSize);
wr.Write(array);
var size = wr.Length;
for (int i = 0; i < array.Length; i++)
wr.Write(array[i]);
Assert.AreEqual(size, wr.Length);
}
const string ucSample = "Ё Ђ Ѓ Є Ѕ І Ї Ј Љ Њ Ћ Ќ Ў Џ А Б В Г Д Е Ж З И Й К Л М Н О П Р С Т У Ф Х Ц Ч Ш Щ Ъ Ы Ь Э Ю Я а б в г д е ж з и й к л м н о п р с т у ф х ц ч ш щ ъ ы ь э ю я ё ђ ѓ є ѕ і ї ј љ њ ћ ќ ў џ Ѡ ѡ Ѣ ѣ Ѥ ѥ Ѧ ѧ Ѩ ѩ Ѫ ѫ Ѭ ѭ Ѯ ѯ Ѱ ѱ Ѳ ѳ Ѵ ѵ Ѷ ѷ Ѹ ѹ Ѻ ѻ Ѽ ѽ Ѿ ѿ Ҁ ҁ ҂ ҃ ...";
string RandomText(int len, bool unicode, char sep)
{
var sb = new StringBuilder(len);
var appendCount = 0;
if (unicode)
{
for (int i = 0; i < len; i++)
{
if (appendCount++ > UnityEngine.Random.Range(10, 30))
{
sb.Append(sep);
appendCount = 0;
}
else
sb.Append(ucSample[UnityEngine.Random.Range(0, ucSample.Length)]);
}
}
else
{
for (int i = 0; i < len; i++)
{
if (appendCount++ > UnityEngine.Random.Range(10, 30))
{
sb.Append(sep);
appendCount = 0;
}
else
sb.Append((char)UnityEngine.Random.Range((int)'a', (int)'z'));
}
}
return sb.ToString();
}
[Test]
public void TestStringAsObject()
{
var txt = RandomText(1000, false, '/');
var objTxt = txt as object;
var wr = new BinaryStorageBuffer.Writer(1024, new ComplexObject.Serializer());
var headerOffset = wr.Reserve<DateTime>();
var reserve2 = wr.Reserve<DateTime>(100000);
wr.Write(new int[100000]);
for (int i = 0; i < 100000; i++)
wr.WriteString(txt);
//wr.WriteObject(new ComplexObject(i), true);
var str = wr.WriteObject(objTxt, true);
var bytes = wr.SerializeToByteArray();
var re = new BinaryStorageBuffer.Reader(wr.SerializeToByteArray(), 1024);
var rStr = re.ReadObject(str) as string;
//var rStr = re.ReadString(
Assert.AreEqual(txt, rStr);
}
[Test]
public void TestASCIIStrings([Values(1024, 1024 * 1024)]int chunkSize, [Values(0, 1, 10, 100, 1000, 5000)]int strLen, [Values(0, 10, 1024)]int cacheSize)
{
var txt = RandomText(strLen, false, '/');
var sep = (char)UnityEngine.Random.Range((int)'a', (int)'z');
var wr = new BinaryStorageBuffer.Writer(chunkSize);
var str = wr.WriteString(txt, sep);
var bytes = wr.SerializeToByteArray();
var re = new BinaryStorageBuffer.Reader(bytes, cacheSize);
var strRes = re.ReadString(str, sep);
Assert.AreEqual(txt, strRes);
}
[Test]
public void TestASCIIStringsDeduplication([Values(1024, 1024 * 1024)]int chunkSize, [Values(0, 1, 10, 100, 1000, 5000)]int strLen, [Values(0, 10, 1024)]int cacheSize)
{
var txt = RandomText(strLen, false, '/');
var sep = (char)UnityEngine.Random.Range((int)'a', (int)'z');
var wr = new BinaryStorageBuffer.Writer(chunkSize);
var str = wr.WriteString(txt, sep);
var size = wr.Length;
var str2 = wr.WriteString(txt, sep);
Assert.AreEqual(size, wr.Length);
Assert.AreEqual(str, str2);
var re = new BinaryStorageBuffer.Reader(wr.SerializeToByteArray(), cacheSize);
Assert.AreEqual(txt, re.ReadString(str, sep));
Assert.AreEqual(txt, re.ReadString(str2, sep));
}
[Test]
public void TestUnicodeStrings([Values(1024, 1024 * 1024)]int chunkSize, [Values(0, 1, 10, 100, 1000, 5000)]int strLen, [Values(0, 10, 1024)]int cacheSize)
{
var txt = RandomText(strLen, true, '/');
var sep = (char)UnityEngine.Random.Range((int)'a', (int)'z');
var wr = new BinaryStorageBuffer.Writer(chunkSize);
var str = wr.WriteString(txt, sep);
var bytes = wr.SerializeToByteArray();
var re = new BinaryStorageBuffer.Reader(bytes, cacheSize);
var strRes = re.ReadString(str, sep);
Assert.AreEqual(txt, strRes);
}
[Test]
public void TestStringExamples(
[Values(8, 256, 1024)]int chunkSize,
[Values(
null,
"",
"1",
"string",
"a/b/c/d/f/g/h/i/j/k",
"rootfolder1/rootfolder2/rootfolder3/long file name",
"rootfolder1/rootЁfolder2/rootfolder3_withЁ/long file name",
"a/b/wergwegbwethgrwtherth/c/e/ffdsfsrgwetghwthwrh/e/s/wergwethgwrthrewthwer",
"a/b/wergwegbwethgrwtherth/Ёc/e/ffdsfsЁrgwetghwthwrh/e/s/wergwethgwrthrewthweЁr"
)]string str, [Values(0, 10, 1024)]int cacheSize)
{
var wr = new BinaryStorageBuffer.Writer(chunkSize);
var id = wr.WriteString(str, '/');
var r = new BinaryStorageBuffer.Reader(wr.SerializeToByteArray(), cacheSize);
var str2 = r.ReadString(id, '/');
Assert.AreEqual(str, str2);
}
[Test]
public void PerfTestStringExamples()
{
var wr = new BinaryStorageBuffer.Writer(1024);
var unicodeStr = "unicode string = Ё";
var asciiStr = "ascii string";
int count = 10000;
var ids = new uint[count];
for (int i = 0; i < count; i++)
{
var sb = new StringBuilder(1000);
var partCount = UnityEngine.Random.Range(2, 10);
for (int j = 0; j < partCount; j++)
{
var v = UnityEngine.Random.Range(0, 10);
if (v > 4)
sb.Append($"{asciiStr} - {v}");
else
sb.Append($"{unicodeStr} - {v}");
sb.Append('/');
}
ids[i] = wr.WriteString(sb.ToString(), '/');
}
var r = new BinaryStorageBuffer.Reader(wr.SerializeToByteArray(), 1024);
var sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 100000; i++)
r.ReadString(ids[UnityEngine.Random.Range(0, ids.Length)], '/');
sw.Stop();
Debug.Log($"ReadString time: {sw.ElapsedMilliseconds}ms.");
}
class ComplexObject : IEquatable<ComplexObject>
{
public class ComplexSubClass : IEquatable<ComplexSubClass>
{
public string stringVal;
public float floatV;
public bool Equals(ComplexSubClass other) => stringVal.Equals(other.stringVal) && floatV.Equals(other.floatV);
}
public int intVal;
public string stringVal;
public ComplexSubClass sub;
public ComplexObject() { }
public ComplexObject(int seed)
{
UnityEngine.Random.InitState(seed);
intVal = UnityEngine.Random.Range(1, 1000);
stringVal = $"string value {UnityEngine.Random.Range(1, 1000)}";
sub = new ComplexSubClass { floatV = UnityEngine.Random.Range(.1f, 10000f), stringVal = $"sub string value {UnityEngine.Random.Range(10000, 100000)}" };
}
public bool Equals(ComplexObject other) => intVal.Equals(other.intVal) && stringVal.Equals(other.stringVal) && sub.Equals(other.sub);
public class Serializer : BinaryStorageBuffer.ISerializationAdapter<ComplexObject>
{
public IEnumerable<BinaryStorageBuffer.ISerializationAdapter> Dependencies => null;
struct Data
{
public struct Sub
{
public uint stringId;
public float floatVal;
}
public int intVal;
public uint stringId;
public uint subId;
}
public object Deserialize(BinaryStorageBuffer.Reader reader, Type type, uint offset)
{
var data = reader.ReadValue<Data>(offset);
var sub = reader.ReadValue<Data.Sub>(data.subId);
return new ComplexObject
{
intVal = data.intVal,
stringVal = reader.ReadString(data.stringId),
sub = new ComplexSubClass
{
floatV = sub.floatVal,
stringVal = reader.ReadString(sub.stringId)
}
};
}
public uint Serialize(BinaryStorageBuffer.Writer writer, object val)
{
var co = val as ComplexObject;
var id = writer.Reserve<Data>();
var data = new Data
{
intVal = co.intVal,
stringId = writer.WriteString(co.stringVal),
subId = writer.Write(new Data.Sub
{
floatVal = co.sub.floatV,
stringId = writer.WriteString(co.sub.stringVal)
})
};
return writer.Write(id, data);
}
}
}
[Test]
public void TestStringsAsObjects([Values("ascii string", "unicode string")] string expected, [Values(0, 10, 1024)]int cacheSize)
{
var wr = new BinaryStorageBuffer.Writer(1024);
var id = wr.WriteObject(expected, false);
var id2 = wr.WriteObject(expected, true);
var r = new BinaryStorageBuffer.Reader(wr.SerializeToByteArray(), cacheSize);
var str = r.ReadObject<string>(id);
Assert.AreEqual(expected, str);
var strObj = r.ReadObject(id2);
Assert.AreEqual(expected, strObj);
}
[Test]
public void TestMixedObjectArray([Values(0, 10, 1024)]int cacheSize)
{
var wr = new BinaryStorageBuffer.Writer(1024, new ComplexObject.Serializer());
var objs = new object[] { "string val", new ComplexObject(1) };
var id = wr.WriteObjects(objs, true);
var r = new BinaryStorageBuffer.Reader(wr.SerializeToByteArray(), cacheSize, new ComplexObject.Serializer());
var objs2 = r.ReadObjectArray(id);
for (int i = 0; i < objs.Length; i++)
Assert.AreEqual(objs[i], objs2[i]);
}
[Test]
public void TestComplexObjectDeduplication([Values(1024, 1024 * 1024)]int chunkSize, [Values(1, 32, 256, 1024)]int count, [Values(0, 10, 1024)]int cacheSize)
{
var wr = new BinaryStorageBuffer.Writer(chunkSize, new ComplexObject.Serializer());
var ids = new uint[count];
for (int i = 0; i < count; i++)
ids[i] = wr.WriteObject(new ComplexObject(i), false);
var size = wr.Length;
for (int i = 0; i < count; i++)
wr.WriteObject(new ComplexObject(i), false);
Assert.Less(wr.Length, size * 2);
var re = new BinaryStorageBuffer.Reader(wr.SerializeToByteArray(), cacheSize, new ComplexObject.Serializer());
for (int i = 0; i < ids.Length; i++)
Assert.AreEqual(new ComplexObject(i), re.ReadObject<ComplexObject>(ids[i]));
}
[Test]
public void TestComplexObjectArray([Values(1024, 1024 * 1024)]int chunkSize, [Values(1, 32, 256, 1024)]int count, [Values(0, 10, 1024)]int cacheSize)
{
var wr = new BinaryStorageBuffer.Writer(chunkSize, new ComplexObject.Serializer());
var objs = new ComplexObject[count];
for (int i = 0; i < count; i++)
objs[i] = new ComplexObject(i);
uint objArrayWithoutType = wr.WriteObjects(objs, false);
uint objArrayWithType = wr.WriteObjects(objs, true);
var re = new BinaryStorageBuffer.Reader(wr.SerializeToByteArray(), cacheSize, new ComplexObject.Serializer());
var typedObjs = re.ReadObjectArray<ComplexObject>(objArrayWithoutType);
for (int i = 0; i < count; i++)
Assert.AreEqual(objs[i], typedObjs[i]);
var untypedObjs = re.ReadObjectArray(objArrayWithType);
for (int i = 0; i < count; i++)
Assert.AreEqual(objs[i], untypedObjs[i]);
}
[Test]
public void TestManyComplexObjectsAndStrings([Values(1024, 1024 * 1024)]int chunkSize, [Values(1024, 10 * 1024, 100 * 1024)]int count, [Values(0, 10, 1024)]int cacheSize)
{
var wr = new BinaryStorageBuffer.Writer(chunkSize, new ComplexObject.Serializer());
var ids = new uint[count];
for (int i = 0; i < count; i++)
{
if (i % 2 == 0)
ids[i] = wr.WriteObject(new ComplexObject(i), true);
else
ids[i] = wr.WriteString($"very long start of string/a middle part that is also somewhat long/almost done.../this part is unique{i}...../this part has unicode characters, see ЁЁЁЁЁЁЁ", '/');
}
var r = new BinaryStorageBuffer.Reader(wr.SerializeToByteArray(), cacheSize, new ComplexObject.Serializer());
for (int i = 0; i < count; i++)
{
if (i % 2 == 0)
Assert.AreEqual(new ComplexObject(i), r.ReadObject(ids[i], false));
else
Assert.AreEqual($"very long start of string/a middle part that is also somewhat long/almost done.../this part is unique{i}...../this part has unicode characters, see ЁЁЁЁЁЁЁ", r.ReadString(ids[i], '/'));
}
}
}
}
#endif