Skip to content
This repository has been archived by the owner on Jul 12, 2024. It is now read-only.

Commit

Permalink
v0.5.0
Browse files Browse the repository at this point in the history
  • Loading branch information
shuritch committed Dec 5, 2023
1 parent ab6c4da commit 28ffb9f
Show file tree
Hide file tree
Showing 12 changed files with 592 additions and 240 deletions.
14 changes: 13 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

## [Unreleased][unreleased]

## [0.5.0][] - 2023-12-06

- New Utility decorator <code>safe</code>
- New Object utilities module
- New structures:
- Linked list
- Pool

## [0.4.2][] - 2023-11-04

- isFunction utility, returns type of function:
Expand Down Expand Up @@ -58,7 +66,11 @@
- Stable release version
- Repository created

[unreleased]: https://github.com/astrohelm/astropack/compare/v0.3.0...HEAD
[unreleased]: https://github.com/astrohelm/astropack/compare/v0.5.0...HEAD
[0.5.0]: https://github.com/astrohelm/astropack/compare/v0.4.0...v0.5.0
[0.4.1]: https://github.com/astrohelm/astropack/compare/v0.4.1...v0.4.2
[0.4.1]: https://github.com/astrohelm/astropack/compare/v0.4.0...v0.4.1
[0.4.0]: https://github.com/astrohelm/astropack/compare/v0.3.0...v0.4.0
[0.3.0]: https://github.com/astrohelm/astropack/compare/v0.2.0...v0.3.0
[0.2.0]: https://github.com/astrohelm/astropack/compare/v0.1.1...v0.2.0
[0.1.1]: https://github.com/astrohelm/astropack/compare/v0.1.0...v0.1.1
Expand Down
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ module.exports = {
utils: require('./lib/utils'),
string: require('./lib/string'),
structs: require('./lib/structs'),
object: require('./lib/object'),
};
30 changes: 30 additions & 0 deletions lib/object.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
'use strict';

const methods = iface => {
const names = [];
for (var name in iface) {
if (typeof iface[name] !== 'function') continue;
names.push(name);
}
return names;
};

const properties = iface => {
const names = [];
for (var name in iface) {
if (typeof iface[name] === 'function') continue;
names.push(name);
}
return names;
};

const entries = sample => {
if (Array.isArray(sample) && sample[0]?.length === 2) return sample;
return sample?.constructor.name === 'Map' ? [...sample.entries()] : Object.entries(sample);
};

module.exports = {
methods,
properties,
entries,
};
7 changes: 7 additions & 0 deletions lib/structs/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict';

module.exports = {
Semaphore: require('./semaphore'),
LinkedList: require('./linked'),
Pool: require('./pool'),
};
125 changes: 125 additions & 0 deletions lib/structs/linked.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
'use strict';

