diff --git a/FdbShell/Commands/BasicCommands.cs b/FdbShell/Commands/BasicCommands.cs index d09be5f24..1243225f4 100644 --- a/FdbShell/Commands/BasicCommands.cs +++ b/FdbShell/Commands/BasicCommands.cs @@ -220,9 +220,9 @@ public static async Task Count(string[] path, ITuple extras, IFdbDatabase db, Te var copy = KeySubspace.Copy(folder); log.WriteLine("# Counting keys under {0} ...", FdbKey.Dump(copy.GetPrefix())); - var progress = new Progress>((state) => + var progress = new Progress<(long Count, Slice Current)>((state) => { - log.Write("\r# Found {0:N0} keys...", state.Item1); + log.Write("\r# Found {0:N0} keys...", state.Count); }); long count = await Fdb.System.EstimateCountAsync(db, copy.ToRange(), progress, ct); diff --git a/FdbTop/Program.cs b/FdbTop/Program.cs index e25d437df..c56a144bc 100644 --- a/FdbTop/Program.cs +++ b/FdbTop/Program.cs @@ -819,7 +819,7 @@ private static void ShowProcessesScreen(FdbSystemStatus status, HistoryMetric cu { foreach(var role in proc.Roles) { - map.Add(role.Value); + map.Add(role.Role); } } @@ -860,7 +860,7 @@ private static void ShowProcessesScreen(FdbSystemStatus status, HistoryMetric cu map = new RoleMap(); foreach (var role in proc.Roles) { - map.Add(role.Value); + map.Add(role.Role); } Console.ForegroundColor = ConsoleColor.DarkGray; WriteAt(1, y, diff --git a/FoundationDB.Client/Fdb.Bulk.cs b/FoundationDB.Client/Fdb.Bulk.cs index be530b06e..64878fc00 100644 --- a/FoundationDB.Client/Fdb.Bulk.cs +++ b/FoundationDB.Client/Fdb.Bulk.cs @@ -31,6 +31,7 @@ namespace FoundationDB.Client using System; using System.Collections.Generic; using System.Diagnostics; + using System.Linq; using System.Threading; using System.Threading.Tasks; using Doxense.Diagnostics.Contracts; @@ -98,7 +99,7 @@ public static Task WriteAsync([NotNull] IFdbDatabase db, [NotNull] IEnumer return RunWriteOperationAsync( db, - data, + data.Select(x => (x.Key, x.Value)), new WriteOptions(), ct ); @@ -118,6 +119,49 @@ public static Task WriteAsync([NotNull] IFdbDatabase db, [NotNull] IEnumer ct.ThrowIfCancellationRequested(); + return RunWriteOperationAsync( + db, + data.Select(x => (x.Key, x.Value)), + options ?? new WriteOptions(), + ct + ); + } + + /// Writes a potentially large sequence of key/value pairs into the database, by using as many transactions as necessary, and automatically scaling the size of each batch. + /// Database used for the operation + /// Sequence of key/value pairs + /// Token used to cancel the operation + /// Total number of values inserted in the database + /// In case of a non-retryable error, some of the keys may remain in the database. Other transactions running at the same time may observe only a fraction of the keys until the operation completes. + public static Task WriteAsync([NotNull] IFdbDatabase db, [NotNull] IEnumerable<(Slice Key, Slice Value)> data, CancellationToken ct) + { + if (db == null) throw new ArgumentNullException(nameof(db)); + if (data == null) throw new ArgumentNullException(nameof(data)); + + ct.ThrowIfCancellationRequested(); + + return RunWriteOperationAsync( + db, + data, + new WriteOptions(), + ct + ); + } + + /// Writes a potentially large sequence of key/value pairs into the database, by using as many transactions as necessary, and automatically scaling the size of each batch. + /// Database used for the operation + /// Sequence of key/value pairs + /// Custom options used to configure the behaviour of the operation + /// Token used to cancel the operation + /// Total number of values inserted in the database + /// In case of a non-retryable error, some of the keys may remain in the database. Other transactions running at the same time may observe only a fraction of the keys until the operation completes. + public static Task WriteAsync([NotNull] IFdbDatabase db, [NotNull] IEnumerable<(Slice Key, Slice Value)> data, WriteOptions options, CancellationToken ct) + { + if (db == null) throw new ArgumentNullException(nameof(db)); + if (data == null) throw new ArgumentNullException(nameof(data)); + + ct.ThrowIfCancellationRequested(); + return RunWriteOperationAsync( db, data, @@ -126,7 +170,7 @@ public static Task WriteAsync([NotNull] IFdbDatabase db, [NotNull] IEnumer ); } - internal static async Task RunWriteOperationAsync([NotNull] IFdbDatabase db, [NotNull] IEnumerable> data, WriteOptions options, CancellationToken ct) + internal static async Task RunWriteOperationAsync([NotNull] IFdbDatabase db, [NotNull] IEnumerable<(Slice Key, Slice Value)> data, WriteOptions options, CancellationToken ct) { Contract.Requires(db != null && data != null && options != null); @@ -145,7 +189,7 @@ internal static async Task RunWriteOperationAsync([NotNull] IFdbDatabase d throw new NotImplementedException("Multiple concurrent transactions are not yet supported"); } - var chunk = new List>(); + var chunk = new List<(Slice Key, Slice Value)>(); long items = 0; using (var iterator = data.GetEnumerator()) @@ -960,7 +1004,6 @@ public static Task ForEachAsync( /// Lambda function that will be called after the last batch, and will be passed the last known state. /// Token used to cancel the operation /// Task that completes when all the elements of have been processed, a non-retryable error occurs, or is triggered - [Obsolete("EXPERIMENTAL: do not use yet!")] public static Task ForEachAsync( [NotNull] IFdbDatabase db, [NotNull] IEnumerable source, diff --git a/FoundationDB.Client/Fdb.System.cs b/FoundationDB.Client/Fdb.System.cs index 9e38daee9..bacb2e3d4 100644 --- a/FoundationDB.Client/Fdb.System.cs +++ b/FoundationDB.Client/Fdb.System.cs @@ -408,7 +408,7 @@ public static Task EstimateCountAsync([NotNull] IFdbDatabase db, KeyRange /// Token used to cancel the operation /// Number of keys k such that range.Begin <= k > range.End /// If the range contains a large of number keys, the operation may need more than one transaction to complete, meaning that the number will not be transactionally accurate. - public static Task EstimateCountAsync([NotNull] IFdbDatabase db, KeyRange range, IProgress> onProgress, CancellationToken ct) + public static Task EstimateCountAsync([NotNull] IFdbDatabase db, KeyRange range, IProgress<(long Count, Slice Current)> onProgress, CancellationToken ct) { return EstimateCountAsync(db, range.Begin, range.End, onProgress, ct); //REVIEW: BUGBUG: REFACTORING: deal with null value for End! @@ -422,7 +422,7 @@ public static Task EstimateCountAsync([NotNull] IFdbDatabase db, KeyRange /// Token used to cancel the operation /// Number of keys k such that <= k > /// If the range contains a large of number keys, the operation may need more than one transaction to complete, meaning that the number will not be transactionally accurate. - public static async Task EstimateCountAsync([NotNull] IFdbDatabase db, Slice beginInclusive, Slice endExclusive, IProgress> onProgress, CancellationToken ct) + public static async Task EstimateCountAsync([NotNull] IFdbDatabase db, Slice beginInclusive, Slice endExclusive, IProgress<(long Count, Slice Current)> onProgress, CancellationToken ct) { const int INIT_WINDOW_SIZE = 1 << 8; // start at 256 //1024 const int MAX_WINDOW_SIZE = 1 << 13; // never use more than 4096 @@ -538,7 +538,7 @@ public static async Task EstimateCountAsync([NotNull] IFdbDatabase db, Sli .ConfigureAwait(false); counter += n; - if (onProgress != null) onProgress.Report(STuple.Create(counter, end)); + onProgress?.Report((counter, end)); #if TRACE_COUNTING ++iter; #endif @@ -552,7 +552,7 @@ public static async Task EstimateCountAsync([NotNull] IFdbDatabase db, Sli // the range is not finished, advance the cursor counter += windowSize; cursor = next; - if (onProgress != null) onProgress.Report(STuple.Create(counter, cursor)); + onProgress?.Report((counter, cursor)); if (!last) { // double the size of the window if we are not in the last segment diff --git a/FoundationDB.Client/FdbDatabase.cs b/FoundationDB.Client/FdbDatabase.cs index ac2da2107..418376da4 100644 --- a/FoundationDB.Client/FdbDatabase.cs +++ b/FoundationDB.Client/FdbDatabase.cs @@ -456,8 +456,8 @@ internal void ChangeRoot(IKeySubspace subspace, IFdbDirectory directory, bool re lock (this)//TODO: don't use this for locking { m_readOnly = readOnly; - m_globalSpace = KeySubspace.Copy(subspace).Using(TypeSystem.Tuples); - m_globalSpaceCopy = KeySubspace.Copy(subspace).Using(TypeSystem.Tuples); // keep another copy + m_globalSpace = KeySubspace.Copy(subspace, TypeSystem.Tuples); + m_globalSpaceCopy = KeySubspace.Copy(subspace, TypeSystem.Tuples); // keep another copy m_directory = directory == null ? null : new FdbDatabasePartition(this, directory); } } diff --git a/FoundationDB.Client/FdbDatabaseExtensions.cs b/FoundationDB.Client/FdbDatabaseExtensions.cs index d94f642a4..9e1177a04 100644 --- a/FoundationDB.Client/FdbDatabaseExtensions.cs +++ b/FoundationDB.Client/FdbDatabaseExtensions.cs @@ -318,18 +318,18 @@ public static Task SetAsync([NotNull] this IFdbRetryable db, Slice key, Slice va return db.WriteAsync((tr) => tr.Set(key, value), ct); } - /// Set the values of a list of keys in the database, using a dedicated transaction. + /// Set the values of a sequence of keys in the database, using a dedicated transaction. /// Database instance /// /// Use this method only if you intend to perform a single operation inside your execution context (ex: HTTP request). /// If you need to combine multiple read or write operations, consider using on of the multiple or overrides. /// - public static Task SetValuesAsync([NotNull] this IFdbRetryable db, KeyValuePair[] keyValuePairs, CancellationToken ct) + public static Task SetValuesAsync([NotNull] this IFdbRetryable db, IEnumerable> items, CancellationToken ct) { Contract.NotNull(db, nameof(db)); return db.WriteAsync((tr) => { - foreach (var kv in keyValuePairs) + foreach (var kv in items) { tr.Set(kv.Key, kv.Value); } @@ -342,12 +342,12 @@ public static Task SetValuesAsync([NotNull] this IFdbRetryable db, KeyValuePair< /// Use this method only if you intend to perform a single operation inside your execution context (ex: HTTP request). /// If you need to combine multiple read or write operations, consider using on of the multiple or overrides. /// - public static Task SetValuesAsync([NotNull] this IFdbRetryable db, IEnumerable> keyValuePairs, CancellationToken ct) + public static Task SetValuesAsync([NotNull] this IFdbRetryable db, IEnumerable<(Slice Key, Slice Value)> items, CancellationToken ct) { Contract.NotNull(db, nameof(db)); return db.WriteAsync((tr) => { - foreach (var kv in keyValuePairs) + foreach (var kv in items) { tr.Set(kv.Key, kv.Value); } diff --git a/FoundationDB.Client/FoundationDB.Client.csproj b/FoundationDB.Client/FoundationDB.Client.csproj index 01a1a8731..3c9e4e746 100644 --- a/FoundationDB.Client/FoundationDB.Client.csproj +++ b/FoundationDB.Client/FoundationDB.Client.csproj @@ -32,4 +32,8 @@ latest + + + + diff --git a/FoundationDB.Client/Layers/Directories/FdbDirectoryLayer.cs b/FoundationDB.Client/Layers/Directories/FdbDirectoryLayer.cs index 3f8ec5628..73a7c089a 100644 --- a/FoundationDB.Client/Layers/Directories/FdbDirectoryLayer.cs +++ b/FoundationDB.Client/Layers/Directories/FdbDirectoryLayer.cs @@ -153,20 +153,14 @@ internal FdbDirectoryLayer(IDynamicKeySubspace nodeSubspace, IDynamicKeySubspace } } - /// Create an instance of the default Directory Layer - [NotNull] - public static FdbDirectoryLayer Create() - { - return Create(Slice.Empty); - } - /// Create an instance of a Directory Layer located under a specific prefix and path /// Prefix for the content. The nodes will be stored under + <FE> /// Optional path, if the Directory Layer is not located at the root of the database. + /// Optional key encoding scheme. If not specified, will use the type system by default. [NotNull] - public static FdbDirectoryLayer Create(Slice prefix, IEnumerable path = null) + public static FdbDirectoryLayer Create(Slice prefix, IEnumerable path = null, IKeyEncoding encoding = null) { - var subspace = KeySubspace.FromKey(prefix).Using(TypeSystem.Tuples); + var subspace = KeySubspace.CreateDynamic(prefix, encoding ?? TypeSystem.Tuples); var location = path != null ? ParsePath(path) : STuple.Empty; return new FdbDirectoryLayer(subspace.Partition[FdbKey.Directory], subspace, location); } @@ -174,13 +168,14 @@ public static FdbDirectoryLayer Create(Slice prefix, IEnumerable path = /// Create an instance of a Directory Layer located under a specific subspace and path /// Subspace for the content. The nodes will be stored under .Key + <FE> /// Optional path, if the Directory Layer is not located at the root of the database. + /// Optional key encoding scheme. If not specified, will use the type system by default. [NotNull] - public static FdbDirectoryLayer Create(IKeySubspace subspace, IEnumerable path = null) + public static FdbDirectoryLayer Create(IKeySubspace subspace, IEnumerable path = null, IKeyEncoding encoding = null) { if (subspace == null) throw new ArgumentNullException(nameof(subspace)); var location = path != null ? ParsePath(path) : STuple.Empty; - var space = subspace.Using(TypeSystem.Tuples); + var space = subspace.Using(encoding ?? TypeSystem.Tuples); return new FdbDirectoryLayer(space.Partition[FdbKey.Directory], space, location); } @@ -539,14 +534,6 @@ internal static ITuple ParsePath(IEnumerable path, string argName = null return STuple.FromArray(pathCopy); } - [NotNull] - internal static ITuple ParsePath([NotNull] string name, string argName = null) - { - if (name == null) throw new ArgumentNullException(argName ?? "name"); - - return STuple.Create(name); - } - [NotNull] internal static ITuple VerifyPath([NotNull] ITuple path, string argName = null) { diff --git a/FoundationDB.Client/Layers/Tuples/Encoding/TupleCodec`1.cs b/FoundationDB.Client/Layers/Tuples/Encoding/TupleCodec`1.cs index c92f0373a..af34a14ec 100644 --- a/FoundationDB.Client/Layers/Tuples/Encoding/TupleCodec`1.cs +++ b/FoundationDB.Client/Layers/Tuples/Encoding/TupleCodec`1.cs @@ -55,7 +55,7 @@ public TupleCodec(T missingValue) public override Slice EncodeOrdered(T value) { - return TupleEncoder.EncodeKey(value); + return TupleEncoder.EncodeKey(default(Slice), value); } public override void EncodeOrderedSelfTerm(ref SliceWriter output, T value) diff --git a/FoundationDB.Client/Layers/Tuples/Encoding/TupleEncoder.cs b/FoundationDB.Client/Layers/Tuples/Encoding/TupleEncoder.cs index 21aebfa00..237bae37f 100644 --- a/FoundationDB.Client/Layers/Tuples/Encoding/TupleEncoder.cs +++ b/FoundationDB.Client/Layers/Tuples/Encoding/TupleEncoder.cs @@ -83,7 +83,6 @@ public static Slice Pack([CanBeNull] TTuple tuple) where TTuple : ITuple { if (tuple == null) return Slice.Nil; - //TODO: maybe optimize for Count==0 => Empty ? (calling .Count may not be fast for all tuples...) var writer = new TupleWriter(); WriteTo(ref writer, tuple); return writer.ToSlice(); @@ -258,7 +257,7 @@ public static Slice[] Pack(Slice prefix, [NotNull] IEnumerable /// Efficiently concatenate a prefix with the packed representation of a 1-tuple [Pure] - public static Slice EncodePrefixedKey(Slice prefix, T value) + public static Slice EncodeKey(Slice prefix, T1 value) { var writer = new TupleWriter(); writer.Output.WriteBytes(prefix); @@ -268,7 +267,7 @@ public static Slice EncodePrefixedKey(Slice prefix, T value) /// Efficiently concatenate a prefix with the packed representation of a 2-tuple [Pure] - public static Slice EncodePrefixedKey(Slice prefix, T1 value1, T2 value2) + public static Slice EncodeKey(Slice prefix, T1 value1, T2 value2) { var writer = new TupleWriter(); writer.Output.WriteBytes(prefix); @@ -277,8 +276,19 @@ public static Slice EncodePrefixedKey(Slice prefix, T1 value1, T2 value2 return writer.ToSlice(); } + /// Efficiently concatenate a prefix with the packed representation of a 2-tuple + [Pure] + public static Slice Pack(Slice prefix, ref (T1, T2) items) + { + var writer = new TupleWriter(); + writer.Output.WriteBytes(prefix); + TuplePackers.SerializeTo(ref writer, items.Item1); + TuplePackers.SerializeTo(ref writer, items.Item2); + return writer.ToSlice(); + } + /// Efficiently concatenate a prefix with the packed representation of a 3-tuple - public static Slice EncodePrefixedKey(Slice prefix, T1 value1, T2 value2, T3 value3) + public static Slice EncodeKey(Slice prefix, T1 value1, T2 value2, T3 value3) { var writer = new TupleWriter(); writer.Output.WriteBytes(prefix); @@ -288,8 +298,19 @@ public static Slice EncodePrefixedKey(Slice prefix, T1 value1, T2 va return writer.ToSlice(); } + /// Efficiently concatenate a prefix with the packed representation of a 3-tuple + public static Slice Pack(Slice prefix, ref (T1, T2, T3) items) + { + var writer = new TupleWriter(); + writer.Output.WriteBytes(prefix); + TuplePackers.SerializeTo(ref writer, items.Item1); + TuplePackers.SerializeTo(ref writer, items.Item2); + TuplePackers.SerializeTo(ref writer, items.Item3); + return writer.ToSlice(); + } + /// Efficiently concatenate a prefix with the packed representation of a 4-tuple - public static Slice EncodePrefixedKey(Slice prefix, T1 value1, T2 value2, T3 value3, T4 value4) + public static Slice EncodeKey(Slice prefix, T1 value1, T2 value2, T3 value3, T4 value4) { var writer = new TupleWriter(); writer.Output.WriteBytes(prefix); @@ -300,8 +321,20 @@ public static Slice EncodePrefixedKey(Slice prefix, T1 value1, T return writer.ToSlice(); } + /// Efficiently concatenate a prefix with the packed representation of a 4-tuple + public static Slice Pack(Slice prefix, ref (T1, T2, T3, T4) items) + { + var writer = new TupleWriter(); + writer.Output.WriteBytes(prefix); + TuplePackers.SerializeTo(ref writer, items.Item1); + TuplePackers.SerializeTo(ref writer, items.Item2); + TuplePackers.SerializeTo(ref writer, items.Item3); + TuplePackers.SerializeTo(ref writer, items.Item4); + return writer.ToSlice(); + } + /// Efficiently concatenate a prefix with the packed representation of a 5-tuple - public static Slice EncodePrefixedKey(Slice prefix, T1 value1, T2 value2, T3 value3, T4 value4, T5 value5) + public static Slice EncodeKey(Slice prefix, T1 value1, T2 value2, T3 value3, T4 value4, T5 value5) { var writer = new TupleWriter(); writer.Output.WriteBytes(prefix); @@ -313,8 +346,21 @@ public static Slice EncodePrefixedKey(Slice prefix, T1 value return writer.ToSlice(); } + /// Efficiently concatenate a prefix with the packed representation of a 5-tuple + public static Slice Pack(Slice prefix, ref (T1, T2, T3, T4, T5) items) + { + var writer = new TupleWriter(); + writer.Output.WriteBytes(prefix); + TuplePackers.SerializeTo(ref writer, items.Item1); + TuplePackers.SerializeTo(ref writer, items.Item2); + TuplePackers.SerializeTo(ref writer, items.Item3); + TuplePackers.SerializeTo(ref writer, items.Item4); + TuplePackers.SerializeTo(ref writer, items.Item5); + return writer.ToSlice(); + } + /// Efficiently concatenate a prefix with the packed representation of a 6-tuple - public static Slice EncodePrefixedKey(Slice prefix, T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6) + public static Slice EncodeKey(Slice prefix, T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6) { var writer = new TupleWriter(); writer.Output.WriteBytes(prefix); @@ -327,8 +373,22 @@ public static Slice EncodePrefixedKey(Slice prefix, T1 v return writer.ToSlice(); } + /// Efficiently concatenate a prefix with the packed representation of a 6-tuple + public static Slice Pack(Slice prefix, ref (T1, T2, T3, T4, T5, T6) items) + { + var writer = new TupleWriter(); + writer.Output.WriteBytes(prefix); + TuplePackers.SerializeTo(ref writer, items.Item1); + TuplePackers.SerializeTo(ref writer, items.Item2); + TuplePackers.SerializeTo(ref writer, items.Item3); + TuplePackers.SerializeTo(ref writer, items.Item4); + TuplePackers.SerializeTo(ref writer, items.Item5); + TuplePackers.SerializeTo(ref writer, items.Item6); + return writer.ToSlice(); + } + /// Efficiently concatenate a prefix with the packed representation of a 7-tuple - public static Slice EncodePrefixedKey(Slice prefix, T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7) + public static Slice EncodeKey(Slice prefix, T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7) { var writer = new TupleWriter(); writer.Output.WriteBytes(prefix); @@ -342,8 +402,23 @@ public static Slice EncodePrefixedKey(Slice prefix, return writer.ToSlice(); } + /// Efficiently concatenate a prefix with the packed representation of a 7-tuple + public static Slice Pack(Slice prefix, ref (T1, T2, T3, T4, T5, T6, T7) items) + { + var writer = new TupleWriter(); + writer.Output.WriteBytes(prefix); + TuplePackers.SerializeTo(ref writer, items.Item1); + TuplePackers.SerializeTo(ref writer, items.Item2); + TuplePackers.SerializeTo(ref writer, items.Item3); + TuplePackers.SerializeTo(ref writer, items.Item4); + TuplePackers.SerializeTo(ref writer, items.Item5); + TuplePackers.SerializeTo(ref writer, items.Item6); + TuplePackers.SerializeTo(ref writer, items.Item7); + return writer.ToSlice(); + } + /// Efficiently concatenate a prefix with the packed representation of a 8-tuple - public static Slice EncodePrefixedKey(Slice prefix, T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8) + public static Slice EncodeKey(Slice prefix, T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8) { var writer = new TupleWriter(); writer.Output.WriteBytes(prefix); @@ -358,6 +433,22 @@ public static Slice EncodePrefixedKey(Slice pref return writer.ToSlice(); } + /// Efficiently concatenate a prefix with the packed representation of a 8-tuple + public static Slice Pack(Slice prefix, ref (T1, T2, T3, T4, T5, T6, T7, T8) items) + { + var writer = new TupleWriter(); + writer.Output.WriteBytes(prefix); + TuplePackers.SerializeTo(ref writer, items.Item1); + TuplePackers.SerializeTo(ref writer, items.Item2); + TuplePackers.SerializeTo(ref writer, items.Item3); + TuplePackers.SerializeTo(ref writer, items.Item4); + TuplePackers.SerializeTo(ref writer, items.Item5); + TuplePackers.SerializeTo(ref writer, items.Item6); + TuplePackers.SerializeTo(ref writer, items.Item7); + TuplePackers.SerializeTo(ref writer, items.Item8); + return writer.ToSlice(); + } + // EncodeKey... //REVIEW: do we really ned "Key" in the name? @@ -420,105 +511,6 @@ public static Slice Pack(Slice prefix, ref STuplePack a 1-tuple directly into a slice - public static Slice EncodeKey(T1 item1) - { - var writer = new TupleWriter(); - TuplePackers.SerializeTo(ref writer, item1); - return writer.ToSlice(); - } - - /// Pack a 2-tuple directly into a slice - public static Slice EncodeKey(T1 item1, T2 item2) - { - var writer = new TupleWriter(); - TuplePackers.SerializeTo(ref writer, item1); - TuplePackers.SerializeTo(ref writer, item2); - return writer.ToSlice(); - } - - /// Pack a 3-tuple directly into a slice - public static Slice EncodeKey(T1 item1, T2 item2, T3 item3) - { - var writer = new TupleWriter(); - TuplePackers.SerializeTo(ref writer, item1); - TuplePackers.SerializeTo(ref writer, item2); - TuplePackers.SerializeTo(ref writer, item3); - return writer.ToSlice(); - } - - /// Pack a 4-tuple directly into a slice - public static Slice EncodeKey(T1 item1, T2 item2, T3 item3, T4 item4) - { - var writer = new TupleWriter(); - TuplePackers.SerializeTo(ref writer, item1); - TuplePackers.SerializeTo(ref writer, item2); - TuplePackers.SerializeTo(ref writer, item3); - TuplePackers.SerializeTo(ref writer, item4); - return writer.ToSlice(); - } - - /// Pack a 5-tuple directly into a slice - public static Slice EncodeKey(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5) - { - var writer = new TupleWriter(); - TuplePackers.SerializeTo(ref writer, item1); - TuplePackers.SerializeTo(ref writer, item2); - TuplePackers.SerializeTo(ref writer, item3); - TuplePackers.SerializeTo(ref writer, item4); - TuplePackers.SerializeTo(ref writer, item5); - return writer.ToSlice(); - } - - /// Pack a 6-tuple directly into a slice - public static Slice EncodeKey(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6) - { - var writer = new TupleWriter(); - TuplePackers.SerializeTo(ref writer, item1); - TuplePackers.SerializeTo(ref writer, item2); - TuplePackers.SerializeTo(ref writer, item3); - TuplePackers.SerializeTo(ref writer, item4); - TuplePackers.SerializeTo(ref writer, item5); - TuplePackers.SerializeTo(ref writer, item6); - return writer.Output.ToSlice(); - } - - /// Pack a 6-tuple directly into a slice - public static Slice EncodeKey(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7) - { - var writer = new TupleWriter(); - TuplePackers.SerializeTo(ref writer, item1); - TuplePackers.SerializeTo(ref writer, item2); - TuplePackers.SerializeTo(ref writer, item3); - TuplePackers.SerializeTo(ref writer, item4); - TuplePackers.SerializeTo(ref writer, item5); - TuplePackers.SerializeTo(ref writer, item6); - TuplePackers.SerializeTo(ref writer, item7); - return writer.ToSlice(); - } - - /// Pack a 6-tuple directly into a slice - public static Slice EncodeKey(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8) - { - var writer = new TupleWriter(); - TuplePackers.SerializeTo(ref writer, item1); - TuplePackers.SerializeTo(ref writer, item2); - TuplePackers.SerializeTo(ref writer, item3); - TuplePackers.SerializeTo(ref writer, item4); - TuplePackers.SerializeTo(ref writer, item5); - TuplePackers.SerializeTo(ref writer, item6); - TuplePackers.SerializeTo(ref writer, item7); - TuplePackers.SerializeTo(ref writer, item8); - return writer.ToSlice(); - } - - [NotNull] - public static Slice[] EncodeKeys([NotNull] IEnumerable keys) - { - var empty = default(Slice); - return EncodePrefixedKeys(empty, keys); - } - /// Pack a 1-tuple directly into a slice public static void WriteKeysTo(ref SliceWriter writer, T1 item1) { @@ -617,12 +609,12 @@ public static void WriteKeysTo(ref SliceWriter w /// Sequence of keys to pack /// Array of slices (for all keys) that share the same underlying buffer [NotNull] - public static Slice[] EncodePrefixedKeys(Slice prefix, [NotNull] IEnumerable keys) + public static Slice[] EncodeKeys(Slice prefix, [NotNull] IEnumerable keys) { Contract.NotNull(keys, nameof(keys)); // use optimized version for arrays - if (keys is T[] array) return EncodePrefixedKeys(prefix, array); + if (keys is T[] array) return EncodeKeys(prefix, array); var next = new List((keys as ICollection)?.Count ?? 0); var writer = new TupleWriter(); @@ -646,7 +638,7 @@ public static Slice[] EncodePrefixedKeys(Slice prefix, [NotNull] IEnumerable< public static Slice[] EncodeKeys([NotNull] params T[] keys) { var empty = default(Slice); - return EncodePrefixedKeys(empty, keys); + return EncodeKeys(empty, keys); } /// Merge an array of keys with a same prefix, all sharing the same buffer @@ -655,7 +647,7 @@ public static Slice[] EncodeKeys([NotNull] params T[] keys) /// Sequence of keys to pack /// Array of slices (for all keys) that share the same underlying buffer [NotNull] - public static Slice[] EncodePrefixedKeys(Slice prefix, [NotNull] params T[] keys) + public static Slice[] EncodeKeys(Slice prefix, [NotNull] params T[] keys) { Contract.NotNull(keys, nameof(keys)); @@ -686,7 +678,7 @@ public static Slice[] EncodePrefixedKeys(Slice prefix, [NotNull] params T[] k public static Slice[] EncodeKeys([NotNull] TElement[] elements, [NotNull] Func selector) { var empty = default(Slice); - return EncodePrefixedKeys(empty, elements, selector); + return EncodeKeys(empty, elements, selector); } /// Merge an array of elements with a same prefix, all sharing the same buffer @@ -697,7 +689,7 @@ public static Slice[] EncodeKeys([NotNull] TElement[] elements, /// Lambda that extract the key from each element /// Array of slices (for all keys) that share the same underlying buffer [NotNull] - public static Slice[] EncodePrefixedKeys(Slice prefix, [NotNull] TElement[] elements, [NotNull] Func selector) + public static Slice[] EncodeKeys(Slice prefix, [NotNull] TElement[] elements, [NotNull] Func selector) { Contract.NotNull(elements, nameof(elements)); Contract.NotNull(selector, nameof(selector)); @@ -726,12 +718,12 @@ public static Slice[] EncodePrefixedKeys(Slice prefix, [NotNull] /// Sequence of keys to pack /// Array of slices (for all keys) that share the same underlying buffer [NotNull] - public static Slice[] EncodePrefixedKeys([NotNull] TTuple prefix, [NotNull] IEnumerable keys) + public static Slice[] EncodeKeys([NotNull] TTuple prefix, [NotNull] IEnumerable keys) where TTuple : ITuple { Contract.NotNullAllowStructs(prefix, nameof(prefix)); var head = Pack(prefix); - return EncodePrefixedKeys(head, keys); + return EncodeKeys(head, keys); } /// Pack a sequence of keys with a same prefix, all sharing the same buffer @@ -741,13 +733,13 @@ public static Slice[] EncodePrefixedKeys([NotNull] TTuple prefix, [N /// Sequence of keys to pack /// Array of slices (for all keys) that share the same underlying buffer [NotNull] - public static Slice[] EncodePrefixedKeys([NotNull] TTuple prefix, [NotNull] params T1[] keys) + public static Slice[] EncodeKeys([NotNull] TTuple prefix, [NotNull] params T1[] keys) where TTuple : ITuple { Contract.NotNullAllowStructs(prefix, nameof(prefix)); var head = Pack(prefix); - return EncodePrefixedKeys(head, keys); + return EncodeKeys(head, keys); } #endregion diff --git a/FoundationDB.Client/Layers/Tuples/Encoding/TupleKeyEncoder.cs b/FoundationDB.Client/Layers/Tuples/Encoding/TupleKeyEncoder.cs index 1b01cbe91..36eb31a0b 100644 --- a/FoundationDB.Client/Layers/Tuples/Encoding/TupleKeyEncoder.cs +++ b/FoundationDB.Client/Layers/Tuples/Encoding/TupleKeyEncoder.cs @@ -43,10 +43,7 @@ public sealed class TupleKeyEncoder : IDynamicKeyEncoder private TupleKeyEncoder() { } - public IKeyEncoding Encoding - { - get { return TypeSystem.Tuples; } - } + public IKeyEncoding Encoding => TypeSystem.Tuples; public void PackKey(ref SliceWriter writer, TTuple items) where TTuple : ITuple @@ -190,50 +187,49 @@ public KeyRange ToRange(Slice prefix) return TuPack.ToRange(prefix); } - public KeyRange ToRange(Slice prefix, ITuple items) { - return TuPack.ToRange(prefix, items); + return TuPack.ToPrefixedKeyRange(prefix, items); } public KeyRange ToKeyRange(Slice prefix, T1 item1) { - return TuPack.ToRange(prefix, STuple.Create(item1)); + return TuPack.ToPrefixedKeyRange(prefix, item1); } public KeyRange ToKeyRange(Slice prefix, T1 item1, T2 item2) { - return TuPack.ToRange(prefix, STuple.Create(item1, item2)); + return TuPack.ToPrefixedKeyRange(prefix, item1, item2); } public KeyRange ToKeyRange(Slice prefix, T1 item1, T2 item2, T3 item3) { - return TuPack.ToRange(prefix, STuple.Create(item1, item2, item3)); + return TuPack.ToPrefixedKeyRange(prefix, item1, item2, item3); } public KeyRange ToKeyRange(Slice prefix, T1 item1, T2 item2, T3 item3, T4 item4) { - return TuPack.ToRange(prefix, STuple.Create(item1, item2, item3, item4)); + return TuPack.ToPrefixedKeyRange(prefix, item1, item2, item3, item4); } public KeyRange ToKeyRange(Slice prefix, T1 item1, T2 item2, T3 item3, T4 item4, T5 item5) { - return TuPack.ToRange(prefix, STuple.Create(item1, item2, item3, item4, item5)); + return TuPack.ToPrefixedKeyRange(prefix, item1, item2, item3, item4, item5); } public KeyRange ToKeyRange(Slice prefix, T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6) { - return TuPack.ToRange(prefix, STuple.Create(item1, item2, item3, item4, item5, item6)); + return TuPack.ToPrefixedKeyRange(prefix, item1, item2, item3, item4, item5, item6); } public KeyRange ToKeyRange(Slice prefix, T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7) { - return TuPack.ToRange(prefix, STuple.Create(item1, item2, item3, item4, item5, item6, item7)); + return TuPack.ToPrefixedKeyRange(prefix, item1, item2, item3, item4, item5, item6, item7); } public KeyRange ToKeyRange(Slice prefix, T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8) { - return TuPack.ToRange(prefix, STuple.Create(item1, item2, item3, item4, item5, item6, item7, item8)); + return TuPack.ToPrefixedKeyRange(prefix, item1, item2, item3, item4, item5, item6, item7, item8); } } diff --git a/FoundationDB.Client/Layers/Tuples/Encoding/TuplePackers.cs b/FoundationDB.Client/Layers/Tuples/Encoding/TuplePackers.cs index a8e8cc9b6..7fa2a4d49 100644 --- a/FoundationDB.Client/Layers/Tuples/Encoding/TuplePackers.cs +++ b/FoundationDB.Client/Layers/Tuples/Encoding/TuplePackers.cs @@ -26,8 +26,6 @@ DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY */ #endregion -//#define ENABLE_VALUETUPLES - namespace Doxense.Collections.Tuples.Encoding { using System; @@ -117,7 +115,6 @@ private static Delegate GetSerializerFor([NotNull] Type type) } } -#if ENABLE_VALUETUPLES if ((type.Name == nameof(System.ValueTuple) || type.Name.StartsWith(nameof(System.ValueTuple) + "`", StringComparison.Ordinal)) && type.Namespace == "System") { typeArgs = type.GetGenericArguments(); @@ -127,7 +124,6 @@ private static Delegate GetSerializerFor([NotNull] Type type) return method.MakeGenericMethod(typeArgs).CreateDelegate(typeof(Encoder<>).MakeGenericType(type)); } } -#endif // TODO: look for a static SerializeTo(ref TupleWriter, T) method on the type itself ? @@ -135,16 +131,14 @@ private static Delegate GetSerializerFor([NotNull] Type type) return null; } -#if ENABLE_VALUETUPLES private static MethodInfo FindValueTupleSerializerMethod(Type[] args) { - //note: we want to find the correct SerializeValueTuple<...>(ref TupleWriter, ValueTuple<...>), but this cannot be done with Type.GetMethod(...) directly + //note: we want to find the correct SerializeValueTuple<...>(ref TupleWriter, (...,), but this cannot be done with Type.GetMethod(...) directly // => we have to scan for all methods with the correct name, and the same number of Type Arguments than the ValueTuple. return typeof(TuplePackers) .GetMethods(BindingFlags.Static | BindingFlags.Public) .SingleOrDefault(m => m.Name == nameof(SerializeValueTupleTo) && m.GetGenericArguments().Length == args.Length); } -#endif [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void SerializeTo(ref TupleWriter writer, T value) @@ -587,8 +581,6 @@ public static void SerializeFormattableTo(ref TupleWriter writer, ITupleFormatta TupleParser.EndTuple(ref writer); } -#if ENABLE_VALUETUPLES - public static void SerializeValueTupleTo(ref TupleWriter writer, ValueTuple tuple) { TupleParser.BeginTuple(ref writer); @@ -596,7 +588,7 @@ public static void SerializeValueTupleTo(ref TupleWriter writer, ValueTuple< TupleParser.EndTuple(ref writer); } - public static void SerializeValueTupleTo(ref TupleWriter writer, ValueTuple tuple) + public static void SerializeValueTupleTo(ref TupleWriter writer, (T1, T2) tuple) { TupleParser.BeginTuple(ref writer); SerializeTo(ref writer, tuple.Item1); @@ -604,7 +596,7 @@ public static void SerializeValueTupleTo(ref TupleWriter writer, ValueTu TupleParser.EndTuple(ref writer); } - public static void SerializeValueTupleTo(ref TupleWriter writer, ValueTuple tuple) + public static void SerializeValueTupleTo(ref TupleWriter writer, (T1, T2, T3) tuple) { TupleParser.BeginTuple(ref writer); SerializeTo(ref writer, tuple.Item1); @@ -613,7 +605,7 @@ public static void SerializeValueTupleTo(ref TupleWriter writer, Val TupleParser.EndTuple(ref writer); } - public static void SerializeValueTupleTo(ref TupleWriter writer, ValueTuple tuple) + public static void SerializeValueTupleTo(ref TupleWriter writer, (T1, T2, T3, T4) tuple) { TupleParser.BeginTuple(ref writer); SerializeTo(ref writer, tuple.Item1); @@ -623,7 +615,7 @@ public static void SerializeValueTupleTo(ref TupleWriter writer, TupleParser.EndTuple(ref writer); } - public static void SerializeValueTupleTo(ref TupleWriter writer, ValueTuple tuple) + public static void SerializeValueTupleTo(ref TupleWriter writer, (T1, T2, T3, T4, T5) tuple) { TupleParser.BeginTuple(ref writer); SerializeTo(ref writer, tuple.Item1); @@ -634,7 +626,7 @@ public static void SerializeValueTupleTo(ref TupleWriter wri TupleParser.EndTuple(ref writer); } - public static void SerializeValueTupleTo(ref TupleWriter writer, ValueTuple tuple) + public static void SerializeValueTupleTo(ref TupleWriter writer, (T1, T2, T3, T4, T5, T6) tuple) { TupleParser.BeginTuple(ref writer); SerializeTo(ref writer, tuple.Item1); @@ -646,8 +638,6 @@ public static void SerializeValueTupleTo(ref TupleWriter TupleParser.EndTuple(ref writer); } -#endif - #endregion #region Deserializers... @@ -714,12 +704,10 @@ internal static Func GetDeserializer(bool required) return (Func) MakeSTupleDeserializer(type); } -#if ENABLE_VALUETUPLES if ((type.Name == nameof(ValueTuple) || type.Name.StartsWith(nameof(ValueTuple) + "`", StringComparison.Ordinal)) && type.Namespace == "System") { return (Func) MakeValueTupleDeserializer(type); } -#endif if (required) { // will throw at runtime @@ -793,8 +781,6 @@ private static Delegate MakeSTupleDeserializer(Type type) return Expression.Lambda(body, prmSlice).Compile(); } -#if ENABLE_VALUETUPLES - [Pure, NotNull] private static Delegate MakeValueTupleDeserializer(Type type) { @@ -821,8 +807,6 @@ private static Delegate MakeValueTupleDeserializer(Type type) return Expression.Lambda(body, prmSlice).Compile(); } -#endif - /// Deserialize a packed element into an object by choosing the most appropriate type at runtime /// Slice that contains a single packed element /// Decoded element, in the type that is the best fit. @@ -1162,8 +1146,6 @@ public static STuple DeserializeTuple DeserializeValueTuple(Slice slice) { @@ -1171,37 +1153,35 @@ public static ValueTuple DeserializeValueTuple(Slice slice) } [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ValueTuple DeserializeValueTuple(Slice slice) + public static (T1, T2) DeserializeValueTuple(Slice slice) { return DeserializeTuple(slice).ToValueTuple(); } [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ValueTuple DeserializeValueTuple(Slice slice) + public static (T1, T2, T3) DeserializeValueTuple(Slice slice) { return DeserializeTuple(slice).ToValueTuple(); } [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ValueTuple DeserializeValueTuple(Slice slice) + public static (T1, T2, T3, T4) DeserializeValueTuple(Slice slice) { return DeserializeTuple(slice).ToValueTuple(); } [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ValueTuple DeserializeValueTuple(Slice slice) + public static (T1, T2, T3, T4, T5) DeserializeValueTuple(Slice slice) { return DeserializeTuple(slice).ToValueTuple(); } [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ValueTuple DeserializeValueTuple(Slice slice) + public static (T1, T2, T3, T4, T5, T6) DeserializeValueTuple(Slice slice) { return DeserializeTuple(slice).ToValueTuple(); } -#endif - /// Deserialize a tuple segment into a Boolean /// Slice that contains a single packed element public static bool DeserializeBoolean(Slice slice) diff --git a/FoundationDB.Client/Layers/Tuples/STuple.cs b/FoundationDB.Client/Layers/Tuples/STuple.cs index 10967f47d..a4597dccf 100644 --- a/FoundationDB.Client/Layers/Tuples/STuple.cs +++ b/FoundationDB.Client/Layers/Tuples/STuple.cs @@ -26,8 +26,6 @@ DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY */ #endregion -//#define ENABLE_VALUETUPLES - namespace Doxense.Collections.Tuples { using System; @@ -340,8 +338,6 @@ public static ITuple Concat([NotNull] ITuple head, [NotNull] ITuple tail) : new JoinedTuple(head, tail); } -#if ENABLE_VALUETUPLES - [Pure] public static STuple Create(ValueTuple tuple) { @@ -355,61 +351,59 @@ public static STuple Create(ref ValueTuple tuple) } [Pure] - public static STuple Create(ValueTuple tuple) + public static STuple Create((T1, T2) tuple) { return new STuple(tuple.Item1, tuple.Item2); } [Pure] - public static STuple Create(ref ValueTuple tuple) + public static STuple Create(ref (T1, T2) tuple) { return new STuple(tuple.Item1, tuple.Item2); } [Pure] - public static STuple Create(ValueTuple tuple) + public static STuple Create((T1, T2, T3) tuple) { return new STuple(tuple.Item1, tuple.Item2, tuple.Item3); } [Pure] - public static STuple Create(ref ValueTuple tuple) + public static STuple Create(ref (T1, T2, T3) tuple) { return new STuple(tuple.Item1, tuple.Item2, tuple.Item3); } [Pure] - public static STuple Create(ValueTuple tuple) + public static STuple Create((T1, T2, T3, T4) tuple) { return new STuple(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4); } [Pure] - public static STuple Create(ref ValueTuple tuple) + public static STuple Create(ref (T1, T2, T3, T4) tuple) { return new STuple(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4); } [Pure] - public static STuple Create(ValueTuple tuple) + public static STuple Create((T1, T2, T3, T4, T5) tuple) { return new STuple(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4, tuple.Item5); } [Pure] - public static STuple Create(ValueTuple tuple) + public static STuple Create((T1, T2, T3, T4, T5, T6) tuple) { return new STuple(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4, tuple.Item5, tuple.Item6); } [Pure] - public static STuple Create(ref ValueTuple tuple) + public static STuple Create(ref (T1, T2, T3, T4, T5, T6) tuple) { return new STuple(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4, tuple.Item5, tuple.Item6); } -#endif - #endregion #region Internal Helpers... diff --git a/FoundationDB.Client/Layers/Tuples/STuple`1.cs b/FoundationDB.Client/Layers/Tuples/STuple`1.cs index bddd4f35f..53a1f3e56 100644 --- a/FoundationDB.Client/Layers/Tuples/STuple`1.cs +++ b/FoundationDB.Client/Layers/Tuples/STuple`1.cs @@ -26,8 +26,6 @@ DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY */ #endregion -//#define ENABLE_VALUETUPLES - namespace Doxense.Collections.Tuples { using System; @@ -44,10 +42,7 @@ namespace Doxense.Collections.Tuples /// Tuple that holds only one item /// Type of the item [ImmutableObject(true), DebuggerDisplay("{ToString(),nq}")] - public readonly struct STuple : ITuple, ITupleSerializable, IEquatable> -#if ENABLE_VALUETUPLES - , IEquatable> -#endif + public readonly struct STuple : ITuple, ITupleSerializable, IEquatable>, IEquatable> { // This is mostly used by code that create a lot of temporary singleton, to reduce the pressure on the Garbage Collector by allocating them on the stack. // Please note that if you return an STuple as an ITuple, it will be boxed by the CLR and all memory gains will be lost @@ -208,12 +203,10 @@ bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer) { return comparer.Equals(this.Item1, stuple.Item1); } -#if ENABLE_VALUETUPLES if (other is ValueTuple vtuple) { return comparer.Equals(this.Item1, vtuple.Item1); } -#endif return TupleHelpers.Equals(this, other, comparer); } @@ -235,10 +228,6 @@ public static explicit operator Tuple(STuple t) return new Tuple(t.Item1); } -#if ENABLE_VALUETUPLES - - // interop with System.ValueTuple - public void Fill(ref ValueTuple t) { t.Item1 = this.Item1; @@ -257,7 +246,7 @@ public STuple Concat(ValueTuple tuple) /// Tuple whose items are to be appended at the end /// New tuple composed of the current tuple's items, followed by 's items [Pure] - public STuple Concat(ValueTuple tuple) + public STuple Concat((T2, T3) tuple) { return new STuple(this.Item1, tuple.Item1, tuple.Item2); } @@ -266,7 +255,7 @@ public STuple Concat(ValueTuple tuple) /// Tuple whose items are to be appended at the end /// New tuple composed of the current tuple's items, followed by 's items [Pure] - public STuple Concat(ValueTuple tuple) + public STuple Concat((T2, T3, T4) tuple) { return new STuple(this.Item1, tuple.Item1, tuple.Item2, tuple.Item3); } @@ -275,7 +264,7 @@ public STuple Concat(ValueTuple tuple) /// Tuple whose items are to be appended at the end /// New tuple composed of the current tuple's items, followed by 's items [Pure] - public STuple Concat(ValueTuple tuple) + public STuple Concat((T2, T3, T4, T5) tuple) { return new STuple(this.Item1, tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4); } @@ -284,7 +273,7 @@ public STuple Concat(ValueTupleTuple whose items are to be appended at the end /// New tuple composed of the current tuple's items, followed by 's items [Pure] - public STuple Concat(ValueTuple tuple) + public STuple Concat((T2, T3, T4, T5, T6) tuple) { return new STuple(this.Item1, tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4, tuple.Item5); } @@ -337,8 +326,6 @@ bool IEquatable>.Equals(ValueTuple other) return !SimilarValueComparer.Default.Equals(left.Item1, right.Item1); } -#endif - public sealed class Comparer : IComparer> { public static Comparer Default { [NotNull] get; } = new Comparer(); diff --git a/FoundationDB.Client/Layers/Tuples/STuple`2.cs b/FoundationDB.Client/Layers/Tuples/STuple`2.cs index 308974fc5..0c48ed299 100644 --- a/FoundationDB.Client/Layers/Tuples/STuple`2.cs +++ b/FoundationDB.Client/Layers/Tuples/STuple`2.cs @@ -26,8 +26,6 @@ DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY */ #endregion -//#define ENABLE_VALUETUPLES - namespace Doxense.Collections.Tuples { using System; @@ -45,10 +43,7 @@ namespace Doxense.Collections.Tuples /// Type of the first item /// Type of the second item [ImmutableObject(true), DebuggerDisplay("{ToString(),nq}")] - public readonly struct STuple : ITuple, ITupleSerializable, IEquatable> -#if ENABLE_VALUETUPLES - , IEquatable> -#endif + public readonly struct STuple : ITuple, ITupleSerializable, IEquatable>, IEquatable<(T1, T2)> { // This is mostly used by code that create a lot of temporary pair, to reduce the pressure on the Garbage Collector by allocating them on the stack. // Please note that if you return an STuple as an ITuple, it will be boxed by the CLR and all memory gains will be lost @@ -249,13 +244,11 @@ bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer) return comparer.Equals(this.Item1, stuple.Item1) && comparer.Equals(this.Item2, stuple.Item2); } -#if ENABLE_VALUETUPLES if (other is ValueTuple vtuple) { return comparer.Equals(this.Item1, vtuple.Item1) && comparer.Equals(this.Item2, vtuple.Item2); } -#endif return TupleHelpers.Equals(this, other, comparer); } @@ -280,11 +273,7 @@ public static explicit operator Tuple(STuple t) return new Tuple(t.Item1, t.Item2); } -#if ENABLE_VALUETUPLES - - // interop with System.ValueTuple - - public void Fill(ref ValueTuple t) + public void Fill(ref (T1, T2) t) { t.Item1 = this.Item1; t.Item2 = this.Item2; @@ -303,7 +292,7 @@ public STuple Concat(ValueTuple tuple) /// Tuple whose items are to be appended at the end /// New tuple composed of the current tuple's items, followed by 's items [Pure] - public STuple Concat(ValueTuple tuple) + public STuple Concat((T3, T4) tuple) { return new STuple(this.Item1, this.Item2, tuple.Item1, tuple.Item2); } @@ -312,66 +301,64 @@ public STuple Concat(ValueTuple tuple) /// Tuple whose items are to be appended at the end /// New tuple composed of the current tuple's items, followed by 's items [Pure] - public STuple Concat(ValueTuple tuple) + public STuple Concat((T3, T4, T5) tuple) { return new STuple(this.Item1, this.Item2, tuple.Item1, tuple.Item2, tuple.Item3); } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTuple ToValueTuple() + public (T1, T2) ToValueTuple() { - return ValueTuple.Create(this.Item1, this.Item2); + return (this.Item1, this.Item2); } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator STuple(ValueTuple t) + public static implicit operator STuple((T1, T2) t) { return new STuple(t.Item1, t.Item2); } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator ValueTuple(STuple t) + public static implicit operator (T1, T2)(STuple t) { - return ValueTuple.Create(t.Item1, t.Item2); + return (t.Item1, t.Item2); } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] - bool IEquatable>.Equals(ValueTuple other) + bool IEquatable<(T1, T2)>.Equals((T1, T2) other) { return SimilarValueComparer.Default.Equals(this.Item1, other.Item1) && SimilarValueComparer.Default.Equals(this.Item2, other.Item2); } - public static bool operator ==(STuple left, ValueTuple right) + public static bool operator ==(STuple left, (T1, T2) right) { return SimilarValueComparer.Default.Equals(left.Item1, right.Item1) && SimilarValueComparer.Default.Equals(left.Item2, right.Item2); } - public static bool operator ==(ValueTuple left, STuple right) + public static bool operator ==((T1, T2) left, STuple right) { return SimilarValueComparer.Default.Equals(left.Item1, right.Item1) && SimilarValueComparer.Default.Equals(left.Item2, right.Item2); } - public static bool operator !=(STuple left, ValueTuple right) + public static bool operator !=(STuple left, (T1, T2) right) { return !SimilarValueComparer.Default.Equals(left.Item1, right.Item1) || !SimilarValueComparer.Default.Equals(left.Item2, right.Item2); } - public static bool operator !=(ValueTuple left, STuple right) + public static bool operator !=((T1, T2) left, STuple right) { return !SimilarValueComparer.Default.Equals(left.Item1, right.Item1) || !SimilarValueComparer.Default.Equals(left.Item2, right.Item2); } -#endif - public sealed class Comparer : IComparer> { diff --git a/FoundationDB.Client/Layers/Tuples/STuple`3.cs b/FoundationDB.Client/Layers/Tuples/STuple`3.cs index 22def275c..ee15aad06 100644 --- a/FoundationDB.Client/Layers/Tuples/STuple`3.cs +++ b/FoundationDB.Client/Layers/Tuples/STuple`3.cs @@ -26,8 +26,6 @@ DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY */ #endregion -//#define ENABLE_VALUETUPLES - namespace Doxense.Collections.Tuples { using System; @@ -46,10 +44,7 @@ namespace Doxense.Collections.Tuples /// Type of the second item /// Type of the third item [ImmutableObject(true), DebuggerDisplay("{ToString(),nq}")] - public readonly struct STuple : ITuple, ITupleSerializable, IEquatable> -#if ENABLE_VALUETUPLES - , IEquatable> -#endif + public readonly struct STuple : ITuple, ITupleSerializable, IEquatable>, IEquatable<(T1, T2, T3)> { // This is mostly used by code that create a lot of temporary triplet, to reduce the pressure on the Garbage Collector by allocating them on the stack. // Please note that if you return an STuple as an ITuple, it will be boxed by the CLR and all memory gains will be lost @@ -277,14 +272,12 @@ bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer) && comparer.Equals(this.Item2, stuple.Item2) && comparer.Equals(this.Item3, stuple.Item3); } -#if ENABLE_VALUETUPLES if (other is ValueTuple vtuple) { return comparer.Equals(this.Item1, vtuple.Item1) && comparer.Equals(this.Item2, vtuple.Item2) && comparer.Equals(this.Item3, vtuple.Item3); } -#endif return TupleHelpers.Equals(this, other, comparer); } @@ -310,11 +303,7 @@ public static explicit operator Tuple(STuple t) return new Tuple(t.Item1, t.Item2, t.Item3); } -#if ENABLE_VALUETUPLES - - // interop with System.ValueTuple - - public void Fill(ref ValueTuple t) + public void Fill(ref (T1, T2, T3) t) { t.Item1 = this.Item1; t.Item2 = this.Item2; @@ -343,71 +332,69 @@ public STuple Concat(ValueTuple tuple) /// Tuple whose items are to be appended at the end /// New tuple composed of the current tuple's items, followed by 's items [Pure] - public STuple Concat(ValueTuple tuple) + public STuple Concat((T4, T5, T6) tuple) { return new STuple(this.Item1, this.Item2, this.Item3, tuple.Item1, tuple.Item2, tuple.Item3); } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTuple ToValueTuple() + public (T1, T2, T3) ToValueTuple() { - return ValueTuple.Create(this.Item1, this.Item2, this.Item3); + return (this.Item1, this.Item2, this.Item3); } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator STuple(ValueTuple t) + public static implicit operator STuple((T1, T2, T3) t) { return new STuple(t.Item1, t.Item2, t.Item3); } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator ValueTuple (STuple t) + public static implicit operator (T1, T2, T3) (STuple t) { - return ValueTuple.Create(t.Item1, t.Item2, t.Item3); + return (t.Item1, t.Item2, t.Item3); } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] - bool IEquatable>.Equals(ValueTuple other) + bool IEquatable<(T1, T2, T3)>.Equals((T1, T2, T3) other) { return SimilarValueComparer.Default.Equals(this.Item1, this.Item1) && SimilarValueComparer.Default.Equals(this.Item2, this.Item2) && SimilarValueComparer.Default.Equals(this.Item3, this.Item3); } - public static bool operator ==(STuple left, ValueTuple right) + public static bool operator ==(STuple left, (T1, T2, T3) right) { return SimilarValueComparer.Default.Equals(left.Item1, right.Item1) && SimilarValueComparer.Default.Equals(left.Item2, right.Item2) && SimilarValueComparer.Default.Equals(left.Item3, right.Item3); } - public static bool operator ==(ValueTuple left, STuple right) + public static bool operator ==((T1, T2, T3) left, STuple right) { return SimilarValueComparer.Default.Equals(left.Item1, right.Item1) && SimilarValueComparer.Default.Equals(left.Item2, right.Item2) && SimilarValueComparer.Default.Equals(left.Item3, right.Item3); } - public static bool operator !=(STuple left, ValueTuple right) + public static bool operator !=(STuple left, (T1, T2, T3) right) { return !SimilarValueComparer.Default.Equals(left.Item1, right.Item1) || !SimilarValueComparer.Default.Equals(left.Item2, right.Item2) || !SimilarValueComparer.Default.Equals(left.Item3, right.Item3); } - public static bool operator !=(ValueTuple left, STuple right) + public static bool operator !=((T1, T2, T3) left, STuple right) { return !SimilarValueComparer.Default.Equals(left.Item1, right.Item1) || !SimilarValueComparer.Default.Equals(left.Item2, right.Item2) || !SimilarValueComparer.Default.Equals(left.Item3, right.Item3); } -#endif - public sealed class Comparer : IComparer> { diff --git a/FoundationDB.Client/Layers/Tuples/STuple`4.cs b/FoundationDB.Client/Layers/Tuples/STuple`4.cs index 02df7669c..e6bbb3fbb 100644 --- a/FoundationDB.Client/Layers/Tuples/STuple`4.cs +++ b/FoundationDB.Client/Layers/Tuples/STuple`4.cs @@ -26,8 +26,6 @@ DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY */ #endregion -//#define ENABLE_VALUETUPLES - namespace Doxense.Collections.Tuples { using System; @@ -47,10 +45,7 @@ namespace Doxense.Collections.Tuples /// Type of the third item /// Type of the fourth item [ImmutableObject(true), DebuggerDisplay("{ToString(),nq}")] - public readonly struct STuple : ITuple, ITupleSerializable, IEquatable> -#if ENABLE_VALUETUPLES - , IEquatable> -#endif + public readonly struct STuple : ITuple, ITupleSerializable, IEquatable>, IEquatable<(T1, T2, T3, T4)> { // This is mostly used by code that create a lot of temporary quartets, to reduce the pressure on the Garbage Collector by allocating them on the stack. // Please note that if you return an STuple as an ITuple, it will be boxed by the CLR and all memory gains will be lost @@ -283,7 +278,6 @@ bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer) && comparer.Equals(this.Item3, stuple.Item3) && comparer.Equals(this.Item4, stuple.Item4); } -#if ENABLE_VALUETUPLES if (other is ValueTuple vtuple) { return comparer.Equals(this.Item1, vtuple.Item1) @@ -291,7 +285,6 @@ bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer) && comparer.Equals(this.Item3, vtuple.Item3) && comparer.Equals(this.Item4, vtuple.Item4); } -#endif return TupleHelpers.Equals(this, other, comparer); } @@ -318,11 +311,7 @@ public static explicit operator Tuple(STuple t) return new Tuple(t.Item1, t.Item2, t.Item3, t.Item4); } -#if ENABLE_VALUETUPLES - - // interop with System.ValueTuple - - public void Fill(ref ValueTuple t) + public void Fill(ref (T1, T2, T3, T4) t) { t.Item1 = this.Item1; t.Item2 = this.Item2; @@ -343,35 +332,35 @@ public STuple Concat(ValueTuple tuple) /// Tuple whose items are to be appended at the end /// New tuple composed of the current tuple's items, followed by 's items [Pure] - public STuple Concat(ValueTuple tuple) + public STuple Concat((T5, T6) tuple) { return new STuple(this.Item1, this.Item2, this.Item3, this.Item4, tuple.Item1, tuple.Item2); } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTuple ToValueTuple() + public (T1, T2, T3, T4) ToValueTuple() { - return ValueTuple.Create(this.Item1, this.Item2, this.Item3, this.Item4); + return (this.Item1, this.Item2, this.Item3, this.Item4); } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator STuple(ValueTuple t) + public static implicit operator STuple((T1, T2, T3, T4) t) { return new STuple(t.Item1, t.Item2, t.Item3, t.Item4); } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator ValueTuple(STuple t) + public static implicit operator (T1, T2, T3, T4) (STuple t) { - return ValueTuple.Create(t.Item1, t.Item2, t.Item3, t.Item4); + return (t.Item1, t.Item2, t.Item3, t.Item4); } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] - bool IEquatable>.Equals(ValueTuple other) + bool IEquatable<(T1, T2, T3, T4)>.Equals((T1, T2, T3, T4) other) { return SimilarValueComparer.Default.Equals(this.Item1, this.Item1) && SimilarValueComparer.Default.Equals(this.Item2, this.Item2) @@ -379,7 +368,7 @@ bool IEquatable>.Equals(ValueTuple ot && SimilarValueComparer.Default.Equals(this.Item4, this.Item4); } - public static bool operator ==(STuple left, ValueTuple right) + public static bool operator ==(STuple left, (T1, T2, T3, T4) right) { return SimilarValueComparer.Default.Equals(left.Item1, right.Item1) && SimilarValueComparer.Default.Equals(left.Item2, right.Item2) @@ -387,7 +376,7 @@ bool IEquatable>.Equals(ValueTuple ot && SimilarValueComparer.Default.Equals(left.Item4, right.Item4); } - public static bool operator ==(ValueTuple left, STuple right) + public static bool operator ==((T1, T2, T3, T4) left, STuple right) { return SimilarValueComparer.Default.Equals(left.Item1, right.Item1) && SimilarValueComparer.Default.Equals(left.Item2, right.Item2) @@ -395,7 +384,7 @@ bool IEquatable>.Equals(ValueTuple ot && SimilarValueComparer.Default.Equals(left.Item4, right.Item4); } - public static bool operator !=(STuple left, ValueTuple right) + public static bool operator !=(STuple left, (T1, T2, T3, T4) right) { return !SimilarValueComparer.Default.Equals(left.Item1, right.Item1) || !SimilarValueComparer.Default.Equals(left.Item2, right.Item2) @@ -403,7 +392,7 @@ bool IEquatable>.Equals(ValueTuple ot || !SimilarValueComparer.Default.Equals(left.Item4, right.Item4); } - public static bool operator !=(ValueTuple left, STuple right) + public static bool operator !=((T1, T2, T3, T4) left, STuple right) { return !SimilarValueComparer.Default.Equals(left.Item1, right.Item1) || !SimilarValueComparer.Default.Equals(left.Item2, right.Item2) @@ -411,8 +400,6 @@ bool IEquatable>.Equals(ValueTuple ot || !SimilarValueComparer.Default.Equals(left.Item4, right.Item4); } -#endif - public sealed class Comparer : IComparer> { diff --git a/FoundationDB.Client/Layers/Tuples/STuple`5.cs b/FoundationDB.Client/Layers/Tuples/STuple`5.cs index 1056f8604..773d1c862 100644 --- a/FoundationDB.Client/Layers/Tuples/STuple`5.cs +++ b/FoundationDB.Client/Layers/Tuples/STuple`5.cs @@ -26,8 +26,6 @@ DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY */ #endregion -//#define ENABLE_VALUETUPLES - namespace Doxense.Collections.Tuples { using System; @@ -48,10 +46,7 @@ namespace Doxense.Collections.Tuples /// Type of the 4th item /// Type of the 5th item [ImmutableObject(true), DebuggerDisplay("{ToString(),nq}")] - public readonly struct STuple : ITuple, ITupleSerializable, IEquatable> -#if ENABLE_VALUETUPLES - , IEquatable> -#endif + public readonly struct STuple : ITuple, ITupleSerializable, IEquatable>, IEquatable<(T1, T2, T3, T4, T5)> { // This is mostly used by code that create a lot of temporary quartets, to reduce the pressure on the Garbage Collector by allocating them on the stack. // Please note that if you return an STuple as an ITuple, it will be boxed by the CLR and all memory gains will be lost @@ -296,7 +291,6 @@ bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer) && comparer.Equals(this.Item4, stuple.Item4) && comparer.Equals(this.Item5, stuple.Item5); } -#if ENABLE_VALUETUPLES if (other is ValueTuple vtuple) { return comparer.Equals(this.Item1, vtuple.Item1) @@ -305,7 +299,6 @@ bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer) && comparer.Equals(this.Item4, vtuple.Item4) && comparer.Equals(this.Item5, vtuple.Item5); } -#endif return TupleHelpers.Equals(this, other, comparer); } @@ -333,11 +326,7 @@ public static explicit operator Tuple(STuple(t.Item1, t.Item2, t.Item3, t.Item4, t.Item5); } -#if ENABLE_VALUETUPLES - - // interop with System.ValueTuple - - public void Fill(ref ValueTuple t) + public void Fill(ref (T1, T2, T3, T4, T5) t) { t.Item1 = this.Item1; t.Item2 = this.Item2; @@ -357,28 +346,28 @@ public STuple Concat(ValueTuple tuple) [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTuple ToValueTuple() + public (T1, T2, T3, T4, T5) ToValueTuple() { - return ValueTuple.Create(this.Item1, this.Item2, this.Item3, this.Item4, this.Item5); + return (this.Item1, this.Item2, this.Item3, this.Item4, this.Item5); } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator STuple(ValueTuple t) + public static implicit operator STuple((T1, T2, T3, T4, T5) t) { return new STuple(t.Item1, t.Item2, t.Item3, t.Item4, t.Item5); } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator ValueTuple(STuple t) + public static implicit operator (T1, T2, T3, T4, T5) (STuple t) { - return ValueTuple.Create(t.Item1, t.Item2, t.Item3, t.Item4, t.Item5); + return (t.Item1, t.Item2, t.Item3, t.Item4, t.Item5); } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] - bool IEquatable>.Equals(ValueTuple other) + bool IEquatable<(T1, T2, T3, T4, T5)>.Equals((T1, T2, T3, T4, T5) other) { return SimilarValueComparer.Default.Equals(this.Item1, this.Item1) && SimilarValueComparer.Default.Equals(this.Item2, this.Item2) @@ -387,7 +376,7 @@ bool IEquatable>.Equals(ValueTuple left, ValueTuple right) + public static bool operator ==(STuple left, (T1, T2, T3, T4, T5) right) { return SimilarValueComparer.Default.Equals(left.Item1, right.Item1) && SimilarValueComparer.Default.Equals(left.Item2, right.Item2) @@ -396,7 +385,7 @@ bool IEquatable>.Equals(ValueTuple left, STuple right) + public static bool operator ==((T1, T2, T3, T4, T5) left, STuple right) { return SimilarValueComparer.Default.Equals(left.Item1, right.Item1) && SimilarValueComparer.Default.Equals(left.Item2, right.Item2) @@ -405,7 +394,7 @@ bool IEquatable>.Equals(ValueTuple left, ValueTuple right) + public static bool operator !=(STuple left, (T1, T2, T3, T4, T5) right) { return !SimilarValueComparer.Default.Equals(left.Item1, right.Item1) || !SimilarValueComparer.Default.Equals(left.Item2, right.Item2) @@ -414,7 +403,7 @@ bool IEquatable>.Equals(ValueTuple left, STuple right) + public static bool operator !=((T1, T2, T3, T4, T5) left, STuple right) { return !SimilarValueComparer.Default.Equals(left.Item1, right.Item1) || !SimilarValueComparer.Default.Equals(left.Item2, right.Item2) @@ -423,8 +412,6 @@ bool IEquatable>.Equals(ValueTuple> { diff --git a/FoundationDB.Client/Layers/Tuples/STuple`6.cs b/FoundationDB.Client/Layers/Tuples/STuple`6.cs index b7f0831a6..7e10645c8 100644 --- a/FoundationDB.Client/Layers/Tuples/STuple`6.cs +++ b/FoundationDB.Client/Layers/Tuples/STuple`6.cs @@ -26,8 +26,6 @@ DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY */ #endregion -//#define ENABLE_VALUETUPLES - namespace Doxense.Collections.Tuples { using System; @@ -49,10 +47,7 @@ namespace Doxense.Collections.Tuples /// Type of the 5th item /// Type of the 5th item [ImmutableObject(true), DebuggerDisplay("{ToString(),nq}")] - public readonly struct STuple : ITuple, ITupleSerializable, IEquatable> -#if ENABLE_VALUETUPLES - , IEquatable> -#endif + public readonly struct STuple : ITuple, ITupleSerializable, IEquatable>, IEquatable<(T1, T2, T3, T4, T5, T6)> { // This is mostly used by code that create a lot of temporary quartets, to reduce the pressure on the Garbage Collector by allocating them on the stack. // Please note that if you return an STuple as an ITuple, it will be boxed by the CLR and all memory gains will be lost @@ -312,7 +307,6 @@ bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer) && comparer.Equals(this.Item5, stuple.Item5) && comparer.Equals(this.Item6, stuple.Item6); } -#if ENABLE_VALUETUPLES if (other is ValueTuple vtuple) { return comparer.Equals(this.Item1, vtuple.Item1) @@ -322,7 +316,6 @@ bool IStructuralEquatable.Equals(object other, IEqualityComparer comparer) && comparer.Equals(this.Item5, vtuple.Item5) && comparer.Equals(this.Item6, vtuple.Item6); } -#endif return TupleHelpers.Equals(this, other, comparer); } @@ -351,11 +344,7 @@ public static explicit operator Tuple(STuple(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4, tuple.Item5, tuple.Item6); } -#if ENABLE_VALUETUPLES - - // interop with System.ValueTuple - - public void Fill(ref ValueTuple t) + public void Fill(ref (T1, T2, T3, T4, T5, T6) t) { t.Item1 = this.Item1; t.Item2 = this.Item2; @@ -367,28 +356,28 @@ public void Fill(ref ValueTuple t) [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTuple ToValueTuple() + public (T1, T2, T3, T4, T5, T6) ToValueTuple() { - return ValueTuple.Create(this.Item1, this.Item2, this.Item3, this.Item4, this.Item5, this.Item6); + return (this.Item1, this.Item2, this.Item3, this.Item4, this.Item5, this.Item6); } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator STuple(ValueTuple t) + public static implicit operator STuple((T1, T2, T3, T4, T5, T6) t) { return new STuple(t.Item1, t.Item2, t.Item3, t.Item4, t.Item5, t.Item6); } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator ValueTuple(STuple t) + public static implicit operator (T1, T2, T3, T4, T5, T6) (STuple t) { - return ValueTuple.Create(t.Item1, t.Item2, t.Item3, t.Item4, t.Item5, t.Item6); + return (t.Item1, t.Item2, t.Item3, t.Item4, t.Item5, t.Item6); } [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] - bool IEquatable>.Equals(ValueTuple other) + bool IEquatable<(T1, T2, T3, T4, T5, T6)>.Equals((T1, T2, T3, T4, T5, T6) other) { return SimilarValueComparer.Default.Equals(this.Item1, this.Item1) && SimilarValueComparer.Default.Equals(this.Item2, this.Item2) @@ -398,7 +387,7 @@ bool IEquatable>.Equals(ValueTuple left, ValueTuple right) + public static bool operator ==(STuple left, (T1, T2, T3, T4, T5, T6) right) { return SimilarValueComparer.Default.Equals(left.Item1, right.Item1) && SimilarValueComparer.Default.Equals(left.Item2, right.Item2) @@ -408,7 +397,7 @@ bool IEquatable>.Equals(ValueTuple left, STuple right) + public static bool operator ==((T1, T2, T3, T4, T5, T6) left, STuple right) { return SimilarValueComparer.Default.Equals(left.Item1, right.Item1) && SimilarValueComparer.Default.Equals(left.Item2, right.Item2) @@ -418,7 +407,7 @@ bool IEquatable>.Equals(ValueTuple left, ValueTuple right) + public static bool operator !=(STuple left, (T1, T2, T3, T4, T5, T6) right) { return !SimilarValueComparer.Default.Equals(left.Item1, right.Item1) || !SimilarValueComparer.Default.Equals(left.Item2, right.Item2) @@ -428,7 +417,7 @@ bool IEquatable>.Equals(ValueTuple left, STuple right) + public static bool operator !=((T1, T2, T3, T4, T5, T6) left, STuple right) { return !SimilarValueComparer.Default.Equals(left.Item1, right.Item1) || !SimilarValueComparer.Default.Equals(left.Item2, right.Item2) @@ -438,8 +427,6 @@ bool IEquatable>.Equals(ValueTuple> { diff --git a/FoundationDB.Client/Layers/Tuples/TuPack.cs b/FoundationDB.Client/Layers/Tuples/TuPack.cs index d0f6b687a..bc8f6ebcc 100644 --- a/FoundationDB.Client/Layers/Tuples/TuPack.cs +++ b/FoundationDB.Client/Layers/Tuples/TuPack.cs @@ -26,8 +26,6 @@ DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY */ #endregion -//#define ENABLE_VALUETUPLES - namespace Doxense.Collections.Tuples { using System; @@ -40,6 +38,7 @@ namespace Doxense.Collections.Tuples using JetBrains.Annotations; /// Tuple Binary Encoding + [PublicAPI] public static class TuPack { @@ -110,8 +109,6 @@ public static Slice Pack(STuple return TupleEncoder.Pack(empty, ref tuple); } -#if ENABLE_VALUETUPLES - /// Pack a tuple into a slice /// Tuple that must be serialized into a binary slice [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -124,7 +121,7 @@ public static Slice Pack(ValueTuple tuple) /// Pack a tuple into a slice /// Tuple that must be serialized into a binary slice [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Slice Pack(ValueTuple tuple) + public static Slice Pack((T1, T2) tuple) { var empty = default(Slice); return TupleEncoder.Pack(empty, tuple.ToSTuple()); @@ -133,7 +130,7 @@ public static Slice Pack(ValueTuple tuple) /// Pack a tuple into a slice /// Tuple that must be serialized into a binary slice [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Slice Pack(ValueTuple tuple) + public static Slice Pack((T1, T2, T3) tuple) { var empty = default(Slice); return TupleEncoder.Pack(empty, tuple.ToSTuple()); @@ -142,7 +139,7 @@ public static Slice Pack(ValueTuple tuple) /// Pack a tuple into a slice /// Tuple that must be serialized into a binary slice [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Slice Pack(ValueTuple tuple) + public static Slice Pack((T1, T2, T3, T4) tuple) { var empty = default(Slice); return TupleEncoder.Pack(empty, tuple.ToSTuple()); @@ -151,7 +148,7 @@ public static Slice Pack(ValueTuple tuple) /// Pack a tuple into a slice /// Tuple that must be serialized into a binary slice [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Slice Pack(ValueTuple tuple) + public static Slice Pack((T1, T2, T3, T4, T5) tuple) { var empty = default(Slice); return TupleEncoder.Pack(empty, tuple.ToSTuple()); @@ -160,14 +157,12 @@ public static Slice Pack(ValueTuple tupl /// Pack a tuple into a slice /// Tuple that must be serialized into a binary slice [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Slice Pack(ValueTuple tuple) + public static Slice Pack((T1, T2, T3, T4, T5, T6) tuple) { var empty = default(Slice); return TupleEncoder.Pack(empty, tuple.ToSTuple()); } -#endif - /// Pack an array of N-tuples, all sharing the same buffer /// Sequence of N-tuples to pack /// Array containing the buffer segment of each packed tuple @@ -379,63 +374,63 @@ public static Slice[] PackTuples(Slice prefix, [NotNull] IEnum [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static Slice EncodeKey(T1 item1) { - return TupleEncoder.EncodeKey(item1); + return TupleEncoder.EncodeKey(default(Slice), item1); } /// Pack a 2-tuple directly into a slice [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static Slice EncodeKey(T1 item1, T2 item2) { - return TupleEncoder.EncodeKey(item1, item2); + return TupleEncoder.EncodeKey(default(Slice), item1, item2); } /// Pack a 3-tuple directly into a slice [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static Slice EncodeKey(T1 item1, T2 item2, T3 item3) { - return TupleEncoder.EncodeKey(item1, item2, item3); + return TupleEncoder.EncodeKey(default(Slice), item1, item2, item3); } /// Pack a 4-tuple directly into a slice [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static Slice EncodeKey(T1 item1, T2 item2, T3 item3, T4 item4) { - return TupleEncoder.EncodeKey(item1, item2, item3, item4); + return TupleEncoder.EncodeKey(default(Slice), item1, item2, item3, item4); } /// Pack a 5-tuple directly into a slice [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static Slice EncodeKey(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5) { - return TupleEncoder.EncodeKey(item1, item2, item3, item4, item5); + return TupleEncoder.EncodeKey(default(Slice), item1, item2, item3, item4, item5); } /// Pack a 6-tuple directly into a slice [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static Slice EncodeKey(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6) { - return TupleEncoder.EncodeKey(item1, item2, item3, item4, item5, item6); + return TupleEncoder.EncodeKey(default(Slice), item1, item2, item3, item4, item5, item6); } /// Pack a 6-tuple directly into a slice [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static Slice EncodeKey(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7) { - return TupleEncoder.EncodeKey(item1, item2, item3, item4, item5, item6, item7); + return TupleEncoder.EncodeKey(default(Slice), item1, item2, item3, item4, item5, item6, item7); } /// Pack a 6-tuple directly into a slice [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static Slice EncodeKey(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8) { - return TupleEncoder.EncodeKey(item1, item2, item3, item4, item5, item6, item7, item8); + return TupleEncoder.EncodeKey(default(Slice), item1, item2, item3, item4, item5, item6, item7, item8); } [Pure, NotNull, MethodImpl(MethodImplOptions.AggressiveInlining)] public static Slice[] EncodeKeys([NotNull] IEnumerable keys) { var empty = default(Slice); - return TupleEncoder.EncodePrefixedKeys(empty, keys); + return TupleEncoder.EncodeKeys(empty, keys); } /// Merge a sequence of keys with a same prefix, all sharing the same buffer @@ -446,14 +441,14 @@ public static Slice[] EncodeKeys([NotNull] IEnumerable keys) [Pure, NotNull, MethodImpl(MethodImplOptions.AggressiveInlining)] public static Slice[] EncodePrefixedKeys(Slice prefix, [NotNull] IEnumerable keys) { - return TupleEncoder.EncodePrefixedKeys(prefix, keys); + return TupleEncoder.EncodeKeys(prefix, keys); } [Pure, NotNull, MethodImpl(MethodImplOptions.AggressiveInlining)] public static Slice[] EncodeKeys([NotNull] params T[] keys) { var empty = default(Slice); - return TupleEncoder.EncodePrefixedKeys(empty, keys); + return TupleEncoder.EncodeKeys(empty, keys); } /// Merge an array of keys with a same prefix, all sharing the same buffer @@ -464,7 +459,7 @@ public static Slice[] EncodeKeys([NotNull] params T[] keys) [Pure, NotNull, MethodImpl(MethodImplOptions.AggressiveInlining)] public static Slice[] EncodePrefixedKeys(Slice prefix, [NotNull] params T[] keys) { - return TupleEncoder.EncodePrefixedKeys(prefix, keys); + return TupleEncoder.EncodeKeys(prefix, keys); } /// Merge an array of elements, all sharing the same buffer @@ -477,7 +472,7 @@ public static Slice[] EncodePrefixedKeys(Slice prefix, [NotNull] params T[] k public static Slice[] EncodeKeys([NotNull] TElement[] elements, [NotNull] Func selector) { var empty = default(Slice); - return TupleEncoder.EncodePrefixedKeys(empty, elements, selector); + return TupleEncoder.EncodeKeys(empty, elements, selector); } /// Merge an array of elements with a same prefix, all sharing the same buffer @@ -490,7 +485,7 @@ public static Slice[] EncodeKeys([NotNull] TElement[] elements, [Pure, NotNull, MethodImpl(MethodImplOptions.AggressiveInlining)] public static Slice[] EncodePrefixedKeys(Slice prefix, [NotNull] TElement[] elements, [NotNull] Func selector) { - return TupleEncoder.EncodePrefixedKeys(prefix, elements, selector); + return TupleEncoder.EncodeKeys(prefix, elements, selector); } /// Pack a sequence of keys with a same prefix, all sharing the same buffer @@ -526,7 +521,7 @@ public static Slice[] EncodePrefixedKeys([NotNull] ITuple prefix, [NotNull] p /// Create a range that selects all tuples that are stored under the specified subspace: 'prefix\x00' <= k < 'prefix\xFF' /// Subspace binary prefix (that will be excluded from the range) /// Range including all possible tuples starting with the specified prefix. - /// FdbTuple.ToRange(Slice.FromAscii("abc")) returns the range [ 'abc\x00', 'abc\xFF' ) + /// TuPack.ToRange(Slice.FromAscii("abc")) returns the range [ 'abc\x00', 'abc\xFF' ) [Pure] public static KeyRange ToRange(Slice prefix) { @@ -541,7 +536,7 @@ public static KeyRange ToRange(Slice prefix) } /// Create a range that selects all the tuples of greater length than the specified , and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' - /// FdbTuple.ToRange(FdbTuple.Create("a", "b")) includes all tuples ("a", "b", ...), but not the tuple ("a", "b") itself. + /// TuPack.ToRange(STuple.Create("a", "b")) includes all tuples ("a", "b", ...), but not the tuple ("a", "b") itself. [Pure] public static KeyRange ToRange([NotNull] TTuple tuple) where TTuple : ITuple @@ -557,14 +552,55 @@ public static KeyRange ToRange([NotNull] TTuple tuple) } /// Create a range that selects all the tuples of greater length than the specified , and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' - /// FdbTuple.ToRange(FdbTuple.Create("a", "b")) includes all tuples ("a", "b", ...), but not the tuple ("a", "b") itself. + /// ToRange(STuple.Create("a", "b")) includes all tuples ("a", "b", ...), but not the tuple ("a", "b") itself. [Pure] public static KeyRange ToRange(STuple tuple) { Contract.NotNullAllowStructs(tuple, nameof(tuple)); // tuple => [ packed."\0", packed."\xFF" ) - var packed = TupleEncoder.Pack(tuple); + var packed = TupleEncoder.EncodeKey(default(Slice), tuple.Item1); + return new KeyRange( + packed + 0x00, + packed + 0xFF + ); + } + + /// Create a range that selects all the tuples of greater length than the specified , and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' + /// ToRange(STuple.Create("a", "b")) includes all tuples ("a", "b", ...), but not the tuple ("a", "b") itself. + [Pure] + public static KeyRange ToRange(ValueTuple tuple) + { + Contract.NotNullAllowStructs(tuple, nameof(tuple)); + + // tuple => [ packed."\0", packed."\xFF" ) + var packed = TupleEncoder.EncodeKey(default(Slice), tuple.Item1); + return new KeyRange( + packed + 0x00, + packed + 0xFF + ); + } + + /// Create a range that selects all the tuples of greater length than the specified element, and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' + /// ToRange(STuple.Create("a", "b")) includes all tuples ("a", "b", ...), but not the tuple ("a", "b") itself. + [Pure] + public static KeyRange ToKeyRange(T1 item1) + { + // tuple => [ packed."\0", packed."\xFF" ) + var packed = TupleEncoder.EncodeKey(default(Slice), item1); + return new KeyRange( + packed + 0x00, + packed + 0xFF + ); + } + + /// Create a range that selects all the tuples of greater length than the specified element, and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' + /// ToRange(STuple.Create("a", "b")) includes all tuples ("a", "b", ...), but not the tuple ("a", "b") itself. + [Pure] + public static KeyRange ToPrefixedKeyRange(Slice prefix, T1 item1) + { + // tuple => [ prefix.packed."\0", prefix.packed."\xFF" ) + var packed = TupleEncoder.EncodeKey(prefix, item1); return new KeyRange( packed + 0x00, packed + 0xFF @@ -572,7 +608,7 @@ public static KeyRange ToRange(STuple tuple) } /// Create a range that selects all the tuples of greater length than the specified , and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' - /// FdbTuple.ToRange(FdbTuple.Create("a", "b")) includes all tuples ("a", "b", ...), but not the tuple ("a", "b") itself. + /// ToRange(STuple.Create("a", "b")) includes all tuples ("a", "b", ...), but not the tuple ("a", "b") itself. [Pure] public static KeyRange ToRange(STuple tuple) { @@ -588,7 +624,47 @@ public static KeyRange ToRange(STuple tuple) } /// Create a range that selects all the tuples of greater length than the specified , and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' - /// FdbTuple.ToRange(FdbTuple.Create("a", "b")) includes all tuples ("a", "b", ...), but not the tuple ("a", "b") itself. + [Pure] + public static KeyRange ToRange((T1, T2) tuple) + { + Contract.NotNullAllowStructs(tuple, nameof(tuple)); + + // tuple => [ packed."\0", packed."\xFF" ) + var empty = default(Slice); + var packed = TupleEncoder.Pack(empty, ref tuple); + return new KeyRange( + packed + 0x00, + packed + 0xFF + ); + } + + /// Create a range that selects all the tuples of greater length than the specified items, and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' + /// ToKeyRange("a", "b") includes all tuples ("a", "b", ...), but not the tuple ("a", "b") itself. + [Pure] + public static KeyRange ToKeyRange(T1 item1, T2 item2) + { + // tuple => [ packed."\0", packed."\xFF" ) + var packed = TupleEncoder.EncodeKey(default(Slice), item1, item2); + return new KeyRange( + packed + 0x00, + packed + 0xFF + ); + } + + /// Create a range that selects all the tuples of greater length than the specified items, and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' + /// ToPrefixedKeyRange(..., "a", "b")) includes all tuples ("a", "b", ...), but not the tuple ("a", "b") itself. + [Pure] + public static KeyRange ToPrefixedKeyRange(Slice prefix, T1 item1, T2 item2) + { + // tuple => [ prefix.packed."\0", prefix.packed."\xFF" ) + var packed = TupleEncoder.EncodeKey(prefix, item1, item2); + return new KeyRange( + packed + 0x00, + packed + 0xFF + ); + } + + /// Create a range that selects all the tuples of greater length than the specified , and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' [Pure] public static KeyRange ToRange(STuple tuple) { @@ -604,7 +680,43 @@ public static KeyRange ToRange(STuple tuple) } /// Create a range that selects all the tuples of greater length than the specified , and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' - /// FdbTuple.ToRange(FdbTuple.Create("a", "b")) includes all tuples ("a", "b", ...), but not the tuple ("a", "b") itself. + [Pure] + public static KeyRange ToRange((T1, T2, T3) tuple) + { + // tuple => [ packed."\0", packed."\xFF" ) + var empty = default(Slice); + var packed = TupleEncoder.Pack(empty, ref tuple); + return new KeyRange( + packed + 0x00, + packed + 0xFF + ); + } + + /// Create a range that selects all the tuples of greater length than the specified items, and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' + [Pure] + public static KeyRange ToKeyRange(T1 item1, T2 item2, T3 item3) + { + // tuple => [ packed."\0", packed."\xFF" ) + var packed = TupleEncoder.EncodeKey(default(Slice), item1, item2, item3); + return new KeyRange( + packed + 0x00, + packed + 0xFF + ); + } + + /// Create a range that selects all the tuples of greater length than the specified items, and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' + [Pure] + public static KeyRange ToPrefixedKeyRange(Slice prefix, T1 item1, T2 item2, T3 item3) + { + // tuple => [ prefix.packed."\0", prefix.packed."\xFF" ) + var packed = TupleEncoder.EncodeKey(prefix, item1, item2, item3); + return new KeyRange( + packed + 0x00, + packed + 0xFF + ); + } + + /// Create a range that selects all the tuples of greater length than the specified , and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' [Pure] public static KeyRange ToRange(STuple tuple) { @@ -620,7 +732,43 @@ public static KeyRange ToRange(STuple tuple) } /// Create a range that selects all the tuples of greater length than the specified , and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' - /// FdbTuple.ToRange(FdbTuple.Create("a", "b")) includes all tuples ("a", "b", ...), but not the tuple ("a", "b") itself. + [Pure] + public static KeyRange ToRange((T1, T2, T3, T4) tuple) + { + // tuple => [ packed."\0", packed."\xFF" ) + var empty = default(Slice); + var packed = TupleEncoder.Pack(empty, ref tuple); + return new KeyRange( + packed + 0x00, + packed + 0xFF + ); + } + + /// Create a range that selects all the tuples of greater length than the specified items, and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' + [Pure] + public static KeyRange ToKeyRange(T1 item1, T2 item2, T3 item3, T4 item4) + { + // tuple => [ packed."\0", packed."\xFF" ) + var packed = TupleEncoder.EncodeKey(default(Slice), item1, item2, item3, item4); + return new KeyRange( + packed + 0x00, + packed + 0xFF + ); + } + + /// Create a range that selects all the tuples of greater length than the specified items, and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' + [Pure] + public static KeyRange ToPrefixedKeyRange(Slice prefix, T1 item1, T2 item2, T3 item3, T4 item4) + { + // tuple => [ prefix.packed."\0", prefix.packed."\xFF" ) + var packed = TupleEncoder.EncodeKey(prefix, item1, item2, item3, item4); + return new KeyRange( + packed + 0x00, + packed + 0xFF + ); + } + + /// Create a range that selects all the tuples of greater length than the specified , and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' [Pure] public static KeyRange ToRange(STuple tuple) { @@ -636,7 +784,43 @@ public static KeyRange ToRange(STuple tu } /// Create a range that selects all the tuples of greater length than the specified , and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' - /// FdbTuple.ToRange(FdbTuple.Create("a", "b")) includes all tuples ("a", "b", ...), but not the tuple ("a", "b") itself. + [Pure] + public static KeyRange ToRange((T1, T2, T3, T4, T5) tuple) + { + // tuple => [ packed."\0", packed."\xFF" ) + var empty = default(Slice); + var packed = TupleEncoder.Pack(empty, ref tuple); + return new KeyRange( + packed + 0x00, + packed + 0xFF + ); + } + + /// Create a range that selects all the tuples of greater length than the specified items, and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' + [Pure] + public static KeyRange ToKeyRange(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5) + { + // tuple => [ packed."\0", packed."\xFF" ) + var packed = TupleEncoder.EncodeKey(default(Slice), item1, item2, item3, item4, item5); + return new KeyRange( + packed + 0x00, + packed + 0xFF + ); + } + + /// Create a range that selects all the tuples of greater length than the specified items, and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' + [Pure] + public static KeyRange ToPrefixedKeyRange(Slice prefix, T1 item1, T2 item2, T3 item3, T4 item4, T5 item5) + { + // tuple => [ prefix.packed."\0", prefix.packed."\xFF" ) + var packed = TupleEncoder.EncodeKey(prefix, item1, item2, item3, item4, item5); + return new KeyRange( + packed + 0x00, + packed + 0xFF + ); + } + + /// Create a range that selects all the tuples of greater length than the specified , and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' [Pure] public static KeyRange ToRange(STuple tuple) { @@ -652,7 +836,92 @@ public static KeyRange ToRange(STupleCreate a range that selects all the tuples of greater length than the specified , and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' - /// FdbTuple.ToRange(Slice.FromInt32(42), FdbTuple.Create("a", "b")) includes all tuples \x2A.("a", "b", ...), but not the tuple \x2A.("a", "b") itself. + [Pure] + public static KeyRange ToRange((T1, T2, T3, T4, T5, T6) tuple) + { + // tuple => [ packed."\0", packed."\xFF" ) + var empty = default(Slice); + var packed = TupleEncoder.Pack(empty, ref tuple); + return new KeyRange( + packed + 0x00, + packed + 0xFF + ); + } + + /// Create a range that selects all the tuples of greater length than the specified items, and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' + [Pure] + public static KeyRange ToKeyRange(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6) + { + // tuple => [ packed."\0", packed."\xFF" ) + var packed = TupleEncoder.EncodeKey(default(Slice), item1, item2, item3, item4, item5, item6); + return new KeyRange( + packed + 0x00, + packed + 0xFF + ); + } + + /// Create a range that selects all the tuples of greater length than the specified items, and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' + [Pure] + public static KeyRange ToPrefixedKeyRange(Slice prefix, T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6) + { + // tuple => [ prefix.packed."\0", prefix.packed."\xFF" ) + var packed = TupleEncoder.EncodeKey(prefix, item1, item2, item3, item4, item5, item6); + return new KeyRange( + packed + 0x00, + packed + 0xFF + ); + } + + /// Create a range that selects all the tuples of greater length than the specified items, and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' + [Pure] + public static KeyRange ToKeyRange(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7) + { + // tuple => [ packed."\0", packed."\xFF" ) + var packed = TupleEncoder.EncodeKey(default(Slice), item1, item2, item3, item4, item5, item6, item7); + return new KeyRange( + packed + 0x00, + packed + 0xFF + ); + } + + /// Create a range that selects all the tuples of greater length than the specified items, and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' + [Pure] + public static KeyRange ToPrefixedKeyRange(Slice prefix, T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7) + { + // tuple => [ prefix.packed."\0", prefix.packed."\xFF" ) + var packed = TupleEncoder.EncodeKey(prefix, item1, item2, item3, item4, item5, item6, item7); + return new KeyRange( + packed + 0x00, + packed + 0xFF + ); + } + + /// Create a range that selects all the tuples of greater length than the specified items, and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' + [Pure] + public static KeyRange ToKeyRange(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8) + { + // tuple => [ packed."\0", packed."\xFF" ) + var packed = TupleEncoder.EncodeKey(default(Slice), item1, item2, item3, item4, item5, item6, item7, item8); + return new KeyRange( + packed + 0x00, + packed + 0xFF + ); + } + + /// Create a range that selects all the tuples of greater length than the specified items, and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' + [Pure] + public static KeyRange ToPrefixedKeyRange(Slice prefix, T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8) + { + // tuple => [ packed."\0", packed."\xFF" ) + var packed = TupleEncoder.EncodeKey(prefix, item1, item2, item3, item4, item5, item6, item7, item8); + return new KeyRange( + packed + 0x00, + packed + 0xFF + ); + } + + /// Create a range that selects all the tuples of greater length than the specified , and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' + /// TuPack.ToRange(Slice.FromInt32(42), Stuple.Create("a", "b")) includes all tuples \x2A.("a", "b", ...), but not the tuple \x2A.("a", "b") itself. /// If is the packed representation of a tuple, then unpacking the resulting key will produce a valid tuple. If not, then the resulting key will need to be truncated first before unpacking. [Pure] public static KeyRange ToRange(Slice prefix, [NotNull] TTuple tuple) @@ -669,7 +938,7 @@ public static KeyRange ToRange(Slice prefix, [NotNull] TTuple tuple) } /// Create a range that selects all the tuples of greater length than the specified , and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' - /// FdbTuple.ToRange(FdbTuple.Create("a", "b")) includes all tuples ("a", "b", ...), but not the tuple ("a", "b") itself. + /// TuPack.ToRange(STuple.Create("a")) includes all tuples ("a", ...), but not the tuple ("a") itself. [Pure] public static KeyRange ToRange(Slice prefix, STuple tuple) { @@ -684,7 +953,7 @@ public static KeyRange ToRange(Slice prefix, STuple tuple) } /// Create a range that selects all the tuples of greater length than the specified , and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' - /// FdbTuple.ToRange(FdbTuple.Create("a", "b")) includes all tuples ("a", "b", ...), but not the tuple ("a", "b") itself. + /// TuPack.ToRange(STuple.Create("a", "b")) includes all tuples ("a", "b", ...), but not the tuple ("a", "b") itself. [Pure] public static KeyRange ToRange(Slice prefix, STuple tuple) { @@ -699,7 +968,6 @@ public static KeyRange ToRange(Slice prefix, STuple tuple) } /// Create a range that selects all the tuples of greater length than the specified , and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' - /// FdbTuple.ToRange(FdbTuple.Create("a", "b")) includes all tuples ("a", "b", ...), but not the tuple ("a", "b") itself. [Pure] public static KeyRange ToRange(Slice prefix, STuple tuple) { @@ -714,7 +982,6 @@ public static KeyRange ToRange(Slice prefix, STuple tupl } /// Create a range that selects all the tuples of greater length than the specified , and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' - /// FdbTuple.ToRange(FdbTuple.Create("a", "b")) includes all tuples ("a", "b", ...), but not the tuple ("a", "b") itself. [Pure] public static KeyRange ToRange(Slice prefix, STuple tuple) { @@ -729,7 +996,6 @@ public static KeyRange ToRange(Slice prefix, STupleCreate a range that selects all the tuples of greater length than the specified , and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' - /// FdbTuple.ToRange(FdbTuple.Create("a", "b")) includes all tuples ("a", "b", ...), but not the tuple ("a", "b") itself. [Pure] public static KeyRange ToRange(Slice prefix, STuple tuple) { @@ -744,7 +1010,6 @@ public static KeyRange ToRange(Slice prefix, STupleCreate a range that selects all the tuples of greater length than the specified , and that start with the specified elements: packed(tuple)+'\x00' <= k < packed(tuple)+'\xFF' - /// FdbTuple.ToRange(FdbTuple.Create("a", "b")) includes all tuples ("a", "b", ...), but not the tuple ("a", "b") itself. [Pure] public static KeyRange ToRange(Slice prefix, STuple tuple) { @@ -905,56 +1170,56 @@ public static bool DecodeNext(ref TupleReader input, out T value) [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static Slice EncodePrefixedKey(Slice prefix, T1 value) { - return TupleEncoder.EncodePrefixedKey(prefix, value); + return TupleEncoder.EncodeKey(prefix, value); } /// Efficiently concatenate a prefix with the packed representation of a 2-tuple [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static Slice EncodePrefixedKey(Slice prefix, T1 value1, T2 value2) { - return TupleEncoder.EncodePrefixedKey(prefix, value1, value2); + return TupleEncoder.EncodeKey(prefix, value1, value2); } /// Efficiently concatenate a prefix with the packed representation of a 3-tuple [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static Slice EncodePrefixedKey(Slice prefix, T1 value1, T2 value2, T3 value3) { - return TupleEncoder.EncodePrefixedKey(prefix, value1, value2, value3); + return TupleEncoder.EncodeKey(prefix, value1, value2, value3); } /// Efficiently concatenate a prefix with the packed representation of a 4-tuple [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static Slice EncodePrefixedKey(Slice prefix, T1 value1, T2 value2, T3 value3, T4 value4) { - return TupleEncoder.EncodePrefixedKey(prefix, value1, value2, value3, value4); + return TupleEncoder.EncodeKey(prefix, value1, value2, value3, value4); } /// Efficiently concatenate a prefix with the packed representation of a 5-tuple [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static Slice EncodePrefixedKey(Slice prefix, T1 value1, T2 value2, T3 value3, T4 value4, T5 value5) { - return TupleEncoder.EncodePrefixedKey(prefix, value1, value2, value3, value4, value5); + return TupleEncoder.EncodeKey(prefix, value1, value2, value3, value4, value5); } /// Efficiently concatenate a prefix with the packed representation of a 6-tuple [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static Slice EncodePrefixedKey(Slice prefix, T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6) { - return TupleEncoder.EncodePrefixedKey(prefix, value1, value2, value3, value4, value5, value6); + return TupleEncoder.EncodeKey(prefix, value1, value2, value3, value4, value5, value6); } /// Efficiently concatenate a prefix with the packed representation of a 7-tuple [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static Slice EncodePrefixedKey(Slice prefix, T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7) { - return TupleEncoder.EncodePrefixedKey(prefix, value1, value2, value3, value4, value5, value6, value7); + return TupleEncoder.EncodeKey(prefix, value1, value2, value3, value4, value5, value6, value7); } /// Efficiently concatenate a prefix with the packed representation of a 8-tuple [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public static Slice EncodePrefixedKey(Slice prefix, T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8) { - return TupleEncoder.EncodePrefixedKey(prefix, value1, value2, value3, value4, value5, value6, value7, value8); + return TupleEncoder.EncodeKey(prefix, value1, value2, value3, value4, value5, value6, value7, value8); } #endregion diff --git a/FoundationDB.Client/Layers/Tuples/TupleComparisons.cs b/FoundationDB.Client/Layers/Tuples/TupleComparisons.cs index 535b390de..7c9c0c7e2 100644 --- a/FoundationDB.Client/Layers/Tuples/TupleComparisons.cs +++ b/FoundationDB.Client/Layers/Tuples/TupleComparisons.cs @@ -44,11 +44,6 @@ public static class TupleComparisons /// Tuple comparer that uses the default BCL object comparison ("123" != 123 != 123L != 123.0d) public static readonly EqualityComparer Bcl = new EqualityComparer(EqualityComparer.Default); -#if false - /// Tuple comparer that compared the packed bytes (slow!) - public static readonly BinaryComparer Binary = new BinaryComparer(); -#endif - public sealed class EqualityComparer : IEqualityComparer, IEqualityComparer { private readonly IEqualityComparer m_comparer; @@ -96,51 +91,6 @@ public int GetHashCode(object obj) } } -#if false - - public sealed class BinaryComparer : IEqualityComparer, IEqualityComparer - { - internal BinaryComparer() - { } - - - public bool Equals(ITuple x, ITuple y) - { - if (object.ReferenceEquals(x, y)) return true; - if (object.ReferenceEquals(x, null) || object.ReferenceEquals(y, null)) return false; - - return x.ToSlice().Equals(y.ToSlice()); - } - - public int GetHashCode(ITuple obj) - { - return object.ReferenceEquals(obj, null) ? 0 : obj.ToSlice().GetHashCode(); - } - - public new bool Equals(object x, object y) - { - if (object.ReferenceEquals(x, y)) return true; - if (x == null || y == null) return false; - - var tx = x as ITuple; - var ty = y as ITuple; - if (object.ReferenceEquals(tx, null) || object.ReferenceEquals(ty, null)) return false; - return tx.ToSlice().Equals(ty.ToSlice()); - } - - public int GetHashCode(object obj) - { - if (obj == null) return 0; - - var tuple = obj as ITuple; - if (!object.ReferenceEquals(tuple, null)) return tuple.ToSlice().GetHashCode(); - - return RuntimeHelpers.GetHashCode(obj); - } - } - -#endif - /// Create a new instance that compares a single item position in two tuples /// Type of the item to compare /// Offset of the item to compare (can be negative) @@ -160,7 +110,7 @@ public static IComparer Composite(int offset = 0, IComparer comp /// Comparer for the second item's type /// New comparer instance [NotNull] - public static IComparer Composite(int offset = 0, IComparer comparer1 = null, IComparer comparer2 = null) + public static CompositeComparer Composite(int offset = 0, IComparer comparer1 = null, IComparer comparer2 = null) { return new CompositeComparer(offset, comparer1, comparer2); } @@ -235,7 +185,7 @@ public int Compare(ITuple x, ITuple y) /// Comparer that compares tuples with at least 2 items /// Type of the first item /// Type of the second item - public sealed class CompositeComparer : IComparer + public sealed class CompositeComparer : IComparer, IComparer>, IComparer<(T1, T2)> { public static readonly IComparer Default = new CompositeComparer(); @@ -286,13 +236,37 @@ public int Compare(ITuple x, ITuple y) int p = this.Offset; - int c = this.Comparer1.Compare(x.Get(p), y.Get(p)); - if (c != 0) return c; + int cmp = this.Comparer1.Compare(x.Get(p), y.Get(p)); + if (cmp != 0) return cmp; if (ny == 1 || nx == 1) return nx - ny; - c = this.Comparer2.Compare(x.Get(p + 1), y.Get(p + 1)); + cmp = this.Comparer2.Compare(x.Get(p + 1), y.Get(p + 1)); - return c; + return cmp; + } + + /// Compare two tuples + /// First tuple + /// Second tuple + /// Returns a positive value if x is greater than y, a negative value if x is less than y and 0 if x is equal to y. + public int Compare(STuple x, STuple y) + { + if (this.Offset != 0) throw new InvalidOperationException("Cannot compare fixed tuples with non-zero offset."); + int cmp = this.Comparer1.Compare(x.Item1, y.Item1); + if (cmp == 0) cmp = this.Comparer2.Compare(x.Item2, y.Item2); + return cmp; + } + + /// Compare two tuples + /// First tuple + /// Second tuple + /// Returns a positive value if x is greater than y, a negative value if x is less than y and 0 if x is equal to y. + public int Compare((T1, T2) x, (T1, T2) y) + { + if (this.Offset != 0) throw new InvalidOperationException("Cannot compare fixed tuples with non-zero offset."); + int cmp = this.Comparer1.Compare(x.Item1, y.Item1); + if (cmp == 0) cmp = this.Comparer2.Compare(x.Item2, y.Item2); + return cmp; } } diff --git a/FoundationDB.Client/Layers/Tuples/TupleExtensions.cs b/FoundationDB.Client/Layers/Tuples/TupleExtensions.cs index 2e19f7fcf..0c0a47911 100644 --- a/FoundationDB.Client/Layers/Tuples/TupleExtensions.cs +++ b/FoundationDB.Client/Layers/Tuples/TupleExtensions.cs @@ -26,8 +26,6 @@ DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY */ #endregion -//#define ENABLE_VALUETUPLES - namespace Doxense.Collections.Tuples { using System; @@ -565,7 +563,7 @@ public static TResult With([NotNull] th #endregion - #region Deconstruction (C#7) + #region Deconstruction [EditorBrowsable(EditorBrowsableState.Never)] public static void Deconstruct(this ITuple value, out T1 item1) @@ -652,9 +650,7 @@ public static void Deconstruct(this ITuple value #endregion - #region ValueTuple (C#7) - -#if ENABLE_VALUETUPLES + #region ValueTuple [Pure] public static STuple ToSTuple(this ValueTuple tuple) @@ -669,37 +665,35 @@ public static STuple ToSTuple(this ValueTuple tuple) } [Pure] - public static STuple ToSTuple(this ValueTuple tuple) + public static STuple ToSTuple(this (T1, T2) tuple) { return new STuple(tuple.Item1, tuple.Item2); } [Pure] - public static STuple ToSTuple(this ValueTuple tuple) + public static STuple ToSTuple(this (T1, T2, T3) tuple) { return new STuple(tuple.Item1, tuple.Item2, tuple.Item3); } [Pure] - public static STuple ToSTuple(this ValueTuple tuple) + public static STuple ToSTuple(this (T1, T2, T3, T4) tuple) { return new STuple(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4); } [Pure] - public static STuple ToSTuple(this ValueTuple tuple) + public static STuple ToSTuple(this (T1, T2, T3, T4, T5) tuple) { return new STuple(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4, tuple.Item5); } [Pure] - public static STuple ToSTuple(this ValueTuple tuple) + public static STuple ToSTuple(this (T1, T2, T3, T4, T5, T6) tuple) { return new STuple(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4, tuple.Item5, tuple.Item6); } -#endif - #endregion } diff --git a/FoundationDB.Client/Status/FdbSystemStatus.cs b/FoundationDB.Client/Status/FdbSystemStatus.cs index 1e8d89f3e..d3526813e 100644 --- a/FoundationDB.Client/Status/FdbSystemStatus.cs +++ b/FoundationDB.Client/Status/FdbSystemStatus.cs @@ -83,8 +83,8 @@ internal Message(string name, string description) internal static Message From(Dictionary data, string field) { - var kvp = TinyJsonParser.GetStringPair(TinyJsonParser.GetMapField(data, field), "name", "description"); - return new Message(kvp.Key ?? String.Empty, kvp.Value ?? String.Empty); + (var key, var value) = TinyJsonParser.GetStringPair(TinyJsonParser.GetMapField(data, field), "name", "description"); + return new Message(key ?? string.Empty, value ?? string.Empty); } internal static Message[] FromArray(Dictionary data, string field) @@ -94,8 +94,8 @@ internal static Message[] FromArray(Dictionary data, string fiel for (int i = 0; i < res.Length; i++) { var obj = (Dictionary)array[i]; - var kvp = TinyJsonParser.GetStringPair(obj, "name", "description"); - res[i] = new Message(kvp.Key, kvp.Value); + (var key, var value) = TinyJsonParser.GetStringPair(obj, "name", "description"); + res[i] = new Message(key, value); } return res; } @@ -566,7 +566,7 @@ internal ProcessStatus(Dictionary data, string id) private ProcessCpuMetrics m_cpu; private ProcessDiskMetrics m_disk; private ProcessMemoryMetrics m_memory; - private KeyValuePair[] m_roles; + private (string Id, string Role)[] m_roles; /// Unique identifier for this process. //TODO: is it stable accross reboots? what are the conditions for a process to change its ID ? @@ -613,7 +613,7 @@ internal ProcessStatus(Dictionary data, string id) /// List of the roles assumed by this process /// The key is the unique role ID in the cluster, and the value is the type of the role itself - public KeyValuePair[] Roles + public (string Id, string Role)[] Roles { get { @@ -622,7 +622,7 @@ public KeyValuePair[] Roles //REVIEW: should we have (K=id, V=role) or (K=role, V=id) ? var arr = GetArray("roles"); - var res = new KeyValuePair[arr.Count]; + var res = new (string, string)[arr.Count]; for (int i = 0; i < res.Length; i++) { var obj = (Dictionary)arr[i]; diff --git a/FoundationDB.Client/Subspaces/DynamicKeySubspace.cs b/FoundationDB.Client/Subspaces/DynamicKeySubspace.cs index 1ae0d821b..49e00ef56 100644 --- a/FoundationDB.Client/Subspaces/DynamicKeySubspace.cs +++ b/FoundationDB.Client/Subspaces/DynamicKeySubspace.cs @@ -30,6 +30,7 @@ namespace FoundationDB.Client { using System; using System.Collections.Generic; + using System.Diagnostics; using System.Runtime.CompilerServices; using Doxense.Collections.Tuples; using Doxense.Diagnostics.Contracts; @@ -43,27 +44,42 @@ public class DynamicKeySubspace : KeySubspace, IDynamicKeySubspace /// Encoder for the keys of this subspace public IKeyEncoding Encoding { get; } + [NotNull] internal IDynamicKeyEncoder KeyEncoder { get; } /// Create a new subspace from a binary prefix /// Prefix of the new subspace - /// Type System used to encode keys in this subspace (optional, will use Tuple Encoding by default) - internal DynamicKeySubspace(Slice prefix, IKeyEncoding encoding) + /// Type System used to encode keys in this subspace + internal DynamicKeySubspace(Slice prefix, [NotNull] IKeyEncoding encoding) : base(prefix) { + Contract.Requires(encoding != null); this.Encoding = encoding; this.KeyEncoder = encoding.GetDynamicEncoder(); this.Keys = new DynamicKeys(this, this.KeyEncoder); this.Partition = new DynamicPartition(this); } + /// Create a new subspace from a binary prefix + /// Prefix of the new subspace + /// Encoder that will be used by this subspace + internal DynamicKeySubspace(Slice prefix, [NotNull] IDynamicKeyEncoder encoder) + : base(prefix) + { + Contract.Requires(encoder != null); + this.Encoding = encoder.Encoding; + this.KeyEncoder = encoder; + this.Keys = new DynamicKeys(this, encoder); + this.Partition = new DynamicPartition(this); + } + /// Return a view of all the possible binary keys of this subspace public DynamicKeys Keys { get; } /// Return a view of all the possible binary keys of this subspace public DynamicPartition Partition { get; } - public Slice this[ITuple item] + public Slice this[[NotNull] ITuple item] { [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] get => this.Keys.Pack(item); @@ -72,6 +88,7 @@ public Slice this[ITuple item] } /// Key helper for a dynamic TypeSystem + [DebuggerDisplay("{Parent.ToString(),nq)}")] public sealed class DynamicKeys { @@ -92,6 +109,7 @@ internal DynamicKeys(DynamicKeySubspace parent, IDynamicKeyEncoder encoder) /// Convert a tuple into a key of this subspace /// Tuple that will be packed and appended to the subspace prefix + [Pure] public Slice Pack([NotNull] TTuple tuple) where TTuple : ITuple { @@ -102,6 +120,42 @@ public Slice Pack([NotNull] TTuple tuple) return sw.ToSlice(); } + [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] + public Slice Pack(ValueTuple items) + { + return Encode(items.Item1); + } + + [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] + public Slice Pack((T1, T2) items) + { + return Encode(items.Item1, items.Item2); + } + + [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] + public Slice Pack((T1, T2, T3) items) + { + return Encode(items.Item1, items.Item2, items.Item3); + } + + [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] + public Slice Pack((T1, T2, T3, T4) items) + { + return Encode(items.Item1, items.Item2, items.Item3, items.Item4); + } + + [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] + public Slice Pack((T1, T2, T3, T4, T5) items) + { + return Encode(items.Item1, items.Item2, items.Item3, items.Item4, items.Item5); + } + + [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] + public Slice Pack((T1, T2, T3, T4, T5, T6) items) + { + return Encode(items.Item1, items.Item2, items.Item3, items.Item4, items.Item5, items.Item6); + } + /// Unpack a key of this subspace, back into a tuple /// Key that was produced by a previous call to /// Original tuple @@ -127,30 +181,62 @@ public KeyRange ToRange([NotNull] ITuple tuple) public KeyRange ToRange(STuple tuple) { - return this.Encoder.ToRange(this.Parent.GetPrefix(), tuple); + return this.Encoder.ToKeyRange(this.Parent.GetPrefix(), tuple.Item1); } public KeyRange ToRange(STuple tuple) { - return this.Encoder.ToRange(this.Parent.GetPrefix(), tuple); + return this.Encoder.ToKeyRange(this.Parent.GetPrefix(), tuple.Item1, tuple.Item2); } public KeyRange ToRange(STuple tuple) { - return this.Encoder.ToRange(this.Parent.GetPrefix(), tuple); + return this.Encoder.ToKeyRange(this.Parent.GetPrefix(), tuple.Item1, tuple.Item2, tuple.Item3); } public KeyRange ToRange(STuple tuple) { - return this.Encoder.ToRange(this.Parent.GetPrefix(), tuple); + return this.Encoder.ToKeyRange(this.Parent.GetPrefix(), tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4); } + public KeyRange ToRange(STuple tuple) { - return this.Encoder.ToRange(this.Parent.GetPrefix(), tuple); + return this.Encoder.ToKeyRange(this.Parent.GetPrefix(), tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4, tuple.Item5); } + public KeyRange ToRange(STuple tuple) { - return this.Encoder.ToRange(this.Parent.GetPrefix(), tuple); + return this.Encoder.ToKeyRange(this.Parent.GetPrefix(), tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4, tuple.Item5); + } + + public KeyRange ToRange(ValueTuple tuple) + { + return this.Encoder.ToKeyRange(this.Parent.GetPrefix(), tuple.Item1); + } + + public KeyRange ToRange((T1, T2) tuple) + { + return this.Encoder.ToKeyRange(this.Parent.GetPrefix(), tuple.Item1, tuple.Item2); + } + + public KeyRange ToRange((T1, T2, T3) tuple) + { + return this.Encoder.ToKeyRange(this.Parent.GetPrefix(), tuple.Item1, tuple.Item2, tuple.Item3); + } + + public KeyRange ToRange((T1, T2, T3, T4) tuple) + { + return this.Encoder.ToKeyRange(this.Parent.GetPrefix(), tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4); + } + + public KeyRange ToRange((T1, T2, T3, T4, T5) tuple) + { + return this.Encoder.ToKeyRange(this.Parent.GetPrefix(), tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4, tuple.Item5); + } + + public KeyRange ToRange((T1, T2, T3, T4, T5, T6) tuple) + { + return this.Encoder.ToKeyRange(this.Parent.GetPrefix(), tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4, tuple.Item5); } #endregion diff --git a/FoundationDB.Client/Subspaces/IKeySubspace.cs b/FoundationDB.Client/Subspaces/IKeySubspace.cs index 00c3d78c2..23383acd7 100644 --- a/FoundationDB.Client/Subspaces/IKeySubspace.cs +++ b/FoundationDB.Client/Subspaces/IKeySubspace.cs @@ -29,8 +29,6 @@ DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY namespace FoundationDB.Client { using System; - using Doxense.Memory; - using FoundationDB.Layers.Directories; using JetBrains.Annotations; /// Represents a sub-partition of the global key space. diff --git a/FoundationDB.Client/Subspaces/KeySubspace.cs b/FoundationDB.Client/Subspaces/KeySubspace.cs index 639394a5e..6a86d6f58 100644 --- a/FoundationDB.Client/Subspaces/KeySubspace.cs +++ b/FoundationDB.Client/Subspaces/KeySubspace.cs @@ -26,22 +26,19 @@ DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY */ #endregion - namespace FoundationDB.Client { using System; - using System.Collections.Generic; using System.Diagnostics; - using System.Linq; using Doxense.Collections.Tuples; using Doxense.Diagnostics.Contracts; using Doxense.Memory; using Doxense.Serialization.Encoders; - using FoundationDB.Layers.Directories; using JetBrains.Annotations; /// Adds a prefix on every keys, to group them inside a common subspace [PublicAPI] + [DebuggerDisplay("{ToString(),nq}")] public class KeySubspace : IKeySubspace, IEquatable, IComparable { @@ -56,25 +53,184 @@ public class KeySubspace : IKeySubspace, IEquatable, IComparable new KeySubspace(Slice.Empty); - /// Initializes a new subspace with the given prefix + #region FromKey... + + /// Initializes a new generic subspace with the given prefix. [Pure, NotNull] public static KeySubspace FromKey(Slice prefix) { return new KeySubspace(prefix.Memoize()); } - /// Initializes a new subspace with the given prefix + /// Initializes a new dynamic subspace with the given binary and key . + /// A subspace that can handle keys of any types and size. [Pure, NotNull] - public static KeySubspace FromKey(ITuple prefix) + public static DynamicKeySubspace CreateDynamic(Slice prefix, [NotNull] IDynamicKeyEncoder encoder) + { + Contract.NotNull(encoder, nameof(encoder)); + return new DynamicKeySubspace(prefix, encoder); + } + + /// Initializes a new subspace with the given binary , that uses a dynamic key . + /// A subspace that can handle keys of any types and size. + [Pure, NotNull] + public static DynamicKeySubspace CreateDynamic(Slice prefix, [NotNull] IKeyEncoding encoding) + { + Contract.NotNull(encoding, nameof(encoding)); + return new DynamicKeySubspace(prefix, encoding.GetDynamicEncoder()); + } + + /// Initializes a new subspace with the given binary , that uses a typed key . + /// A subspace that can handle keys of type . + public static TypedKeySubspace CreateTyped(Slice prefix, [CanBeNull] IKeyEncoding encoding = null) + { + return new TypedKeySubspace(prefix, (encoding ?? TypeSystem.Tuples).GetEncoder()); + } + + /// Initializes a new subspace with the given binary , that uses a typed key . + /// A subspace that can handle keys of type . + public static TypedKeySubspace CreateTyped(Slice prefix, [NotNull] IKeyEncoder encoder) + { + Contract.NotNull(encoder, nameof(encoder)); + return new TypedKeySubspace(prefix, encoder); + } + + /// Initializes a new subspace with the given binary , that uses a typed key . + /// A subspace that can handle composite keys of type (, ). + public static TypedKeySubspace CreateTyped(Slice prefix, [CanBeNull] IKeyEncoding encoding = null) + { + return new TypedKeySubspace(prefix, (encoding ?? TypeSystem.Tuples).GetEncoder()); + } + + /// Initializes a new subspace with the given binary , that uses a typed key . + /// A subspace that can handle composite keys of type (, ). + public static TypedKeySubspace CreateTyped(Slice prefix, [NotNull] ICompositeKeyEncoder encoder) { - return new KeySubspace(TuPack.Pack(prefix).Memoize()); + Contract.NotNull(encoder, nameof(encoder)); + return new TypedKeySubspace(prefix, encoder); } - public static KeySubspace Copy(IKeySubspace subspace) + /// Initializes a new subspace with the given binary , that uses a typed key . + /// A subspace that can handle composite keys of type (, , ). + public static TypedKeySubspace CreateTyped(Slice prefix, [CanBeNull] IKeyEncoding encoding = null) { - return subspace is KeySubspace ks ? new KeySubspace(ks.Key.Memoize()) : new KeySubspace(subspace.GetPrefix().Memoize()); + return new TypedKeySubspace(prefix, (encoding ?? TypeSystem.Tuples).GetEncoder()); } + /// Initializes a new subspace with the given binary , that uses a typed key . + /// A subspace that can handle composite keys of type (, , ). + public static TypedKeySubspace CreateTyped(Slice prefix, [NotNull] ICompositeKeyEncoder encoder) + { + Contract.NotNull(encoder, nameof(encoder)); + return new TypedKeySubspace(prefix, encoder); + } + + /// Initializes a new subspace with the given binary , that uses a typed key . + /// A subspace that can handle composite keys of type (, , ). + public static TypedKeySubspace CreateTyped(Slice prefix, [CanBeNull] IKeyEncoding encoding = null) + { + return new TypedKeySubspace(prefix, (encoding ?? TypeSystem.Tuples).GetEncoder()); + } + + /// Initializes a new subspace with the given binary , that uses a typed key . + /// A subspace that can handle composite keys of type (, , ). + public static TypedKeySubspace CreateTyped(Slice prefix, [NotNull] ICompositeKeyEncoder encoder) + { + Contract.NotNull(encoder, nameof(encoder)); + return new TypedKeySubspace(prefix, encoder); + } + + #endregion + + #region Copy... + + /// Create a new copy of a subspace's prefix + [Pure] + internal static Slice StealPrefix([NotNull] IKeySubspace subspace) + { + //note: we can workaround the 'security' in top directory partition by accessing their key prefix without triggering an exception! + return subspace is KeySubspace ks + ? ks.Key.Memoize() + : subspace.GetPrefix().Memoize(); + } + + /// Create a copy of a generic subspace, sharing the same binary prefix + [Pure, NotNull] + public static KeySubspace Copy([NotNull] IKeySubspace subspace) + { + Contract.NotNull(subspace, nameof(subspace)); + + var prefix = StealPrefix(subspace); + + if (subspace is IDynamicKeySubspace dyn) + { // reuse the encoding of the original + return new DynamicKeySubspace(prefix, dyn.Encoding); + } + + // no encoding + return new KeySubspace(prefix); + } + + /// Create a copy of a generic subspace, sharing the same binary prefix + [Pure, NotNull] + public static DynamicKeySubspace Copy([NotNull] IKeySubspace subspace, IKeyEncoding encoding) + { + Contract.NotNull(subspace, nameof(subspace)); + Contract.NotNull(encoding, nameof(encoding)); + return new DynamicKeySubspace(StealPrefix(subspace), encoding); + } + + /// Create a copy of a generic subspace, sharing the same binary prefix + [Pure, NotNull] + public static DynamicKeySubspace Copy([NotNull] IKeySubspace subspace, IDynamicKeyEncoder encoder) + { + Contract.NotNull(subspace, nameof(subspace)); + Contract.NotNull(encoder, nameof(encoder)); + return new DynamicKeySubspace(StealPrefix(subspace), encoder); + } + + /// Create a copy of a dynamic subspace, sharing the same binary prefix and encoder + [Pure, NotNull] + public static DynamicKeySubspace Copy([NotNull] IDynamicKeySubspace subspace) + { + Contract.NotNull(subspace, nameof(subspace)); + return new DynamicKeySubspace(StealPrefix(subspace), subspace.Encoding); + } + + /// Create a copy of a typed subspace, sharing the same binary prefix and encoder + [Pure, NotNull] + public static TypedKeySubspace Copy([NotNull] ITypedKeySubspace subspace) + { + Contract.NotNull(subspace, nameof(subspace)); + return new TypedKeySubspace(StealPrefix(subspace), subspace.KeyEncoder); + } + + /// Create a copy of a typed subspace, sharing the same binary prefix and encoder + [Pure, NotNull] + public static TypedKeySubspace Copy([NotNull] ITypedKeySubspace subspace) + { + Contract.NotNull(subspace, nameof(subspace)); + return new TypedKeySubspace(StealPrefix(subspace), subspace.KeyEncoder); + } + + /// Create a copy of a typed subspace, sharing the same binary prefix and encoder + [Pure, NotNull] + public static TypedKeySubspace Copy([NotNull] ITypedKeySubspace subspace) + { + Contract.NotNull(subspace, nameof(subspace)); + return new TypedKeySubspace(StealPrefix(subspace), subspace.KeyEncoder); + } + + /// Create a copy of a typed subspace, sharing the same binary prefix and encoder + [Pure, NotNull] + public static TypedKeySubspace Copy([NotNull] ITypedKeySubspace subspace) + { + Contract.NotNull(subspace, nameof(subspace)); + return new TypedKeySubspace(StealPrefix(subspace), subspace.KeyEncoder); + } + + #endregion + internal KeySubspace(Slice prefix) { this.Key = prefix; diff --git a/FoundationDB.Client/Subspaces/KeySubspaceExtensions.cs b/FoundationDB.Client/Subspaces/KeySubspaceExtensions.cs index 3a118cf52..7afb018e9 100644 --- a/FoundationDB.Client/Subspaces/KeySubspaceExtensions.cs +++ b/FoundationDB.Client/Subspaces/KeySubspaceExtensions.cs @@ -46,6 +46,7 @@ public static class KeySubspaceExtensions /// Subspace equivalent to , but augmented with a specific TypeSystem [Pure, NotNull] public static IDynamicKeySubspace Using([NotNull] this IKeySubspace subspace, [NotNull] IKeyEncoding encoding) + //REVIEW: rename to AsDynamic() ? ToDynamic() ? would all to make encoding arg optional (and default to Tuples) { Contract.NotNull(subspace, nameof(subspace)); Contract.NotNull(encoding, nameof(encoding)); @@ -53,7 +54,7 @@ public static IDynamicKeySubspace Using([NotNull] this IKeySubspace subspace, [N } /// Return a version of this subspace, which uses a different type system to produces the keys and values - /// Instance of a generic subspace + /// Instance of a generic subspace to extend /// Custom key encoder /// Subspace equivalent to , but augmented with a specific TypeSystem [Pure, NotNull] @@ -65,19 +66,18 @@ public static IDynamicKeySubspace UsingEncoder([NotNull] this IKeySubspace subsp } /// Return a version of this subspace, which uses a different type system to produces the keys and values - /// Instance of a generic subspace - /// Custom key encoder + /// Instance of a generic subspace to extend + /// Encoding by the keys of this subspace. If not specified, the Tuples Type System will be used to generate an encoder. /// Subspace equivalent to , but augmented with a specific TypeSystem [Pure, NotNull] - public static ITypedKeySubspace UsingEncoder([NotNull] this IKeySubspace subspace, [NotNull] IKeyEncoding encoding) + public static ITypedKeySubspace UsingEncoder([NotNull] this IKeySubspace subspace, [CanBeNull] IKeyEncoding encoding = null) { Contract.NotNull(subspace, nameof(subspace)); - Contract.NotNull(encoding, nameof(encoding)); - return new TypedKeySubspace(subspace.GetPrefix(), encoding.GetEncoder()); + return new TypedKeySubspace(subspace.GetPrefix(), (encoding ?? TypeSystem.Tuples).GetEncoder()); } /// Return a version of this subspace, which uses a different type system to produces the keys and values - /// Instance of a generic subspace + /// Instance of a generic subspace to extend /// Custom key encoder /// Subspace equivalent to , but augmented with a specific TypeSystem [Pure, NotNull] @@ -89,19 +89,18 @@ public static ITypedKeySubspace UsingEncoder([NotNull] this IKeySubspace s } /// Return a version of this subspace, which uses a different type system to produces the keys and values - /// Instance of a generic subspace - /// Custom key encoder + /// Instance of a generic subspace to extend + /// Encoding used by the keys of this subspace. If not specified, the Tuples Type System will be used to generate an encoder. /// Subspace equivalent to , but augmented with a specific TypeSystem [Pure, NotNull] - public static ITypedKeySubspace UsingEncoder([NotNull] this IKeySubspace subspace, [NotNull] IKeyEncoding encoding) + public static ITypedKeySubspace UsingEncoder([NotNull] this IKeySubspace subspace, [CanBeNull] IKeyEncoding encoding = null) { Contract.NotNull(subspace, nameof(subspace)); - Contract.NotNull(encoding, nameof(encoding)); - return new TypedKeySubspace(subspace.GetPrefix(), encoding.GetEncoder()); + return new TypedKeySubspace(subspace.GetPrefix(), (encoding ?? TypeSystem.Tuples).GetEncoder()); } /// Return a version of this subspace, which uses a different type system to produces the keys and values - /// Instance of a generic subspace + /// Instance of a generic subspace to extend /// Custom key encoder /// Subspace equivalent to , but augmented with a specific TypeSystem [Pure, NotNull] @@ -113,19 +112,18 @@ public static ITypedKeySubspace UsingEncoder([NotNull] this IKey } /// Return a version of this subspace, which uses a different type system to produces the keys and values - /// Instance of a generic subspace - /// Custom key encoder + /// Instance of a generic subspace to extend + /// Encoding used by the keys of this subspace. If not specified, the Tuples Type System will be used to generate an encoder. /// Subspace equivalent to , but augmented with a specific TypeSystem [Pure, NotNull] - public static ITypedKeySubspace UsingEncoder([NotNull] this IKeySubspace subspace, [NotNull] IKeyEncoding encoding) + public static ITypedKeySubspace UsingEncoder([NotNull] this IKeySubspace subspace, [CanBeNull] IKeyEncoding encoding = null) { Contract.NotNull(subspace, nameof(subspace)); - Contract.NotNull(encoding, nameof(encoding)); - return new TypedKeySubspace(subspace.GetPrefix(), encoding.GetEncoder()); + return new TypedKeySubspace(subspace.GetPrefix(), (encoding ?? TypeSystem.Tuples).GetEncoder()); } /// Return a version of this subspace, which uses a different type system to produces the keys and values - /// Instance of a generic subspace + /// Instance of a generic subspace to extend /// Custom key encoder /// Subspace equivalent to , but augmented with a specific TypeSystem [Pure, NotNull] @@ -138,19 +136,18 @@ public static ITypedKeySubspace UsingEncoder([NotNull] t /// Return a version of this subspace, which uses a different type system to produces the keys and values /// Instance of a generic subspace - /// Custom key encoder + /// Encoding used by the keys of this namespace. If not specified, the Tuples Type System will be used to generate an encoder. /// Subspace equivalent to , but augmented with a specific TypeSystem [Pure, NotNull] - public static ITypedKeySubspace UsingEncoder([NotNull] this IKeySubspace subspace, [NotNull] IKeyEncoding encoding) + public static ITypedKeySubspace UsingEncoder([NotNull] this IKeySubspace subspace, [CanBeNull] IKeyEncoding encoding = null) { Contract.NotNull(subspace, nameof(subspace)); - Contract.NotNull(encoding, nameof(encoding)); - return new TypedKeySubspace(subspace.GetPrefix(), encoding.GetEncoder()); + return new TypedKeySubspace(subspace.GetPrefix(), (encoding ?? TypeSystem.Tuples).GetEncoder()); } /// Return a version of this subspace, which uses a different type system to produces the keys and values /// Instance of a generic subspace - /// Custom key encoder + /// Encoder used to serialize the keys of this namespace. /// Subspace equivalent to , but augmented with a specific TypeSystem [Pure, NotNull] public static ITypedKeySubspace UsingEncoder([NotNull] this IKeySubspace subspace, [NotNull] ICompositeKeyEncoder encoder) diff --git a/FoundationDB.Client/Subspaces/TypedKeySubspace`1.cs b/FoundationDB.Client/Subspaces/TypedKeySubspace`1.cs index 5a1ae7279..075760b9e 100644 --- a/FoundationDB.Client/Subspaces/TypedKeySubspace`1.cs +++ b/FoundationDB.Client/Subspaces/TypedKeySubspace`1.cs @@ -29,11 +29,11 @@ DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY namespace FoundationDB.Client { using System; + using System.Diagnostics; + using System.Runtime.CompilerServices; using Doxense.Collections.Tuples; using Doxense.Diagnostics.Contracts; - using Doxense.Memory; using Doxense.Serialization.Encoders; - using FoundationDB.Layers.Directories; using JetBrains.Annotations; [PublicAPI] @@ -69,6 +69,7 @@ internal TypedKeySubspace(Slice prefix, [NotNull] IKeyEncoder encoder) /// Encodes and Decodes keys composed of a single element /// Type of the key handled by this subspace + [DebuggerDisplay("{Parent.ToString(),nq)}")] public sealed class TypedKeys { @@ -87,25 +88,78 @@ internal TypedKeys( this.Encoder = encoder; } + #region ToRange() + + /// Return the range of all legal keys in this subpsace + /// A "legal" key is one that can be decoded into the original pair of values + [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] + public KeyRange ToRange() + { + return this.Parent.ToRange(); + } + + /// Return the range of all legal keys in this subpsace, that start with the specified value + /// Range that encompass all keys that start with (tuple.Item1, ..) + [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] + public KeyRange ToRange(STuple tuple) + { + return ToRange(tuple.Item1); + } + + /// Return the range of all legal keys in this subpsace, that start with the specified value + /// Range that encompass all keys that start with (tuple.Item1, ..) + [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] + public KeyRange ToRange(ValueTuple tuple) + { + return ToRange(tuple.Item1); + } + + [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public KeyRange ToRange(T1 item1) { - //HACKHACK: add concept of "range" on IKeyEncoder ? - var prefix = Encode(item1); - return KeyRange.PrefixedBy(prefix); + //TODO: add concept of "range" on IKeyEncoder ? + return KeyRange.PrefixedBy(Encode(item1)); } - [Pure] + #endregion + + #region Pack() + + public Slice this[ValueTuple items] + { + [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Encode(items.Item1); + } + + [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public Slice Pack(STuple tuple) { return Encode(tuple.Item1); } - [Pure] - public Slice Pack([NotNull] ITuple tuple) + [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] + public Slice Pack(ValueTuple tuple) + { + return Encode(tuple.Item1); + } + + [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] + public Slice Pack([NotNull] TTuple tuple) + where TTuple : ITuple { return Encode(tuple.OfSize(1).Get(0)); } + #endregion + + #region Encode() + + public Slice this[T1 item1] + { + [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Encode(item1); + } + [Pure] public Slice Encode(T1 item1) { @@ -115,6 +169,10 @@ public Slice Encode(T1 item1) return sw.ToSlice(); } + #endregion + + #region Decode() + [Pure] public T1 Decode(Slice packedKey) { @@ -126,6 +184,10 @@ public void Decode(Slice packedKey, out T1 item1) item1 = this.Encoder.DecodeKey(this.Parent.ExtractKey(packedKey)); } + #endregion + + #region Dump() + /// Return a user-friendly string representation of a key of this subspace [Pure] public string Dump(Slice packedKey) @@ -144,7 +206,8 @@ public string Dump(Slice packedKey) } } - } + #endregion + } } diff --git a/FoundationDB.Client/Subspaces/TypedKeySubspace`2.cs b/FoundationDB.Client/Subspaces/TypedKeySubspace`2.cs index f4e064c04..85ce04d1b 100644 --- a/FoundationDB.Client/Subspaces/TypedKeySubspace`2.cs +++ b/FoundationDB.Client/Subspaces/TypedKeySubspace`2.cs @@ -26,11 +26,10 @@ DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY */ #endregion -//#define ENABLE_VALUETUPLES - namespace FoundationDB.Client { using System; + using System.Diagnostics; using System.Runtime.CompilerServices; using Doxense.Collections.Tuples; using Doxense.Diagnostics.Contracts; @@ -66,6 +65,7 @@ internal TypedKeySubspace(Slice prefix, [NotNull] ICompositeKeyEncoder e } + [DebuggerDisplay("{Parent.ToString(),nq)}")] public sealed class TypedKeys { @@ -84,6 +84,8 @@ internal TypedKeys( this.Encoder = encoder; } + #region ToRange() + /// Return the range of all legal keys in this subpsace /// A "legal" key is one that can be decoded into the original pair of values [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -110,13 +112,17 @@ public KeyRange ToRange((T1, T2) tuple) /// Return the range of all legal keys in this subpsace, that start with the specified pair of values /// Range that encompass all keys that start with (item1, item2, ..) + [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public KeyRange ToRange(T1 item1, T2 item2) { - //HACKHACK: add concept of "range" on IKeyEncoder ? - var prefix = Encode(item1, item2); - return KeyRange.PrefixedBy(prefix); + //TODO: add concept of "range" on IKeyEncoder ? + return KeyRange.PrefixedBy(Encode(item1, item2)); } + #endregion + + #region ToRangePartial() + /// Return the range of all legal keys in this subpsace, that start with the specified first item /// Range that encompass all keys that start with (tuple.Item1, ..) [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -133,101 +139,116 @@ public KeyRange ToRangePartial(T1 item1) return KeyRange.PrefixedBy(EncodePartial(item1)); } + #endregion + + #region Pack() + /// Pack a 2-tuple into a key in this subspace /// Pair of values /// Encoded key in this subspace [Pure] public Slice Pack(STuple tuple) { - //REVIEW: how could we better guess the capacity, depending on the values of T1/T2? - var sw = this.Parent.OpenWriter(24); - this.Encoder.WriteKeyPartsTo(ref sw, 2, ref tuple); - return sw.ToSlice(); + return Pack(tuple.ToValueTuple()); } -#if ENABLE_VALUETUPLES /// Pack a 2-tuple into a key in this subspace /// Pair of values /// Encoded key in this subspace - [Pure] - public Slice Pack(ValueTuple tuple) + [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] + public Slice Pack((T1, T2) tuple) { - return Encode(tuple.Item1, tuple.Item2); + //REVIEW: how could we better guess the capacity, depending on the values of T1/T2? + var sw = this.Parent.OpenWriter(24); + this.Encoder.WriteKeyPartsTo(ref sw, 2, ref tuple); + return sw.ToSlice(); } -#endif /// Pack a 2-tuple into a key in this subspace /// Tuple that must be of size 2 /// Encoded key in this subspace - [Pure] - public Slice Pack([NotNull] ITuple tuple) + [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] + public Slice Pack([NotNull] TTuple tuple) + where TTuple : ITuple { tuple.OfSize(2); return Encode(tuple.Get(0), tuple.Get(1)); } - /// Pack a partial key only containing the first item of a key - /// Tuple containing a single item - /// Encoded partial key, to be used for generationg key ranges or key selectors - [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] - public Slice PackPartial(STuple tuple) + #endregion + + #region Encode() + + public Slice this[T1 item1, T2 item2] { - return EncodePartial(tuple.Item1); + [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Encode(item1, item2); } - /// Pack a partial key only containing the first item of a key - /// Tuple containing a single item - /// Encoded partial key, to be used for generationg key ranges or key selectors - [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] - public Slice PackPartial(ValueTuple tuple) + public Slice this[(T1, T2) items] { - return EncodePartial(tuple.Item1); + [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Encode(items.Item1, items.Item2); } /// Encode a pair of values into a key in this subspace /// First part of the key /// Second part of the key /// Encoded key in this subspace - /// The key can be decoded back into its original components using or + /// The key can be decoded back into its original components using or [Pure] public Slice Encode(T1 item1, T2 item2) { var sw = this.Parent.OpenWriter(24); - var tuple = new STuple(item1, item2); + var tuple = (item1, item2); this.Encoder.WriteKeyPartsTo(ref sw, 2, ref tuple); return sw.ToSlice(); } + #endregion + + #region EncodePartial() + [Pure] public Slice EncodePartial(T1 item1) { var sw = this.Parent.OpenWriter(16); - var tuple = new STuple(item1, default(T2)); + var tuple = (item1, default(T2)); this.Encoder.WriteKeyPartsTo(ref sw, 1, ref tuple); return sw.ToSlice(); } + #endregion + + #region Decode() + [Pure] + //REVIEW: => Unpack()? //REVIEW: return ValueTuple<..> instead? (C#7) - public STuple Decode(Slice packedKey) //REVIEW: => Unpack() + public STuple Decode(Slice packedKey) { return this.Encoder.DecodeKey(this.Parent.ExtractKey(packedKey)); } - [Pure] - public T1 DecodePartial(Slice packedKey) + public void Decode(Slice packedKey, out T1 item1, out T2 item2) { - var parts = this.Encoder.DecodeKeyParts(1, packedKey); - return parts.Item1; + this.Encoder + .DecodeKey(this.Parent.ExtractKey(packedKey)) + .Deconstruct(out item1, out item2); } - public void Decode(Slice packedKey, out T1 item1, out T2 item2) + #endregion + + #region DecodePartial() + + [Pure] + public T1 DecodePartial(Slice packedKey) { - var tuple = this.Encoder.DecodeKey(this.Parent.ExtractKey(packedKey)); - item1 = tuple.Item1; - item2 = tuple.Item2; + return this.Encoder.DecodeKeyParts(1, packedKey).Item1; } + #endregion + /// Return a user-friendly string representation of a key of this subspace [Pure] public string Dump(Slice packedKey) diff --git a/FoundationDB.Client/Subspaces/TypedKeySubspace`3.cs b/FoundationDB.Client/Subspaces/TypedKeySubspace`3.cs index 6cd64b2c1..91593a3df 100644 --- a/FoundationDB.Client/Subspaces/TypedKeySubspace`3.cs +++ b/FoundationDB.Client/Subspaces/TypedKeySubspace`3.cs @@ -26,11 +26,10 @@ DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY */ #endregion -//#define ENABLE_VALUETUPLES - namespace FoundationDB.Client { using System; + using System.Diagnostics; using System.Runtime.CompilerServices; using Doxense.Collections.Tuples; using Doxense.Diagnostics.Contracts; @@ -65,6 +64,7 @@ internal TypedKeySubspace(Slice prefix, [NotNull] ICompositeKeyEncoder { @@ -83,6 +83,8 @@ internal TypedKeys( this.Encoder = encoder; } + #region ToRange() + /// Return the range of all legal keys in this subpsace /// A "legal" key is one that can be decoded into the original triple of values [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -102,7 +104,7 @@ public KeyRange ToRange(STuple tuple) /// Return the range of all legal keys in this subpsace, that start with the specified triple of values /// Range that encompass all keys that start with (tuple.Item1, tuple.Item2, tuple.Item3) [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] - public KeyRange ToRange(ValueTuple tuple) + public KeyRange ToRange((T1, T2, T3) tuple) { return ToRange(tuple.Item1, tuple.Item2, tuple.Item3); } @@ -115,6 +117,10 @@ public KeyRange ToRange(T1 item1, T2 item2, T3 item3) return KeyRange.PrefixedBy(Encode(item1, item2, item3)); } + #endregion + + #region ToRangePartial() + /// Return the range of all legal keys in this subpsace, that start with the specified triple of values /// Range that encompass all keys that start with (item1, item2, item3) public KeyRange ToRangePartial(STuple tuple) @@ -125,7 +131,7 @@ public KeyRange ToRangePartial(STuple tuple) /// Return the range of all legal keys in this subpsace, that start with the specified triple of values /// Range that encompass all keys that start with (item1, item2, item3) - public KeyRange ToRangePartial(ValueTuple tuple) + public KeyRange ToRangePartial((T1, T2) tuple) { //HACKHACK: add concept of "range" on IKeyEncoder ? return KeyRange.PrefixedBy(EncodePartial(tuple.Item1, tuple.Item2)); @@ -147,19 +153,21 @@ public KeyRange ToRangePartial(T1 item1) return KeyRange.PrefixedBy(EncodePartial(item1)); } + #endregion + + #region Pack() + [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public Slice Pack(STuple tuple) { return Encode(tuple.Item1, tuple.Item2, tuple.Item3); } -#if ENABLE_VALUETUPLES [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] - public Slice Pack(ValueTuple tuple) + public Slice Pack((T1, T2, T3) tuple) { return Encode(tuple.Item1, tuple.Item2, tuple.Item3); } -#endif [Pure] public Slice Pack(TTuple tuple) @@ -169,6 +177,22 @@ public Slice Pack(TTuple tuple) return Encode(tuple.Get(0), tuple.Get(1), tuple.Get(2)); } + #endregion + + #region Encode() + + public Slice this[T1 item1, T2 item2, T3 item3] + { + [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Encode(item1, item2, item3); + } + + public Slice this[(T1, T2, T3) items] + { + [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Encode(items.Item1, items.Item2, items.Item3); + } + [Pure] public Slice Encode(T1 item1, T2 item2, T3 item3) { @@ -182,7 +206,7 @@ public Slice Encode(T1 item1, T2 item2, T3 item3) public Slice EncodePartial(T1 item1, T2 item2) { var sw = this.Parent.OpenWriter(16); - var tuple = new STuple(item1, item2, default(T3)); + var tuple = (item1, item2, default(T3)); this.Encoder.WriteKeyPartsTo(ref sw, 2, ref tuple); return sw.ToSlice(); } @@ -191,12 +215,18 @@ public Slice EncodePartial(T1 item1, T2 item2) public Slice EncodePartial(T1 item1) { var sw = this.Parent.OpenWriter(16); - var tuple = new STuple(item1, default(T2), default(T3)); + var tuple = (item1, default(T2), default(T3)); this.Encoder.WriteKeyPartsTo(ref sw, 1, ref tuple); return sw.ToSlice(); } + #endregion + + #region Decode() + [Pure] + //REVIEW: => Unpack()? + //REVIEW: return ValueTuple<..> instead? (C#7) public STuple Decode(Slice packedKey) { return this.Encoder.DecodeKey(this.Parent.ExtractKey(packedKey)); @@ -204,12 +234,15 @@ public STuple Decode(Slice packedKey) public void Decode(Slice packedKey, out T1 item1, out T2 item2, out T3 item3) { - var tuple = this.Encoder.DecodeKey(this.Parent.ExtractKey(packedKey)); - item1 = tuple.Item1; - item2 = tuple.Item2; - item3 = tuple.Item3; + this.Encoder + .DecodeKey(this.Parent.ExtractKey(packedKey)) + .Deconstruct(out item1, out item2, out item3); } + #endregion + + #region Dump() + /// Return a user-friendly string representation of a key of this subspace [Pure] public string Dump(Slice packedKey) @@ -228,6 +261,8 @@ public string Dump(Slice packedKey) } } + #endregion + } } diff --git a/FoundationDB.Client/Subspaces/TypedKeySubspace`4.cs b/FoundationDB.Client/Subspaces/TypedKeySubspace`4.cs index 7cf2ee4b6..16b870e4a 100644 --- a/FoundationDB.Client/Subspaces/TypedKeySubspace`4.cs +++ b/FoundationDB.Client/Subspaces/TypedKeySubspace`4.cs @@ -26,15 +26,13 @@ DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY */ #endregion -//#define ENABLE_VALUETUPLES - namespace FoundationDB.Client { using System; + using System.Diagnostics; using System.Runtime.CompilerServices; using Doxense.Collections.Tuples; using Doxense.Diagnostics.Contracts; - using Doxense.Memory; using Doxense.Serialization.Encoders; using JetBrains.Annotations; @@ -65,6 +63,7 @@ internal TypedKeySubspace(Slice prefix, [NotNull] ICompositeKeyEncoder { @@ -83,6 +82,8 @@ internal TypedKeys( this.Encoder = encoder; } + #region ToRange() + /// Return the range of all legal keys in this subpsace /// A "legal" key is one that can be decoded into the original triple of values [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -102,7 +103,7 @@ public KeyRange ToRange(STuple tuple) /// Return the range of all legal keys in this subpsace, that start with the specified triple of values /// Range that encompass all keys that start with (tuple.Item1, tuple.Item2, tuple.Item3) [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] - public KeyRange ToRange(ValueTuple tuple) + public KeyRange ToRange((T1, T2, T3, T4) tuple) { return ToRange(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4); } @@ -115,6 +116,10 @@ public KeyRange ToRange(T1 item1, T2 item2, T3 item3, T4 item4) return KeyRange.PrefixedBy(Encode(item1, item2, item3, item4)); } + #endregion + + #region ToRangePartial() + /// Return the range of all legal keys in this subpsace, that start with the specified triple of values /// Range that encompass all keys that start with (item1, item2, item3) public KeyRange ToRangePartial(STuple tuple) @@ -125,7 +130,7 @@ public KeyRange ToRangePartial(STuple tuple) /// Return the range of all legal keys in this subpsace, that start with the specified triple of values /// Range that encompass all keys that start with (item1, item2, item3) - public KeyRange ToRangePartial(ValueTuple tuple) + public KeyRange ToRangePartial((T1, T2, T3) tuple) { //HACKHACK: add concept of "range" on IKeyEncoder ? return KeyRange.PrefixedBy(EncodePartial(tuple.Item1, tuple.Item2, tuple.Item3)); @@ -155,19 +160,33 @@ public KeyRange ToRangePartial(T1 item1) return KeyRange.PrefixedBy(EncodePartial(item1)); } + #endregion + + #region Pack() + + public Slice this[T1 item1, T2 item2, T3 item3, T4 item4] + { + [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Encode(item1, item2, item3, item4); + } + + public Slice this[(T1, T2, T3, T4) items] + { + [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Encode(items.Item1, items.Item2, items.Item3, items.Item4); + } + [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] public Slice Pack(STuple tuple) { return Encode(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4); } -#if ENABLE_VALUETUPLES [Pure, MethodImpl(MethodImplOptions.AggressiveInlining)] - public Slice Pack(ValueTuple tuple) + public Slice Pack((T1, T2, T3, T4) tuple) { return Encode(tuple.Item1, tuple.Item2, tuple.Item3, tuple.Item4); } -#endif [Pure] public Slice Pack(TTuple tuple) @@ -177,6 +196,10 @@ public Slice Pack(TTuple tuple) return Encode(tuple.Get(0), tuple.Get(1), tuple.Get(2), tuple.Get(3)); } + #endregion + + #region Encode() + [Pure] public Slice Encode(T1 item1, T2 item2, T3 item3, T4 item4) { @@ -186,11 +209,16 @@ public Slice Encode(T1 item1, T2 item2, T3 item3, T4 item4) return sw.ToSlice(); } + #endregion + + + #region EncodePartial() + [Pure] public Slice EncodePartial(T1 item1, T2 item2, T3 item3) { var sw = this.Parent.OpenWriter(24); - var tuple = new STuple(item1, item2, item3, default(T4)); + var tuple = (item1, item2, item3, default(T4)); this.Encoder.WriteKeyPartsTo(ref sw, 3, ref tuple); return sw.ToSlice(); } @@ -199,7 +227,7 @@ public Slice EncodePartial(T1 item1, T2 item2, T3 item3) public Slice EncodePartial(T1 item1, T2 item2) { var sw = this.Parent.OpenWriter(16); - var tuple = new STuple(item1, item2, default(T3), default(T4)); + var tuple = (item1, item2, default(T3), default(T4)); this.Encoder.WriteKeyPartsTo(ref sw, 1, ref tuple); return sw.ToSlice(); } @@ -208,12 +236,18 @@ public Slice EncodePartial(T1 item1, T2 item2) public Slice EncodePartial(T1 item1) { var sw = this.Parent.OpenWriter(16); - var tuple = new STuple(item1, default(T2), default(T3), default(T4)); + var tuple = (item1, default(T2), default(T3), default(T4)); this.Encoder.WriteKeyPartsTo(ref sw, 1, ref tuple); return sw.ToSlice(); } + #endregion + + #region Decode() + [Pure] + //REVIEW: => Unpack()? + //REVIEW: return ValueTuple<..> instead? (C#7) public STuple Decode(Slice packedKey) { return this.Encoder.DecodeKey(this.Parent.ExtractKey(packedKey)); @@ -221,13 +255,15 @@ public STuple Decode(Slice packedKey) public void Decode(Slice packedKey, out T1 item1, out T2 item2, out T3 item3, out T4 item4) { - var tuple = this.Encoder.DecodeKey(this.Parent.ExtractKey(packedKey)); - item1 = tuple.Item1; - item2 = tuple.Item2; - item3 = tuple.Item3; - item4 = tuple.Item4; + this.Encoder + .DecodeKey(this.Parent.ExtractKey(packedKey)) + .Deconstruct(out item1, out item2, out item3, out item4); } + #endregion + + #region Dump() + /// Return a user-friendly string representation of a key of this subspace [Pure] public string Dump(Slice packedKey) @@ -246,6 +282,8 @@ public string Dump(Slice packedKey) } } + #endregion + } } diff --git a/FoundationDB.Client/TypeSystem/Encoders/ICompositeKeyEncoder.cs b/FoundationDB.Client/TypeSystem/Encoders/ICompositeKeyEncoder.cs index 92d259548..8e5efa1cf 100644 --- a/FoundationDB.Client/TypeSystem/Encoders/ICompositeKeyEncoder.cs +++ b/FoundationDB.Client/TypeSystem/Encoders/ICompositeKeyEncoder.cs @@ -31,26 +31,51 @@ namespace Doxense.Serialization.Encoders using System; using Doxense.Collections.Tuples; using Doxense.Memory; + + public interface ICompositeKeyEncoder : IKeyEncoder<(T1, T2)> + { + /// Write some or all parts of a composite key + void WriteKeyPartsTo(ref SliceWriter writer, int count, ref (T1, T2) key); + + /// Read some or all parts of a composite key + void ReadKeyPartsFrom(ref SliceReader reader, int count, out (T1, T2) items); + } - /// Encoder for keys that are tuples - /// Type of tuple - public interface ICompositeKeyEncoder : IKeyEncoder - where TTuple : ITuple + public interface ICompositeKeyEncoder : IKeyEncoder<(T1, T2, T3)> { /// Write some or all parts of a composite key - void WriteKeyPartsTo(ref SliceWriter writer, int count, ref TTuple key); + void WriteKeyPartsTo(ref SliceWriter writer, int count, ref (T1, T2, T3) key); /// Read some or all parts of a composite key - void ReadKeyPartsFrom(ref SliceReader reader, int count, out TTuple items); + void ReadKeyPartsFrom(ref SliceReader reader, int count, out (T1, T2, T3) items); } - public interface ICompositeKeyEncoder : ICompositeKeyEncoder> { } + public interface ICompositeKeyEncoder : IKeyEncoder<(T1, T2, T3, T4)> + { + /// Write some or all parts of a composite key + void WriteKeyPartsTo(ref SliceWriter writer, int count, ref (T1, T2, T3, T4) key); - public interface ICompositeKeyEncoder : ICompositeKeyEncoder> { } + /// Read some or all parts of a composite key + void ReadKeyPartsFrom(ref SliceReader reader, int count, out (T1, T2, T3, T4) items); + } - public interface ICompositeKeyEncoder : ICompositeKeyEncoder> { } + public interface ICompositeKeyEncoder : IKeyEncoder<(T1, T2, T3, T4, T5)> + { + /// Write some or all parts of a composite key + void WriteKeyPartsTo(ref SliceWriter writer, int count, ref (T1, T2, T3, T4, T5) key); - public interface ICompositeKeyEncoder : ICompositeKeyEncoder> { } + /// Read some or all parts of a composite key + void ReadKeyPartsFrom(ref SliceReader reader, int count, out (T1, T2, T3, T4, T5) items); + } + + public interface ICompositeKeyEncoder : IKeyEncoder<(T1, T2, T3, T4, T5, T6)> + { + /// Write some or all parts of a composite key + void WriteKeyPartsTo(ref SliceWriter writer, int count, ref (T1, T2, T3, T4, T5, T6) key); + + /// Read some or all parts of a composite key + void ReadKeyPartsFrom(ref SliceReader reader, int count, out (T1, T2, T3, T4, T5, T6) items); + } public static partial class KeyEncoderExtensions { @@ -59,14 +84,14 @@ public static partial class KeyEncoderExtensions public static void WriteKeyTo(this ICompositeKeyEncoder encoder, ref SliceWriter writer, T1 value1, T2 value2) { - var tuple = new STuple(value1, value2); + var tuple = (value1, value2); encoder.WriteKeyPartsTo(ref writer, 2, ref tuple); } public static Slice EncodeKey(this ICompositeKeyEncoder encoder, T1 item1, T2 item2) { var writer = default(SliceWriter); - var tuple = new STuple(item1, item2); + var tuple = (item1, item2); encoder.WriteKeyPartsTo(ref writer, 2, ref tuple); return writer.ToSlice(); } @@ -82,7 +107,7 @@ public static Slice EncodeKey(this ICompositeKeyEncoder encoder, public static Slice EncodePartialKey(this ICompositeKeyEncoder encoder, T1 item1) { var writer = default(SliceWriter); - var tuple = new STuple(item1, default(T2)); + var tuple = (item1, default(T2)); encoder.WriteKeyPartsTo(ref writer, 1, ref tuple); return writer.ToSlice(); } @@ -91,12 +116,12 @@ public static Slice EncodePartialKey(this ICompositeKeyEncoder e { var writer = new SliceWriter(prefix.Count + 16); writer.WriteBytes(prefix); - var tuple = new STuple(item1, default(T2)); + var tuple = (item1, default(T2)); encoder.WriteKeyPartsTo(ref writer, 1, ref tuple); return writer.ToSlice(); } - public static Slice EncodeKeyParts(this ICompositeKeyEncoder encoder, int count, STuple items) + public static Slice EncodeKeyParts(this ICompositeKeyEncoder encoder, int count, (T1, T2) items) { var writer = default(SliceWriter); encoder.WriteKeyPartsTo(ref writer, count, ref items); @@ -106,7 +131,7 @@ public static Slice EncodeKeyParts(this ICompositeKeyEncoder enc public static STuple DecodeKey(this ICompositeKeyEncoder decoder, Slice encoded) { var reader = new SliceReader(encoded); - decoder.ReadKeyFrom(ref reader, out STuple items); + decoder.ReadKeyFrom(ref reader, out var items); //TODO: throw if extra bytes? return items; } @@ -114,7 +139,7 @@ public static STuple DecodeKey(this ICompositeKeyEncoder public static STuple DecodeKeyParts(this ICompositeKeyEncoder encoder, int count, Slice encoded) { var reader = new SliceReader(encoded); - encoder.ReadKeyPartsFrom(ref reader, count, out STuple items); + encoder.ReadKeyPartsFrom(ref reader, count, out var items); return items; } @@ -124,14 +149,14 @@ public static STuple DecodeKeyParts(this ICompositeKeyEncoder(this ICompositeKeyEncoder encoder, ref SliceWriter writer, T1 value1, T2 value2, T3 value3) { - var tuple = new STuple(value1, value2, value3); + var tuple = (value1, value2, value3); encoder.WriteKeyPartsTo(ref writer, 3, ref tuple); } public static Slice EncodeKey(this ICompositeKeyEncoder encoder, T1 item1, T2 item2, T3 item3) { var writer = default(SliceWriter); - var tuple = new STuple(item1, item2, item3); + var tuple = (item1, item2, item3); encoder.WriteKeyPartsTo(ref writer, 3, ref tuple); return writer.ToSlice(); } @@ -144,7 +169,7 @@ public static Slice EncodeKey(this ICompositeKeyEncoder return writer.ToSlice(); } - public static Slice EncodeKeyParts(this ICompositeKeyEncoder encoder, int count, STuple items) + public static Slice EncodeKeyParts(this ICompositeKeyEncoder encoder, int count, (T1, T2, T3) items) { var writer = default(SliceWriter); encoder.WriteKeyPartsTo(ref writer, count, ref items); @@ -154,7 +179,7 @@ public static Slice EncodeKeyParts(this ICompositeKeyEncoder DecodeKey(this ICompositeKeyEncoder decoder, Slice encoded) { var reader = new SliceReader(encoded); - decoder.ReadKeyFrom(ref reader, out STuple items); + decoder.ReadKeyFrom(ref reader, out var items); //TODO: throw if extra bytes? return items; } @@ -162,7 +187,7 @@ public static STuple DecodeKey(this ICompositeKeyEncoder public static STuple DecodeKeyParts(this ICompositeKeyEncoder encoder, int count, Slice encoded) { var reader = new SliceReader(encoded); - encoder.ReadKeyPartsFrom(ref reader, count, out STuple items); + encoder.ReadKeyPartsFrom(ref reader, count, out var items); return items; } @@ -172,14 +197,14 @@ public static STuple DecodeKeyParts(this ICompositeKeyEn public static void WriteKeyTo(this ICompositeKeyEncoder encoder, ref SliceWriter writer, T1 value1, T2 value2, T3 value3, T4 value4) { - var tuple = new STuple(value1, value2, value3, value4); + var tuple = (value1, value2, value3, value4); encoder.WriteKeyPartsTo(ref writer, 4, ref tuple); } public static Slice EncodeKey(this ICompositeKeyEncoder encoder, T1 item1, T2 item2, T3 item3, T4 item4) { var writer = default(SliceWriter); - var tuple = new STuple(item1, item2, item3, item4); + var tuple = (item1, item2, item3, item4); encoder.WriteKeyPartsTo(ref writer, 4, ref tuple); return writer.ToSlice(); } @@ -192,7 +217,7 @@ public static Slice EncodeKey(this ICompositeKeyEncoder(this ICompositeKeyEncoder encoder, int count, STuple items) + public static Slice EncodeKeyParts(this ICompositeKeyEncoder encoder, int count, (T1, T2, T3, T4) items) { var writer = default(SliceWriter); encoder.WriteKeyPartsTo(ref writer, count, ref items); @@ -202,7 +227,7 @@ public static Slice EncodeKeyParts(this ICompositeKeyEncoder DecodeKey(this ICompositeKeyEncoder decoder, Slice encoded) { var reader = new SliceReader(encoded); - decoder.ReadKeyFrom(ref reader, out STuple items); + decoder.ReadKeyFrom(ref reader, out var items); //TODO: throw if extra bytes? return items; } @@ -210,7 +235,7 @@ public static STuple DecodeKey(this ICompositeKe public static STuple DecodeKeyParts(this ICompositeKeyEncoder encoder, int count, Slice encoded) { var reader = new SliceReader(encoded); - encoder.ReadKeyPartsFrom(ref reader, count, out STuple items); + encoder.ReadKeyPartsFrom(ref reader, count, out var items); return items; } @@ -220,14 +245,14 @@ public static STuple DecodeKeyParts(this ICompos public static void WriteKeyTo(this ICompositeKeyEncoder encoder, ref SliceWriter writer, T1 value1, T2 value2, T3 value3, T4 value4, T5 value5) { - var tuple = new STuple(value1, value2, value3, value4, value5); + var tuple = (value1, value2, value3, value4, value5); encoder.WriteKeyPartsTo(ref writer, 5, ref tuple); } public static Slice EncodeKey(this ICompositeKeyEncoder encoder, T1 item1, T2 item2, T3 item3, T4 item4, T5 item5) { var writer = default(SliceWriter); - var tuple = new STuple(item1, item2, item3, item4, item5); + var tuple = (item1, item2, item3, item4, item5); encoder.WriteKeyPartsTo(ref writer, 5, ref tuple); return writer.ToSlice(); } @@ -240,7 +265,7 @@ public static Slice EncodeKey(this ICompositeKeyEncoder(this ICompositeKeyEncoder encoder, int count, STuple items) + public static Slice EncodeKeyParts(this ICompositeKeyEncoder encoder, int count, (T1, T2, T3, T4, T5) items) { var writer = default(SliceWriter); encoder.WriteKeyPartsTo(ref writer, count, ref items); @@ -250,7 +275,7 @@ public static Slice EncodeKeyParts(this ICompositeKeyEncoder public static STuple DecodeKey(this ICompositeKeyEncoder decoder, Slice encoded) { var reader = new SliceReader(encoded); - decoder.ReadKeyFrom(ref reader, out STuple items); + decoder.ReadKeyFrom(ref reader, out var items); //TODO: throw if extra bytes? return items; } @@ -258,7 +283,7 @@ public static STuple DecodeKey(this ICom public static STuple DecodeKeyParts(this ICompositeKeyEncoder encoder, int count, Slice encoded) { var reader = new SliceReader(encoded); - encoder.ReadKeyPartsFrom(ref reader, count, out STuple items); + encoder.ReadKeyPartsFrom(ref reader, count, out var items); return items; } diff --git a/FoundationDB.Client/TypeSystem/Encoders/IDynamicKeyEncoder.cs b/FoundationDB.Client/TypeSystem/Encoders/IDynamicKeyEncoder.cs index 4643a12c2..a4d9b3e9c 100644 --- a/FoundationDB.Client/TypeSystem/Encoders/IDynamicKeyEncoder.cs +++ b/FoundationDB.Client/TypeSystem/Encoders/IDynamicKeyEncoder.cs @@ -39,7 +39,8 @@ public interface IDynamicKeyEncoder { /// Return the parent key encoding - IKeyEncoding Encoding {[NotNull] get; } + [NotNull] + IKeyEncoding Encoding { get; } #region Encoding... @@ -178,6 +179,7 @@ public interface IDynamicKeyEncoder /// Binary slice produced by a previous call to or /// Tuple containing two elements, or an exception if the data is invalid, or the tuples has less or more than two elements STuple DecodeKey(Slice packed); + //REVIEW: return ValueTuple instead? /// Decode a binary slice containing exactly three elements /// Expected type of the first element @@ -186,6 +188,7 @@ public interface IDynamicKeyEncoder /// Binary slice produced by a previous call to or /// Tuple containing three elements, or an exception if the data is invalid, or the tuples has less or more than three elements STuple DecodeKey(Slice packed); + //REVIEW: return ValueTuple instead? /// Decode a binary slice containing exactly four elements /// Expected type of the first element @@ -195,6 +198,7 @@ public interface IDynamicKeyEncoder /// Binary slice produced by a previous call to or /// Tuple containing four elements, or an exception if the data is invalid, or the tuples has less or more than four elements STuple DecodeKey(Slice packed); + //REVIEW: return ValueTuple instead? /// Decode a binary slice containing exactly five elements /// Expected type of the first element @@ -205,6 +209,7 @@ public interface IDynamicKeyEncoder /// Binary slice produced by a previous call to or /// Tuple containing five elements, or an exception if the data is invalid, or the tuples has less or more than five elements STuple DecodeKey(Slice packed); + //REVIEW: return ValueTuple instead? /// Decode a binary slice containing exactly six elements /// Expected type of the first element @@ -216,6 +221,7 @@ public interface IDynamicKeyEncoder /// Binary slice produced by a previous call to or /// Tuple containing five elements, or an exception if the data is invalid, or the tuples has less or more than five elements STuple DecodeKey(Slice packed); + //REVIEW: return ValueTuple instead? #endregion diff --git a/FoundationDB.Client/TypeSystem/Encoders/IKeyEncoder.cs b/FoundationDB.Client/TypeSystem/Encoders/IKeyEncoder.cs index b355227f9..ec9c3fbde 100644 --- a/FoundationDB.Client/TypeSystem/Encoders/IKeyEncoder.cs +++ b/FoundationDB.Client/TypeSystem/Encoders/IKeyEncoder.cs @@ -30,8 +30,17 @@ namespace Doxense.Serialization.Encoders { using System; using Doxense.Memory; + using JetBrains.Annotations; - public interface IKeyEncoder + /// Base interface for all key encoders + public interface IKeyEncoder + { + /// Parent encoding + [NotNull] + IKeyEncoding Encoding { get; } + } + + public interface IKeyEncoder : IKeyEncoder { /// Encode a single value void WriteKeyTo(ref SliceWriter writer, T1 value); @@ -43,14 +52,14 @@ public interface IKeyEncoder public static partial class KeyEncoderExtensions { - public static Slice EncodeKey(this IKeyEncoder encoder, T1 value) + public static Slice EncodeKey([NotNull] this IKeyEncoder encoder, T1 value) { var writer = default(SliceWriter); encoder.WriteKeyTo(ref writer, value); return writer.ToSlice(); } - public static Slice EncodeKey(this IKeyEncoder encoder, Slice prefix, T1 value) + public static Slice EncodeKey([NotNull] this IKeyEncoder encoder, Slice prefix, T1 value) { var writer = new SliceWriter(prefix.Count + 16); // ~16 bytes si T1 = Guid writer.WriteBytes(prefix); @@ -58,7 +67,7 @@ public static Slice EncodeKey(this IKeyEncoder encoder, Slice prefix, T1 return writer.ToSlice(); } - public static T1 DecodeKey(this IKeyEncoder decoder, Slice encoded) + public static T1 DecodeKey([NotNull] this IKeyEncoder decoder, Slice encoded) { var reader = new SliceReader(encoded); decoder.ReadKeyFrom(ref reader, out T1 item); diff --git a/FoundationDB.Client/TypeSystem/Encoders/KeyValueEncoders.Ordered.cs b/FoundationDB.Client/TypeSystem/Encoders/KeyValueEncoders.Ordered.cs index 2cdf95c40..efeab16ff 100644 --- a/FoundationDB.Client/TypeSystem/Encoders/KeyValueEncoders.Ordered.cs +++ b/FoundationDB.Client/TypeSystem/Encoders/KeyValueEncoders.Ordered.cs @@ -1,4 +1,31 @@ - +#region BSD Licence +/* Copyright (c) 2013-2018, Doxense SAS +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Doxense nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#endregion + namespace Doxense.Serialization.Encoders { using JetBrains.Annotations; @@ -12,6 +39,7 @@ public static partial class KeyValueEncoders { /// Encoders that produce lexicographically ordered slices, suitable for keys where lexicographical ordering is required + [PublicAPI] public static class Ordered { [NotNull] @@ -32,7 +60,7 @@ public static class Ordered [NotNull] public static IKeyEncoder GuidEncoder => Tuples.Key(); - public sealed class OrderedKeyEncoder : IKeyEncoder + public sealed class OrderedKeyEncoder : IKeyEncoder, IKeyEncoding { private readonly IOrderedTypeCodec m_codec; @@ -52,9 +80,30 @@ public void ReadKeyFrom(ref SliceReader reader, out T key) { key = m_codec.DecodeOrdered(reader.ReadToEnd()); } + + public IKeyEncoding Encoding => this; + + #region IKeyEncoding... + + IDynamicKeyEncoder IKeyEncoding.GetDynamicEncoder() => throw new NotSupportedException(); + + IKeyEncoder IKeyEncoding.GetEncoder() + { + if (typeof(T1) != typeof(T)) throw new NotSupportedException(); + return (IKeyEncoder) (object) this; + } + + ICompositeKeyEncoder IKeyEncoding.GetEncoder() => throw new NotSupportedException(); + + ICompositeKeyEncoder IKeyEncoding.GetEncoder() => throw new NotSupportedException(); + + ICompositeKeyEncoder IKeyEncoding.GetEncoder() => throw new NotSupportedException(); + + #endregion + } - public sealed class CodecCompositeKeyEncoder : CompositeKeyEncoder + public sealed class CodecCompositeKeyEncoder : CompositeKeyEncoder, IKeyEncoding { private readonly IOrderedTypeCodec m_codec1; private readonly IOrderedTypeCodec m_codec2; @@ -65,25 +114,45 @@ public CodecCompositeKeyEncoder(IOrderedTypeCodec codec1, IOrderedTypeCodec< m_codec2 = codec2; } - public override void WriteKeyPartsTo(ref SliceWriter writer, int count, ref STuple items) + public override void WriteKeyPartsTo(ref SliceWriter writer, int count, ref (T1, T2) items) { Contract.Requires(count > 0); if (count >= 1) m_codec1.EncodeOrderedSelfTerm(ref writer, items.Item1); if (count >= 2) m_codec2.EncodeOrderedSelfTerm(ref writer, items.Item2); } - public override void ReadKeyPartsFrom(ref SliceReader reader, int count, out STuple items) + public override void ReadKeyPartsFrom(ref SliceReader reader, int count, out (T1, T2) items) { Contract.Requires(count > 0); - T1 key1 = count >= 1 ? m_codec1.DecodeOrderedSelfTerm(ref reader) : default(T1); - T2 key2 = count >= 2 ? m_codec2.DecodeOrderedSelfTerm(ref reader) : default(T2); + items.Item1 = count >= 1 ? m_codec1.DecodeOrderedSelfTerm(ref reader) : default; + items.Item2 = count >= 2 ? m_codec2.DecodeOrderedSelfTerm(ref reader) : default; if (reader.HasMore) throw new InvalidOperationException($"Unexpected data at the end of composite key after {count} items"); - items = new STuple(key1, key2); } + + public override IKeyEncoding Encoding => this; + + #region IKeyEncoding... + + IDynamicKeyEncoder IKeyEncoding.GetDynamicEncoder() => throw new NotSupportedException(); + + IKeyEncoder IKeyEncoding.GetEncoder() => throw new NotSupportedException(); + + ICompositeKeyEncoder IKeyEncoding.GetEncoder() + { + if (typeof(T1B) != typeof(T1) && typeof(T2B) != typeof(T2)) throw new NotSupportedException(); + return (ICompositeKeyEncoder) (object) this; + } + + ICompositeKeyEncoder IKeyEncoding.GetEncoder() => throw new NotSupportedException(); + + ICompositeKeyEncoder IKeyEncoding.GetEncoder() => throw new NotSupportedException(); + + #endregion + } - public sealed class CodecCompositeKeyEncoder : CompositeKeyEncoder + public sealed class CodecCompositeKeyEncoder : CompositeKeyEncoder, IKeyEncoding { private readonly IOrderedTypeCodec m_codec1; private readonly IOrderedTypeCodec m_codec2; @@ -96,7 +165,7 @@ public CodecCompositeKeyEncoder(IOrderedTypeCodec codec1, IOrderedTypeCodec< m_codec3 = codec3; } - public override void WriteKeyPartsTo(ref SliceWriter writer, int count, ref STuple items) + public override void WriteKeyPartsTo(ref SliceWriter writer, int count, ref (T1, T2, T3) items) { Contract.Requires(count > 0 && count <= 3); if (count >= 1) m_codec1.EncodeOrderedSelfTerm(ref writer, items.Item1); @@ -104,17 +173,90 @@ public override void WriteKeyPartsTo(ref SliceWriter writer, int count, ref STup if (count >= 3) m_codec3.EncodeOrderedSelfTerm(ref writer, items.Item3); } - public override void ReadKeyPartsFrom(ref SliceReader reader, int count, out STuple items) + public override void ReadKeyPartsFrom(ref SliceReader reader, int count, out (T1, T2, T3) items) { Contract.Requires(count > 0); - T1 key1 = count >= 1 ? m_codec1.DecodeOrderedSelfTerm(ref reader) : default(T1); - T2 key2 = count >= 2 ? m_codec2.DecodeOrderedSelfTerm(ref reader) : default(T2); - T3 key3 = count >= 3 ? m_codec3.DecodeOrderedSelfTerm(ref reader) : default(T3); + items.Item1 = count >= 1 ? m_codec1.DecodeOrderedSelfTerm(ref reader) : default; + items.Item2 = count >= 2 ? m_codec2.DecodeOrderedSelfTerm(ref reader) : default; + items.Item3 = count >= 3 ? m_codec3.DecodeOrderedSelfTerm(ref reader) : default; if (reader.HasMore) throw new InvalidOperationException($"Unexpected data at the end of composite key after {count} items"); - items = new STuple(key1, key2, key3); } + public override IKeyEncoding Encoding => this; + + #region IKeyEncoding... + + IDynamicKeyEncoder IKeyEncoding.GetDynamicEncoder() => throw new NotSupportedException(); + + IKeyEncoder IKeyEncoding.GetEncoder() => throw new NotSupportedException(); + + ICompositeKeyEncoder IKeyEncoding.GetEncoder() => throw new NotSupportedException(); + + ICompositeKeyEncoder IKeyEncoding.GetEncoder() + { + if (typeof(T1B) != typeof(T1) && typeof(T2B) != typeof(T2) && typeof(T3B) != typeof(T3)) throw new NotSupportedException(); + return (ICompositeKeyEncoder) (object) this; + } + + ICompositeKeyEncoder IKeyEncoding.GetEncoder() => throw new NotSupportedException(); + + #endregion + } + + public sealed class CodecCompositeKeyEncoder : CompositeKeyEncoder, IKeyEncoding + { + private readonly IOrderedTypeCodec m_codec1; + private readonly IOrderedTypeCodec m_codec2; + private readonly IOrderedTypeCodec m_codec3; + private readonly IOrderedTypeCodec m_codec4; + + public CodecCompositeKeyEncoder(IOrderedTypeCodec codec1, IOrderedTypeCodec codec2, IOrderedTypeCodec codec3, IOrderedTypeCodec codec4) + { + m_codec1 = codec1; + m_codec2 = codec2; + m_codec3 = codec3; + m_codec4 = codec4; + } + + public override void WriteKeyPartsTo(ref SliceWriter writer, int count, ref (T1, T2, T3, T4) items) + { + Contract.Requires(count > 0 && count <= 4); + if (count >= 1) m_codec1.EncodeOrderedSelfTerm(ref writer, items.Item1); + if (count >= 2) m_codec2.EncodeOrderedSelfTerm(ref writer, items.Item2); + if (count >= 3) m_codec3.EncodeOrderedSelfTerm(ref writer, items.Item3); + if (count >= 4) m_codec4.EncodeOrderedSelfTerm(ref writer, items.Item4); + } + + public override void ReadKeyPartsFrom(ref SliceReader reader, int count, out (T1, T2, T3, T4) items) + { + Contract.Requires(count > 0); + items.Item1 = count >= 1 ? m_codec1.DecodeOrderedSelfTerm(ref reader) : default; + items.Item2 = count >= 2 ? m_codec2.DecodeOrderedSelfTerm(ref reader) : default; + items.Item3 = count >= 3 ? m_codec3.DecodeOrderedSelfTerm(ref reader) : default; + items.Item4 = count >= 4 ? m_codec4.DecodeOrderedSelfTerm(ref reader) : default; + if (reader.HasMore) throw new InvalidOperationException($"Unexpected data at the end of composite key after {count} items"); + } + + public override IKeyEncoding Encoding => this; + + #region IKeyEncoding... + + IDynamicKeyEncoder IKeyEncoding.GetDynamicEncoder() => throw new NotSupportedException(); + + IKeyEncoder IKeyEncoding.GetEncoder() => throw new NotSupportedException(); + + ICompositeKeyEncoder IKeyEncoding.GetEncoder() => throw new NotSupportedException(); + + ICompositeKeyEncoder IKeyEncoding.GetEncoder() => throw new NotSupportedException(); + + ICompositeKeyEncoder IKeyEncoding.GetEncoder() + { + if (typeof(T1B) != typeof(T1) && typeof(T2B) != typeof(T2) && typeof(T3B) != typeof(T3) && typeof(T4B) != typeof(T4)) throw new NotSupportedException(); + return (ICompositeKeyEncoder) (object) this; + } + + #endregion } /// Create a simple encoder from a codec diff --git a/FoundationDB.Client/TypeSystem/Encoders/KeyValueEncoders.Tuples.cs b/FoundationDB.Client/TypeSystem/Encoders/KeyValueEncoders.Tuples.cs index 9950c3069..baaa3177f 100644 --- a/FoundationDB.Client/TypeSystem/Encoders/KeyValueEncoders.Tuples.cs +++ b/FoundationDB.Client/TypeSystem/Encoders/KeyValueEncoders.Tuples.cs @@ -1,18 +1,46 @@ - +#region BSD Licence +/* Copyright (c) 2013-2018, Doxense SAS +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Doxense nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#endregion + namespace Doxense.Serialization.Encoders { - using JetBrains.Annotations; using System; using Doxense.Collections.Tuples; using Doxense.Collections.Tuples.Encoding; using Doxense.Diagnostics.Contracts; using Doxense.Memory; + using JetBrains.Annotations; /// Helper class for all key/value encoders public static partial class KeyValueEncoders { /// Encoders that use the Tuple Encoding, suitable for keys + [PublicAPI] public static class Tuples { @@ -22,6 +50,8 @@ internal class TupleEncoder : IKeyEncoder, IValueEncoder private TupleEncoder() { } + public IKeyEncoding Encoding => TypeSystem.Tuples; + public void WriteKeyTo(ref SliceWriter writer, T key) { TupleEncoder.WriteKeysTo(ref writer, key); @@ -30,18 +60,18 @@ public void WriteKeyTo(ref SliceWriter writer, T key) public void ReadKeyFrom(ref SliceReader reader, out T key) { key = !reader.HasMore - ? default(T) //BUGBUG + ? default //BUGBUG : TuPack.DecodeKey(reader.ReadToEnd()); } public Slice EncodeValue(T key) { - return TupleEncoder.EncodeKey(key); + return TupleEncoder.EncodeKey(default(Slice), key); } public T DecodeValue(Slice encoded) { - if (encoded.IsNullOrEmpty) return default(T); //BUGBUG + if (encoded.IsNullOrEmpty) return default; //BUGBUG return TuPack.DecodeKey(encoded); } @@ -54,7 +84,9 @@ internal class TupleCompositeEncoder : CompositeKeyEncoder private TupleCompositeEncoder() { } - public override void WriteKeyPartsTo(ref SliceWriter writer, int count, ref STuple key) + public override IKeyEncoding Encoding => TypeSystem.Tuples; + + public override void WriteKeyPartsTo(ref SliceWriter writer, int count, ref (T1, T2) key) { switch (count) { @@ -64,16 +96,14 @@ public override void WriteKeyPartsTo(ref SliceWriter writer, int count, ref STup } } - public override void ReadKeyPartsFrom(ref SliceReader reader, int count, out STuple key) + public override void ReadKeyPartsFrom(ref SliceReader reader, int count, out (T1, T2) key) { if (count != 1 & count != 2) throw new ArgumentOutOfRangeException(nameof(count), count, "Item count must be either 1 or 2"); var t = TuPack.Unpack(reader.ReadToEnd()).OfSize(count); Contract.Assert(t != null); - key = new STuple( - t.Get(0), - count == 2 ? t.Get(1) : default(T2) - ); + key.Item1 = t.Get(0); + key.Item2 = count == 2 ? t.Get(1) : default; } } @@ -84,7 +114,9 @@ internal class TupleCompositeEncoder : CompositeKeyEncoder key) + public override IKeyEncoding Encoding => TypeSystem.Tuples; + + public override void WriteKeyPartsTo(ref SliceWriter writer, int count, ref (T1, T2, T3) key) { switch (count) { @@ -95,17 +127,15 @@ public override void WriteKeyPartsTo(ref SliceWriter writer, int count, ref STup } } - public override void ReadKeyPartsFrom(ref SliceReader reader, int count, out STuple key) + public override void ReadKeyPartsFrom(ref SliceReader reader, int count, out (T1, T2, T3) key) { if (count < 1 | count > 3) throw new ArgumentOutOfRangeException(nameof(count), count, "Item count must be between 1 and 3"); var t = TuPack.Unpack(reader.ReadToEnd()).OfSize(count); Contract.Assert(t != null); - key = new STuple( - t.Get(0), - count >= 2 ? t.Get(1) : default(T2), - count >= 3 ? t.Get(2) : default(T3) - ); + key.Item1 = t.Get(0); + key.Item2 = count >= 2 ? t.Get(1) : default; + key.Item3 = count >= 3 ? t.Get(2) : default; } } @@ -116,7 +146,9 @@ internal class TupleCompositeEncoder : CompositeKeyEncoder key) + public override IKeyEncoding Encoding => TypeSystem.Tuples; + + public override void WriteKeyPartsTo(ref SliceWriter writer, int count, ref (T1, T2, T3, T4) key) { switch (count) { @@ -128,18 +160,16 @@ public override void WriteKeyPartsTo(ref SliceWriter writer, int count, ref STup } } - public override void ReadKeyPartsFrom(ref SliceReader reader, int count, out STuple key) + public override void ReadKeyPartsFrom(ref SliceReader reader, int count, out (T1, T2, T3, T4) key) { if (count < 1 || count > 4) throw new ArgumentOutOfRangeException(nameof(count), count, "Item count must be between 1 and 4"); var t = TuPack.Unpack(reader.ReadToEnd()).OfSize(count); Contract.Assert(t != null); - key = new STuple( - t.Get(0), - count >= 2 ? t.Get(1) : default(T2), - count >= 3 ? t.Get(2) : default(T3), - count >= 4 ? t.Get(3) : default(T4) - ); + key.Item1 = t.Get(0); + key.Item2 = count >= 2 ? t.Get(1) : default; + key.Item3 = count >= 3 ? t.Get(2) : default; + key.Item4 = count >= 4 ? t.Get(3) : default; } } @@ -150,7 +180,9 @@ internal class TupleCompositeEncoder : CompositeKeyEncoder key) + public override IKeyEncoding Encoding => TypeSystem.Tuples; + + public override void WriteKeyPartsTo(ref SliceWriter writer, int count, ref (T1, T2, T3, T4, T5) key) { switch (count) { @@ -163,21 +195,58 @@ public override void WriteKeyPartsTo(ref SliceWriter writer, int count, ref STup } } - public override void ReadKeyPartsFrom(ref SliceReader reader, int count, out STuple key) + public override void ReadKeyPartsFrom(ref SliceReader reader, int count, out (T1, T2, T3, T4, T5) key) { if (count < 1 || count > 5) throw new ArgumentOutOfRangeException(nameof(count), count, "Item count must be between 1 and 5"); var t = TuPack.Unpack(reader.ReadToEnd()).OfSize(count); Contract.Assert(t != null); - key = new STuple( - t.Get(0), - count >= 2 ? t.Get(1) : default(T2), - count >= 3 ? t.Get(2) : default(T3), - count >= 4 ? t.Get(3) : default(T4), - count >= 5 ? t.Get(4) : default(T5) - ); + key.Item1 = t.Get(0); + key.Item2 = count >= 2 ? t.Get(1) : default; + key.Item3 = count >= 3 ? t.Get(2) : default; + key.Item4 = count >= 4 ? t.Get(3) : default; + key.Item5 = count >= 5 ? t.Get(4) : default; } } + + internal class TupleCompositeEncoder : CompositeKeyEncoder + { + + public static readonly TupleCompositeEncoder Default = new TupleCompositeEncoder(); + + private TupleCompositeEncoder() { } + + public override IKeyEncoding Encoding => TypeSystem.Tuples; + + public override void WriteKeyPartsTo(ref SliceWriter writer, int count, ref (T1, T2, T3, T4, T5, T6) key) + { + switch (count) + { + case 6: TupleEncoder.WriteKeysTo(ref writer, key.Item1, key.Item2, key.Item3, key.Item4, key.Item5, key.Item6); break; + case 5: TupleEncoder.WriteKeysTo(ref writer, key.Item1, key.Item2, key.Item3, key.Item4, key.Item5); break; + case 4: TupleEncoder.WriteKeysTo(ref writer, key.Item1, key.Item2, key.Item3, key.Item4); break; + case 3: TupleEncoder.WriteKeysTo(ref writer, key.Item1, key.Item2, key.Item3); break; + case 2: TupleEncoder.WriteKeysTo(ref writer, key.Item1, key.Item2); break; + case 1: TupleEncoder.WriteKeysTo(ref writer, key.Item1); break; + default: throw new ArgumentOutOfRangeException(nameof(count), count, "Item count must be between 1 and 6"); + } + } + + public override void ReadKeyPartsFrom(ref SliceReader reader, int count, out (T1, T2, T3, T4, T5, T6) key) + { + if (count < 1 || count > 6) throw new ArgumentOutOfRangeException(nameof(count), count, "Item count must be between 1 and 6"); + + var t = TuPack.Unpack(reader.ReadToEnd()).OfSize(count); + Contract.Assert(t != null); + key.Item1 = t.Get(0); + key.Item2 = count >= 2 ? t.Get(1) : default; + key.Item3 = count >= 3 ? t.Get(2) : default; + key.Item4 = count >= 4 ? t.Get(3) : default; + key.Item5 = count >= 5 ? t.Get(4) : default; + key.Item6 = count >= 6 ? t.Get(5) : default; + } + } + #region Keys [NotNull] diff --git a/FoundationDB.Client/TypeSystem/Encoders/KeyValueEncoders.Unordered.cs b/FoundationDB.Client/TypeSystem/Encoders/KeyValueEncoders.Unordered.cs index 796fe6a4c..2cea05e1e 100644 --- a/FoundationDB.Client/TypeSystem/Encoders/KeyValueEncoders.Unordered.cs +++ b/FoundationDB.Client/TypeSystem/Encoders/KeyValueEncoders.Unordered.cs @@ -1,15 +1,43 @@ - +#region BSD Licence +/* Copyright (c) 2013-2018, Doxense SAS +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Doxense nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#endregion + namespace Doxense.Serialization.Encoders { - using JetBrains.Annotations; using System; using Doxense.Diagnostics.Contracts; + using JetBrains.Annotations; /// Helper class for all key/value encoders public static partial class KeyValueEncoders { /// Encoders that produce compact but unordered slices, suitable for keys that don't benefit from having lexicographical ordering + [PublicAPI] public static class Unordered { diff --git a/FoundationDB.Client/TypeSystem/Encoders/KeyValueEncoders.Values.cs b/FoundationDB.Client/TypeSystem/Encoders/KeyValueEncoders.Values.cs index cf8326c20..2c3ab0e05 100644 --- a/FoundationDB.Client/TypeSystem/Encoders/KeyValueEncoders.Values.cs +++ b/FoundationDB.Client/TypeSystem/Encoders/KeyValueEncoders.Values.cs @@ -1,49 +1,61 @@ - +#region BSD Licence +/* Copyright (c) 2013-2018, Doxense SAS +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Doxense nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#endregion + namespace Doxense.Serialization.Encoders { - using JetBrains.Annotations; using System; using Doxense.Diagnostics.Contracts; - using Doxense.Memory; + using JetBrains.Annotations; /// Helper class for all key/value encoders public static partial class KeyValueEncoders { /// Encoders that produce compact but unordered slices, suitable for values + [PublicAPI] public static class Values { private static readonly GenericEncoder s_default = new GenericEncoder(); - public static IValueEncoder BinaryEncoder - { - [NotNull] - get { return s_default; } - } + [NotNull] + public static IValueEncoder BinaryEncoder => s_default; - public static IValueEncoder StringEncoder - { - [NotNull] - get { return s_default; } - } + [NotNull] + public static IValueEncoder StringEncoder => s_default; - public static IValueEncoder Int32Encoder - { - [NotNull] - get { return s_default; } - } + [NotNull] + public static IValueEncoder Int32Encoder => s_default; - public static IValueEncoder Int64Encoder - { - [NotNull] - get { return s_default; } - } + [NotNull] + public static IValueEncoder Int64Encoder => s_default; - public static IValueEncoder GuidEncoder - { - [NotNull] - get { return s_default; } - } + [NotNull] + public static IValueEncoder GuidEncoder => s_default; /// Create a simple encoder from a codec [NotNull] diff --git a/FoundationDB.Client/TypeSystem/Encoders/KeyValueEncoders.cs b/FoundationDB.Client/TypeSystem/Encoders/KeyValueEncoders.cs index 552b2548d..213f46e80 100644 --- a/FoundationDB.Client/TypeSystem/Encoders/KeyValueEncoders.cs +++ b/FoundationDB.Client/TypeSystem/Encoders/KeyValueEncoders.cs @@ -35,7 +35,6 @@ namespace Doxense.Serialization.Encoders using Doxense.Collections.Tuples; using Doxense.Diagnostics.Contracts; using Doxense.Memory; - using Doxense.Serialization.Encoders; using JetBrains.Annotations; /// Helper class for all key/value encoders @@ -48,11 +47,15 @@ public static partial class KeyValueEncoders #region Nested Classes /// Identity encoder - public sealed class IdentityEncoder : IKeyEncoder, IValueEncoder + public sealed class IdentityEncoder : IKeyEncoder, IValueEncoder, IKeyEncoding { internal IdentityEncoder() { } + #region IKeyEncoder... + + public IKeyEncoding Encoding => this; + public void WriteKeyTo(ref SliceWriter writer, Slice key) { writer.WriteBytes(key); @@ -72,10 +75,27 @@ public Slice DecodeValue(Slice encoded) { return encoded; } + + #endregion + + IKeyEncoder IKeyEncoding.GetEncoder() + { + if (typeof(T1) != typeof(Slice)) throw new NotSupportedException(); + return (IKeyEncoder) (object) this; + } + + IDynamicKeyEncoder IKeyEncoding.GetDynamicEncoder() => throw new NotSupportedException(); + + ICompositeKeyEncoder IKeyEncoding.GetEncoder() => throw new NotSupportedException(); + + ICompositeKeyEncoder IKeyEncoding.GetEncoder() => throw new NotSupportedException(); + + ICompositeKeyEncoder IKeyEncoding.GetEncoder() => throw new NotSupportedException(); + } /// Wrapper for encoding and decoding a singleton with lambda functions - internal sealed class Singleton : IKeyEncoder, IValueEncoder + internal sealed class Singleton : IKeyEncoder, IValueEncoder, IKeyEncoding { private readonly Func m_encoder; private readonly Func m_decoder; @@ -113,23 +133,40 @@ public T DecodeValue(Slice encoded) return m_decoder(encoded); } + public IKeyEncoding Encoding => this; + + IKeyEncoder IKeyEncoding.GetEncoder() + { + if (typeof(T1) != typeof(T)) throw new NotSupportedException(); + return (IKeyEncoder) (object) this; + } + + IDynamicKeyEncoder IKeyEncoding.GetDynamicEncoder() => throw new NotSupportedException(); + + ICompositeKeyEncoder IKeyEncoding.GetEncoder() => throw new NotSupportedException(); + + ICompositeKeyEncoder IKeyEncoding.GetEncoder() => throw new NotSupportedException(); + + ICompositeKeyEncoder IKeyEncoding.GetEncoder() => throw new NotSupportedException(); } /// Wrapper for encoding and decoding a pair with lambda functions public abstract class CompositeKeyEncoder : ICompositeKeyEncoder { - public abstract void WriteKeyPartsTo(ref SliceWriter writer, int count, ref STuple items); + public abstract IKeyEncoding Encoding { get; } - public abstract void ReadKeyPartsFrom(ref SliceReader reader, int count, out STuple items); + public abstract void WriteKeyPartsTo(ref SliceWriter writer, int count, ref (T1, T2) items); + + public abstract void ReadKeyPartsFrom(ref SliceReader reader, int count, out (T1, T2) items); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteKeyTo(ref SliceWriter writer, STuple items) + public void WriteKeyTo(ref SliceWriter writer, (T1, T2) items) { WriteKeyPartsTo(ref writer, 2, ref items); } - public void ReadKeyFrom(ref SliceReader reader, out STuple items) + public void ReadKeyFrom(ref SliceReader reader, out (T1, T2) items) { ReadKeyPartsFrom(ref reader, 2, out items); } @@ -140,16 +177,18 @@ public void ReadKeyFrom(ref SliceReader reader, out STuple items) public abstract class CompositeKeyEncoder : ICompositeKeyEncoder { - public abstract void WriteKeyPartsTo(ref SliceWriter writer, int count, ref STuple items); + public abstract IKeyEncoding Encoding { get; } + + public abstract void WriteKeyPartsTo(ref SliceWriter writer, int count, ref (T1, T2, T3) items); - public abstract void ReadKeyPartsFrom(ref SliceReader reader, int count, out STuple items); + public abstract void ReadKeyPartsFrom(ref SliceReader reader, int count, out (T1, T2, T3) items); - public void WriteKeyTo(ref SliceWriter writer, STuple items) + public void WriteKeyTo(ref SliceWriter writer, (T1, T2, T3) items) { WriteKeyPartsTo(ref writer, 3, ref items); } - public void ReadKeyFrom(ref SliceReader reader, out STuple items) + public void ReadKeyFrom(ref SliceReader reader, out (T1, T2, T3) items) { ReadKeyPartsFrom(ref reader, 3, out items); } @@ -160,42 +199,68 @@ public void ReadKeyFrom(ref SliceReader reader, out STuple items) public abstract class CompositeKeyEncoder : ICompositeKeyEncoder { - public abstract void WriteKeyPartsTo(ref SliceWriter writer, int count, ref STuple items); + public abstract IKeyEncoding Encoding { get; } + + public abstract void WriteKeyPartsTo(ref SliceWriter writer, int count, ref (T1, T2, T3, T4) items); - public abstract void ReadKeyPartsFrom(ref SliceReader reader, int count, out STuple items); + public abstract void ReadKeyPartsFrom(ref SliceReader reader, int count, out (T1, T2, T3, T4) items); - public void WriteKeyTo(ref SliceWriter writer, STuple items) + public void WriteKeyTo(ref SliceWriter writer, (T1, T2, T3, T4) items) { WriteKeyPartsTo(ref writer, 4, ref items); } - public void ReadKeyFrom(ref SliceReader reader, out STuple items) + public void ReadKeyFrom(ref SliceReader reader, out (T1, T2, T3, T4) items) { ReadKeyPartsFrom(ref reader, 4, out items); } } - /// Wrapper for encoding and decoding a quad with lambda functions + /// Wrapper for encoding and decoding five items with lambda functions public abstract class CompositeKeyEncoder : ICompositeKeyEncoder { - public abstract void WriteKeyPartsTo(ref SliceWriter writer, int count, ref STuple items); + public abstract IKeyEncoding Encoding { get; } + + public abstract void WriteKeyPartsTo(ref SliceWriter writer, int count, ref (T1, T2, T3, T4, T5) items); - public abstract void ReadKeyPartsFrom(ref SliceReader reader, int count, out STuple items); + public abstract void ReadKeyPartsFrom(ref SliceReader reader, int count, out (T1, T2, T3, T4, T5) items); - public void WriteKeyTo(ref SliceWriter writer, STuple items) + public void WriteKeyTo(ref SliceWriter writer, (T1, T2, T3, T4, T5) items) { WriteKeyPartsTo(ref writer, 5, ref items); } - public void ReadKeyFrom(ref SliceReader reader, out STuple items) + public void ReadKeyFrom(ref SliceReader reader, out (T1, T2, T3, T4, T5) items) { ReadKeyPartsFrom(ref reader, 5, out items); } } + /// Wrapper for encoding and decoding six items with lambda functions + public abstract class CompositeKeyEncoder : ICompositeKeyEncoder + { + + public abstract IKeyEncoding Encoding { get; } + + public abstract void WriteKeyPartsTo(ref SliceWriter writer, int count, ref (T1, T2, T3, T4, T5, T6) items); + + public abstract void ReadKeyPartsFrom(ref SliceReader reader, int count, out (T1, T2, T3, T4, T5, T6) items); + + public void WriteKeyTo(ref SliceWriter writer, (T1, T2, T3, T4, T5, T6) items) + { + WriteKeyPartsTo(ref writer, 6, ref items); + } + + public void ReadKeyFrom(ref SliceReader reader, out (T1, T2, T3, T4, T5, T6) items) + { + ReadKeyPartsFrom(ref reader, 6, out items); + } + + } + #endregion #region Keys... diff --git a/FoundationDB.Client/Utils/TinyJsonParser.cs b/FoundationDB.Client/Utils/TinyJsonParser.cs index 399582aa8..b67014911 100644 --- a/FoundationDB.Client/Utils/TinyJsonParser.cs +++ b/FoundationDB.Client/Utils/TinyJsonParser.cs @@ -400,10 +400,10 @@ internal static string GetStringField(Dictionary map, string fie return map != null && map.TryGetValue(field, out item) ? (bool)item : default(bool?); } - internal static KeyValuePair GetStringPair(Dictionary map, string key, string value) + internal static (string Key, string Value) GetStringPair(Dictionary map, string key, string value) { object item; - return new KeyValuePair( + return ( map != null && map.TryGetValue(key, out item) ? (string)item : null, map != null && map.TryGetValue(value, out item) ? (string)item : null ); diff --git a/FoundationDB.Layers.Common/Collections/FdbMap`2.cs b/FoundationDB.Layers.Common/Collections/FdbMap`2.cs index 9cc4bdbcb..f4a7eadc2 100644 --- a/FoundationDB.Layers.Common/Collections/FdbMap`2.cs +++ b/FoundationDB.Layers.Common/Collections/FdbMap`2.cs @@ -91,7 +91,7 @@ public async Task GetAsync([NotNull] IFdbReadOnlyTransaction trans, TKey if (trans == null) throw new ArgumentNullException(nameof(trans)); if (id == null) throw new ArgumentNullException(nameof(id)); - var data = await trans.GetAsync(this.Location.Keys.Encode(id)).ConfigureAwait(false); + var data = await trans.GetAsync(this.Location.Keys[id]).ConfigureAwait(false); if (data.IsNull) throw new KeyNotFoundException("The given id was not present in the map."); return this.ValueEncoder.DecodeValue(data); @@ -106,7 +106,7 @@ public async Task> TryGetAsync([NotNull] IFdbReadOnlyTransactio if (trans == null) throw new ArgumentNullException(nameof(trans)); if (id == null) throw new ArgumentNullException(nameof(id)); - var data = await trans.GetAsync(this.Location.Keys.Encode(id)).ConfigureAwait(false); + var data = await trans.GetAsync(this.Location.Keys[id]).ConfigureAwait(false); if (data.IsNull) return default(Optional); return this.ValueEncoder.DecodeValue(data); @@ -122,7 +122,7 @@ public void Set([NotNull] IFdbTransaction trans, TKey id, TValue value) if (trans == null) throw new ArgumentNullException(nameof(trans)); if (id == null) throw new ArgumentNullException(nameof(id)); - trans.Set(this.Location.Keys.Encode(id), this.ValueEncoder.EncodeValue(value)); + trans.Set(this.Location.Keys[id], this.ValueEncoder.EncodeValue(value)); } /// Remove a single entry from the map @@ -134,7 +134,7 @@ public void Remove([NotNull] IFdbTransaction trans, TKey id) if (trans == null) throw new ArgumentNullException(nameof(trans)); if (id == null) throw new ArgumentNullException(nameof(id)); - trans.Clear(this.Location.Keys.Encode(id)); + trans.Clear(this.Location.Keys[id]); } /// Create a query that will attempt to read all the entries in the map within a single transaction. @@ -160,7 +160,7 @@ public async Task[]> GetValuesAsync([NotNull] IFdbReadOnlyTrans if (trans == null) throw new ArgumentNullException(nameof(trans)); if (ids == null) throw new ArgumentNullException(nameof(ids)); - var results = await trans.GetValuesAsync(ids.Select(id => this.Location.Keys.Encode(id))).ConfigureAwait(false); + var results = await trans.GetValuesAsync(ids.Select(id => this.Location.Keys[id])).ConfigureAwait(false); return Optional.DecodeRange(this.ValueEncoder, results); } diff --git a/FoundationDB.Layers.Common/Collections/FdbMultimap`2.cs b/FoundationDB.Layers.Common/Collections/FdbMultimap`2.cs index a8e37639f..b8ab0a3ed 100644 --- a/FoundationDB.Layers.Common/Collections/FdbMultimap`2.cs +++ b/FoundationDB.Layers.Common/Collections/FdbMultimap`2.cs @@ -100,7 +100,7 @@ public Task AddAsync([NotNull] IFdbTransaction trans, TKey key, TValue value) //note: this method does not need to be async, but subtract is, so it's better if both methods have the same shape. if (trans == null) throw new ArgumentNullException(nameof(trans)); - trans.AtomicAdd(this.Location.Keys.Encode(key, value), PlusOne); + trans.AtomicAdd(this.Location.Keys[key, value], PlusOne); return Task.CompletedTask; } @@ -113,7 +113,7 @@ public async Task SubtractAsync([NotNull] IFdbTransaction trans, TKey key, TValu { if (trans == null) throw new ArgumentNullException(nameof(trans)); - Slice k = this.Location.Keys.Encode(key, value); + Slice k = this.Location.Keys[key, value]; if (this.AllowNegativeValues) { trans.AtomicAdd(k, MinusOne); @@ -141,7 +141,7 @@ public async Task ContainsAsync([NotNull] IFdbReadOnlyTransaction trans, T { if (trans == null) throw new ArgumentNullException(nameof(trans)); - var v = await trans.GetAsync(this.Location.Keys.Encode(key, value)).ConfigureAwait(false); + var v = await trans.GetAsync(this.Location.Keys[key, value]).ConfigureAwait(false); return this.AllowNegativeValues ? v.IsPresent : v.ToInt64() > 0; } @@ -155,7 +155,7 @@ public async Task ContainsAsync([NotNull] IFdbReadOnlyTransaction trans, T { if (trans == null) throw new ArgumentNullException(nameof(trans)); - Slice v = await trans.GetAsync(this.Location.Keys.Encode(key, value)).ConfigureAwait(false); + Slice v = await trans.GetAsync(this.Location.Keys[key, value]).ConfigureAwait(false); if (v.IsNullOrEmpty) return null; long c = v.ToInt64(); return this.AllowNegativeValues || c > 0 ? c : default(long?); @@ -200,22 +200,17 @@ public Task> GetAsync([NotNull] IFdbReadOnlyTransaction trans, TKey /// /// [NotNull] - public IAsyncEnumerable> GetCounts([NotNull] IFdbReadOnlyTransaction trans, TKey key) + public IAsyncEnumerable<(TValue Value, long Count)> GetCounts([NotNull] IFdbReadOnlyTransaction trans, TKey key) { var range = KeyRange.StartsWith(this.Location.Keys.EncodePartial(key)); var query = trans .GetRange(range) - .Select(kvp => new KeyValuePair(this.Location.Keys.Decode(kvp.Key).Item2, kvp.Value.ToInt64())); + .Select(kvp => (Value: this.Location.Keys.Decode(kvp.Key).Item2, Count: kvp.Value.ToInt64())); - if (this.AllowNegativeValues) - { - return query; - } - else - { - return query.Where(kvp => kvp.Value > 0); - } + return this.AllowNegativeValues + ? query + : query.Where(x => x.Count > 0); } /// Returns a dictionary with of the counts of each value for a specific key @@ -225,7 +220,7 @@ public IAsyncEnumerable> GetCounts([NotNull] IFdbRead /// public Task> GetCountsAsync([NotNull] IFdbReadOnlyTransaction trans, TKey key, IEqualityComparer comparer = null) { - return GetCounts(trans, key).ToDictionaryAsync(comparer); + return GetCounts(trans, key).ToDictionaryAsync(x => x.Value, x => x.Count, comparer); } /// Remove all the values for a specific key @@ -248,7 +243,7 @@ public void Remove([NotNull] IFdbTransaction trans, TKey key, TValue value) { if (trans == null) throw new ArgumentNullException(nameof(trans)); - trans.Clear(this.Location.Keys.Encode(key, value)); + trans.Clear(this.Location.Keys[key, value]); } #endregion diff --git a/FoundationDB.Layers.Common/Counters/FdbCounterMap.cs b/FoundationDB.Layers.Common/Counters/FdbCounterMap.cs index 72ffe3de0..91e87daba 100644 --- a/FoundationDB.Layers.Common/Counters/FdbCounterMap.cs +++ b/FoundationDB.Layers.Common/Counters/FdbCounterMap.cs @@ -77,7 +77,7 @@ public void Add([NotNull] IFdbTransaction transaction, [NotNull] TKey counterKey //REVIEW: we could no-op if value == 0 but this may change conflict behaviour for other transactions... Slice param = value == 1 ? PlusOne : value == -1 ? MinusOne : Slice.FromFixed64(value); - transaction.AtomicAdd(this.Location.Keys.Encode(counterKey), param); + transaction.AtomicAdd(this.Location.Keys[counterKey], param); } /// Subtract a value from a counter in one atomic operation @@ -117,7 +117,7 @@ public void Decrement([NotNull] IFdbTransaction transaction, [NotNull] TKey coun if (transaction == null) throw new ArgumentNullException("transaction"); if (counterKey == null) throw new ArgumentNullException("counterKey"); - var data = await transaction.GetAsync(this.Location.Keys.Encode(counterKey)).ConfigureAwait(false); + var data = await transaction.GetAsync(this.Location.Keys[counterKey]).ConfigureAwait(false); if (data.IsNullOrEmpty) return default(long?); return data.ToInt64(); } @@ -166,7 +166,7 @@ public async Task ReadThenAddAsync([NotNull] IFdbTransaction transaction, if (transaction == null) throw new ArgumentNullException("transaction"); if (counterKey == null) throw new ArgumentNullException("counterKey"); - var key = this.Location.Keys.Encode(counterKey); + var key = this.Location.Keys[counterKey]; var res = await transaction.GetAsync(key).ConfigureAwait(false); long previous = res.IsNullOrEmpty ? 0 : res.ToInt64(); diff --git a/FoundationDB.Layers.Common/Indexes/FdbIndex`2.cs b/FoundationDB.Layers.Common/Indexes/FdbIndex`2.cs index da530aa8f..146fb9812 100644 --- a/FoundationDB.Layers.Common/Indexes/FdbIndex`2.cs +++ b/FoundationDB.Layers.Common/Indexes/FdbIndex`2.cs @@ -81,7 +81,7 @@ public bool Add([NotNull] IFdbTransaction trans, TId id, TValue value) { if (this.IndexNullValues || value != null) { - trans.Set(this.Location.Keys.Encode(value, id), Slice.Empty); + trans.Set(this.Location.Keys[value, id], Slice.Empty); return true; } return false; @@ -101,13 +101,13 @@ public bool Update([NotNull] IFdbTransaction trans, TId id, TValue newValue, TVa // remove previous value if (this.IndexNullValues || previousValue != null) { - trans.Clear(this.Location.Keys.Encode(previousValue, id)); + trans.Clear(this.Location.Keys[previousValue, id]); } // add new value if (this.IndexNullValues || newValue != null) { - trans.Set(this.Location.Keys.Encode(newValue, id), Slice.Empty); + trans.Set(this.Location.Keys[newValue, id], Slice.Empty); } // cannot be both null, so we did at least something) @@ -124,7 +124,7 @@ public void Remove([NotNull] IFdbTransaction trans, TId id, TValue value) { if (trans == null) throw new ArgumentNullException("trans"); - trans.Clear(this.Location.Keys.Encode(value, id)); + trans.Clear(this.Location.Keys[value, id]); } /// Returns a list of ids matching a specific value diff --git a/FoundationDB.Layers.Experimental/Documents/FdbDocumentCollection.cs b/FoundationDB.Layers.Experimental/Documents/FdbDocumentCollection.cs index 14c8d23d9..1a6153061 100644 --- a/FoundationDB.Layers.Experimental/Documents/FdbDocumentCollection.cs +++ b/FoundationDB.Layers.Experimental/Documents/FdbDocumentCollection.cs @@ -123,7 +123,7 @@ public void Insert(IFdbTransaction trans, TDocument document) while (remaining > 0) { int sz = Math.Max(remaining, this.ChunkSize); - trans.Set(this.Location.Keys.Encode(id, index), packed.Substring(p, sz)); + trans.Set(this.Location.Keys[id, index], packed.Substring(p, sz)); ++index; p += sz; remaining -= sz; diff --git a/FoundationDB.Layers.Experimental/Indexes/FdbCompressedBitmapIndex.cs b/FoundationDB.Layers.Experimental/Indexes/FdbCompressedBitmapIndex.cs index 118e3ba13..f1981697f 100644 --- a/FoundationDB.Layers.Experimental/Indexes/FdbCompressedBitmapIndex.cs +++ b/FoundationDB.Layers.Experimental/Indexes/FdbCompressedBitmapIndex.cs @@ -87,7 +87,7 @@ public async Task AddAsync([NotNull] IFdbTransaction trans, long id, TValu if (this.IndexNullValues || value != null) { - var key = this.Location.Keys.Encode(value); + var key = this.Location.Keys[value]; var data = await trans.GetAsync(key).ConfigureAwait(false); var builder = data.HasValue ? new CompressedBitmapBuilder(data) : CompressedBitmapBuilder.Empty; @@ -117,7 +117,7 @@ public async Task UpdateAsync([NotNull] IFdbTransaction trans, long id, TV // remove previous value if (this.IndexNullValues || previousValue != null) { - var key = this.Location.Keys.Encode(previousValue); + var key = this.Location.Keys[previousValue]; var data = await trans.GetAsync(key).ConfigureAwait(false); if (data.HasValue) { @@ -130,7 +130,7 @@ public async Task UpdateAsync([NotNull] IFdbTransaction trans, long id, TV // add new value if (this.IndexNullValues || newValue != null) { - var key = this.Location.Keys.Encode(newValue); + var key = this.Location.Keys[newValue]; var data = await trans.GetAsync(key).ConfigureAwait(false); var builder = data.HasValue ? new CompressedBitmapBuilder(data) : CompressedBitmapBuilder.Empty; builder.Set((int)id); //BUGBUG: 64 bit id! @@ -151,7 +151,7 @@ public async Task RemoveAsync([NotNull] IFdbTransaction trans, long id, TV { if (trans == null) throw new ArgumentNullException(nameof(trans)); - var key = this.Location.Keys.Encode(value); + var key = this.Location.Keys[value]; var data = await trans.GetAsync(key).ConfigureAwait(false); if (data.HasValue) { @@ -170,7 +170,7 @@ public async Task RemoveAsync([NotNull] IFdbTransaction trans, long id, TV /// List of document ids matching this value for this particular index (can be empty if no document matches) public async Task> LookupAsync([NotNull] IFdbReadOnlyTransaction trans, TValue value, bool reverse = false) { - var key = this.Location.Keys.Encode(value); + var key = this.Location.Keys[value]; var data = await trans.GetAsync(key).ConfigureAwait(false); if (data.IsNull) return null; if (data.IsEmpty) return Enumerable.Empty(); diff --git a/FoundationDB.Linq.Providers/Expressions/FdbQueryExpressions.cs b/FoundationDB.Linq.Providers/Expressions/FdbQueryExpressions.cs index 892e85292..9c2f77bc7 100644 --- a/FoundationDB.Linq.Providers/Expressions/FdbQueryExpressions.cs +++ b/FoundationDB.Linq.Providers/Expressions/FdbQueryExpressions.cs @@ -86,6 +86,7 @@ public static FdbQueryRangeExpression RangeStartsWith(Slice prefix, FdbRangeOpti /// Execute a Range read from the database, and return all the keys and values [NotNull] + [Obsolete] public static FdbQueryRangeExpression RangeStartsWith(ITuple tuple, FdbRangeOptions options = null) { return RangeStartsWith(TuPack.Pack(tuple), options); diff --git a/FoundationDB.Tests.Sandbox/Program.cs b/FoundationDB.Tests.Sandbox/Program.cs index cb1b38c8a..81e276dd8 100644 --- a/FoundationDB.Tests.Sandbox/Program.cs +++ b/FoundationDB.Tests.Sandbox/Program.cs @@ -179,7 +179,7 @@ private static async Task MainAsync(CancellationToken ct) Console.WriteLine("> Connected!"); Console.WriteLine("Opening database 'DB'..."); - using (var db = await cluster.OpenDatabaseAsync(DB_NAME, KeySubspace.FromKey(STuple.Create(SUBSPACE)), false, ct)) + using (var db = await cluster.OpenDatabaseAsync(DB_NAME, KeySubspace.FromKey(TuPack.EncodeKey(SUBSPACE)), false, ct)) { Console.WriteLine("> Connected to db '{0}'", db.Name); diff --git a/FoundationDB.Tests/DatabaseBulkFacts.cs b/FoundationDB.Tests/DatabaseBulkFacts.cs index 035355505..3fb306ff7 100644 --- a/FoundationDB.Tests/DatabaseBulkFacts.cs +++ b/FoundationDB.Tests/DatabaseBulkFacts.cs @@ -59,7 +59,7 @@ public async Task Test_Can_Bulk_Insert_Raw_Data() var rnd = new Random(2403); var data = Enumerable.Range(0, N) - .Select((x) => new KeyValuePair(location.Keys.Encode(x.ToString("x8")), Slice.Random(rnd, 16 + rnd.Next(240)))) + .Select((x) => (Key: location.Keys.Encode(x.ToString("x8")), Value: Slice.Random(rnd, 16 + rnd.Next(240)))) .ToArray(); Log("Total data size is {0:N0} bytes", data.Sum(x => x.Key.Count + x.Value.Count)); @@ -197,7 +197,7 @@ public async Task Test_Can_Batch_ForEach_AsyncWithContextAndState() await Fdb.Bulk.WriteAsync( db, - Enumerable.Range(1, N).Select((x) => new KeyValuePair(location.Keys.Encode(x), Slice.FromInt32(x))), + Enumerable.Range(1, N).Select((x) => (location.Keys.Encode(x), Slice.FromInt32(x))), this.Cancellation ); @@ -210,7 +210,7 @@ await Fdb.Bulk.WriteAsync( await Fdb.Bulk.ForEachAsync( db, Enumerable.Range(1, N).Select(x => location.Keys.Encode(x)), - () => STuple.Create(0L, 0L), + () => (Total: 0L, Count: 0L), async (xs, ctx, state) => { Interlocked.Increment(ref chunks); @@ -221,16 +221,19 @@ await Fdb.Bulk.ForEachAsync( await throttle; long sum = 0; - for (int i = 0; i < results.Length; i++) + foreach (Slice x in results) { - sum += results[i].ToInt32(); + sum += x.ToInt32(); } - return STuple.Create(state.Item1 + sum, state.Item2 + results.Length); + + state.Total += sum; + state.Count += results.Length; + return state; }, (state) => { - Interlocked.Add(ref total, state.Item1); - Interlocked.Add(ref count, state.Item2); + Interlocked.Add(ref total, state.Total); + Interlocked.Add(ref count, state.Count); }, this.Cancellation ); @@ -348,7 +351,7 @@ public async Task Test_Can_Batch_ForEach_WithContextAndState() await Fdb.Bulk.WriteAsync( db, - Enumerable.Range(1, N).Select((x) => new KeyValuePair(location.Keys.Encode(x), Slice.FromInt32(x))), + Enumerable.Range(1, N).Select((x) => (location.Keys.Encode(x), Slice.FromInt32(x))), this.Cancellation ); @@ -361,7 +364,7 @@ await Fdb.Bulk.WriteAsync( await Fdb.Bulk.ForEachAsync( db, Enumerable.Range(1, N).Select(x => location.Keys.Encode(x)), - () => STuple.Create(0L, 0L), // (sum, count) + () => (Total: 0L, Count: 0L), (xs, ctx, state) => { Interlocked.Increment(ref chunks); @@ -372,19 +375,19 @@ await Fdb.Bulk.ForEachAsync( var results = t.Result; // <-- this is bad practice, never do that in real life, 'mkay? long sum = 0; - for (int i = 0; i < results.Length; i++) + foreach (Slice x in results) { - sum += results[i].ToInt32(); + sum += x.ToInt32(); } - return STuple.Create( - state.Item1 + sum, // updated sum - state.Item2 + results.Length // updated count - ); + + state.Total += sum; + state.Count += results.Length; + return state; }, (state) => { - Interlocked.Add(ref total, state.Item1); - Interlocked.Add(ref count, state.Item2); + Interlocked.Add(ref total, state.Total); + Interlocked.Add(ref count, state.Count); }, this.Cancellation ); @@ -413,7 +416,7 @@ public async Task Test_Can_Batch_ForEach_AsyncWithContext() await Fdb.Bulk.WriteAsync( db, - Enumerable.Range(1, N).Select((x) => new KeyValuePair(location.Keys.Encode(x), Slice.FromInt32(x))), + Enumerable.Range(1, N).Select((x) => (location.Keys.Encode(x), Slice.FromInt32(x))), this.Cancellation ); @@ -473,7 +476,7 @@ public async Task Test_Can_Batch_Aggregate() await Fdb.Bulk.WriteAsync( db, - source.Select((x) => new KeyValuePair(location.Keys.Encode(x.Key), Slice.FromInt32(x.Value))), + source.Select((x) => (location.Keys.Encode(x.Key), Slice.FromInt32(x.Value))), this.Cancellation ); @@ -534,7 +537,7 @@ public async Task Test_Can_Batch_Aggregate_With_Transformed_Result() await Fdb.Bulk.WriteAsync( db, - source.Select((x) => new KeyValuePair(location.Keys.Encode(x.Key), Slice.FromInt32(x.Value))), + source.Select((x) => (location.Keys.Encode(x.Key), Slice.FromInt32(x.Value))), this.Cancellation ); @@ -545,7 +548,7 @@ await Fdb.Bulk.WriteAsync( double average = await Fdb.Bulk.AggregateAsync( db, source.Select(x => location.Keys.Encode(x.Key)), - () => STuple.Create(0L, 0L), + () => (Total: 0L, Count: 0L), async (xs, ctx, state) => { Interlocked.Increment(ref chunks); @@ -556,13 +559,15 @@ await Fdb.Bulk.WriteAsync( await throttle; long sum = 0L; - for (int i = 0; i < results.Length; i++) + foreach (Slice x in results) { - sum += results[i].ToInt32(); + sum += x.ToInt32(); } - return STuple.Create(state.Item1 + sum, state.Item2 + results.Length); + state.Total += sum; + state.Count += results.Length; + return state; }, - (state) => (double)state.Item1 / state.Item2, + (state) => (double) state.Total / state.Count, this.Cancellation ); sw.Stop(); @@ -603,7 +608,7 @@ public async Task Test_Can_Export_To_Disk() await Fdb.Bulk.WriteAsync( db.WithoutLogging(), - source.Select((x) => new KeyValuePair(location.Keys.Encode(x.Key), x.Value)), + source.Select((x) => (location.Keys.Encode(x.Key), x.Value)), this.Cancellation ); diff --git a/FoundationDB.Tests/DatabaseFacts.cs b/FoundationDB.Tests/DatabaseFacts.cs index aabdf968d..2dc0b7cb2 100644 --- a/FoundationDB.Tests/DatabaseFacts.cs +++ b/FoundationDB.Tests/DatabaseFacts.cs @@ -278,7 +278,7 @@ public async Task Test_Can_Get_System_Status() public async Task Test_Can_Open_Database_With_Non_Empty_GlobalSpace() { // using a tuple prefix - using (var db = await Fdb.OpenAsync(null, "DB", KeySubspace.FromKey(STuple.Create("test")), false, this.Cancellation)) + using (var db = await Fdb.OpenAsync(null, "DB", KeySubspace.FromKey(TuPack.EncodeKey("test")), false, this.Cancellation)) { Assert.That(db, Is.Not.Null); Assert.That(db.GlobalSpace, Is.Not.Null); diff --git a/FoundationDB.Tests/Layers/DirectoryFacts.cs b/FoundationDB.Tests/Layers/DirectoryFacts.cs index 06bd8d476..c3fe5133a 100644 --- a/FoundationDB.Tests/Layers/DirectoryFacts.cs +++ b/FoundationDB.Tests/Layers/DirectoryFacts.cs @@ -539,7 +539,7 @@ public async Task Test_Can_Change_Layer_Of_Existing_Directory() Assert.That(folder2, Is.Not.Null); Assert.That(folder2.Layer.ToUnicode(), Is.EqualTo("bar")); Assert.That(folder2.FullName, Is.EqualTo("Test")); - Assert.That(folder2.Path, Is.EqualTo(STuple.Create("Test"))); + Assert.That(folder2.Path, Is.EqualTo(new [] { "Test" })); Assert.That(folder2.GetPrefix(), Is.EqualTo(folder.GetPrefix())); // opening the directory with the new layer should succeed diff --git a/FoundationDB.Tests/Linq/AsyncEnumerableFacts.cs b/FoundationDB.Tests/Linq/AsyncEnumerableFacts.cs index 41aa83fc0..0a54272f2 100644 --- a/FoundationDB.Tests/Linq/AsyncEnumerableFacts.cs +++ b/FoundationDB.Tests/Linq/AsyncEnumerableFacts.cs @@ -962,7 +962,7 @@ public async Task Test_Can_Prefetch_On_Constant_Latency_Source() { Interlocked.Increment(ref called); if (index >= 10) return Task.FromResult(Maybe.Nothing()); - return Task.Delay(15).ContinueWith((_) => Maybe.Return((int)index)); + return Task.Delay(15, ct).ContinueWith((_) => Maybe.Return((int)index), ct); }); var results = await source.ToListAsync(); @@ -971,23 +971,23 @@ public async Task Test_Can_Prefetch_On_Constant_Latency_Source() // record the timing and call history to ensure that inner is called at least twice before the first item gets out - Func> record = (x) => STuple.Create(x, Volatile.Read(ref called)); + Func record = (x) => STuple.Create(x, Volatile.Read(ref called)); // without prefetching, the number of calls should match for the producer and the consumer called = 0; sw.Restart(); var withoutPrefetching = await source.Select(record).ToListAsync(this.Cancellation); Log("P0: {0}", String.Join(", ", withoutPrefetching)); - Assert.That(withoutPrefetching.Select(x => x.Item1), Is.EqualTo(Enumerable.Range(0, 10))); - Assert.That(withoutPrefetching.Select(x => x.Item2), Is.EqualTo(Enumerable.Range(1, 10))); + Assert.That(withoutPrefetching.Select(x => x.Value), Is.EqualTo(Enumerable.Range(0, 10))); + Assert.That(withoutPrefetching.Select(x => x.Called), Is.EqualTo(Enumerable.Range(1, 10))); // with prefetching, the consumer should always have one item in advance called = 0; sw.Restart(); var withPrefetching1 = await source.Prefetch().Select(record).ToListAsync(this.Cancellation); Log("P1: {0}", String.Join(", ", withPrefetching1)); - Assert.That(withPrefetching1.Select(x => x.Item1), Is.EqualTo(Enumerable.Range(0, 10))); - Assert.That(withPrefetching1.Select(x => x.Item2), Is.EqualTo(Enumerable.Range(2, 10))); + Assert.That(withPrefetching1.Select(x => x.Value), Is.EqualTo(Enumerable.Range(0, 10))); + Assert.That(withPrefetching1.Select(x => x.Called), Is.EqualTo(Enumerable.Range(2, 10))); // prefetching more than 1 item on a consumer that is not buffered should not change the picture (since we can only read one ahead anyway) //REVIEW: maybe we should change the implementation of the operator so that it still prefetch items in the background if the rest of the query is lagging a bit? @@ -995,8 +995,8 @@ public async Task Test_Can_Prefetch_On_Constant_Latency_Source() sw.Restart(); var withPrefetching2 = await source.Prefetch(2).Select(record).ToListAsync(this.Cancellation); Log("P2: {0}", String.Join(", ", withPrefetching2)); - Assert.That(withPrefetching2.Select(x => x.Item1), Is.EqualTo(Enumerable.Range(0, 10))); - Assert.That(withPrefetching2.Select(x => x.Item2), Is.EqualTo(Enumerable.Range(2, 10))); + Assert.That(withPrefetching2.Select(x => x.Value), Is.EqualTo(Enumerable.Range(0, 10))); + Assert.That(withPrefetching2.Select(x => x.Called), Is.EqualTo(Enumerable.Range(2, 10))); } [Test] diff --git a/FoundationDB.Tests/Linq/FdbQueryExpressionFacts.cs b/FoundationDB.Tests/Linq/FdbQueryExpressionFacts.cs index 7c487950a..bfcffead6 100644 --- a/FoundationDB.Tests/Linq/FdbQueryExpressionFacts.cs +++ b/FoundationDB.Tests/Linq/FdbQueryExpressionFacts.cs @@ -43,9 +43,9 @@ public class FdbQueryExpressionFacts : FdbTest { - private readonly FdbIndex FooBarIndex = new FdbIndex("Foos.ByBar", KeySubspace.FromKey(STuple.Create("Foos", 1))); + private readonly FdbIndex FooBarIndex = new FdbIndex("Foos.ByBar", KeySubspace.FromKey(TuPack.EncodeKey("Foos", 1))); - private readonly FdbIndex FooBazIndex = new FdbIndex("Foos.ByBaz", KeySubspace.FromKey(STuple.Create("Foos", 2))); + private readonly FdbIndex FooBazIndex = new FdbIndex("Foos.ByBaz", KeySubspace.FromKey(TuPack.EncodeKey("Foos", 2))); [Test] public void Test_FdbQueryIndexLookupExpression() @@ -96,7 +96,7 @@ public void Test_FdbQueryIndexLookupExpression_From_Lambda() public void Test_FdbQueryRangeExpression() { var expr = FdbQueryExpressions.Range( - KeySelectorPair.Create(TuPack.ToRange(STuple.Create("Foo"))) + KeySelectorPair.Create(TuPack.ToKeyRange("Foo")) ); Log(expr); @@ -174,7 +174,7 @@ public void Test_FdbQueryUnionExpression() public void Test_FdbQueryTransformExpression() { var expr = FdbQueryExpressions.Transform( - FdbQueryExpressions.RangeStartsWith(STuple.Create("Hello", "World")), + FdbQueryExpressions.RangeStartsWith(TuPack.EncodeKey("Hello", "World")), (kvp) => kvp.Value.ToUnicode() ); Log(expr); @@ -193,7 +193,7 @@ public void Test_FdbQueryTransformExpression() public void Test_FdbQueryFilterExpression() { var expr = FdbQueryExpressions.Filter( - FdbQueryExpressions.RangeStartsWith(STuple.Create("Hello", "World")), + FdbQueryExpressions.RangeStartsWith(TuPack.EncodeKey("Hello", "World")), (kvp) => kvp.Value.ToInt32() % 2 == 0 ); Log(expr); diff --git a/FoundationDB.Tests/RangeQueryFacts.cs b/FoundationDB.Tests/RangeQueryFacts.cs index 26a74bc0f..9a015c47c 100644 --- a/FoundationDB.Tests/RangeQueryFacts.cs +++ b/FoundationDB.Tests/RangeQueryFacts.cs @@ -36,6 +36,7 @@ namespace FoundationDB.Client.Tests using Doxense.Collections.Tuples; using Doxense.Linq; using Doxense.Linq.Async.Iterators; + using Doxense.Serialization.Encoders; using FoundationDB.Layers.Directories; using NUnit.Framework; @@ -360,7 +361,7 @@ public async Task Test_Can_Skip() var location = await GetCleanDirectory(db, "Queries", "Range"); // import test data - var data = Enumerable.Range(0, 100).Select(x => new KeyValuePair(location.Keys.Encode(x), Slice.FromFixed32(x))); + var data = Enumerable.Range(0, 100).Select(x => (location.Keys.Encode(x), Slice.FromFixed32(x))); await Fdb.Bulk.WriteAsync(db, data, this.Cancellation); // from the start @@ -442,7 +443,7 @@ public async Task Test_Original_Range_Does_Not_Overflow() var location = await GetCleanDirectory(db, "Queries", "Range"); // import test data - var data = Enumerable.Range(0, 30).Select(x => new KeyValuePair(location.Keys.Encode(x), Slice.FromFixed32(x))); + var data = Enumerable.Range(0, 30).Select(x => (location.Keys.Encode(x), Slice.FromFixed32(x))); await Fdb.Bulk.WriteAsync(db, data, this.Cancellation); using (var tr = db.BeginReadOnlyTransaction(this.Cancellation)) @@ -687,20 +688,20 @@ public async Task Test_Range_Except_Composite_Key() var location = await GetCleanDirectory(db, "Queries", "ExceptComposite"); // Items contains a list of all ("user", id) that were created - var locItems = await location.CreateOrOpenAsync(db, "Items", this.Cancellation); + var locItems = (await location.CreateOrOpenAsync(db, "Items", this.Cancellation)).UsingEncoder(); // Processed contain the list of all ("user", id) that were processed - var locProcessed = await location.CreateOrOpenAsync(db, "Processed", this.Cancellation); + var locProcessed = (await location.CreateOrOpenAsync(db, "Processed", this.Cancellation)).UsingEncoder(); // the goal is to have a query that returns the list of all unprocessed items (ie: in Items but not in Processed) await db.WriteAsync((tr) => { // Items - tr.Set(locItems.Keys.Encode("userA", 10093), Slice.Empty); - tr.Set(locItems.Keys.Encode("userA", 19238), Slice.Empty); - tr.Set(locItems.Keys.Encode("userB", 20003), Slice.Empty); + tr.Set(locItems.Keys["userA", 10093], Slice.Empty); + tr.Set(locItems.Keys["userA", 19238], Slice.Empty); + tr.Set(locItems.Keys["userB", 20003], Slice.Empty); // Processed - tr.Set(locProcessed.Keys.Encode("userA", 19238), Slice.Empty); + tr.Set(locProcessed.Keys["userA", 19238], Slice.Empty); }, this.Cancellation); // the query (Items ∩ Processed) should return (userA, 10093) and (userB, 20003) @@ -717,7 +718,7 @@ await db.WriteAsync((tr) => // problem: Except() still returns the original (Slice,Slice) pairs from the first range, // meaning that we still need to unpack agin the key (this time knowing the location) - return query.Select(kv => locItems.Keys.Unpack(kv.Key)); + return query.Select(kv => locItems.Keys.Decode(kv.Key)); }, this.Cancellation); foreach(var r in results) @@ -725,8 +726,8 @@ await db.WriteAsync((tr) => Trace.WriteLine(r); } Assert.That(results.Count, Is.EqualTo(2)); - Assert.That(results[0], Is.EqualTo(STuple.Create("userA", 10093))); - Assert.That(results[1], Is.EqualTo(STuple.Create("userB", 20003))); + Assert.That(results[0], Is.EqualTo(("userA", 10093))); + Assert.That(results[1], Is.EqualTo(("userB", 20003))); // Second Method: pre-parse the queries, and merge on the results directly Trace.WriteLine("Method 2:"); @@ -734,11 +735,11 @@ await db.WriteAsync((tr) => { var items = tr .GetRange(locItems.Keys.ToRange()) - .Select(kv => locItems.Keys.Unpack(kv.Key)); + .Select(kv => locItems.Keys.Decode(kv.Key)); var processed = tr .GetRange(locProcessed.Keys.ToRange()) - .Select(kv => locProcessed.Keys.Unpack(kv.Key)); + .Select(kv => locProcessed.Keys.Decode(kv.Key)); // items and processed are lists of (string, int) tuples, we can compare them directly var query = items.Except(processed, TupleComparisons.Composite()); @@ -752,8 +753,8 @@ await db.WriteAsync((tr) => Trace.WriteLine(r); } Assert.That(results.Count, Is.EqualTo(2)); - Assert.That(results[0], Is.EqualTo(STuple.Create("userA", 10093))); - Assert.That(results[1], Is.EqualTo(STuple.Create("userB", 20003))); + Assert.That(results[0], Is.EqualTo(("userA", 10093))); + Assert.That(results[1], Is.EqualTo(("userB", 20003))); } diff --git a/FoundationDB.Tests/SubspaceFacts.cs b/FoundationDB.Tests/SubspaceFacts.cs index 7a3db250b..ec6983561 100644 --- a/FoundationDB.Tests/SubspaceFacts.cs +++ b/FoundationDB.Tests/SubspaceFacts.cs @@ -52,9 +52,7 @@ public void Test_Empty_Subspace_Is_Empty() [Category("LocalCluster")] public void Test_Subspace_With_Binary_Prefix() { - var subspace = KeySubspace - .FromKey(new byte[] { 42, 255, 0, 127 }.AsSlice()) - .Using(TypeSystem.Tuples); + var subspace = KeySubspace.CreateDynamic(new byte[] { 42, 255, 0, 127 }.AsSlice(), TypeSystem.Tuples); Assert.That(subspace.GetPrefix().ToString(), Is.EqualTo("*<00><7F>")); Assert.That(KeySubspace.Copy(subspace), Is.Not.SameAs(subspace)); @@ -69,9 +67,10 @@ public void Test_Subspace_With_Binary_Prefix() Assert.That(subspace.Keys.Encode("hello").ToString(), Is.EqualTo("*<00><7F><02>hello<00>")); Assert.That(subspace.Keys.Encode(Slice.FromStringAscii("world")).ToString(), Is.EqualTo("*<00><7F><01>world<00>")); Assert.That(subspace.Keys.Pack(STuple.Create("hello", 123)).ToString(), Is.EqualTo("*<00><7F><02>hello<00><15>{")); + Assert.That(subspace.Keys.Pack(("hello", 123)).ToString(), Is.EqualTo("*<00><7F><02>hello<00><15>{")); // if we encode a tuple from this subspace, it should keep the binary prefix when converted to a key - var k = subspace.Keys.Pack(STuple.Create("world", 123, false)); + var k = subspace.Keys.Pack(("world", 123, false)); Assert.That(k.ToString(), Is.EqualTo("*<00><7F><02>world<00><15>{<14>")); // if we unpack the key with the binary prefix, we should get a valid tuple @@ -81,6 +80,12 @@ public void Test_Subspace_With_Binary_Prefix() Assert.That(t2.Get(0), Is.EqualTo("world")); Assert.That(t2.Get(1), Is.EqualTo(123)); Assert.That(t2.Get(2), Is.False); + + // ValueTuple + Assert.That(subspace.Keys.Pack(ValueTuple.Create("hello")).ToString(), Is.EqualTo("*<00><7F><02>hello<00>")); + Assert.That(subspace.Keys.Pack(("hello", 123)).ToString(), Is.EqualTo("*<00><7F><02>hello<00><15>{")); + Assert.That(subspace.Keys.Pack(("hello", 123, "world")).ToString(), Is.EqualTo("*<00><7F><02>hello<00><15>{<02>world<00>")); + Assert.That(subspace.Keys.Pack(("hello", 123, "world", 456)).ToString(), Is.EqualTo("*<00><7F><02>hello<00><15>{<02>world<00><16><01>")); } [Test] @@ -112,9 +117,7 @@ public void Test_Cannot_Create_Or_Partition_Subspace_With_Slice_Nil() [Category("LocalCluster")] public void Test_Subspace_With_Tuple_Prefix() { - var subspace = KeySubspace - .FromKey(STuple.Create("hello")) - .Using(TypeSystem.Tuples); + var subspace = KeySubspace.CreateDynamic(TuPack.EncodeKey("hello"), TypeSystem.Tuples); Assert.That(subspace.GetPrefix().ToString(), Is.EqualTo("<02>hello<00>")); Assert.That(KeySubspace.Copy(subspace), Is.Not.SameAs(subspace)); @@ -129,7 +132,7 @@ public void Test_Subspace_With_Tuple_Prefix() Assert.That(subspace.Keys.Encode("world").ToString(), Is.EqualTo("<02>hello<00><02>world<00>")); // even though the subspace prefix is a tuple, appending to it will only return the new items - var k = subspace.Keys.Pack(STuple.Create("world", 123, false)); + var k = subspace.Keys.Pack(("world", 123, false)); Assert.That(k.ToString(), Is.EqualTo("<02>hello<00><02>world<00><15>{<14>")); // if we unpack the key with the binary prefix, we should get a valid tuple @@ -146,7 +149,7 @@ public void Test_Subspace_With_Tuple_Prefix() public void Test_Subspace_Partitioning_With_Binary_Suffix() { // start from a parent subspace - var parent = KeySubspace.FromKey(Slice.Empty).Using(TypeSystem.Tuples); + var parent = KeySubspace.CreateDynamic(Slice.Empty, TypeSystem.Tuples); Assert.That(parent.GetPrefix().ToString(), Is.EqualTo("")); // create a child subspace using a tuple @@ -170,6 +173,178 @@ public void Test_Subspace_Partitioning_With_Binary_Suffix() Assert.That(child.Partition[Slice.Empty].GetPrefix(), Is.EqualTo(child.GetPrefix())); } + [Test] + public void Test_DynamicKeySpace_API() + { + var location = KeySubspace.CreateDynamic(Slice.FromString("PREFIX"), TypeSystem.Tuples); + + Assert.That(location[Slice.FromString("SUFFIX")].ToString(), Is.EqualTo("PREFIXSUFFIX")); + + // Encode(...) + Assert.That(location.Keys.Encode("hello").ToString(), Is.EqualTo("PREFIX<02>hello<00>")); + Assert.That(location.Keys.Encode("hello", 123).ToString(), Is.EqualTo("PREFIX<02>hello<00><15>{")); + Assert.That(location.Keys.Encode("hello", 123, "world").ToString(), Is.EqualTo("PREFIX<02>hello<00><15>{<02>world<00>")); + Assert.That(location.Keys.Encode("hello", 123, "world", 456).ToString(), Is.EqualTo("PREFIX<02>hello<00><15>{<02>world<00><16><01>")); + Assert.That(location.Keys.Encode("hello", 123, "world", 456, "!").ToString(), Is.EqualTo("PREFIX<02>hello<00><15>{<02>world<00><16><01><02>!<00>")); + Assert.That(location.Keys.Encode("hello", 123, "world", 456, "!", 789).ToString(), Is.EqualTo("PREFIX<02>hello<00><15>{<02>world<00><16><01><02>!<00><16><03><15>")); + + // Pack(ITuple) + Assert.That(location.Keys.Pack((ITuple) STuple.Create("hello")).ToString(), Is.EqualTo("PREFIX<02>hello<00>")); + Assert.That(location.Keys.Pack((ITuple) STuple.Create("hello", 123)).ToString(), Is.EqualTo("PREFIX<02>hello<00><15>{")); + Assert.That(location.Keys.Pack((ITuple) STuple.Create("hello", 123, "world")).ToString(), Is.EqualTo("PREFIX<02>hello<00><15>{<02>world<00>")); + Assert.That(location.Keys.Pack((ITuple) STuple.Create("hello", 123, "world", 456)).ToString(), Is.EqualTo("PREFIX<02>hello<00><15>{<02>world<00><16><01>")); + Assert.That(location.Keys.Pack((ITuple) STuple.Create("hello", 123, "world", 456, "!")).ToString(), Is.EqualTo("PREFIX<02>hello<00><15>{<02>world<00><16><01><02>!<00>")); + Assert.That(location.Keys.Pack((ITuple) STuple.Create("hello", 123, "world", 456, "!", 789)).ToString(), Is.EqualTo("PREFIX<02>hello<00><15>{<02>world<00><16><01><02>!<00><16><03><15>")); + + // Pack(ValueTuple) + Assert.That(location.Keys.Pack(ValueTuple.Create("hello")).ToString(), Is.EqualTo("PREFIX<02>hello<00>")); + Assert.That(location.Keys.Pack(("hello", 123)).ToString(), Is.EqualTo("PREFIX<02>hello<00><15>{")); + Assert.That(location.Keys.Pack(("hello", 123, "world")).ToString(), Is.EqualTo("PREFIX<02>hello<00><15>{<02>world<00>")); + Assert.That(location.Keys.Pack(("hello", 123, "world", 456)).ToString(), Is.EqualTo("PREFIX<02>hello<00><15>{<02>world<00><16><01>")); + Assert.That(location.Keys.Pack(("hello", 123, "world", 456, "!")).ToString(), Is.EqualTo("PREFIX<02>hello<00><15>{<02>world<00><16><01><02>!<00>")); + Assert.That(location.Keys.Pack(("hello", 123, "world", 456, "!", 789)).ToString(), Is.EqualTo("PREFIX<02>hello<00><15>{<02>world<00><16><01><02>!<00><16><03><15>")); + + // ITuple Unpack(Slice) + Assert.That(location.Keys.Unpack(Slice.Unescape("PREFIX<02>hello<00>")), Is.EqualTo(STuple.Create("hello"))); + Assert.That(location.Keys.Unpack(Slice.Unescape("PREFIX<02>hello<00><15>{")), Is.EqualTo(STuple.Create("hello", 123))); + Assert.That(location.Keys.Unpack(Slice.Unescape("PREFIX<02>hello<00><15>{<02>world<00>")), Is.EqualTo(STuple.Create("hello", 123, "world"))); + Assert.That(location.Keys.Unpack(Slice.Unescape("PREFIX<02>hello<00><15>{<02>world<00><16><01>")), Is.EqualTo(STuple.Create("hello", 123, "world", 456))); + Assert.That(location.Keys.Unpack(Slice.Unescape("PREFIX<02>hello<00><15>{<02>world<00><16><01><02>!<00>")), Is.EqualTo(STuple.Create("hello", 123, "world", 456, "!"))); + Assert.That(location.Keys.Unpack(Slice.Unescape("PREFIX<02>hello<00><15>{<02>world<00><16><01><02>!<00><16><03><15>")), Is.EqualTo(STuple.Create("hello", 123, "world", 456, "!", 789))); + + // STuple Decode(Slice) + Assert.That(location.Keys.Decode(Slice.Unescape("PREFIX<02>hello<00>")), Is.EqualTo("hello")); + Assert.That(location.Keys.Decode(Slice.Unescape("PREFIX<02>hello<00><15>{")), Is.EqualTo(("hello", 123))); + Assert.That(location.Keys.Decode(Slice.Unescape("PREFIX<02>hello<00><15>{<02>world<00>")), Is.EqualTo(("hello", 123, "world"))); + Assert.That(location.Keys.Decode(Slice.Unescape("PREFIX<02>hello<00><15>{<02>world<00><16><01>")), Is.EqualTo(("hello", 123, "world", 456))); + Assert.That(location.Keys.Decode(Slice.Unescape("PREFIX<02>hello<00><15>{<02>world<00><16><01><02>!<00>")), Is.EqualTo(("hello", 123, "world", 456, "!"))); + Assert.That(location.Keys.Decode(Slice.Unescape("PREFIX<02>hello<00><15>{<02>world<00><16><01><02>!<00><16><03><15>")), Is.EqualTo(("hello", 123, "world", 456, "!", 789))); + + // DecodeFirst/DecodeLast + Assert.That(location.Keys.DecodeFirst(Slice.Unescape("PREFIX<02>hello<00><15>{<02>world<00><16><01><02>!<00><16><03><15>")), Is.EqualTo("hello")); + Assert.That(location.Keys.DecodeLast(Slice.Unescape("PREFIX<02>hello<00><15>{<02>world<00><16><01><02>!<00><16><03><15>")), Is.EqualTo(789)); + + } + + [Test] + public void Test_TypedKeySpace_T1() + { + var location = KeySubspace.CreateTyped(Slice.FromString("PREFIX")); + Assert.That(location.KeyEncoder, Is.Not.Null, "Should have a Key Encoder"); + Assert.That(location.KeyEncoder.Encoding, Is.SameAs(TypeSystem.Tuples), "Encoder should use Tuple type system"); + + // shortcuts + Assert.That(location[Slice.FromString("SUFFIX")].ToString(), Is.EqualTo("PREFIXSUFFIX")); + Assert.That(location.Keys["hello"].ToString(), Is.EqualTo("PREFIX<02>hello<00>")); + Assert.That(location.Keys[ValueTuple.Create("hello")].ToString(), Is.EqualTo("PREFIX<02>hello<00>")); + + // Encode(...) + Assert.That(location.Keys.Encode("hello").ToString(), Is.EqualTo("PREFIX<02>hello<00>")); + + // Pack(ITuple) + Assert.That(location.Keys.Pack((ITuple) STuple.Create("hello")).ToString(), Is.EqualTo("PREFIX<02>hello<00>")); + + // Pack(ValueTuple) + Assert.That(location.Keys.Pack(ValueTuple.Create("hello")).ToString(), Is.EqualTo("PREFIX<02>hello<00>")); + + // STuple Decode(...) + Assert.That(location.Keys.Decode(Slice.Unescape("PREFIX<02>hello<00>")), Is.EqualTo("hello")); + + // Decode(..., out T) + location.Keys.Decode(Slice.Unescape("PREFIX<02>hello<00>"), out string x); + Assert.That(x, Is.EqualTo("hello")); + } + + [Test] + public void Test_TypedKeySpace_T2() + { + var location = new KeySubspace(Slice.FromString("PREFIX")) + .UsingEncoder(TypeSystem.Tuples.GetEncoder()); + + // shortcuts + Assert.That(location[Slice.FromString("SUFFIX")].ToString(), Is.EqualTo("PREFIXSUFFIX")); + Assert.That(location.Keys["hello", 123].ToString(), Is.EqualTo("PREFIX<02>hello<00><15>{")); + Assert.That(location.Keys[("hello", 123)].ToString(), Is.EqualTo("PREFIX<02>hello<00><15>{")); + + // Encode(...) + Assert.That(location.Keys.Encode("hello", 123).ToString(), Is.EqualTo("PREFIX<02>hello<00><15>{")); + + // Pack(ITuple) + Assert.That(location.Keys.Pack((ITuple) STuple.Create("hello", 123)).ToString(), Is.EqualTo("PREFIX<02>hello<00><15>{")); + + // Pack(ValueTuple) + Assert.That(location.Keys.Pack(("hello", 123)).ToString(), Is.EqualTo("PREFIX<02>hello<00><15>{")); + + // STuple Decode(...) + Assert.That(location.Keys.Decode(Slice.Unescape("PREFIX<02>hello<00><15>{")), Is.EqualTo(("hello", 123))); + + // Decode(..., out T) + location.Keys.Decode(Slice.Unescape("PREFIX<02>hello<00><15>{"), out string x1, out int x2); + Assert.That(x1, Is.EqualTo("hello")); + Assert.That(x2, Is.EqualTo(123)); + } + + [Test] + public void Test_TypedKeySpace_T3() + { + var location = new KeySubspace(Slice.FromString("PREFIX")) + .UsingEncoder(TypeSystem.Tuples.GetEncoder()); + + // shortcuts + Assert.That(location[Slice.FromString("SUFFIX")].ToString(), Is.EqualTo("PREFIXSUFFIX")); + Assert.That(location.Keys["hello", 123, "world"].ToString(), Is.EqualTo("PREFIX<02>hello<00><15>{<02>world<00>")); + Assert.That(location.Keys[("hello", 123, "world")].ToString(), Is.EqualTo("PREFIX<02>hello<00><15>{<02>world<00>")); + + // Encode(...) + Assert.That(location.Keys.Encode("hello", 123, "world").ToString(), Is.EqualTo("PREFIX<02>hello<00><15>{<02>world<00>")); + + // Pack(ITuple) + Assert.That(location.Keys.Pack((ITuple) STuple.Create("hello", 123, "world")).ToString(), Is.EqualTo("PREFIX<02>hello<00><15>{<02>world<00>")); + + // Pack(ValueTuple) + Assert.That(location.Keys.Pack(("hello", 123, "world")).ToString(), Is.EqualTo("PREFIX<02>hello<00><15>{<02>world<00>")); + + // STuple Decode(...) + Assert.That(location.Keys.Decode(Slice.Unescape("PREFIX<02>hello<00><15>{<02>world<00>")), Is.EqualTo(("hello", 123, "world"))); + + // Decode(..., out T) + location.Keys.Decode(Slice.Unescape("PREFIX<02>hello<00><15>{<02>world<00>"), out string x1, out int x2, out string x3); + Assert.That(x1, Is.EqualTo("hello")); + Assert.That(x2, Is.EqualTo(123)); + Assert.That(x3, Is.EqualTo("world")); + } + + [Test] + public void Test_TypedKeySpace_T4() + { + var location = new KeySubspace(Slice.FromString("PREFIX")) + .UsingEncoder(TypeSystem.Tuples.GetEncoder()); + + // shortcuts + Assert.That(location[Slice.FromString("SUFFIX")].ToString(), Is.EqualTo("PREFIXSUFFIX")); + Assert.That(location.Keys["hello", 123, "world", 456].ToString(), Is.EqualTo("PREFIX<02>hello<00><15>{<02>world<00><16><01>")); + Assert.That(location.Keys[("hello", 123, "world", 456)].ToString(), Is.EqualTo("PREFIX<02>hello<00><15>{<02>world<00><16><01>")); + + // Encode(...) + Assert.That(location.Keys.Encode("hello", 123, "world", 456).ToString(), Is.EqualTo("PREFIX<02>hello<00><15>{<02>world<00><16><01>")); + + // Pack(ITuple) + Assert.That(location.Keys.Pack((ITuple) STuple.Create("hello", 123, "world", 456)).ToString(), Is.EqualTo("PREFIX<02>hello<00><15>{<02>world<00><16><01>")); + + // Pack(ValueTuple) + Assert.That(location.Keys.Pack(("hello", 123, "world", 456)).ToString(), Is.EqualTo("PREFIX<02>hello<00><15>{<02>world<00><16><01>")); + + // STuple Decode(...) + Assert.That(location.Keys.Decode(Slice.Unescape("PREFIX<02>hello<00><15>{<02>world<00><16><01>")), Is.EqualTo(("hello", 123, "world", 456))); + + // Decode(..., out T) + location.Keys.Decode(Slice.Unescape("PREFIX<02>hello<00><15>{<02>world<00><16><01>"), out string x1, out int x2, out string x3, out int x4); + Assert.That(x1, Is.EqualTo("hello")); + Assert.That(x2, Is.EqualTo(123)); + Assert.That(x3, Is.EqualTo("world")); + Assert.That(x4, Is.EqualTo(456)); + } + } } diff --git a/FoundationDB.Tests/TransactionalFacts.cs b/FoundationDB.Tests/TransactionalFacts.cs index 75c2d0063..397ca89bf 100644 --- a/FoundationDB.Tests/TransactionalFacts.cs +++ b/FoundationDB.Tests/TransactionalFacts.cs @@ -162,7 +162,7 @@ public async Task Test_Transactionals_Retries_Do_Not_Leak_When_Reading_Too_Much( var sw = Stopwatch.StartNew(); Log("Inserting test data (this may take a few minutes)..."); var rnd = new Random(); - await Fdb.Bulk.WriteAsync(db, Enumerable.Range(0, 100 * 1000).Select(i => new KeyValuePair(location.Keys.Encode(i), Slice.Random(rnd, 4096))), this.Cancellation); + await Fdb.Bulk.WriteAsync(db, Enumerable.Range(0, 100 * 1000).Select(i => (location.Keys.Encode(i), Slice.Random(rnd, 4096))), this.Cancellation); sw.Stop(); Log("> done in " + sw.Elapsed); diff --git a/FoundationDB.Tests/Utils/TupleFacts.cs b/FoundationDB.Tests/Utils/TupleFacts.cs index 3c63fe6c8..8921a5709 100644 --- a/FoundationDB.Tests/Utils/TupleFacts.cs +++ b/FoundationDB.Tests/Utils/TupleFacts.cs @@ -26,8 +26,6 @@ DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY */ #endregion -//#define ENABLE_VALUETUPLE - // ReSharper disable AccessToModifiedClosure namespace Doxense.Collections.Tuples.Tests { @@ -121,13 +119,11 @@ public void Test_Tuple_2() Assert.That(item1, Is.EqualTo("hello world")); Assert.That(item2, Is.EqualTo(123)); } -#if ENABLE_VALUETUPLE { // Deconstruct (string item1, int item2) = t2; Assert.That(item1, Is.EqualTo("hello world")); Assert.That(item2, Is.EqualTo(123)); } -#endif } [Test] @@ -174,14 +170,12 @@ public void Test_Tuple_3() Assert.That(item2, Is.EqualTo(123)); Assert.That(item3, Is.False); } -#if ENABLE_VALUETUPLE { // Deconstruct (string item1, int item2, bool item3) = t3; Assert.That(item1, Is.EqualTo("hello world")); Assert.That(item2, Is.EqualTo(123)); Assert.That(item3, Is.False); } -#endif } [Test] @@ -235,7 +229,6 @@ public void Test_Tuple_4() Assert.That(item3, Is.False); Assert.That(item4, Is.EqualTo(1234L)); } -#if ENABLE_VALUETUPLE { // Deconstruct (string item1, int item2, bool item3, long item4) = t4; Assert.That(item1, Is.EqualTo("hello world")); @@ -243,7 +236,6 @@ public void Test_Tuple_4() Assert.That(item3, Is.False); Assert.That(item4, Is.EqualTo(1234L)); } -#endif } [Test] @@ -287,7 +279,6 @@ public void Test_Tuple_5() Assert.That(item4, Is.EqualTo(1234L)); Assert.That(item5, Is.EqualTo(-1234L)); } -#if ENABLE_VALUETUPLE { // Deconstruct (string item1, int item2, bool item3, long item4, long item5) = t5; Assert.That(item1, Is.EqualTo("hello world")); @@ -296,7 +287,6 @@ public void Test_Tuple_5() Assert.That(item4, Is.EqualTo(1234L)); Assert.That(item5, Is.EqualTo(-1234L)); } -#endif } [Test] @@ -344,7 +334,6 @@ public void Test_Tuple_6() Assert.That(item5, Is.EqualTo(-1234L)); Assert.That(item6, Is.EqualTo("six")); } -#if ENABLE_VALUETUPLE { // Deconstruct (string item1, int item2, bool item3, long item4, long item5, string item6) = t6; Assert.That(item1, Is.EqualTo("hello world")); @@ -354,7 +343,6 @@ public void Test_Tuple_6() Assert.That(item5, Is.EqualTo(-1234L)); Assert.That(item6, Is.EqualTo("six")); } -#endif } [Test] @@ -1936,8 +1924,6 @@ void ITupleFormattable.FromTuple(ITuple tuple) #region System.ValueTuple integration... -#if ENABLE_VALUETUPLE - [Test] public void Test_Implicit_Cast_STuple_To_ValueTuple() { @@ -1946,25 +1932,25 @@ public void Test_Implicit_Cast_STuple_To_ValueTuple() Assert.That(t.Item1, Is.EqualTo(11)); } { - ValueTuple t = STuple.Create(11, 22); + (int, int) t = STuple.Create(11, 22); Assert.That(t.Item1, Is.EqualTo(11)); Assert.That(t.Item2, Is.EqualTo(22)); } { - ValueTuple t = STuple.Create(11, 22, 33); + (int, int, int) t = STuple.Create(11, 22, 33); Assert.That(t.Item1, Is.EqualTo(11)); Assert.That(t.Item2, Is.EqualTo(22)); Assert.That(t.Item3, Is.EqualTo(33)); } { - ValueTuple t = STuple.Create(11, 22, 33, 44); + (int, int, int, int) t = STuple.Create(11, 22, 33, 44); Assert.That(t.Item1, Is.EqualTo(11)); Assert.That(t.Item2, Is.EqualTo(22)); Assert.That(t.Item3, Is.EqualTo(33)); Assert.That(t.Item4, Is.EqualTo(44)); } { - ValueTuple t = STuple.Create(11, 22, 33, 44, 55); + (int, int, int, int, int) t = STuple.Create(11, 22, 33, 44, 55); Assert.That(t.Item1, Is.EqualTo(11)); Assert.That(t.Item2, Is.EqualTo(22)); Assert.That(t.Item3, Is.EqualTo(33)); @@ -1972,7 +1958,7 @@ public void Test_Implicit_Cast_STuple_To_ValueTuple() Assert.That(t.Item5, Is.EqualTo(55)); } { - ValueTuple t = STuple.Create(11, 22, 33, 44, 55, 66); + (int, int, int, int, int, int) t = STuple.Create(11, 22, 33, 44, 55, 66); Assert.That(t.Item1, Is.EqualTo(11)); Assert.That(t.Item2, Is.EqualTo(22)); Assert.That(t.Item3, Is.EqualTo(33)); @@ -1990,25 +1976,25 @@ public void Test_Implicit_Cast_ValueTuple_To_STuple() Assert.That(t.Item1, Is.EqualTo(11)); } { - STuple t = ValueTuple.Create(11, 22); + STuple t = (11, 22); Assert.That(t.Item1, Is.EqualTo(11)); Assert.That(t.Item2, Is.EqualTo(22)); } { - STuple t = ValueTuple.Create(11, 22, 33); + STuple t = (11, 22, 33); Assert.That(t.Item1, Is.EqualTo(11)); Assert.That(t.Item2, Is.EqualTo(22)); Assert.That(t.Item3, Is.EqualTo(33)); } { - STuple t = ValueTuple.Create(11, 22, 33, 44); + STuple t = (11, 22, 33, 44); Assert.That(t.Item1, Is.EqualTo(11)); Assert.That(t.Item2, Is.EqualTo(22)); Assert.That(t.Item3, Is.EqualTo(33)); Assert.That(t.Item4, Is.EqualTo(44)); } { - STuple t = ValueTuple.Create(11, 22, 33, 44, 55); + STuple t = (11, 22, 33, 44, 55); Assert.That(t.Item1, Is.EqualTo(11)); Assert.That(t.Item2, Is.EqualTo(22)); Assert.That(t.Item3, Is.EqualTo(33)); @@ -2016,7 +2002,7 @@ public void Test_Implicit_Cast_ValueTuple_To_STuple() Assert.That(t.Item5, Is.EqualTo(55)); } { - STuple t = ValueTuple.Create(11, 22, 33, 44, 55, 66); + STuple t = (11, 22, 33, 44, 55, 66); Assert.That(t.Item1, Is.EqualTo(11)); Assert.That(t.Item2, Is.EqualTo(22)); Assert.That(t.Item3, Is.EqualTo(33)); @@ -2026,7 +2012,7 @@ public void Test_Implicit_Cast_ValueTuple_To_STuple() } } - private static ValueTuple ProduceValueTuple(int item1, int item2) => ValueTuple.Create(item1, item2); + private static (int, int) ProduceValueTuple(int item1, int item2) => (item1, item2); private static int[] ConsumeValueTuple(STuple t) => new[] { t.Item1, t.Item2 }; @@ -2044,7 +2030,7 @@ public void Test_Can_AutoCast_Transparently() Assert.That(res[1], Is.EqualTo(5)); } { // literal => STuple - var res = ConsumeSTuple(ValueTuple.Create(1234, 5)); + var res = ConsumeSTuple((1234, 5)); Assert.That(res[0], Is.EqualTo(1234)); Assert.That(res[1], Is.EqualTo(5)); } @@ -2055,8 +2041,6 @@ public void Test_Can_AutoCast_Transparently() } } -#endif - [Test] public void Test_Deconstruct_STuple() { @@ -2087,17 +2071,16 @@ public void Test_Deconstruct_STuple() Assert.That(e, Is.EqualTo(55)); } { - STuple.Create(11, 22, 33, 44, 55, 66).Deconstruct(out int a, out int b, out int c, out int d, out int e, out _); + STuple.Create(11, 22, 33, 44, 55, 66).Deconstruct(out int a, out int b, out int c, out int d, out int e, out int f); Assert.That(a, Is.EqualTo(11)); Assert.That(b, Is.EqualTo(22)); Assert.That(c, Is.EqualTo(33)); Assert.That(d, Is.EqualTo(44)); Assert.That(e, Is.EqualTo(55)); + Assert.That(f, Is.EqualTo(66)); } } -#if ENABLE_VALUETUPLE - [Test] public void Test_Deconstruct_STuple_TupleSyntax() { @@ -2134,11 +2117,10 @@ public void Test_Deconstruct_STuple_TupleSyntax() Assert.That(c, Is.EqualTo(33)); Assert.That(d, Is.EqualTo(44)); Assert.That(e, Is.EqualTo(55)); + Assert.That(f, Is.EqualTo(66)); } } -#endif - #endregion }