first commit
This commit is contained in:
249
node_modules/number-allocator/lib/number-allocator.js
generated
vendored
Normal file
249
node_modules/number-allocator/lib/number-allocator.js
generated
vendored
Normal file
@@ -0,0 +1,249 @@
|
||||
// Copyright Takatoshi Kondo 2021
|
||||
//
|
||||
// Distributed under the MIT License
|
||||
|
||||
'use strict'
|
||||
|
||||
const SortedSet = require('js-sdsl').OrderedSet
|
||||
const debugTrace = require('debug')('number-allocator:trace')
|
||||
const debugError = require('debug')('number-allocator:error')
|
||||
/**
|
||||
* Interval constructor
|
||||
* @constructor
|
||||
* @param {Number} low - The lowest value of the interval
|
||||
* @param {Number} high - The highest value of the interval
|
||||
*/
|
||||
function Interval (low, high) {
|
||||
this.low = low
|
||||
this.high = high
|
||||
}
|
||||
|
||||
Interval.prototype.equals = function (other) {
|
||||
return this.low === other.low && this.high === other.high
|
||||
}
|
||||
|
||||
Interval.prototype.compare = function (other) {
|
||||
if (this.low < other.low && this.high < other.low) return -1
|
||||
if (other.low < this.low && other.high < this.low) return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
/**
|
||||
* NumberAllocator constructor.
|
||||
* The all numbers are set to vacant status.
|
||||
* Time Complexity O(1)
|
||||
* @constructor
|
||||
* @param {Number} min - The maximum number of allocatable. The number must be integer.
|
||||
* @param {Number} maxh - The minimum number of allocatable. The number must be integer.
|
||||
*/
|
||||
function NumberAllocator (min, max) {
|
||||
if (!(this instanceof NumberAllocator)) {
|
||||
return new NumberAllocator(min, max)
|
||||
}
|
||||
|
||||
this.min = min
|
||||
this.max = max
|
||||
|
||||
this.ss = new SortedSet(
|
||||
[],
|
||||
(lhs, rhs) => {
|
||||
return lhs.compare(rhs)
|
||||
}
|
||||
)
|
||||
debugTrace('Create')
|
||||
this.clear()
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first vacant number. The status of the number is not updated.
|
||||
* Time Complexity O(1)
|
||||
* @return {Number} - The first vacant number. If all numbers are occupied, return null.
|
||||
* When alloc() is called then the same value will be allocated.
|
||||
*/
|
||||
NumberAllocator.prototype.firstVacant = function () {
|
||||
if (this.ss.size() === 0) return null
|
||||
return this.ss.front().low
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate the first vacant number. The number become occupied status.
|
||||
* Time Complexity O(1)
|
||||
* @return {Number} - The first vacant number. If all numbers are occupied, return null.
|
||||
*/
|
||||
NumberAllocator.prototype.alloc = function () {
|
||||
if (this.ss.size() === 0) {
|
||||
debugTrace('alloc():empty')
|
||||
return null
|
||||
}
|
||||
const it = this.ss.begin()
|
||||
const low = it.pointer.low
|
||||
const high = it.pointer.high
|
||||
const num = low
|
||||
if (num + 1 <= high) {
|
||||
// x|----|
|
||||
this.ss.updateKeyByIterator(it, new Interval(low + 1, high))
|
||||
} else {
|
||||
this.ss.eraseElementByPos(0)
|
||||
}
|
||||
debugTrace('alloc():' + num)
|
||||
return num
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the number. The number become occupied status.
|
||||
* If the number has already been occupied, then return false.
|
||||
* Time Complexity O(logN) : N is the number of intervals (not numbers)
|
||||
* @param {Number} num - The number to request use.
|
||||
* @return {Boolean} - If `num` was not occupied, then return true, otherwise return false.
|
||||
*/
|
||||
NumberAllocator.prototype.use = function (num) {
|
||||
const key = new Interval(num, num)
|
||||
const it = this.ss.lowerBound(key)
|
||||
if (!it.equals(this.ss.end())) {
|
||||
const low = it.pointer.low
|
||||
const high = it.pointer.high
|
||||
if (it.pointer.equals(key)) {
|
||||
// |x|
|
||||
this.ss.eraseElementByIterator(it)
|
||||
debugTrace('use():' + num)
|
||||
return true
|
||||
}
|
||||
|
||||
// x |-----|
|
||||
if (low > num) return false
|
||||
|
||||
// |x----|
|
||||
if (low === num) {
|
||||
// x|----|
|
||||
this.ss.updateKeyByIterator(it, new Interval(low + 1, high))
|
||||
debugTrace('use():' + num)
|
||||
return true
|
||||
}
|
||||
|
||||
// |----x|
|
||||
if (high === num) {
|
||||
// |----|x
|
||||
this.ss.updateKeyByIterator(it, new Interval(low, high - 1))
|
||||
debugTrace('use():' + num)
|
||||
return true
|
||||
}
|
||||
|
||||
// |--x--|
|
||||
// x|--|
|
||||
this.ss.updateKeyByIterator(it, new Interval(num + 1, high))
|
||||
// |--|x|--|
|
||||
this.ss.insert(new Interval(low, num - 1))
|
||||
debugTrace('use():' + num)
|
||||
return true
|
||||
}
|
||||
|
||||
debugTrace('use():failed')
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Deallocate the number. The number become vacant status.
|
||||
* Time Complexity O(logN) : N is the number of intervals (not numbers)
|
||||
* @param {Number} num - The number to deallocate. The number must be occupied status.
|
||||
* In other words, the number must be allocated by alloc() or occupied be use().
|
||||
*/
|
||||
NumberAllocator.prototype.free = function (num) {
|
||||
if (num < this.min || num > this.max) {
|
||||
debugError('free():' + num + ' is out of range')
|
||||
return
|
||||
}
|
||||
const key = new Interval(num, num)
|
||||
const it = this.ss.upperBound(key)
|
||||
if (it.equals(this.ss.end())) {
|
||||
// ....v
|
||||
if (it.equals(this.ss.begin())) {
|
||||
// Insert new interval
|
||||
this.ss.insert(key)
|
||||
return
|
||||
}
|
||||
it.pre()
|
||||
const low = it.pointer.high
|
||||
const high = it.pointer.high
|
||||
if (high + 1 === num) {
|
||||
// Concat to left
|
||||
this.ss.updateKeyByIterator(it, new Interval(low, num))
|
||||
} else {
|
||||
// Insert new interval
|
||||
this.ss.insert(key)
|
||||
}
|
||||
} else {
|
||||
if (it.equals(this.ss.begin())) {
|
||||
// v....
|
||||
if (num + 1 === it.pointer.low) {
|
||||
// Concat to right
|
||||
const high = it.pointer.high
|
||||
this.ss.updateKeyByIterator(it, new Interval(num, high))
|
||||
} else {
|
||||
// Insert new interval
|
||||
this.ss.insert(key)
|
||||
}
|
||||
} else {
|
||||
// ..v..
|
||||
const rLow = it.pointer.low
|
||||
const rHigh = it.pointer.high
|
||||
it.pre()
|
||||
const lLow = it.pointer.low
|
||||
const lHigh = it.pointer.high
|
||||
if (lHigh + 1 === num) {
|
||||
if (num + 1 === rLow) {
|
||||
// Concat to left and right
|
||||
this.ss.eraseElementByIterator(it)
|
||||
this.ss.updateKeyByIterator(it, new Interval(lLow, rHigh))
|
||||
} else {
|
||||
// Concat to left
|
||||
this.ss.updateKeyByIterator(it, new Interval(lLow, num))
|
||||
}
|
||||
} else {
|
||||
if (num + 1 === rLow) {
|
||||
// Concat to right
|
||||
this.ss.eraseElementByIterator(it.next())
|
||||
this.ss.insert(new Interval(num, rHigh))
|
||||
} else {
|
||||
// Insert new interval
|
||||
this.ss.insert(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
debugTrace('free():' + num)
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all occupied numbers.
|
||||
* The all numbers are set to vacant status.
|
||||
* Time Complexity O(1)
|
||||
*/
|
||||
NumberAllocator.prototype.clear = function () {
|
||||
debugTrace('clear()')
|
||||
this.ss.clear()
|
||||
this.ss.insert(new Interval(this.min, this.max))
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of intervals. Interval is internal structure of this library.
|
||||
* This function is for debugging.
|
||||
* Time Complexity O(1)
|
||||
* @return {Number} - The number of intervals.
|
||||
*/
|
||||
NumberAllocator.prototype.intervalCount = function () {
|
||||
return this.ss.size()
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump the internal structor of the library.
|
||||
* This function is for debugging.
|
||||
* Time Complexity O(N) : N is the number of intervals (not numbers)
|
||||
*/
|
||||
NumberAllocator.prototype.dump = function () {
|
||||
console.log('length:' + this.ss.size())
|
||||
for (const element of this.ss) {
|
||||
console.log(element)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = NumberAllocator
|
Reference in New Issue
Block a user