Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions RazorEngineCore.Tests/TestSaveLoad.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using RazorEngineCore.Tests.Models;
Expand Down Expand Up @@ -156,5 +157,45 @@ public async Task TestSave_RazorEngineCompiledTemplateMeta_2()
Assert.AreEqual(meta1.GeneratedSourceCode, meta2.GeneratedSourceCode);
Assert.AreEqual(meta1.TemplateSource, meta2.TemplateSource);
}

[TestMethod]
public async Task TestLoadFromStream_HandlesPartialReads()
{
RazorEngine razorEngine = new RazorEngine();
IRazorEngineCompiledTemplate initialTemplate = razorEngine.Compile("Hello @Model.Name");

using (MemoryStream serializedTemplate = new MemoryStream())
{
await initialTemplate.SaveToStreamAsync(serializedTemplate);

using (SingleByteReadStream partialReadStream = new SingleByteReadStream(serializedTemplate.ToArray()))
{
IRazorEngineCompiledTemplate loadedTemplate = await RazorEngineCompiledTemplate.LoadFromStreamAsync(partialReadStream);
string result = await loadedTemplate.RunAsync(new { Name = "Alex" });

Assert.AreEqual("Hello Alex", result);
Assert.AreEqual(serializedTemplate.Length, partialReadStream.Position);
}
}
}

private sealed class SingleByteReadStream : MemoryStream
{
public SingleByteReadStream(byte[] buffer) : base(buffer)
{
}

public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
return base.ReadAsync(buffer, offset, Math.Min(1, count), cancellationToken);
}

#if !NETFRAMEWORK
public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
{
return base.ReadAsync(buffer.Slice(0, Math.Min(1, buffer.Length)), cancellationToken);
}
#endif
}
}
}
23 changes: 19 additions & 4 deletions RazorEngineCore/ObjectExtenders.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,29 @@ public static bool IsAnonymous(this object obj)
&& type.Attributes.HasFlag(TypeAttributes.NotPublic);
}

public static async Task<long> ReadLong(this Stream stream)
internal static async Task ReadExactlyAsync(this Stream stream, byte[] buffer)
{
byte[] buffer = new byte[8];
int offset = 0;
while (offset < buffer.Length)
{
#if NETSTANDARD2_0
_ = await stream.ReadAsync(buffer, 0, buffer.Length);
int bytesRead = await stream.ReadAsync(buffer, offset, buffer.Length - offset);
#else
_ = await stream.ReadAsync(buffer.AsMemory(0, buffer.Length));
int bytesRead = await stream.ReadAsync(buffer.AsMemory(offset, buffer.Length - offset));
#endif
if (bytesRead == 0)
{
throw new EndOfStreamException();
}

offset += bytesRead;
}
}

public static async Task<long> ReadLong(this Stream stream)
{
byte[] buffer = new byte[8];
await stream.ReadExactlyAsync(buffer);
return BitConverter.ToInt64(buffer, 0);
}

Expand Down
6 changes: 1 addition & 5 deletions RazorEngineCore/RazorEngineCompiledTemplateMeta.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,7 @@ private static async Task<byte[]> ReadBuffer(Stream stream)
}

byte[] buffer = new byte[length];
#if NETSTANDARD2_0
_ = await stream.ReadAsync(buffer, 0, buffer.Length);
#else
_ = await stream.ReadAsync(buffer);
#endif
await stream.ReadExactlyAsync(buffer);
return buffer;
}
}
Expand Down