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