diff --git a/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj b/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj
index c97a2503d2a..ebf81309726 100644
--- a/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj
+++ b/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj
@@ -68,6 +68,40 @@
+
+
+ Text\Json\JsonConstants.cs
+
+
+
+ Text\Json\JsonHelpers.cs
+
+
+
+ Text\Json\MetaDbEventSource.cs
+
+
+
+ Text\Json\JsonConstants.cs
+
+
+
+ Text\Json\JsonConstants.cs
+
+
+
+ Text\Json\JsonConstants.cs
+
+
+
+ Text\Json\JsonConstants.cs
+
+
+
+ Text\Json\JsonConstants.cs
+
+
+
ObjectType.cs
@@ -152,4 +186,8 @@
+
+
+
+
diff --git a/src/HotChocolate/Core/src/Types/Text/Json/CompositeResultDocument.MetaDb.cs b/src/HotChocolate/Core/src/Types/Text/Json/CompositeResultDocument.MetaDb.cs
new file mode 100644
index 00000000000..5f6561ac0ec
--- /dev/null
+++ b/src/HotChocolate/Core/src/Types/Text/Json/CompositeResultDocument.MetaDb.cs
@@ -0,0 +1,383 @@
+using System.Buffers;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using static HotChocolate.Text.Json.MetaDbEventSource;
+
+namespace HotChocolate.Text.Json;
+
+public sealed partial class ResultDocument
+{
+ internal struct MetaDb : IDisposable
+ {
+ private const int TokenTypeOffset = 8;
+ private static readonly ArrayPool s_arrayPool = ArrayPool.Shared;
+
+ private byte[][] _chunks;
+ private Cursor _next;
+ private bool _disposed;
+
+ internal static MetaDb CreateForEstimatedRows(int estimatedRows)
+ {
+ var chunksNeeded = Math.Max(4, (estimatedRows / Cursor.RowsPerChunk) + 1);
+ var chunks = s_arrayPool.Rent(chunksNeeded);
+ var log = Log;
+
+ log.MetaDbCreated(2, estimatedRows, 1);
+
+ // Rent the first chunk now to avoid branching on first append
+ chunks[0] = MetaDbMemory.Rent();
+ log.ChunkAllocated(2, 0);
+
+ for (var i = 1; i < chunks.Length; i++)
+ {
+ chunks[i] = [];
+ }
+
+ return new MetaDb
+ {
+ _chunks = chunks,
+ _next = Cursor.Zero
+ };
+ }
+
+ public Cursor NextCursor => _next;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal Cursor Append(
+ ElementTokenType tokenType,
+ int location = 0,
+ int sizeOrLength = 0,
+ int sourceDocumentId = 0,
+ int parentRow = 0,
+ int operationReferenceId = 0,
+ OperationReferenceType operationReferenceType = OperationReferenceType.None,
+ int numberOfRows = 0,
+ ElementFlags flags = ElementFlags.None)
+ {
+ var log = Log;
+ var next = _next;
+ var chunkIndex = next.Chunk;
+ var byteOffset = next.ByteOffset;
+
+ var chunks = _chunks.AsSpan();
+ var chunksLength = chunks.Length;
+
+ if (byteOffset + DbRow.Size > Cursor.ChunkBytes)
+ {
+ chunkIndex++;
+ byteOffset = 0;
+ next = Cursor.FromByteOffset(chunkIndex, byteOffset);
+ }
+
+ // make sure we have enough space for the chunk referenced by the chunkIndex.
+ if (chunkIndex >= chunksLength)
+ {
+ // if we do not have enough space we will double the size we have for
+ // chunks of memory.
+ var nextChunksLength = chunksLength * 2;
+ var newChunks = s_arrayPool.Rent(nextChunksLength);
+ log.ChunksExpanded(2, chunksLength, nextChunksLength);
+
+ // copy chunks to new buffer
+ Array.Copy(_chunks, newChunks, chunksLength);
+
+ for (var i = chunksLength; i < nextChunksLength; i++)
+ {
+ newChunks[i] = [];
+ }
+
+ // clear and return old chunks buffer
+ chunks.Clear();
+ s_arrayPool.Return(_chunks);
+
+ // assign new chunks buffer
+ _chunks = newChunks;
+ chunks = newChunks.AsSpan();
+ }
+
+ var chunk = chunks[chunkIndex];
+
+ // if the chunk is empty we did not yet rent any memory for it
+ if (chunk.Length == 0)
+ {
+ chunk = chunks[chunkIndex] = MetaDbMemory.Rent();
+ log.ChunkAllocated(2, chunkIndex);
+ }
+
+ var row = new DbRow(
+ tokenType,
+ location,
+ sizeOrLength,
+ sourceDocumentId,
+ parentRow,
+ operationReferenceId,
+ operationReferenceType,
+ numberOfRows,
+ flags);
+
+ ref var dest = ref MemoryMarshal.GetArrayDataReference(chunk);
+ Unsafe.WriteUnaligned(ref Unsafe.Add(ref dest, byteOffset), row);
+
+ // Advance write head by one row
+ _next = next + 1;
+ return next;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal void Replace(
+ Cursor cursor,
+ ElementTokenType tokenType,
+ int location = 0,
+ int sizeOrLength = 0,
+ int sourceDocumentId = 0,
+ int parentRow = 0,
+ int operationReferenceId = 0,
+ OperationReferenceType operationReferenceType = OperationReferenceType.None,
+ int numberOfRows = 0,
+ ElementFlags flags = ElementFlags.None)
+ {
+ AssertValidCursor(cursor);
+
+ var row = new DbRow(
+ tokenType,
+ location,
+ sizeOrLength,
+ sourceDocumentId,
+ parentRow,
+ operationReferenceId,
+ operationReferenceType,
+ numberOfRows,
+ flags);
+
+ var span = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset);
+
+ MemoryMarshal.Write(span, in row);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal DbRow Get(Cursor cursor)
+ {
+ AssertValidCursor(cursor);
+
+ var span = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset);
+
+ return MemoryMarshal.Read(span);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal (Cursor, ElementTokenType) GetStartCursor(Cursor cursor)
+ {
+ AssertValidCursor(cursor);
+
+ var chunks = _chunks.AsSpan();
+ var span = chunks[cursor.Chunk].AsSpan(cursor.ByteOffset);
+ var union = MemoryMarshal.Read(span[TokenTypeOffset..]);
+ var tokenType = (ElementTokenType)(union >> 28);
+
+ if (tokenType is ElementTokenType.Reference)
+ {
+ var index = MemoryMarshal.Read(span) & 0x07FFFFFF;
+ cursor = Cursor.FromIndex(index);
+ span = chunks[cursor.Chunk].AsSpan(cursor.ByteOffset + TokenTypeOffset);
+ union = MemoryMarshal.Read(span);
+ tokenType = (ElementTokenType)(union >> 28);
+ }
+
+ return (cursor, tokenType);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal int GetLocation(Cursor cursor)
+ {
+ AssertValidCursor(cursor);
+
+ var span = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset);
+
+ var locationAndOpRefType = MemoryMarshal.Read(span);
+ return locationAndOpRefType & 0x07FFFFFF;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal Cursor GetLocationCursor(Cursor cursor)
+ {
+ AssertValidCursor(cursor);
+
+ var span = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset);
+
+ var locationAndOpRefType = MemoryMarshal.Read(span);
+ return Cursor.FromIndex(locationAndOpRefType & 0x07FFFFFF);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal int GetParent(Cursor cursor)
+ {
+ AssertValidCursor(cursor);
+
+ var span = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset);
+
+ var sourceAndParentHigh = MemoryMarshal.Read(span[12..]);
+ var selectionSetFlagsAndParentLow = MemoryMarshal.Read(span[16..]);
+
+ return (sourceAndParentHigh >>> 15 << 11)
+ | ((selectionSetFlagsAndParentLow >> 21) & 0x7FF);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal Cursor GetParentCursor(Cursor cursor)
+ {
+ AssertValidCursor(cursor);
+
+ var span = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset);
+
+ var sourceAndParentHigh = MemoryMarshal.Read(span[12..]);
+ var selectionSetFlagsAndParentLow = MemoryMarshal.Read(span[16..]);
+
+ var index = (sourceAndParentHigh >>> 15 << 11)
+ | ((selectionSetFlagsAndParentLow >> 21) & 0x7FF);
+
+ return Cursor.FromIndex(index);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal int GetNumberOfRows(Cursor cursor)
+ {
+ AssertValidCursor(cursor);
+
+ var span = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset + TokenTypeOffset);
+
+ var value = MemoryMarshal.Read(span);
+ return value & 0x0FFFFFFF;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal ElementFlags GetFlags(Cursor cursor)
+ {
+ AssertValidCursor(cursor);
+
+ var span = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset + 16);
+
+ var selectionSetFlagsAndParentLow = MemoryMarshal.Read(span);
+ return (ElementFlags)((selectionSetFlagsAndParentLow >> 15) & 0x3F);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal void SetFlags(Cursor cursor, ElementFlags flags)
+ {
+ AssertValidCursor(cursor);
+ Debug.Assert((byte)flags <= 63, "Flags value exceeds 6-bit limit");
+
+ var fieldSpan = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset + 16);
+ var currentValue = MemoryMarshal.Read(fieldSpan);
+
+ var clearedValue = currentValue & 0xFFE07FFF; // ~(0x3F << 15)
+ var newValue = (int)(clearedValue | (uint)((int)flags << 15));
+
+ MemoryMarshal.Write(fieldSpan, newValue);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal int GetSizeOrLength(Cursor cursor)
+ {
+ AssertValidCursor(cursor);
+
+ var span = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset + 4);
+ var value = MemoryMarshal.Read(span);
+
+ return value & int.MaxValue;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal void SetSizeOrLength(Cursor cursor, int sizeOrLength)
+ {
+ AssertValidCursor(cursor);
+ Debug.Assert(sizeOrLength >= 0 && sizeOrLength <= int.MaxValue, "SizeOrLength value exceeds 31-bit limit");
+
+ var fieldSpan = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset + 4);
+ var currentValue = MemoryMarshal.Read(fieldSpan);
+
+ // Keep only the sign bit (HasComplexChildren)
+ var clearedValue = currentValue & unchecked((int)0x80000000);
+ var newValue = clearedValue | (sizeOrLength & int.MaxValue);
+
+ MemoryMarshal.Write(fieldSpan, newValue);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal void SetNumberOfRows(Cursor cursor, int numberOfRows)
+ {
+ AssertValidCursor(cursor);
+ Debug.Assert(numberOfRows >= 0 && numberOfRows <= 0x0FFFFFFF, "NumberOfRows value exceeds 28-bit limit");
+
+ var fieldSpan = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset + TokenTypeOffset);
+ var currentValue = MemoryMarshal.Read(fieldSpan);
+
+ // Keep only the top 4 bits (token type)
+ var clearedValue = currentValue & unchecked((int)0xF0000000);
+ var newValue = clearedValue | (numberOfRows & 0x0FFFFFFF);
+
+ MemoryMarshal.Write(fieldSpan, newValue);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal ElementTokenType GetElementTokenType(Cursor cursor, bool resolveReferences = true)
+ {
+ AssertValidCursor(cursor);
+
+ var union = MemoryMarshal.Read(_chunks[cursor.Chunk].AsSpan(cursor.ByteOffset + TokenTypeOffset));
+ var tokenType = (ElementTokenType)(union >> 28);
+
+ if (resolveReferences && tokenType == ElementTokenType.Reference)
+ {
+ var idx = GetLocation(cursor);
+ var resolved = Cursor.FromIndex(idx);
+ union = MemoryMarshal.Read(_chunks[resolved.Chunk].AsSpan(resolved.ByteOffset + TokenTypeOffset));
+ tokenType = (ElementTokenType)(union >> 28);
+ }
+
+ return tokenType;
+ }
+
+ [Conditional("DEBUG")]
+ private void AssertValidCursor(Cursor cursor)
+ {
+ Debug.Assert(cursor.Chunk >= 0, "Negative chunk");
+ Debug.Assert(cursor.Chunk < _chunks.Length, "Chunk index out of bounds");
+ Debug.Assert(_chunks[cursor.Chunk].Length > 0, "Accessing unallocated chunk");
+
+ var maxExclusive = _next.Chunk * Cursor.RowsPerChunk + _next.Row;
+ var absoluteIndex = (cursor.Chunk * Cursor.RowsPerChunk) + cursor.Row;
+
+ Debug.Assert(absoluteIndex >= 0 && absoluteIndex < maxExclusive,
+ $"Cursor points to row {absoluteIndex}, but only {maxExclusive} rows are valid.");
+ Debug.Assert(cursor.ByteOffset + DbRow.Size <= MetaDbMemory.BufferSize, "Cursor byte offset out of bounds");
+ }
+
+ public void Dispose()
+ {
+ if (!_disposed)
+ {
+ var cursor = _next;
+ var chunksLength = cursor.Chunk + 1;
+ var chunks = _chunks.AsSpan(0, chunksLength);
+ Log.MetaDbDisposed(2, chunksLength, cursor.Row);
+
+ foreach (var chunk in chunks)
+ {
+ if (chunk.Length == 0)
+ {
+ break;
+ }
+
+ MetaDbMemory.Return(chunk);
+ }
+
+ chunks.Clear();
+ s_arrayPool.Return(_chunks);
+
+ _chunks = [];
+ _disposed = true;
+ }
+ }
+ }
+}
diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ElementTokenType.cs b/src/HotChocolate/Core/src/Types/Text/Json/ElementTokenType.cs
new file mode 100644
index 00000000000..095f8521b55
--- /dev/null
+++ b/src/HotChocolate/Core/src/Types/Text/Json/ElementTokenType.cs
@@ -0,0 +1,21 @@
+namespace HotChocolate.Text.Json;
+
+internal enum ElementTokenType : byte
+{
+ None = 0,
+ StartObject = 1,
+ EndObject = 2,
+ StartArray = 3,
+ EndArray = 4,
+ PropertyName = 5,
+ // Retained for compatibility, we do not actually need this.
+ Comment = 6,
+ String = 7,
+ Number = 8,
+ True = 9,
+ False = 10,
+ Null = 11,
+ // A reference in case a property or array element point
+ // to an array or an object
+ Reference = 12
+}
diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.Cursor.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.Cursor.cs
new file mode 100644
index 00000000000..4e8ee11677b
--- /dev/null
+++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.Cursor.cs
@@ -0,0 +1,171 @@
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace HotChocolate.Text.Json;
+
+public sealed partial class ResultDocument
+{
+ ///
+ /// Comparable MetaDb cursor (chunk, row)
+ ///
+ [StructLayout(LayoutKind.Sequential, Size = 4)]
+ internal readonly struct Cursor : IEquatable, IComparable
+ {
+ public const int ChunkBytes = 1 << 17;
+ public const int RowsPerChunk = ChunkBytes / DbRow.Size;
+ public const int MaxChunks = 4096;
+
+ private const int RowBits = 14;
+ private const int ChunkBits = 12;
+ private const int ChunkShift = RowBits;
+
+ private const uint RowMask = (1u << RowBits) - 1u;
+ private const uint ChunkMask = (1u << ChunkBits) - 1u;
+
+ private readonly uint _value;
+
+ public static readonly Cursor Zero = From(0, 0);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private Cursor(uint value) => _value = value;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Cursor From(int chunkIndex, int rowWithinChunk)
+ {
+ Debug.Assert((uint)chunkIndex < MaxChunks);
+ Debug.Assert((uint)rowWithinChunk < RowsPerChunk);
+ return new Cursor(((uint)chunkIndex << ChunkShift) | (uint)rowWithinChunk);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Cursor FromIndex(int rowIndex)
+ {
+ Debug.Assert(rowIndex >= 0);
+ var chunk = rowIndex / RowsPerChunk;
+ var row = rowIndex - (chunk * RowsPerChunk);
+ return From(chunk, row);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Cursor FromByteOffset(int chunkIndex, int byteOffset)
+ {
+ Debug.Assert(byteOffset % DbRow.Size == 0, "byteOffset must be row-aligned.");
+ return From(chunkIndex, byteOffset / DbRow.Size);
+ }
+
+ public uint Value => _value;
+
+ public int Chunk
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => (int)((_value >> ChunkShift) & ChunkMask);
+ }
+
+ public int Row
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => (int)(_value & RowMask);
+ }
+
+ public int ByteOffset
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => Row * DbRow.Size;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Cursor AddRows(int delta)
+ {
+ if (delta == 0)
+ {
+ return this;
+ }
+
+ var row = Row + delta;
+ var chunk = Chunk;
+
+ if (row >= RowsPerChunk)
+ {
+ var carry = row / RowsPerChunk;
+ row -= carry * RowsPerChunk;
+ chunk += carry;
+ }
+ else if (row < 0)
+ {
+ var borrow = (-row + RowsPerChunk - 1) / RowsPerChunk;
+ row += borrow * RowsPerChunk;
+ chunk -= borrow;
+ }
+
+ if (chunk < 0)
+ {
+ Debug.Fail("Cursor underflow");
+ chunk = 0;
+ row = 0;
+ }
+ else if (chunk >= MaxChunks)
+ {
+ Debug.Fail("Cursor overflow");
+ chunk = MaxChunks - 1;
+ row = RowsPerChunk - 1;
+ }
+
+ return From(chunk, row);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Cursor WithChunk(int chunk) => From(chunk, Row);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Cursor WithRow(int row) => From(Chunk, row);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public int ToIndex() => (Chunk * RowsPerChunk) + Row;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public int ToTotalBytes() => (Chunk * ChunkBytes) + ByteOffset;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool Equals(Cursor other) => _value == other._value;
+
+ public override bool Equals(object? obj) => obj is Cursor c && Equals(c);
+
+ public override int GetHashCode() => (int)_value;
+
+ public override string ToString() => $"chunk={Chunk}, row={Row} (0x{_value:X8})";
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public int CompareTo(Cursor other) => _value.CompareTo(other._value);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool operator ==(Cursor a, Cursor b) => a._value == b._value;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool operator !=(Cursor a, Cursor b) => a._value != b._value;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool operator <(Cursor a, Cursor b) => a._value < b._value;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool operator >(Cursor a, Cursor b) => a._value > b._value;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool operator <=(Cursor a, Cursor b) => a._value <= b._value;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool operator >=(Cursor a, Cursor b) => a._value >= b._value;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Cursor operator +(Cursor x, int delta) => x.AddRows(delta);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Cursor operator -(Cursor x, int delta) => x.AddRows(-delta);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Cursor operator ++(Cursor x) => x.AddRows(1);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Cursor operator --(Cursor x) => x.AddRows(-1);
+ }
+}
diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.DbRow.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.DbRow.cs
new file mode 100644
index 00000000000..701c7734e40
--- /dev/null
+++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.DbRow.cs
@@ -0,0 +1,157 @@
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace HotChocolate.Text.Json;
+
+public sealed partial class ResultDocument
+{
+ [StructLayout(LayoutKind.Sequential)]
+ internal readonly struct DbRow
+ {
+ public const int Size = 20;
+ public const int UnknownSize = -1;
+
+ // 27 bits for location + 2 bits OpRefType + 3 reserved bits
+ private readonly int _locationAndOpRefType;
+
+ // Sign bit for HasComplexChildren + 31 bits for size/length
+ private readonly int _sizeOrLengthUnion;
+
+ // 4 bits TokenType + 27 bits NumberOfRows + 1 reserved bit
+ private readonly int _numberOfRowsTypeAndReserved;
+
+ // 15 bits SourceDocumentId + 17 bits (high 17 bits of ParentRow)
+ private readonly int _sourceAndParentHigh;
+
+ // 15 bits OperationReferenceId + 6 bits Flags + 11 bits (low bits of ParentRow)
+ private readonly int _selectionSetFlagsAndParentLow;
+
+ public DbRow(
+ ElementTokenType tokenType,
+ int location,
+ int sizeOrLength = 0,
+ int sourceDocumentId = 0,
+ int parentRow = 0,
+ int operationReferenceId = 0,
+ OperationReferenceType operationReferenceType = OperationReferenceType.None,
+ int numberOfRows = 0,
+ ElementFlags flags = ElementFlags.None)
+ {
+ Debug.Assert((byte)tokenType < 16);
+ Debug.Assert(location is >= 0 and <= 0x07FFFFFF); // 27 bits
+ Debug.Assert(sizeOrLength >= UnknownSize);
+ Debug.Assert(sourceDocumentId is >= 0 and <= 0x7FFF); // 15 bits
+ Debug.Assert(parentRow is >= 0 and <= 0x0FFFFFFF); // 28 bits
+ Debug.Assert(operationReferenceId is >= 0 and <= 0x7FFF); // 15 bits
+ Debug.Assert(numberOfRows is >= 0 and <= 0x07FFFFFF); // 27 bits
+ Debug.Assert((byte)flags <= 63); // 6 bits (0x3F)
+ Debug.Assert((byte)operationReferenceType <= 3); // 2 bits
+ Debug.Assert(Unsafe.SizeOf() == Size);
+
+ _locationAndOpRefType = location | ((int)operationReferenceType << 27);
+ _sizeOrLengthUnion = sizeOrLength;
+ _numberOfRowsTypeAndReserved = ((int)tokenType << 28) | (numberOfRows & 0x07FFFFFF);
+ _sourceAndParentHigh = sourceDocumentId | ((parentRow >> 11) << 15);
+ _selectionSetFlagsAndParentLow = operationReferenceId | ((int)flags << 15) | ((parentRow & 0x7FF) << 21);
+ }
+
+ ///
+ /// Element token type (includes Reference for composition).
+ ///
+ ///
+ /// 4 bits = possible values
+ ///
+ public ElementTokenType TokenType => (ElementTokenType)(unchecked((uint)_numberOfRowsTypeAndReserved) >> 28);
+
+ ///
+ /// Operation reference type indicating the type of GraphQL operation element.
+ ///
+ ///
+ /// 2 bits = 4 possible values
+ ///
+ public OperationReferenceType OperationReferenceType
+ => (OperationReferenceType)((_locationAndOpRefType >> 27) & 0x03);
+
+ ///
+ /// Byte offset in source data OR metaDb row index for references.
+ ///
+ ///
+ /// 2 bits = 4 possible values
+ ///
+ public int Location => _locationAndOpRefType & 0x07FFFFFF;
+
+ ///
+ /// Length of data in JSON payload, number of elements if array or number of properties in an object.
+ ///
+ ///
+ /// 27 bits = 134M limit
+ ///
+ public int SizeOrLength => _sizeOrLengthUnion & int.MaxValue;
+
+ ///
+ /// String/PropertyName: Unescaping required.
+ ///
+ public bool HasComplexChildren => _sizeOrLengthUnion < 0;
+
+ ///
+ /// Specifies if a size for the item has ben set.
+ ///
+ public bool IsUnknownSize => _sizeOrLengthUnion == UnknownSize;
+
+ ///
+ /// Number of metadb rows this element spans.
+ ///
+ ///
+ /// 27 bits = 134M rows
+ ///
+ public int NumberOfRows => _numberOfRowsTypeAndReserved & 0x07FFFFFF;
+
+ ///
+ /// Index of parent element in metadb for navigation and null propagation.
+ ///
+ ///
+ /// 28 bits = 268M rows
+ ///
+ public int ParentRow
+ => ((int)((uint)_sourceAndParentHigh >> 15) << 11) | ((_selectionSetFlagsAndParentLow >> 21) & 0x7FF);
+
+ ///
+ /// Reference to GraphQL selection set or selection metadata.
+ /// 15 bits = 32K selections
+ ///
+ public int OperationReferenceId => _selectionSetFlagsAndParentLow & 0x7FFF;
+
+ ///
+ /// Element metadata flags.
+ ///
+ ///
+ /// 6 bits = 64 combinations
+ ///
+ public ElementFlags Flags => (ElementFlags)((_selectionSetFlagsAndParentLow >> 15) & 0x3F);
+
+ ///
+ /// True for primitive JSON values (strings, numbers, booleans, null).
+ ///
+ public bool IsSimpleValue => TokenType is >= ElementTokenType.PropertyName and <= ElementTokenType.Null;
+ }
+
+ internal enum OperationReferenceType : byte
+ {
+ None = 0,
+ SelectionSet = 1,
+ Selection = 2
+ }
+
+ [Flags]
+ internal enum ElementFlags : byte
+ {
+ None = 0,
+ Invalidated = 1,
+ SourceResult = 2,
+ IsNullable = 4,
+ IsRoot = 8,
+ IsInternal = 16,
+ IsExcluded = 32
+ }
+}
diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/CompositeResultDocument.DbRow.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/CompositeResultDocument.DbRow.cs
index 08fdf7eeff7..42785ef9614 100644
--- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/CompositeResultDocument.DbRow.cs
+++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/CompositeResultDocument.DbRow.cs
@@ -57,67 +57,87 @@ public DbRow(
}
///
- /// Byte offset in source data OR metaDb row index for references.
- /// 27 bits = 134M limit (increased from 26 bits / 67M limit)
+ /// Element token type (includes Reference for composition).
///
- public int Location => _locationAndOpRefType & 0x07FFFFFF;
+ ///
+ /// 4 bits = possible values
+ ///
+ public ElementTokenType TokenType => (ElementTokenType)(unchecked((uint)_numberOfRowsTypeAndReserved) >> 28);
///
/// Operation reference type indicating the type of GraphQL operation element.
- /// 2 bits = 4 possible values
///
+ ///
+ /// 2 bits = 4 possible values
+ ///
public OperationReferenceType OperationReferenceType
=> (OperationReferenceType)((_locationAndOpRefType >> 27) & 0x03);
+ ///
+ /// Byte offset in source data OR metaDb row index for references
+ ///
+ ///
+ /// 27 bits = 134M limit
+ ///
+ public int Location => _locationAndOpRefType & 0x07FFFFFF;
+
///
/// Length of data in JSON payload, number of elements if array or number of properties in an object.
- /// 31 bits = 2GB limit
///
+ ///
+ /// 31 bits = 2GB limit
+ ///
public int SizeOrLength => _sizeOrLengthUnion & int.MaxValue;
///
/// String/PropertyName: Unescaping required.
- /// Array: Contains complex children.
///
public bool HasComplexChildren => _sizeOrLengthUnion < 0;
- public bool IsUnknownSize => _sizeOrLengthUnion == UnknownSize;
-
///
- /// Element token type (includes Reference for composition).
- /// 4 bits = 16 types
+ /// Specifies if a size for the item has ben set.
///
- public ElementTokenType TokenType => (ElementTokenType)(unchecked((uint)_numberOfRowsTypeAndReserved) >> 28);
+ public bool IsUnknownSize => _sizeOrLengthUnion == UnknownSize;
///
/// Number of metadb rows this element spans.
- /// 27 bits = 134M rows
///
+ ///
+ /// 27 bits = 134M rows
+ ///
public int NumberOfRows => _numberOfRowsTypeAndReserved & 0x07FFFFFF;
///
/// Which source JSON document contains the data.
- /// 15 bits = 32K documents
///
+ ///
+ /// 15 bits = 32K documents
+ ///
public int SourceDocumentId => _sourceAndParentHigh & 0x7FFF;
///
/// Index of parent element in metadb for navigation and null propagation.
- /// 28 bits = 268M rows (reconstructed from high+low bits)
///
+ ///
+ /// 28 bits = 268M rows
+ ///
public int ParentRow
=> ((int)((uint)_sourceAndParentHigh >> 15) << 11) | ((_selectionSetFlagsAndParentLow >> 21) & 0x7FF);
///
/// Reference to GraphQL selection set or selection metadata.
- /// 15 bits = 32K selections
///
+ ///
+ /// 15 bits = 32K selections
+ ///
public int OperationReferenceId => _selectionSetFlagsAndParentLow & 0x7FFF;
///
/// Element metadata flags.
- /// 6 bits = 64 combinations
///
+ ///
+ /// 6 bits = 64 combinations
+ ///
public ElementFlags Flags => (ElementFlags)((_selectionSetFlagsAndParentLow >> 15) & 0x3F);
///
diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonConstants.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonConstants.cs
index 7fd0b4379d4..9201cc9fc5d 100644
--- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonConstants.cs
+++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonConstants.cs
@@ -1,4 +1,8 @@
+#if FUSION
namespace HotChocolate.Fusion.Text.Json;
+#else
+namespace HotChocolate.Text.Json;
+#endif
internal static class JsonConstants
{
@@ -13,48 +17,22 @@ internal static class JsonConstants
public const byte LineFeed = (byte)'\n';
public const byte Tab = (byte)'\t';
public const byte Comma = (byte)',';
- public const byte KeyValueSeparator = (byte)':';
public const byte Quote = (byte)'"';
public const byte BackSlash = (byte)'\\';
public const byte Slash = (byte)'/';
public const byte BackSpace = (byte)'\b';
public const byte FormFeed = (byte)'\f';
- public const byte Asterisk = (byte)'*';
public const byte Colon = (byte)':';
- public const byte Period = (byte)'.';
- public const byte Plus = (byte)'+';
- public const byte Hyphen = (byte)'-';
- public const byte UtcOffsetToken = (byte)'Z';
- public const byte TimePrefix = (byte)'T';
-
public const byte NewLineLineFeed = (byte)'\n';
- // \u2028 and \u2029 are considered respectively line and paragraph separators
- // UTF-8 representation for them is E2, 80, A8/A9
- public const byte StartingByteOfNonStandardSeparator = 0xE2;
-
public static ReadOnlySpan Data => "data"u8;
public static ReadOnlySpan Errors => "errors"u8;
public static ReadOnlySpan Extensions => "extensions"u8;
- public static ReadOnlySpan Utf8Bom => [0xEF, 0xBB, 0xBF];
public static ReadOnlySpan TrueValue => "true"u8;
public static ReadOnlySpan FalseValue => "false"u8;
public static ReadOnlySpan NullValue => "null"u8;
- public static ReadOnlySpan NaNValue => "NaN"u8;
- public static ReadOnlySpan PositiveInfinityValue => "Infinity"u8;
- public static ReadOnlySpan NegativeInfinityValue => "-Infinity"u8;
- public const int MaximumFloatingPointConstantLength = 9;
-
- // Used to search for the end of a number
- public static ReadOnlySpan Delimiters => ",}] \n\r\t/"u8;
-
- // Explicitly skipping ReverseSolidus since that is handled separately
- public static ReadOnlySpan EscapableChars => "\"nrt/ubf"u8;
-
- public const int RemoveFlagsBitMask = 0x7FFFFFFF;
-
// In the worst case, an ASCII character represented as a single utf-8 byte could expand 6x when escaped.
// For example: '+' becomes '\u0043'
// Escaping surrogate pairs (represented by 3 or 4 utf-8 bytes) would expand to 12 bytes (which is still <= 6x).
@@ -66,65 +44,10 @@ internal static class JsonConstants
// All other UTF-16 characters can be represented by either 1 or 2 UTF-8 bytes.
public const int MaxExpansionFactorWhileTranscoding = 3;
- // When transcoding from UTF8 -> UTF16, the byte count threshold where we rent from the array pool before performing a normal alloc.
- public const long ArrayPoolMaxSizeBeforeUsingNormalAlloc =
-#if NET
- 1024 * 1024 * 1024; // ArrayPool limit increased in .NET 6
-#else
- 1024 * 1024;
-#endif
-
- // The maximum number of characters allowed when writing raw UTF-16 JSON. This is the maximum length that we can guarantee can
- // be safely transcoded to UTF-8 and fit within an integer-length span, given the max expansion factor of a single character (3).
- public const int MaxUtf16RawValueLength = int.MaxValue / MaxExpansionFactorWhileTranscoding;
-
- public const int MaxEscapedTokenSize = 1_000_000_000; // Max size for already escaped value.
- public const int MaxUnescapedTokenSize = MaxEscapedTokenSize / MaxExpansionFactorWhileEscaping; // 166_666_666 bytes
- public const int MaxCharacterTokenSize = MaxEscapedTokenSize / MaxExpansionFactorWhileEscaping; // 166_666_666 characters
-
- public const int MaximumFormatBooleanLength = 5;
- public const int MaximumFormatInt64Length = 20; // 19 + sign (i.e. -9223372036854775808)
- public const int MaximumFormatUInt32Length = 10; // i.e. 4294967295
- public const int MaximumFormatUInt64Length = 20; // i.e. 18446744073709551615
- public const int MaximumFormatDoubleLength = 128; // default (i.e. 'G'), using 128 (rather than say 32) to be future-proof.
- public const int MaximumFormatSingleLength = 128; // default (i.e. 'G'), using 128 (rather than say 32) to be future-proof.
- public const int MaximumFormatDecimalLength = 31; // default (i.e. 'G')
- public const int MaximumFormatGuidLength = 36; // default (i.e. 'D'), 8 + 4 + 4 + 4 + 12 + 4 for the hyphens (e.g. 094ffa0a-0442-494d-b452-04003fa755cc)
- public const int MaximumEscapedGuidLength = MaxExpansionFactorWhileEscaping * MaximumFormatGuidLength;
- public const int MaximumFormatDateTimeLength = 27; // StandardFormat 'O', e.g. 2017-06-12T05:30:45.7680000
- public const int MaximumFormatDateTimeOffsetLength = 33; // StandardFormat 'O', e.g. 2017-06-12T05:30:45.7680000-07:00
- public const int MaxDateTimeUtcOffsetHours = 14; // The UTC offset portion of a TimeSpan or DateTime can be no more than 14 hours and no less than -14 hours.
- public const int DateTimeNumFractionDigits = 7; // TimeSpan and DateTime formats allow exactly up to many digits for specifying the fraction after the seconds.
- public const int MaxDateTimeFraction = 9_999_999; // The largest fraction expressible by TimeSpan and DateTime formats
- public const int DateTimeParseNumFractionDigits = 16; // The maximum number of fraction digits the Json DateTime parser allows
- public const int MaximumDateTimeOffsetParseLength = MaximumFormatDateTimeOffsetLength
- + (DateTimeParseNumFractionDigits - DateTimeNumFractionDigits); // Like StandardFormat 'O' for DateTimeOffset, but allowing 9 additional (up to 16) fraction digits.
- public const int MinimumDateTimeParseLength = 10; // YYYY-MM-DD
- public const int MaximumEscapedDateTimeOffsetParseLength = MaxExpansionFactorWhileEscaping * MaximumDateTimeOffsetParseLength;
-
- public const int MaximumLiteralLength = 5; // Must be able to fit null, true, & false.
-
- // Encoding Helpers
- public const char HighSurrogateStart = '\ud800';
- public const char HighSurrogateEnd = '\udbff';
- public const char LowSurrogateStart = '\udc00';
- public const char LowSurrogateEnd = '\udfff';
-
public const int UnicodePlane01StartValue = 0x10000;
public const int HighSurrogateStartValue = 0xD800;
public const int HighSurrogateEndValue = 0xDBFF;
public const int LowSurrogateStartValue = 0xDC00;
public const int LowSurrogateEndValue = 0xDFFF;
public const int BitShiftBy10 = 0x400;
-
- // The maximum number of parameters a constructor can have where it can be considered
- // for a path on deserialization where we don't box the constructor arguments.
- public const int UnboxedParameterCountThreshold = 4;
-
- // Two space characters is the default indentation.
- public const char DefaultIndentCharacter = ' ';
- public const char TabIndentCharacter = '\t';
- public const int DefaultIndentSize = 2;
- public const int MinimumIndentSize = 0;
- public const int MaximumIndentSize = 127; // If this value is changed, the impact on the options masking used in the JsonWriterOptions struct must be checked carefully.
}
diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonHelpers.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonHelpers.cs
index 6de1d7c5347..4a281fc2966 100644
--- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonHelpers.cs
+++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonHelpers.cs
@@ -1,16 +1,13 @@
using System.Runtime.CompilerServices;
+#if FUSION
namespace HotChocolate.Fusion.Text.Json;
+#else
+namespace HotChocolate.Text.Json;
+#endif
internal static class JsonHelpers
{
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool IsValidDateTimeOffsetParseLength(int length)
- => IsInRangeInclusive(
- length,
- JsonConstants.MinimumDateTimeParseLength,
- JsonConstants.MaximumEscapedDateTimeOffsetParseLength);
-
///
/// Returns if is between
/// and , inclusive.
@@ -18,19 +15,4 @@ public static bool IsValidDateTimeOffsetParseLength(int length)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsInRangeInclusive(uint value, uint lowerBound, uint upperBound)
=> (value - lowerBound) <= (upperBound - lowerBound);
-
- ///
- /// Returns if is between
- /// and , inclusive.
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool IsInRangeInclusive(int value, int lowerBound, int upperBound)
- => (uint)(value - lowerBound) <= (uint)(upperBound - lowerBound);
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool IsValidUnescapedDateTimeOffsetParseLength(int length)
- => IsInRangeInclusive(
- length,
- JsonConstants.MinimumDateTimeParseLength,
- JsonConstants.MaximumDateTimeOffsetParseLength);
}
diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/MetaDbEventSource.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/MetaDbEventSource.cs
index 8135fd7c4f7..9394f2d213d 100644
--- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/MetaDbEventSource.cs
+++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/MetaDbEventSource.cs
@@ -1,8 +1,14 @@
using System.Diagnostics.Tracing;
+#if FUSION
namespace HotChocolate.Fusion.Text.Json;
[EventSource(Name = "HotChocolate-Fusion-MetaDb")]
+#else
+namespace HotChocolate.Text.Json;
+
+[EventSource(Name = "HotChocolate-MetaDb")]
+#endif
internal sealed class MetaDbEventSource : EventSource
{
public static readonly MetaDbEventSource Log = new();
diff --git a/src/HotChocolate/Json/src/Json/HotChocolate.Text.Json.csproj b/src/HotChocolate/Json/src/Json/HotChocolate.Text.Json.csproj
index c66c31fec8c..3366abcbd96 100644
--- a/src/HotChocolate/Json/src/Json/HotChocolate.Text.Json.csproj
+++ b/src/HotChocolate/Json/src/Json/HotChocolate.Text.Json.csproj
@@ -6,8 +6,4 @@
preview
-
-
-
-