XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESystem

【Rust】XFE系统,基于Rust和汇编

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Buffers.Binary;

namespace XFESystem.Core;

public interface IWorkspaceBlockDevice
{
    ulong SectorCount { get; }
    void Read(ulong lba, Span<byte> destination);
    void Write(ulong lba, ReadOnlySpan<byte> source);
    void Flush();
    void AllowWorkspace(ulong lba, uint sectors);
}

/// <summary>Immutable FAT allocation, two independent data banks, sector-sized commit records.</summary>
public static class UsbWorkspaceFormat
{
    public const int SectorBytes = 512, ContainerSectors = 131072, BankSectors = 65535;
    public const int ContainerBytes = ContainerSectors * SectorBytes;
    public const int MaxPayloadBytes = (BankSectors - 1) * SectorBytes;
    public const ulong Magic = 0x5357425355454658, BankMagic = 0x4b4e414257454658;
    public static uint Crc(ReadOnlySpan<byte> bytes)
    {
        uint crc = uint.MaxValue;
        foreach (byte value in bytes) { crc ^= value; for (int i = 0; i < 8; i++) crc = (crc >> 1) ^ ((crc & 1) != 0 ? 0xedb88320u : 0); }
        return ~crc;
    }
    public static byte[] CreateDescriptor()
    {
        var data = new byte[SectorBytes]; BinaryPrimitives.WriteUInt64LittleEndian(data, Magic);
        BinaryPrimitives.WriteUInt32LittleEndian(data.AsSpan(8), 1);
        BinaryPrimitives.WriteUInt32LittleEndian(data.AsSpan(12), ContainerSectors);
        BinaryPrimitives.WriteUInt32LittleEndian(data.AsSpan(16), BankSectors);
        BinaryPrimitives.WriteUInt32LittleEndian(data.AsSpan(20), Crc(data.AsSpan(0,20))); return data;
    }
    public static bool ValidDescriptor(ReadOnlySpan<byte> data) => data.Length == SectorBytes && data.SequenceEqual(CreateDescriptor());
}

public sealed class UsbWorkspaceJournal
{
    private readonly IWorkspaceBlockDevice device;
    private readonly ulong start;
    private int active = -1;
    public ulong Generation { get; private set; }
    public UsbWorkspaceJournal(IWorkspaceBlockDevice device, ulong start)
    {
        this.device = device; this.start = start;
        if (start >= device.SectorCount || UsbWorkspaceFormat.ContainerSectors > device.SectorCount - start) throw new IOException("USB workspace extent is outside the device.");
        byte[] descriptor = new byte[512]; device.Read(start, descriptor);
        if (!UsbWorkspaceFormat.ValidDescriptor(descriptor)) throw new IOException("USB workspace descriptor is invalid.");
        device.AllowWorkspace(start, UsbWorkspaceFormat.ContainerSectors);
    }
    public byte[]? Load()
    {
        var candidates = new List<(int Bank, ulong Generation, int Length, uint Crc)>(); bool nonzero = false;
        for (int bank = 0; bank < 2; bank++)
        {
            byte[] header = new byte[512]; device.Read(Bank(bank), header); nonzero |= header.Any(b => b != 0);
            uint crc = BinaryPrimitives.ReadUInt32LittleEndian(header.AsSpan(32)); header.AsSpan(32,4).Clear();
            int length = BinaryPrimitives.ReadInt32LittleEndian(header.AsSpan(24));
            ulong generation = BinaryPrimitives.ReadUInt64LittleEndian(header.AsSpan(16));
            if (BinaryPrimitives.ReadUInt64LittleEndian(header) != UsbWorkspaceFormat.BankMagic ||
                BinaryPrimitives.ReadUInt32LittleEndian(header.AsSpan(8)) != 1 ||
                BinaryPrimitives.ReadUInt32LittleEndian(header.AsSpan(12)) != 512 || generation == 0 ||
                length <= 0 || length > UsbWorkspaceFormat.MaxPayloadBytes || UsbWorkspaceFormat.Crc(header) != crc) continue;
            candidates.Add((bank, generation, length, BinaryPrimitives.ReadUInt32LittleEndian(header.AsSpan(28))));
        }
        foreach (var candidate in candidates.OrderByDescending(c => c.Generation))
        {
            byte[] data = new byte[(candidate.Length+511)&~511]; device.Read(Bank(candidate.Bank)+1, data);
            if (UsbWorkspaceFormat.Crc(data.AsSpan(0,candidate.Length)) != candidate.Crc) continue;
            active = candidate.Bank; Generation = candidate.Generation; return data.AsSpan(0,candidate.Length).ToArray();
        }
        if (nonzero) throw new IOException("Both USB workspace records are damaged; automatic overwrite was refused.");
        return null;
    }
    public void Save(ReadOnlySpan<byte> payload)
    {
        if (payload.IsEmpty || payload.Length > UsbWorkspaceFormat.MaxPayloadBytes || Generation == ulong.MaxValue) throw new IOException("USB workspace capacity or generation limit reached.");
        int next = active == 0 ? 1 : 0; byte[] data = new byte[(payload.Length+511)&~511]; payload.CopyTo(data);
        byte[] header = new byte[512]; BinaryPrimitives.WriteUInt64LittleEndian(header, UsbWorkspaceFormat.BankMagic);
        BinaryPrimitives.WriteUInt32LittleEndian(header.AsSpan(8),1); BinaryPrimitives.WriteUInt32LittleEndian(header.AsSpan(12),512);
        BinaryPrimitives.WriteUInt64LittleEndian(header.AsSpan(16),Generation+1);
        BinaryPrimitives.WriteInt32LittleEndian(header.AsSpan(24),payload.Length);
        BinaryPrimitives.WriteUInt32LittleEndian(header.AsSpan(28),UsbWorkspaceFormat.Crc(payload));
        BinaryPrimitives.WriteUInt32LittleEndian(header.AsSpan(32),UsbWorkspaceFormat.Crc(header));
        // Never overwrite the current valid bank. Old commit remains usable if
        // the new data, cache flush or final commit record is interrupted.
        device.Write(Bank(next)+1,data); device.Flush(); device.Write(Bank(next),header); device.Flush();
        active = next; Generation++;
    }
    private ulong Bank(int bank) => start+1+(ulong)bank*UsbWorkspaceFormat.BankSectors;
}