Copied dotnet code from CosmosDB repository

This commit is contained in:
David Noble
2019-08-20 11:58:29 -07:00
parent b0e89b0dda
commit 31f3bc828b
201 changed files with 37803 additions and 0 deletions

View File

@@ -0,0 +1,114 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
namespace Microsoft.Azure.Cosmos.Serialization.HybridRowGenerator
{
using System;
using Microsoft.Azure.Cosmos.Core;
public static unsafe class ByteConverter
{
private static readonly byte[] DecodeTable =
{
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
};
private static readonly uint[] EncodeTable = ByteConverter.Initialize();
public static string ToHex(ReadOnlySpan<byte> bytes)
{
int len = bytes.Length;
string result = new string((char)0, len * 2);
fixed (uint* lp = ByteConverter.EncodeTable)
fixed (byte* bp = bytes)
fixed (char* rp = result)
{
for (int i = 0; i < len; i++)
{
((uint*)rp)[i] = lp[bp[i]];
}
}
return result;
}
public static byte[] ToBytes(string hexChars)
{
Contract.Requires(hexChars != null);
Contract.Requires(hexChars.Length % 2 == 0);
int len = hexChars.Length;
byte[] result = new byte[len / 2];
fixed (byte* lp = ByteConverter.DecodeTable)
fixed (char* cp = hexChars)
fixed (byte* rp = result)
{
for (int i = 0; i < len; i += 2)
{
int c1 = cp[i];
if ((c1 < 0) || (c1 > 255))
{
throw new Exception($"Invalid character: {c1}");
}
byte b1 = lp[c1];
if (b1 == 255)
{
throw new Exception($"Invalid character: {c1}");
}
int c2 = cp[i + 1];
if ((c2 < 0) || (c2 > 255))
{
throw new Exception($"Invalid character: {c2}");
}
byte b2 = lp[c2];
if (b2 == 255)
{
throw new Exception($"Invalid character: {c2}");
}
rp[i / 2] = (byte)((b1 << 4) | b2);
}
}
return result;
}
private static uint[] Initialize()
{
uint[] result = new uint[256];
for (int i = 0; i < 256; i++)
{
string s = i.ToString("X2");
if (BitConverter.IsLittleEndian)
{
result[i] = ((uint)s[0]) + ((uint)s[1] << 16);
}
else
{
result[i] = ((uint)s[1]) + ((uint)s[0] << 16);
}
}
return result;
}
}
}

View File

@@ -0,0 +1,36 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
namespace Microsoft.Azure.Cosmos.Serialization.HybridRowGenerator
{
using Microsoft.Azure.Cosmos.Core;
using Microsoft.Azure.Cosmos.Serialization.HybridRow;
public class CharDistribution
{
private readonly char min;
private readonly char max;
private readonly DistributionType type;
public CharDistribution(char min, char max, DistributionType type = DistributionType.Uniform)
{
this.min = min;
this.max = max;
this.type = type;
}
public char Min => this.min;
public char Max => this.max;
public DistributionType Type => this.type;
public char Next(RandomGenerator rand)
{
Contract.Requires(this.type == DistributionType.Uniform);
return (char)rand.NextUInt16(this.min, this.max);
}
}
}

View File

