From: Jay Freeman (saurik) Date: Wed, 25 Oct 2017 12:02:45 +0000 (-0700) Subject: Use slightly more "normal" npm directory names ;P. X-Git-Url: https://git.saurik.com/logizomai.git/commitdiff_plain/1eb8220b2d0f659b12216c8e99bdd44427e54ffd?ds=sidebyside Use slightly more "normal" npm directory names ;P. --- diff --git a/.gitignore b/.gitignore index 0b40c0f..2716eb6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -/dist +/lib /node_modules /npm-debug.log /package.json diff --git a/.npmignore b/.npmignore index ecf783f..2035d15 100644 --- a/.npmignore +++ b/.npmignore @@ -1,4 +1,4 @@ -/lib /make /package.json5 /publish +/src diff --git a/lib/index.ts b/lib/index.ts deleted file mode 100644 index 3d7f603..0000000 --- a/lib/index.ts +++ /dev/null @@ -1,425 +0,0 @@ -/* 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 . -**/ -/* }}} */ - -export abstract class Resource { - private retainers: number; - public finalizes: Array<() => void>; - - constructor() { - this.retainers = 0; - this.finalizes = []; - } - - public async _(): Promise { - 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(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).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 { - const parent = target.__proto__; - await parent._.call(this); try { - return await old.call(this); - } catch (error) { - parent.finalize.call(this); - throw error; } - }; -} - -interface Waitable { - size: number; - values(): IterableIterator; - waiters: Set<(value: Value | null) => void> | null; -} - -function Get(set: Waitable, code?: () => void): Promise { - return new Promise((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 extends Resource { - private readonly set: Set; - public waiters: Set<(value: Value | null) => void> | null; - - constructor() { super(); - this.set = new Set(); - 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 { - 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 { - return this.set.values(); - } - - public get size(): number { - return this.set.size; - } - - public [Symbol.iterator](): IterableIterator { - return this.set[Symbol.iterator](); - } -} - -export class FutureSet extends Resource { - private readonly set: Set; - public waiters: Set<(value: Value | null) => void> | null; - - constructor() { super(); - this.set = new Set(); - 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 { - 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 { - return this.set.values(); - } - - public get size(): number { - return this.set.size; - } - - public [Symbol.iterator](): IterableIterator { - return this.set[Symbol.iterator](); - } -} - -export class ResourceMap extends Resource { - private readonly map: Map; - - constructor() { super(); - this.map = new Map(); - } - - 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 { - return this.map.keys(); - } - - public values(): IterableIterator { - return this.map.values(); - } - - public get size(): number { - return this.map.size; - } -} - -export class ResourceArray 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(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 { - return this.array[Symbol.iterator](); - } -} - -export class Scoped 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(); - } -} diff --git a/package.json5 b/package.json5 index d71d031..bff000c 100644 --- a/package.json5 +++ b/package.json5 @@ -17,8 +17,8 @@ "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", diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..3d7f603 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,425 @@ +/* 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 . +**/ +/* }}} */ + +export abstract class Resource { + private retainers: number; + public finalizes: Array<() => void>; + + constructor() { + this.retainers = 0; + this.finalizes = []; + } + + public async _(): Promise { + 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(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).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 { + const parent = target.__proto__; + await parent._.call(this); try { + return await old.call(this); + } catch (error) { + parent.finalize.call(this); + throw error; } + }; +} + +interface Waitable { + size: number; + values(): IterableIterator; + waiters: Set<(value: Value | null) => void> | null; +} + +function Get(set: Waitable, code?: () => void): Promise { + return new Promise((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 extends Resource { + private readonly set: Set; + public waiters: Set<(value: Value | null) => void> | null; + + constructor() { super(); + this.set = new Set(); + 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 { + 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 { + return this.set.values(); + } + + public get size(): number { + return this.set.size; + } + + public [Symbol.iterator](): IterableIterator { + return this.set[Symbol.iterator](); + } +} + +export class FutureSet extends Resource { + private readonly set: Set; + public waiters: Set<(value: Value | null) => void> | null; + + constructor() { super(); + this.set = new Set(); + 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 { + 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 { + return this.set.values(); + } + + public get size(): number { + return this.set.size; + } + + public [Symbol.iterator](): IterableIterator { + return this.set[Symbol.iterator](); + } +} + +export class ResourceMap extends Resource { + private readonly map: Map; + + constructor() { super(); + this.map = new Map(); + } + + 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 { + return this.map.keys(); + } + + public values(): IterableIterator { + return this.map.values(); + } + + public get size(): number { + return this.map.size; + } +} + +export class ResourceArray 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(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 { + return this.array[Symbol.iterator](); + } +} + +export class Scoped 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(); + } +} diff --git a/tsconfig.json b/tsconfig.json index 73f1865..ec5260f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,12 +1,12 @@ { - "include": [ "lib/*.ts" ], + "include": [ "src/*.ts" ], "compilerOptions": { "experimentalDecorators": true, "target": "es6", "module": "commonjs", "declaration": true, - "outDir": "dist", + "outDir": "lib", "strict": true } }