diff --git a/src/All.slnx b/src/All.slnx index 5620c0e8200..a0dedb768b2 100644 --- a/src/All.slnx +++ b/src/All.slnx @@ -90,9 +90,7 @@ - - @@ -112,7 +110,6 @@ - diff --git a/src/CookieCrumble/src/CookieCrumble.HotChocolate/CookieCrumble.HotChocolate.csproj b/src/CookieCrumble/src/CookieCrumble.HotChocolate/CookieCrumble.HotChocolate.csproj index d177e47f486..a674bb77188 100644 --- a/src/CookieCrumble/src/CookieCrumble.HotChocolate/CookieCrumble.HotChocolate.csproj +++ b/src/CookieCrumble/src/CookieCrumble.HotChocolate/CookieCrumble.HotChocolate.csproj @@ -10,7 +10,6 @@ - diff --git a/src/HotChocolate/Adapters/src/Adapters.Mcp/HotChocolate.Adapters.Mcp.csproj b/src/HotChocolate/Adapters/src/Adapters.Mcp/HotChocolate.Adapters.Mcp.csproj index 119b51ea708..6d167c021a3 100644 --- a/src/HotChocolate/Adapters/src/Adapters.Mcp/HotChocolate.Adapters.Mcp.csproj +++ b/src/HotChocolate/Adapters/src/Adapters.Mcp/HotChocolate.Adapters.Mcp.csproj @@ -6,7 +6,6 @@ - diff --git a/src/HotChocolate/AspNetCore/src/Transport.Http/GraphQLHttpResponse.cs b/src/HotChocolate/AspNetCore/src/Transport.Http/GraphQLHttpResponse.cs index 4c57bac7179..2281101d5b2 100644 --- a/src/HotChocolate/AspNetCore/src/Transport.Http/GraphQLHttpResponse.cs +++ b/src/HotChocolate/AspNetCore/src/Transport.Http/GraphQLHttpResponse.cs @@ -1,3 +1,5 @@ + +using HotChocolate.Buffers; #if FUSION using System.Buffers; using System.Net; @@ -160,7 +162,7 @@ private async ValueTask ReadAsResultInternalAsync(string? charS #if FUSION // we try and read the first chunk into a single chunk. var reader = PipeReader.Create(stream, s_options); - var currentChunk = JsonMemory.Rent(); + var currentChunk = JsonMemory.Rent(JsonMemoryKind.Json); var currentChunkPosition = 0; var chunkIndex = 0; var chunks = ArrayPool.Shared.Rent(64); @@ -215,7 +217,7 @@ private async ValueTask ReadAsResultInternalAsync(string? charS } chunks[chunkIndex++] = currentChunk; - currentChunk = JsonMemory.Rent(); + currentChunk = JsonMemory.Rent(JsonMemoryKind.Json); currentChunkPosition = 0; } } @@ -253,7 +255,7 @@ private async ValueTask ReadAsResultInternalAsync(string? charS } chunks[chunkIndex++] = currentChunk; - currentChunk = JsonMemory.Rent(); + currentChunk = JsonMemory.Rent(JsonMemoryKind.Json); currentChunkPosition = 0; } } diff --git a/src/HotChocolate/AspNetCore/src/Transport.Http/JsonLines/JsonLinesReader.cs b/src/HotChocolate/AspNetCore/src/Transport.Http/JsonLines/JsonLinesReader.cs index b8bfef41735..f9896ac4442 100644 --- a/src/HotChocolate/AspNetCore/src/Transport.Http/JsonLines/JsonLinesReader.cs +++ b/src/HotChocolate/AspNetCore/src/Transport.Http/JsonLines/JsonLinesReader.cs @@ -2,6 +2,7 @@ using System.Buffers; using System.IO.Pipelines; using System.Runtime.CompilerServices; +using HotChocolate.Buffers; using HotChocolate.Fusion.Text.Json; #else using System.Buffers; @@ -103,7 +104,7 @@ private static SourceResultDocument ParseDocument(ReadOnlySequence lineBuf // Ceiling division to make sure we end up with the right amount of chunks. var chunksNeeded = (requiredSize + JsonMemory.BufferSize - 1) / JsonMemory.BufferSize; - var chunks = JsonMemory.RentRange(chunksNeeded); + var chunks = JsonMemory.RentRange(JsonMemoryKind.Json, chunksNeeded); var chunkIndex = 0; var chunkPosition = 0; diff --git a/src/HotChocolate/AspNetCore/src/Transport.Http/Sse/SseEventParser.cs b/src/HotChocolate/AspNetCore/src/Transport.Http/Sse/SseEventParser.cs index 47d0ba8e6a3..2866a300500 100644 --- a/src/HotChocolate/AspNetCore/src/Transport.Http/Sse/SseEventParser.cs +++ b/src/HotChocolate/AspNetCore/src/Transport.Http/Sse/SseEventParser.cs @@ -2,7 +2,7 @@ using System.Buffers; using System.Diagnostics; using System.Runtime.CompilerServices; -using HotChocolate.Fusion.Text.Json; +using HotChocolate.Buffers; #else using System.Runtime.CompilerServices; diff --git a/src/HotChocolate/AspNetCore/src/Transport.Http/Sse/SseReader.cs b/src/HotChocolate/AspNetCore/src/Transport.Http/Sse/SseReader.cs index f7de7d41d38..3348a1e1100 100644 --- a/src/HotChocolate/AspNetCore/src/Transport.Http/Sse/SseReader.cs +++ b/src/HotChocolate/AspNetCore/src/Transport.Http/Sse/SseReader.cs @@ -2,6 +2,7 @@ using System.Buffers; using System.IO.Pipelines; using System.Runtime.CompilerServices; +using HotChocolate.Buffers; using HotChocolate.Fusion.Text.Json; #else using System.Buffers; @@ -107,7 +108,7 @@ public async IAsyncEnumerator GetAsyncEnumerator( case SseEventType.Complete: reader.AdvanceTo(buffer.GetPosition(1, position.Value)); #if FUSION - JsonMemory.Return(eventBuffers); + JsonMemory.Return(JsonMemoryKind.Json, eventBuffers); eventBuffers.Clear(); #endif yield break; @@ -162,7 +163,7 @@ public async IAsyncEnumerator GetAsyncEnumerator( await reader.CompleteAsync().ConfigureAwait(false); #if FUSION // we return whatever is in here. - JsonMemory.Return(eventBuffers); + JsonMemory.Return(JsonMemoryKind.Json, eventBuffers); #endif } } @@ -232,7 +233,7 @@ private static void WriteBytesToChunks(List chunks, ref int currentPosit if (chunks.Count == 0 || currentPosition >= JsonMemory.BufferSize) { currentPosition = 0; - chunks.Add(JsonMemory.Rent()); + chunks.Add(JsonMemory.Rent(JsonMemoryKind.Json)); } var currentChunk = chunks[^1]; diff --git a/src/HotChocolate/Caching/src/Caching/HotChocolate.Caching.csproj b/src/HotChocolate/Caching/src/Caching/HotChocolate.Caching.csproj index 19568782fb1..76d0623f5d7 100644 --- a/src/HotChocolate/Caching/src/Caching/HotChocolate.Caching.csproj +++ b/src/HotChocolate/Caching/src/Caching/HotChocolate.Caching.csproj @@ -18,7 +18,6 @@ - diff --git a/src/HotChocolate/Core/HotChocolate.Core.slnx b/src/HotChocolate/Core/HotChocolate.Core.slnx index 874b0c44d4f..64c576bc86a 100644 --- a/src/HotChocolate/Core/HotChocolate.Core.slnx +++ b/src/HotChocolate/Core/HotChocolate.Core.slnx @@ -7,9 +7,7 @@ - - @@ -29,7 +27,6 @@ - diff --git a/src/HotChocolate/Core/src/Authorization/HotChocolate.Authorization.csproj b/src/HotChocolate/Core/src/Authorization/HotChocolate.Authorization.csproj index 36369fa8d90..c4fea1db81e 100644 --- a/src/HotChocolate/Core/src/Authorization/HotChocolate.Authorization.csproj +++ b/src/HotChocolate/Core/src/Authorization/HotChocolate.Authorization.csproj @@ -14,7 +14,7 @@ - + diff --git a/src/HotChocolate/Core/src/Core/HotChocolate.Core.csproj b/src/HotChocolate/Core/src/Core/HotChocolate.Core.csproj index 8c5f1883691..21d8f857b7b 100644 --- a/src/HotChocolate/Core/src/Core/HotChocolate.Core.csproj +++ b/src/HotChocolate/Core/src/Core/HotChocolate.Core.csproj @@ -18,8 +18,6 @@ - - diff --git a/src/HotChocolate/Core/src/Execution.Operation.Abstractions/ISelection.cs b/src/HotChocolate/Core/src/Execution.Operation.Abstractions/ISelection.cs index 40d92b1dd1e..5de00d5bd0b 100644 --- a/src/HotChocolate/Core/src/Execution.Operation.Abstractions/ISelection.cs +++ b/src/HotChocolate/Core/src/Execution.Operation.Abstractions/ISelection.cs @@ -96,7 +96,7 @@ public interface ISelection /// Gets the original syntax nodes that contributed to this selection. /// /// - /// An enumerable of field nodes from the original GraphQL document that + /// An enumerable collection of field nodes from the original GraphQL document that /// were merged to create this selection. Multiple nodes may be returned /// if field merging occurred (same response name, compatible arguments). /// diff --git a/src/HotChocolate/Core/src/Execution.Operation.Abstractions/ISelectionSet.cs b/src/HotChocolate/Core/src/Execution.Operation.Abstractions/ISelectionSet.cs index 7bd3aeef3d3..a64c48f961a 100644 --- a/src/HotChocolate/Core/src/Execution.Operation.Abstractions/ISelectionSet.cs +++ b/src/HotChocolate/Core/src/Execution.Operation.Abstractions/ISelectionSet.cs @@ -2,6 +2,11 @@ namespace HotChocolate.Execution; +/// +/// A selection set is primarily composed of field selections. +/// When needed a selection set can preserve fragments so that the execution engine +/// can branch the processing of these fragments. +/// public interface ISelectionSet { /// diff --git a/src/HotChocolate/Core/src/Execution.Projections/HotChocolate.Execution.Projections.csproj b/src/HotChocolate/Core/src/Execution.Projections/HotChocolate.Execution.Projections.csproj index 74997ec1546..8e5a544034f 100644 --- a/src/HotChocolate/Core/src/Execution.Projections/HotChocolate.Execution.Projections.csproj +++ b/src/HotChocolate/Core/src/Execution.Projections/HotChocolate.Execution.Projections.csproj @@ -10,7 +10,7 @@ - + diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/Factories/DeferredWorkStateOwnerFactory.cs b/src/HotChocolate/Core/src/Execution/DependencyInjection/Factories/DeferredWorkStateOwnerFactory.cs deleted file mode 100644 index 0e75c785bae..00000000000 --- a/src/HotChocolate/Core/src/Execution/DependencyInjection/Factories/DeferredWorkStateOwnerFactory.cs +++ /dev/null @@ -1,39 +0,0 @@ -using HotChocolate.Execution.DependencyInjection; -using HotChocolate.Execution.Processing; -using Microsoft.Extensions.ObjectPool; - -namespace Microsoft.Extensions.DependencyInjection; - -/// -/// The deferred is injected as a scoped services and -/// preserves the instance it creates. -/// -/// This is done so that the executions running on one service scope share the deferred execution -/// state between each other. -/// -/// is disposable and will be disposed with the request scope. -/// -internal sealed class DeferredWorkStateOwnerFactory : IFactory -{ - private readonly object _sync = new(); - private readonly ObjectPool _pool; - private DeferredWorkStateOwner? _owner; - - public DeferredWorkStateOwnerFactory(ObjectPool pool) - { - _pool = pool; - } - - public DeferredWorkStateOwner Create() - { - if (_owner is null) - { - lock (_sync) - { - _owner ??= new DeferredWorkStateOwner(_pool); - } - } - - return _owner; - } -} diff --git a/src/HotChocolate/Core/src/Execution/HotChocolate.Execution.csproj b/src/HotChocolate/Core/src/Execution/HotChocolate.Execution.csproj deleted file mode 100644 index a30275088fc..00000000000 --- a/src/HotChocolate/Core/src/Execution/HotChocolate.Execution.csproj +++ /dev/null @@ -1,247 +0,0 @@ - - - - true - - - - HotChocolate.Execution - HotChocolate.Execution - HotChocolate.Execution - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - True - True - Resources.resx - - - OperationCompiler.cs - - - OperationCompiler.cs - - - OperationCompiler.cs - - - OperationCompiler.cs - - - OperationContext.cs - - - OperationContext.cs - - - OperationCompiler.cs - - - MiddlewareContext.Global.cs - - - MiddlewareContext.Global.cs - - - MiddlewareContext.Global.cs - - - MiddlewareContext.Global.cs - - - MiddlewareContext.Global.cs - - - ResolverTask.cs - - - WorkScheduler.cs - - - WorkScheduler.cs - - - SchemaRequestExecutorBuilderExtensions.cs - - - SchemaRequestExecutorBuilderExtensions.cs - - - SchemaRequestExecutorBuilderExtensions.cs - - - SchemaRequestExecutorBuilderExtensions.cs - - - SchemaRequestExecutorBuilderExtensions.cs - - - SchemaRequestExecutorBuilderExtensions.cs - - - SchemaRequestExecutorBuilderExtensions.cs - - - SchemaRequestExecutorBuilderExtensions.cs - - - SchemaRequestExecutorBuilderExtensions.cs - - - RequestExecutorBuilderExtensions.cs - - - RequestExecutorBuilderExtensions.cs - - - RequestExecutorBuilderExtensions.cs - - - RequestExecutorBuilderExtensions.cs - - - RequestExecutorBuilderExtensions.cs - - - RequestExecutorBuilderExtensions.cs - - - RequestExecutorBuilderExtensions.cs - - - RequestExecutorBuilderExtensions.cs - - - RequestExecutorBuilderExtensions.cs - - - ValueCompletion.cs - - - ValueCompletion.cs - - - ValueCompletion.cs - - - ResultBuilder.cs - - - ResultBuilder.cs - - - ResultBuilder.cs - - - ResolverTask.cs - - - ResolverTask.cs - - - OperationContext.cs - - - OperationContext.cs - - - OperationContext.cs - - - OperationContext.cs - - - JsonResultFormatter.cs - - - SubscriptionExecutor.cs - - - RequestExecutorResolver.cs - - - ValueCompletion.cs - - - BatchExecutor.cs - - - RequestExecutorBuilderExtensions.cs - - - RequestExecutorBuilderExtensions.cs - - - RequestExecutorBuilderExtensions.cs - - - RequestExecutorManager.cs - - - RequestExecutorManager.cs - - - RequestExecutorManager.cs - - - RequestExecutorBuilderExtensions.cs - - - RequestExecutorBuilderExtensions.cs - - - - - - ResXFileCodeGenerator - Resources.Designer.cs - - - - diff --git a/src/HotChocolate/Core/src/Execution/Processing/DeferredExecutionTask.cs b/src/HotChocolate/Core/src/Execution/Processing/DeferredExecutionTask.cs deleted file mode 100644 index f4de9856a12..00000000000 --- a/src/HotChocolate/Core/src/Execution/Processing/DeferredExecutionTask.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System.Collections.Immutable; -using HotChocolate.Utilities; -using static HotChocolate.WellKnownContextData; - -namespace HotChocolate.Execution.Processing; - -/// -/// Represents a deprioritized part of the query that will be executed after -/// the main execution has finished. -/// -internal abstract class DeferredExecutionTask -{ - /// - /// Initializes a new instance of . - /// - protected DeferredExecutionTask(IImmutableDictionary scopedContextData) - { - ScopedContextData = scopedContextData; - } - - /// - /// Gets the preserved scoped context from the parent resolver. - /// - public IImmutableDictionary ScopedContextData { get; } - - /// - /// Starts executing the deferred execution task. - /// - /// - /// The operation context owner. - /// - /// - /// The internal result identifier. - /// - /// - /// The internal identifier of the object that the result will be patched into. - /// - public void Begin(OperationContextOwner operationContextOwner, uint resultId, uint patchId) - { - // retrieve the task on which this task depends upon. We do this to ensure that the result - // of this task is not delivered before the parent result is delivered. - uint parentResultId = 0; - if (ScopedContextData.TryGetValue(DeferredResultId, out var value) - && value is uint id) - { - parentResultId = id; - } - - var capturedContext = ExecutionContext.Capture(); - if (capturedContext is null) - { - ExecuteAsync(operationContextOwner, resultId, parentResultId, patchId).FireAndForget(); - } - else - { - var execute = () => - ExecutionContext.Run( - capturedContext, - _ => ExecuteAsync(operationContextOwner, resultId, parentResultId, patchId), - null); - execute.FireAndForget(); - } - } - - /// - /// The task execution logic. - /// - /// - /// The operation context owner. - /// - /// - /// The internal result identifier. - /// - /// - /// The parent result identifier. - /// - /// - /// The internal identifier of the object that the result will be patched into. - /// - protected abstract Task ExecuteAsync( - OperationContextOwner operationContextOwner, - uint resultId, - uint parentResultId, - uint patchId); -} diff --git a/src/HotChocolate/Core/src/Execution/Processing/DeferredExecutionTaskResult.cs b/src/HotChocolate/Core/src/Execution/Processing/DeferredExecutionTaskResult.cs deleted file mode 100644 index 23fad4d1ea5..00000000000 --- a/src/HotChocolate/Core/src/Execution/Processing/DeferredExecutionTaskResult.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace HotChocolate.Execution.Processing; - -internal readonly struct DeferredExecutionTaskResult -{ - public DeferredExecutionTaskResult( - uint taskId, - uint parentTaskId, - IOperationResult? result = null) - { - TaskId = taskId; - ParentTaskId = parentTaskId; - Result = result; - } - - public uint TaskId { get; } - - public uint ParentTaskId { get; } - - public IOperationResult? Result { get; } -} diff --git a/src/HotChocolate/Core/src/Execution/Processing/DeferredFragment.cs b/src/HotChocolate/Core/src/Execution/Processing/DeferredFragment.cs deleted file mode 100644 index 90faf750543..00000000000 --- a/src/HotChocolate/Core/src/Execution/Processing/DeferredFragment.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System.Collections.Immutable; -using static HotChocolate.Execution.Processing.Tasks.ResolverTaskFactory; -using static HotChocolate.WellKnownContextData; - -namespace HotChocolate.Execution.Processing; - -/// -/// Represents a deprioritized fragment of the query that will be executed after -/// the main execution has finished. -/// -internal sealed class DeferredFragment : DeferredExecutionTask -{ - /// - /// Initializes a new instance of . - /// - public DeferredFragment( - IFragment fragment, - string? label, - Path path, - object? parent, - IImmutableDictionary scopedContextData) - : base(scopedContextData) - { - Fragment = fragment; - Label = label; - Path = path; - Parent = parent; - } - - /// - /// Gets the deferred fragment. - /// - public IFragment Fragment { get; } - - /// - /// If this argument label has a value other than null, it will be passed - /// on to the result of this defer directive. This label is intended to - /// give client applications a way to identify to which fragment a deferred - /// result belongs to. - /// - public string? Label { get; } - - /// - /// Gets the result path into which this deferred fragment shall be patched. - /// - public Path Path { get; } - - /// - /// Gets the parent / source value. - /// - public object? Parent { get; } - - protected override async Task ExecuteAsync( - OperationContextOwner operationContextOwner, - uint resultId, - uint parentResultId, - uint patchId) - { - try - { - var operationContext = operationContextOwner.OperationContext; - var parentResult = operationContext.Result.RentObject(Fragment.SelectionSet.Selections.Count); - - parentResult.PatchPath = Path; - - EnqueueResolverTasks( - operationContext, - Fragment.SelectionSet, - Parent, - Path, - // for the execution of this task we set the deferred task ID so that - // child deferrals can lookup their dependency to this task. - ScopedContextData.SetItem(DeferredResultId, resultId), - parentResult); - - // start executing the deferred fragment. - await operationContext.Scheduler.ExecuteAsync().ConfigureAwait(false); - - // we create the result but will not create the final result object yet. - // We will leave the final creation to the deferred work scheduler so that the - // has next property can be correctly set. - var result = - operationContext - .SetLabel(Label) - .SetPath(Path) - .SetData(parentResult) - .SetPatchId(patchId) - .BuildResult(); - - // complete the task and provide the result - operationContext.DeferredScheduler.Complete(new(resultId, parentResultId, result)); - } - finally - { - operationContextOwner.Dispose(); - } - } -} diff --git a/src/HotChocolate/Core/src/Execution/Processing/DeferredStream.cs b/src/HotChocolate/Core/src/Execution/Processing/DeferredStream.cs deleted file mode 100644 index f5d6b220101..00000000000 --- a/src/HotChocolate/Core/src/Execution/Processing/DeferredStream.cs +++ /dev/null @@ -1,158 +0,0 @@ -using System.Collections.Immutable; -using HotChocolate.Execution.Processing.Tasks; -using static HotChocolate.WellKnownContextData; - -namespace HotChocolate.Execution.Processing; - -/// -/// Represents the work to executed the deferred elements of a stream. -/// -internal sealed class DeferredStream : DeferredExecutionTask -{ - private StreamExecutionTask? _task; - - /// - /// Initializes a new instance of . - /// - public DeferredStream( - ISelection selection, - string? label, - Path path, - object? parent, - int index, - IAsyncEnumerator enumerator, - IImmutableDictionary scopedContextData) - : base(scopedContextData) - { - Selection = selection; - Label = label; - Path = path; - Parent = parent; - Index = index; - Enumerator = enumerator; - } - - /// - /// Gets the selection of the streamed field. - /// - public ISelection Selection { get; } - - /// - /// If this argument label has a value other than null, it will be passed - /// on to the result of this defer directive. This label is intended to - /// give client applications a way to identify to which fragment a deferred - /// result belongs to. - /// - public string? Label { get; } - - /// - /// Gets the result path into which this deferred fragment shall be patched. - /// - public Path Path { get; } - - /// - /// Gets the index of the last element. - /// - public int Index { get; private set; } - - /// - /// Gets the parent / source value. - /// - public object? Parent { get; } - - /// - /// Gets the enumerator to retrieve the payloads of the stream. - /// - public IAsyncEnumerator Enumerator { get; } - - protected override async Task ExecuteAsync( - OperationContextOwner operationContextOwner, - uint resultId, - uint parentResultId, - uint patchId) - { - var operationContext = operationContextOwner.OperationContext; - - try - { - _task ??= new StreamExecutionTask(this); - _task.Reset(operationContext, resultId); - operationContext.Scheduler.Register(_task); - await operationContext.Scheduler.ExecuteAsync().ConfigureAwait(false); - - // if there is no child task, then there is no more data, so we can complete. - if (_task.ChildTask is null) - { - operationContext.DeferredScheduler.Complete(new(resultId, parentResultId)); - return; - } - - var item = _task.ChildTask.ParentResult[0].Value!; - - var result = operationContext - .SetLabel(Label) - .SetPath(Path.Append(Index)) - .SetItems([item]) - .SetPatchId(patchId) - .BuildResult(); - - await _task.ChildTask.CompleteUnsafeAsync().ConfigureAwait(false); - - // we will register this same task again to get the next item. - operationContext.DeferredScheduler.Register(this, patchId); - operationContext.DeferredScheduler.Complete(new(resultId, parentResultId, result)); - } - catch (Exception ex) - { - var result = OperationResultBuilder.CreateError(ErrorBuilder.FromException(ex).Build()); - operationContext.DeferredScheduler.Complete(new(resultId, parentResultId, result)); - } - finally - { - operationContextOwner.Dispose(); - } - } - - private sealed class StreamExecutionTask : ExecutionTask - { - private readonly DeferredStream _deferredStream; - private OperationContext _operationContext = null!; - private IImmutableDictionary _scopedContextData; - - public StreamExecutionTask(DeferredStream deferredStream) - { - _deferredStream = deferredStream; - _scopedContextData = _deferredStream.ScopedContextData; - } - - protected override IExecutionTaskContext Context => _operationContext; - - public ResolverTask? ChildTask { get; private set; } - - protected override async ValueTask ExecuteAsync(CancellationToken cancellationToken) - { - ChildTask = null; - _deferredStream.Index++; - var hasNext = await _deferredStream.Enumerator.MoveNextAsync(); - - if (hasNext) - { - ChildTask = ResolverTaskFactory.EnqueueElementTasks( - _operationContext, - _deferredStream.Selection, - _deferredStream.Parent, - _deferredStream.Path, - _deferredStream.Index, - _deferredStream.Enumerator, - _scopedContextData); - } - } - - public void Reset(OperationContext operationContext, uint taskId) - { - _operationContext = operationContext; - _scopedContextData = _scopedContextData.SetItem(DeferredResultId, taskId); - Reset(); - } - } -} diff --git a/src/HotChocolate/Core/src/Execution/Processing/DeferredWorkScheduler.cs b/src/HotChocolate/Core/src/Execution/Processing/DeferredWorkScheduler.cs deleted file mode 100644 index 4bbccb2a888..00000000000 --- a/src/HotChocolate/Core/src/Execution/Processing/DeferredWorkScheduler.cs +++ /dev/null @@ -1,178 +0,0 @@ -using HotChocolate.Execution.DependencyInjection; -using HotChocolate.Execution.Instrumentation; -using Microsoft.Extensions.DependencyInjection; -using static HotChocolate.Execution.OperationResultBuilder; - -namespace HotChocolate.Execution.Processing; - -/// -/// Represents a backlog for deferred work. -/// -internal sealed class DeferredWorkScheduler -{ - private readonly object _stateSync = new(); - private IFactory _operationContextFactory = null!; - private IFactory _deferredWorkStateFactory = null!; - private OperationContext _parentContext = null!; - private DeferredWorkStateOwner? _stateOwner; - - private DeferredWorkStateOwner StateOwner - { - get - { - if (_stateOwner is null) - { - lock (_stateSync) - { - _stateOwner ??= _deferredWorkStateFactory.Create(); - } - } - - return _stateOwner; - } - } - - /// - /// Specifies if there was deferred work enqueued. - /// - public bool HasResults => _stateOwner?.State.HasResults is true; - - public void Initialize(OperationContext operationContext) - { - var services = operationContext.Services; - - _stateOwner = null; - _parentContext = operationContext; - _operationContextFactory = services.GetRequiredService>(); - _deferredWorkStateFactory = services.GetRequiredService>(); - } - - public void InitializeFrom(OperationContext operationContext, DeferredWorkScheduler scheduler) - { - _stateOwner = scheduler.StateOwner; - _parentContext = operationContext; - _operationContextFactory = scheduler._operationContextFactory; - _deferredWorkStateFactory = scheduler._deferredWorkStateFactory; - } - - /// - /// Registers deferred work - /// - public void Register(DeferredExecutionTask task, ResultData parentResult) - { - // first we get the result identifier which is used to refer to the result that we defer. - var resultId = StateOwner.State.CreateId(); - - // next we assign a patch identifier to the result set into which the deferred result - // shall be patched into. - var patchId = StateOwner.State.AssignPatchId(parentResult); - - // for the spawned execution we need an operation context which we will initialize - // from the current operation context. - var taskContextOwner = _operationContextFactory.Create(); - taskContextOwner.OperationContext.InitializeFrom(_parentContext); - - // Last we register our patch identifier with the parent result so that - // we can more efficiently mark discarded result sets to not send down - // patches that cannot be applied. - _parentContext.Result.AddPatchId(patchId); - - // with all in place we will start the execution of the deferred task. - task.Begin(taskContextOwner, resultId, patchId); - } - - public void Register(DeferredExecutionTask task, uint patchId) - { - var resultId = StateOwner.State.CreateId(); - var taskContextOwner = _operationContextFactory.Create(); - taskContextOwner.OperationContext.InitializeFrom(_parentContext); - task.Begin(taskContextOwner, resultId, patchId); - } - - public void Complete(DeferredExecutionTaskResult result) - => StateOwner.State.Complete(result); - - public IAsyncEnumerable CreateResultStream(IOperationResult initialResult) - => new DeferredResultStream( - initialResult, - StateOwner, - _parentContext.Operation, - _parentContext.DiagnosticEvents); - - public void Clear() - { - _stateOwner = null; - _operationContextFactory = null!; - _deferredWorkStateFactory = null!; - _parentContext = null!; - } - - private class DeferredResultStream : IAsyncEnumerable - { - private readonly IOperationResult _initialResult; - private readonly DeferredWorkStateOwner _stateOwner; - private readonly IOperation _operation; - private readonly IExecutionDiagnosticEvents _diagnosticEvents; - - public DeferredResultStream( - IOperationResult initialResult, - DeferredWorkStateOwner stateOwner, - IOperation operation, - IExecutionDiagnosticEvents diagnosticEvents) - { - _initialResult = FromResult(initialResult).SetHasNext(true).Build(); - _stateOwner = stateOwner; - _operation = operation; - _diagnosticEvents = diagnosticEvents; - } - - public async IAsyncEnumerator GetAsyncEnumerator( - CancellationToken cancellationToken = default) - { - var span = _diagnosticEvents.ExecuteStream(_operation); - var state = _stateOwner.State; - var hasNext = true; - var completed = false; - - try - { - yield return _initialResult; - - while (!cancellationToken.IsCancellationRequested) - { - var result = await state - .TryDequeueResultsAsync(cancellationToken) - .ConfigureAwait(false); - - if (result is not null) - { - hasNext = result.HasNext ?? false; - yield return result; - } - else if (state.IsCompleted) - { - if (hasNext) - { - yield return new OperationResult(null, hasNext: false); - } - - yield break; - } - } - - completed = !cancellationToken.IsCancellationRequested; - } - finally - { - span.Dispose(); - } - - // we only return the state back to its pool if the operation was not cancelled - // or otherwise faulted. - if (completed) - { - _stateOwner.Dispose(); - } - } - } -} diff --git a/src/HotChocolate/Core/src/Execution/Processing/DeferredWorkState.cs b/src/HotChocolate/Core/src/Execution/Processing/DeferredWorkState.cs deleted file mode 100644 index ad678864378..00000000000 --- a/src/HotChocolate/Core/src/Execution/Processing/DeferredWorkState.cs +++ /dev/null @@ -1,215 +0,0 @@ -using System.Runtime.CompilerServices; -using static HotChocolate.WellKnownContextData; - -namespace HotChocolate.Execution.Processing; - -internal sealed class DeferredWorkState -{ - private readonly object _completeSync = new(); - private readonly object _deliverSync = new(); - private readonly object _patchSync = new(); - - private readonly List _ready = []; - private readonly Queue _deliverable = new(); - private readonly HashSet _completed = []; - private readonly HashSet _notPatchable = []; - private SemaphoreSlim _semaphore = new(0); - private uint _taskId; - private uint _work; - private uint _patchId; - - public bool HasResults => _taskId > 0; - - public bool IsCompleted => _work is 0; - - public uint CreateId() - { - lock (_deliverSync) - { - _work++; - return ++_taskId; - } - } - - public uint AssignPatchId(ResultData resultData) - { - if (resultData.PatchId == 0) - { - lock (_patchSync) - { - if (resultData.PatchId == 0) - { - var patchId = ++_patchId; - resultData.PatchId = patchId; - return patchId; - } - } - } - - return resultData.PatchId; - } - - public void Complete(DeferredExecutionTaskResult result) - { - var update = true; - - try - { - lock (_completeSync) - { - if (result.ParentTaskId is 0 || _completed.Contains(result.ParentTaskId)) - { - _completed.Add(result.TaskId); - EnqueueResult(result.Result); - - var evaluateDeferredResults = _ready.Count > 0; - - while (evaluateDeferredResults) - { - var i = 0; - evaluateDeferredResults = false; - - while (_ready.Count > 0 && i < _ready.Count) - { - var current = _ready[i]; - - if (_completed.Contains(current.ParentTaskId)) - { - _completed.Add(current.TaskId); - _ready.RemoveAt(i); - EnqueueResult(current.Result); - evaluateDeferredResults = true; - } - else - { - i++; - } - } - } - } - else - { - _ready.Add(result); - update = false; - } - } - } - finally - { - if (update) - { - _semaphore.Release(); - } - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void EnqueueResult(IOperationResult? queryResult) - { - lock (_deliverSync) - { - if (queryResult is not null) - { - _deliverable.Enqueue(queryResult); - } - else - { - _work--; - } - } - } - - public async ValueTask TryDequeueResultsAsync( - CancellationToken cancellationToken) - { - await _semaphore.WaitAsync(cancellationToken); - - lock (_deliverSync) - { - if (_deliverable.Count > 0) - { - var hasNext = true; - var result = new IOperationResult[_deliverable.Count]; - var consumed = 0; - - for (var i = 0; i < result.Length; i++) - { - var deliverable = _deliverable.Dequeue(); - - if (--_work is 0) - { - _semaphore.Release(); - hasNext = false; - } - - // if the deferred result can still be patched into the result set from which - // it was being spawned of we will add it to the result batch. - if ((deliverable.ContextData?.TryGetValue(PatchId, out var value) ?? false) - && value is uint patchId - && !_notPatchable.Contains(patchId)) - { - AddRemovedResultSetsToNotPatchable(deliverable, _notPatchable); - result[consumed++] = deliverable; - } - - // if the item is not patchable we will discard it and mark all dependant - // results as not patchable. - else - { - AddAllResultSetsToNotPatchable(deliverable, _notPatchable); - } - } - - if (consumed < result.Length) - { - Array.Resize(ref result, consumed); - } - - return new OperationResult(null, incremental: result, hasNext: hasNext); - } - } - - return null; - - static void AddRemovedResultSetsToNotPatchable( - IOperationResult result, - HashSet notPatchable) - { - if ((result.ContextData?.TryGetValue(RemovedResults, out var value) ?? false) - && value is IEnumerable patchIds) - { - foreach (var patchId in patchIds) - { - notPatchable.Add(patchId); - } - } - } - - static void AddAllResultSetsToNotPatchable( - IOperationResult result, - HashSet notPatchable) - { - if ((result.ContextData?.TryGetValue(ExpectedPatches, out var value) ?? false) - && value is IEnumerable patchIds) - { - foreach (var patchId in patchIds) - { - notPatchable.Add(patchId); - } - } - } - } - - public void Reset() - { - _semaphore.Dispose(); - _semaphore = new SemaphoreSlim(0); - _ready.Clear(); - _completed.Clear(); - _deliverable.Clear(); - _notPatchable.Clear(); - _taskId = 0; - _work = 0; - _patchId = 0; - } -} diff --git a/src/HotChocolate/Core/src/Execution/Processing/DeferredWorkStateOwner.cs b/src/HotChocolate/Core/src/Execution/Processing/DeferredWorkStateOwner.cs deleted file mode 100644 index 4e9b95ed54d..00000000000 --- a/src/HotChocolate/Core/src/Execution/Processing/DeferredWorkStateOwner.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Microsoft.Extensions.ObjectPool; - -namespace HotChocolate.Execution.Processing; - -internal sealed class DeferredWorkStateOwner : IDisposable -{ - private readonly ObjectPool _pool; - private int _disposed; - - public DeferredWorkStateOwner(ObjectPool pool) - { - _pool = pool ?? throw new ArgumentNullException(nameof(pool)); - State = pool.Get(); - } - - public DeferredWorkState State { get; } - - public void Dispose() - { - if (_disposed == 0 && Interlocked.CompareExchange(ref _disposed, 1, 0) == 0) - { - _pool.Return(State); - } - } -} diff --git a/src/HotChocolate/Core/src/Execution/Processing/Fragment.cs b/src/HotChocolate/Core/src/Execution/Processing/Fragment.cs deleted file mode 100644 index 713b9cb28df..00000000000 --- a/src/HotChocolate/Core/src/Execution/Processing/Fragment.cs +++ /dev/null @@ -1,52 +0,0 @@ -using HotChocolate.Language; -using HotChocolate.Types; - -namespace HotChocolate.Execution.Processing; - -internal sealed class Fragment : IFragment -{ - private readonly long _includeCondition; - private readonly long _deferIfCondition; - - public Fragment( - int id, - IObjectTypeDefinition typeCondition, - ISyntaxNode syntaxNode, - IReadOnlyList directives, - ISelectionSet selectionSet, - long includeCondition, - long deferIfCondition, - bool isInternal = false) - { - Id = id; - TypeCondition = typeCondition; - SyntaxNode = syntaxNode; - Directives = directives; - SelectionSet = selectionSet; - _includeCondition = includeCondition; - _deferIfCondition = deferIfCondition; - IsInternal = isInternal; - } - - public int Id { get; } - - public IObjectTypeDefinition TypeCondition { get; } - - public ISyntaxNode SyntaxNode { get; } - - public IReadOnlyList Directives { get; } - - public ISelectionSet SelectionSet { get; } - - public bool IsInternal { get; } - - public bool IsConditional => _includeCondition is not 0 || _deferIfCondition is not 0; - - public string? GetLabel(IVariableValueCollection variables) - => Directives.GetDeferDirective(variables)?.Label; - - public bool IsIncluded(long includeFlags, bool allowInternals = false) - => (includeFlags & _includeCondition) == _includeCondition - && (_deferIfCondition is 0 || (includeFlags & _deferIfCondition) != _deferIfCondition) - && (!IsInternal || allowInternals); -} diff --git a/src/HotChocolate/Core/src/Execution/Processing/IncludeCondition.cs b/src/HotChocolate/Core/src/Execution/Processing/IncludeCondition.cs deleted file mode 100644 index 87061dba0df..00000000000 --- a/src/HotChocolate/Core/src/Execution/Processing/IncludeCondition.cs +++ /dev/null @@ -1,173 +0,0 @@ -using System.Runtime.CompilerServices; -using HotChocolate.Language; -using HotChocolate.Types; -using HotChocolate.Utilities; - -namespace HotChocolate.Execution.Processing; - -/// -/// This struct represents the include condition of a Field, InlineFragment or FragmentSpread. -/// -public readonly struct IncludeCondition : IEquatable -{ - internal IncludeCondition(IValueNode skip, IValueNode include) - { - Skip = skip; - Include = include; - } - - /// - /// Gets the skip value. - /// - public IValueNode Skip { get; } - - /// - /// Gets the include value. - /// - public IValueNode Include { get; } - - /// - /// If and are null then - /// there is no valid include condition. - /// - public bool IsDefault => Skip is null && Include is null; - - /// - /// Specifies if selections with this include condition are included with the - /// current variable values. - /// - /// - /// The variable values. - /// - /// - /// Returns true if selections with this include condition are included. - /// - public bool IsIncluded(IVariableValueCollection variables) - { - ArgumentNullException.ThrowIfNull(variables); - - if (Skip is null || Include is null) - { - return true; - } - - var skip = false; - - if (Skip.Kind is SyntaxKind.BooleanValue) - { - skip = ((BooleanValueNode)Skip).Value; - } - else if (Skip.Kind is SyntaxKind.Variable) - { - var variable = Unsafe.As(Skip); - skip = variables.GetValue(variable.Name.Value).Value; - } - - var include = true; - - if (Include.Kind is SyntaxKind.BooleanValue) - { - include = ((BooleanValueNode)Include).Value; - } - else if (Include.Kind is SyntaxKind.Variable) - { - var variable = Unsafe.As(Include); - include = variables.GetValue(variable.Name.Value).Value; - } - - return !skip && include; - } - - /// - /// Indicates whether the current object is equal to another object of the same type. - /// - /// An object to compare with this object. - /// - /// if the current object is equal to the - /// parameter; otherwise, . - /// - public bool Equals(IncludeCondition other) - => Skip.Equals(other.Skip, SyntaxComparison.Syntax) - && Include.Equals(other.Include, SyntaxComparison.Syntax); - - /// - /// Indicates whether this instance and a specified object are equal. - /// - /// - /// The object to compare with the current instance. - /// - /// - /// if and this instance is the same - /// type and represents the same value; otherwise, . - /// - public override bool Equals(object? obj) - => obj is IncludeCondition other && Equals(other); - - /// - /// Returns the hash code for this instance. - /// - /// - /// A 32-bit signed integer that is the hash code for this instance. - /// - public override int GetHashCode() - => HashCode.Combine( - SyntaxComparer.BySyntax.GetHashCode(Skip), - SyntaxComparer.BySyntax.GetHashCode(Include)); - - /// - /// Tries to extract the include condition from a field. - /// - /// - /// The selection to extract the include condition from. - /// - /// - /// Returns true if the selection has a custom visibility configuration. - /// - public static IncludeCondition FromSelection(ISelectionNode selection) - { - ArgumentNullException.ThrowIfNull(selection); - - IValueNode? skip = null; - IValueNode? include = null; - - if (selection.Directives.Count == 0) - { - return default; - } - - for (var i = 0; i < selection.Directives.Count; i++) - { - var directive = selection.Directives[i]; - - if (directive.Arguments.Count != 1) - { - // the skip and include arguments have a single argument. - continue; - } - - if (directive.Name.Value.EqualsOrdinal(DirectiveNames.Skip.Name)) - { - skip = directive.Arguments[0].Value; - } - - if (directive.Name.Value.EqualsOrdinal(DirectiveNames.Include.Name)) - { - include = directive.Arguments[0].Value; - } - - if (skip is not null && include is not null) - { - break; - } - } - - if (skip is null && include is null) - { - return default; - } - - return new IncludeCondition( - skip ?? NullValueNode.Default, - include ?? NullValueNode.Default); - } -} diff --git a/src/HotChocolate/Core/src/Execution/Processing/Operation.cs b/src/HotChocolate/Core/src/Execution/Processing/Operation.cs deleted file mode 100644 index df2bf92e50e..00000000000 --- a/src/HotChocolate/Core/src/Execution/Processing/Operation.cs +++ /dev/null @@ -1,218 +0,0 @@ -using System.Collections; -using System.Collections.Immutable; -using HotChocolate.Language; -using HotChocolate.Types; -using static HotChocolate.Execution.Properties.Resources; -using static HotChocolate.Execution.ThrowHelper; - -namespace HotChocolate.Execution.Processing; - -internal sealed class Operation : IOperation -{ - private readonly object _writeLock = new(); - private SelectionVariants[] _selectionVariants = []; - private IncludeCondition[] _includeConditions = []; - private ImmutableDictionary _contextData = -#if NET10_0_OR_GREATER - []; -#else - ImmutableDictionary.Empty; -#endif - private bool _sealed; - - public Operation( - string id, - DocumentNode document, - OperationDefinitionNode definition, - ObjectType rootType, - ISchemaDefinition schema) - { - Id = id; - Document = document; - Definition = definition; - RootType = rootType; - Type = definition.Operation; - Schema = schema; - - if (definition.Name?.Value is { } name) - { - Name = name; - } - } - - public string Id { get; } - - public DocumentNode Document { get; } - - public OperationDefinitionNode Definition { get; } - - public ObjectType RootType { get; } - - public string? Name { get; } - - public OperationType Type { get; } - - public ISelectionSet RootSelectionSet { get; private set; } = null!; - - public IReadOnlyList SelectionVariants - => _selectionVariants; - - public bool HasIncrementalParts { get; private set; } - - public IReadOnlyList IncludeConditions - => _includeConditions; - - public IReadOnlyDictionary ContextData => _contextData; - - public ISchemaDefinition Schema { get; } - - public ISelectionSet GetSelectionSet(ISelection selection, ObjectType typeContext) - { - ArgumentNullException.ThrowIfNull(selection); - ArgumentNullException.ThrowIfNull(typeContext); - - var selectionSetId = ((Selection)selection).SelectionSetId; - - if (selectionSetId is -1) - { - throw Operation_NoSelectionSet(); - } - - return _selectionVariants[selectionSetId].GetSelectionSet(typeContext); - } - - public IEnumerable GetPossibleTypes(ISelection selection) - { - ArgumentNullException.ThrowIfNull(selection); - - var selectionSetId = ((Selection)selection).SelectionSetId; - - if (selectionSetId == -1) - { - throw new ArgumentException(Operation_GetPossibleTypes_NoSelectionSet); - } - - return _selectionVariants[selectionSetId].GetPossibleTypes(); - } - - public long CreateIncludeFlags(IVariableValueCollection variables) - { - long context = 0; - - for (var i = 0; i < _includeConditions.Length; i++) - { - if (_includeConditions[i].IsIncluded(variables)) - { - long flag = 1; - flag <<= i; - context |= flag; - } - } - - return context; - } - - public bool TryGetState(out TState? state) - { - var key = typeof(TState).FullName ?? throw new InvalidOperationException(); - return TryGetState(key, out state); - } - - public bool TryGetState(string key, out TState? state) - { - if (_contextData.TryGetValue(key, out var value) - && value is TState casted) - { - state = casted; - return true; - } - - state = default; - return false; - } - - public TState GetOrAddState(Func createState) - => GetOrAddState(_ => createState(), null); - - public TState GetOrAddState(Func createState, TContext context) - { - var key = typeof(TState).FullName ?? throw new InvalidOperationException(); - - // ReSharper disable once InconsistentlySynchronizedField - if (!_contextData.TryGetValue(key, out var state)) - { - lock (_writeLock) - { - if (!_contextData.TryGetValue(key, out state)) - { - var newState = createState(context); - _contextData = _contextData.SetItem(key, newState); - return newState; - } - } - } - - return (TState)state!; - } - - public TState GetOrAddState( - string key, - Func createState) - => GetOrAddState(key, (k, _) => createState(k), null); - - public TState GetOrAddState( - string key, - Func createState, - TContext context) - { - // ReSharper disable once InconsistentlySynchronizedField - if (!_contextData.TryGetValue(key, out var state)) - { - lock (_writeLock) - { - if (!_contextData.TryGetValue(key, out state)) - { - var newState = createState(key, context); - _contextData = _contextData.SetItem(key, newState); - return newState; - } - } - } - - return (TState)state!; - } - - internal void Seal( - IReadOnlyDictionary contextData, - SelectionVariants[] selectionVariants, - bool hasIncrementalParts, - IncludeCondition[] includeConditions) - { - if (!_sealed) - { - _contextData = contextData.ToImmutableDictionary(); - var root = selectionVariants[0]; - RootSelectionSet = root.GetSelectionSet(RootType); - _selectionVariants = selectionVariants; - HasIncrementalParts = hasIncrementalParts; - _includeConditions = includeConditions; - _sealed = true; - } - } - - public IEnumerator GetEnumerator() - { - foreach (var selectionVariant in _selectionVariants) - { - foreach (var objectType in selectionVariant.GetPossibleTypes()) - { - yield return selectionVariant.GetSelectionSet(objectType); - } - } - } - - IEnumerator IEnumerable.GetEnumerator() - => GetEnumerator(); - - public override string ToString() => OperationPrinter.Print(this); -} diff --git a/src/HotChocolate/Core/src/Execution/Processing/Selection.cs b/src/HotChocolate/Core/src/Execution/Processing/Selection.cs deleted file mode 100644 index 309faace72b..00000000000 --- a/src/HotChocolate/Core/src/Execution/Processing/Selection.cs +++ /dev/null @@ -1,435 +0,0 @@ -using System.Diagnostics; -using HotChocolate.Execution.Properties; -using HotChocolate.Language; -using HotChocolate.Resolvers; -using HotChocolate.Types; - -namespace HotChocolate.Execution.Processing; - -/// -/// Represents a field selection during execution. -/// -public class Selection : ISelection -{ - private static readonly ArgumentMap s_emptyArguments = ArgumentMap.Empty; - private long[] _includeConditions; - private long _streamIfCondition; - private Flags _flags; - private FieldNode _syntaxNode; - private FieldNode[] _syntaxNodes; - - public Selection( - int id, - ObjectType declaringType, - ObjectField field, - IType type, - FieldNode syntaxNode, - string responseName, - ArgumentMap? arguments = null, - long[]? includeConditions = null, - bool isInternal = false, - bool isParallelExecutable = true, - FieldDelegate? resolverPipeline = null, - PureFieldDelegate? pureResolver = null) - { - Id = id; - DeclaringType = declaringType; - Field = field; - Type = type; - _syntaxNode = syntaxNode; - _syntaxNodes = [syntaxNode]; - ResponseName = responseName; - Arguments = arguments ?? s_emptyArguments; - ResolverPipeline = resolverPipeline; - PureResolver = pureResolver; - Strategy = InferStrategy(!isParallelExecutable, pureResolver is not null); - - _includeConditions = includeConditions ?? []; - - _flags = isInternal - ? Flags.Internal - : Flags.None; - - if (Type.IsListType()) - { - _flags |= Flags.List; - } - } - - protected Selection(Selection selection) - { - ArgumentNullException.ThrowIfNull(selection); - - Id = selection.Id; - Strategy = selection.Strategy; - DeclaringType = selection.DeclaringType; - Field = selection.Field; - Type = selection.Type; - _syntaxNode = selection._syntaxNode; - _syntaxNodes = selection._syntaxNodes; - ResponseName = selection.ResponseName; - ResolverPipeline = selection.ResolverPipeline; - PureResolver = selection.PureResolver; - Arguments = selection.Arguments; - _flags = selection._flags; - - _includeConditions = - selection._includeConditions.Length == 0 - ? [] - : selection._includeConditions.ToArray(); - } - - /// - public int Id { get; } - - public CustomOptionsFlags CustomOptions { get; private set; } - - /// - public SelectionExecutionStrategy Strategy { get; private set; } - - /// - public ObjectType DeclaringType { get; } - - /// - public ISelectionSet DeclaringSelectionSet { get; private set; } = null!; - - public IOperation DeclaringOperation { get; private set; } = null!; - - /// - public ObjectField Field { get; } - - /// - public IType Type { get; } - - /// - public TypeKind TypeKind => Type.Kind; - - /// - public bool IsList => (_flags & Flags.List) == Flags.List; - - /// - public FieldNode SyntaxNode => _syntaxNode; - - /// - public IReadOnlyList SyntaxNodes => _syntaxNodes; - - public int SelectionSetId { get; private set; } - - /// - public SelectionSetNode? SelectionSet => _syntaxNode.SelectionSet; - - /// - public string ResponseName { get; } - - /// - public FieldDelegate? ResolverPipeline { get; private set; } - - /// - public PureFieldDelegate? PureResolver { get; private set; } - - /// - public ArgumentMap Arguments { get; } - - /// - public bool HasStreamDirective(long includeFlags) - => (_flags & Flags.Stream) == Flags.Stream - && (_streamIfCondition is 0 || (includeFlags & _streamIfCondition) != _streamIfCondition); - - /// - /// Specifies if the current selection is immutable. - /// - public bool IsReadOnly => (_flags & Flags.Sealed) == Flags.Sealed; - - /// - public bool IsInternal => (_flags & Flags.Internal) == Flags.Internal; - - /// - public bool IsConditional - => _includeConditions.Length > 0 || (_flags & Flags.Internal) == Flags.Internal; - - internal ReadOnlySpan IncludeConditions => _includeConditions; - - public bool IsIncluded(long includeFlags, bool allowInternals = false) - { - // in most case we do not have any include condition, - // so we can take the easy way out here if we do not have any flags. - if (_includeConditions.Length is 0) - { - return !IsInternal || allowInternals; - } - - // if there are flags in most cases we just have one, so we can - // check the first and optimize for this. - var includeCondition = _includeConditions[0]; - - if ((includeFlags & includeCondition) == includeCondition) - { - return !IsInternal || allowInternals; - } - - // if we just have one flag and the flags are not fulfilled we can just exit. - if (_includeConditions.Length is 1) - { - return false; - } - - // else, we will iterate over the rest of the conditions and validate them one by one. - for (var i = 1; i < _includeConditions.Length; i++) - { - includeCondition = _includeConditions[i]; - - if ((includeFlags & includeCondition) == includeCondition) - { - return !IsInternal || allowInternals; - } - } - - return false; - } - - public override string ToString() - => _syntaxNode.ToString(); - - internal void AddSelection(FieldNode selectionSyntax, long includeCondition = 0) - { - if ((_flags & Flags.Sealed) == Flags.Sealed) - { - throw new NotSupportedException(Resources.PreparedSelection_ReadOnly); - } - - if (includeCondition == 0) - { - if (_includeConditions.Length > 0) - { - _includeConditions = []; - } - } - else if (_includeConditions.Length > 0 && Array.IndexOf(_includeConditions, includeCondition) == -1) - { - var next = _includeConditions.Length; - Array.Resize(ref _includeConditions, next + 1); - _includeConditions[next] = includeCondition; - } - - if (!_syntaxNode.Equals(selectionSyntax, SyntaxComparison.Syntax)) - { - // enlarge the syntax nodes array and add the new syntax node. - var temp = new FieldNode[_syntaxNodes.Length + 1]; - Array.Copy(_syntaxNodes, temp, _syntaxNodes.Length); - temp[_syntaxNodes.Length] = selectionSyntax; - _syntaxNodes = temp; - - _syntaxNode = MergeField(_syntaxNode, selectionSyntax); - } - } - - private static FieldNode MergeField( - FieldNode first, - FieldNode other) - { - var directives = first.Directives; - - if (other.Directives.Count > 0) - { - if (directives.Count == 0) - { - directives = other.Directives; - } - else - { - var temp = new DirectiveNode[directives.Count + other.Directives.Count]; - var next = 0; - - for (var i = 0; i < directives.Count; i++) - { - temp[next++] = directives[i]; - } - - for (var i = 0; i < other.Directives.Count; i++) - { - temp[next++] = other.Directives[i]; - } - - directives = temp; - } - } - - var selectionSet = first.SelectionSet; - - if (selectionSet is not null && other.SelectionSet is not null) - { - var selections = new ISelectionNode[ - selectionSet.Selections.Count + other.SelectionSet.Selections.Count]; - var next = 0; - - for (var i = 0; i < selectionSet.Selections.Count; i++) - { - selections[next++] = selectionSet.Selections[i]; - } - - for (var i = 0; i < other.SelectionSet.Selections.Count; i++) - { - selections[next++] = other.SelectionSet.Selections[i]; - } - - selectionSet = selectionSet.WithSelections(selections); - } - - return new FieldNode( - first.Location, - first.Name, - first.Alias, - directives, - first.Arguments, - selectionSet); - } - - internal void SetResolvers( - FieldDelegate? resolverPipeline = null, - PureFieldDelegate? pureResolver = null) - { - if ((_flags & Flags.Sealed) == Flags.Sealed) - { - throw new NotSupportedException(Resources.PreparedSelection_ReadOnly); - } - - ResolverPipeline = resolverPipeline; - PureResolver = pureResolver; - Strategy = InferStrategy(hasPureResolver: pureResolver is not null); - } - - internal void SetSelectionSetId(int selectionSetId) - { - if ((_flags & Flags.Sealed) == Flags.Sealed) - { - throw new NotSupportedException(Resources.PreparedSelection_ReadOnly); - } - - SelectionSetId = selectionSetId; - } - - internal void MarkAsStream(long ifCondition) - { - if ((_flags & Flags.Sealed) == Flags.Sealed) - { - throw new NotSupportedException(Resources.PreparedSelection_ReadOnly); - } - - _streamIfCondition = ifCondition; - _flags |= Flags.Stream; - } - - public void SetOption(CustomOptionsFlags customOptions) - { - if ((_flags & Flags.Sealed) == Flags.Sealed) - { - throw new NotSupportedException(Resources.PreparedSelection_ReadOnly); - } - - CustomOptions |= customOptions; - } - - /// - /// Completes the selection without sealing it. - /// - internal void Complete(IOperation declaringOperation, ISelectionSet declaringSelectionSet) - { - Debug.Assert(declaringSelectionSet is not null); - - if ((_flags & Flags.Sealed) != Flags.Sealed) - { - DeclaringSelectionSet = declaringSelectionSet; - DeclaringOperation = declaringOperation; - } - - Debug.Assert( - ReferenceEquals(declaringSelectionSet, DeclaringSelectionSet), - "Selections can only belong to a single selectionSet."); - } - - internal void Seal(IOperation declaringOperation, ISelectionSet declaringSelectionSet) - { - if ((_flags & Flags.Sealed) != Flags.Sealed) - { - DeclaringSelectionSet = declaringSelectionSet; - DeclaringOperation = declaringOperation; - _flags |= Flags.Sealed; - } - - Debug.Assert( - ReferenceEquals(declaringSelectionSet, DeclaringSelectionSet), - "Selections can only belong to a single selectionSet."); - } - - private SelectionExecutionStrategy InferStrategy( - bool isSerial = false, - bool hasPureResolver = false) - { - // once a field is marked serial it even with a pure resolver cannot become pure. - if (Strategy is SelectionExecutionStrategy.Serial || isSerial) - { - return SelectionExecutionStrategy.Serial; - } - - if (hasPureResolver) - { - return SelectionExecutionStrategy.Pure; - } - - return SelectionExecutionStrategy.Default; - } - - [Flags] - private enum Flags - { - None = 0, - Internal = 1, - Sealed = 2, - List = 4, - Stream = 8 - } - - [Flags] - public enum CustomOptionsFlags : byte - { - None = 0, - Option1 = 1, - Option2 = 2, - Option3 = 4, - Option4 = 8, - Option5 = 16, - Option6 = 32, - Option7 = 64 - } - - internal sealed class Sealed : Selection - { - public Sealed( - int id, - ObjectType declaringType, - ObjectField field, - IType type, - FieldNode syntaxNode, - string responseName, - ArgumentMap? arguments = null, - long[]? includeConditions = null, - bool isInternal = false, - bool isParallelExecutable = true, - FieldDelegate? resolverPipeline = null, - PureFieldDelegate? pureResolver = null) : base( - id, - declaringType, - field, - type, - syntaxNode, - responseName, - arguments, - includeConditions, - isInternal, - isParallelExecutable, - resolverPipeline, - pureResolver) - { - } - } -} diff --git a/src/HotChocolate/Core/src/Execution/Processing/SelectionVariants.cs b/src/HotChocolate/Core/src/Execution/Processing/SelectionVariants.cs deleted file mode 100644 index 7cc029c1c6c..00000000000 --- a/src/HotChocolate/Core/src/Execution/Processing/SelectionVariants.cs +++ /dev/null @@ -1,194 +0,0 @@ -using HotChocolate.Types; -using static HotChocolate.Execution.Properties.Resources; -using static HotChocolate.Execution.ThrowHelper; - -namespace HotChocolate.Execution.Processing; - -internal sealed class SelectionVariants(int id) : ISelectionVariants -{ - private ObjectType? _firstType; - private SelectionSet? _firstSelectionSet; - private ObjectType? _secondType; - private SelectionSet? _secondSelectionSet; - private Dictionary? _map; - private bool _readOnly; - - /// - public int Id { get; } = id; - - /// - public IOperation DeclaringOperation { get; private set; } = null!; - - public IEnumerable GetPossibleTypes() - => _map?.Keys ?? GetPossibleTypesLazy(); - - public bool IsPossibleType(ObjectType typeContext) - { - if (_map is not null) - { - return _map.ContainsKey(typeContext); - } - - if (ReferenceEquals(_firstType, typeContext)) - { - return true; - } - - if (ReferenceEquals(_secondType, typeContext)) - { - return true; - } - - return false; - } - - private IEnumerable GetPossibleTypesLazy() - { - yield return _firstType!; - - if (_secondType is not null) - { - yield return _secondType; - } - } - - public ISelectionSet GetSelectionSet(ObjectType typeContext) - { - if (_map is not null) - { - if (_map.TryGetValue(typeContext, out var selections)) - { - return selections; - } - else - { - throw SelectionSet_TypeContextInvalid(typeContext); - } - } - - if (ReferenceEquals(_firstType, typeContext)) - { - return _firstSelectionSet!; - } - - if (ReferenceEquals(_secondType, typeContext)) - { - return _secondSelectionSet!; - } - - throw SelectionSet_TypeContextInvalid(typeContext); - } - - internal bool ContainsSelectionSet(ObjectType typeContext) - { - if (_map is not null) - { - return _map.ContainsKey(typeContext); - } - - if (ReferenceEquals(_firstType, typeContext)) - { - return true; - } - - if (ReferenceEquals(_secondType, typeContext)) - { - return true; - } - - return false; - } - - internal void AddSelectionSet( - int id, - ObjectType typeContext, - Selection[] selections, - Fragment[]? fragments, - bool isConditional) - { - if (_readOnly) - { - throw new NotSupportedException(SelectionVariants_ReadOnly); - } - - var selectionSet = new SelectionSet(id, selections, fragments, isConditional); - - if (_map is not null) - { - _map[typeContext] = selectionSet; - } - else - { - if (_firstType is null) - { - _firstType = typeContext; - _firstSelectionSet = selectionSet; - } - else if (_secondType is null) - { - if (typeContext == _firstType) - { - throw SelectionSet_TypeAlreadyAdded(typeContext); - } - - _secondType = typeContext; - _secondSelectionSet = selectionSet; - } - else - { - _map = new Dictionary - { - { _firstType, _firstSelectionSet! }, - { _secondType, _secondSelectionSet! }, - { typeContext, selectionSet } - }; - - _firstType = null; - _firstSelectionSet = null; - _secondType = null; - _secondSelectionSet = null; - } - } - } - - /// - /// Completes the selection variant without sealing it. - /// - internal void Complete(IOperation declaringOperation) - { - if (!_readOnly) - { - DeclaringOperation = declaringOperation; - _firstSelectionSet?.Complete(declaringOperation); - _secondSelectionSet?.Complete(declaringOperation); - - if (_map is not null) - { - foreach (var selectionSet in _map.Values) - { - selectionSet.Complete(declaringOperation); - } - } - } - } - - internal void Seal(IOperation declaringOperation) - { - if (!_readOnly) - { - DeclaringOperation = declaringOperation; - _firstSelectionSet?.Seal(declaringOperation); - _secondSelectionSet?.Seal(declaringOperation); - - if (_map is not null) - { - foreach (var selectionSet in _map.Values) - { - selectionSet.Seal(declaringOperation); - } - } - - _readOnly = true; - } - } -} diff --git a/src/HotChocolate/Core/src/Fetching/HotChocolate.Fetching.csproj b/src/HotChocolate/Core/src/Fetching/HotChocolate.Fetching.csproj deleted file mode 100644 index 27229d59adc..00000000000 --- a/src/HotChocolate/Core/src/Fetching/HotChocolate.Fetching.csproj +++ /dev/null @@ -1,40 +0,0 @@ - - - - HotChocolate.Fetching - HotChocolate.Fetching - HotChocolate.Fetching - HC8001;$(NoWarn) - - - - - - - - - - - - - - ResXFileCodeGenerator - FetchingResources.Designer.cs - - - - - - True - True - FetchingResources.resx - - - BatchDispatcher.cs - - - BatchDispatcher.cs - - - - diff --git a/src/HotChocolate/Core/src/Types.Errors/HotChocolate.Types.Errors.csproj b/src/HotChocolate/Core/src/Types.Errors/HotChocolate.Types.Errors.csproj index 04de55ffd98..4bdc4169e1f 100644 --- a/src/HotChocolate/Core/src/Types.Errors/HotChocolate.Types.Errors.csproj +++ b/src/HotChocolate/Core/src/Types.Errors/HotChocolate.Types.Errors.csproj @@ -28,7 +28,7 @@ - + diff --git a/src/HotChocolate/Core/src/Types.Json/HotChocolate.Types.Json.csproj b/src/HotChocolate/Core/src/Types.Json/HotChocolate.Types.Json.csproj index a2ff17d8626..cafc753595e 100644 --- a/src/HotChocolate/Core/src/Types.Json/HotChocolate.Types.Json.csproj +++ b/src/HotChocolate/Core/src/Types.Json/HotChocolate.Types.Json.csproj @@ -13,7 +13,6 @@ - diff --git a/src/HotChocolate/Core/src/Types.Mutations/HotChocolate.Types.Mutations.csproj b/src/HotChocolate/Core/src/Types.Mutations/HotChocolate.Types.Mutations.csproj index 8faf45d5e11..bb59ec657e4 100644 --- a/src/HotChocolate/Core/src/Types.Mutations/HotChocolate.Types.Mutations.csproj +++ b/src/HotChocolate/Core/src/Types.Mutations/HotChocolate.Types.Mutations.csproj @@ -24,7 +24,7 @@ - + diff --git a/src/HotChocolate/Core/src/Types.OffsetPagination/Extensions/OffsetPaginationResolverContextExtensions.cs b/src/HotChocolate/Core/src/Types.OffsetPagination/Extensions/OffsetPaginationResolverContextExtensions.cs index e29fbba5915..63f5829a6a9 100644 --- a/src/HotChocolate/Core/src/Types.OffsetPagination/Extensions/OffsetPaginationResolverContextExtensions.cs +++ b/src/HotChocolate/Core/src/Types.OffsetPagination/Extensions/OffsetPaginationResolverContextExtensions.cs @@ -6,10 +6,11 @@ namespace HotChocolate.Types.Pagination; internal static class OffsetPaginationResolverContextExtensions { /// + /// /// TotalCount is one of the heaviest operations. It is only necessary to load totalCount /// when it is enabled (IncludeTotalCount) and when it is contained in the selection set. - /// - /// This method checks if the total count is selected + /// + /// This method checks if the total count is selected /// /// /// @@ -17,10 +18,14 @@ public static bool IsTotalCountSelected(this IResolverContext context) { // TotalCount is one of the heaviest operations. It is only necessary to load totalCount // when it is enabled (IncludeTotalCount) and when it is contained in the selection set. - if (context.Selection.Type is ObjectType objectType - && context.Selection.SyntaxNode.SelectionSet is not null) + if (context.Selection is { Type: ObjectType objectType, IsLeaf: false }) { - var selections = context.GetSelections(objectType, null, true); + var selectionSet = context.Selection.DeclaringOperation.GetSelectionSet(context.Selection, objectType); + + foreach (var selection in selectionSet.Selections) + { )) + + } for (var i = 0; i < selections.Count; i++) { diff --git a/src/HotChocolate/Core/src/Types.OffsetPagination/HotChocolate.Types.OffsetPagination.csproj b/src/HotChocolate/Core/src/Types.OffsetPagination/HotChocolate.Types.OffsetPagination.csproj index 3561ed6ead9..489e7d6cf91 100644 --- a/src/HotChocolate/Core/src/Types.OffsetPagination/HotChocolate.Types.OffsetPagination.csproj +++ b/src/HotChocolate/Core/src/Types.OffsetPagination/HotChocolate.Types.OffsetPagination.csproj @@ -13,7 +13,6 @@ - diff --git a/src/HotChocolate/Core/src/Types.Queries/HotChocolate.Types.Queries.csproj b/src/HotChocolate/Core/src/Types.Queries/HotChocolate.Types.Queries.csproj index 1a42342fada..2ec4deeeec7 100644 --- a/src/HotChocolate/Core/src/Types.Queries/HotChocolate.Types.Queries.csproj +++ b/src/HotChocolate/Core/src/Types.Queries/HotChocolate.Types.Queries.csproj @@ -25,7 +25,7 @@ - + diff --git a/src/HotChocolate/Core/src/Types.Shared/WellKnownDirectives.cs b/src/HotChocolate/Core/src/Types.Shared/WellKnownDirectives.cs deleted file mode 100644 index 62a1fecea18..00000000000 --- a/src/HotChocolate/Core/src/Types.Shared/WellKnownDirectives.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace HotChocolate.Utilities.Introspection; - -internal static class WellKnownDirectives -{ - public const string Skip = "skip"; - public const string Include = "include"; - public const string Defer = "defer"; - public const string Stream = "stream"; - public const string Deprecated = "deprecated"; - public const string SpecifiedBy = "specifiedBy"; - public const string DeprecationReasonArgument = "reason"; - public const string OneOf = "oneOf"; -} diff --git a/src/HotChocolate/Core/src/Types.Shared/WellKnownTypes.cs b/src/HotChocolate/Core/src/Types.Shared/WellKnownTypes.cs deleted file mode 100644 index 3749d2bfa4f..00000000000 --- a/src/HotChocolate/Core/src/Types.Shared/WellKnownTypes.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace HotChocolate.Utilities.Introspection; - -internal static class WellKnownTypes -{ -#pragma warning disable IDE1006, InconsistentNaming - public const string __Directive = "__Directive"; - public const string __DirectiveLocation = "__DirectiveLocation"; - public const string __EnumValue = "__EnumValue"; - public const string __Field = "__Field"; - public const string __InputValue = "__InputValue"; - public const string __Schema = "__Schema"; - public const string __Type = "__Type"; - public const string __TypeKind = "__TypeKind"; -#pragma warning restore IDE1006, InconsistentNaming - public const string String = "String"; - public const string Boolean = "Boolean"; - public const string Float = "Float"; - public const string ID = "ID"; - public const string Int = "Int"; -} diff --git a/src/HotChocolate/Core/src/Execution/Caching/DefaultDocumentCache.cs b/src/HotChocolate/Core/src/Types/Execution/Caching/DefaultDocumentCache.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Caching/DefaultDocumentCache.cs rename to src/HotChocolate/Core/src/Types/Execution/Caching/DefaultDocumentCache.cs diff --git a/src/HotChocolate/Core/src/Execution/Caching/DefaultPreparedOperationCache.cs b/src/HotChocolate/Core/src/Types/Execution/Caching/DefaultPreparedOperationCache.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Caching/DefaultPreparedOperationCache.cs rename to src/HotChocolate/Core/src/Types/Execution/Caching/DefaultPreparedOperationCache.cs diff --git a/src/HotChocolate/Core/src/Execution/Caching/IPreparedOperationCache.cs b/src/HotChocolate/Core/src/Types/Execution/Caching/IPreparedOperationCache.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Caching/IPreparedOperationCache.cs rename to src/HotChocolate/Core/src/Types/Execution/Caching/IPreparedOperationCache.cs diff --git a/src/HotChocolate/Core/src/Execution/Configuration/ConfigurationContext.cs b/src/HotChocolate/Core/src/Types/Execution/Configuration/ConfigurationContext.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Configuration/ConfigurationContext.cs rename to src/HotChocolate/Core/src/Types/Execution/Configuration/ConfigurationContext.cs diff --git a/src/HotChocolate/Core/src/Execution/Configuration/ConfigureRequestExecutorSetup.cs b/src/HotChocolate/Core/src/Types/Execution/Configuration/ConfigureRequestExecutorSetup.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Configuration/ConfigureRequestExecutorSetup.cs rename to src/HotChocolate/Core/src/Types/Execution/Configuration/ConfigureRequestExecutorSetup.cs diff --git a/src/HotChocolate/Core/src/Execution/Configuration/DefaultRequestExecutorBuilder.cs b/src/HotChocolate/Core/src/Types/Execution/Configuration/DefaultRequestExecutorBuilder.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Configuration/DefaultRequestExecutorBuilder.cs rename to src/HotChocolate/Core/src/Types/Execution/Configuration/DefaultRequestExecutorBuilder.cs diff --git a/src/HotChocolate/Core/src/Execution/Configuration/IConfigureRequestExecutorSetup.cs b/src/HotChocolate/Core/src/Types/Execution/Configuration/IConfigureRequestExecutorSetup.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Configuration/IConfigureRequestExecutorSetup.cs rename to src/HotChocolate/Core/src/Types/Execution/Configuration/IConfigureRequestExecutorSetup.cs diff --git a/src/HotChocolate/Core/src/Execution/Configuration/ITypeModule.cs b/src/HotChocolate/Core/src/Types/Execution/Configuration/ITypeModule.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Configuration/ITypeModule.cs rename to src/HotChocolate/Core/src/Types/Execution/Configuration/ITypeModule.cs diff --git a/src/HotChocolate/Core/src/Execution/Configuration/OnConfigureRequestExecutorOptionsAction.cs b/src/HotChocolate/Core/src/Types/Execution/Configuration/OnConfigureRequestExecutorOptionsAction.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Configuration/OnConfigureRequestExecutorOptionsAction.cs rename to src/HotChocolate/Core/src/Types/Execution/Configuration/OnConfigureRequestExecutorOptionsAction.cs diff --git a/src/HotChocolate/Core/src/Execution/Configuration/OnConfigureSchemaBuilderAction.cs b/src/HotChocolate/Core/src/Types/Execution/Configuration/OnConfigureSchemaBuilderAction.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Configuration/OnConfigureSchemaBuilderAction.cs rename to src/HotChocolate/Core/src/Types/Execution/Configuration/OnConfigureSchemaBuilderAction.cs diff --git a/src/HotChocolate/Core/src/Execution/Configuration/OnConfigureSchemaServices.cs b/src/HotChocolate/Core/src/Types/Execution/Configuration/OnConfigureSchemaServices.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Configuration/OnConfigureSchemaServices.cs rename to src/HotChocolate/Core/src/Types/Execution/Configuration/OnConfigureSchemaServices.cs diff --git a/src/HotChocolate/Core/src/Execution/Configuration/OnRequestExecutorCreatedAction.cs b/src/HotChocolate/Core/src/Types/Execution/Configuration/OnRequestExecutorCreatedAction.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Configuration/OnRequestExecutorCreatedAction.cs rename to src/HotChocolate/Core/src/Types/Execution/Configuration/OnRequestExecutorCreatedAction.cs diff --git a/src/HotChocolate/Core/src/Execution/Configuration/OnRequestExecutorEvictedAction.cs b/src/HotChocolate/Core/src/Types/Execution/Configuration/OnRequestExecutorEvictedAction.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Configuration/OnRequestExecutorEvictedAction.cs rename to src/HotChocolate/Core/src/Types/Execution/Configuration/OnRequestExecutorEvictedAction.cs diff --git a/src/HotChocolate/Core/src/Execution/Configuration/RequestExecutorSetup.cs b/src/HotChocolate/Core/src/Types/Execution/Configuration/RequestExecutorSetup.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Configuration/RequestExecutorSetup.cs rename to src/HotChocolate/Core/src/Types/Execution/Configuration/RequestExecutorSetup.cs diff --git a/src/HotChocolate/Core/src/Execution/Configuration/RootServiceProviderAccessor.cs b/src/HotChocolate/Core/src/Types/Execution/Configuration/RootServiceProviderAccessor.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Configuration/RootServiceProviderAccessor.cs rename to src/HotChocolate/Core/src/Types/Execution/Configuration/RootServiceProviderAccessor.cs diff --git a/src/HotChocolate/Core/src/Execution/DefaultRequestContextAccessor.cs b/src/HotChocolate/Core/src/Types/Execution/DefaultRequestContextAccessor.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DefaultRequestContextAccessor.cs rename to src/HotChocolate/Core/src/Types/Execution/DefaultRequestContextAccessor.cs diff --git a/src/HotChocolate/Core/src/Execution/DefaultRequestExecutor.cs b/src/HotChocolate/Core/src/Types/Execution/DefaultRequestExecutor.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DefaultRequestExecutor.cs rename to src/HotChocolate/Core/src/Types/Execution/DefaultRequestExecutor.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/Factories/IFactory.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/Factories/IFactory.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/Factories/IFactory.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/Factories/IFactory.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/Factories/OperationContextFactory.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/Factories/OperationContextFactory.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/Factories/OperationContextFactory.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/Factories/OperationContextFactory.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/Factories/OperationContextOwnerFactory.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/Factories/OperationContextOwnerFactory.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/Factories/OperationContextOwnerFactory.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/Factories/OperationContextOwnerFactory.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/Factories/PooledServiceFactory.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/Factories/PooledServiceFactory.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/Factories/PooledServiceFactory.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/Factories/PooledServiceFactory.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/InternalSchemaServiceCollectionExtensions.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/InternalSchemaServiceCollectionExtensions.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/InternalSchemaServiceCollectionExtensions.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/InternalSchemaServiceCollectionExtensions.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/InternalServiceCollectionExtensions.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/InternalServiceCollectionExtensions.cs similarity index 90% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/InternalServiceCollectionExtensions.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/InternalServiceCollectionExtensions.cs index 62551e42713..6a1afa07ec7 100644 --- a/src/HotChocolate/Core/src/Execution/DependencyInjection/InternalServiceCollectionExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/InternalServiceCollectionExtensions.cs @@ -97,22 +97,6 @@ internal static IServiceCollection TryAddOperationContextPool( return services; } - internal static IServiceCollection TryAddDeferredWorkStatePool( - this IServiceCollection services) - { - services.TryAddSingleton( - sp => - { - var provider = sp.GetRequiredService(); - var policy = new DeferredWorkStatePooledObjectPolicy(); - return provider.Create(policy); - }); - - services.TryAddScoped, DeferredWorkStateOwnerFactory>(); - - return services; - } - internal static IServiceCollection TryAddTypeConverter( this IServiceCollection services) { @@ -233,15 +217,4 @@ public override bool Return(OperationContext obj) return false; } } - - private sealed class DeferredWorkStatePooledObjectPolicy : PooledObjectPolicy - { - public override DeferredWorkState Create() => new(); - - public override bool Return(DeferredWorkState obj) - { - obj.Reset(); - return true; - } - } } diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Caches.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Caches.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Caches.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Caches.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Composite.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Composite.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Composite.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Composite.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.DataLoader.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.DataLoader.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.DataLoader.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.DataLoader.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.ErrorFilter.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.ErrorFilter.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.ErrorFilter.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.ErrorFilter.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Hashing.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Hashing.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Hashing.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Hashing.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.IdSerializer.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.IdSerializer.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.IdSerializer.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.IdSerializer.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.InputParser.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.InputParser.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.InputParser.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.InputParser.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Instrumentation.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Instrumentation.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Instrumentation.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Instrumentation.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Optimizer.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Optimizer.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Optimizer.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Optimizer.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Services.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Services.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Services.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Services.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.TransactionScope.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.TransactionScope.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.TransactionScope.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.TransactionScope.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.TypeConversion.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.TypeConversion.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.TypeConversion.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.TypeConversion.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.TypeDiscovery.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.TypeDiscovery.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.TypeDiscovery.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.TypeDiscovery.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.UseRequest.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.UseRequest.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.UseRequest.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.UseRequest.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Validation.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Validation.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Validation.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Validation.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorBuilderExtensions.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorServiceCollectionExtensions.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorServiceCollectionExtensions.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorServiceCollectionExtensions.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorServiceCollectionExtensions.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorServiceProviderExtensions.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorServiceProviderExtensions.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorServiceProviderExtensions.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/RequestExecutorServiceProviderExtensions.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Convention.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Convention.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Convention.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Convention.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Document.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Document.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Document.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Document.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Middleware.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Middleware.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Middleware.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Middleware.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Paging.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Paging.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Paging.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Paging.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Relay.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Relay.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Relay.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Relay.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Resolvers.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Resolvers.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Resolvers.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Resolvers.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.TypeInterceptor.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.TypeInterceptor.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.TypeInterceptor.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.TypeInterceptor.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.TypeModules.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.TypeModules.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.TypeModules.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.TypeModules.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Types.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Types.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Types.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.Types.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.cs b/src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.cs rename to src/HotChocolate/Core/src/Types/Execution/DependencyInjection/SchemaRequestExecutorBuilderExtensions.cs diff --git a/src/HotChocolate/Core/src/Execution/ErrorHelper.cs b/src/HotChocolate/Core/src/Types/Execution/ErrorHelper.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/ErrorHelper.cs rename to src/HotChocolate/Core/src/Types/Execution/ErrorHelper.cs diff --git a/src/HotChocolate/Core/src/Execution/Errors/FuncErrorFilterWrapper.cs b/src/HotChocolate/Core/src/Types/Execution/Errors/FuncErrorFilterWrapper.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Errors/FuncErrorFilterWrapper.cs rename to src/HotChocolate/Core/src/Types/Execution/Errors/FuncErrorFilterWrapper.cs diff --git a/src/HotChocolate/Core/src/Execution/Extensions/ExecutionObjectFieldDescriptorExtensions.cs b/src/HotChocolate/Core/src/Types/Execution/Extensions/ExecutionObjectFieldDescriptorExtensions.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Extensions/ExecutionObjectFieldDescriptorExtensions.cs rename to src/HotChocolate/Core/src/Types/Execution/Extensions/ExecutionObjectFieldDescriptorExtensions.cs diff --git a/src/HotChocolate/Core/src/Execution/Extensions/ExecutionRequestExecutorExtensions.cs b/src/HotChocolate/Core/src/Types/Execution/Extensions/ExecutionRequestExecutorExtensions.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Extensions/ExecutionRequestExecutorExtensions.cs rename to src/HotChocolate/Core/src/Types/Execution/Extensions/ExecutionRequestExecutorExtensions.cs diff --git a/src/HotChocolate/Core/src/Execution/Extensions/ExecutionResultExtensions.cs b/src/HotChocolate/Core/src/Types/Execution/Extensions/ExecutionResultExtensions.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Extensions/ExecutionResultExtensions.cs rename to src/HotChocolate/Core/src/Types/Execution/Extensions/ExecutionResultExtensions.cs diff --git a/src/HotChocolate/Core/src/Execution/Extensions/ExecutionSchemaExtensions.cs b/src/HotChocolate/Core/src/Types/Execution/Extensions/ExecutionSchemaExtensions.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Extensions/ExecutionSchemaExtensions.cs rename to src/HotChocolate/Core/src/Types/Execution/Extensions/ExecutionSchemaExtensions.cs diff --git a/src/HotChocolate/Core/src/Execution/Extensions/HotChocolateExecutionRequestContextExtensions.cs b/src/HotChocolate/Core/src/Types/Execution/Extensions/HotChocolateExecutionRequestContextExtensions.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Extensions/HotChocolateExecutionRequestContextExtensions.cs rename to src/HotChocolate/Core/src/Types/Execution/Extensions/HotChocolateExecutionRequestContextExtensions.cs diff --git a/src/HotChocolate/Core/src/Execution/Extensions/OperationContextExtensions.cs b/src/HotChocolate/Core/src/Types/Execution/Extensions/OperationContextExtensions.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Extensions/OperationContextExtensions.cs rename to src/HotChocolate/Core/src/Types/Execution/Extensions/OperationContextExtensions.cs diff --git a/src/HotChocolate/Core/src/Execution/IRequestContextAccessor.cs b/src/HotChocolate/Core/src/Types/Execution/IRequestContextAccessor.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/IRequestContextAccessor.cs rename to src/HotChocolate/Core/src/Types/Execution/IRequestContextAccessor.cs diff --git a/src/HotChocolate/Core/src/Execution/IRequestContextEnricher.cs b/src/HotChocolate/Core/src/Types/Execution/IRequestContextEnricher.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/IRequestContextEnricher.cs rename to src/HotChocolate/Core/src/Types/Execution/IRequestContextEnricher.cs diff --git a/src/HotChocolate/Core/src/Execution/Instrumentation/AggregateExecutionDiagnosticEvents.cs b/src/HotChocolate/Core/src/Types/Execution/Instrumentation/AggregateExecutionDiagnosticEvents.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Instrumentation/AggregateExecutionDiagnosticEvents.cs rename to src/HotChocolate/Core/src/Types/Execution/Instrumentation/AggregateExecutionDiagnosticEvents.cs diff --git a/src/HotChocolate/Core/src/Execution/Instrumentation/ExecutionDiagnosticEventListener.cs b/src/HotChocolate/Core/src/Types/Execution/Instrumentation/ExecutionDiagnosticEventListener.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Instrumentation/ExecutionDiagnosticEventListener.cs rename to src/HotChocolate/Core/src/Types/Execution/Instrumentation/ExecutionDiagnosticEventListener.cs diff --git a/src/HotChocolate/Core/src/Execution/Instrumentation/IExecutionDiagnosticEventListener.cs b/src/HotChocolate/Core/src/Types/Execution/Instrumentation/IExecutionDiagnosticEventListener.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Instrumentation/IExecutionDiagnosticEventListener.cs rename to src/HotChocolate/Core/src/Types/Execution/Instrumentation/IExecutionDiagnosticEventListener.cs diff --git a/src/HotChocolate/Core/src/Execution/Instrumentation/IExecutionDiagnosticEvents.cs b/src/HotChocolate/Core/src/Types/Execution/Instrumentation/IExecutionDiagnosticEvents.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Instrumentation/IExecutionDiagnosticEvents.cs rename to src/HotChocolate/Core/src/Types/Execution/Instrumentation/IExecutionDiagnosticEvents.cs diff --git a/src/HotChocolate/Core/src/Execution/Instrumentation/NoopExecutionDiagnosticEvents.cs b/src/HotChocolate/Core/src/Types/Execution/Instrumentation/NoopExecutionDiagnosticEvents.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Instrumentation/NoopExecutionDiagnosticEvents.cs rename to src/HotChocolate/Core/src/Types/Execution/Instrumentation/NoopExecutionDiagnosticEvents.cs diff --git a/src/HotChocolate/Core/src/Execution/Internal/ArgumentCoercionHelper.cs b/src/HotChocolate/Core/src/Types/Execution/Internal/ArgumentCoercionHelper.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Internal/ArgumentCoercionHelper.cs rename to src/HotChocolate/Core/src/Types/Execution/Internal/ArgumentCoercionHelper.cs diff --git a/src/HotChocolate/Core/src/Execution/Internal/MiddlewareContextMarshal.cs b/src/HotChocolate/Core/src/Types/Execution/Internal/MiddlewareContextMarshal.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Internal/MiddlewareContextMarshal.cs rename to src/HotChocolate/Core/src/Types/Execution/Internal/MiddlewareContextMarshal.cs diff --git a/src/HotChocolate/Core/src/Execution/Internal/SchemaFileExporter.cs b/src/HotChocolate/Core/src/Types/Execution/Internal/SchemaFileExporter.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Internal/SchemaFileExporter.cs rename to src/HotChocolate/Core/src/Types/Execution/Internal/SchemaFileExporter.cs diff --git a/src/HotChocolate/Core/src/Execution/NeedsFormatting.cs b/src/HotChocolate/Core/src/Types/Execution/NeedsFormatting.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/NeedsFormatting.cs rename to src/HotChocolate/Core/src/Types/Execution/NeedsFormatting.cs diff --git a/src/HotChocolate/Core/src/Execution/Options/IErrorHandlerOptionsAccessor.cs b/src/HotChocolate/Core/src/Types/Execution/Options/IErrorHandlerOptionsAccessor.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Options/IErrorHandlerOptionsAccessor.cs rename to src/HotChocolate/Core/src/Types/Execution/Options/IErrorHandlerOptionsAccessor.cs diff --git a/src/HotChocolate/Core/src/Execution/Options/IPersistedOperationOptionsAccessor.cs b/src/HotChocolate/Core/src/Types/Execution/Options/IPersistedOperationOptionsAccessor.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Options/IPersistedOperationOptionsAccessor.cs rename to src/HotChocolate/Core/src/Types/Execution/Options/IPersistedOperationOptionsAccessor.cs diff --git a/src/HotChocolate/Core/src/Execution/Options/IRequestExecutorOptionsAccessor.cs b/src/HotChocolate/Core/src/Types/Execution/Options/IRequestExecutorOptionsAccessor.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Options/IRequestExecutorOptionsAccessor.cs rename to src/HotChocolate/Core/src/Types/Execution/Options/IRequestExecutorOptionsAccessor.cs diff --git a/src/HotChocolate/Core/src/Execution/Options/IRequestTimeoutOptionsAccessor.cs b/src/HotChocolate/Core/src/Types/Execution/Options/IRequestTimeoutOptionsAccessor.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Options/IRequestTimeoutOptionsAccessor.cs rename to src/HotChocolate/Core/src/Types/Execution/Options/IRequestTimeoutOptionsAccessor.cs diff --git a/src/HotChocolate/Core/src/Execution/Options/NodeIdSerializerOptions.cs b/src/HotChocolate/Core/src/Types/Execution/Options/NodeIdSerializerOptions.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Options/NodeIdSerializerOptions.cs rename to src/HotChocolate/Core/src/Types/Execution/Options/NodeIdSerializerOptions.cs diff --git a/src/HotChocolate/Core/src/Execution/Options/RequestExecutorOptions.cs b/src/HotChocolate/Core/src/Types/Execution/Options/RequestExecutorOptions.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Options/RequestExecutorOptions.cs rename to src/HotChocolate/Core/src/Types/Execution/Options/RequestExecutorOptions.cs diff --git a/src/HotChocolate/Core/src/Execution/Options/RequestParserOptions.cs b/src/HotChocolate/Core/src/Types/Execution/Options/RequestParserOptions.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Options/RequestParserOptions.cs rename to src/HotChocolate/Core/src/Types/Execution/Options/RequestParserOptions.cs diff --git a/src/HotChocolate/Core/src/Execution/Options/ResultBufferOptions.cs b/src/HotChocolate/Core/src/Types/Execution/Options/ResultBufferOptions.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Options/ResultBufferOptions.cs rename to src/HotChocolate/Core/src/Types/Execution/Options/ResultBufferOptions.cs diff --git a/src/HotChocolate/Core/src/Execution/Options/TracingPreference.cs b/src/HotChocolate/Core/src/Types/Execution/Options/TracingPreference.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Options/TracingPreference.cs rename to src/HotChocolate/Core/src/Types/Execution/Options/TracingPreference.cs diff --git a/src/HotChocolate/Core/src/Execution/Pipeline/OperationCacheMiddleware.cs b/src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationCacheMiddleware.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Pipeline/OperationCacheMiddleware.cs rename to src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationCacheMiddleware.cs diff --git a/src/HotChocolate/Core/src/Execution/Pipeline/OperationExecutionMiddleware.cs b/src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationExecutionMiddleware.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Pipeline/OperationExecutionMiddleware.cs rename to src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationExecutionMiddleware.cs diff --git a/src/HotChocolate/Core/src/Execution/Pipeline/OperationInfo.cs b/src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationInfo.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Pipeline/OperationInfo.cs rename to src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationInfo.cs diff --git a/src/HotChocolate/Core/src/Execution/Pipeline/OperationResolverMiddleware.cs b/src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationResolverMiddleware.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Pipeline/OperationResolverMiddleware.cs rename to src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationResolverMiddleware.cs diff --git a/src/HotChocolate/Core/src/Execution/Pipeline/OperationVariableCoercionMiddleware.cs b/src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationVariableCoercionMiddleware.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Pipeline/OperationVariableCoercionMiddleware.cs rename to src/HotChocolate/Core/src/Types/Execution/Pipeline/OperationVariableCoercionMiddleware.cs diff --git a/src/HotChocolate/Core/src/Execution/Pipeline/PipelineTools.cs b/src/HotChocolate/Core/src/Types/Execution/Pipeline/PipelineTools.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Pipeline/PipelineTools.cs rename to src/HotChocolate/Core/src/Types/Execution/Pipeline/PipelineTools.cs diff --git a/src/HotChocolate/Core/src/Execution/Pipeline/Pipelines.md b/src/HotChocolate/Core/src/Types/Execution/Pipeline/Pipelines.md similarity index 100% rename from src/HotChocolate/Core/src/Execution/Pipeline/Pipelines.md rename to src/HotChocolate/Core/src/Types/Execution/Pipeline/Pipelines.md diff --git a/src/HotChocolate/Core/src/Execution/Pipeline/RequestClassMiddlewareFactory.cs b/src/HotChocolate/Core/src/Types/Execution/Pipeline/RequestClassMiddlewareFactory.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Pipeline/RequestClassMiddlewareFactory.cs rename to src/HotChocolate/Core/src/Types/Execution/Pipeline/RequestClassMiddlewareFactory.cs diff --git a/src/HotChocolate/Core/src/Execution/Pipeline/TimeoutMiddleware.cs b/src/HotChocolate/Core/src/Types/Execution/Pipeline/TimeoutMiddleware.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Pipeline/TimeoutMiddleware.cs rename to src/HotChocolate/Core/src/Types/Execution/Pipeline/TimeoutMiddleware.cs diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/ArgumentMap.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ArgumentMap.cs index e48ca10171d..354e3bdfd73 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/ArgumentMap.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/ArgumentMap.cs @@ -1,24 +1,25 @@ using System.Collections; +using System.Collections.Frozen; +using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; using HotChocolate.Resolvers; namespace HotChocolate.Execution.Processing; /// -/// Represents the map of argument values that can be accessed on the . +/// Represents a read-only map of argument values for a field selection in a GraphQL query. +/// This map provides efficient access to coerced argument values and tracks coercion errors. /// -public sealed class ArgumentMap - : IReadOnlyDictionary - , IEnumerable +public sealed class ArgumentMap : IReadOnlyDictionary { - private readonly Dictionary _arguments; - private readonly bool _isFinal; + private readonly FrozenDictionary _arguments; private readonly bool _hasErrors; internal ArgumentMap(Dictionary arguments) { - _arguments = arguments; - _isFinal = true; + _arguments = arguments.ToFrozenDictionary(StringComparer.Ordinal); + IsFullyCoercedNoErrors = true; if (_arguments.Count > 0) { @@ -26,7 +27,7 @@ internal ArgumentMap(Dictionary arguments) { if (!argument.IsFullyCoerced) { - _isFinal = false; + IsFullyCoercedNoErrors = false; } if (argument.HasError) @@ -38,49 +39,79 @@ internal ArgumentMap(Dictionary arguments) } /// - /// Gets an empty argument map. + /// Gets an empty argument map with no arguments. /// public static ArgumentMap Empty { get; } = new([]); /// - /// This indexer allows to access the - /// by the argument . + /// Gets the for the specified argument name. /// /// /// The argument name. /// + /// + /// The associated with the specified name. + /// + /// + /// Thrown when the specified is not found. + /// public ArgumentValue this[string name] => _arguments[name]; /// - /// Specifies if the argument map is fully coerced and has no errors. + /// Gets a value indicating whether all arguments in this map are + /// fully coerced and no errors occurred during coercion. /// - public bool IsFullyCoercedNoErrors => _isFinal && !_hasErrors; + /// + /// true if all arguments are fully coerced without errors; otherwise, false. + /// + public bool IsFullyCoercedNoErrors => field && !_hasErrors; /// - /// Specifies if this argument map has errors. + /// Gets a value indicating whether any argument in this map has coercion errors. /// + /// + /// true if at least one argument has errors; otherwise, false. + /// public bool HasErrors => _hasErrors; /// - /// The argument count. + /// Gets the number of arguments in this map. /// + /// + /// The total count of arguments. + /// public int Count => _arguments.Count; + /// + /// Gets an immutable array containing all argument names in this map. + /// + /// + /// An of argument names. + /// + public ImmutableArray ArgumentNames => _arguments.Keys; + IEnumerable IReadOnlyDictionary.Keys => _arguments.Keys; + /// + /// Gets an immutable array containing all argument values in this map. + /// + /// + /// An of instances. + /// + public ImmutableArray Values => _arguments.Values; + IEnumerable IReadOnlyDictionary.Values => _arguments.Values; /// - /// This method allows to check if an argument value with the specified - /// argument exists. + /// Determines whether this map contains an argument with the specified name. /// /// - /// The argument name. + /// The argument name to check. /// /// - /// true if the argument exists; otherwise, false. + /// true if an argument with the specified exists; otherwise, false. /// public bool ContainsName(string name) => _arguments.ContainsKey(name); @@ -88,17 +119,17 @@ bool IReadOnlyDictionary.ContainsKey(string key) => ContainsName(key); /// - /// Tries to get an by its . + /// Attempts to retrieve the associated with the specified argument name. /// /// - /// The argument name. + /// The argument name to locate. /// /// - /// The argument value. + /// When this method returns, contains the associated with the specified + /// , if found; otherwise, null. /// /// - /// true if an argument value with the specified - /// was retrieved; otherwise, false. + /// true if an argument with the specified was found; otherwise, false. /// public bool TryGetValue(string name, [NotNullWhen(true)] out ArgumentValue? value) => _arguments.TryGetValue(name, out value); @@ -108,14 +139,8 @@ bool IReadOnlyDictionary.TryGetValue( out ArgumentValue value) => TryGetValue(key, out value!); - /// - /// Gets an enumerator for the argument values. - /// - public IEnumerator GetEnumerator() - => _arguments.Values.GetEnumerator(); - - IEnumerator> - IEnumerable>.GetEnumerator() + /// + public IEnumerator> GetEnumerator() => _arguments.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() diff --git a/src/HotChocolate/Core/src/Execution/Processing/ArgumentNonNullValidator.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ArgumentNonNullValidator.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/ArgumentNonNullValidator.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/ArgumentNonNullValidator.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/AsyncManualResetEvent.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/AsyncManualResetEvent.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/AsyncManualResetEvent.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/AsyncManualResetEvent.cs diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/CreateFieldPipeline.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/CreateFieldPipeline.cs new file mode 100644 index 00000000000..6d2647936b3 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/CreateFieldPipeline.cs @@ -0,0 +1,10 @@ +using HotChocolate.Language; +using HotChocolate.Resolvers; +using HotChocolate.Types; + +namespace HotChocolate.Execution.Processing; + +internal delegate FieldDelegate CreateFieldPipeline( + Schema schema, + ObjectField field, + FieldNode selection); diff --git a/src/HotChocolate/Core/src/Execution/Processing/DefaultActivator.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/DefaultActivator.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/DefaultActivator.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/DefaultActivator.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/DefaultTransactionScope.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/DefaultTransactionScope.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/DefaultTransactionScope.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/DefaultTransactionScope.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/DefaultTransactionScopeHandler.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/DefaultTransactionScopeHandler.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/DefaultTransactionScopeHandler.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/DefaultTransactionScopeHandler.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/EmptySelectionCollection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/EmptySelectionCollection.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/EmptySelectionCollection.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/EmptySelectionCollection.cs diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/IFragment.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/IFragment.cs deleted file mode 100644 index a2947ee1382..00000000000 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/IFragment.cs +++ /dev/null @@ -1,46 +0,0 @@ -using HotChocolate.Language; -using HotChocolate.Types; - -namespace HotChocolate.Execution.Processing; - -/// -/// Represents a deferred fragment. -/// -public interface IFragment : IOptionalSelection -{ - /// - /// Gets the internal fragment identifier. - /// - int Id { get; } - - /// - /// Gets the type condition. - /// - IObjectTypeDefinition TypeCondition { get; } - - /// - /// Gets the syntax node from the original GraphQL request document. - /// - ISyntaxNode SyntaxNode { get; } - - /// - /// Gets the collection of directives that are annotated to this fragment. - /// - IReadOnlyList Directives { get; } - - /// - /// Gets the selection set of this fragment. - /// - ISelectionSet SelectionSet { get; } - - /// - /// Gets the fragment label. - /// - /// - /// The variable values. - /// - /// - /// Returns the fragment label. - /// - string? GetLabel(IVariableValueCollection variables); -} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/IOperation.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/IOperation.cs deleted file mode 100644 index 9ad9097c0c2..00000000000 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/IOperation.cs +++ /dev/null @@ -1,194 +0,0 @@ -using HotChocolate.Language; -using HotChocolate.Types; - -namespace HotChocolate.Execution.Processing; - -/// -/// Represents a compiled GraphQL operation. -/// -public interface IOperation : IHasReadOnlyContextData, IEnumerable -{ - /// - /// Gets the internal unique identifier for this operation. - /// - string Id { get; } - - /// - /// Gets the parsed query document that contains the - /// operation-. - /// - DocumentNode Document { get; } - - /// - /// Gets the syntax node representing the operation definition. - /// - OperationDefinitionNode Definition { get; } - - /// - /// Gets the root type on which the operation is executed. - /// - ObjectType RootType { get; } - - /// - /// Gets the name of the operation. - /// - string? Name { get; } - - /// - /// Gets the operation type (Query, Mutation, Subscription). - /// - OperationType Type { get; } - - /// - /// Gets the prepared root selections for this operation. - /// - /// - /// Returns the prepared root selections for this operation. - /// - ISelectionSet RootSelectionSet { get; } - - /// - /// Gets all selection variants of this operation. - /// - IReadOnlyList SelectionVariants { get; } - - /// - /// Defines if this operation has deferred fragments or streams. - /// - bool HasIncrementalParts { get; } - - /// - /// Gets the schema for which this operation is compiled. - /// - ISchemaDefinition Schema { get; } - - /// - /// Gets the selection set for the specified and - /// . - /// - /// - /// The selection set for which the selection set shall be resolved. - /// - /// - /// The result type context. - /// - /// - /// Returns the selection set for the specified and - /// . - /// - /// - /// The specified has no selection set. - /// - ISelectionSet GetSelectionSet(ISelection selection, ObjectType typeContext); - - /// - /// Gets the possible return types for the . - /// - /// - /// The selection for which the possible result types shall be returned. - /// - /// - /// Returns the possible return types for the specified . - /// - /// - /// The specified has no selection set. - /// - IEnumerable GetPossibleTypes(ISelection selection); - - /// - /// Creates the include flags for the specified variable values. - /// - /// - /// The variable values. - /// - /// - /// Returns the include flags for the specified variable values. - /// - long CreateIncludeFlags(IVariableValueCollection variables); - - bool TryGetState(out TState? state); - - bool TryGetState(string key, out TState? state); - - /// - /// Gets or adds state to this operation. - /// - /// - /// The type of the state. - /// - /// - /// The factory that creates the state if it does not exist. - /// - /// - /// Returns the state. - /// - TState GetOrAddState( - Func createState); - - /// - /// Gets or adds state to this operation. - /// - /// - /// The type of the state. - /// - /// - /// The type of the context. - /// - /// - /// The factory that creates the state if it does not exist. - /// - /// - /// The context that is passed to the factory. - /// - /// - /// Returns the state. - /// - TState GetOrAddState( - Func createState, - TContext context); - - /// - /// Gets or adds state to this operation. - /// - /// - /// The type of the state. - /// - /// - /// The key of the state. - /// - /// - /// The factory that creates the state if it does not exist. - /// - /// - /// Returns the state. - /// - TState GetOrAddState( - string key, - Func createState); - - /// - /// Gets or adds state to this operation. - /// - /// - /// The type of the state. - /// - /// - /// The type of the context. - /// - /// - /// The key of the state. - /// - /// - /// The factory that creates the state if it does not exist. - /// - /// - /// The context that is passed to the factory. - /// - /// - /// Returns the state. - /// - TState GetOrAddState( - string key, - Func createState, - TContext context); -} diff --git a/src/HotChocolate/Core/src/Execution/Processing/IOperationCompilerOptimizer.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/IOperationCompilerOptimizer.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/IOperationCompilerOptimizer.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/IOperationCompilerOptimizer.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/IOperationOptimizer.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/IOperationOptimizer.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/IOperationOptimizer.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/IOperationOptimizer.cs diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/IOptionalSelection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/IOptionalSelection.cs deleted file mode 100644 index 8345292c0d3..00000000000 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/IOptionalSelection.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace HotChocolate.Execution.Processing; - -/// -/// Represents selections with inclusion conditions. -/// -public interface IOptionalSelection -{ - /// - /// Defines that this selection is only needed for internal processing. - /// - bool IsInternal { get; } - - /// - /// Defines that this selection is conditional and will not always be included. - /// - bool IsConditional { get; } - - /// - /// Defines if this selection will be included into the request execution. - /// - /// - /// The execution include flags. - /// - /// - /// Allow internal selections to be included. - /// - /// - /// True, if this selection shall be included into the request execution. - /// - bool IsIncluded(long includeFlags, bool allowInternals = false); -} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/ISelection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ISelection.cs deleted file mode 100644 index d4880429f14..00000000000 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/ISelection.cs +++ /dev/null @@ -1,108 +0,0 @@ -using HotChocolate.Language; -using HotChocolate.Resolvers; -using HotChocolate.Types; - -namespace HotChocolate.Execution.Processing; - -/// -/// Represents a field selection during execution. -/// -public interface ISelection : IOptionalSelection -{ - /// - /// Gets an operation unique identifier of this selection. - /// - int Id { get; } - - /// - /// Gets the name this field will have in the response map. - /// - string ResponseName { get; } - - /// - /// Gets the field that was selected. - /// - ObjectField Field { get; } - - /// - /// Gets the type of the selection. - /// - IType Type { get; } - - /// - /// Gets the type kind of the selection. - /// - TypeKind TypeKind { get; } - - /// - /// Specifies if the return type of this selection is a list type. - /// - bool IsList { get; } - - /// - /// Gets the type that declares the field that is selected by this selection. - /// - ObjectType DeclaringType { get; } - - /// - /// Gets the selectionSet that declares this selection. - /// - ISelectionSet DeclaringSelectionSet { get; } - - /// - /// Gets the operation that declares this selection. - /// - IOperation DeclaringOperation { get; } - - /// - /// Gets the merged field selection syntax node. - /// - FieldNode SyntaxNode { get; } - - /// - /// Gets the field selection syntax node. - /// - IReadOnlyList SyntaxNodes { get; } - - /// - /// If this selection selects a field that returns a composite type - /// then this selection set represents the fields that are selected - /// on that returning composite type. - /// - /// If this selection however selects a leaf field than this - /// selection set will be null. - /// - SelectionSetNode? SelectionSet { get; } - - /// - /// Gets the execution kind. - /// - SelectionExecutionStrategy Strategy { get; } - - /// - /// The compiled resolver pipeline for this selection. - /// - FieldDelegate? ResolverPipeline { get; } - - /// - /// The compiled pure resolver. - /// - PureFieldDelegate? PureResolver { get; } - - /// - /// The arguments that have been pre-coerced for this field selection. - /// - ArgumentMap Arguments { get; } - - /// - /// Defines if this selection is annotated with the stream directive. - /// - /// - /// The execution include flags that determine if the stream directive is applied for the - /// current execution run. - /// - /// - /// Returns if this selection is annotated with the stream directive. - /// - bool HasStreamDirective(long includeFlags); -} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/ISelectionSet.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ISelectionSet.cs deleted file mode 100644 index df45aea7ef5..00000000000 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/ISelectionSet.cs +++ /dev/null @@ -1,34 +0,0 @@ -namespace HotChocolate.Execution.Processing; - -/// -/// A selection set is primarily composed of field selections. -/// When needed a selection set can preserve fragments so that the execution engine -/// can branch the processing of these fragments. -/// -public interface ISelectionSet -{ - /// - /// Gets an operation unique selection-set identifier of this selection. - /// - int Id { get; } - - /// - /// Defines if this list needs post-processing for skip and include. - /// - bool IsConditional { get; } - - /// - /// Gets the selections that shall be executed. - /// - IReadOnlyList Selections { get; } - - /// - /// Gets the deferred fragments if any were preserved for execution. - /// - IReadOnlyList Fragments { get; } - - /// - /// Gets the declaring operation. - /// - IOperation DeclaringOperation { get; } -} diff --git a/src/HotChocolate/Core/src/Execution/Processing/ISelectionSetOptimizer.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ISelectionSetOptimizer.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/ISelectionSetOptimizer.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/ISelectionSetOptimizer.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/ISubscription.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ISubscription.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/ISubscription.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/ISubscription.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/ITaskStatistics.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ITaskStatistics.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/ITaskStatistics.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/ITaskStatistics.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/ITransactionScope.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ITransactionScope.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/ITransactionScope.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/ITransactionScope.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/ITransactionScopeHandler.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ITransactionScopeHandler.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/ITransactionScopeHandler.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/ITransactionScopeHandler.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Arguments.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Arguments.cs similarity index 91% rename from src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Arguments.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Arguments.cs index 92e99932d2b..e69ab937ba7 100644 --- a/src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Arguments.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Arguments.cs @@ -17,7 +17,7 @@ public T ArgumentValue(string name) if (!Arguments.TryGetValue(name, out var argument)) { - throw ResolverContext_ArgumentDoesNotExist(_selection.SyntaxNode, Path, name); + throw ResolverContext_ArgumentDoesNotExist(_selection.SyntaxNodes[0].Node, Path, name); } try @@ -40,7 +40,7 @@ public Optional ArgumentOptional(string name) if (!Arguments.TryGetValue(name, out var argument)) { - throw ResolverContext_ArgumentDoesNotExist(_selection.SyntaxNode, Path, name); + throw ResolverContext_ArgumentDoesNotExist(_selection.SyntaxNodes[0].Node, Path, name); } return argument.IsDefaultValue @@ -54,7 +54,7 @@ public TValueNode ArgumentLiteral(string name) where TValueNode : IV if (!Arguments.TryGetValue(name, out var argument)) { - throw ResolverContext_ArgumentDoesNotExist(_selection.SyntaxNode, Path, name); + throw ResolverContext_ArgumentDoesNotExist(_selection.SyntaxNodes[0].Node, Path, name); } var literal = argument.ValueLiteral!; @@ -65,7 +65,11 @@ public TValueNode ArgumentLiteral(string name) where TValueNode : IV } throw ResolverContext_LiteralNotCompatible( - _selection.SyntaxNode, Path, name, typeof(TValueNode), literal.GetType()); + _selection.SyntaxNodes[0].Node, + Path, + name, + typeof(TValueNode), + literal.GetType()); } public ValueKind ArgumentKind(string name) @@ -74,10 +78,10 @@ public ValueKind ArgumentKind(string name) if (!Arguments.TryGetValue(name, out var argument)) { - throw ResolverContext_ArgumentDoesNotExist(_selection.SyntaxNode, Path, name); + throw ResolverContext_ArgumentDoesNotExist(_selection.SyntaxNodes[0].Node, Path, name); } - // There can only be no kind if there was an error which would have + // There can only be no kind of there was an error which would have // already been raised at this point. return argument.Kind ?? ValueKind.Unknown; } @@ -114,7 +118,7 @@ private T CoerceArgumentValue(ArgumentValue argument) if (typeof(IValueNode).IsAssignableFrom(typeof(T))) { throw ResolverContext_LiteralsNotSupported( - _selection.SyntaxNode, + _selection.SyntaxNodes[0].Node, Path, argument.Name, typeof(T)); @@ -122,7 +126,7 @@ private T CoerceArgumentValue(ArgumentValue argument) // we are unable to convert the argument to the request type. throw ResolverContext_CannotConvertArgument( - _selection.SyntaxNode, + _selection.SyntaxNodes[0].Node, Path, argument.Name, typeof(T), @@ -173,16 +177,14 @@ public ArgumentValue ReplaceArgument(string argumentName, ArgumentValue newArgum Arguments = mutableArguments; } - if (!mutableArguments.TryGetValue(argumentName, out var argumentValue)) + // we remove the original argument name ... + if (!mutableArguments.Remove(argumentName, out var argumentValue)) { throw new ArgumentException( string.Format(MiddlewareContext_ReplaceArgument_InvalidKey, argumentName), nameof(argumentName)); } - // we remove the original argument name ... - mutableArguments.Remove(argumentName); - // and allow the argument to be replaces with a new argument that could also have // a new name. mutableArguments.Add(newArgumentValue.Name, newArgumentValue); diff --git a/src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Global.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Global.cs similarity index 94% rename from src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Global.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Global.cs index 4b665e1c6fe..9211f4d4c0e 100644 --- a/src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Global.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Global.cs @@ -2,7 +2,6 @@ using HotChocolate.Language; using HotChocolate.Resolvers; using HotChocolate.Types; -using Microsoft.Extensions.DependencyInjection; namespace HotChocolate.Execution.Processing; @@ -26,7 +25,7 @@ public IServiceProvider Services public Schema Schema => _operationContext.Schema; - public IOperation Operation => _operationContext.Operation; + public Operation Operation => _operationContext.Operation; public IOperationResultBuilder OperationResult => _operationResultBuilder; @@ -48,7 +47,7 @@ public void ReportError(string errorMessage) ErrorBuilder.New() .SetMessage(errorMessage) .SetPath(Path) - .AddLocation(_selection.SyntaxNode) + .AddLocations(_selection) .Build()); } @@ -75,7 +74,7 @@ public void ReportError(Exception exception, Action? configure = n var errorBuilder = ErrorBuilder .FromException(exception) .SetPath(Path) - .AddLocation(_selection.SyntaxNode); + .AddLocations(_selection); configure?.Invoke(errorBuilder); @@ -108,14 +107,14 @@ void ReportSingle(IError singleError) { foreach (var ie in ar.Errors) { - var errorWithPath = EnsurePathAndLocation(ie, _selection.SyntaxNode, Path); + var errorWithPath = EnsurePathAndLocation(ie, _selection.SyntaxNodes[0].Node, Path); _operationContext.Result.AddError(errorWithPath, _selection); diagnosticEvents.ResolverError(this, errorWithPath); } } else { - var errorWithPath = EnsurePathAndLocation(handled, _selection.SyntaxNode, Path); + var errorWithPath = EnsurePathAndLocation(handled, _selection.SyntaxNodes[0].Node, Path); _operationContext.Result.AddError(errorWithPath, _selection); diagnosticEvents.ResolverError(this, errorWithPath); } @@ -290,3 +289,16 @@ c is null s))); } } + +file static class Extensions +{ + public static ErrorBuilder AddLocations(this ErrorBuilder errorBuilder, Selection selection) + { + foreach (var (node, _) in selection.SyntaxNodes) + { + errorBuilder.AddLocation(node); + } + + return errorBuilder; + } +} diff --git a/src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Pooling.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Pooling.cs similarity index 98% rename from src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Pooling.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Pooling.cs index 6f29541152f..73d88af6347 100644 --- a/src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Pooling.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Pooling.cs @@ -18,7 +18,7 @@ public MiddlewareContext() public void Initialize( OperationContext operationContext, - ISelection selection, + Selection selection, ObjectResult parentResult, int responseIndex, object? parent, diff --git a/src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Pure.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Pure.cs similarity index 90% rename from src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Pure.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Pure.cs index c8e21be1f27..20c1d467e6f 100644 --- a/src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Pure.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Pure.cs @@ -5,7 +5,6 @@ using HotChocolate.Resolvers; using HotChocolate.Types; using HotChocolate.Utilities; -using Microsoft.Extensions.DependencyInjection; using static HotChocolate.Execution.ThrowHelper; namespace HotChocolate.Execution.Processing; @@ -16,13 +15,13 @@ private sealed class PureResolverContext(MiddlewareContext parentContext) : IRes { private ITypeConverter? _typeConverter; private IReadOnlyDictionary _argumentValues = null!; - private ISelection _selection = null!; + private Selection _selection = null!; private ObjectType _parentType = null!; private ObjectResult _parentResult = null!; private object? _parent; public bool Initialize( - ISelection selection, + Selection selection, ObjectType parentType, ObjectResult parentResult, object? parent) @@ -60,9 +59,9 @@ public void Clear() public ObjectType ObjectType => _parentType; - public IOperation Operation => parentContext.Operation; + public Operation Operation => parentContext.Operation; - public ISelection Selection => _selection; + public Selection Selection => _selection; public Path Path => PathHelper.CreatePathFromContext(_selection, _parentResult, -1); @@ -77,9 +76,9 @@ public void ReportError(IError error) public void ReportError(Exception exception, Action? configure = null) => throw new NotSupportedException(); - public IReadOnlyList GetSelections( + public SelectionEnumerator GetSelections( ObjectType typeContext, - ISelection? selection = null, + Selection? selection = null, bool allowInternals = false) => throw new NotSupportedException(); @@ -135,7 +134,7 @@ public T ArgumentValue(string name) if (!_argumentValues.TryGetValue(name, out var argument)) { - throw ResolverContext_ArgumentDoesNotExist(_selection.SyntaxNode, Path, name); + throw ResolverContext_ArgumentDoesNotExist(_selection.SyntaxNodes[0].Node, Path, name); } return CoerceArgumentValue(argument); @@ -148,7 +147,7 @@ public TValueNode ArgumentLiteral(string name) if (!_argumentValues.TryGetValue(name, out var argument)) { - throw ResolverContext_ArgumentDoesNotExist(_selection.SyntaxNode, Path, name); + throw ResolverContext_ArgumentDoesNotExist(_selection.SyntaxNodes[0].Node, Path, name); } var literal = argument.ValueLiteral!; @@ -159,7 +158,11 @@ public TValueNode ArgumentLiteral(string name) } throw ResolverContext_LiteralNotCompatible( - _selection.SyntaxNode, Path, name, typeof(TValueNode), literal.GetType()); + _selection.SyntaxNodes[0].Node, + Path, + name, + typeof(TValueNode), + literal.GetType()); } public Optional ArgumentOptional(string name) @@ -168,7 +171,7 @@ public Optional ArgumentOptional(string name) if (!_argumentValues.TryGetValue(name, out var argument)) { - throw ResolverContext_ArgumentDoesNotExist(_selection.SyntaxNode, Path, name); + throw ResolverContext_ArgumentDoesNotExist(_selection.SyntaxNodes[0].Node, Path, name); } return argument.IsDefaultValue @@ -180,10 +183,10 @@ public ValueKind ArgumentKind(string name) { if (!_argumentValues.TryGetValue(name, out var argument)) { - throw ResolverContext_ArgumentDoesNotExist(_selection.SyntaxNode, Path, name); + throw ResolverContext_ArgumentDoesNotExist(_selection.SyntaxNodes[0].Node, Path, name); } - // There can only be no kind if there was an error which would have + // There can only be no kind of there was an error which would have // already been raised at this point. return argument.Kind ?? ValueKind.Unknown; } @@ -243,7 +246,10 @@ private T CoerceArgumentValue(ArgumentValue argument) if (typeof(IValueNode).IsAssignableFrom(typeof(T))) { throw ResolverContext_LiteralsNotSupported( - _selection.SyntaxNode, Path, argument.Name, typeof(T)); + _selection.SyntaxNodes[0].Node, + Path, + argument.Name, + typeof(T)); } // If the object is internally held as a dictionary structure we will try to @@ -270,7 +276,7 @@ private T CoerceArgumentValue(ArgumentValue argument) // we are unable to convert the argument to the request type. throw ResolverContext_CannotConvertArgument( - _selection.SyntaxNode, + _selection.SyntaxNodes[0].Node, Path, argument.Name, typeof(T), diff --git a/src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Selection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Selection.cs similarity index 61% rename from src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Selection.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Selection.cs index 96099d85405..660e99ded09 100644 --- a/src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Selection.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.Selection.cs @@ -8,13 +8,13 @@ namespace HotChocolate.Execution.Processing; internal partial class MiddlewareContext { private readonly PureResolverContext _childContext; - private ISelection _selection = null!; + private Selection _selection = null!; public ObjectType ObjectType => _selection.DeclaringType; public ObjectField Field => _selection.Field; - public ISelection Selection => _selection; + public Selection Selection => _selection; public string ResponseName => _selection.ResponseName; @@ -25,7 +25,7 @@ internal partial class MiddlewareContext public PureFieldDelegate? PureResolver => _selection.PureResolver; public bool TryCreatePureContext( - ISelection selection, + Selection selection, ObjectType parentType, ObjectResult parentResult, object? parent, @@ -41,43 +41,22 @@ public bool TryCreatePureContext( return false; } - public IReadOnlyList GetSelections( + public SelectionEnumerator GetSelections( ObjectType typeContext, - ISelection? selection = null, + Selection? selection = null, bool allowInternals = false) { ArgumentNullException.ThrowIfNull(typeContext); selection ??= _selection; - if (selection.SelectionSet is null) + if (selection.IsLeaf) { - return []; + return default; } var selectionSet = _operationContext.CollectFields(selection, typeContext); - - if (selectionSet.IsConditional) - { - var operationIncludeFlags = _operationContext.IncludeFlags; - var selectionCount = selectionSet.Selections.Count; - ref var selectionRef = ref ((SelectionSet)selectionSet).GetSelectionsReference(); - var finalFields = new List(); - - for (var i = 0; i < selectionCount; i++) - { - var childSelection = Unsafe.Add(ref selectionRef, i); - - if (childSelection.IsIncluded(operationIncludeFlags, allowInternals)) - { - finalFields.Add(childSelection); - } - } - - return finalFields; - } - - return selectionSet.Selections; + return new SelectionEnumerator(selectionSet, _operationContext.IncludeFlags); } public ISelectionCollection Select() diff --git a/src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.State.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.State.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.State.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/MiddlewareContext.State.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/NoOpTransactionScope.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/NoOpTransactionScope.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/NoOpTransactionScope.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/NoOpTransactionScope.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/NoOpTransactionScopeHandler.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/NoOpTransactionScopeHandler.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/NoOpTransactionScopeHandler.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/NoOpTransactionScopeHandler.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/NoopBatchDispatcher.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/NoopBatchDispatcher.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/NoopBatchDispatcher.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/NoopBatchDispatcher.cs diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs new file mode 100644 index 00000000000..72c8ed60f88 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Operation.cs @@ -0,0 +1,261 @@ +using System.Collections.Concurrent; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using HotChocolate.Features; +using HotChocolate.Language; +using HotChocolate.Types; +using static HotChocolate.Execution.Properties.Resources; +using static HotChocolate.Execution.ThrowHelper; + +namespace HotChocolate.Execution.Processing; + +public sealed class Operation : IOperation +{ +#if NET9_0_OR_GREATER + private readonly Lock _sync = new(); +#else + private readonly object _sync = new(); +#endif + + private readonly ConcurrentDictionary<(int, string), SelectionSet> _selectionSets = []; + private readonly OperationCompiler2 _compiler; + private readonly IncludeConditionCollection _includeConditions; + private readonly OperationFeatureCollection _features; + private readonly bool _isFinal; + private object[] _elementsById; + private int _lastId; + + internal Operation( + string id, + string hash, + OperationDefinitionNode definition, + ObjectType rootType, + Schema schema, + SelectionSet rootSelectionSet, + OperationCompiler2 compiler, + IncludeConditionCollection includeConditions, + int lastId, + object[] elementsById, + bool isFinal) + { + ArgumentException.ThrowIfNullOrWhiteSpace(id); + ArgumentException.ThrowIfNullOrWhiteSpace(hash); + ArgumentNullException.ThrowIfNull(definition); + ArgumentNullException.ThrowIfNull(rootType); + ArgumentNullException.ThrowIfNull(schema); + ArgumentNullException.ThrowIfNull(rootSelectionSet); + ArgumentNullException.ThrowIfNull(compiler); + ArgumentNullException.ThrowIfNull(includeConditions); + ArgumentNullException.ThrowIfNull(elementsById); + + Id = id; + Hash = hash; + Definition = definition; + RootType = rootType; + Schema = schema; + RootSelectionSet = rootSelectionSet; + _compiler = compiler; + _includeConditions = includeConditions; + _lastId = lastId; + _elementsById = elementsById; + _isFinal = isFinal; + + _features = new OperationFeatureCollection(); + rootSelectionSet.Complete(this, seal: isFinal); + } + + /// + /// Gets the internal unique identifier for this operation. + /// + public string Id { get; } + + /// + /// Gets the hash of the original operation document. + /// + public string Hash { get; } + + /// + /// Gets the name of the operation. + /// + public string? Name => Definition.Name?.Value; + + /// + /// Gets the syntax node representing the operation definition. + /// + public OperationDefinitionNode Definition { get; } + + /// + /// Gets the root type on which the operation is executed. + /// + public ObjectType RootType { get; } + + IObjectTypeDefinition IOperation.RootType => RootType; + + public OperationType Kind => Definition.Operation; + + /// + /// Gets the schema for which this operation is compiled. + /// + public Schema Schema { get; } + + ISchemaDefinition IOperation.Schema => Schema; + + /// + /// Gets the prepared root selections for this operation. + /// + /// + /// Returns the prepared root selections for this operation. + /// + public SelectionSet RootSelectionSet { get; } + + ISelectionSet IOperation.RootSelectionSet + => RootSelectionSet; + + /// + public IFeatureCollection Features => _features; + + /// + /// Gets the selection set for the specified + /// if the selections named return type is an object type. + /// + /// + /// The selection set for which the selection set shall be resolved. + /// + /// + /// Returns the selection set for the specified and + /// the named return type of the selection. + /// + /// + /// - The specified has no selection set. + /// - The specified returns an abstract named type. + /// + public SelectionSet GetSelectionSet(Selection selection) + { + ArgumentNullException.ThrowIfNull(selection); + var typeContext = selection.Field.Type.NamedType(); + return GetSelectionSet(selection, typeContext); + } + + /// + /// Gets the selection set for the specified and + /// . + /// + /// + /// The selection set for which the selection set shall be resolved. + /// + /// + /// The result type context. + /// + /// + /// Returns the selection set for the specified and + /// . + /// + /// + /// The specified has no selection set. + /// + public SelectionSet GetSelectionSet(Selection selection, IObjectTypeDefinition typeContext) + { + ArgumentNullException.ThrowIfNull(selection); + ArgumentNullException.ThrowIfNull(typeContext); + + if (typeContext is not ObjectType objectType) + { + throw new ArgumentException( + "typeContext is not an ObjectType object.", + nameof(typeContext)); + } + + var key = (selection.Id, typeContext.Name); + + if (!_selectionSets.TryGetValue(key, out var selectionSet)) + { + lock (_sync) + { + if (!_selectionSets.TryGetValue(key, out selectionSet)) + { + selectionSet = + _compiler.CompileSelectionSet( + selection, + objectType, + _includeConditions, + ref _elementsById, + ref _lastId); + selectionSet.Complete(this, seal: _isFinal); + _selectionSets.TryAdd(key, selectionSet); + } + } + } + + return selectionSet; + } + + ISelectionSet IOperation.GetSelectionSet(ISelection selection, IObjectTypeDefinition typeContext) + { + ArgumentNullException.ThrowIfNull(selection); + ArgumentNullException.ThrowIfNull(typeContext); + + if (selection is not Selection internalSelection) + { + throw new InvalidOperationException( + $"Only selections of the type {typeof(Selection).FullName} are supported."); + } + + return GetSelectionSet(internalSelection, typeContext); + } + + /// + /// Gets the possible return types for the . + /// + /// + /// The selection for which the possible result types shall be returned. + /// + /// + /// Returns the possible return types for the specified . + /// + /// + /// The specified has no selection set. + /// + public IEnumerable GetPossibleTypes(Selection selection) + { + ArgumentNullException.ThrowIfNull(selection); + + return Schema.GetPossibleTypes(selection.Field.Type.NamedType()); + } + + IEnumerable IOperation.GetPossibleTypes(ISelection selection) + => Schema.GetPossibleTypes(selection.Field.Type.NamedType()); + + /// + /// Creates the include flags for the specified variable values. + /// + /// + /// The variable values. + /// + /// + /// Returns the include flags for the specified variable values. + /// + public ulong CreateIncludeFlags(IVariableValueCollection variables) + { + var index = 0; + var includeFlags = 0ul; + + foreach (var includeCondition in _includeConditions) + { + if (includeCondition.IsIncluded(variables)) + { + includeFlags |= 1ul << index++; + } + } + + return includeFlags; + } + + internal Selection GetSelectionById(int id) + => Unsafe.As(Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_elementsById), id)); + + internal SelectionSet GetSelectionSetById(int id) + => Unsafe.As(Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_elementsById), id)); + + public override string ToString() => OperationPrinter.Print(this); +} diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationCompiler.BacklogItem.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.BacklogItem.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationCompiler.BacklogItem.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.BacklogItem.cs diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CoerceArgumentValues.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CoerceArgumentValues.cs new file mode 100644 index 00000000000..8f6068bbf80 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CoerceArgumentValues.cs @@ -0,0 +1,122 @@ +using System.Buffers; +using System.Collections; +using System.Diagnostics.CodeAnalysis; +using HotChocolate.Fusion.Rewriters; +using HotChocolate.Language; +using HotChocolate.Language.Visitors; +using HotChocolate.Resolvers; +using HotChocolate.Types; +using Microsoft.Extensions.ObjectPool; + +namespace HotChocolate.Execution.Processing; + +internal sealed partial class OperationCompiler2 +{ + private ArgumentMap? CoerceArgumentValues( + ObjectField field, + FieldNode selection) + { + if (field.Arguments.Count == 0) + { + return null; + } + + var arguments = new Dictionary(StringComparer.Ordinal); + + for (var i = 0; i < selection.Arguments.Count; i++) + { + var argumentValue = selection.Arguments[i]; + if (field.Arguments.TryGetField( + argumentValue.Name.Value, + out var argument)) + { + arguments[argument.Name] = CreateArgumentValue(argument, argumentValue, argumentValue.Value, false); + } + } + + for (var i = 0; i < field.Arguments.Count; i++) + { + var argument = field.Arguments[i]; + if (!arguments.ContainsKey(argument.Name)) + { + var value = argument.DefaultValue ?? NullValueNode.Default; + arguments[argument.Name] = CreateArgumentValue(argument, null, value, true); + } + } + + return new ArgumentMap(arguments); + } + + private ArgumentValue CreateArgumentValue( + Argument argument, + ArgumentNode? argumentValue, + IValueNode value, + bool isDefaultValue) + { + var validationResult = + ArgumentNonNullValidator.Validate( + argument, + value, + Path.Root.Append(argument.Name)); + + if (argumentValue is not null && validationResult.HasErrors) + { + return new ArgumentValue( + argument, + ErrorHelper.ArgumentNonNullError( + argumentValue, + validationResult)); + } + + if (argument.Type.IsLeafType() && CanBeCompiled(value)) + { + try + { + return new ArgumentValue( + argument, + value.GetValueKind(), + true, + isDefaultValue, + _inputValueParser.ParseLiteral(value, argument), + value); + } + catch (SerializationException ex) + { + return new ArgumentValue( + argument, + ErrorHelper.ArgumentValueIsInvalid(argumentValue, ex)); + } + } + + return new ArgumentValue( + argument, + value.GetValueKind(), + false, + isDefaultValue, + null, + value); + } + + private static bool CanBeCompiled(IValueNode valueLiteral) + { + switch (valueLiteral.Kind) + { + case SyntaxKind.Variable: + case SyntaxKind.ObjectValue: + return false; + + case SyntaxKind.ListValue: + var list = (ListValueNode)valueLiteral; + for (var i = 0; i < list.Items.Count; i++) + { + if (!CanBeCompiled(list.Items[i])) + { + return false; + } + } + break; + } + + return true; + } +} diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CompileResolver.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CompileResolver.cs new file mode 100644 index 00000000000..d5b73c6b40a --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.CompileResolver.cs @@ -0,0 +1,97 @@ +using HotChocolate.Language; +using HotChocolate.Resolvers; +using HotChocolate.Types; + +namespace HotChocolate.Execution.Processing; + +internal sealed partial class OperationCompiler2 +{ + internal static FieldDelegate CreateFieldPipeline(Schema schema, ObjectField field, FieldNode selection) + { + var pipeline = field.Middleware; + + if (selection.Directives.Count == 0) + { + return pipeline; + } + + var pipelineComponents = new List(); + + // if we have selection directives we will inspect them and try to build a + // pipeline from them if they have middleware components. + BuildDirectivePipeline(schema, selection, pipelineComponents); + + // if we found middleware components on the selection directives we will build a new + // pipeline. + if (pipelineComponents.Count > 0) + { + var next = pipeline; + + for (var i = pipelineComponents.Count - 1; i >= 0; i--) + { + next = pipelineComponents[i](next); + } + + pipeline = next; + } + + return pipeline; + } + + private static PureFieldDelegate? TryCreatePureField( + Schema schema, + ObjectField field, + FieldNode selection) + { + if (field.PureResolver is not null && selection.Directives.Count == 0) + { + return field.PureResolver; + } + + for (var i = 0; i < selection.Directives.Count; i++) + { + if (schema.DirectiveTypes.TryGetDirective(selection.Directives[i].Name.Value, out var type) + && type.Middleware is not null) + { + return null; + } + } + + return field.PureResolver; + } + + // TODO : this needs a rewrite, we need to discuss how we handle field merging with directives + private static void BuildDirectivePipeline( + Schema schema, + FieldNode selection, + List pipelineComponents) + { + for (var i = 0; i < selection.Directives.Count; i++) + { + var directiveNode = selection.Directives[i]; + if (schema.DirectiveTypes.TryGetDirective(directiveNode.Name.Value, out var directiveType) + && directiveType.Middleware is not null) + { + Directive directive; + try + { + directive = new Directive( + directiveType, + directiveNode, + directiveType.Parse(directiveNode)); + } + catch (SerializationException ex) + { + throw new SerializationException( + ErrorBuilder.FromError(ex.Errors[0]) + .TryAddLocation(directiveNode) + .Build(), + ex.Type); + } + + var directiveMiddleware = directiveType.Middleware; + pipelineComponents.Add(next => directiveMiddleware(next, directive)); + } + } + } +} diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationCompiler.Optimizers.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.Optimizers.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationCompiler.Optimizers.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.Optimizers.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationCompiler.SelectionSetInfo.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.SelectionSetInfo.cs similarity index 53% rename from src/HotChocolate/Core/src/Execution/Processing/OperationCompiler.SelectionSetInfo.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.SelectionSetInfo.cs index a6363e4c195..7c46db01ff7 100644 --- a/src/HotChocolate/Core/src/Execution/Processing/OperationCompiler.SelectionSetInfo.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.SelectionSetInfo.cs @@ -4,10 +4,12 @@ namespace HotChocolate.Execution.Processing; public sealed partial class OperationCompiler { - internal readonly struct SelectionSetInfo(SelectionSetNode selectionSet, long includeCondition) + internal readonly struct SelectionSetInfo( + SelectionSetNode selectionSet, + ulong includeCondition) { public SelectionSetNode SelectionSet { get; } = selectionSet; - public long IncludeCondition { get; } = includeCondition; + public ulong IncludeCondition { get; } = includeCondition; } } diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs new file mode 100644 index 00000000000..211d81d9e7d --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompiler.cs @@ -0,0 +1,644 @@ +using System.Buffers; +using System.Collections; +using System.Diagnostics.CodeAnalysis; +using HotChocolate.Fusion.Rewriters; +using HotChocolate.Language; +using HotChocolate.Language.Visitors; +using HotChocolate.Types; +using Microsoft.Extensions.ObjectPool; + +namespace HotChocolate.Execution.Processing; + +internal sealed partial class OperationCompiler2 +{ + private readonly Schema _schema; + private readonly ObjectPool>> _fieldsPool; + private readonly DocumentRewriter _documentRewriter; + private readonly InputParser _inputValueParser; + private static readonly ArrayPool s_objectArrayPool = ArrayPool.Shared; + + public OperationCompiler2( + Schema schema, + InputParser inputValueParser, + ObjectPool>> fieldsPool) + { + ArgumentNullException.ThrowIfNull(schema); + ArgumentNullException.ThrowIfNull(fieldsPool); + + _schema = schema; + _inputValueParser = inputValueParser; + _fieldsPool = fieldsPool; + _documentRewriter = new DocumentRewriter(schema, removeStaticallyExcludedSelections: true); + } + + public Operation Compile(string id, string hash, OperationDefinitionNode operationDefinition) + { + ArgumentException.ThrowIfNullOrWhiteSpace(id); + ArgumentNullException.ThrowIfNull(operationDefinition); + + var document = new DocumentNode(new IDefinitionNode[] { operationDefinition }); + document = _documentRewriter.RewriteDocument(document); + operationDefinition = (OperationDefinitionNode)document.Definitions[0]; + + var includeConditions = new IncludeConditionCollection(); + IncludeConditionVisitor.Instance.Visit(operationDefinition, includeConditions); + var fields = _fieldsPool.Get(); + + var compilationContext = new CompilationContext(s_objectArrayPool.Rent(128)); + + try + { + var lastId = 0; + const ulong parentIncludeFlags = 0ul; + var rootType = _schema.GetOperationType(operationDefinition.Operation); + + CollectFields( + parentIncludeFlags, + operationDefinition.SelectionSet.Selections, + rootType, + fields, + includeConditions); + + var selectionSet = BuildSelectionSet( + fields, + rootType, + compilationContext, + ref lastId); + + compilationContext.Register(selectionSet, selectionSet.Id); + + return new Operation( + id, + hash, + operationDefinition, + rootType, + _schema, + selectionSet, + this, + includeConditions, + lastId, + compilationContext.ElementsById, + isFinal: true); // todo : add the interceptors back + } + finally + { + _fieldsPool.Return(fields); + } + } + + internal SelectionSet CompileSelectionSet( + Selection selection, + ObjectType objectType, + IncludeConditionCollection includeConditions, + ref object[] elementsById, + ref int lastId) + { + var compilationContext = new CompilationContext(elementsById); + var fields = _fieldsPool.Get(); + fields.Clear(); + + try + { + var nodes = selection.SyntaxNodes; + var first = nodes[0]; + + CollectFields( + first.PathIncludeFlags, + first.Node.SelectionSet!.Selections, + objectType, + fields, + includeConditions); + + if (nodes.Length > 1) + { + for (var i = 1; i < nodes.Length; i++) + { + var node = nodes[i]; + + CollectFields( + node.PathIncludeFlags, + node.Node.SelectionSet!.Selections, + objectType, + fields, + includeConditions); + } + } + + var selectionSet = BuildSelectionSet(fields, objectType, compilationContext, ref lastId); + compilationContext.Register(selectionSet, selectionSet.Id); + elementsById = compilationContext.ElementsById; + return selectionSet; + } + finally + { + _fieldsPool.Return(fields); + } + } + + private void CollectFields( + ulong parentIncludeFlags, + IReadOnlyList selections, + IObjectTypeDefinition typeContext, + OrderedDictionary> fields, + IncludeConditionCollection includeConditions) + { + for (var i = 0; i < selections.Count; i++) + { + var selection = selections[i]; + + if (selection is FieldNode fieldNode) + { + var responseName = fieldNode.Alias?.Value ?? fieldNode.Name.Value; + var pathIncludeFlags = parentIncludeFlags; + + if (!fields.TryGetValue(responseName, out var nodes)) + { + nodes = []; + fields.Add(responseName, nodes); + } + + if (IncludeCondition.TryCreate(fieldNode, out var includeCondition)) + { + var index = includeConditions.IndexOf(includeCondition); + pathIncludeFlags |= 1ul << index; + } + + nodes.Add(new FieldSelectionNode(fieldNode, pathIncludeFlags)); + } + + if (selection is InlineFragmentNode inlineFragmentNode + && DoesTypeApply(inlineFragmentNode.TypeCondition, typeContext)) + { + var pathIncludeFlags = parentIncludeFlags; + + if (IncludeCondition.TryCreate(inlineFragmentNode, out var includeCondition)) + { + var index = includeConditions.IndexOf(includeCondition); + pathIncludeFlags |= 1ul << index; + } + + CollectFields( + pathIncludeFlags, + inlineFragmentNode.SelectionSet.Selections, + typeContext, + fields, + includeConditions); + } + } + } + + private SelectionSet BuildSelectionSet( + OrderedDictionary> fieldMap, + ObjectType typeContext, + CompilationContext compilationContext, + ref int lastId) + { + var i = 0; + var selections = new Selection[fieldMap.Count]; + var isConditional = false; + var includeFlags = new List(); + var selectionSetId = ++lastId; + + foreach (var (responseName, nodes) in fieldMap) + { + includeFlags.Clear(); + + var first = nodes[0]; + var isInternal = IsInternal(first.Node); + + if (first.PathIncludeFlags > 0) + { + includeFlags.Add(first.PathIncludeFlags); + } + + if (nodes.Count > 1) + { + for (var j = 1; j < nodes.Count; j++) + { + var next = nodes[j]; + + if (!first.Node.Name.Value.Equals(next.Node.Name.Value, StringComparison.Ordinal)) + { + throw new InvalidOperationException( + $"The syntax nodes for the response name {responseName} are not all the same."); + } + + if (next.PathIncludeFlags > 0) + { + includeFlags.Add(next.PathIncludeFlags); + } + + if (isInternal) + { + isInternal = IsInternal(next.Node); + } + } + } + + if (includeFlags.Count > 1) + { + CollapseIncludeFlags(includeFlags); + } + + var field = typeContext.Fields[first.Node.Name.Value]; + var fieldDelegate = CreateFieldPipeline(_schema, field, first.Node); + var pureFieldDelegate = TryCreatePureField(_schema, field, first.Node); + var arguments = ArgumentMap.Empty; + + if (first.Node.Arguments.Count > 0) + { + arguments = CoerceArgumentValues(field, first.Node); + } + + var selection = new Selection( + ++lastId, + responseName, + field, + nodes.ToArray(), + includeFlags.ToArray(), + isInternal, + arguments, + fieldDelegate, + pureFieldDelegate); + + // Register the selection in the elements array + compilationContext.Register(selection, selection.Id); + selections[i++] = selection; + + if (includeFlags.Count > 1) + { + isConditional = true; + } + } + + return new SelectionSet(selectionSetId, typeContext, selections, isConditional); + } + + private static void CollapseIncludeFlags(List includeFlags) + { + // we sort the include flags to improve early elimination and stability + includeFlags.Sort(); + + var write = 0; + + for (var read = 0; read < includeFlags.Count; read++) + { + var candidate = includeFlags[read]; + var covered = false; + + // we check if the candidate is already covered + for (var i = 0; i < write; i++) + { + if ((candidate & includeFlags[i]) == includeFlags[i]) + { + covered = true; + break; + } + } + + if (!covered) + { + // lastly we remove more restrictive flags from the already written range + for (var i = 0; i < write;) + { + if ((includeFlags[i] & candidate) == candidate) + { + includeFlags[i] = includeFlags[--write]; + } + else + { + i++; + } + } + + if (write < read) + { + includeFlags[write] = candidate; + } + write++; + } + } + + // we trim the list to the collapsed set + if (write < includeFlags.Count) + { + includeFlags.RemoveRange(write, includeFlags.Count - write); + } + } + + private bool DoesTypeApply(NamedTypeNode? typeCondition, IObjectTypeDefinition typeContext) + { + if (typeCondition is null) + { + return true; + } + + if (typeCondition.Name.Value.Equals(typeContext.Name, StringComparison.Ordinal)) + { + return true; + } + + if (_schema.Types.TryGetType(typeCondition.Name.Value, out var type)) + { + return type.IsAssignableFrom(typeContext); + } + + return false; + } + + private static bool IsInternal(FieldNode fieldNode) + { + const string isInternal = "fusion__requirement"; + var directives = fieldNode.Directives; + + if (directives.Count == 0) + { + return false; + } + + if (directives.Count == 1) + { + return directives[0].Name.Value.Equals(isInternal, StringComparison.Ordinal); + } + + if (directives.Count == 2) + { + return directives[0].Name.Value.Equals(isInternal, StringComparison.Ordinal) + || directives[1].Name.Value.Equals(isInternal, StringComparison.Ordinal); + } + + if (directives.Count == 3) + { + return directives[0].Name.Value.Equals(isInternal, StringComparison.Ordinal) + || directives[1].Name.Value.Equals(isInternal, StringComparison.Ordinal) + || directives[2].Name.Value.Equals(isInternal, StringComparison.Ordinal); + } + + for (var i = 0; i < directives.Count; i++) + { + var directive = directives[i]; + + if (directive.Name.Value.Equals(isInternal, StringComparison.Ordinal)) + { + return true; + } + } + + return false; + } + + private class IncludeConditionVisitor : SyntaxWalker + { + public static readonly IncludeConditionVisitor Instance = new(); + + protected override ISyntaxVisitorAction Enter( + FieldNode node, + IncludeConditionCollection context) + { + if (IncludeCondition.TryCreate(node, out var condition)) + { + context.Add(condition); + } + + return base.Enter(node, context); + } + + protected override ISyntaxVisitorAction Enter( + InlineFragmentNode node, + IncludeConditionCollection context) + { + if (IncludeCondition.TryCreate(node, out var condition)) + { + context.Add(condition); + } + + return base.Enter(node, context); + } + } + + private class CompilationContext(object[] elementsById) + { + private object[] _elementsById = elementsById; + + public object[] ElementsById => _elementsById; + + public void Register(object element, int id) + { + if (id >= _elementsById.Length) + { + var newArray = s_objectArrayPool.Rent(_elementsById.Length * 2); + _elementsById.AsSpan().CopyTo(newArray); + s_objectArrayPool.Return(_elementsById); + _elementsById = newArray; + } + + _elementsById[id] = element; + } + } +} + +/// +/// Represents a field selection node with its path include flags. +/// +/// +/// The syntax node that represents the field selection. +/// +/// +/// The flags that must be all set for this selection to be included. +/// +public sealed record FieldSelectionNode(FieldNode Node, ulong PathIncludeFlags); + +internal class IncludeConditionCollection : ICollection +{ + private readonly OrderedDictionary _dictionary = []; + + public IncludeCondition this[int index] + => _dictionary.GetAt(index).Key; + + public int Count => _dictionary.Count; + + public bool IsReadOnly => false; + + public bool Add(IncludeCondition item) + { + if (_dictionary.Count == 64) + { + throw new InvalidOperationException( + "The maximum number of include conditions has been reached."); + } + + return _dictionary.TryAdd(item, _dictionary.Count); + } + + void ICollection.Add(IncludeCondition item) + => Add(item); + + public bool Remove(IncludeCondition item) + => throw new InvalidOperationException("This is an add only collection."); + + void ICollection.Clear() + => throw new InvalidOperationException("This is an add only collection."); + + public bool Contains(IncludeCondition item) + => _dictionary.ContainsKey(item); + + public int IndexOf(IncludeCondition item) + => _dictionary.GetValueOrDefault(item, -1); + + public void CopyTo(IncludeCondition[] array, int arrayIndex) + => _dictionary.Keys.CopyTo(array, arrayIndex); + + public IEnumerator GetEnumerator() + => _dictionary.Keys.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() + => GetEnumerator(); +} + +internal readonly struct IncludeCondition : IEquatable +{ + private readonly string? _skip; + private readonly string? _include; + + public IncludeCondition(string? skip, string? include) + { + _skip = skip; + _include = include; + } + + public string? Skip => _skip; + + public string? Include => _include; + + public bool IsIncluded(IVariableValueCollection variableValues) + { + if (_skip is not null) + { + if (!variableValues.TryGetValue(_skip, out var value)) + { + throw new InvalidOperationException($"The variable {_skip} has an invalid value."); + } + + if (value.Value) + { + return false; + } + } + + if (_include is not null) + { + if (!variableValues.TryGetValue(_include, out var value)) + { + throw new InvalidOperationException($"The variable {_include} has an invalid value."); + } + + if (!value.Value) + { + return false; + } + } + + return true; + } + + public bool Equals(IncludeCondition other) + => string.Equals(Skip, other.Skip, StringComparison.Ordinal) + && string.Equals(Include, other.Include, StringComparison.Ordinal); + + public override bool Equals([NotNullWhen(true)] object? obj) + => obj is IncludeCondition other && Equals(other); + + public override int GetHashCode() + => HashCode.Combine(Skip, Include); + + public static bool TryCreate(FieldNode field, out IncludeCondition includeCondition) + => TryCreate(field.Directives, out includeCondition); + + public static bool TryCreate(InlineFragmentNode inlineFragment, out IncludeCondition includeCondition) + => TryCreate(inlineFragment.Directives, out includeCondition); + + private static bool TryCreate(IReadOnlyList directives, out IncludeCondition includeCondition) + { + string? skip = null; + string? include = null; + + if (directives.Count == 0) + { + includeCondition = default; + return false; + } + + if (directives.Count == 1) + { + TryParseDirective(directives[0], ref skip, ref include); + if (TryCreateIncludeCondition(out includeCondition)) + { + return true; + } + } + + if (directives.Count == 2) + { + TryParseDirective(directives[0], ref skip, ref include); + TryParseDirective(directives[1], ref skip, ref include); + return TryCreateIncludeCondition(out includeCondition); + } + + if (directives.Count == 3) + { + TryParseDirective(directives[0], ref skip, ref include); + TryParseDirective(directives[1], ref skip, ref include); + + if (skip is not null && include is not null) + { + includeCondition = new IncludeCondition(skip, include); + return true; + } + + TryParseDirective(directives[2], ref skip, ref include); + return TryCreateIncludeCondition(out includeCondition); + } + + for (var i = 0; i < directives.Count; i++) + { + TryParseDirective(directives[i], ref skip, ref include); + + if (skip is not null && include is not null) + { + includeCondition = new IncludeCondition(skip, include); + return true; + } + } + + includeCondition = default; + return false; + + bool TryCreateIncludeCondition(out IncludeCondition includeCondition) + { + if (skip is not null || include is not null) + { + includeCondition = new IncludeCondition(skip, include); + return true; + } + + includeCondition = default; + return false; + } + } + + private static void TryParseDirective(DirectiveNode directive, ref string? skip, ref string? include) + { + if (directive.Name.Value.Equals(DirectiveNames.Skip.Name, StringComparison.Ordinal) + && directive.Arguments.Count == 1 + && directive.Arguments[0].Value is VariableNode skipVariable) + { + skip = skipVariable.Name.Value; + } + else if (directive.Name.Value.Equals(DirectiveNames.Include.Name, StringComparison.Ordinal) + && directive.Arguments.Count == 1 + && directive.Arguments[0].Value is VariableNode includeVariable) + { + include = includeVariable.Name.Value; + } + } +} diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationCompilerMetrics.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompilerMetrics.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationCompilerMetrics.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompilerMetrics.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationCompilerOptimizerHelper.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompilerOptimizerHelper.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationCompilerOptimizerHelper.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompilerOptimizerHelper.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationCompilerOptimizers.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompilerOptimizers.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationCompilerOptimizers.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompilerOptimizers.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationCompilerPool.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompilerPool.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationCompilerPool.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationCompilerPool.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationContext.Execution.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Execution.cs similarity index 82% rename from src/HotChocolate/Core/src/Execution/Processing/OperationContext.Execution.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Execution.cs index 862fb41a59a..6e74fb6e5ea 100644 --- a/src/HotChocolate/Core/src/Execution/Processing/OperationContext.Execution.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Execution.cs @@ -21,19 +21,6 @@ internal set } } - /// - /// Gets the backlog of the task that shall be processed after - /// all the main tasks have been executed. - /// - public DeferredWorkScheduler DeferredScheduler - { - get - { - AssertInitialized(); - return _deferredWorkScheduler; - } - } - /// /// The result helper which provides utilities to build up the result. /// diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationContext.IExecutionTaskContext.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.IExecutionTaskContext.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationContext.IExecutionTaskContext.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.IExecutionTaskContext.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationContext.Operation.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Operation.cs similarity index 93% rename from src/HotChocolate/Core/src/Execution/Processing/OperationContext.Operation.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Operation.cs index 8239f55055b..8921485ebd5 100644 --- a/src/HotChocolate/Core/src/Execution/Processing/OperationContext.Operation.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Operation.cs @@ -20,7 +20,7 @@ public Schema Schema /// /// Gets the operation that is being executed. /// - public IOperation Operation + public Operation Operation { get { @@ -44,7 +44,7 @@ public IVariableValueCollection Variables /// /// Gets the include flags for the current request. /// - public long IncludeFlags { get; private set; } + public ulong IncludeFlags { get; private set; } /// /// Gets the value representing the instance of the @@ -70,7 +70,7 @@ public object? RootValue /// The type context. /// /// - public ISelectionSet CollectFields(ISelection selection, ObjectType typeContext) + public SelectionSet CollectFields(Selection selection, ObjectType typeContext) { AssertInitialized(); return Operation.GetSelectionSet(selection, typeContext); diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationContext.Pooling.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Pooling.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationContext.Pooling.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Pooling.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationContext.Services.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Services.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationContext.Services.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Services.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationContext.Utilities.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Utilities.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationContext.Utilities.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.Utilities.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationContext.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationContext.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationContext.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationContextOwner.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationContextOwner.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationContextOwner.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationContextOwner.cs diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/OperationFeatureCollection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationFeatureCollection.cs new file mode 100644 index 00000000000..025d3eb9600 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationFeatureCollection.cs @@ -0,0 +1,113 @@ +using System.Collections; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using HotChocolate.Features; + +namespace HotChocolate.Execution.Processing; + +[SuppressMessage("ReSharper", "NonAtomicCompoundOperator")] +internal sealed class OperationFeatureCollection : IFeatureCollection +{ +#if NET9_0_OR_GREATER + private readonly Lock _writeLock = new(); +#else + private readonly object _writeLock = new(); +#endif +#if NET10_0_OR_GREATER + private ImmutableDictionary _features = []; +#else + private ImmutableDictionary _features = ImmutableDictionary.Empty; +#endif + private volatile int _containerRevision; + + /// + /// Initializes a new instance of . + /// + public OperationFeatureCollection() + { + } + + /// + public bool IsReadOnly => false; + + /// + public bool IsEmpty => _features.Count == 0; + + /// + public int Revision => _containerRevision; + + /// + public object? this[Type key] + { + get + { + ArgumentNullException.ThrowIfNull(key); + + return _features.GetValueOrDefault(key); + } + set + { + ArgumentNullException.ThrowIfNull(key); + + lock (_writeLock) + { + if (value == null) + { + _features = _features.Remove(key); + _containerRevision++; + return; + } + + _features = _features.SetItem(key, value); + _containerRevision++; + } + } + } + + /// + public TFeature? Get() + { + if (typeof(TFeature).IsValueType) + { + var feature = this[typeof(TFeature)]; + if (feature is null && Nullable.GetUnderlyingType(typeof(TFeature)) is null) + { + throw new InvalidOperationException( + $"{typeof(TFeature).FullName} does not exist in the feature collection " + + "and because it is a struct the method can't return null. " + + $"Use 'featureCollection[typeof({typeof(TFeature).FullName})] is not null' " + + "to check if the feature exists."); + } + + return (TFeature?)feature; + } + + return (TFeature?)this[typeof(TFeature)]; + } + + /// + public bool TryGet([NotNullWhen(true)] out TFeature? feature) + { + if (_features.TryGetValue(typeof(TFeature), out var result) + && result is TFeature f) + { + feature = f; + return true; + } + + feature = default; + return false; + } + + /// + public void Set(TFeature? instance) + { + this[typeof(TFeature)] = instance; + } + + /// + public IEnumerator> GetEnumerator() + => _features.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); +} diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationOptimizerContext.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationOptimizerContext.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationOptimizerContext.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationOptimizerContext.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationPrinter.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationPrinter.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationPrinter.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationPrinter.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationResolverHelper.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/OperationResolverHelper.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationResolverHelper.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/OperationResolverHelper.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/PathHelper.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/PathHelper.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/PathHelper.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/PathHelper.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/QueryExecutor.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/QueryExecutor.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/QueryExecutor.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/QueryExecutor.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Result/ListResult.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ListResult.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Result/ListResult.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Result/ListResult.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Result/ListResultPool.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ListResultPool.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Result/ListResultPool.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Result/ListResultPool.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Result/ObjectFieldResult.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ObjectFieldResult.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Result/ObjectFieldResult.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Result/ObjectFieldResult.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Result/ObjectResult.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ObjectResult.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Result/ObjectResult.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Result/ObjectResult.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Result/ObjectResultExtensions.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ObjectResultExtensions.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Result/ObjectResultExtensions.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Result/ObjectResultExtensions.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Result/ObjectResultPool.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ObjectResultPool.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Result/ObjectResultPool.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Result/ObjectResultPool.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Result/ResultBucket.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultBucket.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Result/ResultBucket.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultBucket.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Result/ResultBuilder.NonNullHandling.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultBuilder.NonNullHandling.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Result/ResultBuilder.NonNullHandling.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultBuilder.NonNullHandling.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Result/ResultBuilder.ObjectResult.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultBuilder.ObjectResult.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Result/ResultBuilder.ObjectResult.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultBuilder.ObjectResult.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Result/ResultBuilder.Pooling.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultBuilder.Pooling.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Result/ResultBuilder.Pooling.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultBuilder.Pooling.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Result/ResultBuilder.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultBuilder.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Result/ResultBuilder.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultBuilder.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Result/ResultData.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultData.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Result/ResultData.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultData.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Result/ResultMemoryOwner.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultMemoryOwner.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Result/ResultMemoryOwner.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultMemoryOwner.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Result/ResultPool.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultPool.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Result/ResultPool.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultPool.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Result/ResultPoolDefaults.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultPoolDefaults.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Result/ResultPoolDefaults.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Result/ResultPoolDefaults.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/RootValueResolver.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/RootValueResolver.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/RootValueResolver.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/RootValueResolver.cs diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs new file mode 100644 index 00000000000..1d44a51e445 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/Selection.cs @@ -0,0 +1,274 @@ +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Text; +using HotChocolate.Caching.Memory; +using HotChocolate.Execution.Properties; +using HotChocolate.Language; +using HotChocolate.Resolvers; +using HotChocolate.Types; + +namespace HotChocolate.Execution.Processing; + +/// +/// Represents a field selection during execution. +/// +public class Selection : ISelection +{ + private static readonly ArgumentMap s_emptyArguments = ArgumentMap.Empty; + private readonly FieldSelectionNode[] _syntaxNodes; + private readonly ulong[] _includeFlags; + private readonly byte[] _utf8ResponseName; + private Flags _flags; + + internal Selection( + int id, + string responseName, + ObjectField field, + FieldSelectionNode[] syntaxNodes, + ulong[] includeFlags, + bool isInternal, + ArgumentMap? arguments = null, + FieldDelegate? resolverPipeline = null, + PureFieldDelegate? pureResolver = null) + { + ArgumentNullException.ThrowIfNull(field); + + if (syntaxNodes.Length == 0) + { + throw new ArgumentException( + "The syntaxNodes collection cannot be empty.", + nameof(syntaxNodes)); + } + + Id = id; + ResponseName = responseName; + Field = field; + Arguments = arguments ?? s_emptyArguments; + ResolverPipeline = resolverPipeline; + PureResolver = pureResolver; + Strategy = InferStrategy( + isSerial: !field.IsParallelExecutable, + hasPureResolver: pureResolver is not null); + _syntaxNodes = syntaxNodes; + _includeFlags = includeFlags; + _flags = isInternal ? Flags.Internal : Flags.None; + + if (field.Type.NamedType().IsLeafType()) + { + _flags |= Flags.Leaf; + } + + if (field.Type.IsListType()) + { + _flags |= Flags.List; + } + + _utf8ResponseName = Utf8StringCache.GetUtf8String(responseName); + } + + /// + public int Id { get; } + + /// + public string ResponseName { get; } + + internal ReadOnlySpan Utf8ResponseName => _utf8ResponseName; + + public bool IsInternal => (_flags & Flags.Internal) == Flags.Internal; + + public bool IsConditional => _includeFlags.Length > 0; + + public bool IsList => (_flags & Flags.List) == Flags.List; + + public bool IsLeaf => (_flags & Flags.Leaf) == Flags.Leaf; + + public ObjectField Field { get; } + + /// + IOutputFieldDefinition ISelection.Field => Field; + + public IType Type => Field.Type; + + public ObjectType DeclaringType => Field.DeclaringType; + + public SelectionSet DeclaringSelectionSet { get; private set; } = null!; + + ISelectionSet ISelection.DeclaringSelectionSet => DeclaringSelectionSet; + + public Operation DeclaringOperation => DeclaringSelectionSet.DeclaringOperation; + + public ArgumentMap Arguments { get; } + + public SelectionExecutionStrategy Strategy { get; private set; } + + public FieldDelegate? ResolverPipeline { get; private set; } + + public PureFieldDelegate? PureResolver { get; private set; } + + public ReadOnlySpan SyntaxNodes => _syntaxNodes; + + IEnumerable ISelection.GetSyntaxNodes() + { + for (var i = 0; i < SyntaxNodes.Length; i++) + { + yield return SyntaxNodes[i].Node; + } + } + + public bool IsIncluded(ulong includeFlags) + { + if (_includeFlags.Length == 0) + { + return true; + } + + if (_includeFlags.Length == 1) + { + var flags1 = _includeFlags[0]; + return (flags1 & includeFlags) == flags1; + } + + if (_includeFlags.Length == 2) + { + var flags1 = _includeFlags[0]; + var flags2 = _includeFlags[1]; + return (flags1 & includeFlags) == flags1 || (flags2 & includeFlags) == flags2; + } + + if (_includeFlags.Length == 3) + { + var flags1 = _includeFlags[0]; + var flags2 = _includeFlags[1]; + var flags3 = _includeFlags[2]; + return (flags1 & includeFlags) == flags1 + || (flags2 & includeFlags) == flags2 + || (flags3 & includeFlags) == flags3; + } + + var span = _includeFlags.AsSpan(); + + for (var i = 0; i < span.Length; i++) + { + if ((span[i] & includeFlags) == span[i]) + { + return true; + } + } + + return false; + } + + public override string ToString() + { + if (SyntaxNodes[0].Node.Alias is not null) + { + return $"{ResponseName} : {Field.Name}"; + } + + return Field.Name; + } + + internal void SetResolvers( + FieldDelegate? resolverPipeline = null, + PureFieldDelegate? pureResolver = null) + { + if ((_flags & Flags.Sealed) == Flags.Sealed) + { + throw new NotSupportedException(Resources.PreparedSelection_ReadOnly); + } + + ResolverPipeline = resolverPipeline; + PureResolver = pureResolver; + Strategy = InferStrategy(hasPureResolver: pureResolver is not null); + } + + /// + /// Completes the selection without sealing it. + /// + internal void Complete(SelectionSet selectionSet, bool seal) + { + ArgumentNullException.ThrowIfNull(selectionSet); + + if ((_flags & Flags.Sealed) == Flags.Sealed) + { + throw new InvalidOperationException("Selection is already sealed."); + } + + DeclaringSelectionSet = selectionSet; + + if (seal) + { + _flags |= Flags.Sealed; + } + } + + private SelectionExecutionStrategy InferStrategy( + bool isSerial = false, + bool hasPureResolver = false) + { + // once a field is marked serial it even with a pure resolver cannot become pure. + if (Strategy is SelectionExecutionStrategy.Serial || isSerial) + { + return SelectionExecutionStrategy.Serial; + } + + if (hasPureResolver) + { + return SelectionExecutionStrategy.Pure; + } + + return SelectionExecutionStrategy.Default; + } + + [Flags] + private enum Flags + { + None = 0, + Internal = 1, + Sealed = 2, + List = 4, + Stream = 8, + Leaf = 16 + } + + internal sealed class Sealed : Selection + { + public Sealed( + int id, + ObjectType declaringType, + ObjectField field, + IType type, + FieldNode syntaxNode, + string responseName, + ArgumentMap? arguments = null, + ulong[]? includeConditions = null, + bool isInternal = false, + bool isParallelExecutable = true, + FieldDelegate? resolverPipeline = null, + PureFieldDelegate? pureResolver = null) : base( + id, + declaringType, + field, + type, + syntaxNode, + responseName, + arguments, + includeConditions, + isInternal, + isParallelExecutable, + resolverPipeline, + pureResolver) + { + } + } +} + +internal static class Utf8StringCache +{ + private static readonly Encoding s_utf8 = Encoding.UTF8; + private static readonly Cache s_cache = new(capacity: 4 * 1024); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static byte[] GetUtf8String(string s) + => s_cache.GetOrCreate(s, static k => s_utf8.GetBytes(k)); +} diff --git a/src/HotChocolate/Core/src/Execution/Processing/SelectionCollection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionCollection.cs similarity index 98% rename from src/HotChocolate/Core/src/Execution/Processing/SelectionCollection.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/SelectionCollection.cs index 58fb04ac373..6a7370dfe04 100644 --- a/src/HotChocolate/Core/src/Execution/Processing/SelectionCollection.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionCollection.cs @@ -14,7 +14,7 @@ internal sealed class SelectionCollection( Schema schema, IOperation operation, ISelection[] selections, - long includeFlags) + ulong includeFlags) : ISelectionCollection { private readonly Schema _schema = schema ?? throw new ArgumentNullException(nameof(schema)); @@ -119,7 +119,7 @@ public bool IsSelected(string fieldName) static bool IsChildSelected( IOperation operation, - long includeFlags, + ulong includeFlags, ObjectType objectType, ISelection parent, string fieldName) @@ -199,7 +199,7 @@ public bool IsSelected(string fieldName1, string fieldName2) static bool IsChildSelected( IOperation operation, - long includeFlags, + ulong includeFlags, ObjectType objectType, ISelection parent, string fieldName1, @@ -284,7 +284,7 @@ public bool IsSelected(string fieldName1, string fieldName2, string fieldName3) static bool IsChildSelected( IOperation operation, - long includeFlags, + ulong includeFlags, ObjectType objectType, ISelection parent, string fieldName1, @@ -360,7 +360,7 @@ public bool IsSelected(ISet fieldNames) static bool IsChildSelected( IOperation operation, - long includeFlags, + ulong includeFlags, ObjectType objectType, ISelection parent, ISet fieldNames) @@ -481,7 +481,7 @@ private bool CollectSelections( private static void CollectFields( ReadOnlySpan fieldNames, - long includeFlags, + ulong includeFlags, ref ISelection[] buffer, ISelectionSet selectionSet, int index, diff --git a/src/HotChocolate/Core/src/Execution/Processing/SelectionIncludeCondition.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionIncludeCondition.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/SelectionIncludeCondition.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/SelectionIncludeCondition.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/SelectionInclusionKind.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionInclusionKind.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/SelectionInclusionKind.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/SelectionInclusionKind.cs diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionLookup.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionLookup.cs new file mode 100644 index 00000000000..3986a6aab5a --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionLookup.cs @@ -0,0 +1,156 @@ +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace HotChocolate.Execution.Processing; + +internal sealed class SelectionLookup +{ + private readonly Entry[] _table; + private readonly int _seed; + private readonly int _mask; + + private SelectionLookup(Entry[] table, int seed, int mask) + { + _table = table; + _seed = seed; + _mask = mask; + } + + public static SelectionLookup Create(SelectionSet selectionSet) + { + var selections = selectionSet.Selections; + var tableSize = NextPowerOfTwo(Math.Max(selections.Count * 2, 4)); + var mask = tableSize - 1; + var table = new Entry[tableSize]; + + // We try multiple seeds to find one with minimal clustering + var bestSeed = 0; + var bestMaxProbeLength = int.MaxValue; + + for (var seed = 0; seed < 100; seed++) + { + Array.Clear(table); + var maxProbeLength = 0; + + foreach (var selection in selections) + { + var hashCode = ComputeHash(selection.Utf8ResponseName, seed); + var index = hashCode & mask; + var probeLength = 0; + + // Linear probe to find empty slot + while (table[index].Selection != null) + { + index = (index + 1) & mask; + probeLength++; + } + + table[index] = new Entry(hashCode, selection); + maxProbeLength = Math.Max(maxProbeLength, probeLength); + } + + // Track best seed + if (maxProbeLength < bestMaxProbeLength) + { + bestMaxProbeLength = maxProbeLength; + bestSeed = seed; + + // If we found excellent distribution, stop early + if (maxProbeLength <= 2) + { + break; + } + } + } + + // Rebuild table with best seed + Array.Clear(table); + foreach (var selection in selections) + { + var hashCode = ComputeHash(selection.Utf8ResponseName, bestSeed); + var index = hashCode & mask; + + while (table[index].Selection != null) + { + index = (index + 1) & mask; + } + + table[index] = new Entry(hashCode, selection); + } + + return new SelectionLookup(table, bestSeed, mask); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryGetSelection(ReadOnlySpan name, [NotNullWhen(true)] out ISelection? selection) + { + var table = _table.AsSpan(); + + if (table.Length == 0) + { + selection = null!; + return false; + } + + var hashCode = ComputeHash(name, _seed); + var index = hashCode & _mask; + + while (true) + { + ref var entry = ref table[index]; + + // if we hit an empty slot, then there is no selection with the specified name. + if (entry.Selection is null) + { + selection = null; + return false; + } + + if (entry.HashCode == hashCode && name.SequenceEqual(entry.Selection.Utf8ResponseName)) + { + selection = entry.Selection; + return true; + } + + // we had a hash collision need to find the next slot. + index = (index + 1) & _mask; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int ComputeHash(ReadOnlySpan bytes, int seed) + { + var hash = (uint)seed; + + foreach (var b in bytes) + { + hash = hash * 31 + b; + } + + return (int)(hash & 0x7FFFFFFF); + } + + private static int NextPowerOfTwo(int n) + { + if (n <= 0) + { + return 1; + } + + n--; + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + n |= n >> 16; + n++; + + return n; + } + + private readonly struct Entry(int hashCode, ISelection selection) + { + public readonly int HashCode = hashCode; + public readonly ISelection? Selection = selection; + } +} diff --git a/src/HotChocolate/Core/src/Execution/Processing/SelectionPath.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionPath.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/SelectionPath.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/SelectionPath.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/SelectionSet.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSet.cs similarity index 57% rename from src/HotChocolate/Core/src/Execution/Processing/SelectionSet.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSet.cs index cd38b9f6388..6968ad6a7a3 100644 --- a/src/HotChocolate/Core/src/Execution/Processing/SelectionSet.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSet.cs @@ -1,3 +1,4 @@ +using System.Collections.Frozen; using System.Runtime.InteropServices; using System.Text; using HotChocolate.Types; @@ -9,85 +10,74 @@ namespace HotChocolate.Execution.Processing; /// When needed a selection set can preserve fragments so that the execution engine /// can branch the processing of these fragments. /// -internal sealed class SelectionSet : ISelectionSet +public sealed class SelectionSet : ISelectionSet { - private static readonly Fragment[] s_empty = []; private readonly Selection[] _selections; - private readonly Fragment[] _fragments; + private readonly FrozenDictionary _responseNameLookup; + private readonly SelectionLookup _utf8ResponseNameLookup; private Flags _flags; - /// - /// Initializes a new instance of . - /// - /// - /// The selection set unique id. - /// - /// - /// A list of executable field selections. - /// - /// - /// A list of preserved fragments that can be used to branch of - /// some of the execution. - /// - /// - /// Defines if this list needs post processing for skip and include. - /// - public SelectionSet( - int id, - Selection[] selections, - Fragment[]? fragments, - bool isConditional) + public SelectionSet(int id, IObjectTypeDefinition type, Selection[] selections, bool isConditional) { + ArgumentNullException.ThrowIfNull(selections); + + if (selections.Length == 0) + { + throw new ArgumentException("Selections cannot be empty.", nameof(selections)); + } + Id = id; - _selections = selections; - _fragments = fragments ?? s_empty; + Type = type; _flags = isConditional ? Flags.Conditional : Flags.None; + _selections = selections; + _responseNameLookup = _selections.ToFrozenDictionary(t => t.ResponseName); + _utf8ResponseNameLookup = SelectionLookup.Create(this); } - /// + /// + /// Gets an operation unique selection-set identifier of this selection. + /// public int Id { get; } - /// + /// + /// Defines if this list needs post-processing for skip and include. + /// public bool IsConditional => (_flags & Flags.Conditional) == Flags.Conditional; - /// - public IReadOnlyList Selections => _selections; + /// + /// Gets the type that declares this selection set. + /// + public IObjectTypeDefinition Type { get; } - /// - public IReadOnlyList Fragments => _fragments; + /// + /// Gets the declaring operation. + /// + public Operation DeclaringOperation { get; private set; } = null!; - /// - public IOperation DeclaringOperation { get; private set; } = null!; + IOperation ISelectionSet.DeclaringOperation => DeclaringOperation; /// - /// Completes the selection set without sealing it. + /// Gets the selections that shall be executed. /// - internal void Complete(IOperation declaringOperation) - { - if ((_flags & Flags.Sealed) != Flags.Sealed) - { - DeclaringOperation = declaringOperation; - - for (var i = 0; i < _selections.Length; i++) - { - _selections[i].Complete(declaringOperation, this); - } - } - } + public ReadOnlySpan Selections => _selections; - internal void Seal(IOperation declaringOperation) + IEnumerable ISelectionSet.GetSelections() => _selections; + + internal void Complete(Operation declaringOperation, bool seal) { - if ((_flags & Flags.Sealed) != Flags.Sealed) + if ((_flags & Flags.Sealed) == Flags.Sealed) { - DeclaringOperation = declaringOperation; + throw new InvalidOperationException("Selection set is already sealed."); + } - for (var i = 0; i < _selections.Length; i++) - { - _selections[i].Seal(declaringOperation, this); - } + DeclaringOperation = declaringOperation; - _flags |= Flags.Sealed; + foreach (var selection in _selections) + { + selection.Complete(declaringOperation, this); } + + _flags |= Flags.Sealed; } /// diff --git a/src/HotChocolate/Core/src/Execution/Processing/SelectionSetOptimizerContext.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSetOptimizerContext.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/SelectionSetOptimizerContext.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/SelectionSetOptimizerContext.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/SubscriptionExecutor.Subscription.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SubscriptionExecutor.Subscription.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/SubscriptionExecutor.Subscription.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/SubscriptionExecutor.Subscription.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/SubscriptionExecutor.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/SubscriptionExecutor.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/SubscriptionExecutor.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/SubscriptionExecutor.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Tasks/ExecutionTaskPool.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ExecutionTaskPool.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Tasks/ExecutionTaskPool.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ExecutionTaskPool.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Tasks/ExecutionTaskPoolPolicy.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ExecutionTaskPoolPolicy.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Tasks/ExecutionTaskPoolPolicy.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ExecutionTaskPoolPolicy.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Tasks/ResolverTask.CompleteValue.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.CompleteValue.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Tasks/ResolverTask.CompleteValue.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.CompleteValue.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Tasks/ResolverTask.Execute.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.Execute.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Tasks/ResolverTask.Execute.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.Execute.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Tasks/ResolverTask.Pooling.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.Pooling.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Tasks/ResolverTask.Pooling.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.Pooling.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Tasks/ResolverTask.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Tasks/ResolverTask.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTask.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Tasks/ResolverTaskFactory.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTaskFactory.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Tasks/ResolverTaskFactory.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTaskFactory.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Tasks/ResolverTaskPoolPolicy.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTaskPoolPolicy.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Tasks/ResolverTaskPoolPolicy.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/ResolverTaskPoolPolicy.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/Tasks/StreamHelper.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/StreamHelper.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/Tasks/StreamHelper.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/Tasks/StreamHelper.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/ValueCompletion.Leaf.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletion.Leaf.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/ValueCompletion.Leaf.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletion.Leaf.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/ValueCompletion.List.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletion.List.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/ValueCompletion.List.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletion.List.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/ValueCompletion.Object.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletion.Object.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/ValueCompletion.Object.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletion.Object.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/ValueCompletion.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletion.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/ValueCompletion.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletion.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/ValueCompletionContext.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletionContext.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/ValueCompletionContext.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/ValueCompletionContext.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/VariableCoercionHelper.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/VariableCoercionHelper.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/VariableCoercionHelper.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/VariableCoercionHelper.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/VariableRewriter.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/VariableRewriter.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/VariableRewriter.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/VariableRewriter.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/VariableValueCollection.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/VariableValueCollection.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/VariableValueCollection.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/VariableValueCollection.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/VariableValueOrLiteral.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/VariableValueOrLiteral.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/VariableValueOrLiteral.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/VariableValueOrLiteral.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/WorkQueue.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/WorkQueue.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/WorkQueue.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/WorkQueue.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/WorkScheduler.Execute.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/WorkScheduler.Execute.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/WorkScheduler.Execute.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/WorkScheduler.Execute.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/WorkScheduler.Pooling.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/WorkScheduler.Pooling.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/WorkScheduler.Pooling.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/WorkScheduler.Pooling.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/WorkScheduler.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/WorkScheduler.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/WorkScheduler.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/WorkScheduler.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationCompiler.ArgumentValues.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/_OperationCompiler.ArgumentValues.cs similarity index 97% rename from src/HotChocolate/Core/src/Execution/Processing/OperationCompiler.ArgumentValues.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/_OperationCompiler.ArgumentValues.cs index a5c2aaf134a..b614190999a 100644 --- a/src/HotChocolate/Core/src/Execution/Processing/OperationCompiler.ArgumentValues.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/_OperationCompiler.ArgumentValues.cs @@ -149,7 +149,7 @@ internal static FieldDelegate CreateFieldPipeline( pipeline = next; } - // at last we clear the rented lists + // at last, we clear the rented lists processed.Clear(); pipelineComponents.Clear(); @@ -215,8 +215,3 @@ private static void BuildDirectivePipeline( } } } - -internal delegate FieldDelegate CreateFieldPipeline( - Schema schema, - ObjectField field, - FieldNode selection); diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationCompiler.CompilerContext.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/_OperationCompiler.CompilerContext.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Processing/OperationCompiler.CompilerContext.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/_OperationCompiler.CompilerContext.cs diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationCompiler.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/_OperationCompiler.cs similarity index 99% rename from src/HotChocolate/Core/src/Execution/Processing/OperationCompiler.cs rename to src/HotChocolate/Core/src/Types/Execution/Processing/_OperationCompiler.cs index 02446a02fbd..174bc3ca348 100644 --- a/src/HotChocolate/Core/src/Execution/Processing/OperationCompiler.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/_OperationCompiler.cs @@ -370,7 +370,7 @@ private void CollectFields(CompilerContext context) private void CollectFields( CompilerContext context, SelectionSetNode selectionSet, - long includeCondition) + ulong includeCondition) { for (var j = 0; j < selectionSet.Selections.Count; j++) { @@ -411,7 +411,7 @@ private void ResolveFields( private void ResolveField( CompilerContext context, FieldNode selection, - long includeCondition) + ulong includeCondition) { includeCondition = GetSelectionIncludeCondition(selection, includeCondition); @@ -514,7 +514,7 @@ private void ResolveFragment( NamedTypeNode? typeCondition, SelectionSetNode selectionSet, IReadOnlyList directives, - long includeCondition) + ulong includeCondition) { if (typeCondition is null || (context.Schema.Types.TryGetType(typeCondition, out IType? typeCon) @@ -528,7 +528,7 @@ private void ResolveFragment( var nullValue = NullValueNode.Default; var ifValue = deferDirective?.GetArgumentValue(DirectiveNames.Defer.Arguments.If) ?? nullValue; - long ifConditionFlags = 0; + ulong ifConditionFlags = 0; if (ifValue.Kind is not SyntaxKind.NullValue) { @@ -649,9 +649,9 @@ private SelectionVariants GetOrCreateSelectionVariants(int selectionSetId) return variants; } - private long GetSelectionIncludeCondition( + private ulong GetSelectionIncludeCondition( ISelectionNode selectionSyntax, - long parentIncludeCondition) + ulong parentIncludeCondition) { var condition = IncludeCondition.FromSelection(selectionSyntax); @@ -697,7 +697,7 @@ private long GetSelectionIncludeCondition( private long GetSelectionIncludeCondition( IncludeCondition condition, - long parentIncludeCondition) + ulong parentIncludeCondition) { var pos = Array.IndexOf(_includeConditions, condition); diff --git a/src/HotChocolate/Core/src/Execution/RequestExecutorManager.Events.cs b/src/HotChocolate/Core/src/Types/Execution/RequestExecutorManager.Events.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/RequestExecutorManager.Events.cs rename to src/HotChocolate/Core/src/Types/Execution/RequestExecutorManager.Events.cs diff --git a/src/HotChocolate/Core/src/Execution/RequestExecutorManager.Hooks.cs b/src/HotChocolate/Core/src/Types/Execution/RequestExecutorManager.Hooks.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/RequestExecutorManager.Hooks.cs rename to src/HotChocolate/Core/src/Types/Execution/RequestExecutorManager.Hooks.cs diff --git a/src/HotChocolate/Core/src/Execution/RequestExecutorManager.Warmup.cs b/src/HotChocolate/Core/src/Types/Execution/RequestExecutorManager.Warmup.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/RequestExecutorManager.Warmup.cs rename to src/HotChocolate/Core/src/Types/Execution/RequestExecutorManager.Warmup.cs diff --git a/src/HotChocolate/Core/src/Execution/RequestExecutorManager.cs b/src/HotChocolate/Core/src/Types/Execution/RequestExecutorManager.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/RequestExecutorManager.cs rename to src/HotChocolate/Core/src/Types/Execution/RequestExecutorManager.cs diff --git a/src/HotChocolate/Core/src/Execution/Requirements/FieldRequirementsMetadata.cs b/src/HotChocolate/Core/src/Types/Execution/Requirements/FieldRequirementsMetadata.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Requirements/FieldRequirementsMetadata.cs rename to src/HotChocolate/Core/src/Types/Execution/Requirements/FieldRequirementsMetadata.cs diff --git a/src/HotChocolate/Core/src/Execution/Requirements/PropertyNode.cs b/src/HotChocolate/Core/src/Types/Execution/Requirements/PropertyNode.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Requirements/PropertyNode.cs rename to src/HotChocolate/Core/src/Types/Execution/Requirements/PropertyNode.cs diff --git a/src/HotChocolate/Core/src/Execution/Requirements/PropertyTreeBuilder.cs b/src/HotChocolate/Core/src/Types/Execution/Requirements/PropertyTreeBuilder.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Requirements/PropertyTreeBuilder.cs rename to src/HotChocolate/Core/src/Types/Execution/Requirements/PropertyTreeBuilder.cs diff --git a/src/HotChocolate/Core/src/Execution/Requirements/RequirementsTypeInterceptor.cs b/src/HotChocolate/Core/src/Types/Execution/Requirements/RequirementsTypeInterceptor.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Requirements/RequirementsTypeInterceptor.cs rename to src/HotChocolate/Core/src/Types/Execution/Requirements/RequirementsTypeInterceptor.cs diff --git a/src/HotChocolate/Core/src/Execution/Requirements/TypeContainer.cs b/src/HotChocolate/Core/src/Types/Execution/Requirements/TypeContainer.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Requirements/TypeContainer.cs rename to src/HotChocolate/Core/src/Types/Execution/Requirements/TypeContainer.cs diff --git a/src/HotChocolate/Core/src/Execution/Requirements/TypeNode.cs b/src/HotChocolate/Core/src/Types/Execution/Requirements/TypeNode.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Requirements/TypeNode.cs rename to src/HotChocolate/Core/src/Types/Execution/Requirements/TypeNode.cs diff --git a/src/HotChocolate/Core/src/Execution/SchemaName.cs b/src/HotChocolate/Core/src/Types/Execution/SchemaName.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/SchemaName.cs rename to src/HotChocolate/Core/src/Types/Execution/SchemaName.cs diff --git a/src/HotChocolate/Core/src/Execution/ThrowHelper.cs b/src/HotChocolate/Core/src/Types/Execution/ThrowHelper.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/ThrowHelper.cs rename to src/HotChocolate/Core/src/Types/Execution/ThrowHelper.cs diff --git a/src/HotChocolate/Core/src/Execution/Timestamp.cs b/src/HotChocolate/Core/src/Types/Execution/Timestamp.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Timestamp.cs rename to src/HotChocolate/Core/src/Types/Execution/Timestamp.cs diff --git a/src/HotChocolate/Core/src/Fetching/AdHocBatchDataLoader.cs b/src/HotChocolate/Core/src/Types/Fetching/AdHocBatchDataLoader.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/AdHocBatchDataLoader.cs rename to src/HotChocolate/Core/src/Types/Fetching/AdHocBatchDataLoader.cs diff --git a/src/HotChocolate/Core/src/Fetching/AdHocCacheDataLoader.cs b/src/HotChocolate/Core/src/Types/Fetching/AdHocCacheDataLoader.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/AdHocCacheDataLoader.cs rename to src/HotChocolate/Core/src/Types/Fetching/AdHocCacheDataLoader.cs diff --git a/src/HotChocolate/Core/src/Fetching/AdHocGroupedDataLoader.cs b/src/HotChocolate/Core/src/Types/Fetching/AdHocGroupedDataLoader.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/AdHocGroupedDataLoader.cs rename to src/HotChocolate/Core/src/Types/Fetching/AdHocGroupedDataLoader.cs diff --git a/src/HotChocolate/Core/src/Fetching/AsyncAutoResetEvent.cs b/src/HotChocolate/Core/src/Types/Fetching/AsyncAutoResetEvent.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/AsyncAutoResetEvent.cs rename to src/HotChocolate/Core/src/Types/Fetching/AsyncAutoResetEvent.cs diff --git a/src/HotChocolate/Core/src/Fetching/Attributes/UseDataLoaderAttribute.cs b/src/HotChocolate/Core/src/Types/Fetching/Attributes/UseDataLoaderAttribute.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/Attributes/UseDataLoaderAttribute.cs rename to src/HotChocolate/Core/src/Types/Fetching/Attributes/UseDataLoaderAttribute.cs diff --git a/src/HotChocolate/Core/src/Fetching/BatchDispatchEventArgs.cs b/src/HotChocolate/Core/src/Types/Fetching/BatchDispatchEventArgs.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/BatchDispatchEventArgs.cs rename to src/HotChocolate/Core/src/Types/Fetching/BatchDispatchEventArgs.cs diff --git a/src/HotChocolate/Core/src/Fetching/BatchDispatchEventType.cs b/src/HotChocolate/Core/src/Types/Fetching/BatchDispatchEventType.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/BatchDispatchEventType.cs rename to src/HotChocolate/Core/src/Types/Fetching/BatchDispatchEventType.cs diff --git a/src/HotChocolate/Core/src/Fetching/BatchDispatcher.ExecutorSession.cs b/src/HotChocolate/Core/src/Types/Fetching/BatchDispatcher.ExecutorSession.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/BatchDispatcher.ExecutorSession.cs rename to src/HotChocolate/Core/src/Types/Fetching/BatchDispatcher.ExecutorSession.cs diff --git a/src/HotChocolate/Core/src/Fetching/BatchDispatcher.IBatchScheduler.cs b/src/HotChocolate/Core/src/Types/Fetching/BatchDispatcher.IBatchScheduler.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/BatchDispatcher.IBatchScheduler.cs rename to src/HotChocolate/Core/src/Types/Fetching/BatchDispatcher.IBatchScheduler.cs diff --git a/src/HotChocolate/Core/src/Fetching/BatchDispatcher.cs b/src/HotChocolate/Core/src/Types/Fetching/BatchDispatcher.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/BatchDispatcher.cs rename to src/HotChocolate/Core/src/Types/Fetching/BatchDispatcher.cs diff --git a/src/HotChocolate/Core/src/Fetching/BatchDispatcherOptions.cs b/src/HotChocolate/Core/src/Types/Fetching/BatchDispatcherOptions.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/BatchDispatcherOptions.cs rename to src/HotChocolate/Core/src/Types/Fetching/BatchDispatcherOptions.cs diff --git a/src/HotChocolate/Core/src/Fetching/BatchDispatcherResult.cs b/src/HotChocolate/Core/src/Types/Fetching/BatchDispatcherResult.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/BatchDispatcherResult.cs rename to src/HotChocolate/Core/src/Types/Fetching/BatchDispatcherResult.cs diff --git a/src/HotChocolate/Core/src/Fetching/DataLoaderDefaults.cs b/src/HotChocolate/Core/src/Types/Fetching/DataLoaderDefaults.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/DataLoaderDefaults.cs rename to src/HotChocolate/Core/src/Types/Fetching/DataLoaderDefaults.cs diff --git a/src/HotChocolate/Core/src/Fetching/DataLoaderParameterExpressionBuilder.cs b/src/HotChocolate/Core/src/Types/Fetching/DataLoaderParameterExpressionBuilder.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/DataLoaderParameterExpressionBuilder.cs rename to src/HotChocolate/Core/src/Types/Fetching/DataLoaderParameterExpressionBuilder.cs diff --git a/src/HotChocolate/Core/src/Fetching/DataLoaderRootFieldTypeInterceptor.cs b/src/HotChocolate/Core/src/Types/Fetching/DataLoaderRootFieldTypeInterceptor.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/DataLoaderRootFieldTypeInterceptor.cs rename to src/HotChocolate/Core/src/Types/Fetching/DataLoaderRootFieldTypeInterceptor.cs diff --git a/src/HotChocolate/Core/src/Fetching/DataLoaderScopeHolder.cs b/src/HotChocolate/Core/src/Types/Fetching/DataLoaderScopeHolder.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/DataLoaderScopeHolder.cs rename to src/HotChocolate/Core/src/Types/Fetching/DataLoaderScopeHolder.cs diff --git a/src/HotChocolate/Core/src/Fetching/ExecutionDataLoaderScope.cs b/src/HotChocolate/Core/src/Types/Fetching/ExecutionDataLoaderScope.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/ExecutionDataLoaderScope.cs rename to src/HotChocolate/Core/src/Types/Fetching/ExecutionDataLoaderScope.cs diff --git a/src/HotChocolate/Core/src/Fetching/ExecutionDataLoaderScopeFactory.cs b/src/HotChocolate/Core/src/Types/Fetching/ExecutionDataLoaderScopeFactory.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/ExecutionDataLoaderScopeFactory.cs rename to src/HotChocolate/Core/src/Types/Fetching/ExecutionDataLoaderScopeFactory.cs diff --git a/src/HotChocolate/Core/src/Fetching/Extensions/DataLoaderResolverContextExtensions.cs b/src/HotChocolate/Core/src/Types/Fetching/Extensions/DataLoaderResolverContextExtensions.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/Extensions/DataLoaderResolverContextExtensions.cs rename to src/HotChocolate/Core/src/Types/Fetching/Extensions/DataLoaderResolverContextExtensions.cs diff --git a/src/HotChocolate/Core/src/Fetching/Extensions/DataLoaderServiceProviderExtensions.cs b/src/HotChocolate/Core/src/Types/Fetching/Extensions/DataLoaderServiceProviderExtensions.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/Extensions/DataLoaderServiceProviderExtensions.cs rename to src/HotChocolate/Core/src/Types/Fetching/Extensions/DataLoaderServiceProviderExtensions.cs diff --git a/src/HotChocolate/Core/src/Fetching/Extensions/GetDataLoaderAttribute.cs b/src/HotChocolate/Core/src/Types/Fetching/Extensions/GetDataLoaderAttribute.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/Extensions/GetDataLoaderAttribute.cs rename to src/HotChocolate/Core/src/Types/Fetching/Extensions/GetDataLoaderAttribute.cs diff --git a/src/HotChocolate/Core/src/Fetching/Extensions/ObjectFieldDataLoaderExtensions.cs b/src/HotChocolate/Core/src/Types/Fetching/Extensions/ObjectFieldDataLoaderExtensions.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/Extensions/ObjectFieldDataLoaderExtensions.cs rename to src/HotChocolate/Core/src/Types/Fetching/Extensions/ObjectFieldDataLoaderExtensions.cs diff --git a/src/HotChocolate/Core/src/Fetching/FetchBatch.cs b/src/HotChocolate/Core/src/Types/Fetching/FetchBatch.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/FetchBatch.cs rename to src/HotChocolate/Core/src/Types/Fetching/FetchBatch.cs diff --git a/src/HotChocolate/Core/src/Fetching/FetchCache.cs b/src/HotChocolate/Core/src/Types/Fetching/FetchCache.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/FetchCache.cs rename to src/HotChocolate/Core/src/Types/Fetching/FetchCache.cs diff --git a/src/HotChocolate/Core/src/Fetching/FetchGroup.cs b/src/HotChocolate/Core/src/Types/Fetching/FetchGroup.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/FetchGroup.cs rename to src/HotChocolate/Core/src/Types/Fetching/FetchGroup.cs diff --git a/src/HotChocolate/Core/src/Fetching/IBatchDispatcher.cs b/src/HotChocolate/Core/src/Types/Fetching/IBatchDispatcher.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/IBatchDispatcher.cs rename to src/HotChocolate/Core/src/Types/Fetching/IBatchDispatcher.cs diff --git a/src/HotChocolate/Core/src/Fetching/IDataLoaderScopeFactory.cs b/src/HotChocolate/Core/src/Types/Fetching/IDataLoaderScopeFactory.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/IDataLoaderScopeFactory.cs rename to src/HotChocolate/Core/src/Types/Fetching/IDataLoaderScopeFactory.cs diff --git a/src/HotChocolate/Core/src/Fetching/RegisterDataLoaderException.cs b/src/HotChocolate/Core/src/Types/Fetching/RegisterDataLoaderException.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/RegisterDataLoaderException.cs rename to src/HotChocolate/Core/src/Types/Fetching/RegisterDataLoaderException.cs diff --git a/src/HotChocolate/Core/src/Fetching/SkipDataLoaderCacheAttribute.cs b/src/HotChocolate/Core/src/Types/Fetching/SkipDataLoaderCacheAttribute.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/SkipDataLoaderCacheAttribute.cs rename to src/HotChocolate/Core/src/Types/Fetching/SkipDataLoaderCacheAttribute.cs diff --git a/src/HotChocolate/Core/src/Fetching/Utilities/ThrowHelper.cs b/src/HotChocolate/Core/src/Types/Fetching/Utilities/ThrowHelper.cs similarity index 100% rename from src/HotChocolate/Core/src/Fetching/Utilities/ThrowHelper.cs rename to src/HotChocolate/Core/src/Types/Fetching/Utilities/ThrowHelper.cs diff --git a/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj b/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj index c97a2503d2a..08a474f7f21 100644 --- a/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj +++ b/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj @@ -34,7 +34,6 @@ - @@ -54,18 +53,124 @@ + + + + + + + + + + + + + + + - + + + + + Text\Json\JsonConstants.cs + + + + Text\Json\JsonHelpers.cs + + + + Text\Json\JsonReaderHelper.cs + + + + Text\Json\MetaDbEventSource.cs + + + + Text\Json\ThrowHelper.cs + + + + ResultDocument.cs + + + + ResultDocument.cs + + + + ResultDocument.cs + + + + ResultDocument.cs + + + + ResultDocument.cs + + + + ResultDocument.cs + + + + ResultDocument.cs + + + + ResultElement.cs + + + + ResultElement.cs + + + + True + True + TextJsonResources.resx + + + + MiddlewareContext.Global.cs + + + + MiddlewareContext.Global.cs + + + + MiddlewareContext.Global.cs + + + + MiddlewareContext.Global.cs + + + + MiddlewareContext.Global.cs + + + + OperationCompiler.cs + + + + OperationCompiler.cs + @@ -135,21 +240,38 @@ DescriptorContext.cs - - True - True - TypeResources.resx - InterfaceType.cs + + True + True + TypeResources.resx + + ResXFileCodeGenerator TypeResources.Designer.cs + + + True + True + Resources.resx + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + ResXFileCodeGenerator + TextJsonResources.Designer.cs + diff --git a/src/HotChocolate/Core/src/Types/Internal/IParameterExpressionBuilder.cs b/src/HotChocolate/Core/src/Types/Internal/IParameterExpressionBuilder.cs index 432f3ce243b..8c0e873c448 100644 --- a/src/HotChocolate/Core/src/Types/Internal/IParameterExpressionBuilder.cs +++ b/src/HotChocolate/Core/src/Types/Internal/IParameterExpressionBuilder.cs @@ -4,7 +4,7 @@ namespace HotChocolate.Internal; /// -/// This interface represents an expression builder to resolver resolver parameter values. +/// This interface represents an expression builder to resolver parameter values. /// public interface IParameterExpressionBuilder { diff --git a/src/HotChocolate/Core/src/Fetching/Properties/FetchingResources.Designer.cs b/src/HotChocolate/Core/src/Types/Properties/FetchingResources.Designer.cs similarity index 96% rename from src/HotChocolate/Core/src/Fetching/Properties/FetchingResources.Designer.cs rename to src/HotChocolate/Core/src/Types/Properties/FetchingResources.Designer.cs index ce30a8ad324..83d091f7900 100644 --- a/src/HotChocolate/Core/src/Fetching/Properties/FetchingResources.Designer.cs +++ b/src/HotChocolate/Core/src/Types/Properties/FetchingResources.Designer.cs @@ -9,21 +9,21 @@ namespace HotChocolate.Fetching.Properties { using System; - - + + [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [System.Diagnostics.DebuggerNonUserCodeAttribute()] [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class FetchingResources { - + private static System.Resources.ResourceManager resourceMan; - + private static System.Globalization.CultureInfo resourceCulture; - + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal FetchingResources() { } - + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] internal static System.Resources.ResourceManager ResourceManager { get { @@ -34,7 +34,7 @@ internal static System.Resources.ResourceManager ResourceManager { return resourceMan; } } - + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] internal static System.Globalization.CultureInfo Culture { get { @@ -44,49 +44,49 @@ internal static System.Globalization.CultureInfo Culture { resourceCulture = value; } } - + internal static string DataLoaderRegistry_KeyNullOrEmpty { get { return ResourceManager.GetString("DataLoaderRegistry_KeyNullOrEmpty", resourceCulture); } } - + internal static string DefaultDataLoaderRegistry_GetOrRegister { get { return ResourceManager.GetString("DefaultDataLoaderRegistry_GetOrRegister", resourceCulture); } } - + internal static string DataLoaderResolverContextExtensions_CreateDataLoader_AbstractType { get { return ResourceManager.GetString("DataLoaderResolverContextExtensions_CreateDataLoader_AbstractType", resourceCulture); } } - + internal static string DataLoaderResolverContextExtensions_CreateDataLoader_UnableToCreate { get { return ResourceManager.GetString("DataLoaderResolverContextExtensions_CreateDataLoader_UnableToCreate", resourceCulture); } } - + internal static string DataLoaderResolverContextExtensions_RegistryIsNull { get { return ResourceManager.GetString("DataLoaderResolverContextExtensions_RegistryIsNull", resourceCulture); } } - + internal static string DataLoaderResolverContextExtensions_UnableToRegister { get { return ResourceManager.GetString("DataLoaderResolverContextExtensions_UnableToRegister", resourceCulture); } } - + internal static string ThrowHelper_DataLoader_InvalidType { get { return ResourceManager.GetString("ThrowHelper_DataLoader_InvalidType", resourceCulture); } } - + internal static string BatchDispatcherResult_NoExceptions { get { return ResourceManager.GetString("BatchDispatcherResult_NoExceptions", resourceCulture); diff --git a/src/HotChocolate/Core/src/Fetching/Properties/FetchingResources.resx b/src/HotChocolate/Core/src/Types/Properties/FetchingResources.resx similarity index 100% rename from src/HotChocolate/Core/src/Fetching/Properties/FetchingResources.resx rename to src/HotChocolate/Core/src/Types/Properties/FetchingResources.resx diff --git a/src/HotChocolate/Core/src/Execution/Properties/Resources.Designer.cs b/src/HotChocolate/Core/src/Types/Properties/Resources.Designer.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Properties/Resources.Designer.cs rename to src/HotChocolate/Core/src/Types/Properties/Resources.Designer.cs diff --git a/src/HotChocolate/Core/src/Execution/Properties/Resources.resx b/src/HotChocolate/Core/src/Types/Properties/Resources.resx similarity index 100% rename from src/HotChocolate/Core/src/Execution/Properties/Resources.resx rename to src/HotChocolate/Core/src/Types/Properties/Resources.resx diff --git a/src/HotChocolate/Core/src/Types/Properties/TextJsonResources.Designer.cs b/src/HotChocolate/Core/src/Types/Properties/TextJsonResources.Designer.cs new file mode 100644 index 00000000000..872b64ad013 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Properties/TextJsonResources.Designer.cs @@ -0,0 +1,90 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace HotChocolate.Properties { + using System; + + + [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [System.Diagnostics.DebuggerNonUserCodeAttribute()] + [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class TextJsonResources { + + private static System.Resources.ResourceManager resourceMan; + + private static System.Globalization.CultureInfo resourceCulture; + + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal TextJsonResources() { + } + + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + internal static System.Resources.ResourceManager ResourceManager { + get { + if (object.Equals(null, resourceMan)) { + System.Resources.ResourceManager temp = new System.Resources.ResourceManager("HotChocolate.Properties.TextJsonResources", typeof(TextJsonResources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + internal static System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + internal static string ResultElement_GetBoolean_JsonElementHasWrongType { + get { + return ResourceManager.GetString("ResultElement_GetBoolean_JsonElementHasWrongType", resourceCulture); + } + } + + internal static string Rethrowable { + get { + return ResourceManager.GetString("Rethrowable", resourceCulture); + } + } + + internal static string ResultElement_SetObjectValue_NotObjectType { + get { + return ResourceManager.GetString("ResultElement_SetObjectValue_NotObjectType", resourceCulture); + } + } + + internal static string JsonReaderHelper_TranscodeHelper_CannotTranscodeInvalidUtf8 { + get { + return ResourceManager.GetString("JsonReaderHelper_TranscodeHelper_CannotTranscodeInvalidUtf8", resourceCulture); + } + } + + internal static string ThrowHelper_ReadInvalidUTF16 { + get { + return ResourceManager.GetString("ThrowHelper_ReadInvalidUTF16", resourceCulture); + } + } + + internal static string ThrowHelper_ReadIncompleteUTF16 { + get { + return ResourceManager.GetString("ThrowHelper_ReadIncompleteUTF16", resourceCulture); + } + } + + internal static string ResultElement_SetArrayValue_NotListType { + get { + return ResourceManager.GetString("ResultElement_SetArrayValue_NotListType", resourceCulture); + } + } + } +} diff --git a/src/HotChocolate/Core/src/Types/Properties/TextJsonResources.resx b/src/HotChocolate/Core/src/Types/Properties/TextJsonResources.resx new file mode 100644 index 00000000000..e98294406cd --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Properties/TextJsonResources.resx @@ -0,0 +1,42 @@ + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The requested operation requires an element of type '{0}', but the target element has type '{1}'. + + + HotChocolate.Text.Json.Rethrowable + + + The element is not a composite type. + + + Cannot transcode invalid UTF-8 JSON text to UTF-16 string. + + + Cannot read invalid UTF-16 JSON text as string. Invalid surrogate value: '{0}'. + + + Cannot read incomplete UTF-16 JSON text as string with missing low surrogate. + + + The location type `{0}` is not a list. + + diff --git a/src/HotChocolate/Core/src/Types/Resolvers/IMiddlewareContext.cs b/src/HotChocolate/Core/src/Types/Resolvers/IMiddlewareContext.cs index 0c79f8c852f..3ad1043b3b4 100644 --- a/src/HotChocolate/Core/src/Types/Resolvers/IMiddlewareContext.cs +++ b/src/HotChocolate/Core/src/Types/Resolvers/IMiddlewareContext.cs @@ -33,7 +33,7 @@ public interface IMiddlewareContext : IResolverContext /// Executes the field resolver and returns its result. /// /// - /// The type to which the result shall be casted. + /// The type to which the result shall be cast. /// /// /// Returns the resolved field value. diff --git a/src/HotChocolate/Core/src/Types/Resolvers/IResolverContext.cs b/src/HotChocolate/Core/src/Types/Resolvers/IResolverContext.cs index f3cdd188068..5eef2e5429d 100644 --- a/src/HotChocolate/Core/src/Types/Resolvers/IResolverContext.cs +++ b/src/HotChocolate/Core/src/Types/Resolvers/IResolverContext.cs @@ -26,13 +26,13 @@ public interface IResolverContext : IHasContextData, IFeatureProvider /// /// Gets the operation from the query that is being executed. /// - IOperation Operation { get; } + Operation Operation { get; } /// /// Gets the field selection for which a field resolver is /// being executed. /// - ISelection Selection { get; } + Selection Selection { get; } /// /// Gets access to the coerced variable values of the request. @@ -48,7 +48,7 @@ public interface IResolverContext : IHasContextData, IFeatureProvider /// Gets the previous (parent) resolver result. /// /// - /// The type to which the result shall be casted. + /// The type to which the result shall be cast. /// /// /// Returns the previous (parent) resolver result. @@ -62,7 +62,7 @@ public interface IResolverContext : IHasContextData, IFeatureProvider /// The argument name. /// /// - /// The type to which the argument shall be casted to. + /// The type to which the argument shall be cast to. /// /// /// Returns the value of the specified field argument as literal. @@ -76,7 +76,7 @@ public interface IResolverContext : IHasContextData, IFeatureProvider /// The argument name. /// /// - /// The type to which the argument shall be casted to. + /// The type to which the argument shall be cast to. /// /// /// Returns the value of the specified field argument as literal. @@ -90,7 +90,7 @@ public interface IResolverContext : IHasContextData, IFeatureProvider /// The argument name. /// /// - /// The type to which the argument shall be casted to. + /// The type to which the argument shall be cast to. /// /// /// Returns the value of the specified field argument as optional. @@ -182,7 +182,7 @@ public interface IResolverContext : IHasContextData, IFeatureProvider /// /// Notifies when the connection underlying this request is aborted - /// and thus request operations should be cancelled. + /// and thus request operations should be canceled. /// CancellationToken RequestAborted { get; } @@ -249,9 +249,9 @@ public interface IResolverContext : IHasContextData, IFeatureProvider /// Returns the pre-compiled selections for the /// with the specified . /// - IReadOnlyList GetSelections( + SelectionEnumerator GetSelections( ObjectType typeContext, - ISelection? selection = null, + Selection? selection = null, bool allowInternals = false); /// diff --git a/src/HotChocolate/Core/src/Types/Resolvers/ISelectionCollection.cs b/src/HotChocolate/Core/src/Types/Resolvers/ISelectionCollection.cs index 569871e746b..27de07f4665 100644 --- a/src/HotChocolate/Core/src/Types/Resolvers/ISelectionCollection.cs +++ b/src/HotChocolate/Core/src/Types/Resolvers/ISelectionCollection.cs @@ -1,4 +1,4 @@ -using HotChocolate.Execution.Processing; +using HotChocolate.Execution; using HotChocolate.Types; namespace HotChocolate.Resolvers; @@ -37,14 +37,14 @@ public interface ISelectionCollection : IReadOnlyList ISelectionCollection Select(ReadOnlySpan fieldNames); /// - /// Selects all selections where the typeContext is assignable from the field`s declaring type. + /// Selects all selections where the typeContext is assignable from the field's declaring type. /// /// /// The type context to select. /// /// /// Returns a containing - /// the selections where the typeContext is assignable from the field`s declaring type. + /// the selections where the typeContext is assignable from the field's declaring type. /// ISelectionCollection Select(ITypeDefinition typeContext); diff --git a/src/HotChocolate/Core/src/Types/Resolvers/SelectionEnumerator.cs b/src/HotChocolate/Core/src/Types/Resolvers/SelectionEnumerator.cs new file mode 100644 index 00000000000..aed4577252b --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Resolvers/SelectionEnumerator.cs @@ -0,0 +1,77 @@ +using System.Collections; +using HotChocolate.Execution.Processing; + +namespace HotChocolate.Resolvers; + +/// +/// An SelectionSet enumerator. +/// +public struct SelectionEnumerator : IEnumerable, IEnumerator +{ + private readonly SelectionSet _selectionSet; + private readonly ulong _includeFlags; + private int _position = -1; + + internal SelectionEnumerator(SelectionSet selectionSet, ulong includeFlags) + { + _selectionSet = selectionSet; + _includeFlags = includeFlags; + Current = null!; + } + + /// + /// The currently selected selection. + /// + public Selection Current { get; private set; } + + object? IEnumerator.Current => Current; + + /// + /// Moves to the next visible selection. + /// + /// + /// true if there is another visible selection. + /// + public bool MoveNext() + { + if (_selectionSet is null) + { + return false; + } + + var length = _selectionSet.Selections.Length; + + while (_position < length) + { + _position++; + + if (_position >= length) + { + break; + } + + var selection = _selectionSet.Selections[_position]; + if (selection.IsIncluded(_includeFlags)) + { + Current = selection; + return true; + } + } + + Current = null!; + return false; + } + + public void Reset() + { + _position = -1; + } + + public SelectionEnumerator GetEnumerator() => this; + + IEnumerator IEnumerable.GetEnumerator() => this; + + IEnumerator IEnumerable.GetEnumerator() => this; + + public void Dispose() { } +} diff --git a/src/HotChocolate/Core/src/Types/SchemaBuilder.Setup.cs b/src/HotChocolate/Core/src/Types/SchemaBuilder.Setup.cs index c274967f213..de9f72c32a7 100644 --- a/src/HotChocolate/Core/src/Types/SchemaBuilder.Setup.cs +++ b/src/HotChocolate/Core/src/Types/SchemaBuilder.Setup.cs @@ -12,7 +12,6 @@ using HotChocolate.Types.Pagination; using HotChocolate.Types.Relay; using HotChocolate.Utilities; -using HotChocolate.Utilities.Introspection; using Microsoft.Extensions.DependencyInjection.Extensions; namespace HotChocolate; diff --git a/src/HotChocolate/Core/src/Types/SchemaPrinter.cs b/src/HotChocolate/Core/src/Types/SchemaPrinter.cs index c0e50daa958..4e4449efb4d 100644 --- a/src/HotChocolate/Core/src/Types/SchemaPrinter.cs +++ b/src/HotChocolate/Core/src/Types/SchemaPrinter.cs @@ -4,7 +4,6 @@ using HotChocolate.Language.Utilities; using HotChocolate.Types; using HotChocolate.Utilities; -using HotChocolate.Utilities.Introspection; namespace HotChocolate; 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..5e613d3f224 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.DbRow.cs @@ -0,0 +1,158 @@ +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; + + // 27 bits for size/length + sign bit for HasComplexChildren + 4 reserved bits + private readonly int _sizeOrLengthUnion; + + // 27 bits NumberOfRows + 1 reserved bit + 4 bits TokenType + private readonly int _tokenTypeAndNumberOfRows; + + // 27 bits ParentRow + 5 reserved bits + private readonly int _parentRow; + + // 15 bits OperationReferenceId + 8 bits Flags + 9 reserved bits + private readonly int _opRefIdAndFlags; + + public DbRow( + ElementTokenType tokenType, + int location, + int sizeOrLength = 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 || sizeOrLength is >= 0 and <= 0x07FFFFFF); // 27 bits + Debug.Assert(parentRow is >= 0 and <= 0x07FFFFFF); // 27 bits + Debug.Assert(operationReferenceId is >= 0 and <= 0x7FFF); // 15 bits + Debug.Assert(numberOfRows is >= 0 and <= 0x07FFFFFF); // 27 bits + Debug.Assert((byte)flags <= 255); // 8 bits (0xFF) + Debug.Assert((byte)operationReferenceType <= 3); // 2 bits + Debug.Assert(Unsafe.SizeOf() == Size); + + _locationAndOpRefType = location | ((int)operationReferenceType << 27); + _sizeOrLengthUnion = sizeOrLength; + _tokenTypeAndNumberOfRows = ((int)tokenType << 28) | (numberOfRows & 0x07FFFFFF); + _parentRow = parentRow; + _opRefIdAndFlags = operationReferenceId | ((int)flags << 15); + } + + /// + /// Element token type (includes Reference for composition). + /// + /// + /// 4 bits = possible values + /// + public ElementTokenType TokenType => (ElementTokenType)(unchecked((uint)_tokenTypeAndNumberOfRows) >> 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. + /// + /// + /// 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. + /// + /// + /// 27 bits = 134M limit + /// + public int SizeOrLength => _sizeOrLengthUnion & 0x07FFFFFF; + + /// + /// 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 => _tokenTypeAndNumberOfRows & 0x07FFFFFF; + + /// + /// Index of parent element in metadb for navigation and null propagation. + /// + /// + /// 27 bits = 134M rows + /// + public int ParentRow => _parentRow & 0x07FFFFFF; + + /// + /// Reference to GraphQL selection set or selection metadata. + /// 15 bits = 32K selections + /// + /// + /// 15 bits = 32K selections + /// + public int OperationReferenceId => _opRefIdAndFlags & 0x7FFF; + + /// + /// Element metadata flags. + /// + /// + /// 8 bits = 256 combinations + /// + public ElementFlags Flags => (ElementFlags)((_opRefIdAndFlags >> 15) & 0xFF); + + /// + /// 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, + IsRoot = 1, + IsObject = 2, + IsList = 4, + IsInternal = 8, + IsExcluded = 16, + IsNullable = 32, + IsInvalidated = 64 + } +} diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.MetaDb.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.MetaDb.cs new file mode 100644 index 00000000000..af81d3cc647 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.MetaDb.cs @@ -0,0 +1,369 @@ +using System.Buffers; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using HotChocolate.Buffers; +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] = JsonMemory.Rent(JsonMemoryKind.Metadata); + 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 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] = JsonMemory.Rent(JsonMemoryKind.Metadata); + log.ChunkAllocated(2, chunkIndex); + } + + var row = new DbRow( + tokenType, + location, + sizeOrLength, + 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 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, + 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 + 12); + return MemoryMarshal.Read(span) & 0x07FFFFFF; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal Cursor GetParentCursor(Cursor cursor) + { + AssertValidCursor(cursor); + + var span = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset + 12); + var index = MemoryMarshal.Read(span) & 0x07FFFFFF; + 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 & 0x07FFFFFF; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ElementFlags GetFlags(Cursor cursor) + { + AssertValidCursor(cursor); + + var span = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset + 16); + + var value = MemoryMarshal.Read(span); + return (ElementFlags)((value >> 15) & 0xFF); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void SetFlags(Cursor cursor, ElementFlags flags) + { + AssertValidCursor(cursor); + Debug.Assert((byte)flags <= 255, "Flags value exceeds 8-bit limit"); + + var fieldSpan = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset + 16); + var currentValue = MemoryMarshal.Read(fieldSpan); + + var clearedValue = currentValue & unchecked((int)0xFF807FFF); // ~(0xFF << 15) + var newValue = clearedValue | ((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 & 0x07FFFFFF; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void SetSizeOrLength(Cursor cursor, int sizeOrLength) + { + AssertValidCursor(cursor); + Debug.Assert(sizeOrLength >= 0 && sizeOrLength <= 0x07FFFFFF, "SizeOrLength value exceeds 27-bit limit"); + + var fieldSpan = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset + 4); + var currentValue = MemoryMarshal.Read(fieldSpan); + + // Keep only the sign bit (HasComplexChildren) + 4 reserved bits + var clearedValue = currentValue & unchecked((int)0xF8000000); + var newValue = clearedValue | (sizeOrLength & 0x07FFFFFF); + + MemoryMarshal.Write(fieldSpan, newValue); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void SetNumberOfRows(Cursor cursor, int numberOfRows) + { + AssertValidCursor(cursor); + Debug.Assert(numberOfRows >= 0 && numberOfRows <= 0x07FFFFFF, "NumberOfRows value exceeds 27-bit limit"); + + var fieldSpan = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset + TokenTypeOffset); + var currentValue = MemoryMarshal.Read(fieldSpan); + + // Keep only the top 5 bits (4 bits token type + 1 reserved) + var clearedValue = currentValue & unchecked((int)0xF8000000); + var newValue = clearedValue | (numberOfRows & 0x07FFFFFF); + + 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 <= JsonMemory.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; + } + + JsonMemory.Return(JsonMemoryKind.Metadata, chunk); + } + + chunks.Clear(); + s_arrayPool.Return(_chunks); + + _chunks = []; + _disposed = true; + } + } + } +} diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.Text.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.Text.cs new file mode 100644 index 00000000000..510e4dc3d01 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.Text.cs @@ -0,0 +1,205 @@ +using System.Buffers; +using System.Diagnostics; +using System.Text.Unicode; + +namespace HotChocolate.Text.Json; + +public sealed partial class ResultDocument +{ + internal string? GetString(Cursor cursor, ElementTokenType expectedType) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var row = _metaDb.Get(cursor); + var tokenType = row.TokenType; + + if (tokenType == ElementTokenType.Null) + { + return null; + } + + CheckExpectedType(expectedType, tokenType); + + var segment = ReadRawValue(row); + + if (tokenType is ElementTokenType.String) + { + segment = segment[1..^1]; + } + + return row.HasComplexChildren + ? JsonReaderHelper.GetUnescapedString(segment) + : JsonReaderHelper.TranscodeHelper(segment); + } + + internal string GetRequiredString(Cursor cursor, ElementTokenType expectedType) + { + var value = GetString(cursor, expectedType); + + if (value is null) + { + throw new InvalidOperationException("The element value is null."); + } + + return value; + } + + internal string GetNameOfPropertyValue(Cursor valueCursor) + { + // The property name is one row before the property value + return GetString(valueCursor + (-1), ElementTokenType.PropertyName)!; + } + + internal ReadOnlySpan GetPropertyNameRaw(Cursor valueCursor) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + // The property name is stored one row before the value + var nameCursor = valueCursor + (-1); + var row = _metaDb.Get(nameCursor); + Debug.Assert(row.TokenType is ElementTokenType.PropertyName); + + return ReadRawValue(row); + } + + internal string GetRawValueAsString(Cursor cursor) + { + var segment = GetRawValue(cursor, includeQuotes: true); + return JsonReaderHelper.TranscodeHelper(segment); + } + + internal ReadOnlySpan GetRawValue(Cursor cursor, bool includeQuotes) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var row = _metaDb.Get(cursor); + + if (row.IsSimpleValue) + { + if (!includeQuotes && row.TokenType == ElementTokenType.String) + { + // Skip opening/closing quotes + return ReadRawValue(row)[1..^1]; + } + + return ReadRawValue(row); + } + + // TODO: this is more complex with the new design, we gonna tackle this later. + // var endCursor = GetEndCursor(cursor, includeEndElement: false); + // var start = row.Location; + // var endRow = _metaDb.Get(endCursor); + // return _utf8Json.Slice(start, endRow.Location - start + endRow.SizeOrLength); + throw new NotImplementedException(); + } + + internal string GetPropertyRawValueAsString(Cursor valueCursor) + { + var segment = GetPropertyRawValue(valueCursor); + return JsonReaderHelper.TranscodeHelper(segment); + } + + private ReadOnlySpan GetPropertyRawValue(Cursor valueCursor) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + // The property name is stored one row before the value + Debug.Assert(_metaDb.GetElementTokenType(valueCursor - 1) == ElementTokenType.PropertyName); + + var row = _metaDb.Get(valueCursor); + + if (row.IsSimpleValue) + { + return ReadRawValue(row); + } + + // var endCursor = GetEndCursor(valueCursor, includeEndElement: false); + // var endRow = _metaDb.Get(endCursor); + // return _utf8Json.Slice(start, end - start); + throw new NotSupportedException("Properties are expected to be simple values."); + } + + internal bool TextEquals(Cursor cursor, ReadOnlySpan otherText, bool isPropertyName) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + byte[]? otherUtf8TextArray = null; + + var length = checked(otherText.Length * JsonConstants.MaxExpansionFactorWhileTranscoding); + var otherUtf8Text = length <= JsonConstants.StackallocByteThreshold + ? stackalloc byte[JsonConstants.StackallocByteThreshold] + : (otherUtf8TextArray = ArrayPool.Shared.Rent(length)); + + var status = Utf8.FromUtf16( + otherText, otherUtf8Text, out var charsRead, out var written, + replaceInvalidSequences: false, isFinalBlock: true); + + Debug.Assert(status is OperationStatus.Done or + OperationStatus.DestinationTooSmall or + OperationStatus.InvalidData); + Debug.Assert(charsRead == otherText.Length || status is not OperationStatus.Done); + + bool result; + if (status == OperationStatus.InvalidData) + { + result = false; + } + else + { + Debug.Assert(status == OperationStatus.Done); + result = TextEquals(cursor, otherUtf8Text[..written], isPropertyName, shouldUnescape: true); + } + + if (otherUtf8TextArray != null) + { + otherUtf8Text[..written].Clear(); + ArrayPool.Shared.Return(otherUtf8TextArray); + } + + return result; + } + + internal bool TextEquals(Cursor cursor, ReadOnlySpan otherUtf8Text, bool isPropertyName, bool shouldUnescape) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var matchCursor = isPropertyName ? cursor + (-1) : cursor; + var row = _metaDb.Get(matchCursor); + + CheckExpectedType( + isPropertyName ? ElementTokenType.PropertyName : ElementTokenType.String, + row.TokenType); + + var segment = ReadRawValue(row); + + if (!isPropertyName) + { + segment = segment[1..^1]; + } + + if (otherUtf8Text.Length > segment.Length || (!shouldUnescape && otherUtf8Text.Length != segment.Length)) + { + return false; + } + + if (row.HasComplexChildren && shouldUnescape) + { + if (otherUtf8Text.Length < segment.Length / JsonConstants.MaxExpansionFactorWhileEscaping) + { + return false; + } + + var idx = segment.IndexOf(JsonConstants.BackSlash); + Debug.Assert(idx != -1); + + if (!otherUtf8Text.StartsWith(segment[..idx])) + { + return false; + } + + return JsonReaderHelper.UnescapeAndCompare(segment[idx..], otherUtf8Text[idx..]); + } + + return segment.SequenceEqual(otherUtf8Text); + } +} diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.TryGetProperty.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.TryGetProperty.cs new file mode 100644 index 00000000000..01b41ac815e --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.TryGetProperty.cs @@ -0,0 +1,251 @@ +using System.Buffers; +using System.Diagnostics; + +namespace HotChocolate.Text.Json; + +public sealed partial class ResultDocument +{ + internal bool TryGetNamedPropertyValue( + Cursor startCursor, + string propertyName, + out ResultElement value) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + (startCursor, var tokenType) = _metaDb.GetStartCursor(startCursor); + CheckExpectedType(ElementTokenType.StartObject, tokenType); + + var numberOfRows = _metaDb.GetNumberOfRows(startCursor); + + // Only one row means it was EndObject. + if (numberOfRows == 1) + { + value = default; + return false; + } + + var row = _metaDb.Get(startCursor); + if (row.OperationReferenceType is OperationReferenceType.SelectionSet) + { + var selectionSet = _operation.GetSelectionSetById(row.OperationReferenceId); + if (selectionSet.TryGetSelection(propertyName, out var selection)) + { + var propertyIndex = selection.Id - selectionSet.Id - 1; + var propertyRowIndex = (propertyIndex * 2) + 1; + var propertyCursor = startCursor + propertyRowIndex; + Debug.Assert(_metaDb.GetElementTokenType(propertyCursor) is ElementTokenType.PropertyName); + Debug.Assert(_metaDb.Get(propertyCursor).OperationReferenceId == selection.Id); + value = new ResultElement(this, propertyCursor + 1); + return true; + } + } + + var maxBytes = s_utf8Encoding.GetMaxByteCount(propertyName.Length); + var endCursor = startCursor + (numberOfRows - 1); + + if (maxBytes < JsonConstants.StackallocByteThreshold) + { + Span utf8Name = stackalloc byte[JsonConstants.StackallocByteThreshold]; + var len = s_utf8Encoding.GetBytes(propertyName, utf8Name); + utf8Name = utf8Name[..len]; + + return TryGetNamedPropertyValue( + startCursor + 1, + endCursor, + utf8Name, + out value); + } + + // Unescaping the property name will make the string shorter (or the same) + // So the first viable candidate is one whose length in bytes matches, or + // exceeds, our length in chars. + // + // The maximal escaping seems to be 6 -> 1 ("\u0030" => "0"), but just transcode + // and switch once one viable long property is found. + + var minBytes = propertyName.Length; + var candidate = endCursor; + + while (candidate > startCursor) + { + var passed = candidate; + + row = _metaDb.Get(candidate); + Debug.Assert(row.TokenType != ElementTokenType.PropertyName); + + candidate--; + row = _metaDb.Get(candidate); + Debug.Assert(row.TokenType == ElementTokenType.PropertyName); + + if (row.SizeOrLength >= minBytes) + { + var tmpUtf8 = ArrayPool.Shared.Rent(maxBytes); + Span utf8Name = default; + + try + { + var len = s_utf8Encoding.GetBytes(propertyName, tmpUtf8); + utf8Name = tmpUtf8.AsSpan(0, len); + + return TryGetNamedPropertyValue( + startCursor, + passed + 1, + utf8Name, + out value); + } + finally + { + // While property names aren't usually a secret, they also usually + // aren't long enough to end up in the rented buffer transcode path. + // + // On the basis that this is user data, go ahead and clear it. + utf8Name.Clear(); + ArrayPool.Shared.Return(tmpUtf8); + } + } + + // Move to the previous value + candidate--; + } + + // None of the property names were within the range that the UTF-8 encoding would have been. + value = default; + return false; + } + + internal bool TryGetNamedPropertyValue( + Cursor startCursor, + ReadOnlySpan propertyName, + out ResultElement value) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + (startCursor, var tokenType) = _metaDb.GetStartCursor(startCursor); + CheckExpectedType(ElementTokenType.StartObject, tokenType); + + var numberOfRows = _metaDb.GetNumberOfRows(startCursor); + + // Only one row means it was EndObject. + if (numberOfRows == 1) + { + value = default; + return false; + } + + var row = _metaDb.Get(startCursor); + if (row.OperationReferenceType is OperationReferenceType.SelectionSet) + { + var selectionSet = _operation.GetSelectionSetById(row.OperationReferenceId); + if (selectionSet.TryGetSelection(propertyName, out var selection)) + { + var propertyIndex = selection.Id - selectionSet.Id - 1; + var propertyRowIndex = (propertyIndex * 2) + 1; + var propertyCursor = startCursor + propertyRowIndex; + Debug.Assert(_metaDb.GetElementTokenType(propertyCursor) is ElementTokenType.PropertyName); + Debug.Assert(_metaDb.Get(propertyCursor).OperationReferenceId == selection.Id); + value = new ResultElement(this, propertyCursor + 1); + return true; + } + } + + var endCursor = startCursor + (numberOfRows - 1); + + return TryGetNamedPropertyValue( + startCursor + 1, + endCursor, + propertyName, + out value); + } + + private bool TryGetNamedPropertyValue( + Cursor startCursor, + Cursor endCursor, + ReadOnlySpan propertyName, + out ResultElement value) + { + Span utf8UnescapedStack = stackalloc byte[JsonConstants.StackallocByteThreshold]; + var cursor = endCursor; + + while (cursor > startCursor) + { + var row = _metaDb.Get(cursor); + Debug.Assert(row.TokenType != ElementTokenType.PropertyName); + cursor--; + + row = _metaDb.Get(cursor); + Debug.Assert(row.TokenType == ElementTokenType.PropertyName); + var currentPropertyName = ReadRawValue(row); + + if (row.HasComplexChildren) + { + // An escaped property name will be longer than an unescaped candidate, so only unescape + // when the lengths are compatible. + if (currentPropertyName.Length > propertyName.Length) + { + var idx = currentPropertyName.IndexOf(JsonConstants.BackSlash); + Debug.Assert(idx >= 0); + + // If everything up to where the property name has a backslash matches, keep going. + if (propertyName.Length > idx + && currentPropertyName[..idx].SequenceEqual(propertyName[..idx])) + { + var remaining = currentPropertyName.Length - idx; + var written = 0; + byte[]? rented = null; + + try + { + var utf8Unescaped = remaining <= utf8UnescapedStack.Length + ? utf8UnescapedStack + : (rented = ArrayPool.Shared.Rent(remaining)); + + // Only unescape the part we haven't processed. + JsonReaderHelper.Unescape(currentPropertyName[idx..], utf8Unescaped, 0, out written); + + // If the unescaped remainder matches the input remainder, it's a match. + if (utf8Unescaped[..written].SequenceEqual(propertyName[idx..])) + { + // If the property name is a match, the answer is the next element. + value = new ResultElement(this, cursor + 1); + return true; + } + } + finally + { + if (rented != null) + { + rented.AsSpan(0, written).Clear(); + ArrayPool.Shared.Return(rented); + } + } + } + } + } + else if (currentPropertyName.SequenceEqual(propertyName)) + { + // If the property name is a match, the answer is the next element. + value = new ResultElement(this, cursor + 1); + return true; + } + + // Move to the previous value + cursor--; + } + + value = default; + return false; + } + + internal Cursor GetStartCursor(Cursor cursor) + { + ObjectDisposedException.ThrowIf(_disposed, this); + (cursor, _) = _metaDb.GetStartCursor(cursor); + return cursor; + } + + internal Cursor GetEndCursor(Cursor cursor) + { + ObjectDisposedException.ThrowIf(_disposed, this); + return cursor + _metaDb.GetNumberOfRows(cursor); + } +} diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.TryGetValue.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.TryGetValue.cs new file mode 100644 index 00000000000..2794eb02500 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.TryGetValue.cs @@ -0,0 +1,226 @@ +using System.Buffers.Text; + +namespace HotChocolate.Text.Json; + +public sealed partial class ResultDocument +{ + internal bool TryGetValue(Cursor cursor, out sbyte value) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var row = _metaDb.Get(cursor); + CheckExpectedType(ElementTokenType.Number, row.TokenType); + + var rawValue = ReadRawValue(row); + + if (Utf8Parser.TryParse(rawValue, out sbyte tmp, out var consumed) + && consumed == rawValue.Length) + { + value = tmp; + return true; + } + + value = 0; + return false; + } + + internal bool TryGetValue(Cursor cursor, out byte value) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var row = _metaDb.Get(cursor); + CheckExpectedType(ElementTokenType.Number, row.TokenType); + + var rawValue = ReadRawValue(row); + + if (Utf8Parser.TryParse(rawValue, out byte tmp, out var consumed) + && consumed == rawValue.Length) + { + value = tmp; + return true; + } + + value = 0; + return false; + } + + internal bool TryGetValue(Cursor cursor, out short value) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var row = _metaDb.Get(cursor); + CheckExpectedType(ElementTokenType.Number, row.TokenType); + + var rawValue = ReadRawValue(row); + + if (Utf8Parser.TryParse(rawValue, out short tmp, out var consumed) + && consumed == rawValue.Length) + { + value = tmp; + return true; + } + + value = 0; + return false; + } + + internal bool TryGetValue(Cursor cursor, out ushort value) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var row = _metaDb.Get(cursor); + CheckExpectedType(ElementTokenType.Number, row.TokenType); + + var rawValue = ReadRawValue(row); + + if (Utf8Parser.TryParse(rawValue, out ushort tmp, out var consumed) + && consumed == rawValue.Length) + { + value = tmp; + return true; + } + + value = 0; + return false; + } + + internal bool TryGetValue(Cursor cursor, out int value) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var row = _metaDb.Get(cursor); + CheckExpectedType(ElementTokenType.Number, row.TokenType); + + var rawValue = ReadRawValue(row); + + if (Utf8Parser.TryParse(rawValue, out int tmp, out var consumed) + && consumed == rawValue.Length) + { + value = tmp; + return true; + } + + value = 0; + return false; + } + + internal bool TryGetValue(Cursor cursor, out uint value) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var row = _metaDb.Get(cursor); + CheckExpectedType(ElementTokenType.Number, row.TokenType); + + var rawValue = ReadRawValue(row); + + if (Utf8Parser.TryParse(rawValue, out uint tmp, out var consumed) + && consumed == rawValue.Length) + { + value = tmp; + return true; + } + + value = 0; + return false; + } + + internal bool TryGetValue(Cursor cursor, out long value) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var row = _metaDb.Get(cursor); + CheckExpectedType(ElementTokenType.Number, row.TokenType); + + var rawValue = ReadRawValue(row); + + if (Utf8Parser.TryParse(rawValue, out long tmp, out var consumed) + && consumed == rawValue.Length) + { + value = tmp; + return true; + } + + value = 0; + return false; + } + + internal bool TryGetValue(Cursor cursor, out ulong value) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var row = _metaDb.Get(cursor); + CheckExpectedType(ElementTokenType.Number, row.TokenType); + + var rawValue = ReadRawValue(row); + + if (Utf8Parser.TryParse(rawValue, out ulong tmp, out var consumed) + && consumed == rawValue.Length) + { + value = tmp; + return true; + } + + value = 0; + return false; + } + + internal bool TryGetValue(Cursor cursor, out double value) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var row = _metaDb.Get(cursor); + CheckExpectedType(ElementTokenType.Number, row.TokenType); + + var rawValue = ReadRawValue(row); + + if (Utf8Parser.TryParse(rawValue, out double tmp, out var bytesConsumed) + && rawValue.Length == bytesConsumed) + { + value = tmp; + return true; + } + + value = 0; + return false; + } + + internal bool TryGetValue(Cursor cursor, out float value) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var row = _metaDb.Get(cursor); + CheckExpectedType(ElementTokenType.Number, row.TokenType); + + var rawValue = ReadRawValue(row); + + if (Utf8Parser.TryParse(rawValue, out float tmp, out var bytesConsumed) + && rawValue.Length == bytesConsumed) + { + value = tmp; + return true; + } + + value = 0; + return false; + } + + internal bool TryGetValue(Cursor cursor, out decimal value) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var row = _metaDb.Get(cursor); + CheckExpectedType(ElementTokenType.Number, row.TokenType); + + var rawValue = ReadRawValue(row); + + if (Utf8Parser.TryParse(rawValue, out decimal tmp, out var bytesConsumed) + && rawValue.Length == bytesConsumed) + { + value = tmp; + return true; + } + + value = 0; + return false; + } +} diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.WriteTo.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.WriteTo.cs new file mode 100644 index 00000000000..869b8c26d26 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.WriteTo.cs @@ -0,0 +1,313 @@ +using System.Buffers; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Text.Json; +using HotChocolate.Execution; + +namespace HotChocolate.Text.Json; + +public sealed partial class ResultDocument : IRawJsonFormatter +{ + public void WriteTo(IBufferWriter writer, bool indented = false) + { + var formatter = new RawJsonFormatter(this, writer, indented); + formatter.Write(); + } + + internal ref struct RawJsonFormatter(ResultDocument document, IBufferWriter writer, bool indented) + { + private int _indentLevel = 0; + + public void Write() + { + WriteByte(JsonConstants.OpenBrace); + + if (indented) + { + WriteNewLine(); + _indentLevel++; + } + + if (document._errors?.Count > 0) + { + if (indented) + { + WriteIndent(); + } + + WriteByte(JsonConstants.Quote); + writer.Write(JsonConstants.Errors); + WriteByte(JsonConstants.Quote); + WriteByte(JsonConstants.Colon); + + if (indented) + { + WriteByte(JsonConstants.Space); + } + + var options = new JsonWriterOptions { Indented = indented }; + using var jsonWriter = new Utf8JsonWriter(writer, options); + JsonValueFormatter.WriteErrors( + jsonWriter, + document._errors, + new JsonSerializerOptions(JsonSerializerDefaults.Web), + default); + jsonWriter.Flush(); + + WriteByte(JsonConstants.Comma); + } + + // Write "data": + var root = Cursor.Zero; + var row = document._metaDb.Get(root); + + if (indented) + { + WriteIndent(); + } + + WriteByte(JsonConstants.Quote); + writer.Write(JsonConstants.Data); + WriteByte(JsonConstants.Quote); + WriteByte(JsonConstants.Colon); + + if (indented) + { + WriteByte(JsonConstants.Space); + } + + if (row.TokenType is ElementTokenType.Null + || (ElementFlags.IsInvalidated & row.Flags) == ElementFlags.IsInvalidated) + { + writer.Write(JsonConstants.NullValue); + } + else + { + WriteObject(root, row); + } + + if (document.Extensions?.Count > 0) + { + WriteByte(JsonConstants.Comma); + + if (indented) + { + WriteIndent(); + } + + WriteByte(JsonConstants.Quote); + writer.Write(JsonConstants.Extensions); + WriteByte(JsonConstants.Quote); + WriteByte(JsonConstants.Colon); + + if (indented) + { + WriteByte(JsonConstants.Space); + } + + var options = new JsonWriterOptions { Indented = indented }; + using var jsonWriter = new Utf8JsonWriter(writer, options); + JsonValueFormatter.WriteDictionary( + jsonWriter, + document.Extensions, + new JsonSerializerOptions(JsonSerializerDefaults.Web), + default); + jsonWriter.Flush(); + } + + if (indented) + { + _indentLevel--; + WriteNewLine(); + WriteIndent(); + } + + WriteByte(JsonConstants.CloseBrace); + } + + public void WriteValue(Cursor cursor, DbRow row) + { + var tokenType = row.TokenType; + + // Inline reference resolution + if (tokenType is ElementTokenType.Reference) + { + cursor = document._metaDb.GetLocationCursor(cursor); + row = document._metaDb.Get(cursor); + tokenType = row.TokenType; + } + + Debug.Assert(tokenType is not ElementTokenType.Reference); + Debug.Assert(tokenType is not ElementTokenType.EndObject); + Debug.Assert(tokenType is not ElementTokenType.EndArray); + + switch (tokenType) + { + case ElementTokenType.StartObject + when (ElementFlags.SourceResult & row.Flags) != ElementFlags.SourceResult: + WriteObject(cursor, row); + break; + + case ElementTokenType.StartArray + when (ElementFlags.SourceResult & row.Flags) != ElementFlags.SourceResult: + WriteArray(cursor, row); + break; + + case ElementTokenType.None: + case ElementTokenType.Null: + writer.Write(JsonConstants.NullValue); + break; + + case ElementTokenType.True: + writer.Write(JsonConstants.TrueValue); + break; + + case ElementTokenType.False: + writer.Write(JsonConstants.FalseValue); + break; + + default: + document.WriteRawValueTo(writer, row, _indentLevel, indented); + break; + } + } + + private void WriteObject(Cursor start, DbRow startRow) + { + Debug.Assert(startRow.TokenType is ElementTokenType.StartObject); + + var current = start + 1; + var end = start + startRow.NumberOfRows; + + WriteByte(JsonConstants.OpenBrace); + + if (indented && current < end) + { + _indentLevel++; + } + + var first = true; + while (current < end) + { + var row = document._metaDb.Get(current); + Debug.Assert(row.TokenType is ElementTokenType.PropertyName); + + if ((ElementFlags.IsInternal & row.Flags) == ElementFlags.IsInternal + || (ElementFlags.IsExcluded & row.Flags) == ElementFlags.IsExcluded) + { + // skip name+value + current += 2; + continue; + } + + if (!first) + { + WriteByte(JsonConstants.Comma); + } + first = false; + + if (indented) + { + WriteNewLine(); + WriteIndent(); + } + + // property name (quoted) + WriteByte(JsonConstants.Quote); + writer.Write(document.ReadRawValue(row)); + WriteByte(JsonConstants.Quote); + WriteByte(JsonConstants.Colon); + + if (indented) + { + WriteByte(JsonConstants.Space); + } + + // property value + current++; + row = document._metaDb.Get(current); + WriteValue(current, row); + + // next property (move past value) + current++; + } + + if (indented && !first) + { + _indentLevel--; + WriteNewLine(); + WriteIndent(); + } + + WriteByte(JsonConstants.CloseBrace); + } + + private void WriteArray(Cursor start, DbRow startRow) + { + Debug.Assert(startRow.TokenType is ElementTokenType.StartArray); + + var current = start + 1; + var end = start + startRow.NumberOfRows; + + WriteByte(JsonConstants.OpenBracket); + + if (indented && current < end) + { + _indentLevel++; + } + + var first = true; + while (current < end) + { + if (!first) + { + WriteByte(JsonConstants.Comma); + } + first = false; + + if (indented) + { + WriteNewLine(); + WriteIndent(); + } + + var row = document._metaDb.Get(current); + WriteValue(current, row); + + current++; + } + + if (indented && end > start + 1) + { + _indentLevel--; + WriteNewLine(); + WriteIndent(); + } + + WriteByte(JsonConstants.CloseBracket); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private readonly void WriteNewLine() => WriteByte(JsonConstants.NewLineLineFeed); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private readonly void WriteIndent() + { + var indentSize = _indentLevel * 2; + if (indentSize > 0) + { + var span = writer.GetSpan(indentSize); + span[..indentSize].Fill(JsonConstants.Space); + writer.Advance(indentSize); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private readonly void WriteByte(byte value) + { + var span = writer.GetSpan(1); + span[0] = value; + writer.Advance(1); + } + } +} diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.cs new file mode 100644 index 00000000000..da69ffbf89d --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultDocument.cs @@ -0,0 +1,803 @@ +using System.Buffers; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Text; +using HotChocolate.Buffers; +using HotChocolate.Execution; +using HotChocolate.Execution.Processing; +using HotChocolate.Types; + +namespace HotChocolate.Text.Json; + +public sealed partial class ResultDocument : IDisposable +{ + private static readonly Encoding s_utf8Encoding = Encoding.UTF8; + private readonly Operation _operation; + private readonly ulong _includeFlags; + internal MetaDb _metaDb; + private int _nextDataIndex; + private int _rentedDataSize; + private readonly List _data = []; + private readonly object _dataChunkLock = new(); + private List? _errors; + private bool _disposed; + + internal ResultDocument(Operation operation, ulong includeFlags) + { + _metaDb = MetaDb.CreateForEstimatedRows(Cursor.RowsPerChunk); + _operation = operation; + _includeFlags = includeFlags; + + Data = CreateObject(Cursor.Zero, operation.RootSelectionSet); + } + + public ResultElement Data { get; } + + // we need extra methods to add stuff + public List? Errors + { + get => _errors; + internal set => _errors = value; + } + + public Dictionary? Extensions { get; set; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ElementTokenType GetElementTokenType(Cursor cursor) + => _metaDb.GetElementTokenType(cursor); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal Operation GetOperation() + => _operation; + + internal SelectionSet? GetSelectionSet(Cursor cursor) + { + var row = _metaDb.Get(cursor); + + return row.OperationReferenceType is OperationReferenceType.SelectionSet + ? _operation.GetSelectionSetById(row.OperationReferenceId) + : null; + } + + internal Selection? GetSelection(Cursor cursor) + { + if (cursor == Cursor.Zero) + { + return null; + } + + // If the cursor points at a value, step back to the PropertyName row. + var row = _metaDb.Get(cursor); + + if (row.TokenType is not ElementTokenType.PropertyName) + { + cursor = cursor.AddRows(-1); + row = _metaDb.Get(cursor); + + if (row.TokenType is not ElementTokenType.PropertyName) + { + return null; + } + } + + return row.OperationReferenceType is OperationReferenceType.Selection + ? _operation.GetSelectionById(row.OperationReferenceId) + : null; + } + + internal ResultElement GetArrayIndexElement(Cursor current, int arrayIndex) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var (start, tokenType) = _metaDb.GetStartCursor(current); + + CheckExpectedType(ElementTokenType.StartArray, tokenType); + + var len = _metaDb.GetNumberOfRows(start); + + if ((uint)arrayIndex >= (uint)len) + { + throw new IndexOutOfRangeException(); + } + + // first element is at +1 after StartArray + return new ResultElement(this, start.AddRows(arrayIndex + 1)); + } + + internal int GetArrayLength(Cursor current) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + (current, var tokenType) = _metaDb.GetStartCursor(current); + + CheckExpectedType(ElementTokenType.StartArray, tokenType); + + return _metaDb.GetSizeOrLength(current); + } + + internal int GetPropertyCount(Cursor current) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + (current, var tokenType) = _metaDb.GetStartCursor(current); + + CheckExpectedType(ElementTokenType.StartObject, tokenType); + + return _metaDb.GetSizeOrLength(current); + } + + internal Path CreatePath(Cursor current) + { + // Stop at root via IsRoot flag. + if ((_metaDb.GetFlags(current) & ElementFlags.IsRoot) == ElementFlags.IsRoot) + { + return Path.Root; + } + + Span chain = stackalloc Cursor[64]; + var c = current; + var written = 0; + + do + { + chain[written++] = c; + + var parentIndex = _metaDb.GetParent(c); + if (parentIndex <= 0) + { + break; + } + + c = Cursor.FromIndex(parentIndex); + + if (written >= 64) + { + throw new InvalidOperationException("The path is to deep."); + } + } while (true); + + var path = Path.Root; + var parentTokenType = ElementTokenType.StartObject; + + chain = chain[..written]; + + for (var i = chain.Length - 1; i >= 0; i--) + { + c = chain[i]; + var tokenType = _metaDb.GetElementTokenType(c, resolveReferences: false); + + if (tokenType == ElementTokenType.PropertyName) + { + path = path.Append(GetSelection(c)!.ResponseName); + i--; // skip over the actual value + } + else if (chain.Length - 1 > i) + { + var parentCursor = chain[i + 1]; + + if (parentTokenType is ElementTokenType.StartArray) + { + // arrayIndex = abs(child) - (abs(parent) + 1) + var absChild = c.Chunk * Cursor.RowsPerChunk + c.Row; + var absParent = parentCursor.Chunk * Cursor.RowsPerChunk + parentCursor.Row; + var arrayIndex = absChild - (absParent + 1); + path = path.Append(arrayIndex); + } + } + + parentTokenType = tokenType; + } + + return path; + } + + internal ResultElement GetParent(Cursor current) + { + // The null cursor represents the data object, which is the utmost root. + // If we have reached that we simply return an undefined element + if (current == Cursor.Zero) + { + return default; + } + + var parent = _metaDb.GetParentCursor(current); + + // if the parent element is a property name then we must get the parent of that, + // as property name and value represent the same element. + if (_metaDb.GetElementTokenType(parent) is ElementTokenType.PropertyName) + { + parent = _metaDb.GetParentCursor(parent); + } + + // if we have not yet reached the root and the element type of the parent is an object or an array + // then we need to get still the parent of this row as we want to get the logical parent + // which is the value level of the property or the element in an array. + if (parent != Cursor.Zero + && _metaDb.GetElementTokenType(parent) is ElementTokenType.StartObject or ElementTokenType.StartArray) + { + parent = _metaDb.GetParentCursor(parent); + + // in this case the parent must be a reference, otherwise we would have + // found an inconsistency in the database. + Debug.Assert(_metaDb.GetElementTokenType(parent, resolveReferences: false) == ElementTokenType.Reference); + } + + return new ResultElement(this, parent); + } + + internal bool IsInvalidated(Cursor current) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var tokenType = _metaDb.GetElementTokenType(current, resolveReferences: false); + + if (tokenType is ElementTokenType.StartObject) + { + var flags = _metaDb.GetFlags(current); + return (flags & ElementFlags.IsInvalidated) == ElementFlags.IsInvalidated; + } + + if (tokenType is ElementTokenType.Reference) + { + current = _metaDb.GetLocationCursor(current); + tokenType = _metaDb.GetElementTokenType(current); + + if (tokenType is ElementTokenType.StartObject) + { + var flags = _metaDb.GetFlags(current); + return (flags & ElementFlags.IsInvalidated) == ElementFlags.IsInvalidated; + } + } + + return false; + } + + internal bool IsNullOrInvalidated(Cursor current) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var tokenType = _metaDb.GetElementTokenType(current); + + if (tokenType is ElementTokenType.Null) + { + return true; + } + + if (tokenType is ElementTokenType.StartObject) + { + var flags = _metaDb.GetFlags(current); + return (flags & ElementFlags.IsInvalidated) == ElementFlags.IsInvalidated; + } + + if (tokenType is ElementTokenType.Reference) + { + current = _metaDb.GetLocationCursor(current); + tokenType = _metaDb.GetElementTokenType(current); + + if (tokenType is ElementTokenType.StartObject) + { + var flags = _metaDb.GetFlags(current); + return (flags & ElementFlags.IsInvalidated) == ElementFlags.IsInvalidated; + } + } + + return false; + } + + internal bool IsInternalProperty(Cursor current) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + // The flag sits on the property row (one before value) + var propertyCursor = current.AddRows(-1); + var flags = _metaDb.GetFlags(propertyCursor); + return (flags & ElementFlags.IsInternal) == ElementFlags.IsInternal; + } + + internal void Invalidate(Cursor current) + { + ObjectDisposedException.ThrowIf(_disposed, this); + + var tokenType = _metaDb.GetElementTokenType(current, resolveReferences: false); + + if (tokenType is ElementTokenType.None) + { + return; + } + + if (tokenType is ElementTokenType.StartArray) + { + return; + } + + if (tokenType is ElementTokenType.StartObject) + { + var flags = _metaDb.GetFlags(current); + _metaDb.SetFlags(current, flags | ElementFlags.IsInvalidated); + return; + } + + if (tokenType is ElementTokenType.Reference) + { + current = _metaDb.GetLocationCursor(current); + tokenType = _metaDb.GetElementTokenType(current); + + if (tokenType is ElementTokenType.StartObject) + { + var flags = _metaDb.GetFlags(current); + _metaDb.SetFlags(current, flags | ElementFlags.IsInvalidated); + } + + return; + } + + Debug.Fail("Only objects can be invalidated."); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteRawValueTo(IBufferWriter writer, DbRow row) + { + switch (row.TokenType) + { + case ElementTokenType.Null: + WriteToBuffer(writer, JsonConstants.NullValue); + return; + + case ElementTokenType.True: + WriteToBuffer(writer, JsonConstants.TrueValue); + return; + + case ElementTokenType.False: + WriteToBuffer(writer, JsonConstants.FalseValue); + return; + + case ElementTokenType.PropertyName: + WriteToBuffer(writer, _operation.GetSelectionById(row.OperationReferenceId).Utf8ResponseName); + return; + + case ElementTokenType.String: + case ElementTokenType.Number: + WriteLocalDataTo(writer, row.Location, row.SizeOrLength); + return; + + default: + throw new NotSupportedException(); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void WriteToBuffer(IBufferWriter writer, ReadOnlySpan data) + { + var span = writer.GetSpan(data.Length); + data.CopyTo(span); + writer.Advance(data.Length); + } + + /// + /// Writes local data to the buffer, handling chunk boundaries. + /// + private void WriteLocalDataTo(IBufferWriter writer, int location, int size) + { + var remaining = size; + var currentPos = location; + + while (remaining > 0) + { + var chunkIndex = currentPos / JsonMemory.BufferSize; + var offset = currentPos % JsonMemory.BufferSize; + var availableInChunk = JsonMemory.BufferSize - offset; + var toWrite = Math.Min(remaining, availableInChunk); + + var source = _data[chunkIndex].AsSpan(offset, toWrite); + var dest = writer.GetSpan(toWrite); + source.CopyTo(dest); + writer.Advance(toWrite); + + currentPos += toWrite; + remaining -= toWrite; + } + } + + private ReadOnlySpan ReadRawValue(DbRow row) + { + switch (row.TokenType) + { + case ElementTokenType.Null: + return JsonConstants.NullValue; + + case ElementTokenType.True: + return JsonConstants.TrueValue; + + case ElementTokenType.False: + return JsonConstants.FalseValue; + + case ElementTokenType.PropertyName: + return _operation.GetSelectionById(row.OperationReferenceId).Utf8ResponseName; + + case ElementTokenType.String: + case ElementTokenType.Number: + return ReadLocalData(row.Location, row.SizeOrLength); + + default: + throw new NotSupportedException(); + } + } + + /// + /// Reads local data from the data chunks. + /// + /// + /// This method only supports data that fits within a single chunk. + /// Data that spans chunk boundaries should use instead. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ReadOnlySpan ReadLocalData(int location, int size) + { + var chunkIndex = location / JsonMemory.BufferSize; + var offset = location % JsonMemory.BufferSize; + + // Fast path: data fits in a single chunk + if (offset + size <= JsonMemory.BufferSize) + { + return _data[chunkIndex].AsSpan(offset, size); + } + + // Data spans chunk boundaries - this should be rare for typical JSON values + throw new NotSupportedException( + "Reading data that spans chunk boundaries as a span is not supported. " + + "Use WriteLocalDataTo for writing to an IBufferWriter instead."); + } + + internal ResultElement CreateObject(Cursor parent, SelectionSet selectionSet) + { + var startObjectCursor = WriteStartObject(parent, selectionSet.Id); + + var selectionCount = 0; + foreach (var selection in selectionSet.Selections) + { + WriteEmptyProperty(startObjectCursor, selection); + selectionCount++; + } + + WriteEndObject(startObjectCursor, selectionCount); + + return new ResultElement(this, startObjectCursor); + } + + internal ResultElement CreateObject(Cursor parent, int propertyCount) + { + var startObjectCursor = WriteStartObject(parent, isSelectionSet: false); + + for (var i = 0; i < propertyCount; i++) + { + WriteEmptyProperty(startObjectCursor); + } + + WriteEndObject(startObjectCursor, propertyCount); + + return new ResultElement(this, startObjectCursor); + } + + internal ResultElement CreateArray(Cursor parent, int length) + { + var cursor = WriteStartArray(parent, length); + + for (var i = 0; i < length; i++) + { + WriteEmptyValue(cursor); + } + + WriteEndArray(); + + return new ResultElement(this, cursor); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void AssignObjectOrArray(ResultElement target, ResultElement value) + { + _metaDb.Replace( + cursor: target.Cursor, + tokenType: ElementTokenType.Reference, + location: value.Cursor.ToIndex(), + parentRow: _metaDb.GetParent(target.Cursor)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void AssignStringValue(ResultElement target, ReadOnlySpan value) + { + var totalSize = value.Length + 2; + var position = ClaimDataSpace(totalSize); + WriteData(position, value, withQuotes: true); + + _metaDb.Replace( + cursor: target.Cursor, + tokenType: ElementTokenType.String, + location: position, + sizeOrLength: totalSize, + parentRow: _metaDb.GetParent(target.Cursor)); + } + + internal void AssignPropertyName(ResultElement target, ReadOnlySpan propertyName) + { + var totalSize = propertyName.Length + 2; + var position = ClaimDataSpace(totalSize); + WriteData(position, propertyName, withQuotes: true); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void AssignNumberValue(ResultElement target, ReadOnlySpan value) + { + var position = ClaimDataSpace(value.Length); + WriteData(position, value); + + _metaDb.Replace( + cursor: target.Cursor, + tokenType: ElementTokenType.Number, + location: position, + sizeOrLength: value.Length, + parentRow: _metaDb.GetParent(target.Cursor)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void AssignBooleanValue(ResultElement target, bool value) + { + _metaDb.Replace( + cursor: target.Cursor, + tokenType: value ? ElementTokenType.True : ElementTokenType.False, + parentRow: _metaDb.GetParent(target.Cursor)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void AssignNullValue(ResultElement target) + { + _metaDb.Replace( + cursor: target.Cursor, + tokenType: ElementTokenType.Null, + parentRow: _metaDb.GetParent(target.Cursor)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int ClaimDataSpace(int size) + { + // Atomically claim space + var endPosition = Interlocked.Add(ref _nextDataIndex, size); + var startPosition = endPosition - size; + + // Fast path: we check if we already have enough rented memory + // if so we can directly return and write the data without locking. + if (endPosition <= Volatile.Read(ref _rentedDataSize)) + { + return startPosition; + } + + // Slow path: we need to rent more chunks so in this case + // we will need to do a proper synchronization. + EnsureDataCapacity(endPosition); + return startPosition; + } + + private void EnsureDataCapacity(int requiredSize) + { + lock (_dataChunkLock) + { + // Double-check after acquiring lock + var currentSize = _rentedDataSize; + if (requiredSize <= currentSize) + { + return; + } + + // Rent chunks until we have enough + while (currentSize < requiredSize) + { + _data.Add(JsonMemory.Rent(JsonMemoryKind.Json)); + currentSize += JsonMemory.BufferSize; + } + + // Publish new size (volatile write) + Volatile.Write(ref _rentedDataSize, currentSize); + } + } + + /// + /// Writes data to the data chunks, handling chunk boundaries. + /// + /// The position to start writing at. + /// The data to write. + /// If true, wraps the data with JSON quotes. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteData(int position, ReadOnlySpan data, bool withQuotes = false) + { + if (withQuotes) + { + WriteByte(position, JsonConstants.Quote); + WriteDataCore(position + 1, data); + WriteByte(position + 1 + data.Length, JsonConstants.Quote); + } + else + { + WriteDataCore(position, data); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteByte(int position, byte value) + { + var chunkIndex = position / JsonMemory.BufferSize; + var offset = position % JsonMemory.BufferSize; + _data[chunkIndex][offset] = value; + } + + private void WriteDataCore(int position, ReadOnlySpan data) + { + var chunkIndex = position / JsonMemory.BufferSize; + var offset = position % JsonMemory.BufferSize; + var availableInChunk = JsonMemory.BufferSize - offset; + + // Fast path: we can write all the data into single chunk + if (data.Length <= availableInChunk) + { + data.CopyTo(_data[chunkIndex].AsSpan(offset, data.Length)); + return; + } + + // Slow path: data spans multiple chunks so we need to loop + var remaining = data; + var currentPos = position; + + while (remaining.Length > 0) + { + chunkIndex = currentPos / JsonMemory.BufferSize; + offset = currentPos % JsonMemory.BufferSize; + availableInChunk = JsonMemory.BufferSize - offset; + var toWrite = Math.Min(remaining.Length, availableInChunk); + + remaining[..toWrite].CopyTo(_data[chunkIndex].AsSpan(offset, toWrite)); + + currentPos += toWrite; + remaining = remaining[toWrite..]; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Cursor WriteStartObject(Cursor parent, int selectionSetId = 0, bool isSelectionSet = true) + { + var flags = ElementFlags.None; + var parentRow = ToIndex(parent); + + if (parentRow < 0) + { + parentRow = 0; + flags = ElementFlags.IsRoot; + } + + return _metaDb.Append( + ElementTokenType.StartObject, + parentRow: parentRow, + operationReferenceId: selectionSetId, + operationReferenceType: isSelectionSet + ? OperationReferenceType.SelectionSet + : OperationReferenceType.None, + flags: flags); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteEndObject(Cursor startObjectCursor, int length) + { + _metaDb.Append(ElementTokenType.EndObject); + + _metaDb.SetNumberOfRows(startObjectCursor, (length * 2) + 1); + _metaDb.SetSizeOrLength(startObjectCursor, length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Cursor WriteStartArray(Cursor parent, int length = 0) + { + var flags = ElementFlags.None; + var parentRow = ToIndex(parent); + + if (parentRow < 0) + { + parentRow = 0; + flags = ElementFlags.IsRoot; + } + + return _metaDb.Append( + ElementTokenType.StartArray, + sizeOrLength: length, + parentRow: parentRow, + numberOfRows: length + 1, + flags: flags); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteEndArray() => _metaDb.Append(ElementTokenType.EndArray); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteEmptyProperty(Cursor parent, ISelection selection) + { + var flags = ElementFlags.None; + + if (selection.IsInternal) + { + flags = ElementFlags.IsInternal; + } + + if (!selection.IsIncluded(_includeFlags)) + { + flags |= ElementFlags.IsExcluded; + } + + if (selection.Type.Kind is not TypeKind.NonNull) + { + flags |= ElementFlags.IsNullable; + } + + if (selection.Type.IsListType()) + { + flags |= ElementFlags.IsList; + } + + if (selection.Type.IsCompositeType()) + { + flags |= ElementFlags.IsObject; + } + + var prop = _metaDb.Append( + ElementTokenType.PropertyName, + parentRow: ToIndex(parent), + operationReferenceId: selection.Id, + operationReferenceType: OperationReferenceType.Selection, + flags: flags); + + _metaDb.Append( + ElementTokenType.None, + parentRow: ToIndex(prop)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteEmptyProperty(Cursor parent) + { + var prop = _metaDb.Append( + ElementTokenType.PropertyName, + parentRow: ToIndex(parent)); + + _metaDb.Append( + ElementTokenType.None, + parentRow: ToIndex(prop)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteEmptyValue(Cursor parent) + { + _metaDb.Append( + ElementTokenType.None, + parentRow: ToIndex(parent)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int ToIndex(Cursor c) => (c.Chunk * Cursor.RowsPerChunk) + c.Row; + + private static void CheckExpectedType(ElementTokenType expected, ElementTokenType actual) + { + if (expected != actual) + { + throw new ArgumentOutOfRangeException($"Expected {expected} but found {actual}."); + } + } + + public void Dispose() + { + if (!_disposed) + { + _metaDb.Dispose(); + + if (_data.Count > 0) + { + JsonMemory.Return(JsonMemoryKind.Json, _data); + } + + _disposed = true; + } + } +} diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultElement.ArrayEnumerator.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultElement.ArrayEnumerator.cs new file mode 100644 index 00000000000..b7a04e9ec98 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultElement.ArrayEnumerator.cs @@ -0,0 +1,100 @@ +using System.Collections; +using System.Diagnostics; + +namespace HotChocolate.Text.Json; + +public readonly partial struct ResultElement +{ + /// + /// An enumerable and enumerator for the contents of a JSON array. + /// + [DebuggerDisplay("{Current,nq}")] + public struct ArrayEnumerator : IEnumerable, IEnumerator + { + private readonly ResultDocument _document; + private readonly ResultDocument.Cursor _start; + private readonly ResultDocument.Cursor _end; + private ResultDocument.Cursor _cursor; + + internal ArrayEnumerator(ResultElement target) + { + _document = target._parent; + (_start, var tokenType) = _document._metaDb.GetStartCursor(target._cursor); + Debug.Assert(tokenType is ElementTokenType.StartArray); + _end = _start + _document._metaDb.GetNumberOfRows(_start); + _cursor = _start; + } + + /// + public ResultElement Current + { + get + { + var cursor = _cursor; + if (cursor == _start || cursor >= _end) + { + return default; + } + + return new ResultElement(_document, cursor); + } + } + + /// + object IEnumerator.Current => Current; + + /// + /// Returns an enumerator that iterates through the array. + /// + public ArrayEnumerator GetEnumerator() + { + var enumerator = this; + enumerator._cursor = enumerator._start; + return enumerator; + } + + /// + IEnumerator IEnumerable.GetEnumerator() + => GetEnumerator(); + + /// + IEnumerator IEnumerable.GetEnumerator() + => GetEnumerator(); + + /// + public bool MoveNext() + { + var start = _start; + var end = _end; + + if (_cursor == start) + { + var first = start + 1; + if (first < end) + { + _cursor = first; + return true; + } + + _cursor = end; + return false; + } + + var next = _cursor + 1; + if (next < end) + { + _cursor = next; + return true; + } + + _cursor = end; + return false; + } + + /// + public void Reset() => _cursor = _start; + + /// + public void Dispose() => _cursor = _end; + } +} diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultElement.ObjectEnumerator.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultElement.ObjectEnumerator.cs new file mode 100644 index 00000000000..90725204ee3 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultElement.ObjectEnumerator.cs @@ -0,0 +1,127 @@ +using System.Collections; +using System.Diagnostics; +using static HotChocolate.Text.Json.ResultDocument; + +namespace HotChocolate.Text.Json; + +public readonly partial struct ResultElement +{ + /// + /// An enumerable and enumerator for the properties of a JSON object. + /// + [DebuggerDisplay("{Current,nq}")] + public struct ObjectEnumerator : IEnumerable, IEnumerator + { + private readonly ResultDocument _document; + private readonly Cursor _start; + private readonly Cursor _end; + private Cursor _cursor; + + internal ObjectEnumerator(ResultElement target) + { + _document = target._parent; + (_start, var tokenType) = _document._metaDb.GetStartCursor(target._cursor); + Debug.Assert(tokenType is ElementTokenType.StartObject); + _end = _start + _document._metaDb.GetNumberOfRows(_start); + _cursor = _start; + } + + /// + public ResultProperty Current + { + get + { + if (_cursor == _start || _cursor >= _end) + { + return default; + } + + return new ResultProperty(new ResultElement(_document, _cursor + 1)); + } + } + + /// + /// Returns an enumerator that iterates the properties of an object. + /// + /// + /// An value that can be used to iterate + /// through the object. + /// + /// + /// The enumerator will enumerate the properties in the order they are + /// declared, and when an object has multiple definitions of a single + /// property they will all individually be returned (each in the order + /// they appear in the content). + /// + public ObjectEnumerator GetEnumerator() + { + var enumerator = this; + enumerator._cursor = enumerator._start; + return enumerator; + } + + /// + IEnumerator IEnumerable.GetEnumerator() + => GetEnumerator(); + + /// + IEnumerator IEnumerable.GetEnumerator() + => GetEnumerator(); + + /// + public void Dispose() + { + _cursor = _end; + } + + /// + public void Reset() + { + _cursor = _start; + } + + /// + object IEnumerator.Current => Current; + + /// + public bool MoveNext() + { + while (MoveNextInternal()) + { + var flags = _document._metaDb.GetFlags(_cursor); + if ((ElementFlags.IsExcluded & flags) is not ElementFlags.IsExcluded) + { + return true; + } + } + + return false; + } + + private bool MoveNextInternal() + { + if (_cursor == _start) + { + var first = _start + 1; + if (first < _end) + { + _cursor = first; + return true; + } + + _cursor = _end; + return false; + } + + var next = _cursor += 2; + if (next < _end) + { + _cursor = next; + return true; + } + + _cursor = _end; + return false; + } + } +} diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultElement.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultElement.cs new file mode 100644 index 00000000000..de454de6cd1 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultElement.cs @@ -0,0 +1,1197 @@ +using System.Buffers; +using System.Buffers.Text; +using System.Diagnostics; +using System.Text; +using System.Text.Json; +using HotChocolate.Execution; +using HotChocolate.Execution.Processing; +using HotChocolate.Types; +using static HotChocolate.Properties.TextJsonResources; + +#pragma warning disable CS1574, CS1584, CS1581, CS1580 + +namespace HotChocolate.Text.Json; + +public readonly partial struct ResultElement : IRawJsonFormatter +{ + private readonly ResultDocument _parent; + private readonly ResultDocument.Cursor _cursor; + + internal ResultElement(ResultDocument parent, ResultDocument.Cursor cursor) + { + // parent is usually not null, but the Current property + // on the enumerators (when initialized as `default`) can + // get here with a null. + _parent = parent; + _cursor = cursor; + } + + /// + /// Writes this element as JSON to the specified buffer writer. + /// + /// The buffer writer to write to. + /// + /// true to write indented JSON; otherwise, false. + /// + public void WriteTo(IBufferWriter writer, bool indented = false) + { + var formatter = new ResultDocument.RawJsonFormatter(_parent, writer, indented); + var row = _parent._metaDb.Get(_cursor); + formatter.WriteValue(_cursor, row); + } + + /// + /// Gets the internal meta-db cursor. + /// + internal ResultDocument.Cursor Cursor => _cursor; + + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private ElementTokenType TokenType => _parent?.GetElementTokenType(_cursor) ?? ElementTokenType.None; + + /// + /// Gets the of this element. + /// + public JsonValueKind ValueKind => TokenType.ToValueKind(); + + /// + /// Gets the element at the specified index when the current element is an array. + /// + /// The zero-based index of the element to get. + /// + /// This element's is not . + /// + /// + /// is not in the range [0, ()). + /// + public ResultElement this[int index] + { + get + { + CheckValidInstance(); + + return _parent.GetArrayIndexElement(_cursor, index); + } + } + + /// + /// Gets the operation this element belongs to. + /// + public Operation Operation + { + get + { + CheckValidInstance(); + + return _parent.GetOperation(); + } + } + + /// + /// Gets the if this element represents the data of a selection set; + /// otherwise, null. + /// + public SelectionSet? SelectionSet + { + get + { + CheckValidInstance(); + + return _parent.GetSelectionSet(_cursor); + } + } + + /// + /// Gets the if this element represents the data of a field selection; + /// otherwise, null. + /// + public Selection? Selection + { + get + { + CheckValidInstance(); + + if (_cursor == ResultDocument.Cursor.Zero) + { + return null; + } + + // note: the selection is stored on the property not on the value. + return _parent.GetSelection(_cursor - 1); + } + } + + /// + /// Gets the if this element represents the data of a field selection; + /// otherwise, null. + /// + public IType? Type + { + get + { + if (_cursor == ResultDocument.Cursor.Zero) + { + return null; + } + + var selection = Selection; + + if (selection is not null) + { + return selection.Type; + } + + var type = Parent.Type; + + if (type?.IsListType() == true) + { + return type.ElementType(); + } + + return null; + } + } + + /// + /// Gets a value indicating whether this element has been invalidated during null propagation. + /// + public bool IsInvalidated + { + get + { + CheckValidInstance(); + + return _parent.IsInvalidated(_cursor); + } + } + + /// + /// Gets a value indicating whether this element is either null or was invalidated during null propagation. + /// + public bool IsNullOrInvalidated + { + get + { + if (_parent is null) + { + return true; + } + + return _parent.IsNullOrInvalidated(_cursor); + } + } + + /// + /// Gets the path to this element within the result document. + /// + public Path Path + { + get + { + CheckValidInstance(); + + return _parent.CreatePath(_cursor); + } + } + + /// + /// Gets the parent element that contains this element. + /// + public ResultElement Parent + { + get + { + CheckValidInstance(); + + return _parent.GetParent(_cursor); + } + } + + /// + /// Gets a value indicating whether this element is nullable according to the GraphQL type system. + /// + public bool IsNullable + { + get + { + CheckValidInstance(); + + if (_cursor == ResultDocument.Cursor.Zero) + { + return false; + } + + return Type?.IsNullableType() ?? true; + } + } + + /// + /// Gets a value indicating whether this element represents internal data + /// that is required for processing and must not be written to the GraphQL response. + /// + public bool IsInternal + { + get + { + CheckValidInstance(); + + return _parent.IsInternalProperty(_cursor); + } + } + + /// + /// Gets the for this element. + /// + /// + /// The instance. + /// + /// + /// This element does not represent the data of a selection set. + /// + public SelectionSet AssertSelectionSet() + { + var selectionSet = SelectionSet; + + if (selectionSet is null) + { + throw new InvalidOperationException("The selection set is null.") { Source = Rethrowable }; + } + + return selectionSet; + } + + /// + /// Gets the for this element. + /// + /// + /// The instance. + /// + /// + /// This element does not represent the data of a field selection. + /// + public Selection AssertSelection() + { + var selection = Selection; + + if (selection is null) + { + throw new InvalidOperationException("The selection set is null.") { Source = Rethrowable }; + } + + return selection; + } + + /// + /// Gets the for this element. + /// + /// + /// The instance. + /// + /// + /// This element does not represent the data of a field selection. + /// + public IType AssertType() + { + var type = Type; + + if (type is null) + { + throw new InvalidOperationException("The type is null.") { Source = Rethrowable }; + } + + return type; + } + + /// + /// Marks this element as invalidated, which occurs during null propagation + /// when a non-nullable field returns null. + /// + public void Invalidate() + { + CheckValidInstance(); + + _parent.Invalidate(_cursor); + } + + /// + /// Gets the number of elements contained within the current array element. + /// + /// + /// The number of elements in the array. + /// + public int GetArrayLength() + { + CheckValidInstance(); + + return _parent.GetArrayLength(_cursor); + } + + /// + /// Gets the number of properties contained within the current object element. + /// + /// + /// The number of properties in the object. + /// + public int GetPropertyCount() + { + CheckValidInstance(); + + return _parent.GetPropertyCount(_cursor); + } + + /// + /// Gets a property by name when the current element is an object. + /// + /// The name of the property to find. + /// The property value. + /// + /// No property with the specified name was found. + /// + public ResultElement GetProperty(string propertyName) + { + ArgumentNullException.ThrowIfNull(propertyName); + + if (TryGetProperty(propertyName, out var property)) + { + return property; + } + + throw new KeyNotFoundException(); + } + + /// + /// Gets a property by UTF-8 encoded name when the current element is an object. + /// + /// The UTF-8 encoded name of the property to find. + /// The property value. + /// + /// No property with the specified name was found. + /// + public ResultElement GetProperty(ReadOnlySpan utf8PropertyName) + { + if (TryGetProperty(utf8PropertyName, out var property)) + { + return property; + } + + throw new KeyNotFoundException(); + } + + /// + /// Attempts to get a property by name when the current element is an object. + /// + /// The name of the property to find. + /// + /// When this method returns, contains the property value if found; otherwise, the default value. + /// + /// + /// true if the property was found; otherwise, false. + /// + public bool TryGetProperty(string propertyName, out ResultElement value) + { + ArgumentNullException.ThrowIfNull(propertyName); + + return _parent.TryGetNamedPropertyValue(_cursor, propertyName, out value); + } + + /// + /// Attempts to get a property by UTF-8 encoded name when the current element is an object. + /// + /// The UTF-8 encoded name of the property to find. + /// + /// When this method returns, contains the property value if found; otherwise, the default value. + /// + /// + /// true if the property was found; otherwise, false. + /// + public bool TryGetProperty(ReadOnlySpan utf8PropertyName, out ResultElement value) + { + CheckValidInstance(); + + return _parent.TryGetNamedPropertyValue(_cursor, utf8PropertyName, out value); + } + + /// + /// Gets the value as a . + /// + /// The boolean value. + /// + /// This element's is not or . + /// + public bool GetBoolean() + { + var type = TokenType; + + return type switch + { + ElementTokenType.True => true, + ElementTokenType.False => false, + _ => ThrowJsonElementWrongTypeException(type) + }; + + static bool ThrowJsonElementWrongTypeException(ElementTokenType actualType) + { + throw new InvalidOperationException(string.Format( + ResultElement_GetBoolean_JsonElementHasWrongType, + nameof(Boolean), + actualType.ToValueKind())) + { + Source = Rethrowable + }; + } + } + + /// + /// Gets the value as a . + /// + /// The string value, or null if this element is a JSON null. + public string? GetString() + { + CheckValidInstance(); + + return _parent.GetString(_cursor, ElementTokenType.String); + } + + /// + /// Gets the value as a non-null . + /// + /// The string value. + /// + /// This element is a JSON null. + /// + public string AssertString() + { + CheckValidInstance(); + + return _parent.GetRequiredString(_cursor, ElementTokenType.String); + } + + /// + /// Attempts to get the value as an . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. + public bool TryGetSByte(out sbyte value) + { + CheckValidInstance(); + + return _parent.TryGetValue(_cursor, out value); + } + + /// + /// Gets the value as an . + /// + /// The value. + /// The value cannot be parsed as an . + public sbyte GetSByte() => TryGetSByte(out var value) ? value : throw new FormatException(); + + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. + public bool TryGetByte(out byte value) + { + CheckValidInstance(); + + return _parent.TryGetValue(_cursor, out value); + } + + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . + public byte GetByte() + { + if (TryGetByte(out var value)) + { + return value; + } + + throw new FormatException(); + } + + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. + public bool TryGetInt16(out short value) + { + CheckValidInstance(); + + return _parent.TryGetValue(_cursor, out value); + } + + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . + public short GetInt16() + { + if (TryGetInt16(out var value)) + { + return value; + } + + throw new FormatException(); + } + + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. + public bool TryGetUInt16(out ushort value) + { + CheckValidInstance(); + + return _parent.TryGetValue(_cursor, out value); + } + + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . + public ushort GetUInt16() + { + if (TryGetUInt16(out var value)) + { + return value; + } + + throw new FormatException(); + } + + /// + /// Attempts to get the value as an . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. + public bool TryGetInt32(out int value) + { + CheckValidInstance(); + + return _parent.TryGetValue(_cursor, out value); + } + + /// + /// Gets the value as an . + /// + /// The value. + /// The value cannot be parsed as an . + public int GetInt32() + { + if (!TryGetInt32(out var value)) + { + ThrowHelper.FormatException(); + } + + return value; + } + + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. + public bool TryGetUInt32(out uint value) + { + CheckValidInstance(); + + return _parent.TryGetValue(_cursor, out value); + } + + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . + public uint GetUInt32() + { + if (!TryGetUInt32(out var value)) + { + ThrowHelper.FormatException(); + } + + return value; + } + + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. + public bool TryGetInt64(out long value) + { + CheckValidInstance(); + + return _parent.TryGetValue(_cursor, out value); + } + + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . + public long GetInt64() + { + if (!TryGetInt64(out var value)) + { + ThrowHelper.FormatException(); + } + + return value; + } + + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. + public bool TryGetUInt64(out ulong value) + { + CheckValidInstance(); + + return _parent.TryGetValue(_cursor, out value); + } + + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . + public ulong GetUInt64() + { + if (!TryGetUInt64(out var value)) + { + ThrowHelper.FormatException(); + } + + return value; + } + + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. + public bool TryGetDouble(out double value) + { + CheckValidInstance(); + + return _parent.TryGetValue(_cursor, out value); + } + + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . + public double GetDouble() + { + if (!TryGetDouble(out var value)) + { + ThrowHelper.FormatException(); + } + + return value; + } + + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. + public bool TryGetSingle(out float value) + { + CheckValidInstance(); + + return _parent.TryGetValue(_cursor, out value); + } + + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . + public float GetSingle() + { + if (!TryGetSingle(out var value)) + { + ThrowHelper.FormatException(); + } + + return value; + } + + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. + public bool TryGetDecimal(out decimal value) + { + CheckValidInstance(); + + return _parent.TryGetValue(_cursor, out value); + } + + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . + public decimal GetDecimal() + { + if (!TryGetDecimal(out var value)) + { + ThrowHelper.FormatException(); + } + + return value; + } + + internal string GetPropertyName() + { + CheckValidInstance(); + + return _parent.GetNameOfPropertyValue(_cursor); + } + + internal ReadOnlySpan GetPropertyNameRaw() + { + CheckValidInstance(); + + return _parent.GetPropertyNameRaw(_cursor); + } + + /// + /// Gets the raw JSON text representing this element. + /// + /// The raw JSON text. + public string GetRawText() + { + CheckValidInstance(); + + return _parent.GetRawValueAsString(_cursor); + } + + internal ReadOnlySpan GetRawValue(bool includeQuotes = true) + { + CheckValidInstance(); + + return _parent.GetRawValue(_cursor, includeQuotes: true); + } + + /// + /// Compares the text of this element to the specified string. + /// + /// The text to compare against. + /// + /// true if this element's value equals the specified text; otherwise, false. + /// + public bool ValueEquals(string? text) + { + if (TokenType == ElementTokenType.Null) + { + return text == null; + } + + return TextEqualsHelper(text.AsSpan(), isPropertyName: false); + } + + /// + /// Compares the text of this element to the specified UTF-8 encoded text. + /// + /// The UTF-8 encoded text to compare against. + /// + /// true if this element's value equals the specified text; otherwise, false. + /// + public bool ValueEquals(ReadOnlySpan utf8Text) + { + if (TokenType == ElementTokenType.Null) + { +#pragma warning disable CA2265 + return utf8Text[..0] == default; +#pragma warning restore CA2265 + } + + return TextEqualsHelper(utf8Text, isPropertyName: false, shouldUnescape: true); + } + + /// + /// Compares the text of this element to the specified character span. + /// + /// The text to compare against. + /// + /// true if this element's value equals the specified text; otherwise, false. + /// + public bool ValueEquals(ReadOnlySpan text) + { + if (TokenType == ElementTokenType.Null) + { +#pragma warning disable CA2265 + return text[..0] == default; +#pragma warning restore CA2265 + } + + return TextEqualsHelper(text, isPropertyName: false); + } + + internal bool TextEqualsHelper(ReadOnlySpan utf8Text, bool isPropertyName, bool shouldUnescape) + { + CheckValidInstance(); + + return _parent.TextEquals(_cursor, utf8Text, isPropertyName, shouldUnescape); + } + + internal bool TextEqualsHelper(ReadOnlySpan text, bool isPropertyName) + { + CheckValidInstance(); + + return _parent.TextEquals(_cursor, text, isPropertyName); + } + + internal string GetPropertyRawText() + { + CheckValidInstance(); + + return _parent.GetPropertyRawValueAsString(_cursor); + } + + /// + /// Gets an enumerator to enumerate the elements of this array. + /// + /// An enumerator for the array elements. + /// + /// This element's is not . + /// + public ArrayEnumerator EnumerateArray() + { + CheckValidInstance(); + + var tokenType = TokenType; + + if (tokenType != ElementTokenType.StartArray) + { + throw new InvalidOperationException(string.Format( + "The requested operation requires an element of type '{0}', but the target element has type '{1}'.", + ElementTokenType.StartArray, + tokenType)) + { + Source = Rethrowable + }; + } + + return new ArrayEnumerator(this); + } + + /// + /// Gets an enumerator to enumerate the properties of this object. + /// + /// An enumerator for the object properties. + /// + /// This element's is not . + /// + public ObjectEnumerator EnumerateObject() + { + CheckValidInstance(); + + var tokenType = TokenType; + + if (tokenType is not ElementTokenType.StartObject) + { + throw new InvalidOperationException(string.Format( + "The requested operation requires an element of type '{0}', but the target element has type '{1}'.", + ElementTokenType.StartObject, + tokenType)); + } + + return new ObjectEnumerator(this); + } + + internal void SetObjectValue(SelectionSet selectionSet) + { + CheckValidInstance(); + + ArgumentNullException.ThrowIfNull(selectionSet); + + if (Type is { } type && !type.IsObjectType()) + { + throw new InvalidOperationException( + string.Format(ResultElement_SetObjectValue_NotObjectType, type)); + } + + var obj = _parent.CreateObject(_cursor, selectionSet: selectionSet); + _parent.AssignObjectOrArray(this, obj); + } + + public void SetObjectValue(int propertyCount) + { + CheckValidInstance(); + + ArgumentOutOfRangeException.ThrowIfLessThan(propertyCount, 0); + + var obj = _parent.CreateObject(_cursor, propertyCount); + _parent.AssignObjectOrArray(this, obj); + } + + public void SetPropertyName(ReadOnlySpan propertyName) + { + CheckValidInstance(); + + if (propertyName.Length == 0) + { + throw new ArgumentNullException(nameof(propertyName)); + } + + var requiredBytes = Encoding.UTF8.GetByteCount(propertyName); + byte[]? rented = null; + var buffer = JsonConstants.StackallocByteThreshold <= requiredBytes + ? stackalloc byte[propertyName.Length] + : (rented = ArrayPool.Shared.Rent(requiredBytes)); + + try + { + var usedBytes = Encoding.UTF8.GetBytes(propertyName, buffer); + _parent.AssignPropertyName(this, buffer[..usedBytes]); + } + finally + { + if (rented is not null) + { + ArrayPool.Shared.Return(rented); + } + } + } + + public void SetPropertyName(ReadOnlySpan propertyName) + { + CheckValidInstance(); + + if (propertyName.Length == 0) + { + throw new ArgumentNullException(nameof(propertyName)); + } + + _parent.AssignPropertyName(this, propertyName); + } + + public void SetArrayValue(int length) + { + CheckValidInstance(); + + ArgumentOutOfRangeException.ThrowIfNegative(length); + + if (Type is { } type && !type.IsListType()) + { + throw new InvalidOperationException( + string.Format(ResultElement_SetArrayValue_NotListType, type)); + } + + var arr = _parent.CreateArray(_cursor, length); + _parent.AssignObjectOrArray(this, arr); + } + + public void SetNullValue() + { + CheckValidInstance(); + + _parent.AssignNullValue(this); + } + + public void SetBooleanValue(bool value) + { + CheckValidInstance(); + + _parent.AssignBooleanValue(this, value); + } + + public void SetStringValue(ReadOnlySpan value) + { + CheckValidInstance(); + + _parent.AssignStringValue(this, value); + } + + public void SetStringValue(ReadOnlySpan value) + { + CheckValidInstance(); + + // If we have an empty string, we can directly assign it. + if (value.Length == 0) + { + _parent.AssignStringValue(this, []); + return; + } + + var requiredBytes = Encoding.UTF8.GetByteCount(value); + byte[]? rented = null; + var buffer = JsonConstants.StackallocByteThreshold <= requiredBytes + ? stackalloc byte[value.Length] + : (rented = ArrayPool.Shared.Rent(requiredBytes)); + + try + { + var usedBytes = Encoding.UTF8.GetBytes(value, buffer); + _parent.AssignStringValue(this, buffer[..usedBytes]); + } + finally + { + if (rented is not null) + { + ArrayPool.Shared.Return(rented); + } + } + } + + public void SetNumberValue(ReadOnlySpan value) + { + CheckValidInstance(); + + _parent.AssignNumberValue(this, value); + } + + public void SetNumberValue(sbyte value) + { + CheckValidInstance(); + + Span buffer = stackalloc byte[4]; + Utf8Formatter.TryFormat(value, buffer, out var bytesWritten); + _parent.AssignNumberValue(this, buffer[..bytesWritten]); + } + + public void SetNumberValue(byte value) + { + CheckValidInstance(); + + Span buffer = stackalloc byte[3]; + Utf8Formatter.TryFormat(value, buffer, out var bytesWritten); + _parent.AssignNumberValue(this, buffer[..bytesWritten]); + } + + public void SetNumberValue(short value) + { + CheckValidInstance(); + + Span buffer = stackalloc byte[6]; + Utf8Formatter.TryFormat(value, buffer, out var bytesWritten); + _parent.AssignNumberValue(this, buffer[..bytesWritten]); + } + + public void SetNumberValue(ushort value) + { + CheckValidInstance(); + + Span buffer = stackalloc byte[5]; + Utf8Formatter.TryFormat(value, buffer, out var bytesWritten); + _parent.AssignNumberValue(this, buffer[..bytesWritten]); + } + + public void SetNumberValue(int value) + { + CheckValidInstance(); + + Span buffer = stackalloc byte[11]; + Utf8Formatter.TryFormat(value, buffer, out var bytesWritten); + _parent.AssignNumberValue(this, buffer[..bytesWritten]); + } + + public void SetNumberValue(uint value) + { + CheckValidInstance(); + + Span buffer = stackalloc byte[10]; + Utf8Formatter.TryFormat(value, buffer, out var bytesWritten); + _parent.AssignNumberValue(this, buffer[..bytesWritten]); + } + + public void SetNumberValue(long value) + { + CheckValidInstance(); + + Span buffer = stackalloc byte[20]; + Utf8Formatter.TryFormat(value, buffer, out var bytesWritten); + _parent.AssignNumberValue(this, buffer[..bytesWritten]); + } + + public void SetNumberValue(ulong value) + { + CheckValidInstance(); + + Span buffer = stackalloc byte[20]; + Utf8Formatter.TryFormat(value, buffer, out var bytesWritten); + _parent.AssignNumberValue(this, buffer[..bytesWritten]); + } + + public void SetNumberValue(float value) + { + CheckValidInstance(); + + Span buffer = stackalloc byte[16]; + Utf8Formatter.TryFormat(value, buffer, out var bytesWritten); + _parent.AssignNumberValue(this, buffer[..bytesWritten]); + } + + public void SetNumberValue(double value) + { + CheckValidInstance(); + + Span buffer = stackalloc byte[24]; + Utf8Formatter.TryFormat(value, buffer, out var bytesWritten); + _parent.AssignNumberValue(this, buffer[..bytesWritten]); + } + + public void SetNumberValue(decimal value) + { + CheckValidInstance(); + + Span buffer = stackalloc byte[31]; + Utf8Formatter.TryFormat(value, buffer, out var bytesWritten); + _parent.AssignNumberValue(this, buffer[..bytesWritten]); + } + + /// + public override string ToString() + { + switch (TokenType) + { + case ElementTokenType.None: + case ElementTokenType.Null: + return string.Empty; + + case ElementTokenType.True: + return bool.TrueString; + + case ElementTokenType.False: + return bool.FalseString; + + case ElementTokenType.Number: + case ElementTokenType.StartArray: + case ElementTokenType.StartObject: + Debug.Assert(_parent != null); + return _parent.GetRawValueAsString(_cursor); + + case ElementTokenType.String: + return GetString()!; + + case ElementTokenType.Comment: + case ElementTokenType.EndArray: + case ElementTokenType.EndObject: + default: + Debug.Fail($"No handler for {nameof(JsonTokenType)}.{TokenType}"); + return string.Empty; + } + } + + private void CheckValidInstance() + { + if (_parent == null) + { + throw new InvalidOperationException(); + } + } +} diff --git a/src/HotChocolate/Core/src/Types/Text/Json/ResultProperty.cs b/src/HotChocolate/Core/src/Types/Text/Json/ResultProperty.cs new file mode 100644 index 00000000000..06e15a446d4 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Text/Json/ResultProperty.cs @@ -0,0 +1,123 @@ +using System.Diagnostics; +using System.Text.Json; +using HotChocolate.Execution.Processing; + +namespace HotChocolate.Text.Json; + +/// +/// Represents a single property for a JSON object. +/// +[DebuggerDisplay("{DebuggerDisplay,nq}")] +public readonly struct ResultProperty +{ + internal ResultProperty(ResultElement value) + { + Value = value; + } + + /// + /// The value of this property. + /// + public ResultElement Value { get; } + + /// + /// The name of this property. + /// This allocates a new string instance for each call. + /// + public string Name => Value.GetPropertyName(); + + public Selection? Selection => Value.Selection; + + public Selection AssertSelection() => Value.AssertSelection(); + + /// + /// Compares to the name of this property. + /// + /// The text to compare against. + /// + /// if the name of this property matches , + /// otherwise. + /// + /// + /// This value's is not . + /// + /// + /// This method is functionally equal to doing an ordinal comparison of and + /// , but can avoid creating the string instance. + /// + public bool NameEquals(string? text) + { + return NameEquals(text.AsSpan()); + } + + /// + /// Compares the text represented by to the name of this property. + /// + /// The UTF-8 encoded text to compare against. + /// + /// if the name of this property has the same UTF-8 encoding as + /// , otherwise. + /// + /// + /// This value's is not . + /// + /// + /// This method is functionally equal to doing an ordinal comparison of and + /// , but can avoid creating the string instance. + /// + public bool NameEquals(ReadOnlySpan utf8Text) + { + return Value.TextEqualsHelper(utf8Text, isPropertyName: true, shouldUnescape: true); + } + + /// + /// Compares to the name of this property. + /// + /// The text to compare against. + /// + /// if the name of this property matches , + /// otherwise. + /// + /// + /// This value's is not . + /// + /// + /// This method is functionally equal to doing an ordinal comparison of and + /// , but can avoid creating the string instance. + /// + public bool NameEquals(ReadOnlySpan text) + { + return Value.TextEqualsHelper(text, isPropertyName: true); + } + + internal bool EscapedNameEquals(ReadOnlySpan utf8Text) + { + return Value.TextEqualsHelper(utf8Text, isPropertyName: true, shouldUnescape: false); + } + + internal ReadOnlySpan NameSpan => Value.GetPropertyNameRaw(); + + /// + /// Provides a representation of the property for + /// debugging purposes. + /// + /// + /// A string containing the un-interpreted value of the property, beginning + /// at the declaring open-quote and ending at the last character that is part of + /// the value. + /// + public override string ToString() + { + return Value.GetPropertyRawText(); + } + + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private string DebuggerDisplay + => Value.ValueKind == JsonValueKind.Undefined ? "" : $"\"{ToString()}\""; + + public void Deconstruct(out Selection selection, out ResultElement value) + { + selection = AssertSelection(); + value = Value; + } +} diff --git a/src/HotChocolate/Core/src/Types/Types/Relay/NodeTypeFeature.cs b/src/HotChocolate/Core/src/Types/Types/Relay/NodeTypeFeature.cs index 9106c0b2bfe..d1616dd5694 100644 --- a/src/HotChocolate/Core/src/Types/Types/Relay/NodeTypeFeature.cs +++ b/src/HotChocolate/Core/src/Types/Types/Relay/NodeTypeFeature.cs @@ -4,11 +4,9 @@ namespace HotChocolate.Types.Relay; internal sealed class NodeTypeFeature : ISealable { - private NodeResolverInfo? _nodeResolver; - public NodeResolverInfo? NodeResolver { - get => _nodeResolver; + get; set { if (IsReadOnly) @@ -17,7 +15,7 @@ public NodeResolverInfo? NodeResolver "The node resolver cannot be set after the feature is sealed."); } - _nodeResolver = value; + field = value; } } diff --git a/src/HotChocolate/Core/test/Execution.Tests/HotChocolate.Execution.Tests.csproj b/src/HotChocolate/Core/test/Execution.Tests/HotChocolate.Execution.Tests.csproj index 17b7c9c5d0d..0f46058c97a 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/HotChocolate.Execution.Tests.csproj +++ b/src/HotChocolate/Core/test/Execution.Tests/HotChocolate.Execution.Tests.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/HotChocolate/Core/test/Fetching.Tests/HotChocolate.Fetching.Tests.csproj b/src/HotChocolate/Core/test/Fetching.Tests/HotChocolate.Fetching.Tests.csproj index cd72005885c..e7cb45365b6 100644 --- a/src/HotChocolate/Core/test/Fetching.Tests/HotChocolate.Fetching.Tests.csproj +++ b/src/HotChocolate/Core/test/Fetching.Tests/HotChocolate.Fetching.Tests.csproj @@ -8,7 +8,6 @@ - diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/HotChocolate.Types.NodaTime.Tests.csproj b/src/HotChocolate/Core/test/Types.NodaTime.Tests/HotChocolate.Types.NodaTime.Tests.csproj index d7cdb3cafe4..f773538a6ff 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/HotChocolate.Types.NodaTime.Tests.csproj +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/HotChocolate.Types.NodaTime.Tests.csproj @@ -6,7 +6,6 @@ - diff --git a/src/HotChocolate/Core/test/Types.Scalars.Tests/HotChocolate.Types.Scalars.Tests.csproj b/src/HotChocolate/Core/test/Types.Scalars.Tests/HotChocolate.Types.Scalars.Tests.csproj index b314284c42c..2d157d98a23 100644 --- a/src/HotChocolate/Core/test/Types.Scalars.Tests/HotChocolate.Types.Scalars.Tests.csproj +++ b/src/HotChocolate/Core/test/Types.Scalars.Tests/HotChocolate.Types.Scalars.Tests.csproj @@ -6,7 +6,6 @@ - diff --git a/src/HotChocolate/CostAnalysis/src/CostAnalysis/HotChocolate.CostAnalysis.csproj b/src/HotChocolate/CostAnalysis/src/CostAnalysis/HotChocolate.CostAnalysis.csproj index c3bd3025c3f..a6baf2a2152 100644 --- a/src/HotChocolate/CostAnalysis/src/CostAnalysis/HotChocolate.CostAnalysis.csproj +++ b/src/HotChocolate/CostAnalysis/src/CostAnalysis/HotChocolate.CostAnalysis.csproj @@ -6,7 +6,6 @@ - diff --git a/src/HotChocolate/Data/src/Data/HotChocolate.Data.csproj b/src/HotChocolate/Data/src/Data/HotChocolate.Data.csproj index c6d4fbb16bf..f5f21fbc643 100644 --- a/src/HotChocolate/Data/src/Data/HotChocolate.Data.csproj +++ b/src/HotChocolate/Data/src/Data/HotChocolate.Data.csproj @@ -13,7 +13,7 @@ - + diff --git a/src/HotChocolate/Diagnostics/src/Diagnostics/HotChocolate.Diagnostics.csproj b/src/HotChocolate/Diagnostics/src/Diagnostics/HotChocolate.Diagnostics.csproj index 886bfa3e70d..49885604251 100644 --- a/src/HotChocolate/Diagnostics/src/Diagnostics/HotChocolate.Diagnostics.csproj +++ b/src/HotChocolate/Diagnostics/src/Diagnostics/HotChocolate.Diagnostics.csproj @@ -16,7 +16,7 @@ - + diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Nodes/IncludeConditionCollection.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Nodes/IncludeConditionCollection.cs index a4818fa44ee..c6eed75faa7 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Nodes/IncludeConditionCollection.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Nodes/IncludeConditionCollection.cs @@ -37,14 +37,7 @@ public bool Contains(IncludeCondition item) => _dictionary.ContainsKey(item); public int IndexOf(IncludeCondition item) - { - if (_dictionary.TryGetValue(item, out var index)) - { - return index; - } - - return -1; - } + => _dictionary.GetValueOrDefault(item, -1); public void CopyTo(IncludeCondition[] array, int arrayIndex) => _dictionary.Keys.CopyTo(array, arrayIndex); diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Nodes/SelectionSet.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Nodes/SelectionSet.cs index 01a4d0a8b48..2ab53071b58 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Nodes/SelectionSet.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Execution/Nodes/SelectionSet.cs @@ -5,6 +5,11 @@ namespace HotChocolate.Fusion.Execution.Nodes; +/// +/// A selection set is primarily composed of field selections. +/// When needed a selection set can preserve fragments so that the execution engine +/// can branch the processing of these fragments. +/// public sealed class SelectionSet : ISelectionSet { private readonly Selection[] _selections; @@ -44,6 +49,13 @@ public SelectionSet(int id, IObjectTypeDefinition type, Selection[] selections, /// public IObjectTypeDefinition Type { get; } + /// + /// Gets the declaring operation. + /// + public Operation DeclaringOperation { get; private set; } = null!; + + IOperation ISelectionSet.DeclaringOperation => DeclaringOperation; + /// /// Gets the selections that shall be executed. /// @@ -81,13 +93,6 @@ public bool TryGetSelection(string responseName, [NotNullWhen(true)] out Selecti public bool TryGetSelection(ReadOnlySpan utf8ResponseName, [NotNullWhen(true)] out Selection? selection) => _utf8ResponseNameLookup.TryGetSelection(utf8ResponseName, out selection); - /// - /// Gets the declaring operation. - /// - public Operation DeclaringOperation { get; private set; } = null!; - - IOperation ISelectionSet.DeclaringOperation => DeclaringOperation; - internal void Seal(Operation operation) { if (_isSealed) diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/HotChocolate.Fusion.Execution.csproj b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/HotChocolate.Fusion.Execution.csproj index 1ff85c15c78..784fba4a269 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/HotChocolate.Fusion.Execution.csproj +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/HotChocolate.Fusion.Execution.csproj @@ -90,7 +90,6 @@ Transport\Http\Sse\SseReader.cs - SourceResultElement.cs 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..0a84bde497f 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 @@ -15,7 +15,7 @@ internal readonly struct DbRow // 27 bits for location + 2 bits OpRefType + 3 reserved bits private readonly int _locationAndOpRefType; - // Sign bit for HasComplexChildren + 31 bits for size/length + // A Sign bit for HasComplexChildren + 31 bits for size/length private readonly int _sizeOrLengthUnion; // 4 bits TokenType + 27 bits NumberOfRows + 1 reserved bit @@ -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/CompositeResultDocument.MetaDb.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/CompositeResultDocument.MetaDb.cs index 5d2d0d0d9c8..bda078eb5bf 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/CompositeResultDocument.MetaDb.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/CompositeResultDocument.MetaDb.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using HotChocolate.Buffers; using static HotChocolate.Fusion.Text.Json.MetaDbEventSource; namespace HotChocolate.Fusion.Text.Json; @@ -26,7 +27,7 @@ internal static MetaDb CreateForEstimatedRows(int estimatedRows) log.MetaDbCreated(2, estimatedRows, 1); // Rent the first chunk now to avoid branching on first append - chunks[0] = MetaDbMemory.Rent(); + chunks[0] = JsonMemory.Rent(JsonMemoryKind.Metadata); log.ChunkAllocated(2, 0); for (var i = 1; i < chunks.Length; i++) @@ -101,7 +102,7 @@ internal Cursor Append( // if the chunk is empty we did not yet rent any memory for it if (chunk.Length == 0) { - chunk = chunks[chunkIndex] = MetaDbMemory.Rent(); + chunk = chunks[chunkIndex] = JsonMemory.Rent(JsonMemoryKind.Metadata); log.ChunkAllocated(2, chunkIndex); } @@ -291,7 +292,7 @@ internal int GetSizeOrLength(Cursor cursor) internal void SetSizeOrLength(Cursor cursor, int sizeOrLength) { AssertValidCursor(cursor); - Debug.Assert(sizeOrLength >= 0 && sizeOrLength <= int.MaxValue, "SizeOrLength value exceeds 31-bit limit"); + Debug.Assert(sizeOrLength >= 0, "SizeOrLength value exceeds 31-bit limit"); var fieldSpan = _chunks[cursor.Chunk].AsSpan(cursor.ByteOffset + 4); var currentValue = MemoryMarshal.Read(fieldSpan); @@ -350,7 +351,7 @@ private void AssertValidCursor(Cursor cursor) 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"); + Debug.Assert(cursor.ByteOffset + DbRow.Size <= JsonMemory.BufferSize, "Cursor byte offset out of bounds"); } public void Dispose() @@ -369,7 +370,7 @@ public void Dispose() break; } - MetaDbMemory.Return(chunk); + JsonMemory.Return(JsonMemoryKind.Metadata, chunk); } chunks.Clear(); diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/CompositeResultElement.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/CompositeResultElement.cs index 551df5e0349..a72d6ac3b94 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/CompositeResultElement.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/CompositeResultElement.cs @@ -24,6 +24,13 @@ internal CompositeResultElement(CompositeResultDocument parent, CompositeResultD _cursor = cursor; } + /// + /// Writes this element as JSON to the specified buffer writer. + /// + /// The buffer writer to write to. + /// + /// true to write indented JSON; otherwise, false. + /// public void WriteTo(IBufferWriter writer, bool indented = false) { var formatter = new CompositeResultDocument.RawJsonFormatter(_parent, writer, indented); @@ -41,25 +48,19 @@ public void WriteTo(IBufferWriter writer, bool indented = false) private ElementTokenType TokenType => _parent?.GetElementTokenType(_cursor) ?? ElementTokenType.None; /// - /// The that the value is. + /// Gets the of this element. /// - /// - /// The parent has been disposed. - /// public JsonValueKind ValueKind => TokenType.ToValueKind(); /// - /// Get the value at a specified index when the current value is a - /// . + /// Gets the element at the specified index when the current element is an array. /// + /// The zero-based index of the element to get. /// - /// This value's is not . + /// This element's is not . /// /// - /// is not in the range [0, ()). - /// - /// - /// The parent has been disposed. + /// is not in the range [0, ()). /// public CompositeResultElement this[int index] { @@ -71,6 +72,9 @@ public CompositeResultElement this[int index] } } + /// + /// Gets the operation this element belongs to. + /// public Operation Operation { get @@ -81,6 +85,10 @@ public Operation Operation } } + /// + /// Gets the if this element represents the data of a selection set; + /// otherwise, null. + /// public SelectionSet? SelectionSet { get @@ -91,6 +99,10 @@ public SelectionSet? SelectionSet } } + /// + /// Gets the if this element represents the data of a field selection; + /// otherwise, null. + /// public Selection? Selection { get @@ -107,6 +119,10 @@ public Selection? Selection } } + /// + /// Gets the if this element represents the data of a field selection; + /// otherwise, null. + /// public IType? Type { get @@ -134,6 +150,9 @@ public IType? Type } } + /// + /// Gets a value indicating whether this element has been invalidated during null propagation. + /// public bool IsInvalidated { get @@ -144,6 +163,9 @@ public bool IsInvalidated } } + /// + /// Gets a value indicating whether this element is either null or was invalidated during null propagation. + /// public bool IsNullOrInvalidated { get @@ -157,6 +179,9 @@ public bool IsNullOrInvalidated } } + /// + /// Gets the path to this element within the result document. + /// public Path Path { get @@ -167,6 +192,9 @@ public Path Path } } + /// + /// Gets the parent element that contains this element. + /// public CompositeResultElement Parent { get @@ -177,6 +205,9 @@ public CompositeResultElement Parent } } + /// + /// Gets a value indicating whether this element is nullable according to the GraphQL type system. + /// public bool IsNullable { get @@ -192,6 +223,10 @@ public bool IsNullable } } + /// + /// Gets a value indicating whether this element represents internal data + /// that is required for processing and must not be written to the GraphQL response. + /// public bool IsInternal { get @@ -202,6 +237,15 @@ public bool IsInternal } } + /// + /// Gets the for this element. + /// + /// + /// The instance. + /// + /// + /// This element does not represent the data of a selection set. + /// public SelectionSet AssertSelectionSet() { var selectionSet = SelectionSet; @@ -214,6 +258,15 @@ public SelectionSet AssertSelectionSet() return selectionSet; } + /// + /// Gets the for this element. + /// + /// + /// The instance. + /// + /// + /// This element does not represent the data of a field selection. + /// public Selection AssertSelection() { var selection = Selection; @@ -226,6 +279,15 @@ public Selection AssertSelection() return selection; } + /// + /// Gets the for this element. + /// + /// + /// The instance. + /// + /// + /// This element does not represent the data of a field selection. + /// public IType AssertType() { var type = Type; @@ -238,6 +300,10 @@ public IType AssertType() return type; } + /// + /// Marks this element as invalidated, which occurs during null propagation + /// when a non-nullable field returns null. + /// public void Invalidate() { CheckValidInstance(); @@ -246,8 +312,11 @@ public void Invalidate() } /// - /// Get the number of values contained within the current array value. + /// Gets the number of elements contained within the current array element. /// + /// + /// The number of elements in the array. + /// public int GetArrayLength() { CheckValidInstance(); @@ -256,8 +325,11 @@ public int GetArrayLength() } /// - /// Get the number of properties contained within the current object value. + /// Gets the number of properties contained within the current object element. /// + /// + /// The number of properties in the object. + /// public int GetPropertyCount() { CheckValidInstance(); @@ -265,6 +337,14 @@ public int GetPropertyCount() return _parent.GetPropertyCount(_cursor); } + /// + /// Gets a property by name when the current element is an object. + /// + /// The name of the property to find. + /// The property value. + /// + /// No property with the specified name was found. + /// public CompositeResultElement GetProperty(string propertyName) { ArgumentNullException.ThrowIfNull(propertyName); @@ -277,6 +357,14 @@ public CompositeResultElement GetProperty(string propertyName) throw new KeyNotFoundException(); } + /// + /// Gets a property by UTF-8 encoded name when the current element is an object. + /// + /// The UTF-8 encoded name of the property to find. + /// The property value. + /// + /// No property with the specified name was found. + /// public CompositeResultElement GetProperty(ReadOnlySpan utf8PropertyName) { if (TryGetProperty(utf8PropertyName, out var property)) @@ -287,6 +375,16 @@ public CompositeResultElement GetProperty(ReadOnlySpan utf8PropertyName) throw new KeyNotFoundException(); } + /// + /// Attempts to get a property by name when the current element is an object. + /// + /// The name of the property to find. + /// + /// When this method returns, contains the property value if found; otherwise, the default value. + /// + /// + /// true if the property was found; otherwise, false. + /// public bool TryGetProperty(string propertyName, out CompositeResultElement value) { ArgumentNullException.ThrowIfNull(propertyName); @@ -294,6 +392,16 @@ public bool TryGetProperty(string propertyName, out CompositeResultElement value return _parent.TryGetNamedPropertyValue(_cursor, propertyName, out value); } + /// + /// Attempts to get a property by UTF-8 encoded name when the current element is an object. + /// + /// The UTF-8 encoded name of the property to find. + /// + /// When this method returns, contains the property value if found; otherwise, the default value. + /// + /// + /// true if the property was found; otherwise, false. + /// public bool TryGetProperty(ReadOnlySpan utf8PropertyName, out CompositeResultElement value) { CheckValidInstance(); @@ -301,6 +409,13 @@ public bool TryGetProperty(ReadOnlySpan utf8PropertyName, out CompositeRes return _parent.TryGetNamedPropertyValue(_cursor, utf8PropertyName, out value); } + /// + /// Gets the value as a . + /// + /// The boolean value. + /// + /// This element's is not or . + /// public bool GetBoolean() { var type = TokenType; @@ -324,6 +439,10 @@ static bool ThrowJsonElementWrongTypeException(ElementTokenType actualType) } } + /// + /// Gets the value as a . + /// + /// The string value, or null if this element is a JSON null. public string? GetString() { CheckValidInstance(); @@ -331,6 +450,13 @@ static bool ThrowJsonElementWrongTypeException(ElementTokenType actualType) return _parent.GetString(_cursor, ElementTokenType.String); } + /// + /// Gets the value as a non-null . + /// + /// The string value. + /// + /// This element is a JSON null. + /// public string AssertString() { CheckValidInstance(); @@ -338,6 +464,11 @@ public string AssertString() return _parent.GetRequiredString(_cursor, ElementTokenType.String); } + /// + /// Attempts to get the value as an . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. public bool TryGetSByte(out sbyte value) { CheckValidInstance(); @@ -345,8 +476,18 @@ public bool TryGetSByte(out sbyte value) return _parent.TryGetValue(_cursor, out value); } + /// + /// Gets the value as an . + /// + /// The value. + /// The value cannot be parsed as an . public sbyte GetSByte() => TryGetSByte(out var value) ? value : throw new FormatException(); + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. public bool TryGetByte(out byte value) { CheckValidInstance(); @@ -354,6 +495,11 @@ public bool TryGetByte(out byte value) return _parent.TryGetValue(_cursor, out value); } + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . public byte GetByte() { if (TryGetByte(out var value)) @@ -364,6 +510,11 @@ public byte GetByte() throw new FormatException(); } + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. public bool TryGetInt16(out short value) { CheckValidInstance(); @@ -371,6 +522,11 @@ public bool TryGetInt16(out short value) return _parent.TryGetValue(_cursor, out value); } + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . public short GetInt16() { if (TryGetInt16(out var value)) @@ -381,6 +537,11 @@ public short GetInt16() throw new FormatException(); } + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. public bool TryGetUInt16(out ushort value) { CheckValidInstance(); @@ -388,6 +549,11 @@ public bool TryGetUInt16(out ushort value) return _parent.TryGetValue(_cursor, out value); } + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . public ushort GetUInt16() { if (TryGetUInt16(out var value)) @@ -398,6 +564,11 @@ public ushort GetUInt16() throw new FormatException(); } + /// + /// Attempts to get the value as an . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. public bool TryGetInt32(out int value) { CheckValidInstance(); @@ -405,6 +576,11 @@ public bool TryGetInt32(out int value) return _parent.TryGetValue(_cursor, out value); } + /// + /// Gets the value as an . + /// + /// The value. + /// The value cannot be parsed as an . public int GetInt32() { if (!TryGetInt32(out var value)) @@ -415,6 +591,11 @@ public int GetInt32() return value; } + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. public bool TryGetUInt32(out uint value) { CheckValidInstance(); @@ -422,6 +603,11 @@ public bool TryGetUInt32(out uint value) return _parent.TryGetValue(_cursor, out value); } + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . public uint GetUInt32() { if (!TryGetUInt32(out var value)) @@ -432,6 +618,11 @@ public uint GetUInt32() return value; } + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. public bool TryGetInt64(out long value) { CheckValidInstance(); @@ -439,6 +630,11 @@ public bool TryGetInt64(out long value) return _parent.TryGetValue(_cursor, out value); } + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . public long GetInt64() { if (!TryGetInt64(out var value)) @@ -449,6 +645,11 @@ public long GetInt64() return value; } + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. public bool TryGetUInt64(out ulong value) { CheckValidInstance(); @@ -456,6 +657,11 @@ public bool TryGetUInt64(out ulong value) return _parent.TryGetValue(_cursor, out value); } + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . public ulong GetUInt64() { if (!TryGetUInt64(out var value)) @@ -466,6 +672,11 @@ public ulong GetUInt64() return value; } + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. public bool TryGetDouble(out double value) { CheckValidInstance(); @@ -473,6 +684,11 @@ public bool TryGetDouble(out double value) return _parent.TryGetValue(_cursor, out value); } + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . public double GetDouble() { if (!TryGetDouble(out var value)) @@ -483,6 +699,11 @@ public double GetDouble() return value; } + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. public bool TryGetSingle(out float value) { CheckValidInstance(); @@ -490,6 +711,11 @@ public bool TryGetSingle(out float value) return _parent.TryGetValue(_cursor, out value); } + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . public float GetSingle() { if (!TryGetSingle(out var value)) @@ -500,6 +726,11 @@ public float GetSingle() return value; } + /// + /// Attempts to get the value as a . + /// + /// When this method returns, contains the parsed value. + /// true if the value could be parsed; otherwise, false. public bool TryGetDecimal(out decimal value) { CheckValidInstance(); @@ -507,6 +738,11 @@ public bool TryGetDecimal(out decimal value) return _parent.TryGetValue(_cursor, out value); } + /// + /// Gets the value as a . + /// + /// The value. + /// The value cannot be parsed as a . public decimal GetDecimal() { if (!TryGetDecimal(out var value)) @@ -531,6 +767,10 @@ internal ReadOnlySpan GetPropertyNameRaw() return _parent.GetPropertyNameRaw(_cursor); } + /// + /// Gets the raw JSON text representing this element. + /// + /// The raw JSON text. public string GetRawText() { CheckValidInstance(); @@ -545,6 +785,13 @@ internal ReadOnlySpan GetRawValue(bool includeQuotes = true) return _parent.GetRawValue(_cursor, includeQuotes: true); } + /// + /// Compares the text of this element to the specified string. + /// + /// The text to compare against. + /// + /// true if this element's value equals the specified text; otherwise, false. + /// public bool ValueEquals(string? text) { if (TokenType == ElementTokenType.Null) @@ -555,6 +802,13 @@ public bool ValueEquals(string? text) return TextEqualsHelper(text.AsSpan(), isPropertyName: false); } + /// + /// Compares the text of this element to the specified UTF-8 encoded text. + /// + /// The UTF-8 encoded text to compare against. + /// + /// true if this element's value equals the specified text; otherwise, false. + /// public bool ValueEquals(ReadOnlySpan utf8Text) { if (TokenType == ElementTokenType.Null) @@ -567,6 +821,13 @@ public bool ValueEquals(ReadOnlySpan utf8Text) return TextEqualsHelper(utf8Text, isPropertyName: false, shouldUnescape: true); } + /// + /// Compares the text of this element to the specified character span. + /// + /// The text to compare against. + /// + /// true if this element's value equals the specified text; otherwise, false. + /// public bool ValueEquals(ReadOnlySpan text) { if (TokenType == ElementTokenType.Null) @@ -600,6 +861,13 @@ internal string GetPropertyRawText() return _parent.GetPropertyRawValueAsString(_cursor); } + /// + /// Gets an enumerator to enumerate the elements of this array. + /// + /// An enumerator for the array elements. + /// + /// This element's is not . + /// public ArrayEnumerator EnumerateArray() { CheckValidInstance(); @@ -620,6 +888,13 @@ public ArrayEnumerator EnumerateArray() return new ArrayEnumerator(this); } + /// + /// Gets an enumerator to enumerate the properties of this object. + /// + /// An enumerator for the object properties. + /// + /// This element's is not . + /// public ObjectEnumerator EnumerateObject() { CheckValidInstance(); @@ -671,6 +946,7 @@ internal void SetNullValue() _parent.AssignNullValue(this); } + /// public override string ToString() { switch (TokenType) diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/CompositeResultProperty.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/CompositeResultProperty.cs index d34e5576f8c..e61eab856d8 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/CompositeResultProperty.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/CompositeResultProperty.cs @@ -28,7 +28,7 @@ internal CompositeResultProperty(CompositeResultElement value) public Selection? Selection => Value.Selection; - public Selection GetRequiredSelection() => Value.AssertSelection(); + public Selection AssertSelection() => Value.AssertSelection(); /// /// Compares to the name of this property. @@ -117,7 +117,7 @@ private string DebuggerDisplay public void Deconstruct(out Selection selection, out CompositeResultElement value) { - selection = GetRequiredSelection(); + selection = AssertSelection(); value = Value; } } 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/JsonMemory.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonMemory.cs deleted file mode 100644 index e11f24de5ff..00000000000 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonMemory.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.Buffers; -using System.Runtime.InteropServices; -using HotChocolate.Fusion.Buffers; - -namespace HotChocolate.Fusion.Text.Json; - -/// -/// Manages the memory for storing JSON data. -/// -public static class JsonMemory -{ - public const int BufferSize = 1 << 17; - - private static readonly FixedSizeArrayPool s_pool = new(1, BufferSize, 128 * 6, preAllocate: true); - private static readonly ArrayPool s_chunkPool = ArrayPool.Shared; - - public static byte[] Rent() - => s_pool.Rent(); - - public static byte[][] RentRange(int requiredChunks) - { - var chunks = s_chunkPool.Rent(requiredChunks); - - for (var i = 0; i < requiredChunks; i++) - { - chunks[i] = s_pool.Rent(); - } - - return chunks; - } - - public static void Return(byte[] chunk) - => s_pool.Return(chunk); - - public static void Return(List chunks) - { - ArgumentNullException.ThrowIfNull(chunks); - - foreach (var chunk in CollectionsMarshal.AsSpan(chunks)) - { - s_pool.Return(chunk); - } - } - - public static void Return(byte[][] chunks, int usedChunks) - { - ArgumentNullException.ThrowIfNull(chunks); - - foreach (var chunk in chunks.AsSpan(0, usedChunks)) - { - s_pool.Return(chunk); - } - } -} diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonReaderHelper.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonReaderHelper.cs index 8f3d1a55222..edbbcac3f24 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonReaderHelper.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/JsonReaderHelper.cs @@ -4,9 +4,16 @@ using System.Runtime.CompilerServices; using System.Text; using System.Text.Json; + +#if FUSION using static HotChocolate.Fusion.Properties.FusionExecutionResources; namespace HotChocolate.Fusion.Text.Json; +#else +using static HotChocolate.Properties.TextJsonResources; + +namespace HotChocolate.Text.Json; +#endif internal static class JsonReaderHelper { 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/Fusion-vnext/src/Fusion.Execution/Text/Json/MetaDbMemory.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/MetaDbMemory.cs deleted file mode 100644 index 20c4232e4dd..00000000000 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/MetaDbMemory.cs +++ /dev/null @@ -1,17 +0,0 @@ -using HotChocolate.Fusion.Buffers; - -namespace HotChocolate.Fusion.Text.Json; - -public static class MetaDbMemory -{ - public const int BufferSize = 1 << 17; - public const int RowsPerChunk = 6552; - - private static readonly FixedSizeArrayPool s_pool = new(2, BufferSize, 128 * 6, preAllocate: true); - - public static byte[] Rent() - => s_pool.Rent(); - - public static void Return(byte[] chunk) - => s_pool.Return(chunk); -} diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocument.MetaDb.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocument.MetaDb.cs index 0e5b4815f52..08fc5aadc6e 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocument.MetaDb.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocument.MetaDb.cs @@ -3,6 +3,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text.Json; +using HotChocolate.Buffers; using static HotChocolate.Fusion.Text.Json.MetaDbEventSource; namespace HotChocolate.Fusion.Text.Json; @@ -23,7 +24,7 @@ internal struct MetaDb : IDisposable static MetaDb() { Debug.Assert( - MetaDbMemory.BufferSize >= Cursor.ChunkBytes, + JsonMemory.BufferSize >= Cursor.ChunkBytes, "MetaDb.BufferSize must match Cursor.ChunkBytes for index math to align."); } @@ -36,7 +37,7 @@ internal static MetaDb CreateForEstimatedRows(int estimatedRows) log.MetaDbCreated(1, estimatedRows, 1); // Rent the first chunk now to avoid branching on first append - chunks[0] = MetaDbMemory.Rent(); + chunks[0] = JsonMemory.Rent(JsonMemoryKind.Metadata); log.ChunkAllocated(1, 0); for (var i = 1; i < chunks.Length; i++) @@ -101,7 +102,7 @@ internal Cursor Append( // if the chunk is empty we did not yet rent any memory for it if (chunk.Length == 0) { - chunk = chunks[chunkIndex] = MetaDbMemory.Rent(); + chunk = chunks[chunkIndex] = JsonMemory.Rent(JsonMemoryKind.Metadata); log.ChunkAllocated(1, chunkIndex); } @@ -215,7 +216,7 @@ public void Dispose() break; } - MetaDbMemory.Return(chunk); + JsonMemory.Return(JsonMemoryKind.Metadata, chunk); } chunks.Clear(); diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocument.Parse.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocument.Parse.cs index f7af9525d73..6c3fbf84af7 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocument.Parse.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocument.Parse.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using System.Text.Json; +using HotChocolate.Buffers; namespace HotChocolate.Fusion.Text.Json; @@ -62,7 +63,7 @@ internal static SourceResultDocument ParseSingleSegment( { foreach (var chunk in dataChunksSpan) { - JsonMemory.Return(chunk); + JsonMemory.Return(JsonMemoryKind.Json, chunk); } dataChunksSpan.Clear(); @@ -121,7 +122,7 @@ internal static SourceResultDocument ParseMultipleSegments( { foreach (var chunk in dataChunksSpan) { - JsonMemory.Return(chunk); + JsonMemory.Return(JsonMemoryKind.Json, chunk); } dataChunksSpan.Clear(); diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocument.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocument.cs index 31bce68fb06..b51dc541331 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocument.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocument.cs @@ -2,6 +2,7 @@ using System.Runtime.CompilerServices; using System.Text; using System.Text.Json; +using HotChocolate.Buffers; namespace HotChocolate.Fusion.Text.Json; @@ -228,7 +229,7 @@ public void Dispose() { if (_pooledMemory) { - JsonMemory.Return(_dataChunks, _usedChunks); + JsonMemory.Return(JsonMemoryKind.Json, _dataChunks, _usedChunks); if (_dataChunks.Length > 1) { diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocumentBuilder.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocumentBuilder.cs index 2c5c1b28a13..57a69863033 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocumentBuilder.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/SourceResultDocumentBuilder.cs @@ -116,7 +116,7 @@ public SourceResultDocument Build() } // Rent the first chunk - chunks[0] = JsonMemory.Rent(); + chunks[0] = JsonMemory.Rent(JsonMemoryKind.Json); var currentChunkIndex = 0; var currentChunkOffset = 0; @@ -132,7 +132,7 @@ public SourceResultDocument Build() } catch { - JsonMemory.Return(chunks, currentChunkIndex + 1); + JsonMemory.Return(JsonMemoryKind.Json, chunks, currentChunkIndex + 1); ArrayPool.Shared.Return(chunks); throw; } @@ -334,7 +334,7 @@ private static void EnsureChunkCapacity( { Debug.Fail("foo"); - chunks[currentChunkIndex] = JsonMemory.Rent(); + chunks[currentChunkIndex] = JsonMemory.Rent(JsonMemoryKind.Json); } } diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/ThrowHelper.cs b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/ThrowHelper.cs index 992c9511d9d..5461a82bcee 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/ThrowHelper.cs +++ b/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Text/Json/ThrowHelper.cs @@ -1,7 +1,14 @@ using System.Diagnostics.CodeAnalysis; + +#if FUSION using static HotChocolate.Fusion.Properties.FusionExecutionResources; namespace HotChocolate.Fusion.Text.Json; +#else +using static HotChocolate.Properties.TextJsonResources; + +namespace HotChocolate.Text.Json; +#endif internal static class ThrowHelper { 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 - - - - diff --git a/src/HotChocolate/MongoDb/src/Data/HotChocolate.Data.MongoDb.csproj b/src/HotChocolate/MongoDb/src/Data/HotChocolate.Data.MongoDb.csproj index 35ad9d7e70c..5a8c1cfd60f 100644 --- a/src/HotChocolate/MongoDb/src/Data/HotChocolate.Data.MongoDb.csproj +++ b/src/HotChocolate/MongoDb/src/Data/HotChocolate.Data.MongoDb.csproj @@ -8,7 +8,7 @@ - + diff --git a/src/HotChocolate/MongoDb/src/Types/HotChocolate.Types.MongoDb.csproj b/src/HotChocolate/MongoDb/src/Types/HotChocolate.Types.MongoDb.csproj index def8e832c74..3be34f6d613 100644 --- a/src/HotChocolate/MongoDb/src/Types/HotChocolate.Types.MongoDb.csproj +++ b/src/HotChocolate/MongoDb/src/Types/HotChocolate.Types.MongoDb.csproj @@ -9,7 +9,6 @@ - diff --git a/src/HotChocolate/Core/src/Types.Shared/BuiltInTypes.cs b/src/HotChocolate/Primitives/src/Primitives/Types/BuiltInTypes.cs similarity index 60% rename from src/HotChocolate/Core/src/Types.Shared/BuiltInTypes.cs rename to src/HotChocolate/Primitives/src/Primitives/Types/BuiltInTypes.cs index ba792c03e00..16d5e401aca 100644 --- a/src/HotChocolate/Core/src/Types.Shared/BuiltInTypes.cs +++ b/src/HotChocolate/Primitives/src/Primitives/Types/BuiltInTypes.cs @@ -1,35 +1,35 @@ using HotChocolate.Language; -namespace HotChocolate.Utilities.Introspection; +namespace HotChocolate.Types; public static class BuiltInTypes { private static readonly HashSet s_typeNames = [ - WellKnownTypes.__Directive, - WellKnownTypes.__DirectiveLocation, - WellKnownTypes.__EnumValue, - WellKnownTypes.__Field, - WellKnownTypes.__InputValue, - WellKnownTypes.__Schema, - WellKnownTypes.__Type, - WellKnownTypes.__TypeKind, - WellKnownTypes.String, - WellKnownTypes.Boolean, - WellKnownTypes.Float, - WellKnownTypes.ID, - WellKnownTypes.Int + IntrospectionTypeNames.__Directive, + IntrospectionTypeNames.__DirectiveLocation, + IntrospectionTypeNames.__EnumValue, + IntrospectionTypeNames.__Field, + IntrospectionTypeNames.__InputValue, + IntrospectionTypeNames.__Schema, + IntrospectionTypeNames.__Type, + IntrospectionTypeNames.__TypeKind, + SpecScalarNames.String.Name, + SpecScalarNames.Boolean.Name, + SpecScalarNames.Float.Name, + SpecScalarNames.ID.Name, + SpecScalarNames.Int.Name ]; private static readonly HashSet s_directiveNames = [ - WellKnownDirectives.Skip, - WellKnownDirectives.Include, - WellKnownDirectives.Deprecated, - WellKnownDirectives.Defer, - WellKnownDirectives.Stream, - WellKnownDirectives.SpecifiedBy, - WellKnownDirectives.OneOf + DirectiveNames.Skip.Name, + DirectiveNames.Include.Name, + DirectiveNames.Deprecated.Name, + DirectiveNames.Defer.Name, + DirectiveNames.Stream.Name, + DirectiveNames.SpecifiedBy.Name, + DirectiveNames.OneOf.Name ]; public static bool IsBuiltInType(string name) diff --git a/src/HotChocolate/Primitives/src/Primitives/Types/IntrospectionTypeNames.cs b/src/HotChocolate/Primitives/src/Primitives/Types/IntrospectionTypeNames.cs new file mode 100644 index 00000000000..9057e806224 --- /dev/null +++ b/src/HotChocolate/Primitives/src/Primitives/Types/IntrospectionTypeNames.cs @@ -0,0 +1,15 @@ +namespace HotChocolate.Types; + +public static class IntrospectionTypeNames +{ + // ReSharper disable InconsistentNaming + public const string __Directive = nameof(__Directive); + public const string __DirectiveLocation = nameof(__DirectiveLocation); + public const string __EnumValue = nameof(__EnumValue); + public const string __Field = nameof(__Field); + public const string __InputValue = nameof(__InputValue); + public const string __Schema = nameof(__Schema); + public const string __Type = nameof(__Type); + public const string __TypeKind = nameof(__TypeKind); + // ReSharper restore InconsistentNaming +} diff --git a/src/HotChocolate/Spatial/src/Types/HotChocolate.Types.Spatial.csproj b/src/HotChocolate/Spatial/src/Types/HotChocolate.Types.Spatial.csproj index 505cf7f2374..52a2228e685 100644 --- a/src/HotChocolate/Spatial/src/Types/HotChocolate.Types.Spatial.csproj +++ b/src/HotChocolate/Spatial/src/Types/HotChocolate.Types.Spatial.csproj @@ -12,7 +12,6 @@ - diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Buffers/FixedSizeArrayPool.cs b/src/HotChocolate/Utilities/src/Utilities.Buffers/FixedSizeArrayPool.cs similarity index 90% rename from src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Buffers/FixedSizeArrayPool.cs rename to src/HotChocolate/Utilities/src/Utilities.Buffers/FixedSizeArrayPool.cs index 46b3c20f5a1..a8e4df61cba 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Buffers/FixedSizeArrayPool.cs +++ b/src/HotChocolate/Utilities/src/Utilities.Buffers/FixedSizeArrayPool.cs @@ -1,8 +1,8 @@ using System.Diagnostics; -using static HotChocolate.Fusion.Buffers.FixedSizeArrayPoolEventSource; -using static HotChocolate.Fusion.Properties.FusionExecutionResources; +using static HotChocolate.Buffers.FixedSizeArrayPoolEventSource; +using static HotChocolate.Buffers.Properties.BuffersResources; -namespace HotChocolate.Fusion.Buffers; +namespace HotChocolate.Buffers; internal sealed class FixedSizeArrayPool { @@ -45,7 +45,14 @@ public byte[] Rent() public void Return(byte[] array) { +#if NET8_0_OR_GREATER ArgumentNullException.ThrowIfNull(array); +#else + if(array is null) + { + throw new ArgumentNullException(nameof(array)); + } +#endif if (array.Length != _arraySize) { @@ -149,7 +156,11 @@ internal Bucket(int poolId, int bufferLength, int numberOfBuffers, bool preAlloc internal bool Return(byte[] array) { - Debug.Assert(array.Length == _bufferLength); + // if the returned array has not the expected size we will reject it without throwing an error. + if (array.Length != _bufferLength) + { + return false; + } var returned = false; var lockTaken = false; diff --git a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Buffers/FixedSizeArrayPoolEventSource.cs b/src/HotChocolate/Utilities/src/Utilities.Buffers/FixedSizeArrayPoolEventSource.cs similarity index 95% rename from src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Buffers/FixedSizeArrayPoolEventSource.cs rename to src/HotChocolate/Utilities/src/Utilities.Buffers/FixedSizeArrayPoolEventSource.cs index 0f550556dd3..d219a9e4e2b 100644 --- a/src/HotChocolate/Fusion-vnext/src/Fusion.Execution/Buffers/FixedSizeArrayPoolEventSource.cs +++ b/src/HotChocolate/Utilities/src/Utilities.Buffers/FixedSizeArrayPoolEventSource.cs @@ -1,8 +1,8 @@ using System.Diagnostics.Tracing; -namespace HotChocolate.Fusion.Buffers; +namespace HotChocolate.Buffers; -[EventSource(Name = "HotChocolate-Fusion-FixedSizeArrayPool")] +[EventSource(Name = "HotChocolate-Buffers-FixedSizeArrayPool")] internal sealed class FixedSizeArrayPoolEventSource : EventSource { public static readonly FixedSizeArrayPoolEventSource Log = new(); diff --git a/src/HotChocolate/Utilities/src/Utilities.Buffers/FixedSizeArrayPoolKinds.cs b/src/HotChocolate/Utilities/src/Utilities.Buffers/FixedSizeArrayPoolKinds.cs new file mode 100644 index 00000000000..2f28bf87fb9 --- /dev/null +++ b/src/HotChocolate/Utilities/src/Utilities.Buffers/FixedSizeArrayPoolKinds.cs @@ -0,0 +1,6 @@ +namespace HotChocolate.Buffers; + +internal static class FixedSizeArrayPoolKinds +{ + public const int JsonMemory = 1; +} diff --git a/src/HotChocolate/Utilities/src/Utilities.Buffers/HotChocolate.Utilities.Buffers.csproj b/src/HotChocolate/Utilities/src/Utilities.Buffers/HotChocolate.Utilities.Buffers.csproj index dfbdc8312bc..8bde094eee5 100644 --- a/src/HotChocolate/Utilities/src/Utilities.Buffers/HotChocolate.Utilities.Buffers.csproj +++ b/src/HotChocolate/Utilities/src/Utilities.Buffers/HotChocolate.Utilities.Buffers.csproj @@ -15,6 +15,11 @@ true + + + + + diff --git a/src/HotChocolate/Utilities/src/Utilities.Buffers/JsonMemory.cs b/src/HotChocolate/Utilities/src/Utilities.Buffers/JsonMemory.cs new file mode 100644 index 00000000000..9b21a6fd4d5 --- /dev/null +++ b/src/HotChocolate/Utilities/src/Utilities.Buffers/JsonMemory.cs @@ -0,0 +1,102 @@ +using System.Buffers; +#if NET8_0_OR_GREATER +using System.Runtime.InteropServices; +#endif +using static HotChocolate.Buffers.JsonMemoryEventSource; + +namespace HotChocolate.Buffers; + +/// +/// Manages the memory for storing JSON data. +/// +internal static class JsonMemory +{ + public const int BufferSize = 1 << 17; + + private static FixedSizeArrayPool s_pool = new(FixedSizeArrayPoolKinds.JsonMemory, BufferSize, 128); + private static readonly ArrayPool s_chunkPool = ArrayPool.Shared; + + public static void Reconfigure(Func factory) + { +#if NET8_0_OR_GREATER + ArgumentNullException.ThrowIfNull(factory); +#else + if(factory is null) + { + throw new ArgumentNullException(nameof(factory)); + } +#endif + + s_pool = factory() ?? throw new InvalidOperationException("The factory must create a valid pool."); + Log.ReconfiguredPool(); + } + + public static byte[] Rent(JsonMemoryKind kind) + { + var buffer = s_pool.Rent(); + Log.BufferRented(kind, bufferCount: 1); + return buffer; + } + + public static byte[][] RentRange(JsonMemoryKind kind, int requiredChunks) + { + var chunks = s_chunkPool.Rent(requiredChunks); + + for (var i = 0; i < requiredChunks; i++) + { + chunks[i] = s_pool.Rent(); + } + + Log.BufferReturned(kind, requiredChunks); + return chunks; + } + + public static void Return(JsonMemoryKind kind, byte[] chunk) + { + s_pool.Return(chunk); + Log.BufferReturned(kind, 1); + } + + public static void Return(JsonMemoryKind kind, byte[][] chunks, int usedChunks) + { +#if NET8_0_OR_GREATER + ArgumentNullException.ThrowIfNull(chunks); +#else + if (chunks is null) + { + throw new ArgumentNullException(nameof(chunks)); + } +#endif + + foreach (var chunk in chunks.AsSpan(0, usedChunks)) + { + s_pool.Return(chunk); + } + + Log.BufferReturned(kind, usedChunks); + } + + public static void Return(JsonMemoryKind kind, List chunks) + { +#if NET8_0_OR_GREATER + ArgumentNullException.ThrowIfNull(chunks); + + foreach (var chunk in CollectionsMarshal.AsSpan(chunks)) + { + s_pool.Return(chunk); + } +#else + if (chunks is null) + { + throw new ArgumentNullException(nameof(chunks)); + } + + foreach (var chunk in chunks) + { + s_pool.Return(chunk); + } +#endif + + Log.BufferReturned(kind, chunks.Count); + } +} diff --git a/src/HotChocolate/Utilities/src/Utilities.Buffers/JsonMemoryEventSource.cs b/src/HotChocolate/Utilities/src/Utilities.Buffers/JsonMemoryEventSource.cs new file mode 100644 index 00000000000..58799402b72 --- /dev/null +++ b/src/HotChocolate/Utilities/src/Utilities.Buffers/JsonMemoryEventSource.cs @@ -0,0 +1,47 @@ +using System.Diagnostics.Tracing; + +namespace HotChocolate.Buffers; + +[EventSource(Name = "HotChocolate-Buffers-JsonMemory")] +internal sealed class JsonMemoryEventSource : EventSource +{ + public static readonly JsonMemoryEventSource Log = new(); + + private JsonMemoryEventSource() { } + + [Event( + eventId: 1, + Level = EventLevel.Informational, + Message = "Pool reconfigured.")] + public void ReconfiguredPool() + { + if (IsEnabled(EventLevel.Verbose, EventKeywords.None)) + { + WriteEvent(1); + } + } + + [Event( + eventId: 2, + Level = EventLevel.Verbose, + Message = "Buffer rented (Kind={0}, BufferCount={1})")] + public void BufferRented(JsonMemoryKind kind, int bufferCount) + { + if (IsEnabled(EventLevel.Verbose, EventKeywords.None)) + { + WriteEvent(2, kind, bufferCount); + } + } + + [Event( + eventId: 3, + Level = EventLevel.Verbose, + Message = "Buffer returned (Kind={0}, BufferCount={1})")] + public void BufferReturned(JsonMemoryKind kind, int bufferCount) + { + if (IsEnabled(EventLevel.Verbose, EventKeywords.None)) + { + WriteEvent(3, kind, bufferCount); + } + } +} diff --git a/src/HotChocolate/Utilities/src/Utilities.Buffers/JsonMemoryKind.cs b/src/HotChocolate/Utilities/src/Utilities.Buffers/JsonMemoryKind.cs new file mode 100644 index 00000000000..d4016d2f620 --- /dev/null +++ b/src/HotChocolate/Utilities/src/Utilities.Buffers/JsonMemoryKind.cs @@ -0,0 +1,7 @@ +namespace HotChocolate.Buffers; + +internal enum JsonMemoryKind +{ + Metadata = 1, + Json = 2 +} diff --git a/src/HotChocolate/Utilities/src/Utilities.Buffers/Properties/BuffersResources.Designer.cs b/src/HotChocolate/Utilities/src/Utilities.Buffers/Properties/BuffersResources.Designer.cs index 6388029e0e7..a02fc93a0b6 100644 --- a/src/HotChocolate/Utilities/src/Utilities.Buffers/Properties/BuffersResources.Designer.cs +++ b/src/HotChocolate/Utilities/src/Utilities.Buffers/Properties/BuffersResources.Designer.cs @@ -50,5 +50,11 @@ internal static string ArrayWriter_Advance_BufferOverflow { return ResourceManager.GetString("ArrayWriter_Advance_BufferOverflow", resourceCulture); } } + + internal static string FixedSizeArrayPool_Return_InvalidArraySize { + get { + return ResourceManager.GetString("FixedSizeArrayPool_Return_InvalidArraySize", resourceCulture); + } + } } } diff --git a/src/HotChocolate/Utilities/src/Utilities.Buffers/Properties/BuffersResources.resx b/src/HotChocolate/Utilities/src/Utilities.Buffers/Properties/BuffersResources.resx index 5449972ccdc..05fdeb25e4c 100644 --- a/src/HotChocolate/Utilities/src/Utilities.Buffers/Properties/BuffersResources.resx +++ b/src/HotChocolate/Utilities/src/Utilities.Buffers/Properties/BuffersResources.resx @@ -21,4 +21,7 @@ Cannot advance past the end of the buffer. + + Buffer size {0} does not match expected chunk size {1} + diff --git a/src/HotChocolate/Utilities/src/Utilities.Introspection/HotChocolate.Utilities.Introspection.csproj b/src/HotChocolate/Utilities/src/Utilities.Introspection/HotChocolate.Utilities.Introspection.csproj index a014d417a25..bef5c3c11aa 100644 --- a/src/HotChocolate/Utilities/src/Utilities.Introspection/HotChocolate.Utilities.Introspection.csproj +++ b/src/HotChocolate/Utilities/src/Utilities.Introspection/HotChocolate.Utilities.Introspection.csproj @@ -24,8 +24,8 @@ - + diff --git a/src/HotChocolate/Utilities/src/Utilities.Introspection/IntrospectionClient.cs b/src/HotChocolate/Utilities/src/Utilities.Introspection/IntrospectionClient.cs index afe9cdbf646..b223c7cf5be 100644 --- a/src/HotChocolate/Utilities/src/Utilities.Introspection/IntrospectionClient.cs +++ b/src/HotChocolate/Utilities/src/Utilities.Introspection/IntrospectionClient.cs @@ -2,6 +2,7 @@ using System.Text.Json; using HotChocolate.Language; using HotChocolate.Transport.Http; +using HotChocolate.Types; using static HotChocolate.Utilities.Introspection.CapabilityInspector; using static HotChocolate.Utilities.Introspection.IntrospectionQueryHelper; diff --git a/src/HotChocolate/Utilities/src/Utilities.Introspection/IntrospectionFormatter.cs b/src/HotChocolate/Utilities/src/Utilities.Introspection/IntrospectionFormatter.cs index 16443d7a052..0748992eabd 100644 --- a/src/HotChocolate/Utilities/src/Utilities.Introspection/IntrospectionFormatter.cs +++ b/src/HotChocolate/Utilities/src/Utilities.Introspection/IntrospectionFormatter.cs @@ -1,5 +1,7 @@ using HotChocolate.Language; +using HotChocolate.Types; using HotChocolate.Utilities.Introspection.Properties; +using DirectiveLocation = HotChocolate.Language.DirectiveLocation; namespace HotChocolate.Utilities.Introspection; @@ -333,10 +335,10 @@ private static IReadOnlyList CreateDeprecatedDirective( { new DirectiveNode ( - WellKnownDirectives.Deprecated, + DirectiveNames.Deprecated.Name, new ArgumentNode ( - WellKnownDirectives.DeprecationReasonArgument, + DirectiveNames.Deprecated.Arguments.Reason, new StringValueNode(deprecationReason) ) ) diff --git a/website/src/docs/hotchocolate/v16/migrating/migrate-from-15-to-16.md b/website/src/docs/hotchocolate/v16/migrating/migrate-from-15-to-16.md index 68d903f035e..ef350da0e9d 100644 --- a/website/src/docs/hotchocolate/v16/migrating/migrate-from-15-to-16.md +++ b/website/src/docs/hotchocolate/v16/migrating/migrate-from-15-to-16.md @@ -278,6 +278,12 @@ If you need the old behavior, use can still use the non-generic `ID`-attribute a Previously the `TryConfigure` or `OnConfigure` methods carried a non-nullable parameter of the member the descriptor attribute was annotated to. With the new source generator we moved away from pure reflection based APIs. This means that when you use the source generator +## Merged Assemblies HotChocolate.Types, HotChocolate.Execution, HotChocolate.Fetching + +With Hot Chocolate 16 we introduced a lot more abstractions, meaning we pulled out abstractions of the type system or the execution into separate libraries. But at the same time we simplified the implementation of the type system and the execution by moving the implementations of HotChocolate.Execution and HotChocolate.Fetching into HotChocolate.Types. This allowed us to simplify the implementation and make it more efficient. + +So, if you were referencing HotChocolate.Execution or HotChocolate.Fetching directly make sure to remove references to these libraries and replace them with HotChocolate.Types. + # Deprecations Things that will continue to function this release, but we encourage you to move away from.