gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] [taler-wallet-webex] branch master updated: memidb skeleton


From: gnunet
Subject: [GNUnet-SVN] [taler-wallet-webex] branch master updated: memidb skeleton
Date: Wed, 31 May 2017 11:11:58 +0200

This is an automated email from the git hooks/post-receive script.

dold pushed a commit to branch master
in repository wallet-webex.

The following commit(s) were added to refs/heads/master by this push:
     new 46987684 memidb skeleton
46987684 is described below

commit 4698768459230aeb42a90ef2add3cee44206c23a
Author: Florian Dold <address@hidden>
AuthorDate: Tue May 30 13:35:16 2017 +0200

    memidb skeleton
---
 src/memidb.ts | 585 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 tsconfig.json |   4 +
 2 files changed, 586 insertions(+), 3 deletions(-)

diff --git a/src/memidb.ts b/src/memidb.ts
index fe1d986f..36607d71 100644
--- a/src/memidb.ts
+++ b/src/memidb.ts
@@ -16,12 +16,22 @@
 
 /**
  * In-memory implementation of the IndexedDB interface.
+ *
+ * Transactions support rollback, but they are all run sequentially within the
+ * same MemoryIDBFactory.
+ *
+ * Every operation involves copying the whole database state, making it only
+ * feasible for small databases.
  */
 
 /* work in progres ... */
 /* tslint:disable */ 
 
 
