# Buffer-in-Context terv: `_buffer`/`_position` visszahelyezése a context-be

## Probléma
A TOutput generic refaktorálás ~30-40%-os serialization regressziót okozott.
Ok: .NET JIT reference type generikusoknál SHARED kódot generál → minden `output.WriteByte()` virtuális dispatch, még sealed osztályoknál is.

## Megoldás
`_buffer` + `_position` visszakerül a `BinarySerializationContext<TOutput>`-ba.
Minden hot path write metódus inline context metódus lesz: `_buffer[_position++] = value`.
A `TOutput Output` kizárólag cold path Grow/Flush-t kezel.

---

## 1. Új BinaryOutputBase (3 absztrakt metódus)

A jelenlegi 19 abstract + 9 virtual metódus helyett:

```csharp
public abstract class BinaryOutputBase
{
    public abstract void Initialize(out byte[] buffer, out int position, out int bufferEnd);
    public abstract void Grow(ref byte[] buffer, ref int position, ref int bufferEnd, int needed);
    public abstract int GetTotalPosition(int currentPosition);
}
```

- `Initialize`: kezdeti buffer kiadása
- `Grow`: cold path — buffer betelik → ArrayPool.Rent/copy VAGY Advance+GetMemory
- `GetTotalPosition`: Position property-hez (cold path, 1x hívás per serialize)
- `IBinaryOutput.cs` törlése (nem implementálja többé senki)

## 2. BinarySerializationContext<TOutput> — új mezők + write metódusok

### Új mezők:
```csharp
internal byte[] _buffer = null!;
internal int _position;
internal int _bufferEnd;  // writeable terület vége (_position < _bufferEnd)
```

### Position property:
```csharp
public int Position => Output.GetTotalPosition(_position);
// ArrayBinaryOutput: return currentPosition (CommittedBytes=0, bufferStart=0)
// BufferWriterBinaryOutput: return _committedBytes + (currentPosition - _chunkStart)
```

### EnsureCapacity (privát):
```csharp
[AggressiveInlining]
private void EnsureCapacity(int additionalBytes)
{
    if (_position + additionalBytes > _bufferEnd)
        Output.Grow(ref _buffer, ref _position, ref _bufferEnd, additionalBytes);
}
```

### Write metódusok (mind [AggressiveInlining], az ArrayBinaryOutput implementációiból portolva):
1. WriteByte(byte)
2. WriteTwoBytes(byte, byte)
3. WriteBytes(ReadOnlySpan<byte>)
4. WriteRaw<T>(T) where T : unmanaged
5. WriteTypeCodeAndRaw<T>(byte, T)
6. WriteVarUInt(uint) — fast path < 0x80
7. WriteVarInt(int) — ZigZag + WriteVarUInt
8. WriteVarULong(ulong)
9. WriteVarLong(long)
10. WriteDecimalBits(decimal)
11. WriteDateTimeBits(DateTime)
12. WriteGuidBits(Guid)
13. WriteDateTimeOffsetBits(DateTimeOffset)
14. WriteStringUtf8(string)
15. WriteFixStr(string)
16. WriteFixStrDirect(string)
17. WriteFixStrBytes(ReadOnlySpan<byte>)
18. WritePreencodedPropertyName(ReadOnlySpan<byte>)
19. WriteDoubleArrayBulk(double[])
20. WriteFloatArrayBulk(float[])
21. WriteGuidArrayBulk(Guid[])
22. WriteInt32ArrayOptimized(int[])
23. WriteLongArrayOptimized(long[])
24. WriteBytesSimd(ReadOnlySpan<byte>)

Mind `_buffer[_position++]` pattern-nel — NULLA virtual dispatch a hot path-on.

### WriteHeader / WriteInlineMetadata:
- `Output.WriteByte()` → `WriteByte()` (self)
- `WriteInlineMetadata` signature: `output` param eltávolítása

## 3. ArrayBinaryOutput (~430 → ~120 sor)

```csharp
public sealed class ArrayBinaryOutput : BinaryOutputBase, IDisposable
{
    private byte[] _rentedBuffer;

    public override void Initialize(out byte[] buffer, out int position, out int bufferEnd)
    {
        buffer = _rentedBuffer; position = 0; bufferEnd = _rentedBuffer.Length;
    }

    [NoInlining]
    public override void Grow(ref byte[] buffer, ref int position, ref int bufferEnd, int needed)
    {
        // ArrayPool.Rent bigger + copy + return old
        // position marad, bufferEnd = newBuffer.Length
        _rentedBuffer = newBuffer;
    }

    public override int GetTotalPosition(int currentPosition) => currentPosition;

    // Eredmény metódusok — buffer/position paramétert kapnak a context-ből:
    public ReadOnlySpan<byte> AsSpan(byte[] buffer, int position);
    public byte[] ToArray(byte[] buffer, int position);
    public BinarySerializationResult DetachResult(byte[] buffer, int position);
    public void WriteTo(IBufferWriter<byte> writer, byte[] buffer, int position);
}
```

