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;
}
}
}