UNPKG

5.28 kBJavaScriptView Raw
1var RandomAccess = require('random-access-storage')
2var inherits = require('inherits')
3var nextTick = require('next-tick')
4var once = require('once')
5var blocks = require('./lib/blocks.js')
6var bufferFrom = require('buffer-from')
7var bufferAlloc = require('buffer-alloc')
8
9var DELIM = '\0'
10var win = typeof window !== 'undefined' ? window
11 : (typeof self !== 'undefined' ? self : {})
12
13module.exports = function (dbname, xopts) {
14 if (!xopts) xopts = {}
15 var idb = xopts.idb || (typeof win !== 'undefined'
16 ? win.indexedDB || win.mozIndexedDB
17 || win.webkitIndexedDB || win.msIndexedDB
18 : null)
19 if (!idb) throw new Error('indexedDB not present and not given')
20 var db = null, dbqueue = []
21 if (typeof idb.open === 'function') {
22 var req = idb.open(dbname)
23 req.addEventListener('upgradeneeded', function () {
24 db = req.result
25 db.createObjectStore('data')
26 })
27 req.addEventListener('success', function () {
28 db = req.result
29 dbqueue.forEach(function (cb) { cb(db) })
30 dbqueue = null
31 })
32 } else {
33 db = idb
34 }
35 return function (name, opts) {
36 if (typeof name === 'object') {
37 opts = name
38 name = opts.name
39 }
40
41 if (!opts) opts = {}
42 opts.name = name
43
44 return new Store(Object.assign({ db: getdb }, xopts, opts))
45 }
46 function getdb (cb) {
47 if (db) nextTick(function () { cb(db) })
48 else dbqueue.push(cb)
49 }
50}
51
52function Store (opts) {
53 if (!(this instanceof Store)) return new Store(opts)
54 RandomAccess.call(this)
55 if (!opts) opts = {}
56 if (typeof opts === 'string') opts = { name: opts }
57 this.size = opts.size || 4096
58 this.name = opts.name
59 this.length = opts.length || 0
60 this._getdb = opts.db
61}
62inherits(Store, RandomAccess)
63
64Store.prototype._blocks = function (i, j) {
65 return blocks(this.size, i, j)
66}
67
68Store.prototype._read = function (req) {
69 var self = this
70 var buffers = []
71 self._store('readonly', function (err, store) {
72 if ((self.length || 0) < req.offset+req.size) {
73 return req.callback(new Error('Could not satisfy length'))
74 }
75 if (err) return req.callback(err)
76 var offsets = self._blocks(req.offset, req.offset+req.size)
77 var pending = offsets.length + 1
78 var firstBlock = offsets.length > 0 ? offsets[0].block : 0
79 var j = 0
80 for (var i = 0; i < offsets.length; i++) (function (o) {
81 var key = self.name + DELIM + o.block
82 backify(store.get(key), function (err, ev) {
83 if (err) return req.callback(err)
84 buffers[o.block-firstBlock] = ev.target.result
85 ? bufferFrom(ev.target.result.subarray(o.start,o.end))
86 : bufferAlloc(o.end-o.start)
87 if (--pending === 0) req.callback(null, Buffer.concat(buffers))
88 })
89 })(offsets[i])
90 if (--pending === 0) req.callback(null, Buffer.concat(buffers))
91 })
92}
93
94Store.prototype._write = function (req) {
95 var self = this
96 self._store('readwrite', function (err, store) {
97 if (err) return req.callback(err)
98 var offsets = self._blocks(req.offset, req.offset + req.data.length)
99 var pending = 1
100 var buffers = {}
101 for (var i = 0; i < offsets.length; i++) (function (o,i) {
102 if (o.end-o.start === self.size) return
103 pending++
104 var key = self.name + DELIM + o.block
105 backify(store.get(key), function (err, ev) {
106 if (err) return req.callback(err)
107 buffers[i] = bufferFrom(ev.target.result || bufferAlloc(self.size))
108 if (--pending === 0) write(store, offsets, buffers)
109 })
110 })(offsets[i],i)
111 if (--pending === 0) write(store, offsets, buffers)
112 })
113 function write (store, offsets, buffers) {
114 for (var i = 0, j = 0; i < offsets.length; i++) {
115 var o = offsets[i]
116 var len = o.end - o.start
117 if (len === self.size) {
118 block = bufferFrom(req.data.slice(j, j+len))
119 } else {
120 block = buffers[i]
121 req.data.copy(block, o.start, j, j+len)
122 }
123 store.put(block,self.name + DELIM + o.block)
124 j += len
125 }
126 var length = Math.max(self.length || 0, req.offset + req.data.length)
127 store.put(length, self.name + DELIM + 'length')
128 store.transaction.addEventListener('complete', function () {
129 self.length = length
130 req.callback(null)
131 })
132 store.transaction.addEventListener('error', function (err) {
133 req.callback(err)
134 })
135 }
136}
137
138Store.prototype._store = function (mode, cb) {
139 cb = once(cb)
140 var self = this
141 self._getdb(function (db) {
142 var tx = db.transaction(['data'], mode)
143 var store = tx.objectStore('data')
144 tx.addEventListener('error', cb)
145 cb(null, store)
146 })
147}
148
149Store.prototype._open = function (req) {
150 var self = this
151 this._getdb(function(db) {
152 self._store('readonly', function (err, store) {
153 backify(store.get(self.name + DELIM + "length"), function(err, ev) {
154 self.length = ev.target.result || 0
155 req.callback(null)
156 })
157 })
158 })
159}
160
161Store.prototype._close = function (req) {
162 this._getdb(function (db) {
163 db.close()
164 req.callback()
165 })
166}
167
168Store.prototype._stat = function (req) {
169 var self = this
170 nextTick(function () {
171 req.callback(null, { size: self.length })
172 })
173}
174
175function backify (r, cb) {
176 r.addEventListener('success', function (ev) { cb(null, ev) })
177 r.addEventListener('error', cb)
178}