Mirror of Svelto.ECS because we're a fan of it
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

103 lines
4.2KB

  1. using System;
  2. using System.Reflection;
  3. using System.Runtime.InteropServices;
  4. using Svelto.DataStructures;
  5. using Svelto.ECS.Internal;
  6. using Attribute = System.Attribute;
  7. namespace Svelto.ECS.Serialization
  8. {
  9. [AttributeUsage(AttributeTargets.Field)]
  10. public class PartialSerializerFieldAttribute : Attribute
  11. {}
  12. public class PartialSerializer<T> : IComponentSerializer<T>
  13. where T : unmanaged, _IInternalEntityComponent
  14. {
  15. static PartialSerializer()
  16. {
  17. Type myType = typeof(T);
  18. FieldInfo[] myMembers = myType.GetFields();
  19. for (int i = 0; i < myMembers.Length; i++)
  20. {
  21. Object[] myAttributes = myMembers[i].GetCustomAttributes(true);
  22. for (int j = 0; j < myAttributes.Length; j++)
  23. {
  24. if (myAttributes[j] is PartialSerializerFieldAttribute)
  25. {
  26. var fieldType = myMembers[i].FieldType;
  27. if (fieldType.ContainsCustomAttribute(typeof(DoNotSerializeAttribute)) &&
  28. myMembers[i].IsPrivate == false)
  29. throw new ECSException($"field cannot be serialised {fieldType} in {myType.FullName}");
  30. var offset = Marshal.OffsetOf<T>(myMembers[i].Name);
  31. uint sizeOf;
  32. if (fieldType == typeof(bool))
  33. sizeOf = 1;
  34. else
  35. sizeOf = (uint)Marshal.SizeOf(fieldType);
  36. offsets.Add(((uint) offset.ToInt32(), sizeOf));
  37. totalSize += sizeOf;
  38. }
  39. }
  40. }
  41. if (myType.IsExplicitLayout == false)
  42. throw new ECSException($"PartialSerializer requires explicit layout {myType}");
  43. #if SLOW_SVELTO_SUBMISSION
  44. if (myType.GetProperties().Length > (ComponentBuilder<T>.HAS_EGID ? 1 : 0))
  45. throw new ECSException("serializable entity struct must be property less ".FastConcat(myType.FullName));
  46. #endif
  47. if (totalSize == 0)
  48. throw new ECSException($"{typeof(T).Name} is being serialized with {nameof(PartialSerializer<T>)} but has size 0!");
  49. }
  50. public bool Serialize(in T value, ISerializationData serializationData)
  51. {
  52. unsafe
  53. {
  54. fixed (byte* dataptr = serializationData.data.ToArrayFast(out _))
  55. {
  56. var entityComponent = value;
  57. foreach ((uint offset, uint size) offset in offsets)
  58. {
  59. byte* srcPtr = (byte*) &entityComponent + offset.offset;
  60. //todo move to Unsafe Copy when available as it is faster
  61. Buffer.MemoryCopy(srcPtr, dataptr + serializationData.dataPos,
  62. serializationData.data.count - serializationData.dataPos, offset.size);
  63. serializationData.dataPos += offset.size;
  64. }
  65. }
  66. }
  67. return true;
  68. }
  69. public bool Deserialize(ref T value, ISerializationData serializationData)
  70. {
  71. unsafe
  72. {
  73. T tempValue = value; //todo: temporary solution I want to get rid of this copy
  74. fixed (byte* dataptr = serializationData.data.ToArrayFast(out _))
  75. foreach ((uint offset, uint size) offset in offsets)
  76. {
  77. byte* dstPtr = (byte*) &tempValue + offset.offset;
  78. //todo move to Unsafe Copy when available as it is faster
  79. Buffer.MemoryCopy(dataptr + serializationData.dataPos, dstPtr, offset.size, offset.size);
  80. serializationData.dataPos += offset.size;
  81. }
  82. value = tempValue; //todo: temporary solution I want to get rid of this copy
  83. }
  84. return true;
  85. }
  86. public uint size => totalSize;
  87. static readonly FasterList<(uint, uint)> offsets = new FasterList<(uint, uint)>();
  88. static readonly uint totalSize;
  89. }
  90. }