| File: System\Text\Json\Writer\Utf8JsonWriter.WriteValues.Helpers.cs | Web Access |
| Project: src\runtime\src\libraries\System.Text.Json\src\System.Text.Json.csproj (System.Text.Json) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers; using System.Buffers.Text; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace System.Text.Json { public sealed partial class Utf8JsonWriter { /// <summary> /// Returns whether a JSON value can be written at the current position based on the current <see cref="_enclosingContainer"/>: /// <list type="bullet"> /// <item> /// <see cref="EnclosingContainerType.Array"/>: Writing a value is always allowed. /// </item> /// <item> /// <see cref="EnclosingContainerType.Object"/>: Writing a value is allowed only if <see cref="_tokenType"/> is a property name. /// Because we designed <see cref="EnclosingContainerType.Object"/> == <see cref="JsonTokenType.PropertyName"/>, we can just check for equality. /// </item> /// <item> /// <see cref="EnclosingContainerType.None"/>: Writing a value is allowed only if <see cref="_tokenType"/> is None (only one value may be written at the root). /// This case is identical to the previous case. /// </item> /// <item> /// <see cref="EnclosingContainerType.Utf8StringSequence"/>, <see cref="EnclosingContainerType.Utf16StringSequence"/>, <see cref="EnclosingContainerType.Base64StringSequence"/>: /// Writing a value is never valid and <see cref="_enclosingContainer"/> does not equal any <see cref="JsonTokenType"/> by construction. /// </item> /// </list> /// This method performs better without short circuiting (this often gets inlined so using simple branch free code seems to have some benefits). /// </summary> private bool CanWriteValue => _enclosingContainer == EnclosingContainerType.Array | (byte)_enclosingContainer == (byte)_tokenType; private bool HasPartialStringData => _partialStringDataLength != 0; private void ClearPartialStringData() => _partialStringDataLength = 0; private void ValidateWritingValue() { if (!CanWriteValue) { OnValidateWritingValueFailed(); } } [DoesNotReturn] [MethodImpl(MethodImplOptions.NoInlining)] private void OnValidateWritingValueFailed() { Debug.Assert(!_options.SkipValidation); if (IsWritingPartialString) { ThrowInvalidOperationException(ExceptionResource.CannotWriteWithinString); } Debug.Assert(!HasPartialStringData); if (_enclosingContainer == EnclosingContainerType.Object) { Debug.Assert(_tokenType != JsonTokenType.PropertyName); Debug.Assert(_tokenType != JsonTokenType.None && _tokenType != JsonTokenType.StartArray); ThrowInvalidOperationException(ExceptionResource.CannotWriteValueWithinObject); } else { Debug.Assert(_tokenType != JsonTokenType.PropertyName); Debug.Assert(CurrentDepth == 0 && _tokenType != JsonTokenType.None); ThrowInvalidOperationException(ExceptionResource.CannotWriteValueAfterPrimitiveOrClose); } } private void ValidateWritingSegment(EnclosingContainerType currentSegmentEncoding) { Debug.Assert(currentSegmentEncoding is EnclosingContainerType.Utf8StringSequence or EnclosingContainerType.Utf16StringSequence or EnclosingContainerType.Base64StringSequence); // A string segment can be written if either: // 1) The writer is currently in a partial string of the same type. In this case the new segment // will continue the partial string. // - or - // 2) The writer can write a value at the current position, in which case a new string can be started. if (_enclosingContainer != currentSegmentEncoding && !CanWriteValue) { OnValidateWritingSegmentFailed(currentSegmentEncoding); } } [DoesNotReturn] [MethodImpl(MethodImplOptions.NoInlining)] private void OnValidateWritingSegmentFailed(EnclosingContainerType currentSegmentEncoding) { if (IsWritingPartialString) { ThrowHelper.ThrowInvalidOperationException_CannotMixEncodings(_enclosingContainer, currentSegmentEncoding); } Debug.Assert(!HasPartialStringData); if (_enclosingContainer == EnclosingContainerType.Object) { Debug.Assert(_tokenType != JsonTokenType.PropertyName); Debug.Assert(_tokenType != JsonTokenType.None && _tokenType != JsonTokenType.StartArray); ThrowInvalidOperationException(ExceptionResource.CannotWriteValueWithinObject); } else { Debug.Assert(_tokenType != JsonTokenType.PropertyName); Debug.Assert(CurrentDepth == 0 && _tokenType != JsonTokenType.None); ThrowInvalidOperationException(ExceptionResource.CannotWriteValueAfterPrimitiveOrClose); } } private void ValidateNotWithinUnfinalizedString() { if (IsWritingPartialString) { ThrowInvalidOperationException(ExceptionResource.CannotWriteWithinString); } Debug.Assert(!HasPartialStringData); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Base64EncodeAndWrite(ReadOnlySpan<byte> bytes, Span<byte> output) { Span<byte> destination = output.Slice(BytesPending); OperationStatus status = Base64.EncodeToUtf8(bytes, destination, out int consumed, out int written); Debug.Assert(status == OperationStatus.Done); Debug.Assert(consumed == bytes.Length); BytesPending += written; } } }