public List <KeyValuePair <string, IVarTuple> > Build(KeyValuePair <IVarTuple, Slice>[] parts)
            {
                if (parts == null)
                {
                    throw new ArgumentNullException(nameof(parts));
                }

                var list = new List <KeyValuePair <string, IVarTuple> >(parts.Length);

                foreach (var part in parts)
                {
                    list.Add(new KeyValuePair <string, IVarTuple>(
                                 part.Key.Last <string>(),
                                 TuPack.Unpack(part.Value)
                                 ));
                }
                return(list);
            }
Exemple #2
0
        /// <summary>Return the list the names of all fields of an hashset</summary>
        /// <param name="trans">Transaction that will be used for this request</param>
        /// <param name="id">Unique identifier of the hashset</param>
        /// <returns>List of all fields. If the list is empty, the hashset does not exist</returns>
        public Task <List <string> > GetKeys(IFdbReadOnlyTransaction trans, IVarTuple id)
        {
            //note: As of Beta2, FDB does not have a fdb_get_range that only return the keys. That means that we will have to also read the values from the db, in order to just get the names of the fields :(
            //TODO: find a way to optimize this ?

            if (trans == null)
            {
                throw new ArgumentNullException(nameof(trans));
            }
            if (id == null)
            {
                throw new ArgumentNullException(nameof(id));
            }

            var prefix = GetKey(id);

            return(trans
                   .GetRange(KeyRange.StartsWith(prefix))
                   .Select((kvp) => ParseFieldKey(TuPack.Unpack(kvp.Key)))
                   .ToListAsync());
        }
        public static async Task DumpSubspace(IFdbReadOnlyTransaction tr, IKeySubspace subspace)
        {
            Assert.That(tr, Is.Not.Null);
            Assert.That(subspace, Is.Not.Null);

            FdbTest.Log($"Dumping content of {subspace} at {subspace.GetPrefix():K}:");
            int count = 0;
            await tr
            .GetRange(KeyRange.StartsWith(subspace.GetPrefix()))
            .ForEachAsync((kvp) =>
            {
                var key = subspace.ExtractKey(kvp.Key, boundCheck: true);
                ++count;
                string keyDump;
                try
                {
                    // attempts decoding it as a tuple
                    keyDump = TuPack.Unpack(key).ToString() !;
                }
                catch (Exception)
                {
                    // not a tuple, dump as bytes
                    keyDump = "'" + key.ToString() + "'";
                }

                FdbTest.Log("- " + keyDump + " = " + kvp.Value.ToString());
            });

            if (count == 0)
            {
                FdbTest.Log("> empty !");
            }
            else
            {
                FdbTest.Log("> Found " + count + " values");
            }
        }
        public static async Task Get(string[] path, IVarTuple extras, IFdbDatabase db, TextWriter log, CancellationToken ct)
        {
            if (path == null || path.Length == 0)
            {
                Program.Error(log, "Cannot read keys of the Root Partition.");
                return;
            }

            if (extras.Count == 0)
            {
                Program.Error(log, "You must specify a key of range of keys!");
                return;
            }

            var folder = await db.Directory.TryOpenAsync(db, path, ct : ct);

            if (folder == null)
            {
                Program.Error(log, "The directory does not exist anymore");
                return;
            }
            if (folder.Layer == FdbDirectoryPartition.LayerId)
            {
                Program.Error(log, "Cannot clear the content of a Directory Partition!");
                return;
            }

            object key = extras[0];
            Slice  k   = MakeKey(folder, key);

            Program.Comment(log, "# Reading key: " + k.ToString("K"));

            Slice v = await db.ReadWriteAsync(tr => tr.GetAsync(k), ct);

            if (v.IsNull)
            {
                Program.StdOut(log, "# Key does not exist in the database.", ConsoleColor.Red);
                return;
            }
            if (v.IsEmpty)
            {
                Program.StdOut(log, "# Key exists but is empty.", ConsoleColor.Gray);
                return;
            }

            Program.StdOut(log, $"# Size: {v.Count:N0}", ConsoleColor.Gray);
            string format = extras.Count > 1 ? extras.Get <string>(1) : null;

            switch (format)
            {
            case "--text":
            case "--json":
            case "--utf8":
            {
                Program.StdOut(log, v.ToStringUtf8(), ConsoleColor.Gray);
                break;
            }

            case "--hex":
            case "--hexa":
            {
                Program.StdOut(log, v.ToHexaString(), ConsoleColor.White);
                break;
            }

            case "--dump":
            {
                var sb = new StringBuilder(v.Count * 3 + (v.Count >> 4) * 2 + 16);
                for (int i = 0; i < v.Count; i += 16)
                {
                    sb.AppendLine(v.Substring(i, 16).ToHexaString(' '));
                }
                Program.StdOut(log, sb.ToString(), ConsoleColor.White);
                break;
            }

            case "--int":
            {
                if (v.Count <= 8)
                {
                    long he = v.ToInt64BE();
                    long le = v.ToInt64();
                    Program.StdOut(log, $"BE: {he:X016} ({he:N0})", ConsoleColor.White);
                    Program.StdOut(log, $"LE: {le:X016} ({le:N0})", ConsoleColor.White);
                }
                else
                {
                    Program.StdOut(log, $"Value is too large ({v.Count} bytes)", ConsoleColor.DarkRed);
                    Program.StdOut(log, v.ToHexaString(' '), ConsoleColor.Gray);
                }
                break;
            }

            case "--uint":
            {
                if (v.Count <= 8)
                {
                    ulong he = v.ToUInt64BE();
                    ulong le = v.ToUInt64();
                    Program.StdOut(log, $"BE: {he:X016} ({he:N0})", ConsoleColor.White);
                    Program.StdOut(log, $"LE: {le:X016} ({le:N0})", ConsoleColor.White);
                }
                else
                {
                    Program.StdOut(log, $"Value is too large ({v.Count} bytes)", ConsoleColor.DarkRed);
                    Program.StdOut(log, v.ToHexaString(' '), ConsoleColor.Gray);
                }
                break;
            }

            case "--tuple":
            {
                try
                {
                    var t = TuPack.Unpack(v);
                    Program.StdOut(log, t.ToString(), ConsoleColor.Gray);
                }
                catch (Exception e)
                {
                    Program.Error(log, "Key value does not seem to be a valid Tuple: " + e.Message);
                    Program.StdOut(log, v.ToHexaString(' '), ConsoleColor.Gray);
                }
                break;
            }

            default:
            {
                Program.StdOut(log, v.ToString("V"), ConsoleColor.White);
                break;
            }
            }
        }
        public async Task Test_FdbMap_With_Custom_Key_Encoder()
        {
            // Use a table as a backing store for the rules of a Poor Man's firewall, where each keys are the IPEndPoint (tcp only!), and the values are "pass" or "block"

            // Encode IPEndPoint as the (IP, Port,) encoded with the Tuple codec
            // note: there is a much simpler way or creating composite keys, this is just a quick and dirty test!
            var keyEncoder = new KeyEncoder <IPEndPoint>(
                (ipe) => ipe == null ? Slice.Empty : TuPack.EncodeKey(ipe.Address, ipe.Port),
                (packed) =>
            {
                if (packed.IsNullOrEmpty)
                {
                    return(default(IPEndPoint));
                }
                var t = TuPack.Unpack(packed);
                return(new IPEndPoint(t.Get <IPAddress>(0), t.Get <int>(1)));
            }
                );

            var rules = new Dictionary <IPEndPoint, string>()
            {
                { new IPEndPoint(IPAddress.Parse("172.16.12.34"), 6667), "block" },
                { new IPEndPoint(IPAddress.Parse("192.168.34.56"), 80), "pass" },
                { new IPEndPoint(IPAddress.Parse("192.168.34.56"), 443), "pass" }
            };

            using (var db = await OpenTestPartitionAsync())
            {
                var location = db.Root["Collections"]["Maps"];
                await CleanLocation(db, location);

                var mapHosts = new FdbMap <IPEndPoint, string>(location.ByKey("Hosts").AsTyped <IPEndPoint>(keyEncoder), BinaryEncoding.StringEncoder);

                // import all the rules
                await mapHosts.WriteAsync(db, (tr, hosts) =>
                {
                    foreach (var rule in rules)
                    {
                        hosts.Set(tr, rule.Key, rule.Value);
                    }
                }, this.Cancellation);

#if FULL_DEBUG
                await DumpSubspace(db, location);
#endif

                // test the rules

                await mapHosts.ReadAsync(db, async (tr, hosts) =>
                {
                    var value = await hosts.GetAsync(tr, new IPEndPoint(IPAddress.Parse("172.16.12.34"), 6667));
                    Assert.That(value, Is.EqualTo("block"));

                    value = await hosts.GetAsync(tr, new IPEndPoint(IPAddress.Parse("192.168.34.56"), 443));
                    Assert.That(value, Is.EqualTo("pass"));

                    var baz = new IPEndPoint(IPAddress.Parse("172.16.12.34"), 80);
                    Assert.That(async() => await hosts.GetAsync(tr, baz), Throws.InstanceOf <KeyNotFoundException>());

                    var opt = await hosts.TryGetAsync(tr, baz);
                    Assert.That(opt.HasValue, Is.False);
                }, this.Cancellation);
            }
        }
 public IVarTuple UnpackKey(Slice packed)
 {
     return(TuPack.Unpack(packed));
 }
        public async Task Test_Range_Except_Composite_Key()
        {
            using (var db = await OpenTestPartitionAsync())
            {
                // get a clean new directory
                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)).AsTyped <string, int>();
                // Processed contain the list of all ("user", id) that were processed
                var locProcessed = (await location.CreateOrOpenAsync(db, "Processed", this.Cancellation)).AsTyped <string, int>();

                // 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["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["userA", 19238], Slice.Empty);
                }, this.Cancellation);

                // the query (Items ∩ Processed) should return (userA, 10093) and (userB, 20003)

                // First Method: pass in a list of key ranges, and merge on the (Slice, Slice) pairs
                Trace.WriteLine("Method 1:");
                var results = await db.QueryAsync((tr) =>
                {
                    var query = tr.Except(
                        new[] { locItems.Keys.ToRange(), locProcessed.Keys.ToRange() },
                        (kv) => TuPack.Unpack(kv.Key).Substring(-2),                      // note: keys come from any of the two ranges, so we must only keep the last 2 elements of the tuple
                        TupleComparisons.Composite <string, int>()                        // compares t[0] as a string, and t[1] as an int
                        );

                    // 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.Decode(kv.Key)));
                }, this.Cancellation);

                foreach (var r in results)
                {
                    Trace.WriteLine(r);
                }
                Assert.That(results.Count, Is.EqualTo(2));
                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:");
                results = await db.QueryAsync((tr) =>
                {
                    var items = tr
                                .GetRange(locItems.Keys.ToRange())
                                .Select(kv => locItems.Keys.Decode(kv.Key));

                    var processed = tr
                                    .GetRange(locProcessed.Keys.ToRange())
                                    .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 <string, int>());

                    // query is already a list of tuples, nothing more to do
                    return(query);
                }, this.Cancellation);

                foreach (var r in results)
                {
                    Trace.WriteLine(r);
                }
                Assert.That(results.Count, Is.EqualTo(2));
                Assert.That(results[0], Is.EqualTo(("userA", 10093)));
                Assert.That(results[1], Is.EqualTo(("userB", 20003)));
            }
        }