module.exports = function LinkedList() {
// prettier-ignore
var head = null, tail = null, size = 0;
this.indexOf = value => selectByValue(value)[1];
this.update = (i, v) => (select(i).value = v);
this.pop = () => this.delete(size - 1);
this.shift = () => this.delete(0);
this.at = i => select(i).value;
this.unshift = add.bind(null, true);
this.push = add.bind(null, false);
this[Symbol.iterator] = function* () {
var current = head;
yield current.value;
while (current.next) {
current = current.next;
yield current.value;
}
};

Object.defineProperty(this, 'size', {
enumerable: true,
get: () => size,
set: v => {
if (typeof v !== 'number') throw new Error('Size must be a number');
for (var i = 0, key = size < v ? 'push' : 'shift'; i < Math.abs(size - v); ++i) this[key]();
size = v;
},
});

this.delete = index => {
if (index < 0 || index >= size) throw new Error('Invalid index');
if (index === 0 || index === size - 1) {
var item = head;
if (tail === head) tail = head = null;
else if (index === 0) [head.next.prev, head] = [null, head.next];
else [tail.prev.next, item, tail] = [null, tail, tail.prev];
return size--, item.value;
}

const curr = select(index);
if (curr.prev) curr.prev.next = curr.next;
if (curr.next) curr.next.prev = curr.prev;
return size--, curr.value;
};

this.deleteValue = value => {
const [curr, i] = selectByValue(value);
if (!curr) return i;
if (i === 0) {
head.next.prev = null;
head = head.next;
} else if (i === size - 1) {
tail.prev.next = null;
tail.prev.next = tail.prev;
} else {
if (curr.prev) curr.prev.next = curr.next;
if (curr.next) curr.next.prev = curr.prev;
}
return size--, i;
};

return new Proxy(this, {
get: (target, prop, reciever) => {
var type = typeof prop !== 'number';
if (type === Symbol.iterator) return target[Symbol.iterator].bind(target);
if (type !== 'number') return Reflect.get(target, prop, reciever);
return select(prop);
},
set: (target, prop, value, reciever) => {
var type = typeof prop !== 'number';
if (type !== 'number') Reflect.set(target, prop, value, reciever);
while (prop < 0) prop = size + prop;
if (prop > size - 1) target.size = prop;
if (prop < 0) select(prop).value = value;
return true;
},
});

function selectByValue(value) {
for (var i = 0, curr = head; i < size; ++i, curr = curr.next) {
if (curr.value === value) return [curr, i];
}
return [null, -1];
}

function select(index) {
while (index < 0) index = size + index;
if (index >= size) return null;
if (index === size - 1) return tail;
if (index === 0) return head;
const strategy = size / 2 > index;
var [curr, key, step, i] = strategy ? [head, 'next', 1, 0] : [tail, 'prev', -1, size - 1];
for (; i < index; i += step) curr = curr[key];
return curr;
}

function add(flag, ...elements) {
if (!elements.length) return size;
size += elements.length;
if (head === null) {
tail = head = new Node(elements[0]);
elements.length > 1 && add(flag, elements.slice(1));
return size;
}
elements.forEach(v => {
const node = new Node(v);
if (flag) {
node.next = head;
head = head.prev = node;
return;
}
node.prev = tail;
tail = tail.next = node;
});
return size;
}
};

function Node(value) {
this.value = value;
this.next = null;
this.prev = null;
}
10 changes: 10 additions & 0 deletions lib/structs/pool.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
'use strict';

module.exports = Pool;

const DEFAULT_FACTORY = () => null;
function Pool(factory = DEFAULT_FACTORY) {
const storage = [];
this.put = value => void storage.push(value);
this.pop = () => (storage.length ? storage.pop() : factory());
}
12 changes: 7 additions & 5 deletions lib/structs.js → lib/structs/semaphore.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
'use strict';

const Semaphore = function (concurrency, size = 0, timeout = 0) {
if (new.target === undefined) return new Semaphore(concurrency, size, timeout);
if (!new.target) return new Semaphore(concurrency, size, timeout);
[this.queue, this.counter, this.empty] = [[], concurrency, true];

// prettier-ignore
this.enter = () => new Promise((resolve, reject) => {
if (this.counter > 0) {
[this.counter, this.empty] = [this.counter - 1, false];
this.counter--;
this.empty = false;
return void resolve();
}

Expand All @@ -25,17 +26,18 @@ const Semaphore = function (concurrency, size = 0, timeout = 0) {

this.leave = () => {
if (this.queue.length === 0) {
[this.counter, this.empty] = [this.counter + 1, this.counter + 1 === concurrency];
this.counter++;
this.empty = this.counter === concurrency;
return;
}

const { resolve, timer } = this.queue.shift();
clearTimeout(timer);
if (resolve) setTimeout(resolve, 0);
resolve && setTimeout(resolve, 0);
this.empty = this.queue.length === 0 && this.counter === concurrency;
};

return this;
};

module.exports = { Semaphore };
module.exports = Semaphore;
11 changes: 10 additions & 1 deletion lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,13 @@ function isFunction(x) {
return descriptor.writable ? 'function' : 'class';
}

module.exports = { equals, prettyBytes, isFunction };
// prettier-ignore
const safe = fn => (...args) => {
try {
return [null, fn(...args)];
} catch (err) {
return [err, null];
}
};

module.exports = { equals, prettyBytes, isFunction, safe };
Loading

0 comments on commit 28ffb9f

Please sign in to comment.