forked from MinEduTDF/idb-chunk-store
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
123 lines (106 loc) · 3.62 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
var EventEmitter = require('events').EventEmitter
var inherits = require('inherits')
module.exports = Storage
inherits(Storage, EventEmitter)
// vr idb = require('fake-indexeddb')
/* global self */
var env = typeof window === 'object' ? window : self
var idb = env.indexedDB || env.mozIndexedDB || env.webkitIndexedDB || env.msIndexedDB
function Storage (chunkLength, opts) {
if (!(this instanceof Storage)) return new Storage(chunkLength, opts)
if (!opts) opts = {}
EventEmitter.call(this)
this.setMaxListeners(100)
var self = this
this.chunkLength = Number(chunkLength)
if (!this.chunkLength) throw new Error('First argument must be a chunk length')
this.closed = false
this.length = Number(opts.length) || Infinity
if (this.length !== Infinity) {
this.lastChunkLength = (this.length % this.chunkLength) || this.chunkLength
this.lastChunkIndex = Math.ceil(this.length / this.chunkLength) - 1
}
self._ready = false
var request = idb.open(opts.name || 'chunksDB')
request.addEventListener('upgradeneeded', function () {
var db = request.result
db.createObjectStore('chunks')
})
request.addEventListener('success', function () {
self.db = request.result
self.emit('ready')
})
}
Storage.prototype._store = function (mode, cb) {
var self = this
if (!self.db) return self.once('ready', ready)
else process.nextTick(ready)
function ready () {
var trans = self.db.transaction(['chunks'], mode)
var store = trans.objectStore('chunks')
trans.addEventListener('error', function (err) { cb(err) })
cb(null, store)
}
}
Storage.prototype.put = function (index, buf, cb) {
if (this.closed) return nextTick(cb, new Error('Storage is closed'))
var isLastChunk = (index === this.lastChunkIndex)
if (isLastChunk && buf.length !== this.lastChunkLength) {
return nextTick(cb, new Error('Last chunk length must be ' + this.lastChunkLength))
}
if (!isLastChunk && buf.length !== this.chunkLength) {
return nextTick(cb, new Error('Chunk length must be ' + this.chunkLength))
}
this._store('readwrite', function (err, store) {
if (err) return cb(err)
backify(store.put(buf, index), wait(store, cb))
})
}
function wait (store, cb) {
var pending = 2
store.transaction.addEventListener('complete', done)
return function (err) {
if (err) cb(err)
else done()
}
function done () { if (cb && --pending === 0) cb(null) }
}
Storage.prototype.get = function (index, opts, cb) {
if (typeof opts === 'function') return this.get(index, null, opts)
if (this.closed) return nextTick(cb, new Error('Storage is closed'))
this._store('readonly', function (err, store) {
if (err) {
cb(err)
} else {
backify(store.get(index), function (err, ev) {
if (err) {
cb(err)
} else if (ev.target.result === undefined) {
cb(null, new Buffer(0))
} else {
var buf = new Buffer(ev.target.result)
if (!opts) return nextTick(cb, null, buf)
var offset = opts.offset || 0
var len = opts.length || (buf.length - offset)
cb(null, buf.slice(offset, len + offset))
}
})
}
})
}
Storage.prototype.close = Storage.prototype.destroy = function (cb) {
if (this.closed) return nextTick(cb, new Error('Storage is closed'))
if (!this.db) return nextTick(cb, undefined)
this.closed = true
// self.db.close()
nextTick(cb, null)
}
function nextTick (cb, err, val) {
process.nextTick(function () {
if (cb) cb(err, val)
})
}
function backify (r, cb) {
r.addEventListener('success', function (ev) { cb(null, ev) })
r.addEventListener('error', function (err) { cb(err) })
}