See More

using System; using System.IO; using System.IO.Pipelines; using System.Text; namespace BencodeNET.Objects { ///

/// Represents a bencoded string, i.e. a byte-string. /// It isn't necessarily human-readable. /// /// /// The underlying value is a array. /// public sealed class BString : BObject>, IComparable { /// /// The maximum number of digits that can be handled as the length part of a bencoded string. /// internal const int LengthMaxDigits = 10; /// /// The underlying bytes of the string. /// public override ReadOnlyMemory Value { get; } /// /// Gets the length of the string in bytes. /// public int Length => Value.Length; private static readonly Encoding DefaultEncoding = Encoding.UTF8; /// /// Gets or sets the encoding used as the default with ToString(). /// /// public Encoding Encoding { get => _encoding; set => _encoding = value ?? DefaultEncoding; } private Encoding _encoding; /// /// Creates an empty ('0:'). /// public BString() : this((string)null) { } /// /// Creates a from bytes with the specified encoding. /// /// The bytes representing the data. /// The encoding of the bytes. Defaults to . public BString(byte[] bytes, Encoding encoding = null) { Value = bytes ?? throw new ArgumentNullException(nameof(bytes)); _encoding = encoding ?? DefaultEncoding; } /// /// Creates a using the specified encoding to convert the string to bytes. /// /// The string. /// The encoding used to convert the string to bytes. /// public BString(string str, Encoding encoding = null) { _encoding = encoding ?? DefaultEncoding; if (string.IsNullOrEmpty(str)) { Value = Array.Empty(); } 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(); } } /// public override int GetSizeInBytes() => Value.Length + 1 + Value.Length.DigitCount(); /// protected override void EncodeObject(Stream stream) { stream.Write(Value.Length); stream.Write(':'); stream.Write(Value.Span); } /// 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 /// /// Converts the underlying bytes to a string representation using the current value of the property. /// /// /// A that represents this instance. /// public override string ToString() { return _encoding.GetString(Value.Span); } /// /// Converts the underlying bytes to a string representation using the specified encoding. /// /// The encoding to use to convert the underlying byte array to a . /// /// A that represents this instance. /// public string ToString(Encoding encoding) { encoding ??= _encoding; return encoding.GetString(Value.Span); } } }