serverless-spy
Version:
CDK-based library for writing elegant integration tests on AWS serverless architecture and an additional web console to monitor events in real time.
252 lines (178 loc) • 4.97 kB
JavaScript
/* eslint no-constant-condition: 0 */
/**
* Mnemonist SemiDynamicTrie
* ==========================
*
* Lowlevel Trie working at character level, storing information in typed
* array and organizing its children in linked lists.
*
* This implementation also uses a "fat node" strategy to boost access to some
* bloated node's children when the number of children rises above a certain
* threshold.
*/
var Vector = require('./vector.js');
// TODO: rename => ternary search tree
/**
* Constants.
*/
const MAX_LINKED = 7;
/**
* SemiDynamicTrie.
*
* @constructor
*/
function SemiDynamicTrie() {
// Properties
// TODO: make it 16 bits
this.characters = new Vector.Uint8Vector(256);
this.nextPointers = new Vector.Int32Vector(256);
this.childPointers = new Vector.Uint32Vector(256);
this.maps = new Vector.Uint32Vector(256);
}
/**
* Method used to clear the structure.
*
* @return {undefined}
*/
SemiDynamicTrie.prototype.clear = function() {
// Properties
};
SemiDynamicTrie.prototype.ensureSibling = function(block, character) {
var nextCharacter,
nextBlock,
newBlock;
// Do we have a root?
if (this.characters.length === 0) {
this.nextPointers.push(0);
this.childPointers.push(0);
this.characters.push(character);
return block;
}
// Are we traversing a fat node?
var fatNode = this.nextPointers.array[block];
if (fatNode < 0) {
var mapIndex = -fatNode + character;
nextBlock = this.maps.array[mapIndex];
if (nextBlock !== 0)
return nextBlock;
newBlock = this.characters.length;
this.nextPointers.push(0);
this.childPointers.push(0);
this.characters.push(character);
this.maps.set(mapIndex, newBlock);
return newBlock;
}
var listLength = 1,
startingBlock = block;
while (true) {
nextCharacter = this.characters.array[block];
if (nextCharacter === character)
return block;
nextBlock = this.nextPointers.array[block];
if (nextBlock === 0)
break;
listLength++;
block = nextBlock;
}
// If the list is too long, we create a fat node
if (listLength > MAX_LINKED) {
block = startingBlock;
var offset = this.maps.length;
this.maps.resize(offset + 255);
this.maps.set(offset + 255, 0);
while (true) {
nextBlock = this.nextPointers.array[block];
if (nextBlock === 0)
break;
nextCharacter = this.characters.array[nextBlock];
this.maps.set(offset + nextCharacter, nextBlock);
block = nextBlock;
}
this.nextPointers.set(startingBlock, -offset);
newBlock = this.characters.length;
this.nextPointers.push(0);
this.childPointers.push(0);
this.characters.push(character);
this.maps.set(offset + character, newBlock);
return newBlock;
}
// Else, we append the character to the list
newBlock = this.characters.length;
this.nextPointers.push(0);
this.childPointers.push(0);
this.nextPointers.set(block, newBlock);
this.characters.push(character);
return newBlock;
};
SemiDynamicTrie.prototype.findSibling = function(block, character) {
var nextCharacter;
// Do we have a fat node?
var fatNode = this.nextPointers.array[block];
if (fatNode < 0) {
var mapIndex = -fatNode + character;
var nextBlock = this.maps.array[mapIndex];
if (nextBlock === 0)
return -1;
return nextBlock;
}
while (true) {
nextCharacter = this.characters.array[block];
if (nextCharacter === character)
return block;
block = this.nextPointers.array[block];
if (block === 0)
return -1;
}
};
SemiDynamicTrie.prototype.add = function(key) {
var keyCharacter,
childBlock,
block = 0;
var i = 0, l = key.length;
// Going as far as possible
while (i < l) {
keyCharacter = key.charCodeAt(i);
// Ensuring a correct sibling exists
block = this.ensureSibling(block, keyCharacter);
i++;
if (i < l) {
// Descending
childBlock = this.childPointers.array[block];
if (childBlock === 0)
break;
block = childBlock;
}
}
// Adding as many blocks as necessary
while (i < l) {
childBlock = this.characters.length;
this.characters.push(key.charCodeAt(i));
this.childPointers.push(0);
this.nextPointers.push(0);
this.childPointers.set(block, childBlock);
block = childBlock;
i++;
}
};
SemiDynamicTrie.prototype.has = function(key) {
var i, l;
var block = 0,
siblingBlock;
for (i = 0, l = key.length; i < l; i++) {
siblingBlock = this.findSibling(block, key.charCodeAt(i));
if (siblingBlock === -1)
return false;
// TODO: be sure
if (i === l - 1)
return true;
block = this.childPointers.array[siblingBlock];
if (block === 0)
return false;
}
// TODO: fix, should have a leaf pointer somehow
return true;
};
/**
* Exporting.
*/
module.exports = SemiDynamicTrie;