storage.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. goog.module('protobuf.binary.Storage');
  2. const {checkDefAndNotNull} = goog.require('protobuf.internal.checks');
  3. /**
  4. * 85% of the proto fields have a field number <= 24:
  5. * https://plx.corp.google.com/scripts2/script_5d._f02af6_0000_23b1_a15f_001a1139dd02
  6. *
  7. * @type {number}
  8. */
  9. // LINT.IfChange
  10. const DEFAULT_PIVOT = 24;
  11. // LINT.ThenChange(//depot/google3/third_party/protobuf/javascript/runtime/kernel/storage_test.js,
  12. // //depot/google3/net/proto2/contrib/js_proto/internal/kernel_message_generator.cc)
  13. /**
  14. * Class storing all the fields of a protobuf message.
  15. *
  16. * @package
  17. * @template FieldType
  18. */
  19. class Storage {
  20. /**
  21. * @param {number=} pivot
  22. */
  23. constructor(pivot = DEFAULT_PIVOT) {
  24. /**
  25. * Fields having a field number no greater than the pivot value are stored
  26. * into an array for fast access. A field with field number X is stored into
  27. * the array position X - 1.
  28. *
  29. * @private @const {!Array<!FieldType|undefined>}
  30. */
  31. this.array_ = new Array(pivot);
  32. /**
  33. * Fields having a field number higher than the pivot value are stored into
  34. * the map. We create the map only when it's needed, since even an empty map
  35. * takes up a significant amount of memory.
  36. *
  37. * @private {?Map<number, !FieldType>}
  38. */
  39. this.map_ = null;
  40. }
  41. /**
  42. * Fields having a field number no greater than the pivot value are stored
  43. * into an array for fast access. A field with field number X is stored into
  44. * the array position X - 1.
  45. * @return {number}
  46. */
  47. getPivot() {
  48. return this.array_.length;
  49. }
  50. /**
  51. * Sets a field in the specified field number.
  52. *
  53. * @param {number} fieldNumber
  54. * @param {!FieldType} field
  55. */
  56. set(fieldNumber, field) {
  57. if (fieldNumber <= this.getPivot()) {
  58. this.array_[fieldNumber - 1] = field;
  59. } else {
  60. if (this.map_) {
  61. this.map_.set(fieldNumber, field);
  62. } else {
  63. this.map_ = new Map([[fieldNumber, field]]);
  64. }
  65. }
  66. }
  67. /**
  68. * Returns a field at the specified field number.
  69. *
  70. * @param {number} fieldNumber
  71. * @return {!FieldType|undefined}
  72. */
  73. get(fieldNumber) {
  74. if (fieldNumber <= this.getPivot()) {
  75. return this.array_[fieldNumber - 1];
  76. } else {
  77. return this.map_ ? this.map_.get(fieldNumber) : undefined;
  78. }
  79. }
  80. /**
  81. * Deletes a field from the specified field number.
  82. *
  83. * @param {number} fieldNumber
  84. */
  85. delete(fieldNumber) {
  86. if (fieldNumber <= this.getPivot()) {
  87. delete this.array_[fieldNumber - 1];
  88. } else {
  89. if (this.map_) {
  90. this.map_.delete(fieldNumber);
  91. }
  92. }
  93. }
  94. /**
  95. * Executes the provided function once for each array element.
  96. *
  97. * @param {function(!FieldType, number): void} callback
  98. */
  99. forEach(callback) {
  100. this.array_.forEach((field, fieldNumber) => {
  101. if (field) {
  102. callback(checkDefAndNotNull(field), fieldNumber + 1);
  103. }
  104. });
  105. if (this.map_) {
  106. this.map_.forEach(callback);
  107. }
  108. }
  109. /**
  110. * Creates a shallow copy of the storage.
  111. *
  112. * @return {!Storage}
  113. */
  114. shallowCopy() {
  115. const copy = new Storage(this.getPivot());
  116. this.forEach(
  117. (field, fieldNumber) =>
  118. void copy.set(fieldNumber, field.shallowCopy()));
  119. return copy;
  120. }
  121. }
  122. exports = Storage;