[LOADED_DOCS: 7 files, no new loads]
AcBinary: add framing doc, buffer growth fixes, doc updates - Added `BINARY_WHYUSE.md` for architectural framing and value proposition - Updated `BINARY_FEATURES.md` and `README.md` to reference and prioritize the new doc - Documented AsyncPipeWriterOutput chunk-size limitation and workarounds in `BINARY_ASYNCPIPE_ISSUES.md` - Refactored buffer growth logic in `AcBinarySerializer.BinarySerializationContext.cs` to validate capacity after grow and throw clear exceptions on under-provisioning; removed dead method - Fixed chunk size alignment bug in `AsyncPipeWriterOutput.cs` to prevent buffer under-provisioning - Added `AYCODE_NATIVEAOT` build config support in `Program.cs` - Improved documentation clarity and error diagnostics for streaming/buffered serialization edge cases
This commit is contained in:
parent
96c09a65bb
commit
73d81ea580
|
|
@ -34,7 +34,9 @@ public static class Program
|
|||
{
|
||||
private const string ResultsDirectory = @"H:\Applications\Aycode\Source\AyCode.Core\Test_Benchmark_Results\Benchmark";
|
||||
|
||||
#if DEBUG
|
||||
#if AYCODE_NATIVEAOT
|
||||
private const string BuildConfiguration = "NativeAOT";
|
||||
#elif DEBUG
|
||||
private const string BuildConfiguration = "Debug";
|
||||
#else
|
||||
private const string BuildConfiguration = "Release";
|
||||
|
|
|
|||
|
|
@ -364,18 +364,25 @@ public static partial class AcBinarySerializer
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void EnsureCapacity(int additionalBytes)
|
||||
{
|
||||
if (_position + additionalBytes > _bufferEnd)
|
||||
Output.Grow(ref _buffer, ref _position, ref _bufferEnd, additionalBytes);
|
||||
if (_position + additionalBytes > _bufferEnd) GrowAndValidate(additionalBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the buffer has enough space for the specified number of bytes.
|
||||
/// Called before property writes to avoid mid-object Grow() calls.
|
||||
/// Slow path for <see cref="EnsureCapacity"/>: invokes <c>Output.Grow</c> and revalidates that
|
||||
/// the request was actually satisfied. A chunk-limited output (e.g. <c>AsyncPipeWriterOutput</c>
|
||||
/// on a single-value write > <c>MaxChunkSize</c> data bytes) may return WITHOUT growing the
|
||||
/// active buffer to the requested size — silent under-provisioning would cause downstream
|
||||
/// <c>ArgumentOutOfRangeException</c> in <c>AsSpan</c>/<c>CopyTo</c> at corrupt offsets.
|
||||
/// <c>NoInlining</c> keeps the hot path of <see cref="EnsureCapacity"/> tiny and inline-friendly.
|
||||
/// Related limit: see <c>BINARY_ASYNCPIPE_ISSUES.md#accore-bin-i-b7k9</c>.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReserveCapacity(int bytes)
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private void GrowAndValidate(int additionalBytes)
|
||||
{
|
||||
EnsureCapacity(bytes);
|
||||
Output.Grow(ref _buffer, ref _position, ref _bufferEnd, additionalBytes);
|
||||
|
||||
var available = _bufferEnd - _position;
|
||||
if (available < additionalBytes) ThrowGrowFailedToSatisfy(additionalBytes, available);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
|
|
@ -386,13 +393,6 @@ public static partial class AcBinarySerializer
|
|||
_buffer[_position++] = value;
|
||||
}
|
||||
|
||||
/// <summary>Writes a single byte without capacity check. Caller must ensure buffer space.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteByteUnsafe(byte value)
|
||||
{
|
||||
_buffer[_position++] = value;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void WriteTwoBytes(byte b1, byte b2)
|
||||
{
|
||||
|
|
@ -1004,6 +1004,19 @@ public static partial class AcBinarySerializer
|
|||
$"This limit is dictated by the writer's worst-case 'charLength * 4' UTF-8 byte allocation; " +
|
||||
$"larger inputs would silently overflow int arithmetic.");
|
||||
|
||||
/// <summary>
|
||||
/// Throw helper for <see cref="GrowAndValidate"/>: invoked when <c>Output.Grow</c> returns
|
||||
/// without satisfying the requested capacity (chunk-limited output, single value larger than
|
||||
/// the per-chunk data capacity). Marked <c>NoInlining</c> so the throw-site stays out of the
|
||||
/// inlined caller body. See <c>BINARY_ASYNCPIPE_ISSUES.md#accore-bin-i-b7k9</c> for fix direction.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static void ThrowGrowFailedToSatisfy(int requested, int available) =>
|
||||
throw new InvalidOperationException(
|
||||
$"Output.Grow did not satisfy the requested capacity: {requested} bytes requested, only {available} bytes available after Grow. " +
|
||||
$"This typically indicates a chunk-limited output (e.g. AsyncPipeWriterOutput) with a single serialized value exceeding the per-chunk data capacity. " +
|
||||
$"See BINARY_ASYNCPIPE_ISSUES.md#accore-bin-i-b7k9.");
|
||||
|
||||
#endregion
|
||||
|
||||
#region Bulk Array Writes — inline
|
||||
|
|
|
|||
|
|
@ -284,7 +284,22 @@ public struct AsyncPipeWriterOutput : IBinaryOutputBase
|
|||
}
|
||||
|
||||
// Acquire new chunk with header reservation (common to both paths).
|
||||
AcquireChunk(Math.Max(needed, _chunkSize), out buffer, out position, out bufferEnd);
|
||||
//
|
||||
// Unit alignment: `needed` is the data-byte count requested by the caller (via EnsureCapacity);
|
||||
// `_chunkSize` is the chunk-on-wire total size (header + data) — same units AcquireChunk's
|
||||
// `requestSize` parameter expects. Convert `needed` to the chunk-on-wire scale (`+ headerOffset`)
|
||||
// before the Max() so apples-to-apples. AcquireChunk then subtracts `headerOffset` internally to
|
||||
// size the data region.
|
||||
//
|
||||
// Without this alignment, a caller request of exactly `_chunkSize - headerOffset` data bytes
|
||||
// would yield a chunk whose data region is `headerOffset` bytes SHORT of the request — every
|
||||
// EnsureCapacity for a value larger than the chunk-data capacity would silently under-provision,
|
||||
// surfacing later as `ArgumentOutOfRangeException` in `AsSpan`/`CopyTo` at corrupt offsets.
|
||||
// The sanity guard in `BinarySerializationContext.EnsureCapacity` (post-Grow revalidation +
|
||||
// `ThrowGrowFailedToSatisfy`) catches the remaining > MaxChunkSize case explicitly — see
|
||||
// `BINARY_ASYNCPIPE_ISSUES.md#accore-bin-i-b7k9`.
|
||||
var headerOffset = _multiMessage ? HeaderSize : 0;
|
||||
AcquireChunk(Math.Max(needed + headerOffset, _chunkSize), out buffer, out position, out bufferEnd);
|
||||
_currentChunkStart = position;
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
Advanced serialization features on top of the wire format. Wire format: `BINARY_FORMAT.md` | Options/presets: `BINARY_OPTIONS.md` | Internal architecture: `BINARY_IMPLEMENTATION.md` | Source generation: `BINARY_SGEN.md`.
|
||||
|
||||
> **Architectural framing — why use AcBinary at all?** See [`BINARY_WHYUSE.md`](BINARY_WHYUSE.md) for the category positioning vs wire-only serializers (Protobuf / MessagePack / MemoryPack), the three-pillar value proposition (graph integrity + bandwidth + streaming), real-world WASM SignalR reference numbers, fit/not-fit decision lists, and the framing for how to read AcBinary's benchmark numbers.
|
||||
|
||||
## Optimization Policy (LLM)
|
||||
|
||||
AcBinary is a **general-purpose serializer**, not a benchmark-only implementation.
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -4,6 +4,7 @@ AcBinary serialization system. Primary goal: **speed** (two-phase scan+serialize
|
|||
|
||||
## Files in this folder
|
||||
|
||||
- [`BINARY_WHYUSE.md`](BINARY_WHYUSE.md) — Architectural framing: why AcBinary exists (category vs wire-only serializers, three-pillar value proposition, fit/not-fit decision lists, benchmark reading guide)
|
||||
- [`BINARY_FEATURES.md`](BINARY_FEATURES.md) — High-level features and capabilities
|
||||
- [`BINARY_FORMAT.md`](BINARY_FORMAT.md) — Wire format specification
|
||||
- [`BINARY_OPTIONS.md`](BINARY_OPTIONS.md) — Configuration options (`AcBinaryOptions`)
|
||||
|
|
@ -17,7 +18,7 @@ AcBinary serialization system. Primary goal: **speed** (two-phase scan+serialize
|
|||
|
||||
## Start here
|
||||
|
||||
Start with [`BINARY_FEATURES.md`](BINARY_FEATURES.md) (overview), then [`BINARY_FORMAT.md`](BINARY_FORMAT.md) (wire-level). [`BINARY_SGEN.md`](BINARY_SGEN.md) covers build-time code-gen integration.
|
||||
Start with [`BINARY_WHYUSE.md`](BINARY_WHYUSE.md) (architectural framing — why this serializer over the alternatives), then [`BINARY_FEATURES.md`](BINARY_FEATURES.md) (feature overview), then [`BINARY_FORMAT.md`](BINARY_FORMAT.md) (wire-level). [`BINARY_SGEN.md`](BINARY_SGEN.md) covers build-time code-gen integration.
|
||||
|
||||
## Cross-references
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue