Files

250 lines
8.7 KiB
C#

/*
This file is part of PacketDotNet
PacketDotNet is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
PacketDotNet is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PacketDotNet. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
namespace PacketDotNet.Utils
{
/// <summary>
/// Container class that refers to a segment of bytes in a byte[]
/// Used to ensure high performance by allowing memory copies to
/// be avoided
/// </summary>
public class ByteArraySegment
{
// NOTE: No need to warn about lack of use, the compiler won't
// put any calls to 'log' here but we need 'log' to exist to compile
#pragma warning disable 0169, 0649
private static readonly ILogInactive log;
#pragma warning restore 0169, 0649
private int length;
/// <value>
/// The byte[] array
/// </value>
public byte[] Bytes { get; private set; }
/// <value>
/// The maximum number of bytes we should treat Bytes as having, allows
/// for controling the number of bytes produced by EncapsulatedBytes()
/// </value>
public int BytesLength { get; private set; }
/// <value>
/// Number of bytes beyond the offset into Bytes
///
/// Take care when setting this parameter as many things are based on
/// the value of this property being correct
/// </value>
public int Length
{
get { return length; }
set
{
// check for invalid values
if(value < 0)
throw new System.InvalidOperationException("attempting to set a negative length of " + value);
length = value;
log.DebugFormat("Length: {0}", value);
}
}
/// <value>
/// Offset into Bytes
/// </value>
public int Offset { get; private set; }
/// <summary>
/// Constructor
/// </summary>
/// <param name="Bytes">
/// A <see cref="System.Byte[]"/>
/// </param>
public ByteArraySegment(byte[] Bytes) :
this(Bytes, 0, Bytes.Length)
{ }
/// <summary>
/// Constructor from a byte array, offset into the byte array and
/// a length beyond that offset of the bytes this class is referencing
/// </summary>
/// <param name="Bytes">
/// A <see cref="System.Byte"/>
/// </param>
/// <param name="Offset">
/// A <see cref="System.Int32"/>
/// </param>
/// <param name="Length">
/// A <see cref="System.Int32"/>
/// </param>
public ByteArraySegment(byte[] Bytes, int Offset, int Length)
: this(Bytes, Offset, Length, Bytes.Length)
{ }
/// <summary>
/// Constructor
/// </summary>
/// <param name="Bytes">
/// A <see cref="System.Byte[]"/>
/// </param>
/// <param name="Offset">
/// A <see cref="System.Int32"/>
/// </param>
/// <param name="Length">
/// A <see cref="System.Int32"/>
/// </param>
/// <param name="BytesLength">
/// A <see cref="System.Int32"/>
/// </param>
public ByteArraySegment(byte[] Bytes, int Offset, int Length, int BytesLength)
{
log.DebugFormat("Bytes.Length {0}, Offset {1}, Length {2}, BytesLength {3}",
Bytes.Length,
Offset,
Length,
BytesLength);
this.Bytes = Bytes;
this.Offset = Offset;
this.Length = Length;
this.BytesLength = Math.Min(BytesLength, Bytes.Length);
}
/// <summary>
/// Copy constructor
/// </summary>
/// <param name="original">
/// A <see cref="ByteArraySegment"/>
/// </param>
public ByteArraySegment(ByteArraySegment original)
{
this.Bytes = original.Bytes;
this.Offset = original.Offset;
this.Length = original.Length;
this.BytesLength = original.BytesLength;
}
/// <summary>
/// Returns a contiguous byte[] from this container, if necessary, by copying
/// the bytes from the current offset into a newly allocated byte[].
/// NeedsCopyForActualBytes can be used to determine if the copy is necessary
///
/// </summary>
/// <returns>
/// A <see cref="System.Byte"/>
/// </returns>
public byte[] ActualBytes()
{
log.DebugFormat("{0}", ToString());
if(NeedsCopyForActualBytes)
{
log.Debug("needs copy");
var newBytes = new byte[Length];
Array.Copy(Bytes, Offset, newBytes, 0, Length);
return newBytes;
} else
{
log.Debug("does not need copy");
return Bytes;
}
}
/// <summary>
/// Return true if we need to perform a copy to get
/// the bytes represented by this class
/// </summary>
/// <returns>
/// A <see cref="System.Boolean"/>
/// </returns>
public bool NeedsCopyForActualBytes
{
get
{
// we need a copy unless we are at the start of the byte[]
// and the length is the total byte[] length
var okWithoutCopy = ((Offset == 0) && (Length == Bytes.Length));
var retval = !okWithoutCopy;
log.DebugFormat("retval {0}", retval);
return retval;
}
}
/// <summary>
/// Helper method that returns the segment immediately following
/// this instance, useful for processing where the parent
/// wants to pass the next segment to a sub class for processing
/// </summary>
/// <returns>
/// A <see cref="ByteArraySegment"/>
/// </returns>
public ByteArraySegment EncapsulatedBytes()
{
var numberOfBytesAfterThisSegment = BytesLength - (Offset + Length);
return EncapsulatedBytes(numberOfBytesAfterThisSegment);
}
/// <summary>
/// Create the segment after the current one
/// </summary>
/// <param name="NewSegmentLength">
/// A <see cref="System.Int32"/> that can be used to limit the segment length
/// of the ByteArraySegment that is to be returned. Often used to exclude trailing bytes.
/// </param>
/// <returns>
/// A <see cref="ByteArraySegment"/>
/// </returns>
public ByteArraySegment EncapsulatedBytes(int NewSegmentLength)
{
log.DebugFormat("NewSegmentLength {0}", NewSegmentLength);
int startingOffset = Offset + Length; // start at the end of the current segment
log.DebugFormat("startingOffset({0}) = Offset({1}) + Length({2})",
startingOffset,
Offset,
Length);
// ensure that the new segment length isn't longer than the number of bytes
// available after the current segment
NewSegmentLength = Math.Min(NewSegmentLength, BytesLength - startingOffset);
// calculate the ByteLength property of the new ByteArraySegment
int NewByteLength = startingOffset + NewSegmentLength;
log.DebugFormat("NewSegmentLength {0}, NewByteLength {1}, BytesLength {2}",
NewSegmentLength, NewByteLength, BytesLength);
return new ByteArraySegment(Bytes, startingOffset, NewSegmentLength, NewByteLength);
}
/// <summary>
/// Format the class information as a string
/// </summary>
/// <returns>
/// A <see cref="System.String"/>
/// </returns>
public override string ToString ()
{
return string.Format("[ByteArraySegment: Length={0}, Bytes.Length={1}, BytesLength={2}, Offset={3}, NeedsCopyForActualBytes={4}]",
Length, Bytes.Length, BytesLength, Offset, NeedsCopyForActualBytes);
}
}
}