public static IEnumerable <ObjectProperty> GetObjectProperties(ReadOnlyMemory <byte> buffer)
            {
                byte typeMarker = buffer.Span[0];

                if (!JsonBinaryEncoding.TypeMarker.IsObject(typeMarker))
                {
                    throw new JsonInvalidTokenException();
                }

                int firstValueOffset = JsonBinaryEncoding.GetFirstValueOffset(typeMarker);

                buffer = buffer.Slice(firstValueOffset);
                while (buffer.Length != 0)
                {
                    int nameNodeLength = JsonBinaryEncoding.GetValueLength(buffer.Span);
                    if (nameNodeLength > buffer.Length)
                    {
                        throw new JsonInvalidTokenException();
                    }
                    ReadOnlyMemory <byte> name = buffer.Slice(0, nameNodeLength);
                    buffer = buffer.Slice(nameNodeLength);

                    int valueNodeLength = JsonBinaryEncoding.GetValueLength(buffer.Span);
                    if (valueNodeLength > buffer.Length)
                    {
                        throw new JsonInvalidTokenException();
                    }
                    ReadOnlyMemory <byte> value = buffer.Slice(0, valueNodeLength);
                    buffer = buffer.Slice(valueNodeLength);

                    yield return(new ObjectProperty(name, value));
                }
            }
            /// <inheritdoc />
            public override IEnumerable <ObjectProperty> GetObjectProperties(IJsonNavigatorNode objectNode)
            {
                ReadOnlyMemory <byte> buffer = JsonBinaryNavigator.GetNodeOfType(
                    JsonNodeType.Object,
                    objectNode);

                byte typeMarker       = buffer.Span[0];
                int  firstValueOffset = JsonBinaryEncoding.GetFirstValueOffset(typeMarker);

                buffer = buffer.Slice(firstValueOffset);
                while (buffer.Length != 0)
                {
                    int nameNodeLength = JsonBinaryEncoding.GetValueLength(buffer.Span);
                    if (nameNodeLength > buffer.Length)
                    {
                        throw new JsonInvalidTokenException();
                    }
                    ReadOnlyMemory <byte> nameNode = buffer.Slice(0, nameNodeLength);
                    buffer = buffer.Slice(nameNodeLength);

                    int valueNodeLength = JsonBinaryEncoding.GetValueLength(buffer.Span);
                    if (valueNodeLength > buffer.Length)
                    {
                        throw new JsonInvalidTokenException();
                    }
                    ReadOnlyMemory <byte> valueNode = buffer.Slice(0, valueNodeLength);
                    buffer = buffer.Slice(valueNodeLength);

                    yield return(new ObjectProperty(
                                     new BinaryNavigatorNode(nameNode, JsonNodeType.FieldName),
                                     new BinaryNavigatorNode(valueNode, NodeTypes.GetNodeType(valueNode.Span[0]))));
                }
            }
            /// <summary>
            /// Gets an IEnumerable of <see cref="IJsonNavigatorNode"/> properties from an object node.
            /// </summary>
            /// <param name="objectNavigatorNode">The <see cref="IJsonNavigatorNode"/> of object node to get the properties from.</param>
            /// <returns>The IEnumerable of <see cref="IJsonNavigatorNode"/> properties from an object node.</returns>
            public override IEnumerable <ObjectProperty> GetObjectProperties(IJsonNavigatorNode objectNavigatorNode)
            {
                if (objectNavigatorNode == null)
                {
                    throw new ArgumentNullException(nameof(objectNavigatorNode));
                }

                if (!(((objectNavigatorNode as BinaryNode)?.JsonNodeType ?? JsonNodeType.Unknown) == JsonNodeType.Object))
                {
                    throw new ArgumentException($"{nameof(objectNavigatorNode)} must actually be a binary object node.");
                }

                int  offset           = ((BinaryNode)objectNavigatorNode).Offset;
                byte typeMarker       = this.buffer[offset];
                long objectLength     = JsonBinaryEncoding.GetValueLength(this.buffer, offset);
                long firstValueOffset = offset + JsonBinaryEncoding.GetFirstValueOffset(typeMarker);
                long bytesToProcess   = (offset + objectLength) - firstValueOffset;

                for (int bytesProcessed = 0; bytesProcessed < bytesToProcess;)
                {
                    BinaryNode nameNode = new BinaryNode((int)(firstValueOffset + bytesProcessed), JsonNodeType.FieldName);
                    bytesProcessed += (int)JsonBinaryEncoding.GetValueLength(this.buffer, firstValueOffset + bytesProcessed);
                    BinaryNode valueNode = this.CreateBinaryNode((int)(firstValueOffset + bytesProcessed));
                    bytesProcessed += (int)JsonBinaryEncoding.GetValueLength(this.buffer, firstValueOffset + bytesProcessed);
                    yield return(new ObjectProperty(nameNode, valueNode));
                }
            }
            public static IEnumerable <ReadOnlyMemory <byte> > GetArrayItems(ReadOnlyMemory <byte> buffer)
            {
                byte typeMarker = buffer.Span[0];

                if (!JsonBinaryEncoding.TypeMarker.IsArray(typeMarker))
                {
                    throw new JsonInvalidTokenException();
                }

                int firstArrayItemOffset = JsonBinaryEncoding.GetFirstValueOffset(typeMarker);
                int arrayLength          = JsonBinaryEncoding.GetValueLength(buffer.Span);

                // Scope to just the array
                buffer = buffer.Slice(0, arrayLength);

                // Seek to the first array item
                buffer = buffer.Slice(firstArrayItemOffset);

                while (buffer.Length != 0)
                {
                    int arrayItemLength = JsonBinaryEncoding.GetValueLength(buffer.Span);
                    if (arrayItemLength > buffer.Length)
                    {
                        // Array Item got cut off.
                        throw new JsonInvalidTokenException();
                    }

                    // Create a buffer for that array item
                    ReadOnlyMemory <byte> arrayItem = buffer.Slice(0, arrayItemLength);
                    yield return(arrayItem);

                    // Slice off the array item
                    buffer = buffer.Slice(arrayItemLength);
                }
            }
            /// <summary>
            /// Gets the node at a particular index of an array node
            /// </summary>
            /// <param name="arrayNavigatorNode">The <see cref="IJsonNavigatorNode"/> of the (array) node to index from.</param>
            /// <param name="index">The offset into the array</param>
            /// <returns>The <see cref="IJsonNavigatorNode"/> of the node at a particular index of an array node</returns>
            public override IJsonNavigatorNode GetArrayItemAt(IJsonNavigatorNode arrayNavigatorNode, int index)
            {
                if (arrayNavigatorNode == null)
                {
                    throw new ArgumentNullException(nameof(arrayNavigatorNode));
                }

                if (!(((arrayNavigatorNode as BinaryNode)?.JsonNodeType ?? JsonNodeType.Unknown) == JsonNodeType.Array))
                {
                    throw new ArgumentException($"{nameof(arrayNavigatorNode)} must be a binary array node.");
                }

                // TODO (brchon): We can optimize for the case where the count is serialized so we can avoid using the linear time call to TryGetValueAt().

                int  arrayOffset = ((BinaryNode)arrayNavigatorNode).Offset;
                byte typeMarker  = this.buffer[arrayOffset];

                BinaryNode node;
                long       firstArrayItemOffset = arrayOffset + JsonBinaryEncoding.GetFirstValueOffset(typeMarker);
                long       arrayLength          = JsonBinaryEncoding.GetValueLength(this.buffer, arrayOffset);
                long       arrayItemsLength     = arrayLength - (firstArrayItemOffset - arrayOffset);

                if (!this.TryGetValueAt(firstArrayItemOffset, arrayItemsLength, index, out node))
                {
                    throw new IndexOutOfRangeException($"Tried to access index:{index} in an array.");
                }

                return(node);
            }
