map.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  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. The callback function
  42. * arguments are references to methods in `BinaryReader` and `BinaryWriter`, as
  43. * well as constructors and reader/writer methods in submessage types if
  44. * appropriate, that are used for binary serialization and parsing.
  45. *
  46. * @template K, V
  47. *
  48. * @param {!Array<!Array<!Object>>} arr
  49. *
  50. * @param {function(this:jspb.BinaryWriter,number,K)=} opt_keyWriterFn
  51. * The method on BinaryWriter that writes type K to the stream.
  52. *
  53. * @param {function(this:jspb.BinaryReader):K=} opt_keyReaderFn
  54. * The method on BinaryReader that reads type K from the stream.
  55. *
  56. * @param {function(this:jspb.BinaryWriter,number,V)|
  57. * function(this:jspb.BinaryReader,V,?)=} opt_valueWriterFn
  58. * The method on BinaryWriter that writes type V to the stream. May be
  59. * writeMessage, in which case the second callback arg form is used.
  60. *
  61. * @param {function(this:jspb.BinaryReader):V|
  62. * function(this:jspb.BinaryReader,V,
  63. * function(V,!jspb.BinaryReader))=} opt_valueReaderFn
  64. * The method on BinaryReader that reads type V from the stream. May be
  65. * readMessage, in which case the second callback arg form is used.
  66. *
  67. * @param {?function(new:V)|function(new:V,?)=} opt_valueCtor
  68. * The constructor for type V, if type V is a message type.
  69. *
  70. * @param {?function(V,!jspb.BinaryWriter)=} opt_valueWriterCallback
  71. * The BinaryWriter serialization callback for type V, if V is a message
  72. * type.
  73. *
  74. * @param {?function(V,!jspb.BinaryReader)=} opt_valueReaderCallback
  75. * The BinaryReader parsing callback for type V, if V is a message type.
  76. *
  77. * @constructor
  78. * @struct
  79. */
  80. jspb.Map = function(
  81. arr, opt_keyWriterFn, opt_keyReaderFn, opt_valueWriterFn, opt_valueReaderFn,
  82. opt_valueCtor, opt_valueWriterCallback, opt_valueReaderCallback) {
  83. /** @const @private */
  84. this.arr_ = arr;
  85. /** @const @private */
  86. this.keyWriterFn_ = opt_keyWriterFn;
  87. /** @const @private */
  88. this.keyReaderFn_ = opt_keyReaderFn;
  89. /** @const @private */
  90. this.valueWriterFn_ = opt_valueWriterFn;
  91. /** @const @private */
  92. this.valueReaderFn_ = opt_valueReaderFn;
  93. /** @const @private */
  94. this.valueCtor_ = opt_valueCtor;
  95. /** @const @private */
  96. this.valueWriterCallback_ = opt_valueWriterCallback;
  97. /** @const @private */
  98. this.valueReaderCallback_ = opt_valueReaderCallback;
  99. /** @type {!Object<string, !jspb.Map.Entry_<K,V>>} @private */
  100. this.map_ = {};
  101. /**
  102. * Is `this.arr_ updated with respect to `this.map_`?
  103. * @type {boolean}
  104. */
  105. this.arrClean = true;
  106. if (this.arr_.length > 0) {
  107. this.loadFromArray_();
  108. }
  109. };
  110. /**
  111. * Load initial content from underlying array.
  112. * @private
  113. */
  114. jspb.Map.prototype.loadFromArray_ = function() {
  115. for (var i = 0; i < this.arr_.length; i++) {
  116. var record = this.arr_[i];
  117. var key = record[0];
  118. var value = record[1];
  119. this.map_[key.toString()] = new jspb.Map.Entry_(key, value);
  120. }
  121. this.arrClean = true;
  122. };
  123. /**
  124. * Synchronize content to underlying array, if needed, and return it.
  125. * @return {!Array<!Array<!Object>>}
  126. */
  127. jspb.Map.prototype.toArray = function() {
  128. if (this.arrClean) {
  129. if (this.valueCtor_) {
  130. // We need to recursively sync maps in submessages to their arrays.
  131. var m = this.map_;
  132. for (var p in m) {
  133. if (Object.prototype.hasOwnProperty.call(m, p)) {
  134. m[p].valueWrapper.toArray();
  135. }
  136. }
  137. }
  138. } else {
  139. // Delete all elements.
  140. this.arr_.length = 0;
  141. var strKeys = this.stringKeys_();
  142. // Output keys in deterministic (sorted) order.
  143. strKeys.sort();
  144. for (var i = 0; i < strKeys.length; i++) {
  145. var entry = this.map_[strKeys[i]];
  146. var valueWrapper = /** @type {!Object} */ (entry.valueWrapper);
  147. if (valueWrapper) {
  148. valueWrapper.toArray();
  149. }
  150. this.arr_.push([entry.key, entry.value]);
  151. }
  152. this.arrClean = true;
  153. }
  154. return this.arr_;
  155. };
  156. /**
  157. * Helper: return an iterator over an array.
  158. * @template T
  159. * @param {!Array<T>} arr the array
  160. * @return {!Iterator<T>} an iterator
  161. * @private
  162. */
  163. jspb.Map.arrayIterator_ = function(arr) {
  164. var idx = 0;
  165. return /** @type {!Iterator} */ ({
  166. next: function() {
  167. if (idx < arr.length) {
  168. return { done: false, value: arr[idx++] };
  169. } else {
  170. return { done: true };
  171. }
  172. }
  173. });
  174. };
  175. /**
  176. * Returns the map's length (number of key/value pairs).
  177. * @return {number}
  178. */
  179. jspb.Map.prototype.getLength = function() {
  180. return this.stringKeys_().length;
  181. };
  182. /**
  183. * Clears the map.
  184. */
  185. jspb.Map.prototype.clear = function() {
  186. this.map_ = {};
  187. this.arrClean = false;
  188. };
  189. /**
  190. * Deletes a particular key from the map.
  191. * N.B.: differs in name from ES6 Map's `delete` because IE8 does not support
  192. * reserved words as property names.
  193. * @this {jspb.Map}
  194. * @param {K} key
  195. * @return {boolean} Whether any entry with this key was deleted.
  196. */
  197. jspb.Map.prototype.del = function(key) {
  198. var keyValue = key.toString();
  199. var hadKey = this.map_.hasOwnProperty(keyValue);
  200. delete this.map_[keyValue];
  201. this.arrClean = false;
  202. return hadKey;
  203. };
  204. /**
  205. * Returns an array of [key, value] pairs in the map.
  206. *
  207. * This is redundant compared to the plain entries() method, but we provide this
  208. * to help out Angular 1.x users. Still evaluating whether this is the best
  209. * option.
  210. *
  211. * @return {!Array<K|V>}
  212. */
  213. jspb.Map.prototype.getEntryList = function() {
  214. var entries = [];
  215. var strKeys = this.stringKeys_();
  216. strKeys.sort();
  217. for (var i = 0; i < strKeys.length; i++) {
  218. var entry = this.map_[strKeys[i]];
  219. entries.push([entry.key, entry.value]);
  220. }
  221. return entries;
  222. };
  223. /**
  224. * Returns an iterator over [key, value] pairs in the map.
  225. * Closure compiler sadly doesn't support tuples, ie. Iterator<[K,V]>.
  226. * @return {!Iterator<!Array<K|V>>}
  227. * The iterator
  228. */
  229. jspb.Map.prototype.entries = function() {
  230. var entries = [];
  231. var strKeys = this.stringKeys_();
  232. strKeys.sort();
  233. for (var i = 0; i < strKeys.length; i++) {
  234. var entry = this.map_[strKeys[i]];
  235. entries.push([entry.key, this.wrapEntry_(entry)]);
  236. }
  237. return jspb.Map.arrayIterator_(entries);
  238. };
  239. /**
  240. * Returns an iterator over keys in the map.
  241. * @return {!Iterator<K>} The iterator
  242. */
  243. jspb.Map.prototype.keys = function() {
  244. var keys = [];
  245. var strKeys = this.stringKeys_();
  246. strKeys.sort();
  247. for (var i = 0; i < strKeys.length; i++) {
  248. var entry = this.map_[strKeys[i]];
  249. keys.push(entry.key);
  250. }
  251. return jspb.Map.arrayIterator_(keys);
  252. };
  253. /**
  254. * Returns an iterator over values in the map.
  255. * @return {!Iterator<V>} The iterator
  256. */
  257. jspb.Map.prototype.values = function() {
  258. var values = [];
  259. var strKeys = this.stringKeys_();
  260. strKeys.sort();
  261. for (var i = 0; i < strKeys.length; i++) {
  262. var entry = this.map_[strKeys[i]];
  263. values.push(this.wrapEntry_(entry));
  264. }
  265. return jspb.Map.arrayIterator_(values);
  266. };
  267. /**
  268. * Iterates over entries in the map, calling a function on each.
  269. * @template T
  270. * @param {function(this:T, V, K, ?jspb.Map<K, V>)} cb
  271. * @param {T=} opt_thisArg
  272. */
  273. jspb.Map.prototype.forEach = function(cb, opt_thisArg) {
  274. var strKeys = this.stringKeys_();
  275. strKeys.sort();
  276. for (var i = 0; i < strKeys.length; i++) {
  277. var entry = this.map_[strKeys[i]];
  278. cb.call(opt_thisArg, this.wrapEntry_(entry), entry.key, this);
  279. }
  280. };
  281. /**
  282. * Sets a key in the map to the given value.
  283. * @param {K} key The key
  284. * @param {V} value The value
  285. * @return {!jspb.Map<K,V>}
  286. */
  287. jspb.Map.prototype.set = function(key, value) {
  288. var entry = new jspb.Map.Entry_(key);
  289. if (this.valueCtor_) {
  290. entry.valueWrapper = value;
  291. // .toArray() on a message returns a reference to the underlying array
  292. // rather than a copy.
  293. entry.value = value.toArray();
  294. } else {
  295. entry.value = value;
  296. }
  297. this.map_[key.toString()] = entry;
  298. this.arrClean = false;
  299. return this;
  300. };
  301. /**
  302. * Helper: lazily construct a wrapper around an entry, if needed, and return the
  303. * user-visible type.
  304. * @param {!jspb.Map.Entry_<K,V>} entry
  305. * @return {V}
  306. * @private
  307. */
  308. jspb.Map.prototype.wrapEntry_ = function(entry) {
  309. if (this.valueCtor_) {
  310. if (!entry.valueWrapper) {
  311. entry.valueWrapper = new this.valueCtor_(entry.value);
  312. }
  313. return /** @type {V} */ (entry.valueWrapper);
  314. } else {
  315. return entry.value;
  316. }
  317. };
  318. /**
  319. * Gets the value corresponding to a key in the map.
  320. * @param {K} key
  321. * @return {V|undefined} The value, or `undefined` if key not present
  322. */
  323. jspb.Map.prototype.get = function(key) {
  324. var keyValue = key.toString();
  325. var entry = this.map_[keyValue];
  326. if (entry) {
  327. return this.wrapEntry_(entry);
  328. } else {
  329. return undefined;
  330. }
  331. };
  332. /**
  333. * Determines whether the given key is present in the map.
  334. * @param {K} key
  335. * @return {boolean} `true` if the key is present
  336. */
  337. jspb.Map.prototype.has = function(key) {
  338. var keyValue = key.toString();
  339. return (keyValue in this.map_);
  340. };
  341. /**
  342. * Write this Map field in wire format to a BinaryWriter, using the given field
  343. * number.
  344. * @param {number} fieldNumber
  345. * @param {!jspb.BinaryWriter} writer
  346. */
  347. jspb.Map.prototype.serializeBinary = function(fieldNumber, writer) {
  348. var strKeys = this.stringKeys_();
  349. strKeys.sort();
  350. for (var i = 0; i < strKeys.length; i++) {
  351. var entry = this.map_[strKeys[i]];
  352. writer.beginSubMessage(fieldNumber);
  353. this.keyWriterFn_.call(writer, 1, entry.key);
  354. if (this.valueCtor_) {
  355. this.valueWriterFn_.call(writer, 2, this.wrapEntry_(entry),
  356. this.valueWriterCallback_);
  357. } else {
  358. this.valueWriterFn_.call(writer, 2, entry.value);
  359. }
  360. writer.endSubMessage();
  361. }
  362. };
  363. /**
  364. * Read one key/value message from the given BinaryReader. Compatible as the
  365. * `reader` callback parameter to jspb.BinaryReader.readMessage, to be called
  366. * when a key/value pair submessage is encountered.
  367. * @param {!jspb.Map} map
  368. * @param {!jspb.BinaryReader} reader
  369. */
  370. jspb.Map.deserializeBinary = function(map, reader) {
  371. var key = undefined;
  372. var value = undefined;
  373. while (reader.nextField()) {
  374. if (reader.isEndGroup()) {
  375. break;
  376. }
  377. var field = reader.getFieldNumber();
  378. if (field == 1) {
  379. // Key.
  380. key = map.keyReaderFn_.call(reader);
  381. } else if (field == 2) {
  382. // Value.
  383. if (map.valueCtor_) {
  384. value = new map.valueCtor_();
  385. map.valueReaderFn_.call(reader, value, map.valueReaderCallback_);
  386. } else {
  387. value = map.valueReaderFn_.call(reader);
  388. }
  389. }
  390. }
  391. goog.asserts.assert(key != undefined);
  392. goog.asserts.assert(value != undefined);
  393. map.set(key, value);
  394. };
  395. /**
  396. * Helper: compute the list of all stringified keys in the underlying Object
  397. * map.
  398. * @return {!Array<string>}
  399. * @private
  400. */
  401. jspb.Map.prototype.stringKeys_ = function() {
  402. var m = this.map_;
  403. var ret = [];
  404. for (var p in m) {
  405. if (Object.prototype.hasOwnProperty.call(m, p)) {
  406. ret.push(p);
  407. }
  408. }
  409. return ret;
  410. };
  411. /**
  412. * @param {!K} key The entry's key.
  413. * @param {V=} opt_value The entry's value wrapper.
  414. * @constructor
  415. * @struct
  416. * @template K, V
  417. * @private
  418. */
  419. jspb.Map.Entry_ = function(key, opt_value) {
  420. /** @const {K} */
  421. this.key = key;
  422. // The JSPB-serializable value. For primitive types this will be of type V.
  423. // For message types it will be an array.
  424. /** @type {V} */
  425. this.value = opt_value;
  426. // Only used for submessage values.
  427. /** @type {V} */
  428. this.valueWrapper = undefined;
  429. };