This repository has been archived by the owner on May 11, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 7
/
mem.js
206 lines (186 loc) · 4.92 KB
/
mem.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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
const crypto = require('crypto')
// bit of a hack, treat the address as utf8,
// then xor into a single byte
const getLoc = exports.getLoc = function getLoc (address) {
const buf = Buffer.from(address, 'utf8')
let loc = buf.readInt8(0)
for (let i = 1; i < buf.byteLength; ++i) {
loc = loc ^ buf.readInt8(i)
}
return loc.toString(16)
}
const getHash = exports.getHash = function getHash (str) {
const hasher = crypto.createHash('sha256')
hasher.update(Buffer.from(str, 'utf8'))
return hasher.digest().toString('base64')
}
/**
* _data is datastore of
* Map(loc, Map(entryHash, (entryContent, meta:Map(metaHash, metaContent))))
*/
class Mem {
constructor () {
this._data = new Map()
this._indexers = []
this._locHashes = {}
}
registerIndexer (fn) {
const store = {}
this._indexers.push([store, fn])
return store
}
/**
* Called at every insert.
* we need a way to generate consistent hashes
* (that is, work around insertion order)
*/
_genLocHashes () {
this._locHashes = {}
for (let [loc, sub] of this._data) {
const locData = []
const addrs = Array.from(sub.keys()).sort()
for (let addr of addrs) {
const entry = sub.get(addr)
const meta = []
const metaHashes = Array.from(entry.meta.keys()).sort()
for (let metaHash of metaHashes) {
const metaItem = entry.meta.get(metaHash)
meta.push([metaHash, metaItem])
}
locData.push([addr, entry.entry, meta])
}
this._locHashes[loc] = getHash(JSON.stringify(locData))
}
}
_getEntry (address) {
const loc = getLoc(address)
// create new loc on first address in this loc
if (!this._data.has(loc)) {
this._data.set(loc, new Map())
}
// if loc does not have address, create empty Entry for this address
const ref = this._data.get(loc)
if (!ref.has(address)) {
ref.set(address, {
entry: '{}',
meta: new Map()
})
}
return ref.get(address)
}
_publishIndex (data) {
for (let idx of this._indexers) {
idx[1](idx[0], data)
}
}
/**
* Insert a dhtEntry
* @returns {boolean} - return true on successful insertion
*/
insert (data) {
if (!data || typeof data.address !== 'string' || !data.address.length) {
throw new Error('cannot insert dhtEntry without string address')
}
// get current entry at address or create empty entry
const entry = this._getEntry(data.address)
const strData = JSON.stringify(data)
// if entry is different from previously stored entry, update it
if (entry.entry !== strData) {
entry.entry = strData
this._genLocHashes()
this._publishIndex(data)
return true
}
return false
}
/**
* Insert a dhtMeta
* @param data
* @returns {boolean} - return true on successful insertion
*/
insertMeta (data) {
if (!data || typeof data.entryAddress !== 'string' || !data.entryAddress.length) {
throw new Error('cannot insert dhtMeta without string address')
}
// get current entry at address or create empty entry
const entry = this._getEntry(data.entryAddress)
const strData = JSON.stringify(data)
// create hash of metadata
const hash = getHash(strData)
// add meta to entry's meta Map
if (!entry.meta.has(hash)) {
entry.meta.set(hash, strData)
this._genLocHashes()
this._publishIndex(data)
return true
}
return false
}
has (address) {
const loc = getLoc(address)
if (!this._data.has(loc)) {
return false
}
const ref = this._data.get(loc)
if (ref.has(address)) {
return true
}
return false
}
get (address) {
const loc = getLoc(address)
if (!this._data.has(loc)) {
return
}
const ref = this._data.get(loc)
if (ref.has(address)) {
const entry = ref.get(address)
const out = {
entry: JSON.parse(entry.entry),
meta: []
}
for (let metaItem of entry.meta.values()) {
out.meta.push(JSON.parse(metaItem))
}
return out
}
}
toJSON () {
const out = {}
for (let sub of this._data.values()) {
for (let addr of sub.keys()) {
out[addr] = this.get(addr)
}
}
return out
}
getGossipHashHash () {
return JSON.parse(JSON.stringify(this._locHashes))
}
getGossipLocListForGossipHashHash (gossipHashHash) {
const out = []
for (let loc in gossipHashHash) {
if (loc in this._locHashes) {
if (gossipHashHash[loc] !== this._locHashes[loc]) {
out.push(loc)
}
} else {
out.push(loc)
}
}
return out
}
getGossipHashesForGossipLocList (gossipLocList) {
const out = []
for (let loc of gossipLocList) {
if (this._data.has(loc)) {
for (let addr of this._data.get(loc).keys()) {
out.push(addr)
}
}
}
return out
}
}
exports.Mem = Mem
exports.getHash = getHash