using System.Text; namespace XFESystem.Documents; public sealed class DocumentService(IDocumentStorage storage, int maximumBytes = 4 * 1024 * 1024) { private static readonly UTF8Encoding Utf8 = new(false, true); private readonly IDocumentStorage _storage = storage ?? throw new ArgumentNullException(nameof(storage)); private readonly int _maximumBytes = maximumBytes > 0 ? maximumBytes : throw new ArgumentOutOfRangeException(nameof(maximumBytes)); public async ValueTask OpenAsync(DocumentLocation location, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(location); var source = await _storage.ReadAsync(location, _maximumBytes, cancellationToken).ConfigureAwait(false); if (source.Content.Length > _maximumBytes) throw new IOException("The document exceeds the size limit."); var bytes = source.Content.Span; if (bytes.StartsWith(new byte[] { 0xEF, 0xBB, 0xBF })) bytes = bytes[3..]; var text = Utf8.GetString(bytes); if (text.Contains('\0')) throw new IOException("Binary files cannot be opened as text."); return new DocumentSession(location, source, text); } public ValueTask SaveAsync(DocumentSession session, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(session); return session.SaveAsync(_storage, _maximumBytes, cancellationToken); } }