diff --git a/src/OrchardCore.Modules/OrchardCore.Media.Indexing.Pdf/Services/PdfMediaFileTextProvider.cs b/src/OrchardCore.Modules/OrchardCore.Media.Indexing.Pdf/Services/PdfMediaFileTextProvider.cs index 2893ce94967..3cb4917a52e 100644 --- a/src/OrchardCore.Modules/OrchardCore.Media.Indexing.Pdf/Services/PdfMediaFileTextProvider.cs +++ b/src/OrchardCore.Modules/OrchardCore.Media.Indexing.Pdf/Services/PdfMediaFileTextProvider.cs @@ -1,5 +1,4 @@ using Cysharp.Text; -using Microsoft.IO; using UglyToad.PdfPig; namespace OrchardCore.Media.Indexing; @@ -18,7 +17,7 @@ public async Task GetTextAsync(string path, Stream fileStream) if (!fileStream.CanSeek) { seekableStream = new FileStream(Path.GetTempFileName(), FileMode.OpenOrCreate, FileAccess.Write, FileShare.None, 4096, FileOptions.DeleteOnClose); - + await fileStream.CopyToAsync(seekableStream); seekableStream.Position = 0; diff --git a/src/OrchardCore.Modules/OrchardCore.XmlRpc/Controllers/HomeController.cs b/src/OrchardCore.Modules/OrchardCore.XmlRpc/Controllers/HomeController.cs index c9207b01e09..d69f4cf772c 100644 --- a/src/OrchardCore.Modules/OrchardCore.XmlRpc/Controllers/HomeController.cs +++ b/src/OrchardCore.Modules/OrchardCore.XmlRpc/Controllers/HomeController.cs @@ -54,7 +54,8 @@ public async Task ServiceEndpoint([ModelBinder(BinderType = typeo result.Save(w); } - var content = Encoding.UTF8.GetString(stream.ToArray()); + var content = Encoding.UTF8.GetString(stream.GetBuffer(), 0, (int)stream.Length); + return Content(content, "text/xml"); } diff --git a/src/OrchardCore/OrchardCore.Infrastructure/Documents/DefaultDocumentSerializer.cs b/src/OrchardCore/OrchardCore.Infrastructure/Documents/DefaultDocumentSerializer.cs index 2658f55b5cc..a57a7fb7346 100644 --- a/src/OrchardCore/OrchardCore.Infrastructure/Documents/DefaultDocumentSerializer.cs +++ b/src/OrchardCore/OrchardCore.Infrastructure/Documents/DefaultDocumentSerializer.cs @@ -24,7 +24,7 @@ public Task SerializeAsync(TDocument document, int compressTh var data = JsonSerializer.SerializeToUtf8Bytes(document, _serializerOptions); if (data.Length >= compressThreshold) { - data = Compress(data); + data = Compress(data).ToArray(); } return Task.FromResult(data); @@ -33,12 +33,16 @@ public Task SerializeAsync(TDocument document, int compressTh public Task DeserializeAsync(byte[] data) where TDocument : class, IDocument, new() { + TDocument document; + if (IsCompressed(data)) { - data = Decompress(data); + document = JsonSerializer.Deserialize(Decompress(data), _serializerOptions); + } + else + { + document = JsonSerializer.Deserialize(data, _serializerOptions); } - - var document = JsonSerializer.Deserialize(data, _serializerOptions); return Task.FromResult(document); } @@ -55,37 +59,24 @@ internal static bool IsCompressed(byte[] data) return false; } - internal static byte[] Compress(byte[] data) + internal static ReadOnlySpan Compress(byte[] data) { using var input = new MemoryStream(data); using var output = MemoryStreamFactory.GetStream(); - using (var gzip = new GZipStream(output, CompressionMode.Compress)) - { - input.CopyTo(gzip); - } + using var gZip = new GZipStream(output, CompressionMode.Compress); - if (output.TryGetBuffer(out var buffer)) - { - return buffer.Array; - } + input.CopyTo(gZip); - return output.ToArray(); + return output.GetBuffer().AsSpan().Slice(0, (int)gZip.Length); } - internal static byte[] Decompress(byte[] data) + internal static ReadOnlySpan Decompress(byte[] data) { using var input = new MemoryStream(data); using var output = MemoryStreamFactory.GetStream(); - using (var gzip = new GZipStream(input, CompressionMode.Decompress)) - { - gzip.CopyTo(output); - } - - if (output.TryGetBuffer(out var buffer)) - { - return buffer.Array; - } + using var gZip = new GZipStream(input, CompressionMode.Decompress); + gZip.CopyTo(output); - return output.ToArray(); + return output.GetBuffer().AsSpan().Slice(0, (int)gZip.Length); } } diff --git a/src/OrchardCore/OrchardCore.Infrastructure/Scripting/CommonGeneratorMethods.cs b/src/OrchardCore/OrchardCore.Infrastructure/Scripting/CommonGeneratorMethods.cs index b402ff4633d..8c9bd938f0f 100644 --- a/src/OrchardCore/OrchardCore.Infrastructure/Scripting/CommonGeneratorMethods.cs +++ b/src/OrchardCore/OrchardCore.Infrastructure/Scripting/CommonGeneratorMethods.cs @@ -11,7 +11,15 @@ public class CommonGeneratorMethods : IGlobalMethodProvider Name = "base64", Method = serviceProvider => (Func)(encoded => { - return Encoding.UTF8.GetString(Convert.FromBase64String(encoded)); + var neededLength = GetByteArrayLengthFromBase64(encoded); + Span bytes = new byte[neededLength]; + + if (!Convert.TryFromBase64String(encoded, bytes, out var bytesWritten)) + { + throw new FormatException("Invalid Base64 string."); + } + + return Encoding.UTF8.GetString(bytes); }), }; @@ -33,35 +41,41 @@ public class CommonGeneratorMethods : IGlobalMethodProvider Name = "gzip", Method = serviceProvider => (Func)(encoded => { - var bytes = new Span(); + var neededLength = GetByteArrayLengthFromBase64(encoded); - var stream = MemoryStreamFactory.GetStream(); + Span bytes = new byte[neededLength]; - if (Convert.TryFromBase64String(encoded, bytes, out _)) - { - stream.Write(bytes); - } - else + if (!Convert.TryFromBase64String(encoded, bytes, out var _)) { - stream.Write(Convert.FromBase64String(encoded)); + throw new FormatException("Invalid Base64 string."); } - using var gzip = new GZipStream(stream, CompressionMode.Decompress); + using var stream = MemoryStreamFactory.GetStream(); + stream.Write(bytes); + stream.Seek(0, SeekOrigin.Begin); + + using var gZip = new GZipStream(stream, CompressionMode.Decompress, leaveOpen: true); using var decompressed = MemoryStreamFactory.GetStream(); var buffer = new byte[1024]; int nRead; - while ((nRead = gzip.Read(buffer, 0, buffer.Length)) > 0) + while ((nRead = gZip.Read(buffer, 0, buffer.Length)) > 0) { decompressed.Write(buffer, 0, nRead); } - stream.Dispose(); - - return Convert.ToBase64String(decompressed.GetBuffer()); + return Convert.ToBase64String(decompressed.GetBuffer(), 0, (int)decompressed.Length); }), }; - public IEnumerable GetMethods() => new[] { _base64, _html, _gZip }; + public IEnumerable GetMethods() + => new[] { _base64, _html, _gZip }; + + // Base64 string length gives us the original byte length. It encodes 3 bytes into 4 characters. + // The formula to calculate the number of bytes from the base64 string length is: + private static int GetByteArrayLengthFromBase64(string base64String) + { + return (base64String.Length * 3) / 4; + } } diff --git a/src/OrchardCore/OrchardCore.Infrastructure/Scripting/Files/FilesScriptEngine.cs b/src/OrchardCore/OrchardCore.Infrastructure/Scripting/Files/FilesScriptEngine.cs index b1071259add..8070e5ce5ce 100644 --- a/src/OrchardCore/OrchardCore.Infrastructure/Scripting/Files/FilesScriptEngine.cs +++ b/src/OrchardCore/OrchardCore.Infrastructure/Scripting/Files/FilesScriptEngine.cs @@ -47,8 +47,10 @@ public object Evaluate(IScriptingScope scope, string script) using var fileStream = fileInfo.CreateReadStream(); using var memoryStream = MemoryStreamFactory.GetStream(); + memoryStream.WriteTo(fileStream); + memoryStream.Seek(0, SeekOrigin.Begin); - return Convert.ToBase64String(memoryStream.GetBuffer()); + return Convert.ToBase64String(memoryStream.GetBuffer().AsSpan().Slice(0, (int)memoryStream.Length)); } else { diff --git a/test/OrchardCore.Tests/Stubs/MemoryFileBuilder.cs b/test/OrchardCore.Tests/Stubs/MemoryFileBuilder.cs index 592ecd64e86..c6ab6915358 100644 --- a/test/OrchardCore.Tests/Stubs/MemoryFileBuilder.cs +++ b/test/OrchardCore.Tests/Stubs/MemoryFileBuilder.cs @@ -15,7 +15,7 @@ public async Task SetFileAsync(string subpath, Stream stream) { using var ms = MemoryStreamFactory.GetStream(); await stream.CopyToAsync(ms); - VirtualFiles[subpath] = ms.ToArray(); + VirtualFiles[subpath] = ms.GetBuffer().AsSpan().Slice((int)ms.Length).ToArray(); } ///