-
-
Notifications
You must be signed in to change notification settings - Fork 30
Expand file tree
/
Copy pathBString.cs
More file actions
177 lines (148 loc) · 5.85 KB
/
BString.cs
File metadata and controls
177 lines (148 loc) · 5.85 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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
using System;
using System.IO;
using System.IO.Pipelines;
using System.Text;
namespace BencodeNET.Objects
{
/// <summary>
/// Represents a bencoded string, i.e. a byte-string.
/// It isn't necessarily human-readable.
/// </summary>
/// <remarks>
/// The underlying value is a <see cref="byte"/> array.
/// </remarks>
public sealed class BString : BObject<ReadOnlyMemory<byte>>, IComparable<BString>
{
/// <summary>
/// The maximum number of digits that can be handled as the length part of a bencoded string.
/// </summary>
internal const int LengthMaxDigits = 10;
/// <summary>
/// The underlying bytes of the string.
/// </summary>
public override ReadOnlyMemory<byte> Value { get; }
/// <summary>
/// Gets the length of the string in bytes.
/// </summary>
public int Length => Value.Length;
private static readonly Encoding DefaultEncoding = Encoding.UTF8;
/// <summary>
/// Gets or sets the encoding used as the default with <c>ToString()</c>.
/// </summary>
/// <exception cref="ArgumentNullException"></exception>
public Encoding Encoding
{
get => _encoding;
set => _encoding = value ?? DefaultEncoding;
}
private Encoding _encoding;
/// <summary>
/// Creates an empty <see cref="BString"/> ('0:').
/// </summary>
public BString()
: this((string)null)
{
}
/// <summary>
/// Creates a <see cref="BString"/> from bytes with the specified encoding.
/// </summary>
/// <param name="bytes">The bytes representing the data.</param>
/// <param name="encoding">The encoding of the bytes. Defaults to <see cref="System.Text.Encoding.UTF8"/>.</param>
public BString(byte[] bytes, Encoding encoding = null)
{
Value = bytes ?? throw new ArgumentNullException(nameof(bytes));
_encoding = encoding ?? DefaultEncoding;
}
/// <summary>
/// Creates a <see cref="BString"/> using the specified encoding to convert the string to bytes.
/// </summary>
/// <param name="str">The string.</param>
/// <param name="encoding">The encoding used to convert the string to bytes.</param>
/// <exception cref="ArgumentNullException"></exception>
public BString(string str, Encoding encoding = null)
{
_encoding = encoding ?? DefaultEncoding;
if (string.IsNullOrEmpty(str))
{
Value = Array.Empty<byte>();
}
else
{
var maxByteCount = _encoding.GetMaxByteCount(str.Length);
var span = new byte[maxByteCount].AsSpan();
var length = _encoding.GetBytes(str.AsSpan(), span);
Value = span.Slice(0, length).ToArray();
}
}
/// <inheritdoc/>
public override int GetSizeInBytes() => Value.Length + 1 + Value.Length.DigitCount();
/// <inheritdoc/>
protected override void EncodeObject(Stream stream)
{
stream.Write(Value.Length);
stream.Write(':');
stream.Write(Value.Span);
}
/// <inheritdoc/>
protected override void EncodeObject(PipeWriter writer)
{
// Init
var size = GetSizeInBytes();
var buffer = writer.GetSpan(size);
// Write length
var writtenBytes = Encoding.GetBytes(Value.Length.ToString().AsSpan(), buffer);
// Write ':'
buffer[writtenBytes] = (byte) ':';
// Write value
Value.Span.CopyTo(buffer.Slice(writtenBytes + 1));
// Commit
writer.Advance(size);
}
#pragma warning disable 1591
public static implicit operator BString(string value) => new BString(value);
public static bool operator ==(BString first, BString second)
{
return first?.Equals(second) ?? second is null;
}
public static bool operator !=(BString first, BString second) => !(first == second);
public override bool Equals(object other) => other is BString bstring && Value.Span.SequenceEqual(bstring.Value.Span);
public bool Equals(BString bstring) => bstring != null && Value.Span.SequenceEqual(bstring.Value.Span);
public override int GetHashCode()
{
var bytesToHash = Math.Min(Value.Length, 32);
long hashValue = 0;
for (var i = 0; i < bytesToHash; i++)
{
hashValue = (37 * hashValue + Value.Span[i]) % int.MaxValue;
}
return (int)hashValue;
}
public int CompareTo(BString other)
{
return Value.Span.SequenceCompareTo(other.Value.Span);
}
#pragma warning restore 1591
/// <summary>
/// Converts the underlying bytes to a string representation using the current value of the <see cref="Encoding"/> property.
/// </summary>
/// <returns>
/// A <see cref="System.String" /> that represents this instance.
/// </returns>
public override string ToString()
{
return _encoding.GetString(Value.Span);
}
/// <summary>
/// Converts the underlying bytes to a string representation using the specified encoding.
/// </summary>
/// <param name="encoding">The encoding to use to convert the underlying byte array to a <see cref="System.String" />.</param>
/// <returns>
/// A <see cref="System.String" /> that represents this instance.
/// </returns>
public string ToString(Encoding encoding)
{
encoding ??= _encoding;
return encoding.GetString(Value.Span);
}
}
}