Home Reference Source Repository

lib/strategy.js

'use strict';

const BI = require('BigInt');
const Identifier = require('./identifier.js');

/**
 * Enumerate the available sub-allocation strategies. The signature of these
 * functions is f(Id, Id, N+, N+, N, N): Id.
 */
class Strategy {    
    /**
     * @param {Base} base The base used to create the new identifiers.
     * @param {Number} [boundary = 10] The value used as the default maximum
     * spacing between identifiers.
     */
    constructor (base, boundary = 10) {
        this._base = base;
        this._boundary = boundary;
    };
    
    /**
     * Choose an identifier starting from previous bound and adding random
     * number.
     * @param {LSeqNode} p The previous identifier.
     * @param {LSeqNode} q The next identifier.
     * @param {Number} level The number of concatenation composing the new
     * identifier.
     * @param {Number} interval The interval between p and q.
     * @param {Object} s The source that creates the new identifier.
     * @param {Number} c The counter of that source.
     * @return {Identifier} The new allocated identifier.
     */
    bPlus (p, q, level, interval, s, c) {
        let copyP = p, copyQ = q,
            step = Math.min(this._boundary, interval), //#0 the min interval
            digit = BI.int2bigInt(0, this._base.getSumBit(level)),
            value;
        
        // #1 copy the previous identifier
        for (let i = 0; i <= level; ++i) {
	    value = (p && p.t.p) || 0;
            BI.addInt_(digit, value);
            if (i !== level) {
                BI.leftShift_(digit, this._base.getBitBase(i + 1));
            };
            p = (p && !p.isLeaf && p.child) || null;
        };
        // #2 create a digit for an identifier by adding a random value
        // #A Digit
        BI.addInt_(digit, Math.floor(Math.random() * step + 1));
        // #B Source & counter
        return this._getSC(digit, copyP, copyQ, level, s, c);
    };


    
    /**
     * Choose an identifier starting from next bound and substract a random
     * number.
     * @param {LSeqNode} p The previous identifier.
     * @param {LSeqNode} q The next identifier.
     * @param {Number} level The number of concatenation composing the new
     * identifier.
     * @param {Number} interval The interval between p and q.
     * @param {Object} s The source that creates the new identifier.
     * @param {Number} c The counter of that source.
     */
    bMinus (p, q, level, interval, s, c) {
        let copyP = p, copyQ = q,
            step = Math.min(this._boundary, interval),// #0 process min interval
            digit = BI.int2bigInt(0, this._base.getSumBit(level)),
            pIsGreater = false, commonRoot = true,
            prevValue, nextValue;
        
        // #1 copy next, if previous is greater, copy maxValue @ depth
        for (let i = 0; i <= level; ++i) {
            prevValue = (p && p.t.p) || 0;
            nextValue = (q && q.t.p) || 0;
            
            if (commonRoot && prevValue !== nextValue) {
                commonRoot = false;
                pIsGreater = prevValue > nextValue;
            };
            if (pIsGreater) {
                nextValue = Math.pow(2,this._base.getBitBase(i))-1;
            };
            BI.addInt_(digit, nextValue);
            if (i !== level) {
                BI.leftShift_(digit,this._base.getBitBase(i+1));
            };

            q = (q && !q.isLeaf && q.child) || null;
            p = (p && !p.isLeaf && p.child) || null;
        };
        
        // #3 create a digit for an identifier by subing a random value
        // #A Digit
        if (pIsGreater) {
            BI.addInt_(digit, -Math.floor(Math.random()*step) );
        } else {
            BI.addInt_(digit, -Math.floor(Math.random()*step)-1 );
        };
    
        // #B Source & counter
        return this._getSC(digit, copyP, copyQ, level, s, c);
    };

    /**
     * Copies the appropriates source and counter from the adjacent identifiers
     * at the insertion position.
     * @param {Number} d The digit part of the new identifier.
     * @param {LSeqNode} p The previous identifier.
     * @param {LSeqNode} q the next identifier.
     * @param {Number} level The size of the new identifier.
     * @param {Object} s The local site identifier.
     * @param {Number} c The local monotonic counter.
     * @return {Identifier} The new allocated identifier.
     */
    _getSC (d, p, q, level, s, c) {
        let sources = [], counters = [],
            i = 0,
            sumBit = this._base.getSumBit(level),
            tempDigit, value;
        
        while (i <= level) {
            tempDigit = BI.dup(d);
            BI.rightShift_(tempDigit, sumBit - this._base.getSumBit(i));
            value = BI.modInt(tempDigit, Math.pow(2, this._base.getBitBase(i)));
            sources[i]=s;
            counters[i]=c;
            
            if (q && q.t.p === value) { sources[i]=q.t.s; counters[i]=q.t.c; };
            if (p && p.t.p === value) { sources[i]=p.t.s; counters[i]=p.t.c; };
            
            q = (q && !q.isLeaf && q.child) || null;
            p = (p && !p.isLeaf && p.child) || null;

            ++i;
        };
        
        return new Identifier(this._base, d, sources, counters);
    };
    
};

module.exports = Strategy;