-
Notifications
You must be signed in to change notification settings - Fork 87
Expand file tree
/
Copy pathBinaryDataUtilities.cs
More file actions
131 lines (121 loc) · 6.88 KB
/
Copy pathBinaryDataUtilities.cs
File metadata and controls
131 lines (121 loc) · 6.88 KB
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
// Copyright 2021 Cloud Native Foundation.
// Licensed under the Apache 2.0 license.
// See LICENSE file in the project root for full license information.
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace CloudNative.CloudEvents.Core
{
/// <summary>
/// Utilities methods for dealing with binary data, converting between
/// streams, arrays, Memory{T} etc.
/// </summary>
public static class BinaryDataUtilities
{
/// <summary>
/// Asynchronously consumes the remaining content of the given stream, returning
/// it as a read-only memory segment.
/// </summary>
/// <param name="stream">The stream to read from. Must not be null.</param>
/// <returns>The content of the stream (from its original position), as a read-only memory segment.</returns>
public static async Task<ReadOnlyMemory<byte>> ToReadOnlyMemoryAsync(Stream stream)
{
Validation.CheckNotNull(stream, nameof(stream));
// TODO: Optimize if it's already a MemoryStream? Will only work in some cases,
// and is most likely to occur in tests, where the efficiency doesn't matter as much.
var memory = new MemoryStream();
await stream.CopyToAsync(memory).ConfigureAwait(false);
// It's safe to use memory.GetBuffer() and memory.Position here, as this is a stream
// we've created using the parameterless constructor.
var buffer = memory.GetBuffer();
return new ReadOnlyMemory<byte>(buffer, 0, (int) memory.Position);
}
/// <summary>
/// Consumes the remaining content of the given stream, returning
/// it as a read-only memory segment.
/// </summary>
/// <param name="stream">The stream to read from. Must not be null.</param>
/// <returns>The content of the stream (from its original position), as a read-only memory segment.</returns>
public static ReadOnlyMemory<byte> ToReadOnlyMemory(Stream stream)
{
Validation.CheckNotNull(stream, nameof(stream));
// TODO: Optimize if it's already a MemoryStream? Will only work in some cases,
// and is most likely to occur in tests, where the efficiency doesn't matter as much.
var memory = new MemoryStream();
stream.CopyTo(memory);
// It's safe to use memory.GetBuffer() and memory.Position here, as this is a stream
// we've created using the parameterless constructor.
var buffer = memory.GetBuffer();
return new ReadOnlyMemory<byte>(buffer, 0, (int) memory.Position);
}
/// <summary>
/// Returns a read-only <see cref="MemoryStream"/> view over the given memory where
/// possible, or over a copy of the data if the memory cannot be read as an array segment.
/// This method should be used with care, due to the "sometimes shared, sometimes not"
/// nature of the result.
/// </summary>
/// <param name="memory">The memory to create a stream view over.</param>
/// <returns>A read-only stream view over <paramref name="memory"/>.</returns>
public static MemoryStream AsStream(ReadOnlyMemory<byte> memory)
{
var segment = GetArraySegment(memory);
return new MemoryStream(segment.Array!, segment.Offset, segment.Count, false);
}
/// <summary>
/// Decodes the given memory as a string, using the specified encoding.
/// </summary>
/// <param name="memory">The memory to decode.</param>
/// <param name="encoding">The encoding to use. Must not be null.</param>
public static string GetString(ReadOnlyMemory<byte> memory, Encoding encoding)
{
Validation.CheckNotNull(encoding, nameof(encoding));
// TODO: If we introduce an additional netstandard2.1 target, we can use encoding.GetString(memory.Span)
var segment = GetArraySegment(memory);
return encoding.GetString(segment.Array!, segment.Offset, segment.Count);
}
/// <summary>
/// Copies the given memory to a stream, asynchronously.
/// </summary>
/// <param name="source">The source memory to copy from.</param>
/// <param name="destination">The stream to copy to. Must not be null.</param>
/// <returns>A task representing the asynchronous operation.</returns>
public static async Task CopyToStreamAsync(ReadOnlyMemory<byte> source, Stream destination)
{
Validation.CheckNotNull(destination, nameof(destination));
var segment = GetArraySegment(source);
await destination.WriteAsync(segment.Array!, segment.Offset, segment.Count).ConfigureAwait(false);
}
/// <summary>
/// Returns the data from <paramref name="memory"/> as a byte array, return the underlying array
/// if there is one, or creating a copy otherwise. This method should be used with care, due to the
/// "sometimes shared, sometimes not" nature of the result. (It is generally safe to use this with the result
/// of encoding a CloudEvent, assuming the same memory is not used elsewhere.)
/// </summary>
/// <param name="memory">The memory to obtain the data from.</param>
/// <returns>The data in <paramref name="memory"/> as an array.</returns>
public static byte[] AsArray(ReadOnlyMemory<byte> memory)
{
var segment = GetArraySegment(memory);
// We probably don't actually need to check the offset: if the count is the same as the length,
// I can't see how the offset can be non-zero. But it doesn't *hurt* as a check.
return segment.Offset == 0 && segment.Count == segment.Array!.Length
? segment.Array
: memory.ToArray();
}
// Note: when this returns, the Array property of the returned segment is guaranteed not to be null.
/// <summary>
/// Returns the data from <paramref name="memory"/> as a byte array, return the underlying array
/// if there is one, or creating a copy otherwise. This method should be used with care, due to the
/// "sometimes shared, sometimes not" nature of the result. (It is generally safe to use this with the result
/// of encoding a CloudEvent, assuming the same memory is not used elsewhere.)
/// </summary>
/// <param name="memory">The memory to obtain the data from.</param>
/// <returns>The data in <paramref name="memory"/> as an array segment.</returns>
public static ArraySegment<byte> GetArraySegment(ReadOnlyMemory<byte> memory) =>
MemoryMarshal.TryGetArray(memory, out var segment) && segment.Array is not null
? segment
: new ArraySegment<byte>(memory.ToArray());
}
}