Get lazy writing working
authorNick Downing <nick@ndcode.org>
Wed, 5 Jan 2022 01:08:21 +0000 (12:08 +1100)
committerNick Downing <nick@ndcode.org>
Wed, 5 Jan 2022 01:23:49 +0000 (12:23 +1100)
a.mjs
logjson.mjs

diff --git a/a.mjs b/a.mjs
index a6e4e51..4f546cf 100755 (executable)
--- a/a.mjs
+++ b/a.mjs
@@ -4,8 +4,11 @@ import logjson from './logjson.mjs'
 import fsPromises from 'fs/promises'
 
 let file = new logjson.File()
-await file.open(
-  'a.logjson', 
-  JSON.parse(await fsPromises.readFile('random.json', 'utf-8'))
+await file.open('a.logjson', {})
+file.set(
+  logjson.json_to_logjson(
+    JSON.parse(await fsPromises.readFile('random.json', 'utf-8'))
+  )
 )
+await file.flush()
 await file.close()
index e4800f3..fd06592 100644 (file)
@@ -133,7 +133,7 @@ class File {
 
     let data = JSON.stringify(value, null, 2)
     if (is_root) {
-      let buffer = Buffer.from(`<${data}>\n`)
+      let buffer = Buffer.from(`<${data}>\n`, 'utf-8')
       let ptr = this.eof
       let len = buffer.length
       assert(
@@ -142,7 +142,7 @@ class File {
       this.eof += len
       return [ptr + 1, len - 3] // ptr, len
     }
-    let buffer = Buffer.from(`${data}\n`)
+    let buffer = Buffer.from(`${data}\n`, 'utf-8')
     let ptr = this.eof
     let len = buffer.length
     assert(
@@ -153,22 +153,65 @@ class File {
   }
 
   async get() {
-    let [ptr, len, value] = this.value
-    if (value === undefined) {
-      let buffer = Buffer.alloc(len)
-      assert(
-        (await this.log.read(buffer, 0, len, ptr)).bytesRead === len
-      )
-      value = JSON.parse(buffer.toString('utf-8'))
-      if (typeof value === 'object' && value !== null)
-        value =
-          value instanceof Array ?
-            new LazyArray(this, value) :
-            new LazyObject(this, value)
-      this.value[2] = value
-    }
+    let value = this.value
+
+    //if (value instanceof Array) {
+      let [ptr, len, new_value] = value
+      if (new_value === undefined) {
+        let buffer = Buffer.alloc(len)
+        assert(
+          (await this.log.read(buffer, 0, len, ptr)).bytesRead === len
+        )
+        new_value = JSON.parse(buffer.toString('utf-8'))
+        if (typeof new_value === 'object' && new_value !== null)
+          new_value =
+            new_value instanceof Array ?
+              new LazyArray(this, new_value) :
+              new LazyObject(this, new_value)
+        value[2] = new_value
+      }
+      value = new_value
+    //}
     return value
   }
+
+  set(value) {
+    assert(
+      typeof value !== 'object' ||
+      value === null ||
+      value instanceof Lazy
+    )
+    //if (typeof value === 'object' && value !== null)
+      value = [-1, 0, value]
+    this.value = value
+  }
+
+  async flush() {
+    //if (this.value instanceof Array) {
+      let [ptr, len, value] = this.value
+      if (
+        (value instanceof Lazy && await value.flush(this)) ||
+          ptr === -1
+      ) {
+        if (value instanceof Lazy)
+          value = value.pack()
+        let buffer = Buffer.from(
+          `<${JSON.stringify(value, null, 2)}>\n`,
+          'utf-8'
+        )
+
+        ptr = this.eof
+        len = buffer.length
+        assert(
+          (await this.log.write(buffer, 0, len, ptr)).bytesWritten === len
+        )
+        this.eof += len
+  
+        this.value[0] = ptr + 1
+        this.value[1] = len - 3
+      }
+    //}
+  }
 }
 
 // logjson array or object
@@ -192,6 +235,14 @@ class Lazy {
   delete(key) {
     throw new Error('not implemented')
   }
+
+  async flush(file) {
+    throw new Error('not implemented')
+  }
+
+  pack() {
+    throw new Error('not implemented')
+  }
 }
 
 class LazyArray extends Lazy {
@@ -211,6 +262,7 @@ class LazyArray extends Lazy {
     if (key < 0 || key >= this.length)
       return default_value
     let value = this.array[key]
+
     //if (value instanceof Array) {
       let [ptr, len, new_value] = value
       if (new_value === undefined) {
@@ -233,12 +285,14 @@ class LazyArray extends Lazy {
 
   set(key, value) {
     assert(typeof key === 'number')
-    //if (typeof value === 'object' && value !== null) {
-    //  assert(value instanceof Lazy)
-    //  value = [-1, 0, value]
-    //}
-    //this.array[key] = value
-    this.array[key] = [-1, 0, value]
+    assert(
+      typeof value !== 'object' ||
+      value === null ||
+      value instanceof Lazy
+    )
+    //if (typeof value === 'object' && value !== null)
+      value = [-1, 0, value]
+    this.array[key] = value
     this.length = this.array.length
   }
 
@@ -248,7 +302,7 @@ class LazyArray extends Lazy {
   }
 
   push(value) {
-    set(this.length, value)
+    this.set(this.length, value)
   }
 
   async pop() {
@@ -258,6 +312,56 @@ class LazyArray extends Lazy {
     this.delete(i)
     return value
   }
+
+  async flush(file) {
+    if (file != this.file) {
+      assert(this.file === null)
+      this.file = file
+    }
+
+    let dirty = false
+    for (let i = 0; i < this.length; ++i) {
+      let item = this.array[i]
+      //if (item instanceof Array) {
+        let [ptr, len, value] = item
+        if (
+          (value instanceof Lazy && await value.flush(file)) ||
+            ptr === -1
+        ) {
+          if (value instanceof Lazy)
+            value = value.pack()
+          let buffer = Buffer.from(
+            JSON.stringify(value, null, 2) + '\n',
+            'utf-8'
+          )
+
+          ptr = this.file.eof
+          len = buffer.length
+          assert(
+            (await this.file.log.write(buffer, 0, len, ptr)).bytesWritten ===
+              len
+          )
+          this.file.eof += len
+
+          item[0] = ptr
+          item[1] = len - 1
+          dirty = true
+        }
+      //}
+    }
+    return dirty
+  }
+
+  pack() {
+    let new_array = []
+    for (let i = 0; i < this.length; ++i) {
+      let item = this.array[i]
+      //if (item instanceof Array)
+        item = item.slice(0, 2)
+      new_array.push(item)
+    }
+    return new_array
+  }
 }
 
 class LazyObject extends Lazy {
@@ -276,6 +380,7 @@ class LazyObject extends Lazy {
     if (!Object.prototype.hasOwnProperty.call(this.object, key))
       return default_value
     let value = this.object[key]
+
     //if (value instanceof Array) {
       let [ptr, len, new_value] = value
       if (new_value === undefined) {
@@ -298,10 +403,13 @@ class LazyObject extends Lazy {
 
   set(key, value) {
     assert(typeof key === 'string')
-    if (typeof value === 'object' && value !== null) {
-      assert(value instanceof Lazy)
+    assert(
+      typeof value !== 'object' ||
+      value === null ||
+      value instanceof Lazy
+    )
+    //if (typeof value === 'object' && value !== null)
       value = [-1, 0, value]
-    }
     this.object[key] = value
   }
 
@@ -315,6 +423,56 @@ class LazyObject extends Lazy {
       keys.push(i)
     return keys
   }
+
+  async flush(file) {
+    if (file != this.file) {
+      assert(this.file === null)
+      this.file = file
+    }
+
+    let dirty = false
+    for (let i in this.object) {
+      let item = this.object[i]
+      //if (item instanceof Array) {
+        let [ptr, len, value] = item
+        if (
+          (value instanceof Lazy && await value.flush(file)) ||
+            ptr === -1
+        ) {
+          if (value instanceof Lazy)
+            value = value.pack()
+          let buffer = Buffer.from(
+            JSON.stringify(value, null, 2) + '\n',
+            'utf-8'
+          )
+
+          ptr = this.file.eof
+          len = buffer.length
+          assert(
+            (await this.file.log.write(buffer, 0, len, ptr)).bytesWritten ===
+              len
+          )
+          this.file.eof += len
+
+          item[0] = ptr
+          item[1] = len
+          dirty = true
+        }
+      //}
+    }
+    return dirty
+  }
+
+  pack() {
+    let new_object = {}
+    for (let i in this.object) {
+      let item = this.object[i]
+      //if (item instanceof Array)
+        item = item.slice(0, 2)
+      new_object[i] = item
+    }
+    return new_object
+  }
 }
 
 let logjson_to_json = async value => {
@@ -342,12 +500,12 @@ let json_to_logjson = value => {
   if (typeof value === 'object' && value !== null) {
     let new_value
     if (value instanceof Array) {
-      new_value = LazyArray()
+      new_value = new LazyArray()
       for (let i = 0; i < value.length; ++i)
         new_value.push(json_to_logjson(value[i]))
     }
     else {
-      new_value = LazyObject()
+      new_value = new LazyObject()
       for (let i in value)
         new_value.set(i, json_to_logjson(value[i]))
     }