Level / community

Discussion, support and common information for projects in the community.
MIT License
42 stars 15 forks source link

Refactor encodings #102

Closed vweevers closed 2 years ago

vweevers commented 2 years ago

As written in https://github.com/Level/community/issues/58, there are too many forms of encoding now. Yet for an abstract-leveldown implementation there's no builtin primitive to decode/deserialize data.

So here's a WIP plan for refactoring. I'll be editing this.

vweevers commented 2 years ago

Updated the above with a more complete plan, that removes the need for serialization and asBuffer options, and also paves the way for typed arrays.

Example of a transcoder (some should be hardcoded to optimize them, others like json+buffer can be dynamic):

exports['utf8+view'] = new Encoding({
  encode (data) {
    return ArrayBuffer.isView(data) ? data : textEncoder.encode(data)
  },
  decode (data) {
    return textDecoder.decode(data)
  },
  type: 'utf8+view',
  format: 'view'
})
vweevers commented 2 years ago

Doing this in abstract-leveldown could have performance benefits too. For example, if we know that a particular value encoding on a get() operation is idempotent (like buffer or utf8) and we know that the implementation supports that encoding "natively", then we can do _get(..., callback) without wrapping the callback in another function that decodes the value.

vweevers commented 2 years ago

https://github.com/Level/transcoder

vweevers commented 2 years ago

Ugh, this works better than I expected. Now subleveldown can just forward operations to a db, without unwrapping or rewrapping that db with encoding-down or levelup. Doesn't matter what encoding options that db used, or whether it stores data as buffers or Uint8Array internally. On the subleveldown db you can use any encoding too, including Uint8Array even though subleveldown internally works with Buffers and strings. It just fucking works.

The following tests are passing (locally; haven't pushed yet):

Click to expand ```js const test = require('tape') const suite = require('abstract-leveldown/test') const memdown = require('memdown') const subleveldown = require('subleveldown') // Test abstract-leveldown compliance function runSuite (factory) { suite({ test, factory }) } // Test basic prefix runSuite(function factory (opts) { return subleveldown(memdown(), 'test', opts) }) // Test empty prefix runSuite(function factory (opts) { return subleveldown(memdown(), '', opts) }) // Test custom separator runSuite(function factory (opts) { return subleveldown(memdown(), 'test', { ...opts, separator: '%' }) }) // Test on db with buffer encoding runSuite(function factory (opts) { return subleveldown(memdown({ keyEncoding: 'buffer' }), 'test', opts) }) // Test on db with view encoding (Uint8Array) runSuite(function factory (opts) { return subleveldown(memdown({ keyEncoding: 'view' }), 'test', opts) }) // Have memdown internally use views too runSuite(function factory (opts) { return subleveldown(memdown({ keyEncoding: 'view', storeEncoding: 'view' }), 'test', opts) }) // Lastly, for good measure: runSuite(function factory (opts) { return subleveldown(memdown({ keyEncoding: 'buffer', storeEncoding: 'view' }), 'test', opts) }) ```

@ralphtheninja @juliangruber @MeirionHughes ARE YOU EXCITED? Because I am! Fuck!

juliangruber commented 2 years ago

Sounds like this gets us a lot of flexibility and api simplicity as well. Well done! 👏

vweevers commented 2 years ago

Added support of other ecosystem encodings (codecs, abstract-encoding, multiformats) to level-transcoder, fixed some bugs, and updated its README: https://github.com/Level/transcoder. That part is now done.