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

83 lines
3.1 KiB
C#

using Qrakhen.Qamp.Core.Collections;
using Qrakhen.Qamp.Core.Values;
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.Select(c => c.Unsigned.GetBytes()).ToArray();
byte[] bytes = new byte[sizeof(long) * 2 + opCodes.Length + constants.Length * 8];
Array.Copy(opCodes.LongLength.GetBytes(), 0, bytes, 0, sizeof(long));
Array.Copy((constants.LongLength * sizeof(long)).GetBytes(), 0, bytes, sizeof(long), sizeof(long));
int offset = sizeof(long) * 2;
for (long i = 0; i < opCodes.LongLength; i++) {
bytes[offset++] = opCodes[i];
}
for (long i = 0; i < constants.LongLength; i++) {
for (int j = 0; j < sizeof(long); j++) {
bytes[offset++] = constants[i][j];
}
}
return bytes;
}
public static Segment Deserialize(byte[] data)
{
return null;
}
}