Last active
December 30, 2015 08:30
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <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