/// <summary>Delete the first N bytes of the buffer, and shift the remaining to the front</summary> /// <param name="bytes">Number of bytes to remove at the head of the buffer</param> /// <returns>New size of the buffer (or 0 if it is empty)</returns> /// <remarks>This should be called after every successfull write to the underlying stream, to update the buffer.</remarks> public int Flush(int bytes) { if (bytes == 0) { return(this.Position); } if (bytes < 0) { throw new ArgumentOutOfRangeException("bytes"); } if (bytes < this.Position) { // copy the left over data to the start of the buffer int remaining = this.Position - bytes; SliceHelpers.CopyBytesUnsafe(this.Buffer, 0, this.Buffer, bytes, remaining); this.Position = remaining; return(remaining); } else { //REVIEW: should we throw if there are less bytes in the buffer than we want to flush ? this.Position = 0; return(0); } }
/// <summary>Writes a length-prefixed byte array, and advances the cursor</summary> public void WriteVarbytes(Slice value) { //REVIEW: what should we do for Slice.Nil ? SliceHelpers.EnsureSliceIsValid(ref value); int n = value.Count; if (n < 128) { EnsureBytes(n + 1); var buffer = this.Buffer; int p = this.Position; // write the count (single byte) buffer[p] = (byte)n; // write the bytes if (n > 0) { SliceHelpers.CopyBytesUnsafe(buffer, p + 1, value.Array, value.Offset, n); } this.Position = p + n + 1; } else { // write the count WriteVarint32((uint)value.Count); // write the bytes SliceHelpers.CopyBytesUnsafe(this.Buffer, this.Position, value.Array, value.Offset, n); this.Position += n; } }
/// <summary>Copy the content of a byte segment into another. CAUTION: The arguments are NOT in the same order as Buffer.BlockCopy() or Array.Copy() !</summary> /// <param name="dst">Destination buffer</param> /// <param name="dstOffset">Offset in destination buffer</param> /// <param name="src">Source buffer</param> /// <param name="srcOffset">Offset in source buffer</param> /// <param name="count">Number of bytes to copy</param> /// <remarks>CAUTION: THE ARGUMENTS ARE REVERSED! They are in the same order as memcpy() and memmove(), with destination first, and source second!</remarks> public static void CopyBytes(byte[] dst, int dstOffset, byte[] src, int srcOffset, int count) { SliceHelpers.EnsureBufferIsValid(dst, dstOffset, count); SliceHelpers.EnsureBufferIsValid(src, srcOffset, count); CopyBytesUnsafe(dst, dstOffset, src, srcOffset, count); }
/// <summary>Creates a new binary buffer, initialized by copying pre-existing data</summary> /// <param name="prefix">Data that will be copied at the start of the buffer</param> /// <param name="capacity">Optional initial capacity of the buffer</param> /// <remarks>The cursor will already be placed at the end of the prefix</remarks> public SliceWriter(Slice prefix, int capacity = 0) { if (capacity < 0) { throw new ArgumentException("Capacity must be a positive integer.", "capacity"); } int n = prefix.Count; Contract.Assert(n >= 0); if (capacity == 0) { // most frequent usage is to add a packed integer at the end of a prefix capacity = SliceHelpers.Align(n + 8); } else { capacity = Math.Max(capacity, n); } var buffer = new byte[capacity]; if (n > 0) { prefix.CopyTo(buffer, 0); } this.Buffer = buffer; this.Position = n; }
/// <summary>Compare two byte segments lexicographically</summary> /// <param name="left">Left buffer</param> /// <param name="leftOffset">Start offset in left buffer</param> /// <param name="leftCount">Number of bytes in left buffer</param> /// <param name="right">Right buffer</param> /// <param name="rightOffset">Start offset in right buffer</param> /// <param name="rightCount">Number of bytes in right buffer</param> /// <returns>Returns zero if segments are identical (same bytes), a negative value if left is lexicographically less than right, or a positive value if left is lexicographically greater than right</returns> /// <remarks>The comparison algorithm respect the following: /// * "A" < "B" /// * "A" < "AA" /// * "AA" < "B"</remarks> public static int CompareBytes(byte[] left, int leftOffset, int leftCount, byte[] right, int rightOffset, int rightCount) { SliceHelpers.EnsureBufferIsValid(left, leftOffset, leftCount); SliceHelpers.EnsureBufferIsValid(right, rightOffset, rightCount); return(CompareBytesUnsafe(left, leftOffset, leftCount, right, rightOffset, rightCount)); }
/// <summary>Computes the hash code of a slice</summary> /// <param name="obj">A slice</param> /// <returns>A 32-bit signed hash coded calculated from all the bytes in the slice</returns> public int GetHashCode(Slice obj) { if (obj.Array == null) { return(0); } return(SliceHelpers.ComputeHashCode(obj.Array, obj.Offset, obj.Count)); }
/// <summary>Compute the hash code of a byte segment</summary> /// <param name="bytes">Buffer</param> /// <param name="offset">Offset of the start of the segment in the buffer</param> /// <param name="count">Number of bytes in the segment</param> /// <returns>A 32-bit signed hash code calculated from all the bytes in the segment.</returns> public static int ComputeHashCode([NotNull] byte[] bytes, int offset, int count) { if (bytes == null || offset < 0 || count < 0 || offset + count > bytes.Length) { SliceHelpers.ThrowMalformedBuffer(bytes, offset, count); } return(ComputeHashCodeUnsafe(bytes, offset, count)); }
internal void UnsafeWriteBytes(byte[] data, int offset, int count) { Contract.Requires(this.Buffer != null && this.Position >= 0 && data != null && count >= 0 && this.Position + count <= this.Buffer.Length && offset >= 0 && offset + count <= data.Length); if (count > 0) { SliceHelpers.CopyBytesUnsafe(this.Buffer, this.Position, data, offset, count); this.Position += count; } }
/// <summary>Compare two byte segments for equality</summary> /// <param name="left">Left buffer</param> /// <param name="leftOffset">Start offset in left buffer</param> /// <param name="right">Right buffer</param> /// <param name="rightOffset">Start offset in right buffer</param> /// <param name="count">Number of bytes to compare</param> /// <returns>true if all bytes are the same in both segments</returns> public static bool SameBytes(byte[] left, int leftOffset, byte[] right, int rightOffset, int count) { SliceHelpers.EnsureBufferIsValid(left, leftOffset, count); SliceHelpers.EnsureBufferIsValid(right, rightOffset, count); if (left == null || right == null) { return(left == right); } return(SameBytesUnsafe(left, leftOffset, right, rightOffset, count)); }
/// <summary>Set the value of a network option on the database handler</summary> private static FdbError SetNetworkOption(FdbNetworkOption option, Slice value) { SliceHelpers.EnsureSliceIsValid(ref value); unsafe { fixed(byte *ptr = value.Array) { return(FdbNative.NetworkSetOption(option, ptr + value.Offset, value.Count)); } } }
/// <summary>Append a chunk of a byte array to the end of the buffer</summary> /// <param name="data"></param> /// <param name="offset"></param> /// <param name="count"></param> public void WriteBytes(byte[] data, int offset, int count) { SliceHelpers.EnsureBufferIsValid(data, offset, count); if (count > 0) { EnsureBytes(count); SliceHelpers.CopyBytesUnsafe(this.Buffer, this.Position, data, offset, count); this.Position += count; } }
internal unsafe void UnsafeWriteBytes(byte *data, int count) { if (count <= 0) { return; } Contract.Requires(this.Buffer != null && this.Position >= 0 && data != null && count >= 0 && this.Position + count <= this.Buffer.Length); SliceHelpers.CopyBytesUnsafe(this.Buffer, this.Position, data, count); this.Position += count; }
/// <summary>Lexicographically compare two slices and returns an indication of their relative sort order.</summary> /// <param name="x">Slice compared with <paramref name="y"/></param> /// <param name="y">Slice compared with <paramref name="x"/></param> /// <returns>Returns a NEGATIVE value if <paramref name="x"/> is LESS THAN <paramref name="y"/>, ZERO if <paramref name="x"/> is EQUAL TO <paramref name="y"/>, and a POSITIVE value if <paramref name="x"/> is GREATER THAN <paramref name="y"/>.</returns> /// <remarks> /// <para>If both <paramref name="x"/> and <paramref name="y"/> are nil or empty, the comparison will return ZERO. If only <paramref name="y"/> is nil or empty, it will return a NEGATIVE value. If only <paramref name="x"/> is nil or empty, it will return a POSITIVE value.</para> /// <para>There are no guarantees that non-zero results will be exactly -1 or +1. You should always use comparison operators or the sign of the returned value, instead of testing for equality with -1 or +1.</para> /// </remarks> public int Compare(Slice x, Slice y) { //REVIEW: cmp(Nil, Empty) returns 0 but Nil != Empty ? if (x.Count == 0) { return(y.Count == 0 ? 0 : -1); } if (y.Count == 0) { return(+1); } return(SliceHelpers.CompareBytes(x.Array, x.Offset, x.Count, y.Array, y.Offset, y.Count)); }
public byte[] GetBytes() { Contract.Requires(this.Position >= 0); var bytes = new byte[this.Position]; if (this.Position > 0) { Contract.Assert(this.Buffer != null && this.Buffer.Length >= this.Position); SliceHelpers.CopyBytesUnsafe(bytes, 0, this.Buffer, 0, bytes.Length); } return(bytes); }
/// <summary>Append a segment of bytes to the end of the buffer</summary> public void WriteBytes(Slice data) { SliceHelpers.EnsureSliceIsValid(ref data); int n = data.Count; if (n > 0) { EnsureBytes(n); SliceHelpers.CopyBytesUnsafe(this.Buffer, this.Position, data.Array, data.Offset, n); this.Position += n; } }
/// <summary>Resize a buffer by doubling its capacity</summary> /// <param name="buffer">Reference to the variable holding the buffer to create/resize. If null, a new buffer will be allocated. If not, the content of the buffer will be copied into the new buffer.</param> /// <param name="minimumCapacity">Mininum guaranteed buffer size after resizing.</param> /// <remarks>The buffer will be resized to the maximum betweeb the previous size multiplied by 2, and <paramref name="minimumCapacity"/>. The capacity will always be rounded to a multiple of 16 to reduce memory fragmentation</remarks> public static void GrowBuffer(ref byte[] buffer, int minimumCapacity = 0) { Contract.Requires(minimumCapacity >= 0); // double the size of the buffer, or use the minimum required long newSize = Math.Max(buffer == null ? 0 : (((long)buffer.Length) << 1), minimumCapacity); // .NET (as of 4.5) cannot allocate an array with more than 2^31 - 1 items... if (newSize > 0x7fffffffL) { FailCannotGrowBuffer(); } // round up to 16 bytes, to reduce fragmentation int size = SliceHelpers.Align((int)newSize); Array.Resize(ref buffer, size); }
/// <summary>Empties the current buffer after a succesfull write</summary> /// <remarks>Shrink the buffer if a lot of memory is wated</remarks> public void Reset() { if (this.Position > 0) { // reduce size ? // If the buffer exceeds 4K and we used less than 1/8 of it the last time, we will "shrink" the buffer if (this.Buffer.Length > 4096 && (this.Position << 3) <= Buffer.Length) { // Shrink it Buffer = new byte[SliceHelpers.NextPowerOfTwo(this.Position)]; } else { // Clear it //TODO: native memset() ? Array.Clear(Buffer, 0, this.Position); } this.Position = 0; } }
/// <summary>Append a chunk of memory to the end of the buffer</summary> public unsafe void WriteBytesUnsafe(byte *data, int count) { if (data == null) { throw new ArgumentNullException("data"); } if (count < 0) { throw new ArgumentOutOfRangeException("count"); } if (count > 0) { EnsureBytes(count); SliceHelpers.CopyBytesUnsafe(this.Buffer, this.Position, data, count); this.Position += count; } }
internal unsafe void WriteBytes(byte *data, int count) { if (count == 0) { return; } if (data == null) { throw new ArgumentNullException("data"); } if (count < 0) { throw new ArgumentException("count"); } EnsureBytes(count); Contract.Assert(this.Buffer != null && this.Position >= 0 && this.Position + count <= this.Buffer.Length); SliceHelpers.CopyBytesUnsafe(this.Buffer, this.Position, data, count); this.Position += count; }
/// <summary>Fill the content of a managed segment with the same byte repeated</summary> public static void SetBytes(byte[] bytes, int offset, int count, byte value) { SliceHelpers.EnsureBufferIsValid(bytes, offset, count); if (count <= 8) { // for very small keys, the cost of pinning and marshalling may be to high while (count-- > 0) { bytes[offset++] = value; } } else { unsafe { fixed(byte *ptr = bytes) { SetMemoryUnsafe(ptr + offset, value, count); } } } }
/// <summary>Checks if two slices are equal.</summary> /// <param name="x">Slice compared with <paramref name="y"/></param> /// <param name="y">Slice compared with <paramref name="x"/></param> /// <returns>true if <paramref name="x"/> and <paramref name="y"/> have the same size and contain the same sequence of bytes; otherwise, false.</returns> public bool Equals(Slice x, Slice y) { return(x.Count == y.Count && SliceHelpers.SameBytes(x.Array, x.Offset, y.Array, y.Offset, y.Count)); }