-
Notifications
You must be signed in to change notification settings - Fork 453
Expand file tree
/
Copy pathmap.js
More file actions
182 lines (168 loc) · 5.06 KB
/
map.js
File metadata and controls
182 lines (168 loc) · 5.06 KB
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
'use strict'
const {Buffer} = require('buffer')
const encoder = require('./encoder')
const decoder = require('./decoder')
const {MT} = require('./constants')
/**
* Wrapper around a JavaScript Map object that allows the keys to be
* any complex type. The base Map object allows this, but will only
* compare the keys by identity, not by value. CborMap translates keys
* to CBOR first (and base64's them to ensure by-value comparison).
*
* This is not a subclass of Object, because it would be tough to get
* the semantics to be an exact match.
*
* @extends Map
*/
class CborMap extends Map {
/**
* Creates an instance of CborMap.
*
* @param {Iterable<any>} [iterable] An Array or other iterable
* object whose elements are key-value pairs (arrays with two elements, e.g.
* <code>[[ 1, 'one' ],[ 2, 'two' ]]</code>). Each key-value pair is added
* to the new CborMap; null values are treated as undefined.
*/
constructor(iterable) {
super(iterable)
}
/**
* @ignore
*/
static _encode(key) {
return encoder.encodeCanonical(key).toString('base64')
}
/**
* @ignore
*/
static _decode(key) {
return decoder.decodeFirstSync(key, 'base64')
}
/**
* Retrieve a specified element.
*
* @param {any} key The key identifying the element to retrieve.
* Can be any type, which will be serialized into CBOR and compared by
* value.
* @returns {any} The element if it exists, or <code>undefined</code>.
*/
get(key) {
return super.get(CborMap._encode(key))
}
/**
* Adds or updates an element with a specified key and value.
*
* @param {any} key The key identifying the element to store.
* Can be any type, which will be serialized into CBOR and compared by
* value.
* @param {any} val The element to store.
* @returns {this} This object.
*/
set(key, val) {
return super.set(CborMap._encode(key), val)
}
/**
* Removes the specified element.
*
* @param {any} key The key identifying the element to delete. Can be any
* type, which will be serialized into CBOR and compared by value.
* @returns {boolean} True if an element in the Map object existed and has
* been removed, or false if the element does not exist.
*/
delete(key) {
return super.delete(CborMap._encode(key))
}
/**
* Does an element with the specified key exist?
*
* @param {any} key The key identifying the element to check.
* Can be any type, which will be serialized into CBOR and compared by
* value.
* @returns {boolean} True if an element with the specified key exists in
* the Map object; otherwise false.
*/
has(key) {
return super.has(CborMap._encode(key))
}
/**
* Returns a new Iterator object that contains the keys for each element
* in the Map object in insertion order. The keys are decoded into their
* original format.
*
* @yields {any} The keys of the map.
*/
*keys() {
for (const k of super.keys()) {
yield CborMap._decode(k)
}
}
/* eslint-disable jsdoc/require-returns-check */
/**
* Returns a new Iterator object that contains the [key, value] pairs for
* each element in the Map object in insertion order.
*
* @yields {any[]} Key value pairs.
* @returns {IterableIterator<any, any>} Key value pairs.
*/
*entries() {
for (const kv of super.entries()) {
yield [CborMap._decode(kv[0]), kv[1]]
}
}
/* eslint-enable jsdoc/require-returns-check */
/**
* Returns a new Iterator object that contains the [key, value] pairs for
* each element in the Map object in insertion order.
*
* @returns {IterableIterator} Key value pairs.
*/
[Symbol.iterator]() {
return this.entries()
}
/**
* Executes a provided function once per each key/value pair in the Map
* object, in insertion order.
*
* @param {function(any, any, Map): undefined} fun Function to execute for
* each element, which takes a value, a key, and the Map being traversed.
* @param {any} thisArg Value to use as this when executing callback.
* @throws {TypeError} Invalid function.
*/
forEach(fun, thisArg) {
if (typeof fun !== 'function') {
throw new TypeError('Must be function')
}
for (const kv of super.entries()) {
fun.call(this, kv[1], CborMap._decode(kv[0]), this)
}
}
/**
* Push the simple value onto the CBOR stream.
*
* @param {object} gen The generator to push onto.
* @returns {boolean} True on success.
*/
encodeCBOR(gen) {
if (!gen._pushInt(this.size, MT.MAP)) {
return false
}
if (gen.canonical) {
const entries = Array.from(super.entries())
.map(kv => [Buffer.from(kv[0], 'base64'), kv[1]])
entries.sort((a, b) => a[0].compare(b[0]))
for (const kv of entries) {
if (!(gen.push(kv[0]) && gen.pushAny(kv[1]))) {
return false
}
}
} else {
for (const kv of super.entries()) {
if (!(gen.push(Buffer.from(kv[0], 'base64')) && gen.pushAny(kv[1]))) {
return false
}
}
}
return true
}
}
module.exports = CborMap