Exemple #8
0
        public static string PrettyPrint(Slice key, PrettyPrintMode mode)
        {
            if (key.Count > 1)
            {
                byte c = key[0];
                //OPTIMIZE: maybe we need a lookup table
                if (c <= 28 || c == 32 || c == 33 || c == 48 || c == 49 || c >= 254)
                {                 // it could be a tuple...
                    try
                    {
                        ITuple tuple  = null;
                        string suffix = null;
                        bool   skip   = false;

                        try
                        {
                            switch (mode)
                            {
                            case PrettyPrintMode.End:
                            {                                     // the last byte will either be FF, or incremented
                                // for tuples, the really bad cases are for byte[]/strings (which normally end with 00)
                                // => pack(("string",))+\xFF => <02>string<00><FF>
                                // => string(("string",)) => <02>string<01>
                                switch (key[-1])
                                {
                                case 0xFF:
                                {
                                    //***README*** if you break under here, see README in the last catch() block
                                    tuple  = TuPack.Unpack(key[0, -1]);
                                    suffix = ".<FF>";
                                    break;
                                }

                                case 0x01:
                                {
                                    var tmp = key[0, -1] + (byte)0;
                                    //***README*** if you break under here, see README in the last catch() block
                                    tuple  = TuPack.Unpack(tmp);
                                    suffix = " + 1";
                                    break;
                                }
                                }
                                break;
                            }

                            case PrettyPrintMode.Begin:
                            {                                     // the last byte will usually be 00
                                // We can't really know if the tuple ended with NULL (serialized to <00>) or if a <00> was added,
                                // but since the ToRange() on tuples add a <00> we can bet on the fact that it is not part of the tuple itself.
                                // except maybe if we have "00 FF 00" which would be the expected form of a string that ends with a <00>

                                if (key.Count > 2 && key[-1] == 0 && key[-2] != 0xFF)
                                {
                                    //***README*** if you break under here, see README in the last catch() block
                                    tuple  = TuPack.Unpack(key[0, -1]);
                                    suffix = ".<00>";
                                }
                                break;
                            }
                            }
                        }
                        catch (Exception e)
                        {
                            suffix = null;
                            skip   = !(e is FormatException || e is ArgumentOutOfRangeException);
                        }

                        if (tuple == null && !skip)
                        {                         // attempt a regular decoding
                            //***README*** if you break under here, see README in the last catch() block
                            tuple = TuPack.Unpack(key);
                        }

                        if (tuple != null)
                        {
                            return(tuple.ToString() + suffix);
                        }
                    }
                    catch (Exception)
                    {
                        //README: If Visual Studio is breaking inside some Tuple parsing method somewhere inside this try/catch,
                        // this is because your debugger is configured to automatically break on thrown exceptions of type FormatException, ArgumentException, or InvalidOperation.
                        // Unfortunately, there isn't much you can do except unchecking "break when this exception type is thrown". If you know a way to disable locally this behaviour, please fix this!
                        // => only other option would be to redesign the parsing of tuples as a TryParseXXX() that does not throw, OR to have a VerifyTuple() methods that only checks for validity....
                    }
                }
            }

            return(Slice.Dump(key));
        }