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

XFESystem

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

公开
关注 0 Fork 0 Star 0
UTF-8
namespace XFESystem.Core;

/// <summary>Explicit RAM-live storage. This implementation never opens a physical disk or a host file.</summary>
public sealed class MemoryWorkspace : IWorkspaceBinaryStorage
{
    private readonly Dictionary<string, string?> _entries = new(StringComparer.OrdinalIgnoreCase);
    private readonly Dictionary<string, byte[]> _binary = new(StringComparer.OrdinalIgnoreCase);
    private const int MaxFileCharacters = 256 * 1024;
    private const int MaxEntries = 1024;
    public bool IsPersistent => false;

    public MemoryWorkspace(bool seed = true)
    {
        _entries.Add(SystemPaths.Root, null);
        foreach (var path in new[] { SystemPaths.Configuration, SystemPaths.Programs, SystemPaths.Desktop, SystemPaths.Documents, SystemPaths.Downloads, SystemPaths.Temporary })
            CreateDirectory(path);
        if (seed)
        {
            WriteText(SystemPaths.Combine(SystemPaths.Desktop, "Welcome.md"), "# XFESystem | XFEstudio\n\nC# native desktop development.\n\nThis is a RAM-live workspace: files disappear after shutdown.\n\n- Open Files or Editor from the desktop.\n- Ctrl+S saves the current file to this session.\n- CoreCLR and independent .NET DLL loading are still being ported.\n");
            WriteText(SystemPaths.Combine(SystemPaths.Documents, "Hello.js"), "const greeting = 'XFESystem';\nconsole.log(`Hello from ${greeting}!`);\n");
            WriteText(SystemPaths.Combine(SystemPaths.Documents, "Hello.cs"), "using System;\n\nConsole.ForegroundColor = ConsoleColor.Cyan;\nConsole.WriteLine(\"Hello XFESystem!\");\nConsole.ResetColor();\n");
        }
    }

