Example #1
0
        public static MPTNode Decode(byte[] data)
        {
            if (data is null || data.Length == 0)
            {
                return(null);
            }

            MPTNode node;

            using (BinaryReader reader = new BinaryReader(new MemoryStream(data, false), Encoding.UTF8))
            {
                var nodeType = (NodeType)reader.ReadByte();
                switch (nodeType)
                {
                case NodeType.BranchNode:
                {
                    node = new BranchNode();
                    break;
                }

                case NodeType.ExtensionNode:
                {
                    node = new ExtensionNode();
                    break;
                }

                case NodeType.LeafNode:
                {
                    node = new LeafNode();
                    break;
                }

                default:
                    throw new System.InvalidOperationException();
                }

                node.DecodeSpecific(reader);
            }
            return(node);
        }
Example #2
0
        private bool Put(ref MPTNode node, byte[] path, MPTNode val)
        {
            switch (node)
            {
            case LeafNode leafNode:
            {
                if (val is LeafNode v)
                {
                    if (path.Length == 0)
                    {
                        node = v;
                        db.Put(node);
                        return(true);
                    }
                    var branch = new BranchNode();
                    branch.Children[BranchNode.ChildCount - 1] = leafNode;
                    Put(ref branch.Children[path[0]], path.Skip(1), v);
                    db.Put(branch);
                    node = branch;
                    return(true);
                }
                return(false);
            }

            case ExtensionNode extensionNode:
            {
                if (path.AsSpan().StartsWith(extensionNode.Key))
                {
                    var result = Put(ref extensionNode.Next, path.Skip(extensionNode.Key.Length), val);
                    if (result)
                    {
                        extensionNode.SetDirty();
                        db.Put(extensionNode);
                    }
                    return(result);
                }
                var     prefix     = extensionNode.Key.CommonPrefix(path);
                var     pathRemain = path.Skip(prefix.Length);
                var     keyRemain  = extensionNode.Key.Skip(prefix.Length);
                var     son        = new BranchNode();
                MPTNode grandSon1  = HashNode.EmptyNode();
                MPTNode grandSon2  = HashNode.EmptyNode();

                Put(ref grandSon1, keyRemain.Skip(1), extensionNode.Next);
                son.Children[keyRemain[0]] = grandSon1;

                if (pathRemain.Length == 0)
                {
                    Put(ref grandSon2, pathRemain, val);
                    son.Children[BranchNode.ChildCount - 1] = grandSon2;
                }
                else
                {
                    Put(ref grandSon2, pathRemain.Skip(1), val);
                    son.Children[pathRemain[0]] = grandSon2;
                }
                db.Put(son);
                if (prefix.Length > 0)
                {
                    var exNode = new ExtensionNode()
                    {
                        Key  = prefix,
                        Next = son,
                    };
                    db.Put(exNode);
                    node = exNode;
                }
                else
                {
                    node = son;
                }
                return(true);
            }

            case BranchNode branchNode:
            {
                bool result;
                if (path.Length == 0)
                {
                    result = Put(ref branchNode.Children[BranchNode.ChildCount - 1], path, val);
                }
                else
                {
                    result = Put(ref branchNode.Children[path[0]], path.Skip(1), val);
                }
                if (result)
                {
                    branchNode.SetDirty();
                    db.Put(branchNode);
                }
                return(result);
            }

            case HashNode hashNode:
            {
                MPTNode newNode;
                if (hashNode.IsEmptyNode)
                {
                    if (path.Length == 0)
                    {
                        newNode = val;
                    }
                    else
                    {
                        newNode = new ExtensionNode()
                        {
                            Key  = path,
                            Next = val,
                        };
                        db.Put(newNode);
                    }
                    node = newNode;
                    if (val is LeafNode)
                    {
                        db.Put(val);
                    }
                    return(true);
                }
                newNode = Resolve(hashNode);
                if (newNode is null)
                {
                    return(false);
                }
                node = newNode;
                return(Put(ref node, path, val));
            }

            default:
                throw new System.InvalidOperationException("Invalid node type.");
            }
        }
Example #3
0
        private bool TryDelete(ref MPTNode node, byte[] path)
        {
            switch (node)
            {
            case LeafNode _:
            {
                if (path.Length == 0)
                {
                    node = HashNode.EmptyNode();
                    return(true);
                }
                return(false);
            }

            case ExtensionNode extensionNode:
            {
                if (path.AsSpan().StartsWith(extensionNode.Key))
                {
                    var result = TryDelete(ref extensionNode.Next, path.Skip(extensionNode.Key.Length));
                    if (!result)
                    {
                        return(false);
                    }
                    if (extensionNode.Next is HashNode hashNode && hashNode.IsEmptyNode)
                    {
                        node = extensionNode.Next;
                        return(true);
                    }
                    if (extensionNode.Next is ExtensionNode en)
                    {
                        extensionNode.Key  = extensionNode.Key.Concat(en.Key);
                        extensionNode.Next = en.Next;
                    }
                    extensionNode.SetDirty();
                    db.Put(extensionNode);
                    return(true);
                }
                return(false);
            }

            case BranchNode branchNode:
            {
                bool result;
                if (path.Length == 0)
                {
                    result = TryDelete(ref branchNode.Children[BranchNode.ChildCount - 1], path);
                }
                else
                {
                    result = TryDelete(ref branchNode.Children[path[0]], path.Skip(1));
                }
                if (!result)
                {
                    return(false);
                }
                List <byte> childrenIndexes = new List <byte>();
                for (int i = 0; i < BranchNode.ChildCount; i++)
                {
                    if (branchNode.Children[i] is HashNode hn && hn.IsEmptyNode)
                    {
                        continue;
                    }
                    childrenIndexes.Add((byte)i);
                }
                if (childrenIndexes.Count > 1)
                {
                    branchNode.SetDirty();
                    db.Put(branchNode);
                    return(true);
                }
                var lastChildIndex = childrenIndexes[0];
                var lastChild      = branchNode.Children[lastChildIndex];
                if (lastChildIndex == BranchNode.ChildCount - 1)
                {
                    node = lastChild;
                    return(true);
                }
                if (lastChild is HashNode hashNode)
                {
                    lastChild = Resolve(hashNode);
                    if (lastChild is null)
                    {
                        throw new System.ArgumentNullException("Invalid hash node");
                    }
                }
                if (lastChild is ExtensionNode exNode)
                {
                    exNode.Key = Helper.Concat(childrenIndexes.ToArray(), exNode.Key);
                    exNode.SetDirty();
                    db.Put(exNode);
                    node = exNode;
                    return(true);
                }
                var newNode = new ExtensionNode()
                {
                    Key  = childrenIndexes.ToArray(),
                    Next = lastChild,
                };
                node = newNode;
                db.Put(newNode);
                return(true);
            }

            case HashNode hashNode:
            {
                if (hashNode.IsEmptyNode)
                {
                    return(true);
                }
                var new_node = Resolve(hashNode);
                if (new_node is null)
                {
                    return(false);
                }
                node = new_node;
                return(TryDelete(ref node, path));
            }

            default:
                return(false);
            }
        }