using System;
using System.Text;
namespace LaunchDarkly.EventSource
{
///
/// Points to a span of UTF-8-encoded text in a buffer.
///
///
/// This is similar to the Span type in .NET 5. It is used internally by
/// EventSource to store event data if the stream is using UTF-8 encoding.
/// If so, reading will return the same
/// byte array, to avoid unnecessary copying.
///
public struct Utf8ByteSpan
{
///
/// The byte array containing the data. May be null if is zero.
///
///
/// It is the caller's responsibility not to modify the array.
///
public byte[] Data { get; }
///
/// The offset of the first relevant byte of data within the array. This may be
/// greater than zero if the span represents a subset of a larger buffer.
///
public int Offset { get; }
///
/// The number of bytes of relevant data within the array. This may be less than
/// Data.Length if the span represents a subset of a larger buffer.
///
public int Length { get; }
///
/// Constructs a new instance.
///
/// The byte array containing the data.
/// The offset of the first relevant byte of data within the array.
/// The number of bytes of relevant data within the array.
public Utf8ByteSpan(byte[] data, int offset, int length)
{
Data = length == 0 ? null : data;
Offset = offset;
Length = length;
}
///
/// Constructs a new instance by copying a string.
///
/// A string to convert to UTF-8 bytes.
public Utf8ByteSpan(string s)
{
Data = s.Length == 0 ? null : Encoding.UTF8.GetBytes(s);
Offset = 0;
Length = Data is null ? 0 : Data.Length;
}
///
/// Converts the UTF-8 byte data to a string.
///
/// A new string.
public string GetString() => Data is null ? "" : Encoding.UTF8.GetString(Data, Offset, Length);
///
/// Tests whether the bytes in this span are the same as another span.
///
/// Another Utf8ByteSpan.
/// True if the two spans have the same length and the same
/// data, starting from each one's Offset.
public bool Equals(Utf8ByteSpan other)
{
var len = Length;
if (len != other.Length)
{
return false;
}
int offset = Offset, otherOffset = other.Offset;
byte[] data = Data, otherData = other.Data;
for (int i = 0; i < len; i++)
{
if (data[i + offset] != otherData[i + otherOffset])
{
return false;
}
}
return true;
}
///
/// Tests whether the bytes in this span are the same as the UTF-8 encoding
/// of the specified string.
///
///
/// This method is potentially inefficient and should be used only in testing.
///
/// A string.
/// True if the bytes are equivalent.
public bool Equals(string s)
{
byte[] data = Data;
int offset = Offset, len = Length;
for (int i = 0; i < len; i++)
{
if (data[offset + i] > 127)
{
// There are multi-byte characters in the span, so we can't do a 1:1 comparison of bytes
return GetString().Equals(s);
}
}
// There are no multi-byte characters, so we don't need to convert to or from a UTF-16 string
if (len != s.Length)
{
return false;
}
for (int i = 0; i < len; i++)
{
if ((char)data[offset + i] != s[i])
{
return false;
}
}
return true;
}
}
}