diff --git a/sdk/core/Azure.Core/api/Azure.Core.net461.cs b/sdk/core/Azure.Core/api/Azure.Core.net461.cs index 6c0484f3b014..33c92794e183 100644 --- a/sdk/core/Azure.Core/api/Azure.Core.net461.cs +++ b/sdk/core/Azure.Core/api/Azure.Core.net461.cs @@ -724,7 +724,6 @@ public SequenceWriter(int bufferSize = 4096) { } public void Advance(int bytesWritten) { } public void Dispose() { } public System.Memory GetMemory(int sizeHint = 0) { throw null; } - public System.Buffers.ReadOnlySequence GetReadOnlySequence() { throw null; } public System.Span GetSpan(int sizeHint = 0) { throw null; } public bool TryComputeLength(out long length) { throw null; } public void WriteTo(System.IO.Stream stream, System.Threading.CancellationToken cancellation) { } diff --git a/sdk/core/Azure.Core/api/Azure.Core.net5.0.cs b/sdk/core/Azure.Core/api/Azure.Core.net5.0.cs index 248afde3da71..aed8aa17984b 100644 --- a/sdk/core/Azure.Core/api/Azure.Core.net5.0.cs +++ b/sdk/core/Azure.Core/api/Azure.Core.net5.0.cs @@ -724,7 +724,6 @@ public SequenceWriter(int bufferSize = 4096) { } public void Advance(int bytesWritten) { } public void Dispose() { } public System.Memory GetMemory(int sizeHint = 0) { throw null; } - public System.Buffers.ReadOnlySequence GetReadOnlySequence() { throw null; } public System.Span GetSpan(int sizeHint = 0) { throw null; } public bool TryComputeLength(out long length) { throw null; } public void WriteTo(System.IO.Stream stream, System.Threading.CancellationToken cancellation) { } diff --git a/sdk/core/Azure.Core/api/Azure.Core.net6.0.cs b/sdk/core/Azure.Core/api/Azure.Core.net6.0.cs index 248afde3da71..aed8aa17984b 100644 --- a/sdk/core/Azure.Core/api/Azure.Core.net6.0.cs +++ b/sdk/core/Azure.Core/api/Azure.Core.net6.0.cs @@ -724,7 +724,6 @@ public SequenceWriter(int bufferSize = 4096) { } public void Advance(int bytesWritten) { } public void Dispose() { } public System.Memory GetMemory(int sizeHint = 0) { throw null; } - public System.Buffers.ReadOnlySequence GetReadOnlySequence() { throw null; } public System.Span GetSpan(int sizeHint = 0) { throw null; } public bool TryComputeLength(out long length) { throw null; } public void WriteTo(System.IO.Stream stream, System.Threading.CancellationToken cancellation) { } diff --git a/sdk/core/Azure.Core/api/Azure.Core.netcoreapp2.1.cs b/sdk/core/Azure.Core/api/Azure.Core.netcoreapp2.1.cs index 6c0484f3b014..33c92794e183 100644 --- a/sdk/core/Azure.Core/api/Azure.Core.netcoreapp2.1.cs +++ b/sdk/core/Azure.Core/api/Azure.Core.netcoreapp2.1.cs @@ -724,7 +724,6 @@ public SequenceWriter(int bufferSize = 4096) { } public void Advance(int bytesWritten) { } public void Dispose() { } public System.Memory GetMemory(int sizeHint = 0) { throw null; } - public System.Buffers.ReadOnlySequence GetReadOnlySequence() { throw null; } public System.Span GetSpan(int sizeHint = 0) { throw null; } public bool TryComputeLength(out long length) { throw null; } public void WriteTo(System.IO.Stream stream, System.Threading.CancellationToken cancellation) { } diff --git a/sdk/core/Azure.Core/api/Azure.Core.netstandard2.0.cs b/sdk/core/Azure.Core/api/Azure.Core.netstandard2.0.cs index 6c0484f3b014..33c92794e183 100644 --- a/sdk/core/Azure.Core/api/Azure.Core.netstandard2.0.cs +++ b/sdk/core/Azure.Core/api/Azure.Core.netstandard2.0.cs @@ -724,7 +724,6 @@ public SequenceWriter(int bufferSize = 4096) { } public void Advance(int bytesWritten) { } public void Dispose() { } public System.Memory GetMemory(int sizeHint = 0) { throw null; } - public System.Buffers.ReadOnlySequence GetReadOnlySequence() { throw null; } public System.Span GetSpan(int sizeHint = 0) { throw null; } public bool TryComputeLength(out long length) { throw null; } public void WriteTo(System.IO.Stream stream, System.Threading.CancellationToken cancellation) { } diff --git a/sdk/core/Azure.Core/src/SequenceWriter.cs b/sdk/core/Azure.Core/src/SequenceWriter.cs index 5d0fbc980249..d74e8fa78a55 100644 --- a/sdk/core/Azure.Core/src/SequenceWriter.cs +++ b/sdk/core/Azure.Core/src/SequenceWriter.cs @@ -10,7 +10,7 @@ namespace Azure.Core { /// - /// . + /// A buffer writer which writes large sequences of data into smaller shared buffers. /// public sealed class SequenceWriter : IBufferWriter, IDisposable { @@ -25,9 +25,9 @@ private struct Buffer private int _bufferSize; /// - /// . + /// Initializes a new instance of . /// - /// + /// The max size of each buffer segment. public SequenceWriter(int bufferSize = 4096) { _bufferSize = bufferSize; @@ -35,9 +35,10 @@ public SequenceWriter(int bufferSize = 4096) } /// - /// . + /// Notifies the that bytes bytes were written to the output or . + /// You must request a new buffer after calling to continue writing more data; you cannot write to a previously acquired buffer. /// - /// + /// The number of bytes written to the or . /// public void Advance(int bytesWritten) { @@ -50,19 +51,21 @@ public void Advance(int bytesWritten) } /// - /// . + /// Returns a to write to that is at least the requested size, as specified by the parameter. /// - /// - /// + /// The minimum length of the returned . If less than 256, a buffer of size 256 will be returned. + /// A memory buffer of at least bytes. If is less than 256, a buffer of size 256 will be returned. public Memory GetMemory(int sizeHint = 0) { if (sizeHint < 256) sizeHint = 256; + int sizeToRent = sizeHint > _bufferSize ? sizeHint : _bufferSize; + if (_buffers.Length == 0) { _buffers = new Buffer[1]; - _buffers[0].Array = ArrayPool.Shared.Rent(_bufferSize); + _buffers[0].Array = ArrayPool.Shared.Rent(sizeToRent); _count = 1; } @@ -72,7 +75,7 @@ public Memory GetMemory(int sizeHint = 0) return free; // else allocate a new buffer: - var newArray = ArrayPool.Shared.Rent(_bufferSize); + var newArray = ArrayPool.Shared.Rent(sizeToRent); // add buffer to _buffers if (_buffers.Length == _count) @@ -89,10 +92,10 @@ public Memory GetMemory(int sizeHint = 0) } /// - /// . + /// Returns a to write to that is at least the requested size, as specified by the parameter. /// - /// - /// + /// The minimum length of the returned . If less than 256, a buffer of size 256 will be returned. + /// A buffer of at least bytes. If is less than 256, a buffer of size 256 will be returned. public Span GetSpan(int sizeHint = 0) { Memory memory = GetMemory(sizeHint); @@ -115,11 +118,7 @@ public void Dispose() _count = 0; } - /// - /// Compute the length of the data written to the SequenceWriter. - /// - /// The length of the buffer returned. - /// A bool indicating whether or not the length was able to be calculated. + /// public bool TryComputeLength(out long length) { length = 0; @@ -131,11 +130,7 @@ public bool TryComputeLength(out long length) return true; } - /// - /// . - /// - /// - /// + /// public void WriteTo(Stream stream, CancellationToken cancellation) { for (int i = 0; i < _count; i++) @@ -145,12 +140,7 @@ public void WriteTo(Stream stream, CancellationToken cancellation) } } - /// - /// . - /// - /// - /// - /// + /// public async Task WriteToAsync(Stream stream, CancellationToken cancellation) { for (int i = 0; i < _count; i++) @@ -175,10 +165,9 @@ public void Add(byte[] array, int length) } /// - /// . + /// Gets a representing the data written to the SequenceWriter. /// - /// - public ReadOnlySequence GetReadOnlySequence() + internal ReadOnlySequence GetReadOnlySequence() { if (_count == 0) return ReadOnlySequence.Empty; diff --git a/sdk/core/Azure.Core/tests/SequenceWriterTests.cs b/sdk/core/Azure.Core/tests/SequenceWriterTests.cs new file mode 100644 index 000000000000..9113b0152a11 --- /dev/null +++ b/sdk/core/Azure.Core/tests/SequenceWriterTests.cs @@ -0,0 +1,73 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Buffers; +using NUnit.Framework; + +namespace Azure.Core.Tests +{ + internal class SequenceWriterTests + { + [Test] + public void ValidateEmptyBuffer() + { + SequenceWriter writer = new SequenceWriter(); + Assert.IsTrue(writer.TryComputeLength(out var length)); + Assert.AreEqual(0, length); + Assert.AreEqual(ReadOnlySequence.Empty, writer.GetReadOnlySequence()); + } + + [Test] + public void ValidateSingleBuffer() + { + SequenceWriter writer = new SequenceWriter(512); + WriteMemory(writer, 400, 0xFF); + + var sequence = writer.GetReadOnlySequence(); + + Assert.AreEqual(400, sequence.Length); + Assert.AreEqual(0xFF, sequence.First.Span[0]); + Assert.AreEqual(0xFF, sequence.Slice(399).First.Span[0]); + } + + [Test] + public void ValidateMultiBuffer() + { + SequenceWriter writer = new SequenceWriter(512); + WriteMemory(writer, 400, 0xFF); + WriteMemory(writer, 400, 0xFF); + + var sequence = writer.GetReadOnlySequence(); + + Assert.AreEqual(800, sequence.Length); + Assert.AreEqual(0xFF, sequence.First.Span[0]); + Assert.AreEqual(0xFF, sequence.Slice(399).First.Span[0]); + Assert.AreEqual(0xFF, sequence.Slice(400).First.Span[0]); + Assert.AreEqual(0xFF, sequence.Slice(799).First.Span[0]); + } + + [Test] + public void CanWriteMoreThanBufferSize() + { + SequenceWriter writer = new SequenceWriter(512); + WriteMemory(writer, 513, 0xFF); + WriteMemory(writer, 256, 0xFE); + + Assert.IsTrue(writer.TryComputeLength(out var length)); + Assert.AreEqual(769, length); + + var sequence = writer.GetReadOnlySequence(); + Assert.AreEqual(769, sequence.Length); + Assert.AreEqual(0xFF, sequence.First.Span[0]); + Assert.AreEqual(0xFE, sequence.Slice(514).First.Span[0]); + } + + private static void WriteMemory(SequenceWriter writer, int size, byte data) + { + var memory = writer.GetMemory(size); + memory.Span.Slice(0, size).Fill(data); + writer.Advance(size); + } + } +} diff --git a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/AvailabilitySetDataTests.cs b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/AvailabilitySetDataTests.cs index dab341ef7af8..3d360ededb00 100644 --- a/sdk/core/Azure.Core/tests/public/ModelSerializationTests/AvailabilitySetDataTests.cs +++ b/sdk/core/Azure.Core/tests/public/ModelSerializationTests/AvailabilitySetDataTests.cs @@ -14,47 +14,18 @@ namespace Azure.Core.Tests.Public.ModelSerializationTests { - internal class AvailabilitySetDataTests + internal class AvailabilitySetDataTests : ModelTests { - private const string _serviceResponse = "{\"name\":\"testAS-3375\",\"id\":\"/subscriptions/e37510d7-33b6-4676-886f-ee75bcc01871/resourceGroups/testRG-6497/providers/Microsoft.Compute/availabilitySets/testAS-3375\",\"type\":\"Microsoft.Compute/availabilitySets\",\"location\":\"eastus\",\"tags\":{\"key\":\"value\"},\"properties\":{\"platformUpdateDomainCount\":5,\"platformFaultDomainCount\":3},\"sku\":{\"name\":\"Classic\",\"extraSku\":\"extraSku\"},\"extraRoot\":\"extraRoot\"}"; + protected override string WirePayload => "{\"name\":\"testAS-3375\",\"id\":\"/subscriptions/e37510d7-33b6-4676-886f-ee75bcc01871/resourceGroups/testRG-6497/providers/Microsoft.Compute/availabilitySets/testAS-3375\",\"type\":\"Microsoft.Compute/availabilitySets\",\"location\":\"eastus\",\"tags\":{\"key\":\"value\"},\"properties\":{\"platformUpdateDomainCount\":5,\"platformFaultDomainCount\":3},\"sku\":{\"name\":\"Classic\",\"extraSku\":\"extraSku\"},\"extraRoot\":\"extraRoot\"}"; - [TestCase("W")] - [TestCase("J")] - public void RoundTripTest(string format) => - RoundTripTest(format, SerializeWithModelSerializer, DeserializeWithModelSerializer); + protected override string JsonPayload => WirePayload; - [TestCase("W")] - [TestCase("J")] - public void BufferTest(string format) => - RoundTripTest(format, SerializeWithBuffer, DeserializeWithModelSerializer); + protected override Func ToRequestContent => model => model; - [Test] - public void ImplicitCastTest() => - RoundTripTest(ModelSerializerFormat.Wire, SerializeWithImplicitCast, DeserializeWithModelSerializer); + protected override Func FromResponse => response => (AvailabilitySetData)response; - [TestCase("W")] - [TestCase("J")] - public void JsonReaderTest(string format) => - RoundTripTest(format, SerializeWithModelSerializer, DeserializeWithJsonReader); - - [TestCase("W")] - [TestCase("J")] - public void UsingSequence(string format) => - RoundTripTest(format, SerializeWithModelSerializer, DeserializeWithSequence); - - [TestCase("W")] - [TestCase("J")] - public void UseNonGeneric(string format) => - RoundTripTest(format, SerializeWithModelSerializerNonGeneric, DeserializeWithModelSerializerNonGeneric); - - [Test] - public void UseInternal() => - RoundTripTest("W", SerializeWithInternal, DeserializeWithInternal); - - private void RoundTripTest(ModelSerializerFormat format, Func serialize, Func deserialize) + protected override string GetExpectedResult(ModelSerializerFormat format) { - ModelSerializerOptions options = new ModelSerializerOptions(format); - var expectedSerializedString = "{"; if (format == ModelSerializerFormat.Json) expectedSerializedString += "\"name\":\"testAS-3375\",\"id\":\"/subscriptions/e37510d7-33b6-4676-886f-ee75bcc01871/resourceGroups/testRG-6497/providers/Microsoft.Compute/availabilitySets/testAS-3375\",\"type\":\"Microsoft.Compute/availabilitySets\","; @@ -65,113 +36,24 @@ private void RoundTripTest(ModelSerializerFormat format, Func(new BinaryData(Encoding.UTF8.GetBytes(json)), options); - } - private AvailabilitySetData DeserializeWithInternal(string json, ModelSerializerOptions options) - { - using JsonDocument doc = JsonDocument.Parse(json); - return AvailabilitySetData.DeserializeAvailabilitySetData(doc.RootElement, options); - } - - private AvailabilitySetData DeserializeWithModelSerializerNonGeneric(string json, ModelSerializerOptions options) - { - return (AvailabilitySetData)ModelSerializer.Deserialize(new BinaryData(Encoding.UTF8.GetBytes(json)), typeof(AvailabilitySetData), options); - } - - private SequenceWriter WriteStringToBuffer(string json) - { - SequenceWriter writer = new SequenceWriter(); - var bytes = Encoding.UTF8.GetBytes(json); - int fullBuffers = bytes.Length / 4096; - for (int i = 0; i < fullBuffers; i++) - { - int index = i * 4096; - bytes.AsSpan(index, 4096).CopyTo(writer.GetSpan(4096)); - writer.Advance(4096); - } - var remainder = bytes.Length % 4096; - bytes.AsSpan(fullBuffers * 4096, remainder).CopyTo(writer.GetSpan(remainder)); - writer.Advance(remainder); - return writer; - } - - private string SerializeWithImplicitCast(AvailabilitySetData model, ModelSerializerOptions options) - { - RequestContent content = model; - return GetStringFromContent(content); - } - - private string SerializeWithModelSerializer(AvailabilitySetData model, ModelSerializerOptions options) - { - var data = ModelSerializer.Serialize(model, options); - return data.ToString(); - } - - private string SerializeWithModelSerializerNonGeneric(object model, ModelSerializerOptions options) - { - var data = ModelSerializer.Serialize(model, options); - return data.ToString(); + return expectedSerializedString; ; } - private string SerializeWithInternal(AvailabilitySetData model, ModelSerializerOptions options) + protected override void VerifyModel(AvailabilitySetData model, ModelSerializerFormat format) { - using MemoryStream stream = new MemoryStream(); - using var writer = new Utf8JsonWriter(stream); - model.Serialize(writer); - writer.Flush(); - stream.Position = 0; - using var reader = new StreamReader(stream); - return reader.ReadToEnd(); - } - - private string SerializeWithBuffer(AvailabilitySetData model, ModelSerializerOptions options) - { - using var sequenceWriter = new SequenceWriter(); - using var writer = new Utf8JsonWriter(sequenceWriter); - ((IJsonModelSerializable)model).Serialize(writer, options); - writer.Flush(); - return GetStringFromContent(RequestContent.Create(sequenceWriter)); - } + Dictionary expectedTags = new Dictionary() { { "key", "value" } }; - private static string GetStringFromContent(RequestContent content) - { - MemoryStream stream = new MemoryStream(); - content.WriteTo(stream, default); - stream.Position = 0; - StreamReader reader = new StreamReader(stream); - return reader.ReadToEnd(); + Assert.AreEqual("/subscriptions/e37510d7-33b6-4676-886f-ee75bcc01871/resourceGroups/testRG-6497/providers/Microsoft.Compute/availabilitySets/testAS-3375", model.Id.ToString()); + CollectionAssert.AreEquivalent(expectedTags, model.Tags); + Assert.AreEqual(AzureLocation.EastUS, model.Location); + Assert.AreEqual("testAS-3375", model.Name); + Assert.AreEqual("Microsoft.Compute/availabilitySets", model.ResourceType.ToString()); + Assert.AreEqual(5, model.PlatformUpdateDomainCount); + Assert.AreEqual(3, model.PlatformFaultDomainCount); + Assert.AreEqual("Classic", model.Sku.Name); } - private void CompareModels(AvailabilitySetData model, AvailabilitySetData model2, ModelSerializerFormat format) + protected override void CompareModels(AvailabilitySetData model, AvailabilitySetData model2, ModelSerializerFormat format) { Assert.AreEqual(format == ModelSerializerFormat.Wire ? null : model.Id, model2.Id); Assert.AreEqual(model.Location, model2.Location); @@ -183,19 +65,5 @@ private void CompareModels(AvailabilitySetData model, AvailabilitySetData model2 CollectionAssert.AreEquivalent(model.Tags, model2.Tags); Assert.AreEqual(model.Sku.Name, model2.Sku.Name); } - - private void ValidateModel(AvailabilitySetData model) - { - Dictionary expectedTags = new Dictionary() { { "key", "value" } }; - - Assert.AreEqual("/subscriptions/e37510d7-33b6-4676-886f-ee75bcc01871/resourceGroups/testRG-6497/providers/Microsoft.Compute/availabilitySets/testAS-3375", model.Id.ToString()); - CollectionAssert.AreEquivalent(expectedTags, model.Tags); - Assert.AreEqual(AzureLocation.EastUS, model.Location); - Assert.AreEqual("testAS-3375", model.Name); - Assert.AreEqual("Microsoft.Compute/availabilitySets", model.ResourceType.ToString()); - Assert.AreEqual(5, model.PlatformUpdateDomainCount); - Assert.AreEqual(3, model.PlatformFaultDomainCount); - Assert.AreEqual("Classic", model.Sku.Name); - } } }