Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -515,7 +515,7 @@ public int AppendEmptyProperty(int parentRow, int selectionId, ElementFlags flag
Unsafe.WriteUnaligned(
ref Unsafe.Add(ref row, 4),
selectionId
| ((int)OperationReferenceType.Selection << 15)
| ((int)OperationReferenceType.Selection << DbRow.OperationReferenceTypeShift)
| (((int)flags & DbRow.FlagsMask) << DbRow.FlagsShift));

// ints 2..3 must be zero (int 4 is written directly below)
Expand Down Expand Up @@ -555,7 +555,7 @@ public int AppendEmptyPropertyWithNullValue(int parentRow, int selectionId, Elem
Unsafe.WriteUnaligned(
ref Unsafe.Add(ref row0, 4),
selectionId
| ((int)OperationReferenceType.Selection << 15)
| ((int)OperationReferenceType.Selection << DbRow.OperationReferenceTypeShift)
| (((int)flags & DbRow.FlagsMask) << DbRow.FlagsShift));
Unsafe.InitBlockUnaligned(ref Unsafe.Add(ref row0, 8), 0, 8);
Unsafe.WriteUnaligned(
Expand Down Expand Up @@ -597,7 +597,7 @@ public int AppendStartObject(int parentRow, int selectionSetId, int propertyCoun
Unsafe.WriteUnaligned(
ref Unsafe.Add(ref row, 4),
selectionSetId
| ((int)OperationReferenceType.SelectionSet << 15)
| ((int)OperationReferenceType.SelectionSet << DbRow.OperationReferenceTypeShift)
| (((int)flags & DbRow.FlagsMask) << DbRow.FlagsShift));

// int 2: sizeOrLength = property count
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using BenchmarkDotNet.Attributes;
using DbRow = HotChocolate.Fusion.Text.Json.CompositeResultDocument.DbRow;

namespace HotChocolate.Fusion.Execution.Benchmarks;

Expand Down Expand Up @@ -83,7 +84,7 @@ public static void WriteEmptyProp_FiveScalar(ref byte row, int parent, int selec
Unsafe.WriteUnaligned(ref row, 3 /*PropertyName*/ | (parent << 4));
Unsafe.WriteUnaligned(
ref Unsafe.Add(ref row, 4),
selectionId | (2 << 15) | (flags << 17));
selectionId | (2 << DbRow.OperationReferenceTypeShift) | (flags << DbRow.FlagsShift));
Unsafe.WriteUnaligned(ref Unsafe.Add(ref row, 8), 0);
Unsafe.WriteUnaligned(ref Unsafe.Add(ref row, 12), 0);
Unsafe.WriteUnaligned(ref Unsafe.Add(ref row, 16), 0);
Expand All @@ -95,15 +96,15 @@ public static void WriteEmptyProp_TwoScalarPlusInitBlock(ref byte row, int paren
Unsafe.WriteUnaligned(ref row, 3 | (parent << 4));
Unsafe.WriteUnaligned(
ref Unsafe.Add(ref row, 4),
selectionId | (2 << 15) | (flags << 17));
selectionId | (2 << DbRow.OperationReferenceTypeShift) | (flags << DbRow.FlagsShift));
Unsafe.InitBlockUnaligned(ref Unsafe.Add(ref row, 8), 0, 12);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteEmptyProp_Vec128PlusScalar(ref byte row, int parent, int selectionId, int flags)
{
var int0 = 3 | (parent << 4);
var int1 = selectionId | (2 << 15) | (flags << 17);
var int1 = selectionId | (2 << DbRow.OperationReferenceTypeShift) | (flags << DbRow.FlagsShift);

var v = Vector128.Create(int0, int1, 0, 0).AsByte();
v.StoreUnsafe(ref row);
Expand All @@ -116,7 +117,7 @@ public static void WriteEmptyProp_TwoScalarPlusVec128Zero(ref byte row, int pare
Unsafe.WriteUnaligned(ref row, 3 | (parent << 4));
Unsafe.WriteUnaligned(
ref Unsafe.Add(ref row, 4),
selectionId | (2 << 15) | (flags << 17));
selectionId | (2 << DbRow.OperationReferenceTypeShift) | (flags << DbRow.FlagsShift));
// Covers ints 2..4 using a 16-byte zero store (overwrites 4 bytes past end, but row is 20B
// and buffer is row-aligned multiples of 20B — works only when room exists after).
// To be safe here we do two stores: Vec128.Zero at offset 8 would write 16 bytes,
Expand All @@ -134,7 +135,7 @@ public static void WriteStartObj_FiveScalar(ref byte row, int parent, int select
Unsafe.WriteUnaligned(ref row, 1 | (parent << 4));
Unsafe.WriteUnaligned(
ref Unsafe.Add(ref row, 4),
selectionId | (1 << 15) | (flags << 17));
selectionId | (1 << DbRow.OperationReferenceTypeShift) | (flags << DbRow.FlagsShift));
Unsafe.WriteUnaligned(ref Unsafe.Add(ref row, 8), propertyCount);
Unsafe.WriteUnaligned(
ref Unsafe.Add(ref row, 12),
Expand All @@ -160,7 +161,7 @@ public static void WriteStartObj_Struct(ref byte row, int parent, int selectionI
public static void WriteStartObj_Vec128PlusScalar(ref byte row, int parent, int selectionId, int propertyCount, int flags)
{
var int0 = 1 | (parent << 4);
var int1 = selectionId | (1 << 15) | (flags << 17);
var int1 = selectionId | (1 << DbRow.OperationReferenceTypeShift) | (flags << DbRow.FlagsShift);
var int2 = propertyCount;
var int3 = ((propertyCount * 2) + 1) & 0x07FFFFFF;

Expand Down Expand Up @@ -192,8 +193,8 @@ public DbRowLocal(
var locationOrRows = location != 0 ? location : numberOfRows;
_typeAndParent = (tokenType & 0x0F) | (parentRow << 4);
_selectionAndFlags = operationReferenceId
| (operationReferenceType << 15)
| (flags << 17);
| (operationReferenceType << DbRow.OperationReferenceTypeShift)
| (flags << DbRow.FlagsShift);
_sizeOrLengthUnion = sizeOrLength;
_locationOrRows = locationOrRows & 0x07FFFFFF;
_source = sourceDocumentId & 0x7FFF;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,21 @@ internal readonly struct DbRow
internal const int SizeOffset = 8;
internal const int LocationOrRowsOffset = 12;
internal const int SourceAndTypeOffset = 16;
internal const int FlagsShift = 17;

// Bit layout of _selectionAndFlags, low to high: OperationReferenceId,
// OperationReferenceType, Flags. The layout fills the int exactly.
internal const int OperationReferenceIdBitCount = 22;
internal const int OperationReferenceIdMask = (1 << OperationReferenceIdBitCount) - 1;
internal const int OperationReferenceTypeShift = OperationReferenceIdBitCount;
internal const int OperationReferenceTypeMask = 0x03;
internal const int FlagsShift = OperationReferenceTypeShift + 2;
internal const int FlagsBitCount = 8;
internal const int FlagsMask = 0xFF;

// 29 bits parent cursor value + 3 reserved
private readonly int _parent;

// 15 bits OperationReferenceId + 2 bits OperationReferenceType + 8 bits Flags + 7 reserved
// 22 bits OperationReferenceId + 2 bits OperationReferenceType + 8 bits Flags
private readonly int _selectionAndFlags;

// 1 bit HasComplexChildren (sign) + 31 bits SizeOrLength
Expand Down Expand Up @@ -53,17 +60,17 @@ public DbRow(
Debug.Assert(sizeOrLength >= UnknownSize);
Debug.Assert(sourceDocumentId is >= 0 and <= 0x7FFF); // 15 bits
Debug.Assert(parentRow is >= 0 and <= 0x1FFFFFFF); // 29 bits (cursor value)
Debug.Assert(operationReferenceId is >= 0 and <= 0x7FFF); // 15 bits
Debug.Assert(operationReferenceId is >= 0 and <= OperationReferenceIdMask); // 22 bits
Debug.Assert(numberOfRows is >= 0 and <= 0x1FFFFFFF); // 29 bits
Debug.Assert((byte)operationReferenceType <= 3); // 2 bits
Debug.Assert((byte)operationReferenceType <= OperationReferenceTypeMask); // 2 bits
Debug.Assert((int)flags is >= 0 and <= FlagsMask);
Debug.Assert(Unsafe.SizeOf<DbRow>() == Size);

var locationOrRows = location != 0 ? location : numberOfRows;

_parent = parentRow & 0x1FFFFFFF;
_selectionAndFlags = operationReferenceId
| ((int)operationReferenceType << 15)
| ((int)operationReferenceType << OperationReferenceTypeShift)
| (((int)flags & FlagsMask) << FlagsShift);
_sizeOrLengthUnion = sizeOrLength;
_locationOrRows = locationOrRows & 0x1FFFFFFF;
Expand All @@ -85,7 +92,8 @@ public DbRow(
/// 2 bits = 4 possible values
/// </remarks>
public OperationReferenceType OperationReferenceType
=> (OperationReferenceType)((_selectionAndFlags >> 15) & 0x03);
=> (OperationReferenceType)(
(_selectionAndFlags >>> OperationReferenceTypeShift) & OperationReferenceTypeMask);

/// <summary>
/// Byte offset in source data, or the packed cursor value of the target row for references.
Expand Down Expand Up @@ -141,9 +149,9 @@ public OperationReferenceType OperationReferenceType
/// Reference to GraphQL selection set or selection metadata.
/// </summary>
/// <remarks>
/// 15 bits = 32K selections
/// 22 bits = 4M selections
/// </remarks>
public int OperationReferenceId => _selectionAndFlags & 0x7FFF;
public int OperationReferenceId => _selectionAndFlags & OperationReferenceIdMask;

/// <summary>
/// Element metadata flags.
Expand All @@ -159,11 +167,11 @@ public bool IsNullMarker

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static int ReadOperationReferenceId(int selectionAndFlags)
=> selectionAndFlags & 0x7FFF;
=> selectionAndFlags & OperationReferenceIdMask;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static ElementFlags ReadFlags(int selectionAndFlags)
=> (ElementFlags)((selectionAndFlags >> 17) & 0x7F);
=> (ElementFlags)((selectionAndFlags >>> FlagsShift) & FlagsMask);

/// <summary>
/// True for primitive JSON values (strings, numbers, booleans, null).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ internal Cursor AppendNull(int parentRow)
internal Cursor AppendEmptyProperty(int parentRow, int selectionId, ElementFlags flags)
{
Debug.Assert(parentRow is >= 0 and <= 0x1FFFFFFF);
Debug.Assert(selectionId is >= 0 and <= 0x7FFF);
Debug.Assert(selectionId is >= 0 and <= DbRow.OperationReferenceIdMask);
Debug.Assert((int)flags is >= 0 and <= DbRow.FlagsMask);

var (chunk, byteOffset, cursor) = ReserveRow();
Expand All @@ -116,7 +116,7 @@ internal Cursor AppendEmptyProperty(int parentRow, int selectionId, ElementFlags
Unsafe.WriteUnaligned(
ref Unsafe.Add(ref row, 4),
selectionId
| ((int)OperationReferenceType.Selection << 15)
| ((int)OperationReferenceType.Selection << DbRow.OperationReferenceTypeShift)
| (((int)flags & DbRow.FlagsMask) << DbRow.FlagsShift));

// ints 2..3 must be zero (int 4 is written directly below)
Expand All @@ -135,7 +135,7 @@ ref Unsafe.Add(ref row, 16),
internal Cursor AppendEmptyPropertyWithNullValue(int parentRow, int selectionId, ElementFlags flags)
{
Debug.Assert(parentRow is >= 0 and <= 0x1FFFFFFF);
Debug.Assert(selectionId is >= 0 and <= 0x7FFF);
Debug.Assert(selectionId is >= 0 and <= DbRow.OperationReferenceIdMask);
Debug.Assert((int)flags is >= 0 and <= DbRow.FlagsMask);

var next = _next;
Expand All @@ -155,7 +155,7 @@ internal Cursor AppendEmptyPropertyWithNullValue(int parentRow, int selectionId,
Unsafe.WriteUnaligned(
ref Unsafe.Add(ref row0, 4),
selectionId
| ((int)OperationReferenceType.Selection << 15)
| ((int)OperationReferenceType.Selection << DbRow.OperationReferenceTypeShift)
| (((int)flags & DbRow.FlagsMask) << DbRow.FlagsShift));
Unsafe.InitBlockUnaligned(ref Unsafe.Add(ref row0, 8), 0, 8);
// int 4: PropertyName token
Expand All @@ -182,7 +182,7 @@ ref Unsafe.Add(ref row0, 16),
internal Cursor AppendStartObject(int parentRow, int selectionSetId, int propertyCount, ElementFlags flags)
{
Debug.Assert(parentRow is >= 0 and <= 0x1FFFFFFF);
Debug.Assert(selectionSetId is >= 0 and <= 0x7FFF);
Debug.Assert(selectionSetId is >= 0 and <= DbRow.OperationReferenceIdMask);
Debug.Assert(propertyCount is >= 0 and <= 0x0FFFFFFF); // room for (count*2)+1 in 29 bits
Debug.Assert((int)flags is >= 0 and <= DbRow.FlagsMask);

Expand All @@ -198,7 +198,7 @@ internal Cursor AppendStartObject(int parentRow, int selectionSetId, int propert
Unsafe.WriteUnaligned(
ref Unsafe.Add(ref row, 4),
selectionSetId
| ((int)OperationReferenceType.SelectionSet << 15)
| ((int)OperationReferenceType.SelectionSet << DbRow.OperationReferenceTypeShift)
| (((int)flags & DbRow.FlagsMask) << DbRow.FlagsShift));

// int 2: sizeOrLength = property count
Expand Down Expand Up @@ -623,7 +623,7 @@ internal readonly void ReplacePreserveParent(
Unsafe.WriteUnaligned(
ref Unsafe.Add(ref row, DbRow.SelectionAndFlagsOffset),
operationReferenceId
| ((int)operationReferenceType << 15)
| ((int)operationReferenceType << DbRow.OperationReferenceTypeShift)
| (((int)flags & DbRow.FlagsMask) << DbRow.FlagsShift));

// int 2: SizeOrLength (full 32 bits; preserves the sign bit / UnknownSize sentinel)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,11 @@ public void PackedFlags_Should_PreserveAllEightBits_WhenRowsAreCreated()
_metaDb.ReplacePreserveParent(replaceCursor, ElementTokenType.String, flags: flags);

// Assert
Assert.Equal(17, DbRow.FlagsShift);
// The flags sit above the 22-bit id and the 2-bit reference type and fill the int.
Assert.Equal(24, DbRow.FlagsShift);
Assert.Equal(8, DbRow.FlagsBitCount);
Assert.Equal(0xFF, DbRow.FlagsMask);
Assert.Equal(32, DbRow.FlagsShift + DbRow.FlagsBitCount);
Assert.All(cursors, cursor => Assert.Equal(flags, _metaDb.Get(cursor).Flags));
}

Expand Down Expand Up @@ -231,7 +233,7 @@ public void Append_WithMaxValues_StoresCorrectly()
const int maxSizeOrLength = int.MaxValue; // 31 bits
const int maxSourceDocumentId = 0x7FFF; // 15 bits (reduced from 16)
const int maxParentRow = 0x0FFFFFFF; // 28 bits
const int maxSelectionSetId = 0x7FFF; // 15 bits
const int maxSelectionSetId = CompositeResultDocument.DbRow.OperationReferenceIdMask; // 22 bits

// Act
var index = _metaDb.Append(
Expand All @@ -253,6 +255,78 @@ public void Append_WithMaxValues_StoresCorrectly()
Assert.Equal(maxSelectionSetId, row.OperationReferenceId);
}

[Fact]
public void OperationReferenceId_Should_NotOverlapTypeAndFlags_When_AllBitsAreSet()
{
// Arrange
// The widest id, a non-zero reference type and every flag share one int; each
// field must survive the round trip untouched by its neighbors.
const int maxId = CompositeResultDocument.DbRow.OperationReferenceIdMask;
const ElementFlags allFlags = (ElementFlags)CompositeResultDocument.DbRow.FlagsMask;

// Act
var index = _metaDb.Append(
ElementTokenType.StartObject,
operationReferenceId: maxId,
operationReferenceType: OperationReferenceType.Selection,
flags: allFlags);
var row = _metaDb.Get(index);

var snapshot =
$$"""
Id: {{row.OperationReferenceId}}
Type: {{row.OperationReferenceType}}
Flags: {{row.Flags}}
IdBits: {{CompositeResultDocument.DbRow.OperationReferenceIdBitCount}}
IdMax: {{maxId}}
""";

// Assert
snapshot.MatchInlineSnapshot(
"""
Id: 4194303
Type: Selection
Flags: Invalidated, SourceResult, IsNullable, IsRoot, IsInternal, IsExcluded, IsEnumValue, NullMarker
IdBits: 22
IdMax: 4194303
""");
}

[Fact]
public void InlineAppenders_Should_EncodeSameLayoutAsRowConstructor_When_IdExceedsFifteenBits()
{
// Arrange
// The templated appenders write the selection-and-flags word inline instead of
// going through the DbRow constructor, so an id above the old 15-bit ceiling
// must decode identically from every producer.
const int wideId = 0x8000 + 12345; // needs bit 15 and above
const ElementFlags flags = ElementFlags.IsNullable | ElementFlags.NullMarker;

// Act
var constructed = _metaDb.Append(
ElementTokenType.PropertyName,
operationReferenceId: wideId,
operationReferenceType: OperationReferenceType.Selection,
flags: flags);
var emptyProperty = _metaDb.AppendEmptyProperty(parentRow: 0, wideId, flags);
var propertyWithNull = _metaDb.AppendEmptyPropertyWithNullValue(parentRow: 0, wideId, flags);
var startObject = _metaDb.AppendStartObject(parentRow: 0, wideId, propertyCount: 1, flags);

var rows = new[]
{
_metaDb.Get(constructed),
_metaDb.Get(emptyProperty),
_metaDb.Get(propertyWithNull),
_metaDb.Get(startObject)
};

// Assert
Assert.All(rows, row => Assert.Equal(wideId, row.OperationReferenceId));
Assert.All(rows, row => Assert.Equal(flags, row.Flags));
Assert.Equal(OperationReferenceType.Selection, rows[1].OperationReferenceType);
Assert.Equal(OperationReferenceType.SelectionSet, rows[3].OperationReferenceType);
}

[Fact]
public void IsSimpleValue_ForPrimitiveTypes_ReturnsTrue()
{
Expand Down
Loading