SmallSortedMap.java
Go to the documentation of this file.
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 
31 package com.google.protobuf;
32 
33 import java.util.AbstractMap;
34 import java.util.AbstractSet;
35 import java.util.ArrayList;
36 import java.util.Collections;
37 import java.util.Iterator;
38 import java.util.List;
39 import java.util.Map;
40 import java.util.NoSuchElementException;
41 import java.util.Set;
42 import java.util.SortedMap;
43 import java.util.TreeMap;
44 
80 // This class is final for all intents and purposes because the constructor is
81 // private. However, the FieldDescriptor-specific logic is encapsulated in
82 // a subclass to aid testability of the core logic.
83 class SmallSortedMap<K extends Comparable<K>, V> extends AbstractMap<K, V> {
84 
93  static <FieldDescriptorType extends FieldSet.FieldDescriptorLite<FieldDescriptorType>>
94  SmallSortedMap<FieldDescriptorType, Object> newFieldMap(int arraySize) {
95  return new SmallSortedMap<FieldDescriptorType, Object>(arraySize) {
96  @Override
97  @SuppressWarnings("unchecked")
98  public void makeImmutable() {
99  if (!isImmutable()) {
100  for (int i = 0; i < getNumArrayEntries(); i++) {
101  final Map.Entry<FieldDescriptorType, Object> entry = getArrayEntryAt(i);
102  if (entry.getKey().isRepeated()) {
103  final List value = (List) entry.getValue();
104  entry.setValue(Collections.unmodifiableList(value));
105  }
106  }
107  for (Map.Entry<FieldDescriptorType, Object> entry : getOverflowEntries()) {
108  if (entry.getKey().isRepeated()) {
109  final List value = (List) entry.getValue();
110  entry.setValue(Collections.unmodifiableList(value));
111  }
112  }
113  }
114  super.makeImmutable();
115  }
116  };
117  }
118 
125  static <K extends Comparable<K>, V> SmallSortedMap<K, V> newInstanceForTest(int arraySize) {
126  return new SmallSortedMap<K, V>(arraySize);
127  }
128 
129  private final int maxArraySize;
130  // The "entry array" is actually a List because generic arrays are not
131  // allowed. ArrayList also nicely handles the entry shifting on inserts and
132  // removes.
133  private List<Entry> entryList;
134  private Map<K, V> overflowEntries;
135  private boolean isImmutable;
136  // The EntrySet is a stateless view of the Map. It's initialized the first
137  // time it is requested and reused henceforth.
138  private volatile EntrySet lazyEntrySet;
139  private Map<K, V> overflowEntriesDescending;
140  private volatile DescendingEntrySet lazyDescendingEntrySet;
141 
146  private SmallSortedMap(int arraySize) {
147  this.maxArraySize = arraySize;
148  this.entryList = Collections.emptyList();
149  this.overflowEntries = Collections.emptyMap();
150  this.overflowEntriesDescending = Collections.emptyMap();
151  }
152 
154  public void makeImmutable() {
155  if (!isImmutable) {
156  // Note: There's no need to wrap the entryList in an unmodifiableList
157  // because none of the list's accessors are exposed. The iterator() of
158  // overflowEntries, on the other hand, is exposed so it must be made
159  // unmodifiable.
160  overflowEntries =
161  overflowEntries.isEmpty()
162  ? Collections.<K, V>emptyMap()
163  : Collections.unmodifiableMap(overflowEntries);
164  overflowEntriesDescending =
165  overflowEntriesDescending.isEmpty()
166  ? Collections.<K, V>emptyMap()
167  : Collections.unmodifiableMap(overflowEntriesDescending);
168  isImmutable = true;
169  }
170  }
171 
173  public boolean isImmutable() {
174  return isImmutable;
175  }
176 
178  public int getNumArrayEntries() {
179  return entryList.size();
180  }
181 
183  public Map.Entry<K, V> getArrayEntryAt(int index) {
184  return entryList.get(index);
185  }
186 
188  public int getNumOverflowEntries() {
189  return overflowEntries.size();
190  }
191 
193  public Iterable<Map.Entry<K, V>> getOverflowEntries() {
194  return overflowEntries.isEmpty()
195  ? EmptySet.<Map.Entry<K, V>>iterable()
196  : overflowEntries.entrySet();
197  }
198 
199  Iterable<Map.Entry<K, V>> getOverflowEntriesDescending() {
200  return overflowEntriesDescending.isEmpty()
201  ? EmptySet.<Map.Entry<K, V>>iterable()
202  : overflowEntriesDescending.entrySet();
203  }
204 
205  @Override
206  public int size() {
207  return entryList.size() + overflowEntries.size();
208  }
209 
215  @Override
216  public boolean containsKey(Object o) {
217  @SuppressWarnings("unchecked")
218  final K key = (K) o;
219  return binarySearchInArray(key) >= 0 || overflowEntries.containsKey(key);
220  }
221 
227  @Override
228  public V get(Object o) {
229  @SuppressWarnings("unchecked")
230  final K key = (K) o;
231  final int index = binarySearchInArray(key);
232  if (index >= 0) {
233  return entryList.get(index).getValue();
234  }
235  return overflowEntries.get(key);
236  }
237 
238  @Override
239  public V put(K key, V value) {
240  checkMutable();
241  final int index = binarySearchInArray(key);
242  if (index >= 0) {
243  // Replace existing array entry.
244  return entryList.get(index).setValue(value);
245  }
246  ensureEntryArrayMutable();
247  final int insertionPoint = -(index + 1);
248  if (insertionPoint >= maxArraySize) {
249  // Put directly in overflow.
250  return getOverflowEntriesMutable().put(key, value);
251  }
252  // Insert new Entry in array.
253  if (entryList.size() == maxArraySize) {
254  // Shift the last array entry into overflow.
255  final Entry lastEntryInArray = entryList.remove(maxArraySize - 1);
256  getOverflowEntriesMutable().put(lastEntryInArray.getKey(), lastEntryInArray.getValue());
257  }
258  entryList.add(insertionPoint, new Entry(key, value));
259  return null;
260  }
261 
262  @Override
263  public void clear() {
264  checkMutable();
265  if (!entryList.isEmpty()) {
266  entryList.clear();
267  }
268  if (!overflowEntries.isEmpty()) {
269  overflowEntries.clear();
270  }
271  }
272 
278  @Override
279  public V remove(Object o) {
280  checkMutable();
281  @SuppressWarnings("unchecked")
282  final K key = (K) o;
283  final int index = binarySearchInArray(key);
284  if (index >= 0) {
285  return removeArrayEntryAt(index);
286  }
287  // overflowEntries might be Collections.unmodifiableMap(), so only
288  // call remove() if it is non-empty.
289  if (overflowEntries.isEmpty()) {
290  return null;
291  } else {
292  return overflowEntries.remove(key);
293  }
294  }
295 
296  private V removeArrayEntryAt(int index) {
297  checkMutable();
298  final V removed = entryList.remove(index).getValue();
299  if (!overflowEntries.isEmpty()) {
300  // Shift the first entry in the overflow to be the last entry in the
301  // array.
302  final Iterator<Map.Entry<K, V>> iterator = getOverflowEntriesMutable().entrySet().iterator();
303  entryList.add(new Entry(iterator.next()));
304  iterator.remove();
305  }
306  return removed;
307  }
308 
314  private int binarySearchInArray(K key) {
315  int left = 0;
316  int right = entryList.size() - 1;
317 
318  // Optimization: For the common case in which entries are added in
319  // ascending tag order, check the largest element in the array before
320  // doing a full binary search.
321  if (right >= 0) {
322  int cmp = key.compareTo(entryList.get(right).getKey());
323  if (cmp > 0) {
324  return -(right + 2); // Insert point is after "right".
325  } else if (cmp == 0) {
326  return right;
327  }
328  }
329 
330  while (left <= right) {
331  int mid = (left + right) / 2;
332  int cmp = key.compareTo(entryList.get(mid).getKey());
333  if (cmp < 0) {
334  right = mid - 1;
335  } else if (cmp > 0) {
336  left = mid + 1;
337  } else {
338  return mid;
339  }
340  }
341  return -(left + 1);
342  }
343 
351  @Override
352  public Set<Map.Entry<K, V>> entrySet() {
353  if (lazyEntrySet == null) {
354  lazyEntrySet = new EntrySet();
355  }
356  return lazyEntrySet;
357  }
358 
359  Set<Map.Entry<K, V>> descendingEntrySet() {
360  if (lazyDescendingEntrySet == null) {
361  lazyDescendingEntrySet = new DescendingEntrySet();
362  }
363  return lazyDescendingEntrySet;
364  }
365 
367  private void checkMutable() {
368  if (isImmutable) {
369  throw new UnsupportedOperationException();
370  }
371  }
372 
377  @SuppressWarnings("unchecked")
378  private SortedMap<K, V> getOverflowEntriesMutable() {
379  checkMutable();
380  if (overflowEntries.isEmpty() && !(overflowEntries instanceof TreeMap)) {
381  overflowEntries = new TreeMap<K, V>();
382  overflowEntriesDescending = ((TreeMap<K, V>) overflowEntries).descendingMap();
383  }
384  return (SortedMap<K, V>) overflowEntries;
385  }
386 
388  private void ensureEntryArrayMutable() {
389  checkMutable();
390  if (entryList.isEmpty() && !(entryList instanceof ArrayList)) {
391  entryList = new ArrayList<Entry>(maxArraySize);
392  }
393  }
394 
399  private class Entry implements Map.Entry<K, V>, Comparable<Entry> {
400 
401  private final K key;
402  private V value;
403 
404  Entry(Map.Entry<K, V> copy) {
405  this(copy.getKey(), copy.getValue());
406  }
407 
408  Entry(K key, V value) {
409  this.key = key;
410  this.value = value;
411  }
412 
413  @Override
414  public K getKey() {
415  return key;
416  }
417 
418  @Override
419  public V getValue() {
420  return value;
421  }
422 
423  @Override
424  public int compareTo(Entry other) {
425  return getKey().compareTo(other.getKey());
426  }
427 
428  @Override
429  public V setValue(V newValue) {
430  checkMutable();
431  final V oldValue = this.value;
432  this.value = newValue;
433  return oldValue;
434  }
435 
436  @Override
437  public boolean equals(Object o) {
438  if (o == this) {
439  return true;
440  }
441  if (!(o instanceof Map.Entry)) {
442  return false;
443  }
444  @SuppressWarnings("unchecked")
445  Map.Entry<?, ?> other = (Map.Entry<?, ?>) o;
446  return equals(key, other.getKey()) && equals(value, other.getValue());
447  }
448 
449  @Override
450  public int hashCode() {
451  return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode());
452  }
453 
454  @Override
455  public String toString() {
456  return key + "=" + value;
457  }
458 
460  private boolean equals(Object o1, Object o2) {
461  return o1 == null ? o2 == null : o1.equals(o2);
462  }
463  }
464 
466  private class EntrySet extends AbstractSet<Map.Entry<K, V>> {
467 
468  @Override
469  public Iterator<Map.Entry<K, V>> iterator() {
470  return new EntryIterator();
471  }
472 
473  @Override
474  public int size() {
475  return SmallSortedMap.this.size();
476  }
477 
483  @Override
484  public boolean contains(Object o) {
485  @SuppressWarnings("unchecked")
486  final Map.Entry<K, V> entry = (Map.Entry<K, V>) o;
487  final V existing = get(entry.getKey());
488  final V value = entry.getValue();
489  return existing == value || (existing != null && existing.equals(value));
490  }
491 
492  @Override
493  public boolean add(Map.Entry<K, V> entry) {
494  if (!contains(entry)) {
495  put(entry.getKey(), entry.getValue());
496  return true;
497  }
498  return false;
499  }
500 
506  @Override
507  public boolean remove(Object o) {
508  @SuppressWarnings("unchecked")
509  final Map.Entry<K, V> entry = (Map.Entry<K, V>) o;
510  if (contains(entry)) {
511  SmallSortedMap.this.remove(entry.getKey());
512  return true;
513  }
514  return false;
515  }
516 
517  @Override
518  public void clear() {
519  SmallSortedMap.this.clear();
520  }
521  }
522 
523  private class DescendingEntrySet extends EntrySet {
524  @Override
525  public Iterator<java.util.Map.Entry<K, V>> iterator() {
526  return new DescendingEntryIterator();
527  }
528  }
529 
534  private class EntryIterator implements Iterator<Map.Entry<K, V>> {
535 
536  private int pos = -1;
537  private boolean nextCalledBeforeRemove;
538  private Iterator<Map.Entry<K, V>> lazyOverflowIterator;
539 
540  @Override
541  public boolean hasNext() {
542  return (pos + 1) < entryList.size()
543  || (!overflowEntries.isEmpty() && getOverflowIterator().hasNext());
544  }
545 
546  @Override
547  public Map.Entry<K, V> next() {
548  nextCalledBeforeRemove = true;
549  // Always increment pos so that we know whether the last returned value
550  // was from the array or from overflow.
551  if (++pos < entryList.size()) {
552  return entryList.get(pos);
553  }
554  return getOverflowIterator().next();
555  }
556 
557  @Override
558  public void remove() {
559  if (!nextCalledBeforeRemove) {
560  throw new IllegalStateException("remove() was called before next()");
561  }
562  nextCalledBeforeRemove = false;
563  checkMutable();
564 
565  if (pos < entryList.size()) {
566  removeArrayEntryAt(pos--);
567  } else {
568  getOverflowIterator().remove();
569  }
570  }
571 
577  private Iterator<Map.Entry<K, V>> getOverflowIterator() {
578  if (lazyOverflowIterator == null) {
579  lazyOverflowIterator = overflowEntries.entrySet().iterator();
580  }
581  return lazyOverflowIterator;
582  }
583  }
584 
589  private class DescendingEntryIterator implements Iterator<Map.Entry<K, V>> {
590 
591  private int pos = entryList.size();
592  private Iterator<Map.Entry<K, V>> lazyOverflowIterator;
593 
594  @Override
595  public boolean hasNext() {
596  return (pos > 0 && pos <= entryList.size()) || getOverflowIterator().hasNext();
597  }
598 
599  @Override
600  public Map.Entry<K, V> next() {
601  if (getOverflowIterator().hasNext()) {
602  return getOverflowIterator().next();
603  }
604  return entryList.get(--pos);
605  }
606 
607  @Override
608  public void remove() {
609  throw new UnsupportedOperationException();
610  }
611 
617  private Iterator<Map.Entry<K, V>> getOverflowIterator() {
618  if (lazyOverflowIterator == null) {
619  lazyOverflowIterator = overflowEntriesDescending.entrySet().iterator();
620  }
621  return lazyOverflowIterator;
622  }
623  }
624 
630  private static class EmptySet {
631 
632  private static final Iterator<Object> ITERATOR =
633  new Iterator<Object>() {
634  @Override
635  public boolean hasNext() {
636  return false;
637  }
638 
639  @Override
640  public Object next() {
641  throw new NoSuchElementException();
642  }
643 
644  @Override
645  public void remove() {
646  throw new UnsupportedOperationException();
647  }
648  };
649 
650  private static final Iterable<Object> ITERABLE =
651  new Iterable<Object>() {
652  @Override
653  public Iterator<Object> iterator() {
654  return ITERATOR;
655  }
656  };
657 
658  @SuppressWarnings("unchecked")
659  static <T> Iterable<T> iterable() {
660  return (Iterable<T>) ITERABLE;
661  }
662  }
663 
664  @Override
665  public boolean equals(Object o) {
666  if (this == o) {
667  return true;
668  }
669 
670  if (!(o instanceof SmallSortedMap)) {
671  return super.equals(o);
672  }
673 
674  SmallSortedMap<?, ?> other = (SmallSortedMap<?, ?>) o;
675  final int size = size();
676  if (size != other.size()) {
677  return false;
678  }
679 
680  // Best effort try to avoid allocating an entry set.
681  final int numArrayEntries = getNumArrayEntries();
682  if (numArrayEntries != other.getNumArrayEntries()) {
683  return entrySet().equals(other.entrySet());
684  }
685 
686  for (int i = 0; i < numArrayEntries; i++) {
687  if (!getArrayEntryAt(i).equals(other.getArrayEntryAt(i))) {
688  return false;
689  }
690  }
691 
692  if (numArrayEntries != size) {
693  return overflowEntries.equals(other.overflowEntries);
694  }
695 
696  return true;
697  }
698 
699  @Override
700  public int hashCode() {
701  int h = 0;
702  final int listSize = getNumArrayEntries();
703  for (int i = 0; i < listSize; i++) {
704  h += entryList.get(i).hashCode();
705  }
706  // Avoid the iterator allocation if possible.
707  if (getNumOverflowEntries() > 0) {
708  h += overflowEntries.hashCode();
709  }
710  return h;
711  }
712 }
K
#define K(t)
Definition: sha1.c:43
Map
Definition: ruby/ext/google/protobuf_c/protobuf.h:442
left
GLint left
Definition: glcorearb.h:4150
if
PHP_PROTO_OBJECT_FREE_END PHP_PROTO_OBJECT_DTOR_END if(!upb_strtable_init(&intern->table, UPB_CTYPE_UINT64))
Definition: php/ext/google/protobuf/map.c:232
T
#define T(upbtypeconst, upbtype, ctype, default_value)
benchmark::o1
@ o1
Definition: benchmark.h:375
size
#define size
Definition: glcorearb.h:2944
tests.google.protobuf.internal.message_test.cmp
cmp
Definition: compatibility_tests/v2.5.0/tests/google/protobuf/internal/message_test.py:61
key
const SETUP_TEARDOWN_TESTCONTEXT char * key
Definition: test_wss_transport.cpp:10
i
int i
Definition: gmock-matchers_test.cc:764
java
size
GLsizeiptr size
Definition: glcorearb.h:2943
next
static size_t next(const upb_table *t, size_t i)
Definition: php/ext/google/protobuf/upb.c:4889
value
GLsizei const GLfloat * value
Definition: glcorearb.h:3093
index
GLuint index
Definition: glcorearb.h:3055
h
GLfloat GLfloat GLfloat GLfloat h
Definition: glcorearb.h:4147


libaditof
Author(s):
autogenerated on Wed May 21 2025 02:06:58