map.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. // Protocol Buffers - Google's data interchange format
  2. // Copyright 2008 Google Inc. All rights reserved.
  3. // https://developers.google.com/protocol-buffers/
  4. //
  5. // Redistribution and use in source and binary forms, with or without
  6. // modification, are permitted provided that the following conditions are
  7. // met:
  8. //
  9. // * Redistributions of source code must retain the above copyright
  10. // notice, this list of conditions and the following disclaimer.
  11. // * Redistributions in binary form must reproduce the above
  12. // copyright notice, this list of conditions and the following disclaimer
  13. // in the documentation and/or other materials provided with the
  14. // distribution.
  15. // * Neither the name of Google Inc. nor the names of its
  16. // contributors may be used to endorse or promote products derived from
  17. // this software without specific prior written permission.
  18. //
  19. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  20. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  21. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  22. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  23. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  24. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  25. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  26. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  27. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  28. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  29. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30. goog.provide('jspb.Map');
  31. goog.require('goog.asserts');
  32. goog.forwardDeclare('jspb.BinaryReader');
  33. goog.forwardDeclare('jspb.BinaryWriter');
  34. /**
  35. * Constructs a new Map. A Map is a container that is used to implement map
  36. * fields on message objects. It closely follows the ES6 Map API; however,
  37. * it is distinct because we do not want to depend on external polyfills or
  38. * on ES6 itself.
  39. *
  40. * This constructor should only be called from generated message code. It is not
  41. * intended for general use by library consumers.
  42. *
  43. * @template K, V
  44. *
  45. * @param {!Array<!Array<!Object>>} arr
  46. *
  47. * @param {?function(new:V)|function(new:V,?)=} opt_valueCtor
  48. * The constructor for type V, if type V is a message type.
  49. *
  50. * @constructor
  51. * @struct
  52. */
  53. jspb.Map = function(arr, opt_valueCtor) {
  54. /** @const @private */
  55. this.arr_ = arr;
  56. /** @const @private */
  57. this.valueCtor_ = opt_valueCtor;
  58. /** @type {!Object<string, !jspb.Map.Entry_<K,V>>} @private */
  59. this.map_ = {};
  60. /**
  61. * Is `this.arr_ updated with respect to `this.map_`?
  62. * @type {boolean}
  63. */
  64. this.arrClean = true;
  65. if (this.arr_.length > 0) {
  66. this.loadFromArray_();
  67. }
  68. };
  69. /**
  70. * Load initial content from underlying array.
  71. * @private
  72. */
  73. jspb.Map.prototype.loadFromArray_ = function() {
  74. for (var i = 0; i < this.arr_.length; i++) {
  75. var record = this.arr_[i];
  76. var key = record[0];
  77. var value = record[1];
  78. this.map_[key.toString()] = new jspb.Map.Entry_(key, value);
  79. }
  80. this.arrClean = true;
  81. };
  82. /**
  83. * Synchronize content to underlying array, if needed, and return it.
  84. * @return {!Array<!Array<!Object>>}
  85. */
  86. jspb.Map.prototype.toArray = function() {
  87. if (this.arrClean) {
  88. if (this.valueCtor_) {
  89. // We need to recursively sync maps in submessages to their arrays.
  90. var m = this.map_;
  91. for (var p in m) {
  92. if (Object.prototype.hasOwnProperty.call(m, p)) {
  93. var valueWrapper = /** @type {?jspb.Message} */ (m[p].valueWrapper);
  94. if (valueWrapper) {
  95. valueWrapper.toArray();
  96. }
  97. }
  98. }
  99. }
  100. } else {
  101. // Delete all elements.
  102. this.arr_.length = 0;
  103. var strKeys = this.stringKeys_();
  104. // Output keys in deterministic (sorted) order.
  105. strKeys.sort();
  106. for (var i = 0; i < strKeys.length; i++) {
  107. var entry = this.map_[strKeys[i]];
  108. var valueWrapper = /** @type {!Object} */ (entry.valueWrapper);
  109. if (valueWrapper) {
  110. valueWrapper.toArray();
  111. }
  112. this.arr_.push([entry.key, entry.value]);
  113. }
  114. this.arrClean = true;
  115. }
  116. return this.arr_;
  117. };
  118. /**
  119. * Helper: return an iterator over an array.
  120. * @template T
  121. * @param {!Array<T>} arr the array
  122. * @return {!Iterator<T>} an iterator
  123. * @private
  124. */
  125. jspb.Map.arrayIterator_ = function(arr) {
  126. var idx = 0;
  127. return /** @type {!Iterator} */ ({
  128. next: function() {
  129. if (idx < arr.length) {
  130. return { done: false, value: arr[idx++] };
  131. } else {
  132. return { done: true };
  133. }
  134. }
  135. });
  136. };
  137. /**
  138. * Returns the map's length (number of key/value pairs).
  139. * @return {number}
  140. */
  141. jspb.Map.prototype.getLength = function() {
  142. return this.stringKeys_().length;
  143. };
  144. /**
  145. * Clears the map.
  146. */
  147. jspb.Map.prototype.clear = function() {
  148. this.map_ = {};
  149. this.arrClean = false;
  150. };
  151. /**
  152. * Deletes a particular key from the map.
  153. * N.B.: differs in name from ES6 Map's `delete` because IE8 does not support
  154. * reserved words as property names.
  155. * @this {jspb.Map}
  156. * @param {K} key
  157. * @return {boolean} Whether any entry with this key was deleted.
  158. */
  159. jspb.Map.prototype.del = function(key) {
  160. var keyValue = key.toString();
  161. var hadKey = this.map_.hasOwnProperty(keyValue);
  162. delete this.map_[keyValue];
  163. this.arrClean = false;
  164. return hadKey;
  165. };
  166. /**
  167. * Returns an array of [key, value] pairs in the map.
  168. *
  169. * This is redundant compared to the plain entries() method, but we provide this
  170. * to help out Angular 1.x users. Still evaluating whether this is the best
  171. * option.
  172. *
  173. * @return {!Array<K|V>}
  174. */
  175. jspb.Map.prototype.getEntryList = function() {
  176. var entries = [];
  177. var strKeys = this.stringKeys_();
  178. strKeys.sort();
  179. for (var i = 0; i < strKeys.length; i++) {
  180. var entry = this.map_[strKeys[i]];
  181. entries.push([entry.key, entry.value]);
  182. }
  183. return entries;
  184. };
  185. /**
  186. * Returns an iterator over [key, value] pairs in the map.
  187. * Closure compiler sadly doesn't support tuples, ie. Iterator<[K,V]>.
  188. * @return {!Iterator<!Array<K|V>>}
  189. * The iterator
  190. */
  191. jspb.Map.prototype.entries = function() {
  192. var entries = [];
  193. var strKeys = this.stringKeys_();
  194. strKeys.sort();
  195. for (var i = 0; i < strKeys.length; i++) {
  196. var entry = this.map_[strKeys[i]];
  197. entries.push([entry.key, this.wrapEntry_(entry)]);
  198. }
  199. return jspb.Map.arrayIterator_(entries);
  200. };
  201. /**
  202. * Returns an iterator over keys in the map.
  203. * @return {!Iterator<K>} The iterator
  204. */
  205. jspb.Map.prototype.keys = function() {
  206. var keys = [];
  207. var strKeys = this.stringKeys_();
  208. strKeys.sort();
  209. for (var i = 0; i < strKeys.length; i++) {
  210. var entry = this.map_[strKeys[i]];
  211. keys.push(entry.key);
  212. }
  213. return jspb.Map.arrayIterator_(keys);
  214. };
  215. /**
  216. * Returns an iterator over values in the map.
  217. * @return {!Iterator<V>} The iterator
  218. */
  219. jspb.Map.prototype.values = function() {
  220. var values = [];
  221. var strKeys = this.stringKeys_();
  222. strKeys.sort();
  223. for (var i = 0; i < strKeys.length; i++) {
  224. var entry = this.map_[strKeys[i]];
  225. values.push(this.wrapEntry_(entry));
  226. }
  227. return jspb.Map.arrayIterator_(values);
  228. };
  229. /**
  230. * Iterates over entries in the map, calling a function on each.
  231. * @template T
  232. * @param {function(this:T, V, K, ?jspb.Map<K, V>)} cb
  233. * @param {T=} opt_thisArg
  234. */
  235. jspb.Map.prototype.forEach = function(cb, opt_thisArg) {
  236. var strKeys = this.stringKeys_();
  237. strKeys.sort();
  238. for (var i = 0; i < strKeys.length; i++) {
  239. var entry = this.map_[strKeys[i]];
  240. cb.call(opt_thisArg, this.wrapEntry_(entry), entry.key, this);
  241. }
  242. };
  243. /**
  244. * Sets a key in the map to the given value.
  245. * @param {K} key The key
  246. * @param {V} value The value
  247. * @return {!jspb.Map<K,V>}
  248. */
  249. jspb.Map.prototype.set = function(key, value) {
  250. var entry = new jspb.Map.Entry_(key);
  251. if (this.valueCtor_) {
  252. entry.valueWrapper = value;
  253. // .toArray() on a message returns a reference to the underlying array
  254. // rather than a copy.
  255. entry.value = value.toArray();
  256. } else {
  257. entry.value = value;
  258. }
  259. this.map_[key.toString()] = entry;
  260. this.arrClean = false;
  261. return this;
  262. };
  263. /**
  264. * Helper: lazily construct a wrapper around an entry, if needed, and return the
  265. * user-visible type.
  266. * @param {!jspb.Map.Entry_<K,V>} entry
  267. * @return {V}
  268. * @private
  269. */
  270. jspb.Map.prototype.wrapEntry_ = function(entry) {
  271. if (this.valueCtor_) {
  272. if (!entry.valueWrapper) {
  273. entry.valueWrapper = new this.valueCtor_(entry.value);
  274. }
  275. return /** @type {V} */ (entry.valueWrapper);
  276. } else {
  277. return entry.value;
  278. }
  279. };
  280. /**
  281. * Gets the value corresponding to a key in the map.
  282. * @param {K} key
  283. * @return {V|undefined} The value, or `undefined` if key not present
  284. */
  285. jspb.Map.prototype.get = function(key) {
  286. var keyValue = key.toString();
  287. var entry = this.map_[keyValue];
  288. if (entry) {
  289. return this.wrapEntry_(entry);
  290. } else {
  291. return undefined;
  292. }
  293. };
  294. /**
  295. * Determines whether the given key is present in the map.
  296. * @param {K} key
  297. * @return {boolean} `true` if the key is present
  298. */
  299. jspb.Map.prototype.has = function(key) {
  300. var keyValue = key.toString();
  301. return (keyValue in this.map_);
  302. };
  303. /**
  304. * Write this Map field in wire format to a BinaryWriter, using the given field
  305. * number.
  306. * @param {number} fieldNumber
  307. * @param {!jspb.BinaryWriter} writer
  308. * @param {!function(this:jspb.BinaryWriter,number,K)} keyWriterFn
  309. * The method on BinaryWriter that writes type K to the stream.
  310. * @param {!function(this:jspb.BinaryWriter,number,V)|
  311. * function(this:jspb.BinaryReader,V,?)} valueWriterFn
  312. * The method on BinaryWriter that writes type V to the stream. May be
  313. * writeMessage, in which case the second callback arg form is used.
  314. * @param {function(V,!jspb.BinaryWriter)=} opt_valueWriterCallback
  315. * The BinaryWriter serialization callback for type V, if V is a message
  316. * type.
  317. */
  318. jspb.Map.prototype.serializeBinary = function(
  319. fieldNumber, writer, keyWriterFn, valueWriterFn, opt_valueWriterCallback) {
  320. var strKeys = this.stringKeys_();
  321. strKeys.sort();
  322. for (var i = 0; i < strKeys.length; i++) {
  323. var entry = this.map_[strKeys[i]];
  324. writer.beginSubMessage(fieldNumber);
  325. keyWriterFn.call(writer, 1, entry.key);
  326. if (this.valueCtor_) {
  327. valueWriterFn.call(writer, 2, this.wrapEntry_(entry),
  328. opt_valueWriterCallback);
  329. } else {
  330. valueWriterFn.call(writer, 2, entry.value);
  331. }
  332. writer.endSubMessage();
  333. }
  334. };
  335. /**
  336. * Read one key/value message from the given BinaryReader. Compatible as the
  337. * `reader` callback parameter to jspb.BinaryReader.readMessage, to be called
  338. * when a key/value pair submessage is encountered.
  339. * @template K, V
  340. * @param {!jspb.Map} map
  341. * @param {!jspb.BinaryReader} reader
  342. * @param {!function(this:jspb.BinaryReader):K} keyReaderFn
  343. * The method on BinaryReader that reads type K from the stream.
  344. *
  345. * @param {!function(this:jspb.BinaryReader):V|
  346. * function(this:jspb.BinaryReader,V,
  347. * function(V,!jspb.BinaryReader))} valueReaderFn
  348. * The method on BinaryReader that reads type V from the stream. May be
  349. * readMessage, in which case the second callback arg form is used.
  350. *
  351. * @param {?function(V,!jspb.BinaryReader)=} opt_valueReaderCallback
  352. * The BinaryReader parsing callback for type V, if V is a message type.
  353. *
  354. */
  355. jspb.Map.deserializeBinary = function(map, reader, keyReaderFn, valueReaderFn,
  356. opt_valueReaderCallback) {
  357. var key = undefined;
  358. var value = undefined;
  359. while (reader.nextField()) {
  360. if (reader.isEndGroup()) {
  361. break;
  362. }
  363. var field = reader.getFieldNumber();
  364. if (field == 1) {
  365. // Key.
  366. key = keyReaderFn.call(reader);
  367. } else if (field == 2) {
  368. // Value.
  369. if (map.valueCtor_) {
  370. value = new map.valueCtor_();
  371. valueReaderFn.call(reader, value, opt_valueReaderCallback);
  372. } else {
  373. value = valueReaderFn.call(reader);
  374. }
  375. }
  376. }
  377. goog.asserts.assert(key != undefined);
  378. goog.asserts.assert(value != undefined);
  379. map.set(key, value);
  380. };
  381. /**
  382. * Helper: compute the list of all stringified keys in the underlying Object
  383. * map.
  384. * @return {!Array<string>}
  385. * @private
  386. */
  387. jspb.Map.prototype.stringKeys_ = function() {
  388. var m = this.map_;
  389. var ret = [];
  390. for (var p in m) {
  391. if (Object.prototype.hasOwnProperty.call(m, p)) {
  392. ret.push(p);
  393. }
  394. }
  395. return ret;
  396. };
  397. /**
  398. * @param {!K} key The entry's key.
  399. * @param {V=} opt_value The entry's value wrapper.
  400. * @constructor
  401. * @struct
  402. * @template K, V
  403. * @private
  404. */
  405. jspb.Map.Entry_ = function(key, opt_value) {
  406. /** @const {K} */
  407. this.key = key;
  408. // The JSPB-serializable value. For primitive types this will be of type V.
  409. // For message types it will be an array.
  410. /** @type {V} */
  411. this.value = opt_value;
  412. // Only used for submessage values.
  413. /** @type {V} */
  414. this.valueWrapper = undefined;
  415. };