Skip to content

Commit

Permalink
[Hotfix] JSON Binary Encoding: Adds support for encoding uniform arra…
Browse files Browse the repository at this point in the history
…ys (#4889)

# Pull Request Template

## Description

- JSON Binary Encoding: Adds support for encoding uniform arrays.

## Type of change

Please delete options that are not relevant.

- [x] New feature (non-breaking change which adds functionality)

## Closing issues

To automatically close an issue: closes #IssueNumber

---------

Co-authored-by: Samer Boshra <sboshra@microsoft.com>
  • Loading branch information
kundadebdatta and sboshra authored Nov 13, 2024
1 parent d41592f commit d5e954c
Show file tree
Hide file tree
Showing 26 changed files with 12,967 additions and 1,778 deletions.
4 changes: 2 additions & 2 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ClientOfficialVersion>3.45.1</ClientOfficialVersion>
<ClientOfficialVersion>3.45.2</ClientOfficialVersion>
<ClientPreviewVersion>3.46.0</ClientPreviewVersion>
<ClientPreviewSuffixVersion>preview.1</ClientPreviewSuffixVersion>
<ClientPreviewSuffixVersion>preview.2</ClientPreviewSuffixVersion>
<DirectVersion>3.37.1</DirectVersion>
<EncryptionOfficialVersion>2.0.4</EncryptionOfficialVersion>
<EncryptionPreviewVersion>2.1.0</EncryptionPreviewVersion>
Expand Down
1,655 changes: 1,655 additions & 0 deletions Microsoft.Azure.Cosmos/contracts/API_3.45.2.txt

Large diffs are not rendered by default.

1,752 changes: 1,752 additions & 0 deletions Microsoft.Azure.Cosmos/contracts/API_3.46.0-preview.2.txt

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ internal interface IJsonBinaryWriterExtensions : IJsonWriter
{
void WriteRawJsonValue(
ReadOnlyMemory<byte> rootBuffer,
ReadOnlyMemory<byte> rawJsonValue,
bool isRootNode,
int valueOffset,
JsonBinaryEncoding.UniformArrayInfo externalArrayInfo,
bool isFieldName);
}
}
53 changes: 52 additions & 1 deletion Microsoft.Azure.Cosmos/src/Json/IJsonWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
namespace Microsoft.Azure.Cosmos.Json
{
using System;
using System.Collections.Generic;
using Microsoft.Azure.Cosmos.Core.Utf8;

/// <summary>
/// Interface for all JsonWriters that know how to write jsons of a specific serialization format.
/// Common interface for all JSON writers that can write JSON in a specific serialization format.
/// </summary>
#if INTERNAL
public
Expand Down Expand Up @@ -87,6 +88,54 @@ interface IJsonWriter
/// </summary>
void WriteNullValue();

#region Number Arrays

/// <summary>
/// Writes an array of 8-byte unsigned integer values.
/// </summary>
/// <param name="values">The array of 8-byte unsigned integer values to write.</param>
void WriteNumberArray(IReadOnlyList<byte> values);

/// <summary>
/// Writes an array of 8-byte signed integer values.
/// </summary>
/// <param name="values">The array of 8-byte signed integer values to write.</param>
void WriteNumberArray(IReadOnlyList<sbyte> values);

/// <summary>
/// Writes an array of 16-byte signed integer values.
/// </summary>
/// <param name="values">The array of 16-byte signed integer values to write.</param>
void WriteNumberArray(IReadOnlyList<short> values);

/// <summary>
/// Writes an array of 32-byte signed integer values.
/// </summary>
/// <param name="values">The array of 32-byte signed integer values to write.</param>
void WriteNumberArray(IReadOnlyList<int> values);

/// <summary>
/// Writes an array of 64-byte signed integer values.
/// </summary>
/// <param name="values">The array of 64-byte signed integer values to write.</param>
void WriteNumberArray(IReadOnlyList<long> values);

/// <summary>
/// Writes an array of single-precision floating-point numbers.
/// </summary>
/// <param name="values">The array of single-precision floating-point numbers to write.</param>
void WriteNumberArray(IReadOnlyList<float> values);

/// <summary>
/// Writes an array of double-precision floating-point numbers.
/// </summary>
/// <param name="values">The array of double-precision floating-point numbers to write.</param>
void WriteNumberArray(IReadOnlyList<double> values);

#endregion

#region Extended Types

/// <summary>
/// Writes an single signed byte integer to the internal buffer.
/// </summary>
Expand Down Expand Up @@ -141,6 +190,8 @@ interface IJsonWriter
/// <param name="value">The value of the bytes to write.</param>
void WriteBinaryValue(ReadOnlySpan<byte> value);

#endregion

/// <summary>
/// Gets the result of the JsonWriter.
/// </summary>
Expand Down
136 changes: 66 additions & 70 deletions Microsoft.Azure.Cosmos/src/Json/JsonBinaryEncoding.Enumerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,57 +13,73 @@ internal static partial class JsonBinaryEncoding
{
public static class Enumerator
{
public static IEnumerable<ReadOnlyMemory<byte>> GetArrayItems(ReadOnlyMemory<byte> buffer)
public static IEnumerable<ArrayItem> GetArrayItems(
ReadOnlyMemory<byte> rootBuffer,
int arrayOffset,
UniformArrayInfo externalArrayInfo)
{
ReadOnlyMemory<byte> buffer = rootBuffer.Slice(arrayOffset);
byte typeMarker = buffer.Span[0];
if (!JsonBinaryEncoding.TypeMarker.IsArray(typeMarker))

UniformArrayInfo uniformArrayInfo;
if (externalArrayInfo != null)
{
throw new JsonInvalidTokenException();
uniformArrayInfo = externalArrayInfo.NestedArrayInfo;
}
else
{
uniformArrayInfo = IsUniformArrayTypeMarker(typeMarker) ? GetUniformArrayInfo(buffer.Span) : null;
}

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)
if (uniformArrayInfo != null)
{
int arrayItemLength = JsonBinaryEncoding.GetValueLength(buffer.Span);
if (arrayItemLength > buffer.Length)
int itemStartOffset = arrayOffset + uniformArrayInfo.PrefixSize;
int itemEndOffset = itemStartOffset + (uniformArrayInfo.ItemSize * uniformArrayInfo.ItemCount);
for (int offset = itemStartOffset; offset < itemEndOffset; offset += uniformArrayInfo.ItemSize)
{
yield return new ArrayItem(offset, uniformArrayInfo);
}
}
else
{
if (!TypeMarker.IsArray(typeMarker))
{
// 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;
int firstArrayItemOffset = JsonBinaryEncoding.GetFirstValueOffset(typeMarker);
int arrayLength = JsonBinaryEncoding.GetValueLength(buffer.Span);

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

public static IEnumerable<Memory<byte>> GetMutableArrayItems(Memory<byte> buffer)
{
foreach (ReadOnlyMemory<byte> readOnlyArrayItem in Enumerator.GetArrayItems(buffer))
{
if (!MemoryMarshal.TryGetArray(readOnlyArrayItem, out ArraySegment<byte> segment))
// Seek to the first array item
buffer = buffer.Slice(firstArrayItemOffset);

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

yield return segment;
yield return new ArrayItem(arrayOffset + (arrayLength - buffer.Length), null);

// Slice off the array item
buffer = buffer.Slice(arrayItemLength);
}
}
}

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

if (!JsonBinaryEncoding.TypeMarker.IsObject(typeMarker))
{
throw new JsonInvalidTokenException();
Expand All @@ -73,7 +89,7 @@ public static IEnumerable<ObjectProperty> GetObjectProperties(ReadOnlyMemory<byt
int objectLength = JsonBinaryEncoding.GetValueLength(buffer.Span);

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

// Seek to the first object property
buffer = buffer.Slice(firstValueOffset);
Expand All @@ -85,7 +101,8 @@ public static IEnumerable<ObjectProperty> GetObjectProperties(ReadOnlyMemory<byt
throw new JsonInvalidTokenException();
}

ReadOnlyMemory<byte> name = buffer.Slice(0, nameNodeLength);
int nameOffset = objectOffset + (objectLength - buffer.Length);

buffer = buffer.Slice(nameNodeLength);

int valueNodeLength = JsonBinaryEncoding.GetValueLength(buffer.Span);
Expand All @@ -94,57 +111,36 @@ public static IEnumerable<ObjectProperty> GetObjectProperties(ReadOnlyMemory<byt
throw new JsonInvalidTokenException();
}

ReadOnlyMemory<byte> value = buffer.Slice(0, valueNodeLength);
buffer = buffer.Slice(valueNodeLength);

yield return new ObjectProperty(name, value);
}
}

public static IEnumerable<MutableObjectProperty> GetMutableObjectProperties(Memory<byte> buffer)
{
foreach (ObjectProperty objectProperty in GetObjectProperties(buffer))
{
if (!MemoryMarshal.TryGetArray(objectProperty.Name, out ArraySegment<byte> nameSegment))
{
throw new InvalidOperationException("failed to get array segment.");
}
int valueOffset = objectOffset + (objectLength - buffer.Length);

if (!MemoryMarshal.TryGetArray(objectProperty.Value, out ArraySegment<byte> valueSegment))
{
throw new InvalidOperationException("failed to get array segment.");
}
buffer = buffer.Slice(valueNodeLength);

yield return new MutableObjectProperty(nameSegment, valueSegment);
yield return new ObjectProperty(nameOffset, valueOffset);
}
}

public readonly struct ObjectProperty
public readonly struct ArrayItem
{
public ObjectProperty(
ReadOnlyMemory<byte> name,
ReadOnlyMemory<byte> value)
public ArrayItem(int offset, UniformArrayInfo externalArrayInfo)
{
this.Name = name;
this.Value = value;
this.Offset = offset;
this.ExternalArrayInfo = externalArrayInfo;
}

public ReadOnlyMemory<byte> Name { get; }
public ReadOnlyMemory<byte> Value { get; }
public int Offset { get; }
public UniformArrayInfo ExternalArrayInfo { get; }
}

public readonly struct MutableObjectProperty
public readonly struct ObjectProperty
{
public MutableObjectProperty(
Memory<byte> name,
Memory<byte> value)
public ObjectProperty(int nameOffset, int valueOffset)
{
this.Name = name;
this.Value = value;
this.NameOffset = nameOffset;
this.ValueOffset = valueOffset;
}

public Memory<byte> Name { get; }
public Memory<byte> Value { get; }
public int NameOffset { get; }
public int ValueOffset { get; }
}
}
}
Expand Down
16 changes: 8 additions & 8 deletions Microsoft.Azure.Cosmos/src/Json/JsonBinaryEncoding.NodeTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public static class NodeTypes
String, // StrR2 (Reference string of 2-byte offset)
String, // StrR3 (Reference string of 3-byte offset)
String, // StrR4 (Reference string of 4-byte offset)
Unknown, // <empty> 0xC7
Number, // NumUI64

// Number Values
Number, // NumUI8
Expand All @@ -109,7 +109,7 @@ public static class NodeTypes
Number, // NumDbl,
Float32, // Float32
Float64, // Float64
Unknown, // <empty> 0xCF
Unknown, // Float16 (No corresponding JsonNodeType at the moment)

// Other Value Types
Null, // Null
Expand All @@ -119,7 +119,7 @@ public static class NodeTypes
Unknown, // <empty> 0xD4
Unknown, // <empty> 0xD5
Unknown, // <empty> 0xD6
Unknown, // <empty> 0xD7
Unknown, // UInt8 (No corresponding JsonNodeType at the moment)

Int8, // Int8
Int16, // Int16
Expand Down Expand Up @@ -150,11 +150,11 @@ public static class NodeTypes
Object, // ObjLC2 (2-byte length and count)
Object, // ObjLC4 (4-byte length and count)

// Empty Range
Unknown, // <empty> 0xF0
Unknown, // <empty> 0xF1
Unknown, // <empty> 0xF2
Unknown, // <empty> 0xF3
// Array and Object Special Type Markers
Array, // ArrNumC1 Uniform number array of 1-byte item count
Array, // ArrNumC2 Uniform number array of 2-byte item count
Array, // Array of 1-byte item count of Uniform number array of 1-byte item count
Array, // Array of 2-byte item count of Uniform number array of 2-byte item count
Unknown, // <empty> 0xF4
Unknown, // <empty> 0xF5
Unknown, // <empty> 0xF6
Expand Down
Loading

0 comments on commit d5e954c

Please sign in to comment.