-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathvector.js
191 lines (174 loc) · 6.78 KB
/
vector.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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
'use strict'
var Blocks = require('./blocks')
var Stream = require('./stream')
//reads data in blocks, into memory.
//creates vectors in that, always keeping any vector
//within a block.
var Format = require('./format')
const constants = require('./constants')
const MAGIC_NUMBER = constants.magic
const FREE = constants.free
const B_HEADER = constants.block
const V_HEADER = constants.vector
const V_PREV = constants.prev
const V_START = constants.start
//allocate a new root vector, requested size, little smaller if necessary to fit into block.
function alloc_new (blocks, _size, prev_vector, start, format) {
//if(!size) throw new Error('invalid size:'+size)
//always alloc into the last block
var block_index = blocks.last()
var block_size = blocks.block_size
var block_start = (block_index * block_size)
var block = blocks.blocks[block_index]
var free = block.readUInt32LE(FREE) || B_HEADER
var max_size = (block_size - (free + V_HEADER)) / 4
//+2 because that is enough room for the header of a vector.
var new_size = max_size <= _size * 1.5 ? max_size : _size
var vector2 = block_start + format.alloc(block, new_size)
block.writeUInt32LE(prev_vector, vector2%block_size + V_PREV)
block.writeUInt32LE(start, vector2%block_size + V_START)
//if(new_size < 32) console.error('small vector:'+new_size+', after:'+_size)
blocks.free = Math.max(blocks.free, block_start + block.readUInt32LE(FREE))
blocks.dirty(block_index)
return vector2
}
//append a vector, including updating pointer from previous vector
function alloc_append(blocks, vector, format) {
var block_size = blocks.block_size
var block_index = ~~(vector/block_size)
//address of vector, relative to block
var _vector = vector%block_size
var block_index = ~~(vector/block_size)
var block = blocks.blocks[block_index]
if(!block) throw new Error('block:'+block_index+' was not ready before calling alloc_append')
var _size = format.size(block, _vector)
// always fill one block before going to the next one.
// to avoid leaving small allocations at the end of a block,
// if an allocation will leave a smallish space, increase it's size to fill the block.
// normally, double the size of the vector from last time, but sometimes tripple it
// to fill the block.
// if there is no space remaining in the block, next size is doubled, and allocation
// happens in a new block.
var free = block.readUInt32LE(FREE) || B_HEADER
var vector2 = alloc_new(blocks, _size*2, vector, format.start(block, _vector) + _size, format)
//write the next pointer in the previous vector
block.writeUInt32LE(vector2, _vector + 4)
blocks.dirty(block_index)
return vector2
}
function inject (blocks, block_size) {
block_size = block_size || 65536
var format = new Format(block_size)
var self
return self = {
ready: blocks.ready,
alloc: function (size, cb) {
blocks.ready(function () {
cb(null, alloc_new(blocks, size, 0, 0, format))
})
},
get: function (vector, index, cb) {
blocks.ready(function () {
var block_index = ~~(vector/block_size)
//address of vector, relative to block
var _vector = vector%block_size
blocks.get(block_index, function (err, block) {
if(err) return cb(err)
var _size = format.size(block, _vector)
var _start = format.start(block, _vector)
var _next = format.next(block, _vector)
var _prev = format.prev(block, _vector)
var _index = index - _start
if(_index < 0) {
self.get(_prev, index, cb)
}
else if(_size > _index) cb(null, format.get(block, _vector, _index))
else if(_next) {
//TODO: optimize for case where next is within this same block
self.get(_next, index, cb)
}
else cb(new Error('not found'))
})
})
},
set: function (vector, index, value, cb) {
if(vector == 0) return cb(new Error('cannot set to from null vector:'+vector))
if(index < 0) return cb(new Error('index' + index + ' must be >= 0'))
blocks.ready(function () {
var block_index = ~~(vector/block_size)
//address of vector, relative to block
var _vector = vector%block_size
blocks.get(block_index, function (err, block) {
if(!block.readUInt32LE(FREE)) throw new Error('block missing free pointer')
var _start = format.start(block, _vector)
var _next, _size = format.size(block, _vector)
var _index = index - _start
if(!_size) throw new Error('zero size vector '+_vector)
if(_index < 0)
self.set(format.prev(block, _vector), index, value, cb)
else if(_size > _index) {
format.set(block, _vector, _index, value)
blocks.dirty(block_index)
cb(null, vector, index)
}
else if(_next = format.next(block, _vector)) {
//TODO: optimize for case where next is within this same block
self.set(_next, index, value, cb)
}
else {
self.set(alloc_append(blocks, vector, format), index, value, cb)
}
})
})
},
length: function (vector, cb) {
blocks.ready(function () {
var block_index = ~~(vector/block_size)
//address of vector, relative to block
blocks.get(block_index, function (err, block) {
var _vector = vector%block_size
var _next = format.next(block, _vector)
if(_next) return self.length(_next, cb)
else cb(null, format.start(block, _vector) + format.length(block, _vector))
})
})
},
//add a value to the end of a vector. much like [].push()
append: function (vector, value, cb) {
self.length(vector, function (err, i) {
if(err) cb(err)
else self.set(vector, i, value, cb)
})
},
size: function (cb) {
cb(null, blocks.length * block_size)
},
dump: function (vector, cb) {
var data = []
;(function dump (vector) {
var block_index = ~~(vector/block_size)
//address of vector, relative to block
var _vector = vector%block_size
blocks.get(block_index, function (err, block) {
var _size = format.size(block, _vector)
var _next = format.next(block, _vector)
data.push({id: vector, size: _size, next: _next, block: ~~(vector/block_size)})
if(_next)
dump(_next)
else
cb(null, data)
})
})(vector)
},
drain: blocks.drain,
stream: function (opts) {
return new Stream(opts, self)
}
}
}
module.exports = function (raf, block_size) {
block_size = block_size || 65536
var blocks = Blocks(raf, block_size, MAGIC_NUMBER)
return inject(blocks, block_size)
}
module.exports.inject = inject