    public bool Exists(string path) => _entries.ContainsKey(SystemPaths.Normalize(path));
    public void CreateDirectory(string path)
    {
        path = SystemPaths.Normalize(path);
        if (_entries.TryGetValue(path, out var old))
        {
            if (old is not null) throw new IOException("A file occupies this directory path.");
            return;
        }
        CreateDirectory(SystemPaths.Parent(path));
        CheckCapacity();
        _entries.Add(path, null);
    }
    public WorkspaceEntry[] List(string directory)
    {
        directory = SystemPaths.Normalize(directory);
        if (!_entries.TryGetValue(directory, out var content) || content is not null) throw new DirectoryNotFoundException(directory);
        var result = new List<WorkspaceEntry>();
        foreach (var item in _entries)
            if (!item.Key.Equals(directory, StringComparison.OrdinalIgnoreCase) && SystemPaths.Parent(item.Key).Equals(directory, StringComparison.OrdinalIgnoreCase))
                result.Add(new(item.Key, item.Value is null, _binary.TryGetValue(item.Key, out var bytes) ? bytes.Length : item.Value?.Length ?? 0));
        result.Sort((a, b) => a.IsDirectory != b.IsDirectory ? (a.IsDirectory ? -1 : 1) : StringComparer.OrdinalIgnoreCase.Compare(a.Name, b.Name));
        return result.ToArray();
    }
    public string ReadText(string path)
    {
        path = SystemPaths.Normalize(path);
        if (_binary.ContainsKey(path)) throw new IOException("Binary files must be opened by their application handler.");
        if (!_entries.TryGetValue(path, out var result) || result is null) throw new FileNotFoundException("Text file was not found.", path);
        return result;
    }
    public void WriteText(string path, string content)
    {
        ArgumentNullException.ThrowIfNull(content);
        path = SystemPaths.Normalize(path);
        if (content.Length > MaxFileCharacters) throw new IOException("The RAM-live file size limit was reached.");
        int used = 0;
        foreach (var item in _entries) if (!_binary.ContainsKey(item.Key)) used += item.Value?.Length ?? 0;
        int previous = !_binary.ContainsKey(path) && _entries.TryGetValue(path, out var existing) ? existing?.Length ?? 0 : 0;
        if (used - previous + content.Length > 4 * 1024 * 1024) throw new IOException("RAM-live text quota reached.");
        var parent = SystemPaths.Parent(path);
        if (!_entries.TryGetValue(parent, out var directory) || directory is not null) throw new DirectoryNotFoundException(parent);
        if (_entries.TryGetValue(path, out var old) && old is null) throw new IOException("Cannot overwrite a directory.");
        if (!_entries.ContainsKey(path)) CheckCapacity();
        _entries[path] = content;
        _binary.Remove(path);
    }
    public byte[] ReadBytes(string path)
    {
        path = SystemPaths.Normalize(path);
        if (_binary.TryGetValue(path, out var bytes)) return (byte[])bytes.Clone();
        return System.Text.Encoding.UTF8.GetBytes(ReadText(path));
    }
    public void WriteBytes(string path, ReadOnlySpan<byte> content)
    {
        if (content.Length > 4 * 1024 * 1024) throw new IOException("Binary file size limit reached.");
        int used = 0;
        foreach (var item in _binary) used += item.Value.Length;
        path = SystemPaths.Normalize(path);
        int previous = _binary.TryGetValue(path, out var old) ? old.Length : 0;
        if (used - previous + content.Length > 8 * 1024 * 1024) throw new IOException("Binary workspace quota reached.");
        byte[] copy = content.ToArray();
        WriteText(path, "");
        _binary[path] = copy;
    }
    public void Delete(string path)
    {
        path = SystemPaths.Normalize(path);
        if (path == SystemPaths.Root) throw new IOException("Cannot delete the system root.");
        if (!_entries.ContainsKey(path)) throw new FileNotFoundException(path);
        foreach (var entry in _entries.Keys)
            if (entry.StartsWith(path + "\\", StringComparison.OrdinalIgnoreCase)) throw new IOException("The directory is not empty.");
        _entries.Remove(path);
        _binary.Remove(path);
    }
    public void Move(string source, string destination)
    {
        source = SystemPaths.Normalize(source);
        destination = SystemPaths.Normalize(destination);
        if (source == SystemPaths.Root || destination.StartsWith(source + "\\", StringComparison.OrdinalIgnoreCase)) throw new IOException("Invalid move destination.");
        if (!_entries.TryGetValue(source, out var item)) throw new FileNotFoundException(source);
        if (_entries.ContainsKey(destination)) throw new IOException("The destination already exists.");
        var parent = SystemPaths.Parent(destination);
        if (!_entries.TryGetValue(parent, out var parentData) || parentData is not null) throw new DirectoryNotFoundException(parent);
        var descendants = new List<KeyValuePair<string, string?>>();
        foreach (var entry in _entries)
            if (entry.Key.Equals(source, StringComparison.OrdinalIgnoreCase) || entry.Key.StartsWith(source + "\\", StringComparison.OrdinalIgnoreCase)) descendants.Add(entry);
        var targets = new List<string>();
        foreach (var entry in descendants) targets.Add(SystemPaths.Normalize(destination + entry.Key[source.Length..]));
        foreach (var entry in descendants) _entries.Remove(entry.Key);
        for (int i = 0; i < descendants.Count; i++)
        {
            var entry = descendants[i];
            var target = targets[i];
            _entries.Add(target, entry.Value);
            if (_binary.TryGetValue(entry.Key, out var bytes)) { _binary.Remove(entry.Key); _binary.Add(target, bytes); }
        }
    }
    private void CheckCapacity()
    {
        if (_entries.Count >= MaxEntries) throw new IOException("The RAM-live entry limit was reached.");
    }
}