2018-06-28 15:39:04 +03:00
|
|
|
/*
|
|
|
|
* Copyright 2018 Fluence Labs Limited
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2018-06-05 13:15:12 +05:00
|
|
|
package kvstore
|
|
|
|
|
|
|
|
import kvstore.MerkleUtil._
|
|
|
|
|
|
|
|
import scala.collection.immutable.HashMap
|
|
|
|
import scala.util.Try
|
2018-06-09 01:42:02 +05:00
|
|
|
import scala.util.matching.Regex
|
2018-06-05 13:15:12 +05:00
|
|
|
|
2018-06-06 07:25:50 +05:00
|
|
|
/**
|
|
|
|
* Node
|
|
|
|
*
|
|
|
|
* merkleHash set to None when branch changed. Later None merkle hashes recalculated when [[Node.merkelize]] invoked
|
|
|
|
*
|
2018-06-09 01:42:02 +05:00
|
|
|
* @param children child nodes
|
|
|
|
* @param value assigned value, if exists
|
2018-06-06 07:25:50 +05:00
|
|
|
* @param merkleHash Merkle hash of a node's subtree, if calculated
|
|
|
|
*/
|
2018-06-05 13:15:12 +05:00
|
|
|
case class Node(children: NodeStorage, value: Option[String], merkleHash: Option[MerkleHash]) {
|
|
|
|
def merkelize(): Node =
|
|
|
|
if (merkleHash.isDefined)
|
|
|
|
this
|
|
|
|
else {
|
2018-06-09 01:42:02 +05:00
|
|
|
val newChildren = children.mapValues(_.merkelize())
|
2018-06-05 13:15:12 +05:00
|
|
|
val withNewChildren = Node(newChildren, value, None)
|
2018-06-09 01:42:02 +05:00
|
|
|
Node(newChildren, value, Some(mergeMerkle(withNewChildren.merkleItems(), HexBasedMerkleMerge)))
|
2018-06-05 13:15:12 +05:00
|
|
|
}
|
|
|
|
|
|
|
|
private def merkleItems(): List[MerkleHash] =
|
2018-06-09 01:42:02 +05:00
|
|
|
singleHash(value.getOrElse("")) :: children.flatMap(x => List(singleHash(x._1), x._2.merkleHash.get)).toList
|
2018-06-05 13:15:12 +05:00
|
|
|
|
2018-06-09 01:42:02 +05:00
|
|
|
def getProof(key: String): List[List[MerkleHash]] =
|
2018-06-05 13:15:12 +05:00
|
|
|
if (key.isEmpty)
|
|
|
|
List(merkleItems())
|
|
|
|
else {
|
|
|
|
val (next, rest) = splitPath(key)
|
|
|
|
merkleItems() :: children(next).getProof(rest)
|
|
|
|
}
|
|
|
|
|
|
|
|
def longValue: Option[Long] = value.flatMap(x => Try(x.toLong).toOption)
|
|
|
|
|
2018-06-09 01:42:02 +05:00
|
|
|
val rangeKeyValuePattern: Regex = "(\\d{1,8})-(\\d{1,8}):(.+)".r
|
2018-06-05 13:15:12 +05:00
|
|
|
|
2018-06-09 01:42:02 +05:00
|
|
|
def add(key: String, value: String): Node =
|
2018-06-05 13:15:12 +05:00
|
|
|
key match {
|
|
|
|
case rangeKeyValuePattern(rangeStartStr, rangeEndStr, keyPattern) =>
|
2018-06-06 07:25:50 +05:00
|
|
|
// range pattern allows to set multiple keys in single transaction
|
|
|
|
// range defined by starting and ending index, key pattern may contains hexadecimal digits of current index
|
|
|
|
|
2018-06-05 13:15:12 +05:00
|
|
|
val rangeStart = rangeStartStr.toInt
|
|
|
|
val rangeEnd = rangeEndStr.toInt
|
|
|
|
System.out.println(s"setting range from=$rangeStart to=$rangeEnd keyPattern=$keyPattern valuePattern=$value")
|
|
|
|
|
|
|
|
var currentNode = this
|
|
|
|
for (index <- rangeStart until rangeEnd) {
|
|
|
|
var key = keyPattern
|
|
|
|
var effectiveValue = value
|
|
|
|
for (hexPosition <- 0 to 6) {
|
|
|
|
val target = "@" + hexPosition
|
|
|
|
val replacement = ((index >> hexPosition * 4) & 0xf).toHexString
|
|
|
|
key = key.replace(target, replacement)
|
|
|
|
effectiveValue = effectiveValue.replace(target, replacement)
|
|
|
|
}
|
|
|
|
System.out.println(s"setting key=$key value=$effectiveValue")
|
|
|
|
currentNode = currentNode.addValue(key, effectiveValue)
|
|
|
|
}
|
|
|
|
|
|
|
|
currentNode
|
|
|
|
case _ =>
|
|
|
|
System.out.println(s"setting key=$key value=$value")
|
|
|
|
addValue(key, value)
|
|
|
|
}
|
|
|
|
|
2018-06-09 01:42:02 +05:00
|
|
|
private def addValue(key: String, value: String): Node =
|
2018-06-05 13:15:12 +05:00
|
|
|
if (key.isEmpty)
|
|
|
|
Node(children, Some(value), None)
|
|
|
|
else {
|
|
|
|
val (next, rest) = splitPath(key)
|
|
|
|
Node(children + (next -> children.getOrElse(next, Node.emptyNode).addValue(rest, value)), this.value, None)
|
|
|
|
}
|
|
|
|
|
2018-06-09 01:42:02 +05:00
|
|
|
def getNode(key: String): Option[Node] =
|
2018-06-05 13:15:12 +05:00
|
|
|
if (key.isEmpty)
|
|
|
|
Some(this)
|
|
|
|
else {
|
|
|
|
val (next, rest) = splitPath(key)
|
|
|
|
children.get(next).flatMap(_.getNode(rest))
|
|
|
|
}
|
|
|
|
|
|
|
|
def getValue(key: String): Option[String] = getNode(key).flatMap(_.value)
|
|
|
|
|
|
|
|
def getLongValue(key: String): Option[Long] = getNode(key).flatMap(_.longValue)
|
|
|
|
|
2018-06-09 01:42:02 +05:00
|
|
|
def listChildren(key: String): Option[List[String]] =
|
2018-06-05 13:15:12 +05:00
|
|
|
if (key.isEmpty)
|
|
|
|
Some(children.keys.toList)
|
|
|
|
else {
|
|
|
|
val (next, rest) = splitPath(key)
|
|
|
|
children.get(next).flatMap(_.listChildren(rest))
|
|
|
|
}
|
|
|
|
|
|
|
|
private def splitPath(path: String): (String, String) = {
|
|
|
|
val (next, rest) = path.span(_ != '/')
|
|
|
|
(next, rest.replaceFirst("/", ""))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
object Node {
|
|
|
|
val emptyNode: Node = Node(HashMap.empty[String, Node], None, None)
|
|
|
|
}
|