+const structuredClone = require("structured-clone");
+const structuredSerialize = require("structured-clone").serialize;
+
+
 interface StoredObject {
   key: any;
   object: string;
@@ -30,6 +40,8 @@ interface StoredObject {
 interface Store {
   name: string;
   keyPath: string | string[];
+  keyGenerator: number;
+  autoIncrement: boolean;
   objects: { [strKey: string]: StoredObject };
 }
 
@@ -46,18 +58,585 @@ interface Databases {
 }
 
 
+/**
+ * Resolved promise, used to schedule various things
+ * by calling .next on it.
+ */
+const alreadyResolved = Promise.resolve();
+
+
+class MyDomStringList extends Array<string> implements DOMStringList {
+  contains(s: string) {
+    for (let i = 0; i < this.length; i++) {
+      if (s === this[i]) {
+        return true;
+      }
+    }
+    return false;
+  }
+  item(i: number) {
+    return this[i];
+  }
+}
+
+
+function callEventHandler(h: EventListenerOrEventListenerObject, evt: Event, 
target: any) {
+  if ("handleEvent" in h) {
+    (h as EventListenerObject).handleEvent(evt);
+  } else {
+    (h as EventListener).call(target, evt);
+  }
+}
+
+class MyRequest implements IDBRequest {
+  onerror: (this: IDBRequest, ev: Event) => any;
+
+  onsuccess: (this: IDBRequest, ev: Event) => any;
+  successHandlers: Array<(this: IDBRequest, ev: Event) => any>;
+
+  done: boolean = false;
+
+  constructor(public _transaction: Transaction, public runner: () => void) {
+  }
+
+  callSuccess(ev: Event) {
+    if (this.onsuccess) {
+      this.onsuccess(ev);
+    }
+    for (let h of this.successHandlers) {
+      h.call(this, ev);
+    }
+  }
+
+  get error(): DOMException {
+    return (null as any) as DOMException;
+  }
+
+  get result(): any {
+    return null;
+  }
+
+  get source() {
+    // buggy type definitions don't allow null even though it's in
+    // the spec.
+    return (null as any) as (IDBObjectStore | IDBIndex | IDBCursor);
+  }
+
+  get transaction() {
+    return this._transaction;
+  }
+
+  dispatchEvent(evt: Event): boolean {
+    return false;
+  }
+
+  get readyState() {
+    if (this.done) {
+      return "done";
+    }
+    return "pending";
+  }
+
+  removeEventListener(type: string,
+                      listener?: EventListenerOrEventListenerObject,
+                      options?: boolean | EventListenerOptions): void {
+    throw Error("not implemented");
+  }
+
+  addEventListener(type: string,
+                   listener: EventListenerOrEventListenerObject,
+                   useCapture?: boolean): void {
+    switch (type) {
+      case "success":
+        this.successHandlers.push(listener as any);
+        break;
+    }
+  }
+}
+
+class OpenDBRequest extends MyRequest implements IDBOpenDBRequest {
+  onblocked: (this: IDBOpenDBRequest, ev: Event) => any;
+
+  onupgradeneeded: (this: IDBOpenDBRequest, ev: IDBVersionChangeEvent) => any;
+  upgradeneededHandlers: Array<(this: IDBOpenDBRequest, ev: 
IDBVersionChangeEvent) => any> = [];
+
+  callOnupgradeneeded(ev: IDBVersionChangeEvent) {
+    if (this.onupgradeneeded) {
+      this.onupgradeneeded(ev);
+    }
+    for (let h of this.upgradeneededHandlers) {
+      h.call(this, ev);
+    }
+  }
+
+  removeEventListener(type: string,
+                      listener?: EventListenerOrEventListenerObject,
+                      options?: boolean | EventListenerOptions): void {
+    throw Error("not implemented");
+  }
+
+  addEventListener(type: string,
+                   listener: EventListenerOrEventListenerObject,
+                   useCapture?: boolean): void {
+    switch (type) {
+      case "upgradeneeded":
+        this.upgradeneededHandlers.push(listener as any);
+        break;
+      default:
+        super.addEventListener(type, listener, useCapture);
+    }
+  }
+}
+
+
+class MyObjectStore implements IDBObjectStore  {
+  get indexNames() {
+    return new DOMStringList();
+  }
+
+  constructor(public transaction: Transaction, public dbName: string, public 
storeName: string) {
+  }
+
+  get keyPath() {
+    return this.transaction.db.dbData.stores[this.storeName].keyPath;
+  }
+
+  get name() {
+    return this.storeName;
+  }
+
+  get autoIncrement() {
+    return this.transaction.db.dbData.stores[this.storeName].autoIncrement;
+  }
+
+  add(value: any, key?: any): IDBRequest {
+    throw Error("not implemented");
+  }
+
+  put(value: any, key?: any): IDBRequest {
+    throw Error("not implemented");
+  }
+
+  delete(key: any): IDBRequest {
+    throw Error("not implemented");
+  }
+
+  get(key: any): IDBRequest {
+    throw Error("not implemented");
+  }
+
+  deleteIndex(indexName: string) {
+    throw Error("not implemented");
+  }
+
+  clear(): IDBRequest {
+    throw Error("not implemented");
+  }
+
+  count(key?: any): IDBRequest {
+    throw Error("not implemented");
+  }
+
+  createIndex(name: string, keyPath: string | string[], optionalParameters?: 
IDBIndexParameters): IDBIndex {
+    throw Error("not implemented");
+  }
+
+  index(indexName: string): IDBIndex {
+    throw Error("not implemented");
+  }
+
+  openCursor(range?: IDBKeyRange | IDBValidKey, direction?: 
IDBCursorDirection): IDBRequest {
+    throw Error("not implemented");
+  }
+}
+
+
+class Db implements IDBDatabase {
+  
+  onabort: (this: IDBDatabase, ev: Event) => any;
+  onerror: (this: IDBDatabase, ev: Event) => any;
+  onversionchange: (ev: IDBVersionChangeEvent) => any;
+
+  constructor(private _name: string, private _version: number, private 
factory: MemoryIDBFactory) {
+  }
+
+  get dbData() {
+    return this.factory.data[this._name];
+  }
+
+  get name() {
+    return this._name;
+  }
+
+  get objectStoreNames() {
+    return new MyDomStringList();
+  }
+
+  get version() {
+    return this._version;
+  }
+
+  close() {
+  }
+
+  createObjectStore(name: string, optionalParameters?: 
IDBObjectStoreParameters): IDBObjectStore {
+    let tx = this.factory.getTransaction();
+    if (tx.mode !== "versionchange") {
+      throw Error("invalid mode");
+    }
+    throw Error("not implemented");
+  }
+
+  deleteObjectStore(name: string): void {
+    throw Error("not implemented");
+  }
+
+  transaction(storeNames: string | string[], mode: IDBTransactionMode = 
"readonly"): IDBTransaction {
+    const tx = new Transaction(this._name, this, mode);
+    return tx;
+  }
+
+  dispatchEvent(evt: Event): boolean {
+    throw Error("not implemented");
+  }
+
+  removeEventListener(type: string,
+                      listener?: EventListenerOrEventListenerObject,
+                      options?: boolean | EventListenerOptions): void {
+    throw Error("not implemented");
+  }
+
+  addEventListener(type: string,
+                   listener: EventListenerOrEventListenerObject,
+                   useCapture?: boolean): void {
+    throw Error("not implemented");
+  }
+}
+
+enum TransactionState {
+  Created = 1,
+  Running = 2,
+  Commited = 3,
+  Aborted = 4,
+}
+
+class Transaction implements IDBTransaction {
+  readonly READ_ONLY: string = "readonly";
+  readonly READ_WRITE: string = "readwrite";
+  readonly VERSION_CHANGE: string = "versionchange";
+
+  onabort: (this: IDBTransaction, ev: Event) => any;
+  onerror: (this: IDBTransaction, ev: Event) => any;
+  oncomplete: (this: IDBTransaction, ev: Event) => any;
+
+  completeHandlers: Array<(this: IDBTransaction, ev: Event) => any> = [];
+
+  state: TransactionState = TransactionState.Created;
+
+  _transactionDbData: Database|undefined;
+
+  constructor(public dbName: string, public dbHandle: Db, public _mode: 
IDBTransactionMode) {
+  }
+
+  get mode() {
+    return this._mode;
+  }
+
+  start() {
+    if (this.state != TransactionState.Created) {
+      throw Error();
+    }
+    this._transactionDbData = structuredClone(this.dbHandle.dbData);
+  }
+
+  get error(): DOMException {
+    throw Error("not implemented");
+  }
+
+  get db() {
+    return this.dbHandle;
+  }
+
+  get transactionDbData() {
+    if (this.state != TransactionState.Running) {
+      throw Error();
+    }
+    let d = this._transactionDbData;
+    if (!d) {
+      throw Error();
+    }
+    return d;
+  }
+
+  abort() {
+    throw Error("not implemented");
+  }
+
+  objectStore(storeName: string): IDBObjectStore {
+    return new MyObjectStore(this, this.dbName, storeName);
+  }
+
+  dispatchEvent(evt: Event): boolean {
+    throw Error("not implemented");
+  }
+
+  removeEventListener(type: string,
+                      listener?: EventListenerOrEventListenerObject,
+                      options?: boolean | EventListenerOptions): void {
+    throw Error("not implemented");
+  }
+
+  addEventListener(type: string,
+                   listener: EventListenerOrEventListenerObject,
+                   useCapture?: boolean): void {
+    switch (type) {
+      case "complete":
+        this.completeHandlers.push(listener as any);
+      break;
+    }
+  }
+
+  callComplete(ev: Event) {
+    if (this.oncomplete) {
+      this.oncomplete(ev);
+    }
+    for (let h of this.completeHandlers) {
+      h.call(this, ev);
+    }
+  }
+}
+
+
+/**
+ * Polyfill for CustomEvent.
+ */
+class MyEvent implements Event {
+  readonly NONE: number = 0;
+  readonly CAPTURING_PHASE: number = 1;
+  readonly AT_TARGET: number = 2;
+  readonly BUBBLING_PHASE: number = 3;
+
+  _bubbles = false;
+  _cancelable = false;
+  _target: any;
+  _currentTarget: any;
+  _defaultPrevented: boolean = false;
+  _eventPhase: number = 0;
+  _timeStamp: number = 0;
+  _type: string;
+
+  constructor(typeArg: string) {
+    this._type = typeArg;
+  }
+
+  get eventPhase() {
+    return this._eventPhase;
+  }
+
+  get returnValue() {
+    return this.defaultPrevented;
+  }
+
+  set returnValue(v: boolean) {
+    if (v) {
+      this.preventDefault();
+    }
+  }
+
+  get isTrusted() {
+    return false;
+  }
+
+  get bubbles() {
+    return this._bubbles;
+  }
+
+  get cancelable() {
+    return this._cancelable;
+  }
+
+  set cancelBubble(v: boolean) {
+    if (v) {
+      this.stopPropagation();
+    }
+  }
+
+  get defaultPrevented() {
+    return this._defaultPrevented;
+  }
+
+  stopPropagation() {
+    throw Error("not implemented");
+  }
+
+  get currentTarget() {
+    return this._currentTarget;
+  }
+
+  get target() {
+    return this._target;
+  }
+
+  preventDefault() {
+  }
+
+  get srcElement() {
+    return this.target;
+  }
+
+  get timeStamp() {
+    return this._timeStamp;
+  }
+
+  get type() {
+    return this._type;
+  }
+
+  get scoped() {
+    return false;
+  }
+
+  initEvent(eventTypeArg: string, canBubbleArg: boolean, cancelableArg: 
boolean) {
+    if (this._eventPhase != 0) {
+      return;
+    }
+
+    this._type = eventTypeArg;
+    this._bubbles = canBubbleArg;
+    this._cancelable = cancelableArg;
+  }
+
+  stopImmediatePropagation() {
+    throw Error("not implemented");
+  }
+
+  deepPath(): EventTarget[] {
+    return [];
+  }
+}
+
+
+class VersionChangeEvent extends MyEvent {
+  _newVersion: number|null;
+  _oldVersion: number;
+  constructor(oldVersion: number, newVersion?: number) {
+    super("VersionChange");
+    this._oldVersion = oldVersion;
+    this._newVersion = newVersion || null;
+  }
+
+  get newVersion() {
+    return this._newVersion;
+  }
+
+  get oldVersion() {
+    return this._oldVersion;
+  }
+}
+
+
 class MemoryIDBFactory implements IDBFactory {
   data: Databases = {};
 
+  currentRequest: MyRequest|undefined;
+
+  scheduledRequests: MyRequest[] = [];
+
+  private addRequest(r: MyRequest) {
+    this.scheduledRequests.push(r);
+    if (this.currentRequest) {
+      return;
+    }
+    const runNext = (prevRequest?: MyRequest) => {
+      const nextRequest = this.scheduledRequests.shift();
+      if (nextRequest) {
+        const tx = nextRequest.transaction;
+
+        if (tx.state === TransactionState.Running) {
+          // Okay, we're continuing with the same transaction
+        } else if (tx.state === TransactionState.Created) {
+          tx.start();
+        } else {
+          throw Error();
+        }
+
+        this.currentRequest = nextRequest;
+        this.currentRequest.runner();
+        this.currentRequest.done = true;
+        this.currentRequest = undefined;
+        runNext(nextRequest);
+      } else if (prevRequest) {
+        // We have no other request scheduled, so
+        // auto-commit the transaction that the
+        // previous request worked on.
+        let lastTx = prevRequest._transaction;
+        this.data[lastTx.dbName] = lastTx.transactionDbData;
+      }
+    };
+    alreadyResolved.then(() => {
+      runNext();
+    });
+  }
+
+  /**
+   * Get the only transaction that is active right now
+   * or throw if no transaction is active.
+   */
+  getTransaction() {
+    const req = this.currentRequest;
+    if (!req) {
+      throw Error();
+    }
+    return req.transaction;
+  }
+
   cmp(a: any, b: any): number {
-    return 0;
+    throw Error("not implemented");
   }
 
   deleteDatabase(name: string): IDBOpenDBRequest {
     throw Error("not implemented");
   }
 
-  open(name: string, version?: number): IDBOpenDBRequest {
-    throw Error("not implemented");
+  open(dbName: string, version?: number): IDBOpenDBRequest {
+    if (version !== undefined && version <= 0) {
+      throw Error("invalid version");
+    }
+
+    let upgradeNeeded = false;
+    let mydb: Database;
+    if (dbName in this.data) {
+      mydb = this.data[dbName];
+      if (version === undefined || version == mydb.version) {
+        // we can open without upgrading
+      } else if (version > mydb.version) {
+        upgradeNeeded = true;
+      } else {
+        throw Error("version error");
+      }
+    } else {
+      mydb = {
+        name: dbName,
+        stores: {},
+        version: (version || 1),
+      };
+      upgradeNeeded = true;
+    }
+
+    const db = new Db(dbName, mydb.version, this);
+    const tx = new Transaction(dbName, db, "versionchange");
+
+    const req = new OpenDBRequest(tx, () => {
+      if (upgradeNeeded) {
+        let versionChangeEvt = new VersionChangeEvent(mydb.version, version);
+        req.callOnupgradeneeded(versionChangeEvt);
+      }
+      let successEvent = new MyEvent("success");
+      req.callSuccess(successEvent);
+    });
+
+    this.addRequest(req);
+
+    return req;
   }
 }
diff --git a/tsconfig.json b/tsconfig.json
index 2ba62b51..ef56fdd2 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -41,6 +41,10 @@
     "src/i18n/strings.ts",
     "src/logging.ts",
     "src/memidb.ts",
+    "src/node_modules/structured-clone/clone.js",
+    "src/node_modules/structured-clone/index.js",
+    "src/node_modules/structured-clone/serialize.js",
+    "src/node_modules/structured-clone/test/test.js",
     "src/query.ts",
     "src/timer.ts",
     "src/types-test.ts",

-- 
To stop receiving notification emails like this one, please contact
address@hidden



reply via email to

[Prev in Thread] Current Thread [Next in Thread]