qamp/Qrakhen.Qamp.Core/Execution/Segment.cs

86 lines
3.0 KiB
C#

using Qrakhen.Qamp.Core.Abstractions;
using Qrakhen.Qamp.Core.Collections;
using Qrakhen.Qamp.Core.Values;
using Qrakhen.Qamp.Core.Values.Objects;
namespace Qrakhen.Qamp.Core.Execution;
using Unsigned = ulong;
using Signed = long;
using Char = char;
using Byte = byte;
using Bool = bool;
using Decimal = double;
public class Segment(
IEnumerable<Instruction> instructions,
IEnumerable<Value> constants) : ISerialize<Segment>
{
public readonly FixedArray<Instruction> Instructions = new(instructions);
public readonly FixedArray<Value> Constants = new(constants);
public byte Read(long offset) => Instructions[offset];
public byte[] Read(long offset, int length)
{
ArgumentOutOfRangeException.ThrowIfGreaterThan(offset + length, Instructions.Length);
byte[] bytes = new byte[length];
for (int i = 0; i < length; i++)
bytes[i] = Instructions[offset + i];
return bytes;
}
/// <summary>
/// Although this always returns a long, the actually stored data in the instruction set has a dynamic width
/// (anything between 1 and 9 bytes, 1 if it's just a byte, and 1 + n, where n is up to 8 bytes).<br/>
/// If the first byte read is masked with 0x80, it means that this byte represents the amount of bytes that
/// shall be read. If it is not, the byte is returned directly (for smaller offsets).
/// </summary>
/// <remarks>
/// All of this is done to compress compiled instruction size.
/// Todo: It will need to be tested wheter the cost on performance is stronger than anticipated.
/// </remarks>
public long ReadDynamic(long offset, out int read)
{
byte value = Read(offset);
read = 1;
if (value < 0x80) // 0x80 flag for length, less than 0x80 means direct read
return value;
int length = value ^ 0x80;
read += length;
var data = new byte[8];
for (int i = 0; i < length; i++)
data[i] = Read(offset + i);
return data.ToInt64();
}
public short ReadShort(long offset) => BitConverter.ToInt16(Read(offset, 2));
public int ReadInt(long offset) => BitConverter.ToInt32(Read(offset, 4));
public long ReadLong(long offset) => BitConverter.ToInt64(Read(offset, 8));
public byte[] Serialize()
{
var opCodes = Instructions.Select(i => (byte)i).ToArray();
var constants = Constants.ToArray();
byte[] bytes = new byte[sizeof(long) + opCodes.Length];
System.Array.Copy(opCodes.LongLength.GetBytes(), 0, bytes, 0, sizeof(long));
System.Array.Copy(opCodes, 0, bytes, sizeof(long), opCodes.Length);
var list = new List<byte>(bytes);
foreach (var constant in constants) {
var data = constant.Signed.GetDynamicBytes();
if (data.Length == 1)
list.Add(data[0]);
else {
list.Add((byte)(data.Length | 0x80));
list.AddRange(data);
}
}
return list.ToArray();
}
public static Segment Deserialize(byte[] data)
{
return null;
}
}