-/dist
+/lib
/node_modules
/npm-debug.log
/package.json
-/lib
/make
/package.json5
/publish
+/src
+++ /dev/null
-/* Logizomai - Reference Counting for TypeScript
- * Copyright (C) 2017 Jay Freeman (saurik)
-*/
-
-/* GNU Affero General Public License, Version 3 {{{ */
-/*
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
-
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
-**/
-/* }}} */
-
-export abstract class Resource {
- private retainers: number;
- public finalizes: Array<() => void>;
-
- constructor() {
- this.retainers = 0;
- this.finalizes = [];
- }
-
- public async _(): Promise<this> {
- return this;
- }
-
- public retain(): void {
- ++this.retainers;
- }
-
- public release(): void {
- if (--this.retainers === 0)
- this.finalize();
- }
-
- protected finalize(): void {
- for (let prototype = this; prototype !== null; prototype = Object.getPrototypeOf(prototype))
- if (prototype.hasOwnProperty("@resource"))
- for (const property of (prototype as any)["@resource"] as string[])
- (this as any)[property] = null;
- for (const finalizes of this.finalizes)
- finalizes();
- }
-}
-
-export function resource(target: any, property: string): void {
- let resources = target["@resource"];
- if (resources === undefined)
- target["@resource"] = resources = [];
- resources.push(property);
-
- const mangled = "@resource " + property;
-
- Object.defineProperty(target, mangled, {
- enumerable: false,
- writable: true,
- value: null,
- });
-
- Object.defineProperty(target, property, {
- enumerable: false,
-
- get: function() {
- return (this as any)[mangled] as Resource | null;
- },
-
- set: function(value) {
- const old = (this as any)[mangled] as Resource | null;
- if (old === value)
- return;
- if (value !== null)
- value.retain();
- (this as any)[mangled] = value;
- if (old !== null)
- old.release();
- },
- });
-}
-
-export function using<Type extends Resource, Value>(resource: Type, code: (resource: Type) => Value): Value {
- resource.retain();
- let release = true;
- try {
- const value = code(resource);
- if (!(value instanceof Promise))
- return value;
- release = false;
- return (value as any as Promise<any>).then((value) => {
- resource.release();
- return value;
- }) as any as Value;
- } finally {
- if (release)
- resource.release();
- }
-}
-
-export function construct(target: any, property: string, descriptor: PropertyDescriptor) {
- const old = descriptor.value;
- descriptor.value = async function(this: Resource): Promise<any> {
- const parent = target.__proto__;
- await parent._.call(this); try {
- return await old.call(this);
- } catch (error) {
- parent.finalize.call(this);
- throw error; }
- };
-}
-
-interface Waitable<Value> {
- size: number;
- values(): IterableIterator<Value>;
- waiters: Set<(value: Value | null) => void> | null;
-}
-
-function Get<Value>(set: Waitable<Value>, code?: () => void): Promise<Value> {
- return new Promise<Value>((resolve, reject) => {
- if (set.size !== 0)
- resolve(set.values().next().value);
- else {
- if (set.waiters === null)
- set.waiters = new Set<(value: Value | null) => void>();
- set.waiters.add((value: Value | null) => {
- if (value === null)
- reject();
- else
- resolve(value);
- });
- if (code !== undefined)
- code();
- }
- });
-}
-
-export class ResourceSet<Value extends Resource> extends Resource {
- private readonly set: Set<Value>;
- public waiters: Set<(value: Value | null) => void> | null;
-
- constructor() { super();
- this.set = new Set<Value>();
- this.waiters = null;
- }
-
- protected finalize(): void {
- this.cancel();
- this.clear();
- super.finalize();
- }
-
- public clear(): void {
- for (const value of this.set.values())
- value.release();
- return this.set.clear();
- }
-
- public cancel(): void {
- const waiters = this.waiters;
- this.waiters = null;
- if (waiters !== null)
- for (const waiter of waiters)
- waiter(null);
- }
-
- public has(value: Value): boolean {
- return this.set.has(value);
- }
-
- public get(code?: () => void): Promise<Value> {
- return Get(this, code);
- }
-
- public add(value: Value): this {
- // .add() should return a boolean
- // this is simply incompetence :/
- if (!this.set.has(value)) {
- value.retain();
- this.set.add(value);
- }
-
- const waiters = this.waiters;
- this.waiters = null;
- if (waiters !== null)
- for (const waiter of waiters)
- waiter(value);
-
- return this;
- }
-
- public delete(value: Value): boolean {
- const deleted = this.set.delete(value);
- if (deleted)
- value.release();
- return deleted;
- }
-
- public values(): IterableIterator<Value> {
- return this.set.values();
- }
-
- public get size(): number {
- return this.set.size;
- }
-
- public [Symbol.iterator](): IterableIterator<Value> {
- return this.set[Symbol.iterator]();
- }
-}
-
-export class FutureSet<Value> extends Resource {
- private readonly set: Set<Value>;
- public waiters: Set<(value: Value | null) => void> | null;
-
- constructor() { super();
- this.set = new Set<Value>();
- this.waiters = null;
- }
-
- protected finalize(): void {
- this.cancel();
- this.clear();
- super.finalize();
- }
-
- public clear(): void {
- return this.set.clear();
- }
-
- public cancel(): void {
- const waiters = this.waiters;
- this.waiters = null;
- if (waiters !== null)
- for (const waiter of waiters)
- waiter(null);
- }
-
- public has(value: Value): boolean {
- return this.set.has(value);
- }
-
- public get(code?: () => void): Promise<Value> {
- return Get(this, code);
- }
-
- public add(value: Value): this {
- this.set.add(value);
- return this;
- }
-
- public delete(value: Value): boolean {
- return this.set.delete(value);
- }
-
- public values(): IterableIterator<Value> {
- return this.set.values();
- }
-
- public get size(): number {
- return this.set.size;
- }
-
- public [Symbol.iterator](): IterableIterator<Value> {
- return this.set[Symbol.iterator]();
- }
-}
-
-export class ResourceMap<Key, Value extends Resource> extends Resource {
- private readonly map: Map<Key, Value>;
-
- constructor() { super();
- this.map = new Map<Key, Value>();
- }
-
- protected finalize(): void {
- this.clear();
- super.finalize();
- }
-
- public clear(): void {
- for (const value of this.map.values())
- value.release();
- return this.map.clear();
- }
-
- public has(key: Key): boolean {
- return this.map.has(key);
- }
-
- public get(key: Key): Value | undefined {
- return this.map.get(key);
- }
-
- public set(key: Key, value: Value): this {
- // .set() should return old value
- // this is simply incompetence :/
- const old = this.map.get(key);
- if (old !== value) {
- if (value !== undefined && value !== null)
- value.retain();
- this.map.set(key, value);
- if (old !== undefined && old !== null)
- old.release();
- }
- return this;
- }
-
- public vet(key: Key, code: () => Value): Value {
- const old = this.map.get(key);
- if (old !== undefined)
- return old;
- const value = code();
- if (value !== null)
- value.retain();
- this.map.set(key, value);
- return value;
- }
-
- public delete(key: Key): boolean {
- // .delete() should return old value
- // since undefined is also a *value*
- // you can't use .get() to .delete()
- // this is all stupid incompetent :/
- const old = this.map.get(key);
- const deleted = this.map.delete(key);
- if (old !== undefined)
- old.release();
- return deleted;
- }
-
- public keys(): IterableIterator<Key> {
- return this.map.keys();
- }
-
- public values(): IterableIterator<Value> {
- return this.map.values();
- }
-
- public get size(): number {
- return this.map.size;
- }
-}
-
-export class ResourceArray<Value extends Resource | null> extends Resource {
- private readonly array: Value[];
-
- constructor(size: number = 0) { super();
- this.array = new Array(size).fill(null);
- }
-
- protected finalize(): void {
- for (const value of this.array)
- if (value !== null)
- value.release();
- this.array.length = 0;
- super.finalize();
- }
-
- public fill(value: Value): this {
- const array = this.array;
- for (let index = 0; index !== array.length; ++index) {
- const old = array[index];
- if (old === value)
- continue;
-
- if (value !== null)
- value.retain();
- array[index] = value;
- if (old !== null)
- old.release();
- }
-
- return this;
- }
-
- public map<T>(code: (value: Value) => T): T[] {
- return this.array.map(code);
- }
-
- public get length(): number {
- return this.array.length;
- }
-
- public get(index: number): Value {
- if (index < 0 || (index | 0) !== index) throw new Error();
- if (index >= this.array.length) throw new Error();
- return this.array[index];
- }
-
- public set(index: number, value: Value): void {
- if (index < 0 || (index | 0) !== index) throw new Error();
- if (index >= this.array.length) throw new Error();
- const old = this.array[index];
- if (value !== null)
- value.retain();
- this.array[index] = value;
- if (old !== null)
- old.release();
- }
-
- public [Symbol.iterator](): IterableIterator<Value> {
- return this.array[Symbol.iterator]();
- }
-}
-
-export class Scoped<T> extends Resource {
- public readonly value: T;
- private readonly remove: () => void;
-
- constructor(value: T, remove: () => void) { super();
- this.value = value;
- this.remove = remove;
- }
-
- protected finalize(): void {
- this.remove();
- super.finalize();
- }
-}
"lint": "tslint --project .",
"prepublishOnly": "npm run build && npm run lint",
},
- "main": "dist/index.js",
- "types": "dist/index.d.ts",
+ "main": "lib/index.js",
+ "types": "lib/index.d.ts",
"devDependencies": {
"@types/node": "8.0.15",
"browserify": "14.4.0",
--- /dev/null
+/* Logizomai - Reference Counting for TypeScript
+ * Copyright (C) 2017 Jay Freeman (saurik)
+*/
+
+/* GNU Affero General Public License, Version 3 {{{ */
+/*
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+**/
+/* }}} */
+
+export abstract class Resource {
+ private retainers: number;
+ public finalizes: Array<() => void>;
+
+ constructor() {
+ this.retainers = 0;
+ this.finalizes = [];
+ }
+
+ public async _(): Promise<this> {
+ return this;
+ }
+
+ public retain(): void {
+ ++this.retainers;
+ }
+
+ public release(): void {
+ if (--this.retainers === 0)
+ this.finalize();
+ }
+
+ protected finalize(): void {
+ for (let prototype = this; prototype !== null; prototype = Object.getPrototypeOf(prototype))
+ if (prototype.hasOwnProperty("@resource"))
+ for (const property of (prototype as any)["@resource"] as string[])
+ (this as any)[property] = null;
+ for (const finalizes of this.finalizes)
+ finalizes();
+ }
+}
+
+export function resource(target: any, property: string): void {
+ let resources = target["@resource"];
+ if (resources === undefined)
+ target["@resource"] = resources = [];
+ resources.push(property);
+
+ const mangled = "@resource " + property;
+
+ Object.defineProperty(target, mangled, {
+ enumerable: false,
+ writable: true,
+ value: null,
+ });
+
+ Object.defineProperty(target, property, {
+ enumerable: false,
+
+ get: function() {
+ return (this as any)[mangled] as Resource | null;
+ },
+
+ set: function(value) {
+ const old = (this as any)[mangled] as Resource | null;
+ if (old === value)
+ return;
+ if (value !== null)
+ value.retain();
+ (this as any)[mangled] = value;
+ if (old !== null)
+ old.release();
+ },
+ });
+}
+
+export function using<Type extends Resource, Value>(resource: Type, code: (resource: Type) => Value): Value {
+ resource.retain();
+ let release = true;
+ try {
+ const value = code(resource);
+ if (!(value instanceof Promise))
+ return value;
+ release = false;
+ return (value as any as Promise<any>).then((value) => {
+ resource.release();
+ return value;
+ }) as any as Value;
+ } finally {
+ if (release)
+ resource.release();
+ }
+}
+
+export function construct(target: any, property: string, descriptor: PropertyDescriptor) {
+ const old = descriptor.value;
+ descriptor.value = async function(this: Resource): Promise<any> {
+ const parent = target.__proto__;
+ await parent._.call(this); try {
+ return await old.call(this);
+ } catch (error) {
+ parent.finalize.call(this);
+ throw error; }
+ };
+}
+
+interface Waitable<Value> {
+ size: number;
+ values(): IterableIterator<Value>;
+ waiters: Set<(value: Value | null) => void> | null;
+}
+
+function Get<Value>(set: Waitable<Value>, code?: () => void): Promise<Value> {
+ return new Promise<Value>((resolve, reject) => {
+ if (set.size !== 0)
+ resolve(set.values().next().value);
+ else {
+ if (set.waiters === null)
+ set.waiters = new Set<(value: Value | null) => void>();
+ set.waiters.add((value: Value | null) => {
+ if (value === null)
+ reject();
+ else
+ resolve(value);
+ });
+ if (code !== undefined)
+ code();
+ }
+ });
+}
+
+export class ResourceSet<Value extends Resource> extends Resource {
+ private readonly set: Set<Value>;
+ public waiters: Set<(value: Value | null) => void> | null;
+
+ constructor() { super();
+ this.set = new Set<Value>();
+ this.waiters = null;
+ }
+
+ protected finalize(): void {
+ this.cancel();
+ this.clear();
+ super.finalize();
+ }
+
+ public clear(): void {
+ for (const value of this.set.values())
+ value.release();
+ return this.set.clear();
+ }
+
+ public cancel(): void {
+ const waiters = this.waiters;
+ this.waiters = null;
+ if (waiters !== null)
+ for (const waiter of waiters)
+ waiter(null);
+ }
+
+ public has(value: Value): boolean {
+ return this.set.has(value);
+ }
+
+ public get(code?: () => void): Promise<Value> {
+ return Get(this, code);
+ }
+
+ public add(value: Value): this {
+ // .add() should return a boolean
+ // this is simply incompetence :/
+ if (!this.set.has(value)) {
+ value.retain();
+ this.set.add(value);
+ }
+
+ const waiters = this.waiters;
+ this.waiters = null;
+ if (waiters !== null)
+ for (const waiter of waiters)
+ waiter(value);
+
+ return this;
+ }
+
+ public delete(value: Value): boolean {
+ const deleted = this.set.delete(value);
+ if (deleted)
+ value.release();
+ return deleted;
+ }
+
+ public values(): IterableIterator<Value> {
+ return this.set.values();
+ }
+
+ public get size(): number {
+ return this.set.size;
+ }
+
+ public [Symbol.iterator](): IterableIterator<Value> {
+ return this.set[Symbol.iterator]();
+ }
+}
+
+export class FutureSet<Value> extends Resource {
+ private readonly set: Set<Value>;
+ public waiters: Set<(value: Value | null) => void> | null;
+
+ constructor() { super();
+ this.set = new Set<Value>();
+ this.waiters = null;
+ }
+
+ protected finalize(): void {
+ this.cancel();
+ this.clear();
+ super.finalize();
+ }
+
+ public clear(): void {
+ return this.set.clear();
+ }
+
+ public cancel(): void {
+ const waiters = this.waiters;
+ this.waiters = null;
+ if (waiters !== null)
+ for (const waiter of waiters)
+ waiter(null);
+ }
+
+ public has(value: Value): boolean {
+ return this.set.has(value);
+ }
+
+ public get(code?: () => void): Promise<Value> {
+ return Get(this, code);
+ }
+
+ public add(value: Value): this {
+ this.set.add(value);
+ return this;
+ }
+
+ public delete(value: Value): boolean {
+ return this.set.delete(value);
+ }
+
+ public values(): IterableIterator<Value> {
+ return this.set.values();
+ }
+
+ public get size(): number {
+ return this.set.size;
+ }
+
+ public [Symbol.iterator](): IterableIterator<Value> {
+ return this.set[Symbol.iterator]();
+ }
+}
+
+export class ResourceMap<Key, Value extends Resource> extends Resource {
+ private readonly map: Map<Key, Value>;
+
+ constructor() { super();
+ this.map = new Map<Key, Value>();
+ }
+
+ protected finalize(): void {
+ this.clear();
+ super.finalize();
+ }
+
+ public clear(): void {
+ for (const value of this.map.values())
+ value.release();
+ return this.map.clear();
+ }
+
+ public has(key: Key): boolean {
+ return this.map.has(key);
+ }
+
+ public get(key: Key): Value | undefined {
+ return this.map.get(key);
+ }
+
+ public set(key: Key, value: Value): this {
+ // .set() should return old value
+ // this is simply incompetence :/
+ const old = this.map.get(key);
+ if (old !== value) {
+ if (value !== undefined && value !== null)
+ value.retain();
+ this.map.set(key, value);
+ if (old !== undefined && old !== null)
+ old.release();
+ }
+ return this;
+ }
+
+ public vet(key: Key, code: () => Value): Value {
+ const old = this.map.get(key);
+ if (old !== undefined)
+ return old;
+ const value = code();
+ if (value !== null)
+ value.retain();
+ this.map.set(key, value);
+ return value;
+ }
+
+ public delete(key: Key): boolean {
+ // .delete() should return old value
+ // since undefined is also a *value*
+ // you can't use .get() to .delete()
+ // this is all stupid incompetent :/
+ const old = this.map.get(key);
+ const deleted = this.map.delete(key);
+ if (old !== undefined)
+ old.release();
+ return deleted;
+ }
+
+ public keys(): IterableIterator<Key> {
+ return this.map.keys();
+ }
+
+ public values(): IterableIterator<Value> {
+ return this.map.values();
+ }
+
+ public get size(): number {
+ return this.map.size;
+ }
+}
+
+export class ResourceArray<Value extends Resource | null> extends Resource {
+ private readonly array: Value[];
+
+ constructor(size: number = 0) { super();
+ this.array = new Array(size).fill(null);
+ }
+
+ protected finalize(): void {
+ for (const value of this.array)
+ if (value !== null)
+ value.release();
+ this.array.length = 0;
+ super.finalize();
+ }
+
+ public fill(value: Value): this {
+ const array = this.array;
+ for (let index = 0; index !== array.length; ++index) {
+ const old = array[index];
+ if (old === value)
+ continue;
+
+ if (value !== null)
+ value.retain();
+ array[index] = value;
+ if (old !== null)
+ old.release();
+ }
+
+ return this;
+ }
+
+ public map<T>(code: (value: Value) => T): T[] {
+ return this.array.map(code);
+ }
+
+ public get length(): number {
+ return this.array.length;
+ }
+
+ public get(index: number): Value {
+ if (index < 0 || (index | 0) !== index) throw new Error();
+ if (index >= this.array.length) throw new Error();
+ return this.array[index];
+ }
+
+ public set(index: number, value: Value): void {
+ if (index < 0 || (index | 0) !== index) throw new Error();
+ if (index >= this.array.length) throw new Error();
+ const old = this.array[index];
+ if (value !== null)
+ value.retain();
+ this.array[index] = value;
+ if (old !== null)
+ old.release();
+ }
+
+ public [Symbol.iterator](): IterableIterator<Value> {
+ return this.array[Symbol.iterator]();
+ }
+}
+
+export class Scoped<T> extends Resource {
+ public readonly value: T;
+ private readonly remove: () => void;
+
+ constructor(value: T, remove: () => void) { super();
+ this.value = value;
+ this.remove = remove;
+ }
+
+ protected finalize(): void {
+ this.remove();
+ super.finalize();
+ }
+}
{
- "include": [ "lib/*.ts" ],
+ "include": [ "src/*.ts" ],
"compilerOptions": {
"experimentalDecorators": true,
"target": "es6",
"module": "commonjs",
"declaration": true,
- "outDir": "dist",
+ "outDir": "lib",
"strict": true
}
}