Map.js
6.3 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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/**
* @alias ProtoBuf.Map
* @expose
*/
ProtoBuf.Map = (function(ProtoBuf, Reflect) {
"use strict";
/**
* Constructs a new Map. A Map is a container that is used to implement map
* fields on message objects. It closely follows the ES6 Map API; however,
* it is distinct because we do not want to depend on external polyfills or
* on ES6 itself.
*
* @exports ProtoBuf.Map
* @param {!ProtoBuf.Reflect.Field} field Map field
* @param {Object.<string,*>=} contents Initial contents
* @constructor
*/
var Map = function(field, contents) {
if (!field.map)
throw Error("field is not a map");
/**
* The field corresponding to this map.
* @type {!ProtoBuf.Reflect.Field}
*/
this.field = field;
/**
* Element instance corresponding to key type.
* @type {!ProtoBuf.Reflect.Element}
*/
this.keyElem = new Reflect.Element(field.keyType, null, true, field.syntax);
/**
* Element instance corresponding to value type.
* @type {!ProtoBuf.Reflect.Element}
*/
this.valueElem = new Reflect.Element(field.type, field.resolvedType, false, field.syntax);
/**
* Internal map: stores mapping of (string form of key) -> (key, value)
* pair.
*
* We provide map semantics for arbitrary key types, but we build on top
* of an Object, which has only string keys. In order to avoid the need
* to convert a string key back to its native type in many situations,
* we store the native key value alongside the value. Thus, we only need
* a one-way mapping from a key type to its string form that guarantees
* uniqueness and equality (i.e., str(K1) === str(K2) if and only if K1
* === K2).
*
* @type {!Object<string, {key: *, value: *}>}
*/
this.map = {};
/**
* Returns the number of elements in the map.
*/
Object.defineProperty(this, "size", {
get: function() { return Object.keys(this.map).length; }
});
// Fill initial contents from a raw object.
if (contents) {
var keys = Object.keys(contents);
for (var i = 0; i < keys.length; i++) {
var key = this.keyElem.valueFromString(keys[i]);
var val = this.valueElem.verifyValue(contents[keys[i]]);
this.map[this.keyElem.valueToString(key)] =
{ key: key, value: val };
}
}
};
var MapPrototype = Map.prototype;
/**
* Helper: return an iterator over an array.
* @param {!Array<*>} arr the array
* @returns {!Object} an iterator
* @inner
*/
function arrayIterator(arr) {
var idx = 0;
return {
next: function() {
if (idx < arr.length)
return { done: false, value: arr[idx++] };
return { done: true };
}
}
}
/**
* Clears the map.
*/
MapPrototype.clear = function() {
this.map = {};
};
/**
* Deletes a particular key from the map.
* @returns {boolean} Whether any entry with this key was deleted.
*/
MapPrototype["delete"] = function(key) {
var keyValue = this.keyElem.valueToString(this.keyElem.verifyValue(key));
var hadKey = keyValue in this.map;
delete this.map[keyValue];
return hadKey;
};
/**
* Returns an iterator over [key, value] pairs in the map.
* @returns {Object} The iterator
*/
MapPrototype.entries = function() {
var entries = [];
var strKeys = Object.keys(this.map);
for (var i = 0, entry; i < strKeys.length; i++)
entries.push([(entry=this.map[strKeys[i]]).key, entry.value]);
return arrayIterator(entries);
};
/**
* Returns an iterator over keys in the map.
* @returns {Object} The iterator
*/
MapPrototype.keys = function() {
var keys = [];
var strKeys = Object.keys(this.map);
for (var i = 0; i < strKeys.length; i++)
keys.push(this.map[strKeys[i]].key);
return arrayIterator(keys);
};
/**
* Returns an iterator over values in the map.
* @returns {!Object} The iterator
*/
MapPrototype.values = function() {
var values = [];
var strKeys = Object.keys(this.map);
for (var i = 0; i < strKeys.length; i++)
values.push(this.map[strKeys[i]].value);
return arrayIterator(values);
};
/**
* Iterates over entries in the map, calling a function on each.
* @param {function(this:*, *, *, *)} cb The callback to invoke with value, key, and map arguments.
* @param {Object=} thisArg The `this` value for the callback
*/
MapPrototype.forEach = function(cb, thisArg) {
var strKeys = Object.keys(this.map);
for (var i = 0, entry; i < strKeys.length; i++)
cb.call(thisArg, (entry=this.map[strKeys[i]]).value, entry.key, this);
};
/**
* Sets a key in the map to the given value.
* @param {*} key The key
* @param {*} value The value
* @returns {!ProtoBuf.Map} The map instance
*/
MapPrototype.set = function(key, value) {
var keyValue = this.keyElem.verifyValue(key);
var valValue = this.valueElem.verifyValue(value);
this.map[this.keyElem.valueToString(keyValue)] =
{ key: keyValue, value: valValue };
return this;
};
/**
* Gets the value corresponding to a key in the map.
* @param {*} key The key
* @returns {*|undefined} The value, or `undefined` if key not present
*/
MapPrototype.get = function(key) {
var keyValue = this.keyElem.valueToString(this.keyElem.verifyValue(key));
if (!(keyValue in this.map))
return undefined;
return this.map[keyValue].value;
};
/**
* Determines whether the given key is present in the map.
* @param {*} key The key
* @returns {boolean} `true` if the key is present
*/
MapPrototype.has = function(key) {
var keyValue = this.keyElem.valueToString(this.keyElem.verifyValue(key));
return (keyValue in this.map);
};
return Map;
})(ProtoBuf, ProtoBuf.Reflect);