Пример #6
0
            private IEnumerable <BinaryNavigatorNode> GetArrayItemsInternal(ReadOnlyMemory <byte> buffer)
            {
                byte typeMarker = buffer.Span[0];

                int firstArrayItemOffset = JsonBinaryEncoding.GetFirstValueOffset(typeMarker);
                int arrayLength          = JsonBinaryEncoding.GetValueLength(buffer.Span);

                // Scope to just the array
                buffer = buffer.Slice(0, (int)arrayLength);

                // Seek to the first array item
                buffer = buffer.Slice(firstArrayItemOffset);

                while (buffer.Length != 0)
                {
                    int arrayItemLength = JsonBinaryEncoding.GetValueLength(buffer.Span);
                    if (arrayItemLength > buffer.Length)
                    {
                        // Array Item got cut off.
                        throw new JsonInvalidTokenException();
                    }

                    // Create a buffer for that array item
                    BinaryNavigatorNode arrayItem = new BinaryNavigatorNode(
                        buffer.Slice(0, arrayItemLength),
                        NodeTypes.GetNodeType(buffer.Span[0]));
                    yield return(arrayItem);

                    // Slice off the array item
                    buffer = buffer.Slice(arrayItemLength);
                }
            }
            /// <inheritdoc />
            public override int GetObjectPropertyCount(IJsonNavigatorNode objectNode)
            {
                ReadOnlyMemory <byte> buffer = JsonBinaryNavigator.GetNodeOfType(
                    JsonNodeType.Object,
                    objectNode);

                byte typeMarker = buffer.Span[0];
                int  firstObjectPropertyOffset = JsonBinaryEncoding.GetFirstValueOffset(typeMarker);
                long count;

                switch (typeMarker)
                {
                // Empty and Single Object
                case JsonBinaryEncoding.TypeMarker.EmptyObject:
                    count = 0;
                    break;

                case JsonBinaryEncoding.TypeMarker.SinglePropertyObject:
                    // This number gets divided by 2 later.
                    count = 2;
                    break;

                // Object with length and count prefix
                case JsonBinaryEncoding.TypeMarker.Object1ByteLengthAndCount:
                    count = MemoryMarshal.Read <byte>(buffer
                                                      .Slice(JsonBinaryEncoding.TypeMarkerLength + JsonBinaryEncoding.OneByteLength).Span);
                    break;

                case JsonBinaryEncoding.TypeMarker.Object2ByteLengthAndCount:
                    count = MemoryMarshal.Read <ushort>(buffer
                                                        .Slice(JsonBinaryEncoding.TypeMarkerLength + JsonBinaryEncoding.TwoByteLength).Span);
                    break;

                case JsonBinaryEncoding.TypeMarker.Object4ByteLengthAndCount:
                    count = MemoryMarshal.Read <uint>(buffer
                                                      .Slice(JsonBinaryEncoding.TypeMarkerLength + JsonBinaryEncoding.FourByteLength).Span);
                    break;

                // Object with length prefix
                case JsonBinaryEncoding.TypeMarker.Object1ByteLength:
                case JsonBinaryEncoding.TypeMarker.Object2ByteLength:
                case JsonBinaryEncoding.TypeMarker.Object4ByteLength:
                    count = JsonBinaryNavigator.GetValueCount(buffer.Slice(firstObjectPropertyOffset).Span);
                    break;

                default:
                    throw new InvalidOperationException($"Unexpected object type marker: {typeMarker}");
                }

                // Divide by 2 since the count includes fieldname and value as seperate entities
                count = count / 2;
                if (count > int.MaxValue)
                {
                    throw new InvalidOperationException("count can not be more than int.MaxValue");
                }

                return((int)count);
            }
            /// <inheritdoc />
            public override int GetArrayItemCount(IJsonNavigatorNode arrayNode)
            {
                ReadOnlyMemory <byte> buffer = JsonBinaryNavigator.GetNodeOfType(
                    JsonNodeType.Array,
                    arrayNode);
                byte typeMarker       = buffer.Span[0];
                int  firstValueOffset = JsonBinaryEncoding.GetFirstValueOffset(typeMarker);
                long count;

                switch (typeMarker)
                {
                // Empty and Single Array
                case JsonBinaryEncoding.TypeMarker.EmptyArray:
                    count = 0;
                    break;

                case JsonBinaryEncoding.TypeMarker.SingleItemArray:
                    count = 1;
                    break;

                // Arrays with length and count prefix
                case JsonBinaryEncoding.TypeMarker.Array1ByteLengthAndCount:
                    count = MemoryMarshal.Read <byte>(buffer
                                                      .Slice(JsonBinaryEncoding.TypeMarkerLength + JsonBinaryEncoding.OneByteLength).Span);
                    break;

                case JsonBinaryEncoding.TypeMarker.Array2ByteLengthAndCount:
                    count = MemoryMarshal.Read <ushort>(buffer
                                                        .Slice(JsonBinaryEncoding.TypeMarkerLength + JsonBinaryEncoding.TwoByteLength).Span);
                    break;

                case JsonBinaryEncoding.TypeMarker.Array4ByteLengthAndCount:
                    count = MemoryMarshal.Read <uint>(buffer
                                                      .Slice(JsonBinaryEncoding.TypeMarkerLength + JsonBinaryEncoding.FourByteLength).Span);
                    break;

                // Arrays with length prefix
                case JsonBinaryEncoding.TypeMarker.Array1ByteLength:
                case JsonBinaryEncoding.TypeMarker.Array2ByteLength:
                case JsonBinaryEncoding.TypeMarker.Array4ByteLength:
                    count = JsonBinaryNavigator.GetValueCount(buffer.Slice(firstValueOffset).Span);
                    break;

                default:
                    throw new InvalidOperationException($"Unexpected array type marker: {typeMarker}");
                }

                if (count > int.MaxValue)
                {
                    throw new InvalidOperationException("count can not be more than int.MaxValue");
                }

                return((int)count);
            }
            private static bool TryGetValueAt(
                ReadOnlyMemory <byte> arrayNode,
                long index,
                out ReadOnlyMemory <byte> arrayItem)
            {
                ReadOnlyMemory <byte> buffer = arrayNode;
                byte typeMarker = buffer.Span[0];

                int firstArrayItemOffset = JsonBinaryEncoding.GetFirstValueOffset(typeMarker);
                int arrayLength          = JsonBinaryEncoding.GetValueLength(buffer.Span);

                // Scope to just the array
                buffer = buffer.Slice(0, (int)arrayLength);

                // Seek to the first array item
                buffer = buffer.Slice(firstArrayItemOffset);

                for (long count = 0; count < index; count++)
                {
                    // Skip over the array item.
                    int arrayItemLength = JsonBinaryEncoding.GetValueLength(buffer.Span);
                    if (arrayItemLength >= buffer.Length)
                    {
                        arrayItem = default;
                        return(false);
                    }

                    buffer = buffer.Slice(arrayItemLength);
                }

                // Scope to just that array item
                int itemLength = JsonBinaryEncoding.GetValueLength(buffer.Span);

                buffer = buffer.Slice(0, itemLength);

                arrayItem = buffer;
                return(true);
            }
            /// <summary>
            /// Gets an IEnumerable of <see cref="IJsonNavigatorNode"/>s for an arrayNode.
            /// </summary>
            /// <param name="arrayNavigatorNode">The <see cref="IJsonNavigatorNode"/> of the array to get the items from</param>
            /// <returns>The IEnumerable of <see cref="IJsonNavigatorNode"/>s for an arrayNode.</returns>
            public override IEnumerable <IJsonNavigatorNode> GetArrayItems(IJsonNavigatorNode arrayNavigatorNode)
            {
                if (arrayNavigatorNode == null)
                {
                    throw new ArgumentNullException(nameof(arrayNavigatorNode));
                }

                if (!(((arrayNavigatorNode as BinaryNode)?.JsonNodeType ?? JsonNodeType.Unknown) == JsonNodeType.Array))
                {
                    throw new ArgumentException($"{nameof(arrayNavigatorNode)} must be a binary array node.");
                }

                int  arrayOffset = ((BinaryNode)arrayNavigatorNode).Offset;
                byte typeMarker  = this.buffer[arrayOffset];

                long arrayLength      = JsonBinaryEncoding.GetValueLength(this.buffer, arrayOffset);
                long firstValueOffset = arrayOffset + JsonBinaryEncoding.GetFirstValueOffset(typeMarker);
                long bytesToProcess   = (arrayOffset + arrayLength) - firstValueOffset;

                for (int bytesProcessed = 0; bytesProcessed < bytesToProcess; bytesProcessed += (int)JsonBinaryEncoding.GetValueLength(this.buffer, firstValueOffset + bytesProcessed))
                {
                    yield return(this.CreateBinaryNode((int)(firstValueOffset + bytesProcessed)));
                }
            }
            /// <summary>
            /// Gets the number of properties in an object node.
            /// </summary>
            /// <param name="objectNavigatorNode">The <see cref="IJsonNavigatorNode"/> of node to get the property count from.</param>
            /// <returns>The number of properties in an object node.</returns>
            public override int GetObjectPropertyCount(IJsonNavigatorNode objectNavigatorNode)
            {
                if (objectNavigatorNode == null)
                {
                    throw new ArgumentNullException(nameof(objectNavigatorNode));
                }

                if (!(((objectNavigatorNode as BinaryNode)?.JsonNodeType ?? JsonNodeType.Unknown) == JsonNodeType.Object))
                {
                    throw new ArgumentException($"{nameof(objectNavigatorNode)} must be a binary object node.");
                }

                int  objectOffset = ((BinaryNode)objectNavigatorNode).Offset;
                byte typeMarker   = this.buffer[objectOffset];
                int  firstObjectPropertyOffset = objectOffset + JsonBinaryEncoding.GetFirstValueOffset(typeMarker);
                long count;

                switch (typeMarker)
                {
                // Empty and Single Object
                case JsonBinaryEncoding.TypeMarker.EmptyObject:
                    count = 0;
                    break;

                case JsonBinaryEncoding.TypeMarker.SinglePropertyObject:
                    // This number gets divided by 2 later.
                    count = 2;
                    break;

                // Object with length and count prefix
                case JsonBinaryEncoding.TypeMarker.Object1ByteLengthAndCount:
                    count = this.buffer[objectOffset + JsonBinaryEncoding.TypeMarkerLength + JsonBinaryEncoding.OneByteLength];
                    break;

                case JsonBinaryEncoding.TypeMarker.Object2ByteLengthAndCount:
                    count = BitConverter.ToUInt16(this.buffer, objectOffset + JsonBinaryEncoding.TypeMarkerLength + JsonBinaryEncoding.TwoByteLength);
                    break;

                case JsonBinaryEncoding.TypeMarker.Object4ByteLengthAndCount:
                    count = BitConverter.ToUInt32(this.buffer, objectOffset + JsonBinaryEncoding.TypeMarkerLength + JsonBinaryEncoding.FourByteLength);
                    break;

                // Object with length prefix
                case JsonBinaryEncoding.TypeMarker.Object1ByteLength:
                    count = this.GetValueCount(firstObjectPropertyOffset, this.buffer[objectOffset + JsonBinaryEncoding.TypeMarkerLength]);
                    break;

                case JsonBinaryEncoding.TypeMarker.Object2ByteLength:
                    count = this.GetValueCount(firstObjectPropertyOffset, BitConverter.ToUInt16(this.buffer, objectOffset + JsonBinaryEncoding.TypeMarkerLength));
                    break;

                case JsonBinaryEncoding.TypeMarker.Object4ByteLength:
                    count = this.GetValueCount(firstObjectPropertyOffset, BitConverter.ToUInt32(this.buffer, objectOffset + JsonBinaryEncoding.TypeMarkerLength));
                    break;

                default:
                    throw new InvalidOperationException($"Unexpected object type marker: {typeMarker}");
                }

                // Divide by 2 since the count includes fieldname and value as seperate entities
                count = count / 2;
                if (count > int.MaxValue)
                {
                    throw new InvalidOperationException("count can not be more than int.MaxValue");
                }

                return((int)count);
            }
            /// <summary>
            /// Gets the number of elements in an array node.
            /// </summary>
            /// <param name="arrayNavigatorNode">The <see cref="IJsonNavigatorNode"/> of the (array) node to get the count of.</param>
            /// <returns>The number of elements in the array node.</returns>
            public override int GetArrayItemCount(IJsonNavigatorNode arrayNavigatorNode)
            {
                if (arrayNavigatorNode == null)
                {
                    throw new ArgumentNullException(nameof(arrayNavigatorNode));
                }

                if (!(((arrayNavigatorNode as BinaryNode)?.JsonNodeType ?? JsonNodeType.Unknown) == JsonNodeType.Array))
                {
                    throw new ArgumentException($"{nameof(arrayNavigatorNode)} must be a binary array node.");
                }

                int  offset           = ((BinaryNode)arrayNavigatorNode).Offset;
                byte typeMarker       = this.buffer[offset];
                int  firstValueOffset = offset + JsonBinaryEncoding.GetFirstValueOffset(typeMarker);
                long count;

                switch (typeMarker)
                {
                // Empty and Single Array
                case JsonBinaryEncoding.TypeMarker.EmptyArray:
                    count = 0;
                    break;

                case JsonBinaryEncoding.TypeMarker.SingleItemArray:
                    count = 1;
                    break;

                // Arrays with length and count prefix
                case JsonBinaryEncoding.TypeMarker.Array1ByteLengthAndCount:
                    count = this.buffer[offset + JsonBinaryEncoding.TypeMarkerLength + JsonBinaryEncoding.OneByteLength];
                    break;

                case JsonBinaryEncoding.TypeMarker.Array2ByteLengthAndCount:
                    count = BitConverter.ToUInt16(this.buffer, offset + JsonBinaryEncoding.TypeMarkerLength + JsonBinaryEncoding.TwoByteLength);
                    break;

                case JsonBinaryEncoding.TypeMarker.Array4ByteLengthAndCount:
                    count = BitConverter.ToUInt32(this.buffer, offset + JsonBinaryEncoding.TypeMarkerLength + JsonBinaryEncoding.FourByteLength);
                    break;

                // Arrays with length prefix
                case JsonBinaryEncoding.TypeMarker.Array1ByteLength:
                    count = this.GetValueCount(firstValueOffset, this.buffer[offset + JsonBinaryEncoding.TypeMarkerLength]);
                    break;

                case JsonBinaryEncoding.TypeMarker.Array2ByteLength:
                    count = this.GetValueCount(firstValueOffset, BitConverter.ToUInt16(this.buffer, offset + JsonBinaryEncoding.TypeMarkerLength));
                    break;

                case JsonBinaryEncoding.TypeMarker.Array4ByteLength:
                    count = this.GetValueCount(firstValueOffset, BitConverter.ToUInt32(this.buffer, offset + JsonBinaryEncoding.TypeMarkerLength));
                    break;

                default:
                    throw new InvalidOperationException($"Unexpected array type marker: {typeMarker}");
                }

                if (count > int.MaxValue)
                {
                    throw new InvalidOperationException("count can not be more than int.MaxValue");
                }

                return((int)count);
            }