You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
151 lines
3.7 KiB
JavaScript
151 lines
3.7 KiB
JavaScript
2 years ago
|
/*
|
||
|
* Copyright DataStax, Inc.
|
||
|
*
|
||
|
* 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.
|
||
|
*/
|
||
|
|
||
|
'use strict';
|
||
|
|
||
|
const EventEmitter = require('events');
|
||
|
|
||
|
/**
|
||
|
* Represents a tree node where the key is composed by 1 or more strings.
|
||
|
* @ignore
|
||
|
*/
|
||
|
class Node extends EventEmitter {
|
||
|
/**
|
||
|
* Creates a new instance of {@link Node}.
|
||
|
* @param {Array<String>} key
|
||
|
* @param {Object} value
|
||
|
* @param {Array} [edges]
|
||
|
*/
|
||
|
constructor(key, value, edges) {
|
||
|
super();
|
||
|
this.key = key;
|
||
|
this.value = value;
|
||
|
this.edges = edges || [];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* A radix tree where each node contains a key, a value and edges.
|
||
|
* @ignore
|
||
|
*/
|
||
|
class Tree extends Node {
|
||
|
constructor() {
|
||
|
super([], null);
|
||
|
this.length = 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets the existing item in the tree or creates a new one with the value provided by valueHandler
|
||
|
* @param {Iterator} keyIterator
|
||
|
* @param {Function} valueHandler
|
||
|
* @return {Object}
|
||
|
*/
|
||
|
getOrCreate(keyIterator, valueHandler) {
|
||
|
if (typeof keyIterator.next !== 'function') {
|
||
|
keyIterator = keyIterator[Symbol.iterator]();
|
||
|
}
|
||
|
let node = this;
|
||
|
let isMatch = false;
|
||
|
let item = keyIterator.next();
|
||
|
while (true) {
|
||
|
let newBranch;
|
||
|
// Check node keys at position 1 and above
|
||
|
for (let i = 1; i < node.key.length; i++) {
|
||
|
if (item.done || node.key[i] !== item.value) {
|
||
|
// We should branch out
|
||
|
newBranch = this._createBranch(node, i, item.done, valueHandler);
|
||
|
break;
|
||
|
}
|
||
|
item = keyIterator.next();
|
||
|
}
|
||
|
|
||
|
if (item.done) {
|
||
|
isMatch = true;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (newBranch !== undefined) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
const edges = node.edges;
|
||
|
let nextNode;
|
||
|
for (let i = 0; i < edges.length; i++) {
|
||
|
const e = edges[i];
|
||
|
if (e.key[0] === item.value) {
|
||
|
// its a match
|
||
|
nextNode = e;
|
||
|
item = keyIterator.next();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (nextNode === undefined) {
|
||
|
// Current node is the root for a new leaf
|
||
|
break;
|
||
|
}
|
||
|
else {
|
||
|
node = nextNode;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!isMatch) {
|
||
|
// Create using "node" as the root
|
||
|
const value = valueHandler();
|
||
|
node.edges.push(new Node(iteratorToArray(item.value, keyIterator), value));
|
||
|
this._onItemAdded();
|
||
|
return value;
|
||
|
}
|
||
|
if (node.value === null && node.edges.length > 0) {
|
||
|
node.value = valueHandler();
|
||
|
}
|
||
|
return node.value;
|
||
|
}
|
||
|
|
||
|
_createBranch(node, index, useNewValue, valueHandler) {
|
||
|
const newBranch = new Node(node.key.slice(index), node.value, node.edges);
|
||
|
node.key = node.key.slice(0, index);
|
||
|
node.edges = [ newBranch ];
|
||
|
if (useNewValue) {
|
||
|
// The previous node value has moved to a leaf
|
||
|
// The node containing the new leaf should use the new value
|
||
|
node.value = valueHandler();
|
||
|
this._onItemAdded();
|
||
|
}
|
||
|
else {
|
||
|
// Clear the value as it was copied in the branch
|
||
|
node.value = null;
|
||
|
}
|
||
|
return newBranch;
|
||
|
}
|
||
|
|
||
|
_onItemAdded() {
|
||
|
this.length++;
|
||
|
this.emit('add', this.length);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function iteratorToArray(value, iterator) {
|
||
|
const values = [ value ];
|
||
|
let item = iterator.next();
|
||
|
while (!item.done) {
|
||
|
values.push(item.value);
|
||
|
item = iterator.next();
|
||
|
}
|
||
|
return values;
|
||
|
}
|
||
|
|
||
|
module.exports = Tree;
|