## 4. BufferWriterBinaryOutput (~350 → ~100 sor)

```csharp
public sealed class BufferWriterBinaryOutput : BinaryOutputBase
{
    private readonly IBufferWriter<byte> _writer;
    private int _committedBytes;
    private int _currentChunkStart;
    private bool _ownedBuffer;

    public override void Initialize(out byte[] buffer, out int position, out int bufferEnd)
    {
        _committedBytes = 0;
        AcquireChunk(MinChunkRequest, out buffer, out position, out bufferEnd);
        _currentChunkStart = position;
    }

    [NoInlining]
    public override void Grow(ref byte[] buffer, ref int position, ref int bufferEnd, int needed)
    {
        // 1. Advance current chunk: _writer.Advance(position - _currentChunkStart)
        // 2. _committedBytes += bytesInChunk
        // 3. AcquireChunk(needed, out buffer, out position, out bufferEnd)
        // 4. _currentChunkStart = position
    }

    public override int GetTotalPosition(int currentPosition)
        => _committedBytes + (currentPosition - _currentChunkStart);

    public void Flush(byte[] buffer, int position)
    {
        // Utolsó chunk commit-ja
    }

    private void AcquireChunk(int requestSize, out byte[] buffer, out int position, out int bufferEnd)
    {
        // GetMemory() + TryGetArray() → buffer=segment.Array, position=segment.Offset
        // Fallback: ArrayPool.Rent owned buffer
    }
}
```

## 5. AcBinarySerializer.cs (~130 call site változás)

### Signature változások:
- `WriteInt32<TOutput>(int, TOutput output)` → `WriteInt32<TOutput>(int, BinarySerializationContext<TOutput> context)`
- `WriteString<TOutput>(string, TOutput output, context)` → `WriteString<TOutput>(string, BinarySerializationContext<TOutput> context)`
- Minden helper: `output` param eltávolítása, `context` marad
- `var output = context.Output;` sorok törlése
- `output.WriteByte(...)` → `context.WriteByte(...)`

### TryWritePrimitiveArrayCore:
- Jelenleg non-generic `BinaryOutputBase output` param
- Új: generic `BinarySerializationContext<TOutput> context` param (2 JIT copy elfogadható, per-array hívás)

### Public API metódusok:
```csharp
// Serialize<T> (byte[]):
context.Output.Initialize(out context._buffer, out context._position, out context._bufferEnd);
// ... serialize ...
return context.Output.ToArray(context._buffer, context._position);

// Serialize<T> (IBufferWriter):
output.Initialize(out context._buffer, out context._position, out context._bufferEnd);
// ... serialize ...
output.Flush(context._buffer, context._position);
```

## 6. ScanPass.cs — NINCS VÁLTOZÁS
Már most is csak context-et kap, nem ír semmit.

## 7. IBinaryOutput.cs — TÖRLÉS
Senki nem implementálja többé.

## 8. Implementációs sorrend

1. Context: `_buffer`/`_position`/`_bufferEnd` mezők + 24 write metódus hozzáadása
2. BinaryOutputBase: 28 metódus → 3 (Initialize, Grow, GetTotalPosition)
3. ArrayBinaryOutput: egyszerűsítés (Grow + result metódusok)
4. BufferWriterBinaryOutput: egyszerűsítés (Grow + Flush + AcquireChunk)
5. AcBinarySerializer.cs: ~130 hívás átírása output→context
6. Public API: Initialize/Flush/ToArray hívások buffer/position paraméterekkel
7. Context WriteHeader/WriteInlineMetadata: output param eltávolítása
8. IBinaryOutput.cs törlése
9. Build + teszt

## Várható eredmény
- Hot path: `_buffer[_position++]` — nulla virtual dispatch (baseline szintű teljesítmény)
- Cold path (Grow): 1 virtual call buffer beteltkor → elhanyagolható
- Position: 1 virtual call, de csak 1x hívódik per serialize → elhanyagolható
- IBufferWriter streaming: 100% megmarad (Grow = Advance + GetMemory)
