fcoin/lib/utils/list.js
2018-03-29 21:56:47 -07:00

291 lines
4.5 KiB
JavaScript

/*!
* list.js - double linked list for bcoin
* Copyright (c) 2014-2017, Christopher Jeffrey (MIT License).
* https://github.com/bcoin-org/bcoin
*/
'use strict';
const assert = require('assert');
/**
* Double Linked List
* @alias module:utils.List
*/
class List {
/**
* Create a list.
* @constructor
* @property {ListItem|null} head
* @property {ListItem|null} tail
* @property {Number} size
*/
constructor() {
this.head = null;
this.tail = null;
this.size = 0;
}
/**
* Reset the cache. Clear all items.
*/
reset() {
let item, next;
for (item = this.head; item; item = next) {
next = item.next;
item.prev = null;
item.next = null;
}
assert(!item);
this.head = null;
this.tail = null;
this.size = 0;
}
/**
* Remove the first item in the list.
* @returns {ListItem}
*/
shift() {
const item = this.head;
if (!item)
return null;
this.remove(item);
return item;
}
/**
* Prepend an item to the linked list (sets new head).
* @param {ListItem}
* @returns {Boolean}
*/
unshift(item) {
return this.insert(null, item);
}
/**
* Append an item to the linked list (sets new tail).
* @param {ListItem}
* @returns {Boolean}
*/
push(item) {
return this.insert(this.tail, item);
}
/**
* Remove the last item in the list.
* @returns {ListItem}
*/
pop() {
const item = this.tail;
if (!item)
return null;
this.remove(item);
return item;
}
/**
* Insert item into the linked list.
* @private
* @param {ListItem|null} ref
* @param {ListItem} item
* @returns {Boolean}
*/
insert(ref, item) {
if (item.prev || item.next || item === this.head)
return false;
assert(!item.prev);
assert(!item.next);
if (ref == null) {
if (!this.head) {
this.head = item;
this.tail = item;
} else {
this.head.prev = item;
item.next = this.head;
this.head = item;
}
this.size += 1;
return true;
}
item.next = ref.next;
item.prev = ref;
ref.next = item;
if (ref === this.tail)
this.tail = item;
this.size += 1;
return true;
}
/**
* Remove item from the linked list.
* @private
* @param {ListItem}
* @returns {Boolean}
*/
remove(item) {
if (!item.prev && !item.next && item !== this.head)
return false;
if (item.prev)
item.prev.next = item.next;
if (item.next)
item.next.prev = item.prev;
if (item === this.head)
this.head = item.next;
if (item === this.tail)
this.tail = item.prev || this.head;
if (!this.head)
assert(!this.tail);
if (!this.tail)
assert(!this.head);
item.prev = null;
item.next = null;
this.size -= 1;
return true;
}
/**
* Replace an item in-place.
* @param {ListItem} ref
* @param {ListItem} item
*/
replace(ref, item) {
if (ref.prev)
ref.prev.next = item;
if (ref.next)
ref.next.prev = item;
item.prev = ref.prev;
item.next = ref.next;
ref.next = null;
ref.prev = null;
if (this.head === ref)
this.head = item;
if (this.tail === ref)
this.tail = item;
}
/**
* Slice the list to an array of items.
* Will remove the items sliced.
* @param {Number?} total
* @returns {ListItem[]}
*/
slice(total) {
if (total == null)
total = -1;
const items = [];
let next = null;
for (let item = this.head; item; item = next) {
next = item.next;
item.prev = null;
item.next = null;
this.size -= 1;
items.push(item);
if (items.length === total)
break;
}
if (next) {
this.head = next;
next.prev = null;
} else {
this.head = null;
this.tail = null;
}
return items;
}
/**
* Convert the list to an array of items.
* @returns {ListItem[]}
*/
toArray() {
const items = [];
for (let item = this.head; item; item = item.next)
items.push(item);
return items;
}
}
/**
* List Item
* @alias module:utils.ListItem
*/
class ListItem {
/**
* Create a list item.
* @constructor
* @private
* @param {String} key
* @param {Object} value
*/
constructor(value) {
this.next = null;
this.prev = null;
this.value = value;
}
}
/*
* Expose
*/
exports = List;
exports.List = List;
exports.ListItem = ListItem;
exports.Item = ListItem;
module.exports = exports;