using System; using UnsafeIL; namespace Cryville.Common.Pdt { /// /// Span on the memory of a . /// public unsafe struct PdtVariableMemory : IEquatable { readonly byte* _ptr; /// /// The length of the span. /// public int Length { get; private set; } /// /// The type of the span. /// public int Type { get; private set; } internal PdtVariableMemory(int type, byte* ptr, int len) { Type = type; _ptr = ptr; Length = len; } /// /// Copies the memory in the span to another span. /// /// The destination span. public void CopyTo(PdtVariableMemory dest) { CopyTo(dest._ptr, 0, Length); } /// /// Copies the memory in the span to a buffer. /// /// The destination buffer. /// The offset on the destination buffer to start copying to. public void CopyTo(byte[] dest, int destOffset) { fixed (byte* ptr = dest) { CopyTo(ptr, destOffset, Length); } } /// /// Copies the memory in the span to a buffer. /// /// The destination buffer. /// The offset on the destination buffer to start copying to. /// The length to copy. /// is greater than the length of the span. public void CopyTo(byte* dest, int destOffset, int length) { if (length > Length) throw new ArgumentOutOfRangeException("length"); for (int i = 0; i < length; i++) dest[destOffset + i] = _ptr[i]; } /// public bool Equals(PdtVariableMemory obj) { if (Type != obj.Type || Length != obj.Length) return false; for (int i = 0; i < Length; i++) { if (*(_ptr + i) != *(obj._ptr + i)) return false; } return true; } /// /// Gets the memory of the span as a number. /// /// The offset on the span to start reading from. /// A number. /// The span at the offset does not represent a number. public float AsNumber(int offset = 0) { if (Type != PdtInternalType.Number && Type != PdtInternalType.Vector) throw new InvalidCastException("Not a number"); float value; byte* ptr = (byte*)&value; for (int i = 0; i < sizeof(float); i++) ptr[i] = _ptr[i + offset]; return value; } /// /// Sets the memory of the span to a number. /// /// The number. /// The offset from the start of the span. /// The span at the offset does not represent a number. /// The length of the span is not sufficient. public void SetNumber(float value, int offset = 0) { if (Type != PdtInternalType.Number && Type != PdtInternalType.Vector) throw new InvalidCastException("Not a number"); if (Length < sizeof(float) + offset) throw new InvalidOperationException("Frame length not sufficient"); byte* ptr = (byte*)&value; for (int i = 0; i < sizeof(float); i++) _ptr[i + offset] = ptr[i]; } /// /// Gets the memory of the span as a string. /// /// The offset on the span to start reading from. /// A string. /// The span at the offset does not represent a string. public string AsString(int offset = 0) { if (Type != PdtInternalType.String && Type != PdtInternalType.Array) throw new InvalidCastException("Not a string"); var len = *(int*)(_ptr + offset); return new string((char*)(_ptr + offset + sizeof(int)), 0, len); } /// /// Sets the memory of the span to a string. /// /// The string. /// The offset from the start of the span. /// The span at the offset does not represent a string. /// The length of the span is not sufficient. public void SetString(string value, int offset = 0) { if (Type != PdtInternalType.String && Type != PdtInternalType.Array) throw new InvalidCastException("Not a string"); int strlen = value.Length; if (Length < strlen * sizeof(char) + sizeof(int) + offset) throw new InvalidOperationException("Frame length not sufficient"); char* ptr = (char*)(_ptr + offset + sizeof(int)); *(int*)(_ptr + offset) = strlen; int i = 0; foreach (var c in value) ptr[i++] = c; } /// /// Gets the memory of the span as an undefined identifier. /// /// The offset on the span to start reading from. /// The name of an undefined identifier. /// The span does not represent an undefined identifier. public int AsIdentifier(int offset = 0) { if (Type != PdtInternalType.Undefined && Type != PdtInternalType.Array) throw new InvalidCastException("Not an identifier"); return *(int*)(_ptr + offset); } /// /// Gets the memory of the span as an instance of the specified type. /// /// The specified type. /// The offset on the span to start reading from. /// An instance of the specified type. /// is not less than the length of the span. /// The length of the span is not sufficient. /// /// Use instead while reading an unaligned number. /// public T As(int offset = 0) { var len = Unsafe.SizeOf(); if (offset >= Length) throw new ArgumentOutOfRangeException("offset"); if (offset + len > Length) throw new InvalidCastException("Frame length not sufficient"); return Unsafe.Read(_ptr + offset); } /// /// Sets the memory of the span to an instance of the specified type. /// /// The specified type. /// The value. /// The offset from the start of the span. /// is not less than the length of the span. /// The length of the span is not sufficient. /// /// Use instead while writing an unaligned number. /// public void Set(T value, int offset = 0) { var len = Unsafe.SizeOf(); if (offset >= Length) throw new ArgumentOutOfRangeException("offset"); if (offset + len > Length) throw new InvalidCastException("Frame length not sufficient"); Unsafe.Write(_ptr + offset, value); } /// /// Gets the array suffix. /// /// The type of the array. /// The item count of the array. /// The span does not represent an array. public void GetArraySuffix(out int arrtype, out int pc) { if (Type != PdtInternalType.Vector && Type != PdtInternalType.Array) throw new InvalidCastException("Not an array or vector"); arrtype = *(int*)(_ptr + Length - sizeof(int)); if (Type == PdtInternalType.Array) pc = *(int*)(_ptr + Length - 2 * sizeof(int)); else pc = -1; } /// /// Sets the array suffix. /// /// The type of the array. /// The item count of the array. /// The span does not represent an array. public void SetArraySuffix(int arrtype, int pc = 0) { if (Type != PdtInternalType.Vector && Type != PdtInternalType.Array) throw new InvalidCastException("Not an array or vector"); *(int*)(_ptr + Length - sizeof(int)) = arrtype; if (Type == PdtInternalType.Array) *(int*)(_ptr + Length - 2 * sizeof(int)) = pc; } } }