104 lines
1.9 KiB
JavaScript
104 lines
1.9 KiB
JavaScript
'use strict'
|
|
|
|
class Hoopy extends Array {
|
|
constructor (size) {
|
|
let index, isIndexOverflowed
|
|
|
|
if (! isPositiveInteger(size)) {
|
|
throw new TypeError('Argument `size` must be a positive integer.')
|
|
}
|
|
|
|
super(size)
|
|
|
|
this.grow = by => {
|
|
if (! isPositiveInteger(by)) {
|
|
throw new TypeError('Argument `by` must be a positive integer.')
|
|
}
|
|
|
|
let i
|
|
const newSize = size + by
|
|
|
|
for (i = size; i < newSize; ++i) {
|
|
this[i] = undefined
|
|
}
|
|
|
|
if (isIndexOverflowed) {
|
|
for (i = 0; i <= index; ++i) {
|
|
let j = size + i
|
|
if (j >= newSize) {
|
|
j %= newSize
|
|
}
|
|
this[j] = this[i]
|
|
this[i] = undefined
|
|
}
|
|
}
|
|
|
|
size = newSize
|
|
}
|
|
|
|
return new Proxy(this, {
|
|
get (target, key) {
|
|
if (isInteger(key)) {
|
|
return target[getIndex(key, size)]
|
|
}
|
|
|
|
return target[key]
|
|
},
|
|
|
|
set (target, key, value) {
|
|
if (isInteger(key)) {
|
|
index = getIndex(key, size)
|
|
target[index] = value
|
|
|
|
if (Math.abs(key) >= size) {
|
|
isIndexOverflowed = true
|
|
} else {
|
|
isIndexOverflowed = false
|
|
}
|
|
} else {
|
|
target[key] = value
|
|
}
|
|
return true
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
function isPositiveInteger (thing) {
|
|
return isInteger(thing) && thing > 0
|
|
}
|
|
|
|
function isInteger (thing) {
|
|
try {
|
|
return +thing % 1 === 0
|
|
} catch (error) {
|
|
// Coercing symbols to numbers throws an error
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
function getIndex (key, size) {
|
|
if (key === 0) {
|
|
return 0
|
|
}
|
|
|
|
if (key < 0) {
|
|
return (size - Math.abs(key)) % size
|
|
}
|
|
|
|
return key % size
|
|
}
|
|
|
|
function nop () {
|
|
throw new Error('Not implemented')
|
|
}
|
|
|
|
Hoopy.prototype.push = nop
|
|
Hoopy.prototype.pop = nop
|
|
Hoopy.prototype.shift = nop
|
|
Hoopy.prototype.unshift = nop
|
|
|
|
module.exports = Hoopy
|
|
|