-
Notifications
You must be signed in to change notification settings - Fork 299
/
IBufferWriterExtensions.cs
133 lines (112 loc) · 5.65 KB
/
IBufferWriterExtensions.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Buffers;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using CommunityToolkit.HighPerformance.Buffers;
using CommunityToolkit.HighPerformance.Streams;
namespace CommunityToolkit.HighPerformance;
/// <summary>
/// Helpers for working with the <see cref="IBufferWriter{T}"/> type.
/// </summary>
public static class IBufferWriterExtensions
{
/// <summary>
/// Returns a <see cref="Stream"/> that can be used to write to a target an <see cref="IBufferWriter{T}"/> of <see cref="byte"/> instance.
/// </summary>
/// <param name="writer">The target <see cref="IBufferWriter{T}"/> instance.</param>
/// <returns>A <see cref="Stream"/> wrapping <paramref name="writer"/> and writing data to its underlying buffer.</returns>
/// <remarks>The returned <see cref="Stream"/> can only be written to and does not support seeking.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Stream AsStream(this IBufferWriter<byte> writer)
{
if (writer.GetType() == typeof(ArrayPoolBufferWriter<byte>))
{
// If the input writer is of type ArrayPoolBufferWriter<byte>, we can use the type
// specific buffer writer owner to let the JIT elide callvirts when accessing it.
ArrayPoolBufferWriter<byte>? internalWriter = Unsafe.As<ArrayPoolBufferWriter<byte>>(writer)!;
return new IBufferWriterStream<ArrayBufferWriterOwner>(new ArrayBufferWriterOwner(internalWriter));
}
return new IBufferWriterStream<IBufferWriterOwner>(new IBufferWriterOwner(writer));
}
/// <summary>
/// Writes a value of a specified type into a target <see cref="IBufferWriter{T}"/> instance.
/// </summary>
/// <typeparam name="T">The type of value to write.</typeparam>
/// <param name="writer">The target <see cref="IBufferWriter{T}"/> instance to write to.</param>
/// <param name="value">The input value to write to <paramref name="writer"/>.</param>
/// <exception cref="ArgumentException">Thrown if <paramref name="writer"/> reaches the end.</exception>
public static unsafe void Write<T>(this IBufferWriter<byte> writer, T value)
where T : unmanaged
{
int length = sizeof(T);
Span<byte> span = writer.GetSpan(1);
if (span.Length < length)
{
ThrowArgumentExceptionForEndOfBuffer();
}
ref byte r0 = ref MemoryMarshal.GetReference(span);
Unsafe.WriteUnaligned(ref r0, value);
writer.Advance(length);
}
/// <summary>
/// Writes a value of a specified type into a target <see cref="IBufferWriter{T}"/> instance.
/// </summary>
/// <typeparam name="T">The type of value to write.</typeparam>
/// <param name="writer">The target <see cref="IBufferWriter{T}"/> instance to write to.</param>
/// <param name="value">The input value to write to <paramref name="writer"/>.</param>
/// <exception cref="ArgumentException">Thrown if <paramref name="writer"/> reaches the end.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Write<T>(this IBufferWriter<T> writer, T value)
{
Span<T> span = writer.GetSpan(1);
if (span.Length < 1)
{
ThrowArgumentExceptionForEndOfBuffer();
}
MemoryMarshal.GetReference(span) = value;
writer.Advance(1);
}
/// <summary>
/// Writes a series of items of a specified type into a target <see cref="IBufferWriter{T}"/> instance.
/// </summary>
/// <typeparam name="T">The type of value to write.</typeparam>
/// <param name="writer">The target <see cref="IBufferWriter{T}"/> instance to write to.</param>
/// <param name="span">The input <see cref="ReadOnlySpan{T}"/> to write to <paramref name="writer"/>.</param>
/// <exception cref="ArgumentException">Thrown if <paramref name="writer"/> reaches the end.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Write<T>(this IBufferWriter<byte> writer, ReadOnlySpan<T> span)
where T : unmanaged
{
ReadOnlySpan<byte> source = MemoryMarshal.AsBytes(span);
Span<byte> destination = writer.GetSpan(source.Length);
source.CopyTo(destination);
writer.Advance(source.Length);
}
#if !NETSTANDARD2_1_OR_GREATER
/// <summary>
/// Writes a series of items of a specified type into a target <see cref="IBufferWriter{T}"/> instance.
/// </summary>
/// <typeparam name="T">The type of value to write.</typeparam>
/// <param name="writer">The target <see cref="IBufferWriter{T}"/> instance to write to.</param>
/// <param name="span">The input <see cref="ReadOnlySpan{T}"/> to write to <paramref name="writer"/>.</param>
/// <exception cref="ArgumentException">Thrown if <paramref name="writer"/> reaches the end.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Write<T>(this IBufferWriter<T> writer, ReadOnlySpan<T> span)
{
Span<T> destination = writer.GetSpan(span.Length);
span.CopyTo(destination);
writer.Advance(span.Length);
}
#endif
/// <summary>
/// Throws an <see cref="ArgumentException"/> when trying to write too many bytes to the target writer.
/// </summary>
private static void ThrowArgumentExceptionForEndOfBuffer()
{
throw new ArgumentException("The current buffer writer can't contain the requested input data.");
}
}