Skip to content

Instantly share code, notes, and snippets.

@jhgbrt
Last active December 30, 2015 08:30
Show Gist options
  • Save jhgbrt/5fedd37f0abfcfbdf64a to your computer and use it in GitHub Desktop.
Save jhgbrt/5fedd37f0abfcfbdf64a to your computer and use it in GitHub Desktop.
helper class to manage a segment of a byte array, without copying the underlying array
/// <summary>
/// Helper class to manage a (part of) an array of T (similar to the BCL's ArraySegment, but actually providing some useful functionality)
/// </summary>
public class ArraySegment<T> : IEnumerable<T>
{
private readonly T[] _rawArray;
private readonly int _offset;
private readonly int _length;
public ArraySegment(T[] rawArray, int offset, int length)
{
if (offset > rawArray.Length) throw new ArgumentOutOfRangeException("offset");
if (length + offset > rawArray.Length) throw new ArgumentOutOfRangeException("length");
_rawArray = rawArray;
_offset = offset;
_length = length;
}
public ArraySegment(T[] rawArray)
: this(rawArray, 0, rawArray.Length)
{
}
public T this[int index]
{
get { return _rawArray[_offset + index]; }
set { _rawArray[_offset + index] = value; }
}
/// <summary>
/// The raw underlying array of T
/// </summary>
protected internal T[] RawArray
{
get { return _rawArray; }
}
/// <summary>
/// Enumeration of the underlying instances of T that ar actually considered by this ArraySegment.
/// </summary>
protected IEnumerable<T> Items
{
get
{
for (int i = 0; i < _length; i++) yield return this[i];
}
}
#region IEnumerable
public IEnumerator<T> GetEnumerator()
{
return Items.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
/// <summary>
/// The number of items managed by this ArraySegment
/// </summary>
public int Length
{
get { return _length; }
}
/// <summary>
/// The offset from the start of the underlying array
/// </summary>
public int Offset
{
get { return _offset; }
}
/// <summary>
/// Returns a new ArraySegment instance, managing the underlying byte array
/// starting at the first item that doesn't satisfy the given condition.
/// </summary>
public ArraySegment<T> SkipWhile(Func<T, bool> condition)
{
var offset = Items
.Select((b, i) => new { b, i })
.SkipWhile(x => condition(x.b))
.Select(x => x.i)
.FirstOrDefault();
return At(offset);
}
/// <summary>
/// returns a new ArraySegment instance, managing the underlying byte array starting at offset
/// (relative to this ByteArraySegment); limited to the given length
/// </summary>
public ArraySegment<T> At(int offset, int length = 0)
{
return new ArraySegment<T>(RawArray, Offset + offset, length > 0 ? length : _length - offset);
}
/// <summary>
/// returns a new ArraySegment instance, managing the underlying byte array starting at current offset, limited to given length
/// </summary>
/// <param name="length"></param>
/// <returns></returns>
public ArraySegment<T> Resize(int length)
{
return At(0, length);
}
}
/// <summary>
/// Helper class to manage a (part of) an array of bytes
/// </summary>
public class ByteArraySegment : ArraySegment<byte>
{
public ByteArraySegment(byte[] rawArray, int offset, int length)
: base(rawArray, offset, length)
{
}
public ByteArraySegment(byte[] rawArray)
: base(rawArray)
{
}
private ByteArraySegment(ArraySegment<byte> segment)
: base(segment.RawArray, segment.Offset, segment.Length)
{
}
/// <summary>
/// Opens a stream (to be disposed by caller) for streaming the bytes from the underlying array managed by this ByteArraySegment
/// </summary>
public MemoryStream Stream()
{
return new MemoryStream(RawArray, Offset, Length);
}
/// <summary>
/// returns a new ByteArraySegment instance, managing the underlying byte array starting at offset
/// (relative to this ByteArraySegment); limited to the given length
/// </summary>
public new ByteArraySegment At(int offset, int length = 0)
{
return new ByteArraySegment(base.At(offset, length));
}
/// <summary>
/// Returns a new ByteArraySegment instance, managing the underlying byte array
/// starting at the first item that doesn't satisfy the given condition.
/// </summary>
public new ByteArraySegment SkipWhile(Func<byte, bool> condition)
{
return new ByteArraySegment(base.SkipWhile(condition));
}
/// <summary>
/// returns a new ByteArraySegment instance, managing the underlying byte array starting at current offset,
/// limited to given length
/// </summary>
public new ByteArraySegment Resize(int length)
{
return new ByteArraySegment(base.Resize(length));
}
/// <summary>
/// overwrites the rawArray managed by this ByteArraySegment with the data in the input array
/// </summary>
/// <param name="bytes"></param>
public void Write(byte[] bytes)
{
if (bytes.Length > Length) throw new ArgumentException("The input byte array is longer than the part managed by this ByteArraySegment instance", "bytes");
for (int i = 0; i < Length && i < bytes.Length; i++)
{
this[i] = bytes[i];
}
}
/// <summary>
/// Reads the rawArray in this ByteArraySegment as a null-terminated string
/// </summary>
/// <param name="encoding"></param>
/// <returns></returns>
public string ReadNullTerminatedString(Encoding encoding)
{
var item = Items
.Select((b, index) => new { b, index })
.TakeWhile(x => x.b != 0)
.LastOrDefault() ?? new { b = (byte)0, index = -1 };
var stringLength = item.index + 1;
return encoding.GetString(RawArray, Offset, stringLength);
}
public void WriteString(string value, Encoding encoding)
{
Write(encoding.GetBytes(value));
}
/// <summary>
/// Reads an Int32 from the first (4) bytes
/// </summary>
public int AsInt32()
{
return Read(reader => reader.ReadInt32());
}
/// <summary>
/// Reads an UInt32 from the first (4) bytes
/// </summary>
public uint AsUInt32()
{
return Read(reader => reader.ReadUInt32());
}
private T Read<T>(Func<BinaryReader, T> read)
{
using (var ms = Stream())
using (var reader = new BinaryReader(ms))
{
return read(reader);
}
}
public void Write(Action<BinaryWriter> write)
{
using (var ms = Stream())
using (var writer = new BinaryWriter(ms))
{
write(writer);
}
}
public override string ToString()
{
return ToString(32);
}
public string ToString(int max)
{
var sb = new StringBuilder();
using (var sw = new StringWriter(sb))
{
int idx = 0;
foreach (var x in Items.Take(max).Select((b, i) => new { b, i }))
{
idx++;
sw.Write("{0:00}", x.b);
if (idx == max) sw.Write("-...");
else if (idx < Length) sw.Write("-");
}
}
return sb.ToString();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment