From f16c699d3bfc5c2b92ddbfdc4aa890bc86cbe7d9 Mon Sep 17 00:00:00 2001 From: David Sehnal <david.sehnal@gmail.com> Date: Fri, 8 Dec 2017 17:21:36 +0100 Subject: [PATCH] Added linked list to mol-data, refactoring --- src/mol-data/generic.ts | 9 ++ .../generic/_spec/linked-list.spec.ts | 63 ++++++++++ src/mol-data/{util => generic}/hash-set.ts | 0 src/mol-data/generic/linked-list.ts | 119 ++++++++++++++++++ .../{util => generic}/unique-array.ts | 0 src/mol-data/index.ts | 3 +- src/mol-data/util.ts | 2 - src/mol-data/util/array.ts | 2 +- src/mol-model/structure/query/selection.ts | 2 +- .../structure/structure/structure.ts | 2 +- src/mol-task/computation.ts | 2 +- src/mol-task/execution/observable.ts | 55 +++++++- src/mol-task/index.ts | 3 +- src/mol-task/task.ts | 13 +- src/mol-task/util/chunked.ts | 0 src/mol-task/util/multistep.ts | 0 src/mol-task/{time.ts => util/now.ts} | 0 17 files changed, 257 insertions(+), 18 deletions(-) create mode 100644 src/mol-data/generic.ts create mode 100644 src/mol-data/generic/_spec/linked-list.spec.ts rename src/mol-data/{util => generic}/hash-set.ts (100%) create mode 100644 src/mol-data/generic/linked-list.ts rename src/mol-data/{util => generic}/unique-array.ts (100%) create mode 100644 src/mol-task/util/chunked.ts create mode 100644 src/mol-task/util/multistep.ts rename src/mol-task/{time.ts => util/now.ts} (100%) diff --git a/src/mol-data/generic.ts b/src/mol-data/generic.ts new file mode 100644 index 000000000..d837d1060 --- /dev/null +++ b/src/mol-data/generic.ts @@ -0,0 +1,9 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +export * from './generic/hash-set' +export * from './generic/linked-list' +export * from './generic/unique-array' \ No newline at end of file diff --git a/src/mol-data/generic/_spec/linked-list.spec.ts b/src/mol-data/generic/_spec/linked-list.spec.ts new file mode 100644 index 000000000..cb439b3da --- /dev/null +++ b/src/mol-data/generic/_spec/linked-list.spec.ts @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { LinkedList } from '../linked-list' + +describe('linked list', () => { + + function toArray<T>(list: LinkedList<T>) { + const ret: T[] = []; + for (let t = list.first; !!t; t = t.next) { + ret[ret.length] = t.value; + } + return ret; + } + + function create<T>(xs: T[]) { + const list = LinkedList<T>(); + for (const x of xs) list.addLast(x); + return list; + } + + it('add', () => { + const list = LinkedList<number>(); + list.addFirst(1); + list.addLast(2); + list.addFirst(3); + list.addFirst(4); + list.addLast(5); + expect(toArray(list)).toEqual([4, 3, 1, 2, 5]); + expect(list.count).toBe(5); + }); + + it ('remove', () => { + const list = create([1, 2, 3, 4]); + let fst = list.removeFirst(); + expect(fst).toBe(1); + expect(list.last!.value).toBe(4); + expect(list.count).toBe(3); + expect(toArray(list)).toEqual([2, 3, 4]); + + let last = list.removeLast(); + expect(last).toBe(4); + expect(list.last!.value).toBe(3); + expect(list.count).toBe(2); + expect(toArray(list)).toEqual([2, 3]); + + let n3 = list.find(3)!; + list.remove(n3); + expect(list.first!.value).toBe(2); + expect(list.last!.value).toBe(2); + expect(list.count).toBe(1); + expect(toArray(list)).toEqual([2]); + + list.removeFirst(); + expect(list.first).toBe(null); + expect(list.last).toBe(null); + expect(list.count).toBe(0); + expect(toArray(list)).toEqual([]); + }) +}); \ No newline at end of file diff --git a/src/mol-data/util/hash-set.ts b/src/mol-data/generic/hash-set.ts similarity index 100% rename from src/mol-data/util/hash-set.ts rename to src/mol-data/generic/hash-set.ts diff --git a/src/mol-data/generic/linked-list.ts b/src/mol-data/generic/linked-list.ts new file mode 100644 index 000000000..3f5422699 --- /dev/null +++ b/src/mol-data/generic/linked-list.ts @@ -0,0 +1,119 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +interface LinkedList<T> { + readonly count: number, + readonly first: LinkedList.Node<T> | null, + readonly last: LinkedList.Node<T> | null, + addFirst(value: T): LinkedList.Node<T>, + addLast(value: T): LinkedList.Node<T>, + remove(node: LinkedList.Node<T>): void, + removeFirst(): T | undefined, + removeLast(): T | undefined, + find(value: T): LinkedList.Node<T> | undefined +} + +function LinkedList<T>(): LinkedList<T> { + return new LinkedListImpl(); +} + +namespace LinkedList { + export interface Node<T> { + previous: Node<T> | null, + next: Node<T> | null, + inList: boolean, + value: T + } +} + +function createListNode<T>(value: T): LinkedList.Node<T> { + return { previous: null, next: null, inList: true, value }; +} + +class LinkedListImpl<T> implements LinkedList<T> { + count: number = 0; + first: LinkedList.Node<T> | null = null; + last: LinkedList.Node<T> | null = null; + + addFirst(value: T) { + const node = createListNode(value); + node.inList = true; + if (this.first) this.first.previous = node; + node.next = this.first; + this.first = node; + this.count++; + if (!this.last) this.last = node; + return node; + } + + addLast(value: T) { + const node = createListNode(value); + if (this.last !== null) { + this.last.next = node; + } + node.previous = this.last; + this.last = node; + if (this.first === null) { + this.first = node; + } + node.inList = true; + this.count++; + return node; + } + + removeFirst(): T | undefined { + const fst = this.first; + if (fst) { + this.remove(fst); + return fst.value; + } + return void 0; + } + + removeLast(): T | undefined { + const last = this.last; + if (last) { + this.remove(last); + return last.value; + } + return void 0; + } + + remove(node: LinkedList.Node<T>) { + if (!node.inList) return; + + node.inList = false; + + if (node.previous !== null) { + node.previous.next = node.next; + } + else if (/*first == item*/ node.previous === null) { + this.first = node.next; + } + + if (node.next !== null) { + node.next.previous = node.previous; + } + else if (/*last == item*/ node.next === null) { + this.last = node.previous; + } + + node.next = null; + node.previous = null; + this.count--; + } + + find(value: T): LinkedList.Node<T> | undefined { + let current = this.first; + while (current !== null) { + if (current.value === value) return current; + current = current.next; + } + return void 0; + } +} + +export { LinkedList } \ No newline at end of file diff --git a/src/mol-data/util/unique-array.ts b/src/mol-data/generic/unique-array.ts similarity index 100% rename from src/mol-data/util/unique-array.ts rename to src/mol-data/generic/unique-array.ts diff --git a/src/mol-data/index.ts b/src/mol-data/index.ts index 1db90c26e..b1d250084 100644 --- a/src/mol-data/index.ts +++ b/src/mol-data/index.ts @@ -8,5 +8,6 @@ import * as DB from './db' import * as Int from './int' import Iterator from './iterator' import * as Util from './util' +import * as Generic from './generic' -export { DB, Int, Iterator, Util } \ No newline at end of file +export { DB, Int, Iterator, Util, Generic } \ No newline at end of file diff --git a/src/mol-data/util.ts b/src/mol-data/util.ts index 629b3b517..c541a1e9e 100644 --- a/src/mol-data/util.ts +++ b/src/mol-data/util.ts @@ -5,8 +5,6 @@ */ export * from './util/chunked-array' -export * from './util/unique-array' -export * from './util/hash-set' export * from './util/equivalence-classes' export * from './util/hash-functions' export * from './util/sort' diff --git a/src/mol-data/util/array.ts b/src/mol-data/util/array.ts index 664138327..93707e797 100644 --- a/src/mol-data/util/array.ts +++ b/src/mol-data/util/array.ts @@ -14,7 +14,7 @@ export function arrayFind<T>(array: ArrayLike<T>, f: (v: T) => boolean): T | und export function iterableToArray<T>(it: IterableIterator<T>): T[] { if (Array.from) return Array.from(it); - + const ret = []; while (true) { const { done, value } = it.next(); diff --git a/src/mol-model/structure/query/selection.ts b/src/mol-model/structure/query/selection.ts index 2d8467e07..5b317240a 100644 --- a/src/mol-model/structure/query/selection.ts +++ b/src/mol-model/structure/query/selection.ts @@ -4,7 +4,7 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { HashSet } from 'mol-data/util' +import { HashSet } from 'mol-data/generic' import { Structure, AtomSet } from '../structure' // A selection is a pair of a Structure and a sequence of unique AtomSets diff --git a/src/mol-model/structure/structure/structure.ts b/src/mol-model/structure/structure/structure.ts index 6cac5a7b8..dfd764e47 100644 --- a/src/mol-model/structure/structure/structure.ts +++ b/src/mol-model/structure/structure/structure.ts @@ -5,7 +5,7 @@ */ import { OrderedSet, Iterator } from 'mol-data/int' -import { UniqueArray } from 'mol-data/util' +import { UniqueArray } from 'mol-data/generic' import SymmetryOperator from 'mol-math/geometry/symmetry-operator' import { Model, Format } from '../model' import Unit from './unit' diff --git a/src/mol-task/computation.ts b/src/mol-task/computation.ts index 3d956de70..b4b1c9d13 100644 --- a/src/mol-task/computation.ts +++ b/src/mol-task/computation.ts @@ -6,7 +6,7 @@ */ import Scheduler from './scheduler' -import timeNow from './time' +import timeNow from './util/now' interface Computation<A> { (ctx?: Computation.Context): Promise<A> diff --git a/src/mol-task/execution/observable.ts b/src/mol-task/execution/observable.ts index f06abbf0c..44fa65170 100644 --- a/src/mol-task/execution/observable.ts +++ b/src/mol-task/execution/observable.ts @@ -7,10 +7,38 @@ import Task from '../task' import RuntimeContext from './runtime-context' import Progress from './progress' +import now from '../util/now' + +function defaultProgress(rootTaskId: number, task: Task<any>): Task.Progress { + return { + rootTaskId, + taskId: task.id, + taskName: task.name, + message: 'Running...', + elapsedMs: { real: 0, cpu: 0 }, + canAbort: true, + isIndeterminate: true, + current: 0, + max: 0 + }; +} + +class ProgressInfo { + taskId: number; + elapsedMs: { real: number, cpu: number }; + tree: Progress.Node; + tryAbort?: (reason?: string) => void; + + snapshot(): Progress { + return 0 as any; + } +} class ObservableExecutor { + progressInfo: ProgressInfo; + async run<T>(task: Task<T>): Promise<T> { - const ctx = new ObservableRuntimeContext(); + const ctx = new ObservableRuntimeContext(task.id, task, 0); if (!task.__onAbort) return task.__f(ctx); try { @@ -27,14 +55,35 @@ class ObservableExecutor { } class ObservableRuntimeContext implements RuntimeContext { - id: number = 0; - requiresUpdate: boolean = false; + elapsedCpuMs: number; + lastScheduledTime: number; + + started: number; + taskId: number; + taskName: string; + progress: Task.Progress; + updateRateMs: number; + + get requiresUpdate(): boolean { + return now() - this.started > this.updateRateMs; + } + update(progress: Partial<RuntimeContext.ProgressUpdate>): Promise<void> { return 0 as any; } + runChild<T>(progress: Partial<RuntimeContext.ProgressUpdate>, task: Task<T>): Promise<T> { return 0 as any; } + + constructor(parentId: number, task: Task<any>, updateRateMs: number) { + this.started = now(); + this.lastScheduledTime = this.started; + this.taskId = task.id; + this.taskName = task.name; + this.progress = defaultProgress(parentId, task); + this.updateRateMs = updateRateMs; + } } function ExecuteObservable<T>(task: Task<T>, observer: Progress.Observer, updateRateMs = 250) { diff --git a/src/mol-task/index.ts b/src/mol-task/index.ts index d9626148c..6367cb572 100644 --- a/src/mol-task/index.ts +++ b/src/mol-task/index.ts @@ -9,6 +9,7 @@ import RuntimeContext from './execution/runtime-context' import ExecuteSynchronous from './execution/synchronous' import ExecuteObservable from './execution/observable' import Progress from './execution/progress' +import now from './util/now' function Run<T>(task: Task<T>): Promise<T>; function Run<T>(task: Task<T>, observer: Progress.Observer, updateRateMs?: number): Promise<T>; @@ -17,4 +18,4 @@ function Run<T>(task: Task<T>, observer?: Progress.Observer, updateRateMs?: numb return ExecuteSynchronous(task); } -export { Task, RuntimeContext, Progress, Run } \ No newline at end of file +export { Task, RuntimeContext, Progress, Run, now } \ No newline at end of file diff --git a/src/mol-task/task.ts b/src/mol-task/task.ts index e0e5174a9..8f6a700c3 100644 --- a/src/mol-task/task.ts +++ b/src/mol-task/task.ts @@ -35,18 +35,17 @@ namespace Task { return ret; } - export type Progress = IndeterminateProgress | DeterminateProgress - - interface ProgressBase { + export interface Progress { rootTaskId: number, taskId: number, + taskName: string, message: string, elapsedMs: { real: number, cpu: number }, - canAbort: boolean + canAbort: boolean, + isIndeterminate: boolean, + current: number, + max: number } - - export interface IndeterminateProgress extends ProgressBase { isIndeterminate: true } - export interface DeterminateProgress extends ProgressBase { isIndeterminate: false, current: number, max: number } } export default Task \ No newline at end of file diff --git a/src/mol-task/util/chunked.ts b/src/mol-task/util/chunked.ts new file mode 100644 index 000000000..e69de29bb diff --git a/src/mol-task/util/multistep.ts b/src/mol-task/util/multistep.ts new file mode 100644 index 000000000..e69de29bb diff --git a/src/mol-task/time.ts b/src/mol-task/util/now.ts similarity index 100% rename from src/mol-task/time.ts rename to src/mol-task/util/now.ts -- GitLab