@@ -0,0 +1,749 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
namespace Microsoft.Azure.Cosmos.Serialization.HybridRowGenerator
{
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Azure.Cosmos.Core;
using Microsoft.Azure.Cosmos.Core.Utf8;
using Microsoft.Azure.Cosmos.Serialization.HybridRow;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.IO;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.Layouts;
public static class DiagnosticConverter
{
public static Result ReaderToString(ref RowReader reader, out string str)
{
ReaderStringContext ctx = new ReaderStringContext(new StringBuilder());
Result result = DiagnosticConverter.ReaderToString(ref reader, ctx);
if (result != Result.Success)
{
str = null;
return result;
}
str = ctx.Builder.ToString();
return Result.Success;
}
public static Result ReaderToDynamic(ref RowReader reader, out Dictionary<Utf8String, object> scope)
{
scope = new Dictionary<Utf8String, object>(SamplingUtf8StringComparer.Default);
return DiagnosticConverter.ReaderToDynamic(ref reader, scope);
}
private static Result ReaderToDynamic(ref RowReader reader, object scope)
{
while (reader.Read())
{
Result r;
switch (reader.Type.LayoutCode)
{
case LayoutCode.Null:
{
r = reader.ReadNull(out NullValue value);
if (r != Result.Success)
{
return r;
}
DiagnosticConverter.AddToScope(scope, reader.Path, value);
break;
}
case LayoutCode.Boolean:
{
r = reader.ReadBool(out bool value);
if (r != Result.Success)
{
return r;
}
DiagnosticConverter.AddToScope(scope, reader.Path, value);
break;
}
case LayoutCode.Int8:
{
r = reader.ReadInt8(out sbyte value);
if (r != Result.Success)
{
return r;
}
DiagnosticConverter.AddToScope(scope, reader.Path, value);
break;
}
case LayoutCode.Int16:
{
r = reader.ReadInt16(out short value);
if (r != Result.Success)
{
return r;
}
DiagnosticConverter.AddToScope(scope, reader.Path, value);
break;
}
case LayoutCode.Int32:
{
r = reader.ReadInt32(out int value);
if (r != Result.Success)
{
return r;
}
DiagnosticConverter.AddToScope(scope, reader.Path, value);
break;
}
case LayoutCode.Int64:
{
r = reader.ReadInt64(out long value);
if (r != Result.Success)
{
return r;
}
DiagnosticConverter.AddToScope(scope, reader.Path, value);
break;
}
case LayoutCode.UInt8:
{
r = reader.ReadUInt8(out byte value);
if (r != Result.Success)
{
return r;
}
DiagnosticConverter.AddToScope(scope, reader.Path, value);
break;
}
case LayoutCode.UInt16:
{
r = reader.ReadUInt16(out ushort value);
if (r != Result.Success)
{
return r;
}
DiagnosticConverter.AddToScope(scope, reader.Path, value);
break;
}
case LayoutCode.UInt32:
{
r = reader.ReadUInt32(out uint value);
if (r != Result.Success)
{
return r;
}
DiagnosticConverter.AddToScope(scope, reader.Path, value);
break;
}
case LayoutCode.UInt64:
{
r = reader.ReadUInt64(out ulong value);
if (r != Result.Success)
{
return r;
}
DiagnosticConverter.AddToScope(scope, reader.Path, value);
break;
}
case LayoutCode.VarInt:
{
r = reader.ReadVarInt(out long value);
if (r != Result.Success)
{
return r;
}
DiagnosticConverter.AddToScope(scope, reader.Path, value);
break;
}
case LayoutCode.VarUInt:
{
r = reader.ReadVarUInt(out ulong value);
if (r != Result.Success)
{
return r;
}
DiagnosticConverter.AddToScope(scope, reader.Path, value);
break;
}
case LayoutCode.Float32:
{
r = reader.ReadFloat32(out float value);
if (r != Result.Success)
{
return r;
}
DiagnosticConverter.AddToScope(scope, reader.Path, value);
break;
}
case LayoutCode.Float64:
{
r = reader.ReadFloat64(out double value);
if (r != Result.Success)
{
return r;
}
DiagnosticConverter.AddToScope(scope, reader.Path, value);
break;
}
case LayoutCode.Float128:
{
r = reader.ReadFloat128(out Float128 value);
if (r != Result.Success)
{
return r;
}
DiagnosticConverter.AddToScope(scope, reader.Path, value);
break;
}
case LayoutCode.Decimal:
{
r = reader.ReadDecimal(out decimal value);
if (r != Result.Success)
{
return r;
}
DiagnosticConverter.AddToScope(scope, reader.Path, value);
break;
}
case LayoutCode.DateTime:
{
r = reader.ReadDateTime(out DateTime value);
if (r != Result.Success)
{
return r;
}
DiagnosticConverter.AddToScope(scope, reader.Path, value);
break;
}
case LayoutCode.UnixDateTime:
{
r = reader.ReadUnixDateTime(out UnixDateTime value);
if (r != Result.Success)
{
return r;
}
DiagnosticConverter.AddToScope(scope, reader.Path, value);
break;
}
case LayoutCode.Guid:
{
r = reader.ReadGuid(out Guid value);
if (r != Result.Success)
{
return r;
}
DiagnosticConverter.AddToScope(scope, reader.Path, value);
break;
}
case LayoutCode.MongoDbObjectId:
{
r = reader.ReadMongoDbObjectId(out MongoDbObjectId value);
if (r != Result.Success)
{
return r;
}
DiagnosticConverter.AddToScope(scope, reader.Path, value);
break;
}
case LayoutCode.Utf8:
{
r = reader.ReadString(out Utf8String value);
if (r != Result.Success)
{
return r;
}
DiagnosticConverter.AddToScope(scope, reader.Path, value);
break;
}
case LayoutCode.Binary:
{
r = reader.ReadBinary(out byte[] value);
if (r != Result.Success)
{
return r;
}
DiagnosticConverter.AddToScope(scope, reader.Path, value);
break;
}
case LayoutCode.NullableScope:
case LayoutCode.ImmutableNullableScope:
{
if (!reader.HasValue)
{
break;
}
goto case LayoutCode.TypedTupleScope;
}
case LayoutCode.ObjectScope:
case LayoutCode.ImmutableObjectScope:
case LayoutCode.Schema:
case LayoutCode.ImmutableSchema:
{
object childScope = new Dictionary<Utf8String, object>(SamplingUtf8StringComparer.Default);
r = reader.ReadScope(childScope, DiagnosticConverter.ReaderToDynamic);
if (r != Result.Success)
{
return r;
}
DiagnosticConverter.AddToScope(scope, reader.Path, childScope);
break;
}
case LayoutCode.ArrayScope:
case LayoutCode.ImmutableArrayScope:
case LayoutCode.TypedArrayScope:
case LayoutCode.ImmutableTypedArrayScope:
case LayoutCode.TypedSetScope:
case LayoutCode.ImmutableTypedSetScope:
case LayoutCode.TypedMapScope:
case LayoutCode.ImmutableTypedMapScope:
case LayoutCode.TupleScope:
case LayoutCode.ImmutableTupleScope:
case LayoutCode.TypedTupleScope:
case LayoutCode.ImmutableTypedTupleScope:
case LayoutCode.TaggedScope:
case LayoutCode.ImmutableTaggedScope:
case LayoutCode.Tagged2Scope:
case LayoutCode.ImmutableTagged2Scope:
{
object childScope = new List<object>();
r = reader.ReadScope(childScope, DiagnosticConverter.ReaderToDynamic);
if (r != Result.Success)
{
return r;
}
DiagnosticConverter.AddToScope(scope, reader.Path, childScope);
break;
}
default:
{
Contract.Assert(false, $"Unknown type will be ignored: {reader.Type.LayoutCode}");
break;
}
}
}
return Result.Success;
}
private static void AddToScope(object scope, UtfAnyString path, object value)
{
if (scope is List<object> linearScope)
{
Contract.Assert(path.IsNull);
linearScope.Add(value);
}
else
{
Contract.Assert(!string.IsNullOrWhiteSpace(path.ToString()));
((Dictionary<Utf8String, object>)scope)[path.ToUtf8String()] = value;
}
}
private static Result ReaderToString(ref RowReader reader, ReaderStringContext ctx)
{
while (reader.Read())
{
string path = !reader.Path.IsNull ? $"\"{reader.Path}\"" : "null";
ctx.Builder.Append(
$"{new string(' ', ctx.Indent * 2)}Storage: {reader.Storage}, Path: {path}, Index: {reader.Index}, Type: {reader.Type.Name + reader.TypeArgs.ToString()}, Value: ");
Result r;
switch (reader.Type.LayoutCode)
{
case LayoutCode.Null:
{
r = reader.ReadNull(out NullValue _);
if (r != Result.Success)
{
return r;
}
ctx.Builder.AppendLine("null");
break;
}
case LayoutCode.Boolean:
case LayoutCode.BooleanFalse:
{
r = reader.ReadBool(out bool value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(value);
ctx.Builder.AppendLine();
break;
}
case LayoutCode.Int8:
{
r = reader.ReadInt8(out sbyte value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(value);
ctx.Builder.AppendLine();
break;
}
case LayoutCode.Int16:
{
r = reader.ReadInt16(out short value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(value);
ctx.Builder.AppendLine();
break;
}
case LayoutCode.Int32:
{
r = reader.ReadInt32(out int value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(value);
ctx.Builder.AppendLine();
break;
}
case LayoutCode.Int64:
{
r = reader.ReadInt64(out long value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(value);
ctx.Builder.AppendLine();
break;
}
case LayoutCode.UInt8:
{
r = reader.ReadUInt8(out byte value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(value);
ctx.Builder.AppendLine();
break;
}
case LayoutCode.UInt16:
{
r = reader.ReadUInt16(out ushort value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(value);
ctx.Builder.AppendLine();
break;
}
case LayoutCode.UInt32:
{
r = reader.ReadUInt32(out uint value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(value);
ctx.Builder.AppendLine();
break;
}
case LayoutCode.UInt64:
{
r = reader.ReadUInt64(out ulong value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(value);
ctx.Builder.AppendLine();
break;
}
case LayoutCode.VarInt:
{
r = reader.ReadVarInt(out long value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(value);
ctx.Builder.AppendLine();
break;
}
case LayoutCode.VarUInt:
{
r = reader.ReadVarUInt(out ulong value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(value);
ctx.Builder.AppendLine();
break;
}
case LayoutCode.Float32:
{
r = reader.ReadFloat32(out float value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(value);
ctx.Builder.AppendLine();
break;
}
case LayoutCode.Float64:
{
r = reader.ReadFloat64(out double value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(value);
ctx.Builder.AppendLine();
break;
}
case LayoutCode.Float128:
{
r = reader.ReadFloat128(out Float128 value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.AppendFormat("High: {0}, Low: {1}\n", value.High, value.Low);
break;
}
case LayoutCode.Decimal:
{
r = reader.ReadDecimal(out decimal value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(value);
ctx.Builder.AppendLine();
break;
}
case LayoutCode.DateTime:
{
r = reader.ReadDateTime(out DateTime value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(value);
ctx.Builder.AppendLine();
break;
}
case LayoutCode.UnixDateTime:
{
r = reader.ReadUnixDateTime(out UnixDateTime value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.Append(value.Milliseconds);
ctx.Builder.AppendLine();
break;
}
case LayoutCode.Guid:
{
r = reader.ReadGuid(out Guid value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.AppendLine(value.ToString());
break;
}
case LayoutCode.MongoDbObjectId:
{
r = reader.ReadMongoDbObjectId(out MongoDbObjectId value);
if (r != Result.Success)
{
return r;
}
ReadOnlyMemory<byte> bytes = value.ToByteArray();
ctx.Builder.AppendLine(ByteConverter.ToHex(bytes.Span));
break;
}
case LayoutCode.Utf8:
{
r = reader.ReadString(out Utf8String value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.AppendLine(value.ToString());
break;
}
case LayoutCode.Binary:
{
r = reader.ReadBinary(out ReadOnlySpan<byte> value);
if (r != Result.Success)
{
return r;
}
ctx.Builder.AppendLine(ByteConverter.ToHex(value));
break;
}
case LayoutCode.NullableScope:
case LayoutCode.ImmutableNullableScope:
{
if (!reader.HasValue)
{
ctx.Builder.AppendLine("null");
break;
}
goto case LayoutCode.TypedTupleScope;
}
case LayoutCode.ObjectScope:
case LayoutCode.ImmutableObjectScope:
case LayoutCode.ArrayScope:
case LayoutCode.ImmutableArrayScope:
case LayoutCode.TypedArrayScope:
case LayoutCode.ImmutableTypedArrayScope:
case LayoutCode.TypedSetScope:
case LayoutCode.ImmutableTypedSetScope:
case LayoutCode.TypedMapScope:
case LayoutCode.ImmutableTypedMapScope:
case LayoutCode.TupleScope:
case LayoutCode.ImmutableTupleScope:
case LayoutCode.TypedTupleScope:
case LayoutCode.ImmutableTypedTupleScope:
case LayoutCode.TaggedScope:
case LayoutCode.ImmutableTaggedScope:
case LayoutCode.Tagged2Scope:
case LayoutCode.ImmutableTagged2Scope:
case LayoutCode.Schema:
case LayoutCode.ImmutableSchema:
{
ctx.Builder.AppendLine("{");
r = reader.ReadScope(new ReaderStringContext(ctx.Builder, ctx.Indent + 1), DiagnosticConverter.ReaderToString);
if (r != Result.Success)
{
return r;
}
ctx.Builder.AppendLine($"{new string(' ', ctx.Indent * 2)}}}");
break;
}
default:
{
Contract.Assert(false, $"Unknown type will be ignored: {reader.Type.LayoutCode}");
break;
}
}
}
return Result.Success;
}
private readonly struct ReaderStringContext
{
public readonly int Indent;
public readonly StringBuilder Builder;
public ReaderStringContext(StringBuilder builder, int indent = 0)
{
this.Indent = indent;
this.Builder = builder;
}
}
}
}

View File

@@ -0,0 +1,11 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
namespace Microsoft.Azure.Cosmos.Serialization.HybridRowGenerator
{
public enum DistributionType
{
Uniform = 0,
}
}

View File

@@ -0,0 +1,105 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
namespace Microsoft.Azure.Cosmos.Serialization.HybridRowGenerator
{
using Microsoft.Azure.Cosmos.Serialization.HybridRow.Schemas;
public class HybridRowGeneratorConfig
{
/// <summary>The number of attempts to allocate a value given an exclusion list before aborting.</summary>
private const int ConflictRetryAttemptsDefault = 100;
/// <summary>
/// The rate at which the cardinality of substructures decays as a function of depth. This
/// ensures that randomly generated values don't become infinitely large.
/// </summary>
private const double DepthDecayFactorDefault = -1.0D;
/// <summary>
/// The length (in chars) of identifiers including namespace names, schema names, property
/// names, etc.
/// </summary>
private static readonly IntDistribution IdentifierLengthDefault = new IntDistribution(1, 20);
/// <summary>
/// The distribution of unicode characters used in constructing identifiers including
/// namespace names, schema names, property names, etc.
/// </summary>
private static readonly CharDistribution IdentifierCharactersDefault = new CharDistribution('a', 'z');
/// <summary>The length (in chars) of annotation comments within the schema.</summary>
private static readonly IntDistribution CommentLengthDefault = new IntDistribution(0, 50);
/// <summary>The length (in chars) of string values.</summary>
private static readonly IntDistribution StringValueLengthDefault = new IntDistribution(0, 100);
/// <summary>The length (in bytes) of binary values.</summary>
private static readonly IntDistribution BinaryValueLengthDefault = new IntDistribution(0, 100);
/// <summary>The length (in number of elements) of collection scopes.</summary>
private static readonly IntDistribution CollectionValueLengthDefault = new IntDistribution(0, 10);
/// <summary>The distribution of unicode characters used in constructing Unicode field values.</summary>
private static readonly CharDistribution UnicodeCharactersDefault = new CharDistribution('\u0001', char.MaxValue);
/// <summary>The space of SchemaId values assigned to schemas (table or UDT) within a single namespace.</summary>
private static readonly IntDistribution SchemaIdsDefault = new IntDistribution(int.MinValue, int.MaxValue);
/// <summary>The number of properties (i.e. columns, fields) to appear in a table or UDT definition.</summary>
private static readonly IntDistribution NumTablePropertiesDefault = new IntDistribution(1, 10);
/// <summary>The number of items to appear in a tuple field.</summary>
private static readonly IntDistribution NumTupleItemsDefault = new IntDistribution(2, 5);
/// <summary>The number of items to appear in a tagged field.</summary>
private static readonly IntDistribution NumTaggedItemsDefault = new IntDistribution(1, 2);
/// <summary>The length (in units, e.g. chars, bytes, etc.) of variable length primitive field values.</summary>
private static readonly IntDistribution PrimitiveFieldValueLengthDefault = new IntDistribution(1, 1024);
/// <summary>The distribution of types for fields.</summary>
private static readonly IntDistribution FieldTypeDefault = new IntDistribution((int)TypeKind.Null, (int)TypeKind.Schema);
/// <summary>The distribution of storage for fields.</summary>
private static readonly IntDistribution FieldStorageDefault = new IntDistribution((int)StorageKind.Sparse, (int)StorageKind.Variable);
/// <summary>The distribution of initial sizes for RowBuffers.</summary>
private static readonly IntDistribution RowBufferInitialCapacityDefault = new IntDistribution(0, 2 * 1024 * 1024);
public IntDistribution IdentifierLength { get; set; } = HybridRowGeneratorConfig.IdentifierLengthDefault;
public CharDistribution IdentifierCharacters { get; set; } = HybridRowGeneratorConfig.IdentifierCharactersDefault;
public IntDistribution CommentLength { get; set; } = HybridRowGeneratorConfig.CommentLengthDefault;
public IntDistribution StringValueLength { get; set; } = HybridRowGeneratorConfig.StringValueLengthDefault;
public IntDistribution BinaryValueLength { get; set; } = HybridRowGeneratorConfig.BinaryValueLengthDefault;
public IntDistribution CollectionValueLength { get; set; } = HybridRowGeneratorConfig.CollectionValueLengthDefault;
public CharDistribution UnicodeCharacters { get; set; } = HybridRowGeneratorConfig.UnicodeCharactersDefault;
public IntDistribution SchemaIds { get; set; } = HybridRowGeneratorConfig.SchemaIdsDefault;
public IntDistribution NumTableProperties { get; set; } = HybridRowGeneratorConfig.NumTablePropertiesDefault;
public IntDistribution NumTupleItems { get; set; } = HybridRowGeneratorConfig.NumTupleItemsDefault;
public IntDistribution NumTaggedItems { get; set; } = HybridRowGeneratorConfig.NumTaggedItemsDefault;
public IntDistribution PrimitiveFieldValueLength { get; set; } = HybridRowGeneratorConfig.PrimitiveFieldValueLengthDefault;
public IntDistribution FieldType { get; set; } = HybridRowGeneratorConfig.FieldTypeDefault;
public IntDistribution FieldStorage { get; set; } = HybridRowGeneratorConfig.FieldStorageDefault;
public IntDistribution RowBufferInitialCapacity { get; set; } = HybridRowGeneratorConfig.RowBufferInitialCapacityDefault;
public int ConflictRetryAttempts { get; set; } = HybridRowGeneratorConfig.ConflictRetryAttemptsDefault;
public double DepthDecayFactor { get; set; } = HybridRowGeneratorConfig.DepthDecayFactorDefault;
}
}

View File

@@ -0,0 +1,635 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
#pragma warning disable SA1137 // Elements should have the same indentation
namespace Microsoft.Azure.Cosmos.Serialization.HybridRowGenerator
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Azure.Cosmos.Core;
using Microsoft.Azure.Cosmos.Core.Utf8;
using Microsoft.Azure.Cosmos.Serialization.HybridRow;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.Layouts;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.Schemas;
public class HybridRowValueGenerator
{
private readonly RandomGenerator rand;
private readonly HybridRowGeneratorConfig config;
public HybridRowValueGenerator(RandomGenerator rand, HybridRowGeneratorConfig config)
{
this.rand = rand;
this.config = config;
}
public static bool DynamicTypeArgumentEquals(LayoutResolver resolver, object left, object right, TypeArgument typeArg)
{
if (object.ReferenceEquals(left, null))
{
return object.ReferenceEquals(null, right);
}
if (object.ReferenceEquals(null, right))
{
return false;
}
switch (typeArg.Type.LayoutCode)
{
case LayoutCode.Null:
case LayoutCode.Boolean:
case LayoutCode.Int8:
case LayoutCode.Int16:
case LayoutCode.Int32:
case LayoutCode.Int64:
case LayoutCode.UInt8:
case LayoutCode.UInt16:
case LayoutCode.UInt32:
case LayoutCode.UInt64:
case LayoutCode.VarInt:
case LayoutCode.VarUInt:
case LayoutCode.Float32:
case LayoutCode.Float64:
case LayoutCode.Float128:
case LayoutCode.Decimal:
case LayoutCode.DateTime:
case LayoutCode.UnixDateTime:
case LayoutCode.Guid:
case LayoutCode.MongoDbObjectId:
case LayoutCode.Utf8:
return object.Equals(left, right);
case LayoutCode.Binary:
return ((byte[])left).SequenceEqual((byte[])right);
case LayoutCode.ObjectScope:
case LayoutCode.ImmutableObjectScope:
{
Dictionary<Utf8String, object> leftDict = (Dictionary<Utf8String, object>)left;
Dictionary<Utf8String, object> rightDict = (Dictionary<Utf8String, object>)right;
if (leftDict.Count != rightDict.Count)
{
return false;
}
// TODO: add properties to an object scope.
return true;
}
case LayoutCode.TypedArrayScope:
case LayoutCode.ImmutableTypedArrayScope:
{
List<object> leftList = (List<object>)left;
List<object> rightList = (List<object>)right;
if (leftList.Count != rightList.Count)
{
return false;
}
for (int i = 0; i < leftList.Count; i++)
{
if (!HybridRowValueGenerator.DynamicTypeArgumentEquals(resolver, leftList[i], rightList[i], typeArg.TypeArgs[0]))
{
return false;
}
}
return true;
}
case LayoutCode.TypedSetScope:
case LayoutCode.ImmutableTypedSetScope:
{
List<object> leftList = (List<object>)left;
List<object> rightList = (List<object>)right;
if (leftList.Count != rightList.Count)
{
return false;
}
List<object> working = new List<object>(leftList);
foreach (object rightItem in rightList)
{
int i = HybridRowValueGenerator.SetContains(resolver, working, rightItem, typeArg);
if (i == -1)
{
return false;
}
working.RemoveAt(i);
}
return true;
}
case LayoutCode.TypedMapScope:
case LayoutCode.ImmutableTypedMapScope:
{
List<List<object>> leftList = (List<List<object>>)left;
List<List<object>> rightList = (List<List<object>>)right;
if (leftList.Count != rightList.Count)
{
return false;
}
List<List<object>> working = new List<List<object>>(leftList);
foreach (List<object> rightItem in rightList)
{
int i = HybridRowValueGenerator.MapContains(resolver, working, rightItem[0], typeArg);
if (i == -1)
{
return false;
}
List<object> leftItem = working[i];
if (!HybridRowValueGenerator.DynamicTypeArgumentEquals(resolver, leftItem[1], rightItem[1], typeArg.TypeArgs[1]))
{
return false;
}
working.RemoveAt(i);
}
return true;
}
case LayoutCode.TupleScope:
case LayoutCode.ImmutableTupleScope:
case LayoutCode.TypedTupleScope:
case LayoutCode.ImmutableTypedTupleScope:
case LayoutCode.TaggedScope:
case LayoutCode.ImmutableTaggedScope:
case LayoutCode.Tagged2Scope:
case LayoutCode.ImmutableTagged2Scope:
{
List<object> leftList = (List<object>)left;
List<object> rightList = (List<object>)right;
if (leftList.Count != rightList.Count)
{
return false;
}
Contract.Assert(leftList.Count == typeArg.TypeArgs.Count);
for (int i = 0; i < leftList.Count; i++)
{
if (!HybridRowValueGenerator.DynamicTypeArgumentEquals(resolver, leftList[i], rightList[i], typeArg.TypeArgs[i]))
{
return false;
}
}
return true;
}
case LayoutCode.NullableScope:
case LayoutCode.ImmutableNullableScope:
{
Contract.Assert(typeArg.TypeArgs.Count == 1);
return HybridRowValueGenerator.DynamicTypeArgumentEquals(resolver, left, right, typeArg.TypeArgs[0]);
}
case LayoutCode.Schema:
case LayoutCode.ImmutableSchema:
{
SchemaId schemaId = typeArg.TypeArgs.SchemaId;
Contract.Assert(schemaId != SchemaId.Invalid);
Layout layout = resolver.Resolve(schemaId);
Contract.Assert(layout != null);
Dictionary<Utf8String, object> leftDict = (Dictionary<Utf8String, object>)left;
Dictionary<Utf8String, object> rightDict = (Dictionary<Utf8String, object>)right;
if (leftDict.Count != rightDict.Count)
{
return false;
}
foreach (KeyValuePair<Utf8String, object> pair in leftDict)
{
if (!rightDict.TryGetValue(pair.Key, out object rightValue))
{
return false;
}
Contract.Requires(layout.TryFind(pair.Key, out LayoutColumn c));
return HybridRowValueGenerator.DynamicTypeArgumentEquals(
resolver,
pair.Value,
rightValue,
new TypeArgument(c.Type, c.TypeArgs));
}
return true;
}
default:
Contract.Assert(false, $"Unknown type will be ignored: {typeArg}");
return false;
}
}
public static T GenerateExclusive<T>(Func<T> op, IEnumerable<T> exclusions, int maxCandidates)
{
HashSet<T> exclusionSet = new HashSet<T>(exclusions);
T candidate;
int retryCount = 0;
do
{
if (retryCount >= maxCandidates)
{
throw new Exception(
$"Max Candidates Reached: {maxCandidates} : " +
string.Join(",", from e in exclusions select e.ToString()));
}
candidate = op();
retryCount++;
}
while (exclusionSet.Contains(candidate));
return candidate;
}
public string GenerateIdentifier()
{
int length = this.config.IdentifierLength.Next(this.rand);
CharDistribution distribution = this.config.IdentifierCharacters;
return this.GenerateString(length, distribution);
}
public unsafe string GenerateString(int length, CharDistribution distribution)
{
if (length == 0)
{
return string.Empty;
}
char* result = stackalloc char[length];
int trim;
do
{
for (int i = 0; i < length; i++)
{
result[i] = distribution.Next(this.rand);
}
// Drop characters until we are under the encoded length.
trim = length;
while ((trim > 0) && Encoding.UTF8.GetByteCount(result, trim) > length)
{
trim--;
result[trim] = '\0';
}
}
while (trim == 0);
// Pad with zero's if the resulting encoding is too short.
while (Encoding.UTF8.GetByteCount(result, trim) < length)
{
trim++;
}
Contract.Assert(Encoding.UTF8.GetByteCount(result, trim) == length);
return new string(result, 0, trim);
}
public byte[] GenerateBinary(int length)
{
byte[] result = new byte[length];
for (int i = 0; i < length; i++)
{
result[i] = this.rand.NextUInt8();
}
return result;
}
public SchemaId GenerateSchemaId()
{
return new SchemaId(this.config.SchemaIds.Next(this.rand));
}
public StorageKind GenerateStorageKind()
{
return (StorageKind)this.config.FieldStorage.Next(this.rand);
}
public TypeKind GenerateTypeKind()
{
return (TypeKind)this.config.FieldType.Next(this.rand);
}
public string GenerateComment()
{
int length = this.config.CommentLength.Next(this.rand);
CharDistribution distribution = this.config.UnicodeCharacters;
return this.GenerateString(length, distribution);
}
public bool GenerateBool()
{
return this.rand.NextInt32(0, 1) != 0;
}
public object GenerateLayoutType(
LayoutResolver resolver,
TypeArgument typeArg,
bool nullable = false,
int length = 0,
StorageKind storage = StorageKind.Sparse)
{
switch (typeArg.Type.LayoutCode)
{
case LayoutCode.Null:
return nullable && this.GenerateBool() ? (object)null : NullValue.Default;
case LayoutCode.Boolean:
return nullable && this.GenerateBool() ? (object)null : this.GenerateBool();
case LayoutCode.Int8:
return nullable && this.GenerateBool() ? (object)null : this.rand.NextInt8();
case LayoutCode.Int16:
return nullable && this.GenerateBool() ? (object)null : this.rand.NextInt16();
case LayoutCode.Int32:
return nullable && this.GenerateBool() ? (object)null : this.rand.NextInt32();
case LayoutCode.Int64:
return nullable && this.GenerateBool() ? (object)null : this.rand.NextInt64();
case LayoutCode.UInt8:
return nullable && this.GenerateBool() ? (object)null : this.rand.NextUInt8();
case LayoutCode.UInt16:
return nullable && this.GenerateBool() ? (object)null : this.rand.NextUInt16();
case LayoutCode.UInt32:
return nullable && this.GenerateBool() ? (object)null : this.rand.NextUInt32();
case LayoutCode.UInt64:
return nullable && this.GenerateBool() ? (object)null : this.rand.NextUInt64();
case LayoutCode.VarInt:
return nullable && this.GenerateBool() ? (object)null : this.rand.NextInt64();
case LayoutCode.VarUInt:
return nullable && this.GenerateBool() ? (object)null : this.rand.NextUInt64();
case LayoutCode.Float32:
return nullable && this.GenerateBool() ? (object)null : (float)this.rand.NextInt32();
case LayoutCode.Float64:
return nullable && this.GenerateBool() ? (object)null : (double)this.rand.NextInt64();
case LayoutCode.Float128:
return nullable && this.GenerateBool() ? (object)null : new Float128(this.rand.NextInt64(), this.rand.NextInt64());
case LayoutCode.Decimal:
return nullable && this.GenerateBool()
? (object)null
: new decimal(
this.rand.NextInt32(),
this.rand.NextInt32(),
this.rand.NextInt32(),
this.GenerateBool(),
this.rand.NextUInt8(0, 28));
case LayoutCode.DateTime:
{
Contract.Assert(DateTime.MinValue.Ticks == 0);
long ticks = unchecked((long)(this.rand.NextUInt64() % (ulong)(DateTime.MaxValue.Ticks + 1)));
return nullable && this.GenerateBool() ? (object)null : new DateTime(ticks);
}
case LayoutCode.UnixDateTime:
{
return nullable && this.GenerateBool() ? (object)null : new UnixDateTime(this.rand.NextInt64());
}
case LayoutCode.Guid:
return nullable && this.GenerateBool()
? (object)null
: new Guid(
this.rand.NextInt32(),
this.rand.NextInt16(),
this.rand.NextInt16(),
this.rand.NextUInt8(),
this.rand.NextUInt8(),
this.rand.NextUInt8(),
this.rand.NextUInt8(),
this.rand.NextUInt8(),
this.rand.NextUInt8(),
this.rand.NextUInt8(),
this.rand.NextUInt8());
case LayoutCode.MongoDbObjectId:
{
return nullable && this.GenerateBool() ? (object)null : new MongoDbObjectId(this.rand.NextUInt32(), this.rand.NextUInt64());
}
case LayoutCode.Utf8:
{
if (nullable && this.GenerateBool())
{
return null;
}
switch (storage)
{
case StorageKind.Variable:
length = this.rand.NextInt32(0, length == 0 ? this.config.StringValueLength.Next(this.rand) : length);
break;
case StorageKind.Sparse:
length = this.config.StringValueLength.Next(this.rand);
break;
}
CharDistribution distribution = this.config.UnicodeCharacters;
return Utf8String.TranscodeUtf16(this.GenerateString(length, distribution));
}
case LayoutCode.Binary:
{
if (nullable && this.GenerateBool())
{
return null;
}
switch (storage)
{
case StorageKind.Variable:
length = this.rand.NextInt32(0, length == 0 ? this.config.StringValueLength.Next(this.rand) : length);
break;
case StorageKind.Sparse:
length = this.config.StringValueLength.Next(this.rand);
break;
}
return this.GenerateBinary(length);
}
case LayoutCode.ObjectScope:
case LayoutCode.ImmutableObjectScope:
{
if (nullable && this.GenerateBool())
{
return null;
}
Dictionary<Utf8String, object> dict = new Dictionary<Utf8String, object>(SamplingUtf8StringComparer.Default);
// TODO: add properties to an object scope.
return dict;
}
case LayoutCode.TypedArrayScope:
case LayoutCode.ImmutableTypedArrayScope:
{
if (nullable && this.GenerateBool())
{
return null;
}
Contract.Assert(typeArg.TypeArgs.Count == 1);
length = this.config.CollectionValueLength.Next(this.rand);
List<object> arrayValue = new List<object>(length);
for (int i = 0; i < length; i++)
{
arrayValue.Add(this.GenerateLayoutType(resolver, typeArg.TypeArgs[0]));
}
return arrayValue;
}
case LayoutCode.TypedSetScope:
case LayoutCode.ImmutableTypedSetScope:
{
if (nullable && this.GenerateBool())
{
return null;
}
Contract.Assert(typeArg.TypeArgs.Count == 1);
length = this.config.CollectionValueLength.Next(this.rand);
List<object> setValue = new List<object>(length);
for (int i = 0; i < length; i++)
{
object value = this.GenerateLayoutType(resolver, typeArg.TypeArgs[0]);
if (HybridRowValueGenerator.SetContains(resolver, setValue, value, typeArg) == -1)
{
setValue.Add(value);
}
}
return setValue;
}
case LayoutCode.TypedMapScope:
case LayoutCode.ImmutableTypedMapScope:
{
if (nullable && this.GenerateBool())
{
return null;
}
Contract.Assert(typeArg.TypeArgs.Count == 2);
length = this.config.CollectionValueLength.Next(this.rand);
List<List<object>> mapValue = new List<List<object>>(length);
for (int i = 0; i < length; i++)
{
object key = this.GenerateLayoutType(resolver, typeArg.TypeArgs[0]);
if (HybridRowValueGenerator.MapContains(resolver, mapValue, key, typeArg) == -1)
{
object value = this.GenerateLayoutType(resolver, typeArg.TypeArgs[1]);
mapValue.Add(new List<object> { key, value });
}
}
return mapValue;
}
case LayoutCode.TupleScope:
case LayoutCode.ImmutableTupleScope:
case LayoutCode.TypedTupleScope:
case LayoutCode.ImmutableTypedTupleScope:
case LayoutCode.TaggedScope:
case LayoutCode.ImmutableTaggedScope:
case LayoutCode.Tagged2Scope:
case LayoutCode.ImmutableTagged2Scope:
{
if (nullable && this.GenerateBool())
{
return null;
}
List<object> tupleValue = new List<object>(typeArg.TypeArgs.Count);
foreach (TypeArgument t in typeArg.TypeArgs)
{
tupleValue.Add(this.GenerateLayoutType(resolver, t));
}
return tupleValue;
}
case LayoutCode.NullableScope:
case LayoutCode.ImmutableNullableScope:
{
if (this.GenerateBool())
{
return null;
}
return this.GenerateLayoutType(resolver, typeArg.TypeArgs[0]);
}
case LayoutCode.Schema:
case LayoutCode.ImmutableSchema:
{
if (nullable && this.GenerateBool())
{
return null;
}
SchemaId schemaId = typeArg.TypeArgs.SchemaId;
Contract.Assert(schemaId != SchemaId.Invalid);
Layout layout = resolver.Resolve(schemaId);
Contract.Assert(layout != null);
Dictionary<Utf8String, object> dict = new Dictionary<Utf8String, object>(SamplingUtf8StringComparer.Default);
foreach (LayoutColumn c in layout.Columns)
{
dict[c.Path] = this.GenerateLayoutType(
resolver,
new TypeArgument(c.Type, c.TypeArgs),
length: c.Size,
storage: c.Storage);
}
return dict;
}
default:
Contract.Assert(false, $"Unknown type will be ignored: {typeArg}");
return null;
}
}
private static int SetContains(LayoutResolver resolver, List<object> set, object right, TypeArgument typeArg)
{
for (int i = 0; i < set.Count; i++)
{
object left = set[i];
if (HybridRowValueGenerator.DynamicTypeArgumentEquals(resolver, left, right, typeArg.TypeArgs[0]))
{
return i;
}
}
return -1;
}
private static int MapContains(LayoutResolver resolver, List<List<object>> map, object right, TypeArgument typeArg)
{
for (int i = 0; i < map.Count; i++)
{
List<object> pair = map[i];
Contract.Assert(pair.Count == 2);
if (HybridRowValueGenerator.DynamicTypeArgumentEquals(resolver, pair[0], right, typeArg.TypeArgs[0]))
{
return i;
}
}
return -1;
}
}
}

View File

@@ -0,0 +1,36 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
namespace Microsoft.Azure.Cosmos.Serialization.HybridRowGenerator
{
using Microsoft.Azure.Cosmos.Core;
using Microsoft.Azure.Cosmos.Serialization.HybridRow;
public class IntDistribution
{
private readonly int min;
private readonly int max;
private readonly DistributionType type;
public IntDistribution(int min, int max, DistributionType type = DistributionType.Uniform)
{
this.min = min;
this.max = max;
this.type = type;
}
public int Min => this.min;
public int Max => this.max;
public DistributionType Type => this.type;
public int Next(RandomGenerator rand)
{
Contract.Requires(this.type == DistributionType.Uniform);
return rand.NextInt32(this.min, this.max);
}
}
}

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
<ProjectGuid>{B3F04B26-800A-4097-95CE-EACECA9ACE23}</ProjectGuid>
<OutputType>Library</OutputType>
<SigningType>Test</SigningType>
<RootNamespace>Microsoft.Azure.Cosmos.Serialization.HybridRowGenerator</RootNamespace>
<AssemblyName>Microsoft.Azure.Cosmos.Serialization.HybridRowGenerator</AssemblyName>
<TargetFramework>netstandard2.0</TargetFramework>
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), Build.props))\build.props" />
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" />
<PackageReference Include="System.Memory" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Core\Core\Microsoft.Azure.Cosmos.Core.csproj" />
<ProjectReference Include="..\HybridRow\Microsoft.Azure.Cosmos.Serialization.HybridRow.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,20 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Microsoft.Azure.Cosmos.Serialization.HybridRowGenerator")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("B3F04B26-800A-4097-95CE-EACECA9ACE23")]

View File

@@ -0,0 +1,131 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
namespace Microsoft.Azure.Cosmos.Serialization.HybridRowGenerator
{
using System;
using System.Runtime.InteropServices;
using Microsoft.Azure.Cosmos.Core;
public class RandomGenerator
{
private readonly Random root;
public RandomGenerator(Random root)
{
this.root = root;
}
/// <summary>Returns a uniformly distributed 8-bit signed integer in the range specified.</summary>
/// <param name="min">The inclusive lower bound of the random number returned.</param>
/// <param name="max">The inclusive upper bound of the random number returned.</param>
/// <remarks>Requires <paramref name="min" /> &lt; <paramref name="max" />.</remarks>
public sbyte NextInt8(sbyte min = sbyte.MinValue, sbyte max = sbyte.MaxValue)
{
Contract.Requires(min <= max);
unchecked
{
sbyte result = (sbyte)(this.NextUInt8(0, (byte)(max - min)) + min);
return result;
}
}
/// <summary>Returns a uniformly distributed 16-bit signed integer in the range specified.</summary>
/// <param name="min">The inclusive lower bound of the random number returned.</param>
/// <param name="max">The inclusive upper bound of the random number returned.</param>
/// <remarks>Requires <paramref name="min" /> &lt; <paramref name="max" />.</remarks>
public short NextInt16(short min = short.MinValue, short max = short.MaxValue)
{
Contract.Requires(min <= max);
unchecked
{
short result = (short)(this.NextUInt16(0, (ushort)(max - min)) + min);
return result;
}
}
/// <summary>Returns a uniformly distributed 32-bit signed integer in the range specified.</summary>
/// <param name="min">The inclusive lower bound of the random number returned.</param>
/// <param name="max">The inclusive upper bound of the random number returned.</param>
/// <remarks>Requires <paramref name="min" /> &lt; <paramref name="max" />.</remarks>
public int NextInt32(int min = int.MinValue, int max = int.MaxValue)
{
Contract.Requires(min <= max);
unchecked
{
int result = (int)(this.NextUInt32(0, (uint)(max - min)) + min);
return result;
}
}
/// <summary>Returns a uniformly distributed 64-bit signed integer.</summary>
public long NextInt64()
{
unchecked
{
long result = (long)this.NextUInt64();
return result;
}
}
/// <summary>Returns a uniformly distributed 8-bit unsigned integer in the range specified.</summary>
/// <param name="min">The inclusive lower bound of the random number returned.</param>
/// <param name="max">The inclusive upper bound of the random number returned.</param>
/// <remarks>Requires <paramref name="min" /> &lt; <paramref name="max" />.</remarks>
public byte NextUInt8(byte min = byte.MinValue, byte max = byte.MaxValue)
{
Contract.Requires(min <= max);
ulong result = this.NextUInt64();
unchecked
{
result = (result % (((ulong)max - (ulong)min) + 1)) + (ulong)min;
return (byte)result;
}
}
/// <summary>Returns a uniformly distributed 16-bit unsigned integer in the range specified.</summary>
/// <param name="min">The inclusive lower bound of the random number returned.</param>
/// <param name="max">The inclusive upper bound of the random number returned.</param>
/// <remarks>Requires <paramref name="min" /> &lt; <paramref name="max" />.</remarks>
public ushort NextUInt16(ushort min = ushort.MinValue, ushort max = ushort.MaxValue)
{
Contract.Requires(min <= max);
ulong result = this.NextUInt64();
unchecked
{
result = (result % (((ulong)max - (ulong)min) + 1)) + (ulong)min;
return (ushort)result;
}
}
/// <summary>Returns a uniformly distributed 32-bit unsigned integer in the range specified.</summary>
/// <param name="min">The inclusive lower bound of the random number returned.</param>
/// <param name="max">The inclusive upper bound of the random number returned.</param>
/// <remarks>Requires <paramref name="min" /> &lt; <paramref name="max" />.</remarks>
public uint NextUInt32(uint min = uint.MinValue, uint max = uint.MaxValue)
{
Contract.Requires(min <= max);
ulong result = this.NextUInt64();
unchecked
{
result = (result % (((ulong)max - (ulong)min) + 1)) + (ulong)min;
return (uint)result;
}
}
/// <summary>Returns a uniformly distributed 64-bit unsigned integer.</summary>
public ulong NextUInt64()
{
Span<ulong> result = stackalloc ulong[1];
this.root.NextBytes(MemoryMarshal.Cast<ulong, byte>(result));
return result[0];
}
}
}

View File

@@ -0,0 +1,277 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
namespace Microsoft.Azure.Cosmos.Serialization.HybridRowGenerator
{
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Azure.Cosmos.Core;
using Microsoft.Azure.Cosmos.Serialization.HybridRow;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.Schemas;
public class SchemaGenerator
{
private readonly RandomGenerator rand;
private readonly HybridRowGeneratorConfig config;
private readonly HybridRowValueGenerator generator;
public SchemaGenerator(RandomGenerator rand, HybridRowGeneratorConfig config, HybridRowValueGenerator generator)
{
this.rand = rand;
this.config = config;
this.generator = generator;
}
public Namespace InitializeRandomNamespace()
{
Namespace ns = new Namespace()
{
Name = this.generator.GenerateIdentifier(),
};
return ns;
}
public Schema InitializeRandomSchema(Namespace ns, int depth)
{
string name = HybridRowValueGenerator.GenerateExclusive(
this.generator.GenerateIdentifier,
from s1 in ns.Schemas select s1.Name,
this.config.ConflictRetryAttempts);
SchemaId sid = HybridRowValueGenerator.GenerateExclusive(
this.generator.GenerateSchemaId,
from s1 in ns.Schemas select s1.SchemaId,
this.config.ConflictRetryAttempts);
// Allocate and insert the schema *before* recursing to allocate properties. This ensures that nested structure
// doesn't conflict with name or id constraints.
Schema s = new Schema()
{
Name = name,
SchemaId = sid,
Type = TypeKind.Schema,
Comment = this.generator.GenerateComment(),
Options = this.InitializeRandomSchemaOptions(),
};
ns.Schemas.Add(s);
// Recurse and allocate its properties.
s.Properties = this.InitializeRandomProperties(ns, TypeKind.Schema, depth);
return s;
}
private List<Property> InitializeRandomProperties(Namespace ns, TypeKind scope, int depth)
{
int length = this.config.NumTableProperties.Next(this.rand);
// Introduce some decay rate for the number of properties in nested contexts
// to limit the depth of schemas.
if (depth > 0)
{
double scaled = Math.Floor(length * Math.Exp(depth * this.config.DepthDecayFactor));
Contract.Assert(scaled <= length);
length = (int)scaled;
}
List<Property> properties = new List<Property>(length);
for (int i = 0; i < length; i++)
{
PropertyType propType = this.InitializeRandomPropertyType(ns, scope, depth);
string path = HybridRowValueGenerator.GenerateExclusive(
this.generator.GenerateIdentifier,
from s1 in properties select s1.Path,
this.config.ConflictRetryAttempts);
Property prop = new Property()
{
Comment = this.generator.GenerateComment(),
Path = path,
PropertyType = propType,
};
properties.Add(prop);
}
return properties;
}
private PropertyType InitializeRandomPropertyType(Namespace ns, TypeKind scope, int depth)
{
TypeKind type = this.generator.GenerateTypeKind();
PropertyType propType;
switch (type)
{
case TypeKind.Object:
propType = new ObjectPropertyType()
{
Immutable = this.generator.GenerateBool(),
// TODO: add properties to object scopes.
// Properties = this.InitializeRandomProperties(ns, type, depth + 1),
};
break;
case TypeKind.Array:
propType = new ArrayPropertyType()
{
Immutable = this.generator.GenerateBool(),
Items = this.InitializeRandomPropertyType(ns, type, depth + 1),
};
break;
case TypeKind.Set:
propType = new SetPropertyType()
{
Immutable = this.generator.GenerateBool(),
Items = this.InitializeRandomPropertyType(ns, type, depth + 1),
};
break;
case TypeKind.Map:
propType = new MapPropertyType()
{
Immutable = this.generator.GenerateBool(),
Keys = this.InitializeRandomPropertyType(ns, type, depth + 1),
Values = this.InitializeRandomPropertyType(ns, type, depth + 1),
};
break;
case TypeKind.Tuple:
int numItems = this.config.NumTupleItems.Next(this.rand);
List<PropertyType> itemTypes = new List<PropertyType>(numItems);
for (int i = 0; i < numItems; i++)
{
itemTypes.Add(this.InitializeRandomPropertyType(ns, type, depth + 1));
}
propType = new TuplePropertyType()
{
Immutable = this.generator.GenerateBool(),
Items = itemTypes,
};
break;
case TypeKind.Tagged:
int numTagged = this.config.NumTaggedItems.Next(this.rand);
List<PropertyType> taggedItemTypes = new List<PropertyType>(numTagged);
for (int i = 0; i < numTagged; i++)
{
taggedItemTypes.Add(this.InitializeRandomPropertyType(ns, type, depth + 1));
}
propType = new TaggedPropertyType()
{
Immutable = this.generator.GenerateBool(),
Items = taggedItemTypes,
};
break;
case TypeKind.Schema:
Schema udt = this.InitializeRandomSchema(ns, depth + 1);
propType = new UdtPropertyType()
{
Immutable = this.generator.GenerateBool(),
Name = udt.Name,
};
break;
default:
StorageKind storage = (scope == TypeKind.Schema) ? this.generator.GenerateStorageKind() : StorageKind.Sparse;
switch (storage)
{
case StorageKind.Sparse:
// All types are supported in Sparse.
break;
case StorageKind.Fixed:
switch (type)
{
case TypeKind.Null:
case TypeKind.Boolean:
case TypeKind.Int8:
case TypeKind.Int16:
case TypeKind.Int32:
case TypeKind.Int64:
case TypeKind.UInt8:
case TypeKind.UInt16:
case TypeKind.UInt32:
case TypeKind.UInt64:
case TypeKind.Float32:
case TypeKind.Float64:
case TypeKind.Float128:
case TypeKind.Decimal:
case TypeKind.DateTime:
case TypeKind.UnixDateTime:
case TypeKind.Guid:
case TypeKind.MongoDbObjectId:
case TypeKind.Utf8:
case TypeKind.Binary:
// Only these types are supported with fixed storage today.
break;
default:
storage = StorageKind.Sparse;
break;
}
break;
case StorageKind.Variable:
switch (type)
{
case TypeKind.Binary:
case TypeKind.Utf8:
case TypeKind.VarInt:
case TypeKind.VarUInt:
// Only these types are supported with variable storage today.
break;
default:
storage = StorageKind.Sparse;
break;
}
break;
}
propType = new PrimitivePropertyType()
{
Length = storage == StorageKind.Sparse ? 0 : this.config.PrimitiveFieldValueLength.Next(this.rand),
Storage = storage,
};
break;
}
propType.ApiType = this.generator.GenerateIdentifier();
propType.Type = type;
switch (scope)
{
case TypeKind.Array:
case TypeKind.Map:
case TypeKind.Set:
case TypeKind.Tuple:
case TypeKind.Tagged:
propType.Nullable = this.generator.GenerateBool();
break;
default:
propType.Nullable = true;
break;
}
return propType;
}
private SchemaOptions InitializeRandomSchemaOptions()
{
SchemaOptions o = new SchemaOptions()
{
DisallowUnschematized = this.generator.GenerateBool(),
};
return o;
}
}
}

View File

@@ -0,0 +1,330 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
namespace Microsoft.Azure.Cosmos.Serialization.HybridRowGenerator
{
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Azure.Cosmos.Core;
using Microsoft.Azure.Cosmos.Core.Utf8;
using Microsoft.Azure.Cosmos.Serialization.HybridRow;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.IO;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.Layouts;
public ref struct StreamingRowGenerator
{
private RowBuffer row;
public StreamingRowGenerator(int capacity, Layout layout, LayoutResolver resolver, ISpanResizer<byte> resizer = default)
{
this.row = new RowBuffer(capacity, resizer);
this.row.InitLayout(HybridRowVersion.V1, layout, resolver);
}
public int Length => this.row.Length;
public byte[] ToArray() => this.row.ToArray();
public void WriteTo(Stream stream)
{
this.row.WriteTo(stream);
}
public bool ReadFrom(Stream stream, int length)
{
return this.row.ReadFrom(stream, length, HybridRowVersion.V1, this.row.Resolver);
}
public void Reset()
{
Layout layout = this.row.Resolver.Resolve(this.row.Header.SchemaId);
this.row.InitLayout(HybridRowVersion.V1, layout, this.row.Resolver);
}
public RowReader GetReader()
{
return new RowReader(ref this.row);
}
public Result WriteBuffer(Dictionary<Utf8String, object> value)
{
return RowWriter.WriteBuffer(
ref this.row,
value,
(ref RowWriter writer, TypeArgument typeArg, Dictionary<Utf8String, object> dict) =>
{
Layout layout = writer.Resolver.Resolve(typeArg.TypeArgs.SchemaId);
foreach (LayoutColumn c in layout.Columns)
{
Result result = StreamingRowGenerator.LayoutCodeSwitch(ref writer, c.Path, c.TypeArg, dict[c.Path]);
if (result != Result.Success)
{
return result;
}
}
return Result.Success;
});
}
private static Result LayoutCodeSwitch(ref RowWriter writer, Utf8String path, TypeArgument typeArg, object value)
{
switch (typeArg.Type.LayoutCode)
{
case LayoutCode.Null:
return writer.WriteNull(path);
case LayoutCode.Boolean:
return writer.WriteBool(path, value == null ? default : (bool)value);
case LayoutCode.Int8:
return writer.WriteInt8(path, value == null ? default : (sbyte)value);
case LayoutCode.Int16:
return writer.WriteInt16(path, value == null ? default : (short)value);
case LayoutCode.Int32:
return writer.WriteInt32(path, value == null ? default : (int)value);
case LayoutCode.Int64:
return writer.WriteInt64(path, value == null ? default : (long)value);
case LayoutCode.UInt8:
return writer.WriteUInt8(path, value == null ? default : (byte)value);
case LayoutCode.UInt16:
return writer.WriteUInt16(path, value == null ? default : (ushort)value);
case LayoutCode.UInt32:
return writer.WriteUInt32(path, value == null ? default : (uint)value);
case LayoutCode.UInt64:
return writer.WriteUInt64(path, value == null ? default : (ulong)value);
case LayoutCode.VarInt:
return writer.WriteVarInt(path, value == null ? default : (long)value);
case LayoutCode.VarUInt:
return writer.WriteVarUInt(path, value == null ? default : (ulong)value);
case LayoutCode.Float32:
return writer.WriteFloat32(path, value == null ? default : (float)value);
case LayoutCode.Float64:
return writer.WriteFloat64(path, value == null ? default : (double)value);
case LayoutCode.Float128:
return writer.WriteFloat128(path, value == null ? default : (Float128)value);
case LayoutCode.Decimal:
return writer.WriteDecimal(path, value == null ? default : (decimal)value);
case LayoutCode.DateTime:
return writer.WriteDateTime(path, value == null ? default : (DateTime)value);
case LayoutCode.UnixDateTime:
return writer.WriteUnixDateTime(path, value == null ? default : (UnixDateTime)value);
case LayoutCode.Guid:
return writer.WriteGuid(path, value == null ? default : (Guid)value);
case LayoutCode.MongoDbObjectId:
return writer.WriteMongoDbObjectId(path, value == null ? default : (MongoDbObjectId)value);
case LayoutCode.Utf8:
return writer.WriteString(path, value == null ? default : (Utf8String)value);
case LayoutCode.Binary:
return writer.WriteBinary(path, value == null ? default : (byte[])value);
case LayoutCode.ObjectScope:
case LayoutCode.ImmutableObjectScope:
return StreamingRowGenerator.DispatchObject(ref writer, path, typeArg, value);
case LayoutCode.TypedArrayScope:
case LayoutCode.ImmutableTypedArrayScope:
return StreamingRowGenerator.DispatchArray(ref writer, path, typeArg, value);
case LayoutCode.TypedSetScope:
case LayoutCode.ImmutableTypedSetScope:
return StreamingRowGenerator.DispatchSet(ref writer, path, typeArg, value);
case LayoutCode.TypedMapScope:
case LayoutCode.ImmutableTypedMapScope:
return StreamingRowGenerator.DispatchMap(ref writer, path, typeArg, value);
case LayoutCode.TupleScope:
case LayoutCode.ImmutableTupleScope:
case LayoutCode.TypedTupleScope:
case LayoutCode.ImmutableTypedTupleScope:
case LayoutCode.TaggedScope:
case LayoutCode.ImmutableTaggedScope:
case LayoutCode.Tagged2Scope:
case LayoutCode.ImmutableTagged2Scope:
return StreamingRowGenerator.DispatchTuple(ref writer, path, typeArg, value);
case LayoutCode.NullableScope:
case LayoutCode.ImmutableNullableScope:
return StreamingRowGenerator.DispatchNullable(ref writer, path, typeArg, value);
case LayoutCode.Schema:
case LayoutCode.ImmutableSchema:
return StreamingRowGenerator.DispatchUDT(ref writer, path, typeArg, value);
default:
Contract.Assert(false, $"Unknown type will be ignored: {typeArg}");
return Result.Failure;
}
}
private static Result DispatchObject(ref RowWriter writer, Utf8String path, TypeArgument typeArg, object value)
{
return writer.WriteScope(
path,
typeArg,
(Dictionary<Utf8String, object>)value,
(ref RowWriter writer2, TypeArgument typeArg2, Dictionary<Utf8String, object> value2) =>
{
// TODO: support properties in an object scope.
return Result.Success;
});
}
private static Result DispatchArray(ref RowWriter writer, Utf8String path, TypeArgument typeArg, object value)
{
Contract.Requires(typeArg.TypeArgs.Count == 1);
return writer.WriteScope(
path,
typeArg,
(List<object>)value,
(ref RowWriter writer2, TypeArgument typeArg2, List<object> items2) =>
{
foreach (object item in items2)
{
Result r = StreamingRowGenerator.LayoutCodeSwitch(ref writer2, null, typeArg2.TypeArgs[0], item);
if (r != Result.Success)
{
return r;
}
}
return Result.Success;
});
}
private static Result DispatchTuple(ref RowWriter writer, Utf8String path, TypeArgument typeArg, object value)
{
Contract.Requires(typeArg.TypeArgs.Count >= 2);
List<object> items = (List<object>)value;
Contract.Assert(items.Count == typeArg.TypeArgs.Count);
return writer.WriteScope(
path,
typeArg,
items,
(ref RowWriter writer2, TypeArgument typeArg2, List<object> items2) =>
{
for (int i = 0; i < items2.Count; i++)
{
object item = items2[i];
Result r = StreamingRowGenerator.LayoutCodeSwitch(ref writer2, null, typeArg2.TypeArgs[i], item);
if (r != Result.Success)
{
return r;
}
}
return Result.Success;
});
}
private static Result DispatchNullable(ref RowWriter writer, Utf8String path, TypeArgument typeArg, object value)
{
Contract.Requires(typeArg.TypeArgs.Count == 1);
RowWriter.WriterFunc<object> f0 = null;
if (value != null)
{
f0 = (ref RowWriter writer2, TypeArgument typeArg2, object value2) =>
StreamingRowGenerator.LayoutCodeSwitch(ref writer2, null, typeArg2.TypeArgs[0], value2);
}
return writer.WriteScope(path, typeArg, value, f0);
}
private static Result DispatchSet(ref RowWriter writer, Utf8String path, TypeArgument typeArg, object value)
{
Contract.Requires(typeArg.TypeArgs.Count == 1);
return writer.WriteScope(
path,
typeArg,
(List<object>)value,
(ref RowWriter writer2, TypeArgument typeArg2, List<object> items2) =>
{
foreach (object item in items2)
{
Result r = StreamingRowGenerator.LayoutCodeSwitch(ref writer2, null, typeArg2.TypeArgs[0], item);
if (r != Result.Success)
{
return r;
}
}
return Result.Success;
});
}
private static Result DispatchMap(ref RowWriter writer, Utf8String path, TypeArgument typeArg, object value)
{
Contract.Requires(typeArg.TypeArgs.Count == 2);
return writer.WriteScope(
path,
typeArg,
(List<object>)value,
(ref RowWriter writer2, TypeArgument typeArg2, List<object> items2) =>
{
TypeArgument fieldType = new TypeArgument(
typeArg2.Type.Immutable ? LayoutType.ImmutableTypedTuple : LayoutType.TypedTuple,
typeArg2.TypeArgs);
foreach (object item in items2)
{
Result r = StreamingRowGenerator.DispatchTuple(ref writer2, null, fieldType, item);
if (r != Result.Success)
{
return r;
}
}
return Result.Success;
});
}
private static Result DispatchUDT(ref RowWriter writer, Utf8String path, TypeArgument typeArg, object value)
{
return writer.WriteScope(
path,
typeArg,
(Dictionary<Utf8String, object>)value,
(ref RowWriter writer2, TypeArgument typeArg2, Dictionary<Utf8String, object> dict) =>
{
Layout udt = writer2.Resolver.Resolve(typeArg2.TypeArgs.SchemaId);
foreach (LayoutColumn c in udt.Columns)
{
Result result = StreamingRowGenerator.LayoutCodeSwitch(ref writer2, c.Path, c.TypeArg, dict[c.Path]);
if (result != Result.Success)
{
return result;
}
}
return Result.Success;
});
}
}
}

View File

@@ -0,0 +1,260 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
namespace Microsoft.Azure.Cosmos.Serialization.HybridRowGenerator
{
using System;
using Microsoft.Azure.Cosmos.Core;
using Microsoft.Azure.Cosmos.Serialization.HybridRow;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.Layouts;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.Schemas;
public ref struct VisitRowGenerator
{
private readonly LayoutResolver resolver;
private RowBuffer row;
public VisitRowGenerator(Span<byte> span, LayoutResolver resolver)
{
this.resolver = resolver;
this.row = new RowBuffer(span, HybridRowVersion.V1, this.resolver);
}
public int Length => this.row.Length;
public Result DispatchLayout(Layout layout)
{
RowCursor scope = RowCursor.Create(ref this.row);
return this.DispatchLayout(ref scope, layout);
}
private Result LayoutCodeSwitch(
ref RowCursor scope,
LayoutColumn col = default,
TypeArgument typeArg = default)
{
if (col != null)
{
typeArg = col.TypeArg;
}
switch (typeArg.Type.LayoutCode)
{
case LayoutCode.ObjectScope:
case LayoutCode.ImmutableObjectScope:
return this.DispatchObject(ref scope, typeArg);
case LayoutCode.TypedArrayScope:
case LayoutCode.ImmutableTypedArrayScope:
return this.DispatchArray(ref scope, typeArg);
case LayoutCode.TypedSetScope:
case LayoutCode.ImmutableTypedSetScope:
return this.DispatchSet(ref scope, typeArg);
case LayoutCode.TypedMapScope:
case LayoutCode.ImmutableTypedMapScope:
return this.DispatchMap(ref scope, typeArg);
case LayoutCode.TupleScope:
case LayoutCode.ImmutableTupleScope:
case LayoutCode.TypedTupleScope:
case LayoutCode.ImmutableTypedTupleScope:
case LayoutCode.TaggedScope:
case LayoutCode.ImmutableTaggedScope:
case LayoutCode.Tagged2Scope:
case LayoutCode.ImmutableTagged2Scope:
return this.DispatchTuple(ref scope, typeArg);
case LayoutCode.NullableScope:
case LayoutCode.ImmutableNullableScope:
return this.DispatchNullable(ref scope, typeArg);
case LayoutCode.Schema:
case LayoutCode.ImmutableSchema:
return this.DispatchUDT(ref scope, typeArg);
default:
return Result.Success;
}
}
private Result DispatchObject(ref RowCursor scope, TypeArgument t)
{
Result r = t.TypeAs<LayoutObject>().ReadScope(ref this.row, ref scope, out RowCursor childScope);
if (r != Result.Success)
{
return r;
}
// TODO: support properties in an object scope.
scope.Skip(ref this.row, ref childScope);
return Result.Success;
}
private Result DispatchArray(ref RowCursor scope, TypeArgument t)
{
Contract.Assert(t.TypeArgs.Count == 1);
Result r = t.TypeAs<LayoutTypedArray>().ReadScope(ref this.row, ref scope, out RowCursor childScope);
if (r != Result.Success)
{
return r;
}
while (childScope.MoveNext(ref this.row))
{
r = this.LayoutCodeSwitch(ref childScope, null, t.TypeArgs[0]);
if (r != Result.Success)
{
return r;
}
}
scope.Skip(ref this.row, ref childScope);
return Result.Success;
}
private Result DispatchTuple(ref RowCursor scope, TypeArgument t)
{
Contract.Assert(t.TypeArgs.Count >= 2);
Result r = t.TypeAs<LayoutIndexedScope>().ReadScope(ref this.row, ref scope, out RowCursor childScope);
if (r != Result.Success)
{
return r;
}
while (childScope.MoveNext(ref this.row))
{
r = this.LayoutCodeSwitch(ref childScope, null, t.TypeArgs[childScope.Index]);
if (r != Result.Success)
{
return r;
}
}
scope.Skip(ref this.row, ref childScope);
return Result.Success;
}
private Result DispatchNullable(ref RowCursor scope, TypeArgument t)
{
Contract.Assert(t.TypeArgs.Count == 1);
Result r = t.TypeAs<LayoutNullable>().ReadScope(ref this.row, ref scope, out RowCursor childScope);
if (r != Result.Success)
{
return r;
}
if (childScope.MoveNext(ref this.row))
{
r = this.LayoutCodeSwitch(ref childScope, null, t.TypeArgs[0]);
if (r != Result.Success)
{
return r;
}
}
scope.Skip(ref this.row, ref childScope);
return Result.Success;
}
private Result DispatchSet(ref RowCursor scope, TypeArgument t)
{
Contract.Assert(t.TypeArgs.Count == 1);
Result r = t.TypeAs<LayoutTypedSet>().ReadScope(ref this.row, ref scope, out RowCursor childScope);
if (r != Result.Success)
{
return r;
}
while (childScope.MoveNext(ref this.row))
{
r = this.LayoutCodeSwitch(ref childScope, null, t.TypeArgs[0]);
if (r != Result.Success)
{
return r;
}
}
scope.Skip(ref this.row, ref childScope);
return Result.Success;
}
private Result DispatchMap(ref RowCursor scope, TypeArgument t)
{
Contract.Assert(t.TypeArgs.Count == 2);
Result r = t.TypeAs<LayoutUniqueScope>().ReadScope(ref this.row, ref scope, out RowCursor childScope);
if (r != Result.Success)
{
return r;
}
TypeArgument fieldType = t.TypeAs<LayoutUniqueScope>().FieldType(ref childScope);
while (childScope.MoveNext(ref this.row))
{
r = this.LayoutCodeSwitch(ref childScope, null, fieldType);
if (r != Result.Success)
{
return r;
}
}
scope.Skip(ref this.row, ref childScope);
return Result.Success;
}
private Result DispatchUDT(ref RowCursor scope, TypeArgument t)
{
Result r = t.TypeAs<LayoutUDT>().ReadScope(ref this.row, ref scope, out RowCursor childScope);
if (r != Result.Success)
{
return r;
}
Layout layout = this.resolver.Resolve(t.TypeArgs.SchemaId);
r = this.DispatchLayout(ref childScope, layout);
if (r != Result.Success)
{
return r;
}
scope.Skip(ref this.row, ref childScope);
return Result.Success;
}
private Result DispatchLayout(ref RowCursor scope, Layout layout)
{
// Process schematized segment.
foreach (LayoutColumn c in layout.Columns)
{
if (c.Storage == StorageKind.Sparse)
{
break;
}
Result r = this.LayoutCodeSwitch(ref scope, c);
if (r != Result.Success)
{
return r;
}
}
// Process sparse segment.
while (scope.MoveNext(ref this.row))
{
Result r = this.LayoutCodeSwitch(ref scope, null, scope.TypeArg);
if (r != Result.Success)
{
return r;
}
}
return Result.Success;
}
}
}

View File

@@ -0,0 +1,502 @@
// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------
namespace Microsoft.Azure.Cosmos.Serialization.HybridRowGenerator
{
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Azure.Cosmos.Core;
using Microsoft.Azure.Cosmos.Core.Utf8;
using Microsoft.Azure.Cosmos.Serialization.HybridRow;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.IO;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.Layouts;
using Microsoft.Azure.Cosmos.Serialization.HybridRow.Schemas;
[SuppressMessage("Microsoft.StyleCop.CSharp.OrderingRules", "SA1401", Justification = "Test types.")]
public ref struct WriteRowGenerator
{
private RowBuffer row;
public WriteRowGenerator(int capacity, Layout layout, LayoutResolver resolver)
{
this.row = new RowBuffer(capacity);
this.row.InitLayout(HybridRowVersion.V1, layout, resolver);
}
public int Length => this.row.Length;
public void Reset()
{
Layout layout = this.row.Resolver.Resolve(this.row.Header.SchemaId);
this.row.InitLayout(HybridRowVersion.V1, layout, this.row.Resolver);
}
public RowReader GetReader()
{
return new RowReader(ref this.row);
}
public Result DispatchLayout(Layout layout, Dictionary<Utf8String, object> dict)
{
RowCursor scope = RowCursor.Create(ref this.row);
return WriteRowGenerator.DispatchLayout(ref this.row, ref scope, layout, dict);
}
private static Result LayoutCodeSwitch(
ref RowBuffer row,
ref RowCursor scope,
LayoutColumn col = default,
TypeArgument typeArg = default,
object value = null)
{
if (col != null)
{
typeArg = col.TypeArg;
}
// ReSharper disable MergeConditionalExpression
// ReSharper disable SimplifyConditionalTernaryExpression
// ReSharper disable RedundantTypeSpecificationInDefaultExpression
#pragma warning disable IDE0034 // Simplify 'default' expression
switch (typeArg.Type.LayoutCode)
{
case LayoutCode.Null:
return WriteRowGenerator.Dispatch<LayoutNull, NullValue>(ref row, ref scope, col, typeArg.Type, NullValue.Default);
case LayoutCode.Boolean:
return WriteRowGenerator.Dispatch<LayoutBoolean, bool>(
ref row,
ref scope,
col,
typeArg.Type,
value == null ? default(bool) : (bool)value);
case LayoutCode.Int8:
return WriteRowGenerator.Dispatch<LayoutInt8, sbyte>(
ref row,
ref scope,
col,
typeArg.Type,
value == null ? default(sbyte) : (sbyte)value);
case LayoutCode.Int16:
return WriteRowGenerator.Dispatch<LayoutInt16, short>(
ref row,
ref scope,
col,
typeArg.Type,
value == null ? default(short) : (short)value);
case LayoutCode.Int32:
return WriteRowGenerator.Dispatch<LayoutInt32, int>(
ref row,
ref scope,
col,
typeArg.Type,
value == null ? default(int) : (int)value);
case LayoutCode.Int64:
return WriteRowGenerator.Dispatch<LayoutInt64, long>(
ref row,
ref scope,
col,
typeArg.Type,
value == null ? default(long) : (long)value);
case LayoutCode.UInt8:
return WriteRowGenerator.Dispatch<LayoutUInt8, byte>(
ref row,
ref scope,
col,
typeArg.Type,
value == null ? default(byte) : (byte)value);
case LayoutCode.UInt16:
return WriteRowGenerator.Dispatch<LayoutUInt16, ushort>(
ref row,
ref scope,
col,
typeArg.Type,
value == null ? default(ushort) : (ushort)value);
case LayoutCode.UInt32:
return WriteRowGenerator.Dispatch<LayoutUInt32, uint>(
ref row,
ref scope,
col,
typeArg.Type,
value == null ? default(uint) : (uint)value);
case LayoutCode.UInt64:
return WriteRowGenerator.Dispatch<LayoutUInt64, ulong>(
ref row,
ref scope,
col,
typeArg.Type,
value == null ? default(ulong) : (ulong)value);
case LayoutCode.VarInt:
return WriteRowGenerator.Dispatch<LayoutVarInt, long>(
ref row,
ref scope,
col,
typeArg.Type,
value == null ? default(long) : (long)value);
case LayoutCode.VarUInt:
return WriteRowGenerator.Dispatch<LayoutVarUInt, ulong>(
ref row,
ref scope,
col,
typeArg.Type,
value == null ? default(ulong) : (ulong)value);
case LayoutCode.Float32:
return WriteRowGenerator.Dispatch<LayoutFloat32, float>(
ref row,
ref scope,
col,
typeArg.Type,
value == null ? default(float) : (float)value);
case LayoutCode.Float64:
return WriteRowGenerator.Dispatch<LayoutFloat64, double>(
ref row,
ref scope,
col,
typeArg.Type,
value == null ? default(double) : (double)value);
case LayoutCode.Float128:
return WriteRowGenerator.Dispatch<LayoutFloat128, Float128>(
ref row,
ref scope,
col,
typeArg.Type,
value == null ? default(Float128) : (Float128)value);
case LayoutCode.Decimal:
return WriteRowGenerator.Dispatch<LayoutDecimal, decimal>(
ref row,
ref scope,
col,
typeArg.Type,
value == null ? default(decimal) : (decimal)value);
case LayoutCode.DateTime:
return WriteRowGenerator.Dispatch<LayoutDateTime, DateTime>(
ref row,
ref scope,
col,
typeArg.Type,
value == null ? default(DateTime) : (DateTime)value);
case LayoutCode.UnixDateTime:
return WriteRowGenerator.Dispatch<LayoutUnixDateTime, UnixDateTime>(
ref row,
ref scope,
col,
typeArg.Type,
value == null ? default(UnixDateTime) : (UnixDateTime)value);
case LayoutCode.Guid:
return WriteRowGenerator.Dispatch<LayoutGuid, Guid>(
ref row,
ref scope,
col,
typeArg.Type,
value == null ? default(Guid) : (Guid)value);
case LayoutCode.MongoDbObjectId:
return WriteRowGenerator.Dispatch<LayoutMongoDbObjectId, MongoDbObjectId>(
ref row,
ref scope,
col,
typeArg.Type,
value == null ? default(MongoDbObjectId) : (MongoDbObjectId)value);
case LayoutCode.Utf8:
return WriteRowGenerator.Dispatch(ref row, ref scope, col, (Utf8String)value);
case LayoutCode.Binary:
return WriteRowGenerator.Dispatch<LayoutBinary, byte[]>(
ref row,
ref scope,
col,
typeArg.Type,
value == null ? default(byte[]) : (byte[])value);
case LayoutCode.ObjectScope:
case LayoutCode.ImmutableObjectScope:
return WriteRowGenerator.DispatchObject(ref row, ref scope, typeArg, value);
case LayoutCode.TypedArrayScope:
case LayoutCode.ImmutableTypedArrayScope:
return WriteRowGenerator.DispatchArray(ref row, ref scope, typeArg, value);
case LayoutCode.TypedSetScope:
case LayoutCode.ImmutableTypedSetScope:
return WriteRowGenerator.DispatchSet(ref row, ref scope, typeArg, value);
case LayoutCode.TypedMapScope:
case LayoutCode.ImmutableTypedMapScope:
return WriteRowGenerator.DispatchMap(ref row, ref scope, typeArg, value);
case LayoutCode.TupleScope:
case LayoutCode.ImmutableTupleScope:
case LayoutCode.TypedTupleScope:
case LayoutCode.ImmutableTypedTupleScope:
case LayoutCode.TaggedScope:
case LayoutCode.ImmutableTaggedScope:
case LayoutCode.Tagged2Scope:
case LayoutCode.ImmutableTagged2Scope:
return WriteRowGenerator.DispatchTuple(ref row, ref scope, typeArg, value);
case LayoutCode.NullableScope:
case LayoutCode.ImmutableNullableScope:
return WriteRowGenerator.DispatchNullable(ref row, ref scope, typeArg, value);
case LayoutCode.Schema:
case LayoutCode.ImmutableSchema:
return WriteRowGenerator.DispatchUDT(ref row, ref scope, typeArg, value);
default:
Contract.Assert(false, $"Unknown type will be ignored: {typeArg.Type.LayoutCode}");
return Result.Failure;
}
// ReSharper restore SimplifyConditionalTernaryExpression
// ReSharper restore MergeConditionalExpression
// ReSharper restore RedundantTypeSpecificationInDefaultExpression
#pragma warning restore IDE0034 // Simplify 'default' expression
}
private static Result Dispatch<TLayout, TValue>(ref RowBuffer row, ref RowCursor root, LayoutColumn col, LayoutType t, TValue value)
where TLayout : LayoutType<TValue>
{
switch (col?.Storage)
{
case StorageKind.Fixed:
return t.TypeAs<TLayout>().WriteFixed(ref row, ref root, col, value);
case StorageKind.Variable:
return t.TypeAs<TLayout>().WriteVariable(ref row, ref root, col, value);
default:
return t.TypeAs<TLayout>().WriteSparse(ref row, ref root, value);
}
}
private static Result Dispatch(ref RowBuffer row, ref RowCursor root, LayoutColumn col, Utf8String value)
{
switch (col?.Storage)
{
case StorageKind.Fixed:
return LayoutType.Utf8.WriteFixed(ref row, ref root, col, value);
case StorageKind.Variable:
return LayoutType.Utf8.WriteVariable(ref row, ref root, col, value);
default:
return LayoutType.Utf8.WriteSparse(ref row, ref root, value);
}
}
private static Result DispatchObject(ref RowBuffer row, ref RowCursor scope, TypeArgument t, object value)
{
Result r = t.TypeAs<LayoutObject>().WriteScope(ref row, ref scope, t.TypeArgs, out RowCursor childScope);
if (r != Result.Success)
{
return r;
}
// TODO: support properties in an object scope.
Dictionary<Utf8String, object> dict = (Dictionary<Utf8String, object>)value;
Contract.Assert(dict.Count == 0);
scope.Skip(ref row, ref childScope);
return Result.Success;
}
private static Result DispatchArray(ref RowBuffer row, ref RowCursor scope, TypeArgument t, object value)
{
Contract.Assert(t.TypeArgs.Count == 1);
Result r = t.TypeAs<LayoutTypedArray>().WriteScope(ref row, ref scope, t.TypeArgs, out RowCursor childScope);
if (r != Result.Success)
{
return r;
}
List<object> items = (List<object>)value;
foreach (object item in items)
{
r = WriteRowGenerator.LayoutCodeSwitch(ref row, ref childScope, null, t.TypeArgs[0], item);
if (r != Result.Success)
{
return r;
}
childScope.MoveNext(ref row);
}
scope.Skip(ref row, ref childScope);
return Result.Success;
}
private static Result DispatchTuple(ref RowBuffer row, ref RowCursor scope, TypeArgument t, object value)
{
Contract.Assert(t.TypeArgs.Count >= 2);
return t.TypeAs<LayoutIndexedScope>()
.WriteScope(
ref row,
ref scope,
t.TypeArgs,
(List<object>)value,
(ref RowBuffer row2, ref RowCursor childScope, List<object> items) =>
{
Contract.Assert(items.Count == childScope.ScopeTypeArgs.Count);
for (int i = 0; i < items.Count; i++)
{
Result r = WriteRowGenerator.LayoutCodeSwitch(ref row2, ref childScope, null, childScope.ScopeTypeArgs[i], items[i]);
if (r != Result.Success)
{
return r;
}
childScope.MoveNext(ref row2);
}
return Result.Success;
});
}
private static Result DispatchNullable(ref RowBuffer row, ref RowCursor scope, TypeArgument t, object value)
{
Contract.Assert(t.TypeArgs.Count == 1);
bool hasValue = value != null;
Result r = t.TypeAs<LayoutNullable>().WriteScope(ref row, ref scope, t.TypeArgs, hasValue, out RowCursor childScope);
if (r != Result.Success)
{
return r;
}
if (hasValue)
{
r = WriteRowGenerator.LayoutCodeSwitch(ref row, ref childScope, null, t.TypeArgs[0], value);
if (r != Result.Success)
{
return r;
}
childScope.MoveNext(ref row);
}
scope.Skip(ref row, ref childScope);
return Result.Success;
}
private static Result DispatchSet(ref RowBuffer row, ref RowCursor scope, TypeArgument t, object value)
{
Contract.Assert(t.TypeArgs.Count == 1);
return t.TypeAs<LayoutTypedSet>()
.WriteScope(
ref row,
ref scope,
t.TypeArgs,
(List<object>)value,
(ref RowBuffer row2, ref RowCursor childScope, List<object> items) =>
{
foreach (object item in items)
{
Result r = WriteRowGenerator.LayoutCodeSwitch(ref row2, ref childScope, null, childScope.ScopeTypeArgs[0], item);
if (r != Result.Success)
{
return r;
}
childScope.MoveNext(ref row2);
}
return Result.Success;
});
}
private static Result DispatchMap(ref RowBuffer row, ref RowCursor scope, TypeArgument t, object value)
{
Contract.Assert(t.TypeArgs.Count == 2);
return t.TypeAs<LayoutUniqueScope>()
.WriteScope(
ref row,
ref scope,
t.TypeArgs,
(List<object>)value,
(ref RowBuffer row2, ref RowCursor childScope, List<object> pairs) =>
{
TypeArgument fieldType = childScope.ScopeType.TypeAs<LayoutUniqueScope>().FieldType(ref childScope);
foreach (object elm in pairs)
{
Result r = WriteRowGenerator.LayoutCodeSwitch(ref row2, ref childScope, null, fieldType, elm);
if (r != Result.Success)
{
return r;
}
childScope.MoveNext(ref row2);
}
return Result.Success;
});
}
private static Result DispatchUDT(ref RowBuffer row, ref RowCursor scope, TypeArgument t, object value)
{
Result r = t.TypeAs<LayoutUDT>().WriteScope(ref row, ref scope, t.TypeArgs, out RowCursor childScope);
if (r != Result.Success)
{
return r;
}
Dictionary<Utf8String, object> dict = (Dictionary<Utf8String, object>)value;
Layout layout = row.Resolver.Resolve(t.TypeArgs.SchemaId);
r = WriteRowGenerator.DispatchLayout(ref row, ref childScope, layout, dict);
if (r != Result.Success)
{
return r;
}
scope.Skip(ref row, ref childScope);
return Result.Success;
}
private static Result DispatchLayout(ref RowBuffer row, ref RowCursor scope, Layout layout, Dictionary<Utf8String, object> dict)
{
foreach (LayoutColumn c in layout.Columns)
{
if (c.Storage != StorageKind.Sparse)
{
Result r = WriteRowGenerator.LayoutCodeSwitch(ref row, ref scope, c, value: dict[c.Path]);
if (r != Result.Success)
{
return r;
}
}
else
{
scope.Find(ref row, c.Path);
Result r = WriteRowGenerator.LayoutCodeSwitch(ref row, ref scope, null, c.TypeArg, dict[c.Path]);
if (r != Result.Success)
{
return r;
}
}
}
return Result.Success;
}
}
}