mirror of
https://github.com/esiur/esiur-dotnet.git
synced 2026-07-30 17:30:40 +00:00
Permissions, RateControl and Auditing
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
|
||||
Copyright (c) 2017 Ahmed Kh. Zamil
|
||||
|
||||
@@ -23,31 +23,25 @@ SOFTWARE.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Esiur.Data;
|
||||
using Esiur.Misc;
|
||||
|
||||
namespace Esiur.Net;
|
||||
|
||||
public class NetworkBuffer
|
||||
{
|
||||
byte[] data;
|
||||
private static readonly byte[] Empty = Array.Empty<byte>();
|
||||
|
||||
uint neededDataLength = 0;
|
||||
|
||||
object syncLock = new object();
|
||||
|
||||
public NetworkBuffer()
|
||||
{
|
||||
data = new byte[0];
|
||||
}
|
||||
private readonly object syncLock = new object();
|
||||
private byte[] buffer = Empty;
|
||||
private int start;
|
||||
private int length;
|
||||
private uint neededDataLength;
|
||||
|
||||
public bool Protected
|
||||
{
|
||||
get
|
||||
{
|
||||
return neededDataLength > data.Length;
|
||||
lock (syncLock)
|
||||
return (uint)length < neededDataLength;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,76 +49,88 @@ public class NetworkBuffer
|
||||
{
|
||||
get
|
||||
{
|
||||
return (uint)data.Length;
|
||||
lock (syncLock)
|
||||
return (uint)length;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void HoldForNextWrite(byte[] src)
|
||||
{
|
||||
HoldFor(src, (uint)src.Length + 1);
|
||||
if (src == null)
|
||||
throw new ArgumentNullException(nameof(src));
|
||||
|
||||
HoldFor(src, 0, (uint)src.Length, CheckedNextLength((uint)src.Length));
|
||||
}
|
||||
|
||||
public void HoldForNextWrite(byte[] src, uint offset, uint size)
|
||||
{
|
||||
HoldFor(src, offset, size, size + 1);
|
||||
}
|
||||
|
||||
=> HoldFor(src, offset, size, CheckedNextLength(size));
|
||||
|
||||
public void HoldFor(byte[] src, uint offset, uint size, uint needed)
|
||||
{
|
||||
ValidateRange(src, offset, size);
|
||||
ValidateNeededLength(needed);
|
||||
|
||||
if (size >= needed)
|
||||
// Preserve the historical exception contract for this semantic error.
|
||||
throw new Exception("Size >= Needed !");
|
||||
|
||||
lock (syncLock)
|
||||
{
|
||||
if (size >= needed)
|
||||
throw new Exception("Size >= Needed !");
|
||||
|
||||
//trim = true;
|
||||
data = DC.Combine(src, offset, size, data, 0, (uint)data.Length);
|
||||
Prepend(src, (int)offset, (int)size);
|
||||
neededDataLength = needed;
|
||||
}
|
||||
}
|
||||
|
||||
public void HoldFor(byte[] src, uint needed)
|
||||
{
|
||||
if (src == null)
|
||||
throw new ArgumentNullException(nameof(src));
|
||||
|
||||
HoldFor(src, 0, (uint)src.Length, needed);
|
||||
}
|
||||
|
||||
public bool Protect(byte[] data, uint offset, uint needed)
|
||||
{
|
||||
uint dataLength = (uint)data.Length - offset;
|
||||
if (data == null)
|
||||
throw new ArgumentNullException(nameof(data));
|
||||
if (offset > (uint)data.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(offset));
|
||||
|
||||
// protection
|
||||
if (dataLength < needed)
|
||||
{
|
||||
HoldFor(data, offset, dataLength, needed);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
ValidateNeededLength(needed);
|
||||
|
||||
var dataLength = (uint)data.Length - offset;
|
||||
if (dataLength >= needed)
|
||||
return false;
|
||||
|
||||
// HoldFor validates that dataLength is strictly less than needed.
|
||||
HoldFor(data, offset, dataLength, needed);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Write(byte[] src)
|
||||
{
|
||||
if (src == null)
|
||||
throw new ArgumentNullException(nameof(src));
|
||||
|
||||
Write(src, 0, (uint)src.Length);
|
||||
}
|
||||
|
||||
public void Write(byte[] src, uint offset, uint length)
|
||||
{
|
||||
ValidateRange(src, offset, length);
|
||||
if (length == 0)
|
||||
return;
|
||||
|
||||
lock (syncLock)
|
||||
DC.Append(ref data, src, offset, length);
|
||||
Append(src, (int)offset, (int)length);
|
||||
}
|
||||
|
||||
public bool CanRead
|
||||
{
|
||||
get
|
||||
{
|
||||
if (data.Length == 0)
|
||||
return false;
|
||||
if (data.Length < neededDataLength)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
lock (syncLock)
|
||||
return length != 0 && (uint)length >= neededDataLength;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,34 +138,176 @@ public class NetworkBuffer
|
||||
{
|
||||
lock (syncLock)
|
||||
{
|
||||
if (data.Length == 0)
|
||||
if (length == 0 || (uint)length < neededDataLength)
|
||||
return null;
|
||||
|
||||
byte[] rt = null;
|
||||
byte[] result;
|
||||
|
||||
if (neededDataLength == 0)
|
||||
// A single write, and geometrically grown power-of-two payloads, can transfer
|
||||
// ownership without one final copy. Otherwise return an exact-size array, as the
|
||||
// historical API did, and release the working buffer.
|
||||
if (start == 0 && length == buffer.Length)
|
||||
{
|
||||
rt = data;
|
||||
data = new byte[0];
|
||||
return rt;
|
||||
result = buffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (data.Length >= neededDataLength)
|
||||
{
|
||||
rt = data;
|
||||
data = new byte[0];
|
||||
|
||||
neededDataLength = 0;
|
||||
return rt;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
result = new byte[length];
|
||||
Buffer.BlockCopy(buffer, start, result, 0, length);
|
||||
}
|
||||
|
||||
buffer = Empty;
|
||||
start = 0;
|
||||
length = 0;
|
||||
neededDataLength = 0;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private void Append(byte[] src, int offset, int count)
|
||||
{
|
||||
if (length == 0 && buffer.Length == 0)
|
||||
{
|
||||
// The overwhelmingly common path is one socket read followed by one Read().
|
||||
// Allocate exactly once so Read can transfer this array without copying it.
|
||||
buffer = new byte[count];
|
||||
Buffer.BlockCopy(src, offset, buffer, 0, count);
|
||||
start = 0;
|
||||
length = count;
|
||||
return;
|
||||
}
|
||||
|
||||
var requiredLength = CheckedCombinedLength(length, count);
|
||||
var writeOffset = start + length;
|
||||
|
||||
if (buffer.Length - writeOffset < count)
|
||||
{
|
||||
if (requiredLength <= buffer.Length)
|
||||
{
|
||||
// Reuse headroom left by a prepend operation.
|
||||
Buffer.BlockCopy(buffer, start, buffer, 0, length);
|
||||
start = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Grow(requiredLength, prependLength: 0);
|
||||
}
|
||||
|
||||
writeOffset = start + length;
|
||||
}
|
||||
|
||||
Buffer.BlockCopy(src, offset, buffer, writeOffset, count);
|
||||
length = requiredLength;
|
||||
}
|
||||
|
||||
private void Prepend(byte[] src, int offset, int count)
|
||||
{
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
if (length == 0 && buffer.Length == 0)
|
||||
{
|
||||
buffer = new byte[count];
|
||||
Buffer.BlockCopy(src, offset, buffer, 0, count);
|
||||
start = 0;
|
||||
length = count;
|
||||
return;
|
||||
}
|
||||
|
||||
var requiredLength = CheckedCombinedLength(length, count);
|
||||
|
||||
if (start >= count)
|
||||
{
|
||||
start -= count;
|
||||
}
|
||||
else if (requiredLength <= buffer.Length)
|
||||
{
|
||||
// Re-center the live region once and leave any remaining spare capacity around it.
|
||||
var combinedStart = (buffer.Length - requiredLength) / 2;
|
||||
Buffer.BlockCopy(buffer, start, buffer, combinedStart + count, length);
|
||||
start = combinedStart;
|
||||
}
|
||||
else
|
||||
{
|
||||
Grow(requiredLength, count);
|
||||
}
|
||||
|
||||
Buffer.BlockCopy(src, offset, buffer, start, count);
|
||||
length = requiredLength;
|
||||
}
|
||||
|
||||
private void Grow(int requiredLength, int prependLength)
|
||||
{
|
||||
var newCapacity = GetExpandedCapacity(buffer.Length, requiredLength);
|
||||
var replacement = new byte[newCapacity];
|
||||
|
||||
if (prependLength == 0)
|
||||
{
|
||||
if (length > 0)
|
||||
Buffer.BlockCopy(buffer, start, replacement, 0, length);
|
||||
start = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
var combinedStart = (newCapacity - requiredLength) / 2;
|
||||
if (length > 0)
|
||||
Buffer.BlockCopy(buffer, start, replacement, combinedStart + prependLength, length);
|
||||
start = combinedStart;
|
||||
}
|
||||
|
||||
buffer = replacement;
|
||||
}
|
||||
|
||||
private static int GetExpandedCapacity(int currentCapacity, int requiredLength)
|
||||
{
|
||||
var capacity = currentCapacity == 0 ? 256 : currentCapacity;
|
||||
|
||||
while (capacity < requiredLength)
|
||||
{
|
||||
if (capacity > int.MaxValue / 2)
|
||||
return requiredLength;
|
||||
|
||||
capacity *= 2;
|
||||
}
|
||||
|
||||
return capacity;
|
||||
}
|
||||
|
||||
private static int CheckedCombinedLength(int currentLength, int additionalLength)
|
||||
{
|
||||
if (additionalLength > int.MaxValue - currentLength)
|
||||
throw new ArgumentOutOfRangeException(
|
||||
nameof(additionalLength),
|
||||
"The buffered data exceeds the maximum managed array length.");
|
||||
|
||||
return currentLength + additionalLength;
|
||||
}
|
||||
|
||||
private static uint CheckedNextLength(uint size)
|
||||
{
|
||||
if (size >= int.MaxValue)
|
||||
throw new ArgumentOutOfRangeException(
|
||||
nameof(size),
|
||||
"The requested held length exceeds the maximum managed array length.");
|
||||
|
||||
return size + 1;
|
||||
}
|
||||
|
||||
private static void ValidateNeededLength(uint needed)
|
||||
{
|
||||
if (needed > int.MaxValue)
|
||||
throw new ArgumentOutOfRangeException(
|
||||
nameof(needed),
|
||||
"The requested held length exceeds the maximum managed array length.");
|
||||
}
|
||||
|
||||
private static void ValidateRange(byte[] src, uint offset, uint count)
|
||||
{
|
||||
if (src == null)
|
||||
throw new ArgumentNullException(nameof(src));
|
||||
if (offset > (uint)src.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(offset));
|
||||
if (count > (uint)src.Length - offset)
|
||||
throw new ArgumentOutOfRangeException(nameof(count));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user