Permissions, RateControl and Auditing

This commit is contained in:
2026-07-16 14:01:08 +03:00
parent 3a1b95dbc5
commit ba64a0c95a
62 changed files with 6095 additions and 2366 deletions
+211 -63
View File
@@ -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));
}
}