1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.htmlunit.util;
16
17 import java.io.IOException;
18 import java.io.ObjectInputStream;
19 import java.io.ObjectOutputStream;
20 import java.io.Serializable;
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.Collection;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.NoSuchElementException;
28 import java.util.Objects;
29 import java.util.Set;
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61 public class OrderedFastHashMap<K, V> implements Map<K, V>, Serializable {
62
63 private static final Object FREE_KEY_ = null;
64 private static final Object REMOVED_KEY_ = new Object();
65
66
67 private static final double FILLFACTOR_ = 0.7d;
68
69
70 private Object[] mapData_;
71
72
73 private int mapThreshold_;
74
75
76 private int mapSize_;
77
78
79
80
81 private int[] orderedList_;
82
83
84
85 private int orderedListSize_;
86
87
88
89
90 public OrderedFastHashMap() {
91 this(8);
92 }
93
94
95
96
97
98
99
100 public OrderedFastHashMap(final int size) {
101 if (size > 0) {
102 final int capacity = arraySize(size, FILLFACTOR_);
103
104 this.mapData_ = new Object[capacity << 1];
105 this.mapThreshold_ = (int) (capacity * FILLFACTOR_);
106
107 this.orderedList_ = new int[capacity];
108 }
109 else {
110 this.mapData_ = new Object[0];
111 this.mapThreshold_ = 0;
112
113 this.orderedList_ = new int[0];
114 }
115 }
116
117
118
119
120
121
122
123
124 @Override
125 public V get(final Object key) {
126 final int length = this.mapData_.length;
127
128
129 if (length == 0) {
130 return null;
131 }
132
133 int ptr = (key.hashCode() & ((length >> 1) - 1)) << 1;
134 Object k = mapData_[ptr];
135
136 if (k == FREE_KEY_) {
137 return null;
138 }
139
140
141 if (k.hashCode() == key.hashCode() && k.equals(key)) {
142 return (V) this.mapData_[ptr + 1];
143 }
144
145
146 final int originalPtr = ptr;
147 while (true) {
148 ptr = (ptr + 2) & (length - 1);
149
150
151 if (originalPtr == ptr) {
152 return null;
153 }
154
155 k = this.mapData_[ptr];
156
157 if (k == FREE_KEY_) {
158 return null;
159 }
160
161 if (k != REMOVED_KEY_) {
162 if (k.hashCode() == key.hashCode() && k.equals(key)) {
163 return (V) this.mapData_[ptr + 1];
164 }
165 }
166 }
167 }
168
169
170
171
172
173
174
175
176
177
178 private V put(final K key, final V value, final Position listPosition) {
179 if (mapSize_ >= mapThreshold_) {
180 rehash(this.mapData_.length == 0 ? 4 : this.mapData_.length << 1);
181 }
182
183 int ptr = getStartIndex(key) << 1;
184 Object k = mapData_[ptr];
185
186 if (k == FREE_KEY_) {
187
188 mapData_[ptr] = key;
189 mapData_[ptr + 1] = value;
190
191
192 orderedListAdd(listPosition, ptr);
193
194 mapSize_++;
195
196 return null;
197 }
198 else if (k.equals(key)) {
199
200 final Object ret = mapData_[ptr + 1];
201 mapData_[ptr + 1] = value;
202
203
204
205 return (V) ret;
206 }
207
208 int firstRemoved = -1;
209 if (k == REMOVED_KEY_) {
210 firstRemoved = ptr;
211 }
212
213 while (true) {
214 ptr = (ptr + 2) & (this.mapData_.length - 1);
215 k = mapData_[ptr];
216
217 if (k == FREE_KEY_) {
218 if (firstRemoved != -1) {
219 ptr = firstRemoved;
220 }
221 mapData_[ptr] = key;
222 mapData_[ptr + 1] = value;
223
224
225 orderedListAdd(listPosition, ptr);
226
227 mapSize_++;
228
229 return null;
230 }
231 else if (k.equals(key)) {
232 final Object ret = mapData_[ptr + 1];
233 mapData_[ptr + 1] = value;
234
235
236
237 return (V) ret;
238 }
239 else if (k == REMOVED_KEY_) {
240 if (firstRemoved == -1) {
241 firstRemoved = ptr;
242 }
243 }
244 }
245 }
246
247
248
249
250
251
252
253
254 @Override
255 public V remove(final Object key) {
256 final int length = this.mapData_.length;
257
258 if (length == 0) {
259 return null;
260 }
261
262 int ptr = getStartIndex(key) << 1;
263 Object k = this.mapData_[ptr];
264
265 if (k == FREE_KEY_) {
266 return null;
267 }
268 else if (k.equals(key)) {
269
270 this.mapSize_--;
271
272 if (this.mapData_[(ptr + 2) & (length - 1)] == FREE_KEY_) {
273 this.mapData_[ptr] = FREE_KEY_;
274 }
275 else {
276 this.mapData_[ptr] = REMOVED_KEY_;
277 }
278
279 final V ret = (V) this.mapData_[ptr + 1];
280 this.mapData_[ptr + 1] = null;
281
282
283 orderedListRemove(ptr);
284
285 return ret;
286 }
287
288 while (true) {
289 ptr = (ptr + 2) & (length - 1);
290 k = this.mapData_[ptr];
291
292 if (k == FREE_KEY_) {
293 return null;
294 }
295 else if (k.equals(key)) {
296 this.mapSize_--;
297 if (this.mapData_[(ptr + 2) & (length - 1)] == FREE_KEY_) {
298 this.mapData_[ptr] = FREE_KEY_;
299 }
300 else {
301 this.mapData_[ptr] = REMOVED_KEY_;
302 }
303
304 final V ret = (V) this.mapData_[ptr + 1];
305 this.mapData_[ptr + 1] = null;
306
307
308 orderedListRemove(ptr);
309
310 return ret;
311 }
312 }
313 }
314
315
316
317
318
319
320 @Override
321 public int size() {
322 return mapSize_;
323 }
324
325
326
327
328
329
330 private void rehash(final int newCapacity) {
331 this.mapThreshold_ = (int) (newCapacity / 2 * FILLFACTOR_);
332
333 final Object[] oldData = this.mapData_;
334
335 this.mapData_ = new Object[newCapacity];
336
337
338
339 final int[] oldOrderedList = this.orderedList_;
340 final int oldOrderedListSize = this.orderedListSize_;
341 this.orderedList_ = new int[newCapacity];
342
343 this.mapSize_ = 0;
344 this.orderedListSize_ = 0;
345
346
347
348
349
350 for (int i = 0; i < oldOrderedListSize; i++) {
351 final int pos = oldOrderedList[i];
352
353
354 final K key = (K) oldData[pos];
355 final V value = (V) oldData[pos + 1];
356
357
358
359 put(key, value, Position.LAST);
360 }
361 }
362
363
364
365
366
367
368
369
370
371 public List<K> keys() {
372 final List<K> result = new ArrayList<>(this.orderedListSize_);
373
374 for (int i = 0; i < this.orderedListSize_; i++) {
375 final int pos = this.orderedList_[i];
376 final Object o = this.mapData_[pos];
377 result.add((K) o);
378 }
379
380 return result;
381 }
382
383
384
385
386
387
388
389
390
391 @Override
392 public List<V> values() {
393 final List<V> result = new ArrayList<>(this.orderedListSize_);
394
395 for (int i = 0; i < this.orderedListSize_; i++) {
396 final int pos = this.orderedList_[i];
397 final Object o = this.mapData_[pos + 1];
398 result.add((V) o);
399 }
400
401 return result;
402 }
403
404
405
406
407
408 @Override
409 public void clear() {
410 this.mapSize_ = 0;
411 this.orderedListSize_ = 0;
412 Arrays.fill(this.mapData_, FREE_KEY_);
413
414 }
415
416
417
418
419
420
421
422 private int getStartIndex(final Object key) {
423
424 return key.hashCode() & ((this.mapData_.length >> 1) - 1);
425 }
426
427
428
429
430
431
432
433
434
435
436 private static long nextPowerOfTwo(final long x) {
437 if (x == 0) {
438 return 1;
439 }
440
441 long r = x - 1;
442 r |= r >> 1;
443 r |= r >> 2;
444 r |= r >> 4;
445 r |= r >> 8;
446 r |= r >> 16;
447
448 return (r | r >> 32) + 1;
449 }
450
451
452
453
454
455
456
457
458
459
460
461 private static int arraySize(final int expected, final double f) {
462 final long s = Math.max(2, nextPowerOfTwo((long) Math.ceil(expected / f)));
463
464 if (s > (1 << 30)) {
465 throw new IllegalArgumentException(
466 "Too large (" + expected + " expected elements with load factor " + f + ")");
467 }
468 return (int) s;
469 }
470
471
472
473
474
475
476
477
478
479
480 public Entry<K, V> getEntry(final int index) {
481 if (index < 0 || index >= this.orderedListSize_) {
482 throw new IndexOutOfBoundsException(String.format("Index: %s, Size: %s", index, this.orderedListSize_));
483 }
484
485 final int pos = this.orderedList_[index];
486 return new Entry(this.mapData_[pos], this.mapData_[pos + 1]);
487 }
488
489
490
491
492
493
494
495
496
497 public K getKey(final int index) {
498 if (index < 0 || index >= this.orderedListSize_) {
499 throw new IndexOutOfBoundsException(String.format("Index: %s, Size: %s", index, this.orderedListSize_));
500 }
501
502 final int pos = this.orderedList_[index];
503 return (K) this.mapData_[pos];
504 }
505
506
507
508
509
510
511
512
513
514 public V getValue(final int index) {
515 if (index < 0 || index >= this.orderedListSize_) {
516 throw new IndexOutOfBoundsException(String.format("Index: %s, Size: %s", index, this.orderedListSize_));
517 }
518
519 final int pos = this.orderedList_[index];
520 return (V) this.mapData_[pos + 1];
521 }
522
523
524
525
526
527
528
529
530
531 public V remove(final int index) {
532 if (index < 0 || index >= this.orderedListSize_) {
533 throw new IndexOutOfBoundsException(String.format("Index: %s, Size: %s", index, this.orderedListSize_));
534 }
535
536 final int pos = this.orderedList_[index];
537 final K key = (K) this.mapData_[pos];
538
539 return remove(key);
540 }
541
542 @Override
543 public V put(final K key, final V value) {
544 return this.put(key, value, Position.LAST);
545 }
546
547
548
549
550
551
552
553 public V addFirst(final K key, final V value) {
554 return this.put(key, value, Position.FIRST);
555 }
556
557
558
559
560
561
562
563 public V add(final K key, final V value) {
564 return this.put(key, value, Position.LAST);
565 }
566
567
568
569
570
571
572
573 public V addLast(final K key, final V value) {
574 return this.put(key, value, Position.LAST);
575 }
576
577
578
579
580 public V getFirst() {
581 return getValue(0);
582 }
583
584
585
586
587 public V getLast() {
588 return getValue(this.orderedListSize_ - 1);
589 }
590
591
592
593
594
595 public V removeFirst() {
596 if (this.orderedListSize_ > 0) {
597 final int pos = this.orderedList_[0];
598 final K key = (K) this.mapData_[pos];
599 return remove(key);
600 }
601 return null;
602 }
603
604
605
606
607
608 public V removeLast() {
609 if (this.orderedListSize_ > 0) {
610 final int pos = this.orderedList_[this.orderedListSize_ - 1];
611 final K key = (K) this.mapData_[pos];
612 return remove(key);
613 }
614 return null;
615 }
616
617
618
619
620
621
622
623 @Override
624 public boolean containsKey(final Object key) {
625 return get(key) != null;
626 }
627
628 @Override
629 public boolean containsValue(final Object value) {
630
631 for (int i = 0; i < this.orderedListSize_; i++) {
632 final int pos = this.orderedList_[i] + 1;
633 final Object v = this.mapData_[pos];
634
635
636 if (v == value || v.equals(value)) {
637 return true;
638 }
639 }
640
641 return false;
642 }
643
644 @Override
645 public boolean isEmpty() {
646 return this.mapSize_ == 0;
647 }
648
649 @Override
650 public Set<Map.Entry<K, V>> entrySet() {
651 return new OrderedEntrySet<>(this);
652 }
653
654 @Override
655 public Set<K> keySet() {
656 return new OrderedKeySet<>(this);
657 }
658
659
660
661
662 public void reverse() {
663
664 final int to = this.orderedListSize_ / 2;
665
666 for (int i = 0; i < to; i++) {
667
668 final int j = this.orderedList_[i];
669 this.orderedList_[i] = this.orderedList_[this.orderedListSize_ - i - 1];
670 this.orderedList_[this.orderedListSize_ - i - 1] = j;
671 }
672 }
673
674
675
676
677
678
679
680
681 private void readObject(final ObjectInputStream aInputStream) throws ClassNotFoundException, IOException {
682
683 aInputStream.defaultReadObject();
684
685
686 final Object[] srcData = Arrays.copyOf(this.mapData_, this.mapData_.length);
687 final int[] srcIndex = Arrays.copyOf(this.orderedList_, this.orderedList_.length);
688 final int orderedListSize = this.orderedListSize_;
689
690
691 clear();
692
693
694
695
696 for (int i = 0; i < orderedListSize; i++) {
697 final int pos = srcIndex[i];
698
699 final K key = (K) srcData[pos];
700 final V value = (V) srcData[pos + 1];
701 put(key, value);
702 }
703 }
704
705
706
707
708
709
710
711 private void writeObject(final ObjectOutputStream aOutputStream) throws IOException {
712
713
714 for (int i = 0; i < this.mapData_.length; i++) {
715 final Object entry = this.mapData_[i];
716 if (entry == FREE_KEY_ || entry == REMOVED_KEY_) {
717 this.mapData_[i] = null;
718 }
719 }
720
721
722 aOutputStream.defaultWriteObject();
723 }
724
725
726
727
728
729 static class OrderedEntrySet<K, V> implements Set<Map.Entry<K, V>> {
730 private final OrderedFastHashMap<K, V> backingMap_;
731
732 OrderedEntrySet(final OrderedFastHashMap<K, V> backingMap) {
733 this.backingMap_ = backingMap;
734 }
735
736 @Override
737 public int size() {
738 return this.backingMap_.size();
739 }
740
741 @Override
742 public boolean isEmpty() {
743 return this.backingMap_.isEmpty();
744 }
745
746 @Override
747 public boolean contains(final Object o) {
748 if (o instanceof Map.Entry) {
749 final Map.Entry ose = (Map.Entry) o;
750 final Object k = ose.getKey();
751 final Object v = ose.getValue();
752
753 final Object value = this.backingMap_.get(k);
754 if (value != null) {
755 return v.equals(value);
756 }
757 }
758
759 return false;
760 }
761
762 @Override
763 public Object[] toArray() {
764 final Object[] array = new Object[this.backingMap_.orderedListSize_];
765 return toArray(array);
766 }
767
768 @Override
769 @SuppressWarnings("unchecked")
770 public <T> T[] toArray(final T[] a) {
771 final T[] array;
772 if (a.length >= this.backingMap_.orderedListSize_) {
773 array = a;
774 }
775 else {
776 array = (T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(),
777 this.backingMap_.orderedListSize_);
778 }
779
780 for (int i = 0; i < this.backingMap_.orderedListSize_; i++) {
781 array[i] = (T) this.backingMap_.getEntry(i);
782 }
783
784 return array;
785 }
786
787 @Override
788 public Iterator<Map.Entry<K, V>> iterator() {
789 return new OrderedEntryIterator();
790 }
791
792 @Override
793 public boolean add(final Map.Entry<K, V> e) {
794 throw new UnsupportedOperationException();
795 }
796
797 @Override
798 public boolean remove(final Object o) {
799 throw new UnsupportedOperationException();
800 }
801
802 @Override
803 public boolean containsAll(final Collection<?> c) {
804 throw new UnsupportedOperationException();
805 }
806
807 @Override
808 public boolean addAll(final Collection<? extends Map.Entry<K, V>> c) {
809 throw new UnsupportedOperationException();
810 }
811
812 @Override
813 public boolean retainAll(final Collection<?> c) {
814 throw new UnsupportedOperationException();
815 }
816
817 @Override
818 public boolean removeAll(final Collection<?> c) {
819 throw new UnsupportedOperationException();
820 }
821
822 @Override
823 public void clear() {
824 throw new UnsupportedOperationException();
825 }
826
827 class OrderedEntryIterator implements Iterator<Map.Entry<K, V>> {
828 private int pos_ = 0;
829
830 @Override
831 public boolean hasNext() {
832 return pos_ < backingMap_.orderedListSize_;
833 }
834
835 @Override
836 public Map.Entry<K, V> next() {
837 if (pos_ < backingMap_.orderedListSize_) {
838 return backingMap_.getEntry(pos_++);
839 }
840 throw new NoSuchElementException();
841 }
842 }
843 }
844
845 static class OrderedKeySet<K, V> implements Set<K> {
846 private final OrderedFastHashMap<K, V> backingMap_;
847
848 OrderedKeySet(final OrderedFastHashMap<K, V> backingMap) {
849 this.backingMap_ = backingMap;
850 }
851
852 @Override
853 public int size() {
854 return this.backingMap_.size();
855 }
856
857 @Override
858 public boolean isEmpty() {
859 return this.backingMap_.isEmpty();
860 }
861
862 @Override
863 public boolean contains(final Object o) {
864 return this.backingMap_.containsKey(o);
865 }
866
867 @Override
868 public Object[] toArray() {
869 final Object[] array = new Object[this.backingMap_.orderedListSize_];
870 return toArray(array);
871 }
872
873 @Override
874 @SuppressWarnings("unchecked")
875 public <T> T[] toArray(final T[] a) {
876 final T[] array;
877
878 if (a.length >= this.backingMap_.orderedListSize_) {
879 array = a;
880 }
881 else {
882 array = (T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(),
883 this.backingMap_.orderedListSize_);
884 }
885
886 for (int i = 0; i < this.backingMap_.orderedListSize_; i++) {
887 array[i] = (T) this.backingMap_.getKey(i);
888 }
889
890 return array;
891 }
892
893 @Override
894 public Iterator<K> iterator() {
895 return new OrderedKeyIterator();
896 }
897
898 class OrderedKeyIterator implements Iterator<K> {
899 private int pos_ = 0;
900
901 @Override
902 public boolean hasNext() {
903 return this.pos_ < backingMap_.orderedListSize_;
904 }
905
906 @Override
907 public K next() {
908 if (this.pos_ < backingMap_.orderedListSize_) {
909 return backingMap_.getKey(this.pos_++);
910 }
911 throw new NoSuchElementException();
912 }
913 }
914
915 @Override
916 public boolean add(final K e) {
917 throw new UnsupportedOperationException();
918 }
919
920 @Override
921 public boolean remove(final Object o) {
922 throw new UnsupportedOperationException();
923 }
924
925 @Override
926 public boolean containsAll(final Collection<?> c) {
927 throw new UnsupportedOperationException();
928 }
929
930 @Override
931 public boolean addAll(final Collection<? extends K> c) {
932 throw new UnsupportedOperationException();
933 }
934
935 @Override
936 public boolean retainAll(final Collection<?> c) {
937 throw new UnsupportedOperationException();
938 }
939
940 @Override
941 public boolean removeAll(final Collection<?> c) {
942 throw new UnsupportedOperationException();
943 }
944
945 @Override
946 public void clear() {
947 throw new UnsupportedOperationException();
948 }
949 }
950
951 @Override
952 public void putAll(final Map<? extends K, ? extends V> src) {
953 if (src == this) {
954 throw new IllegalArgumentException("Cannot add myself");
955 }
956
957 for (final Map.Entry<? extends K, ? extends V> entry : src.entrySet()) {
958 put(entry.getKey(), entry.getValue(), Position.LAST);
959 }
960 }
961
962 private void orderedListAdd(final Position listPosition, final int position) {
963
964
965 if (listPosition == Position.FIRST) {
966 System.arraycopy(this.orderedList_, 0, this.orderedList_, 1, this.orderedList_.length - 1);
967 this.orderedList_[0] = position;
968 this.orderedListSize_++;
969 }
970 else if (listPosition == Position.LAST) {
971 this.orderedList_[this.orderedListSize_] = position;
972 this.orderedListSize_++;
973 }
974 else {
975
976 }
977 }
978
979 private void orderedListRemove(final int position) {
980
981 int i = 0;
982 for ( ; i < this.orderedListSize_; i++) {
983 if (this.orderedList_[i] == position) {
984 this.orderedList_[i] = -1;
985 if (i < this.orderedListSize_) {
986
987 System.arraycopy(this.orderedList_, i + 1, this.orderedList_, i, this.orderedListSize_ - i);
988 }
989 this.orderedListSize_--;
990
991 return;
992 }
993 }
994
995 if (i == this.orderedListSize_) {
996 throw new IllegalArgumentException(String.format("Position %s was not in order list", position));
997 }
998 }
999
1000 @Override
1001 public String toString() {
1002 final int maxLen = 10;
1003
1004 return String.format(
1005 "mapData=%s, mapFillFactor=%s, mapThreshold=%s, mapSize=%s,%norderedList=%s, orderedListSize=%s",
1006 mapData_ != null ? Arrays.asList(mapData_).subList(0, Math.min(mapData_.length, maxLen)) : null,
1007 FILLFACTOR_, mapThreshold_, mapSize_,
1008 orderedList_ != null
1009 ? Arrays.toString(Arrays.copyOf(orderedList_, Math.min(orderedList_.length, maxLen)))
1010 : null,
1011 orderedListSize_);
1012 }
1013
1014
1015
1016
1017 private enum Position {
1018 NONE, FIRST, LAST
1019 }
1020
1021
1022
1023
1024
1025
1026
1027 static class Entry<K, V> implements Map.Entry<K, V> {
1028 private final K key_;
1029 private final V value_;
1030
1031 Entry(final K key, final V value) {
1032 this.key_ = key;
1033 this.value_ = value;
1034 }
1035
1036 @Override
1037 public K getKey() {
1038 return key_;
1039 }
1040
1041 @Override
1042 public V getValue() {
1043 return value_;
1044 }
1045
1046 @Override
1047 public V setValue(final V value) {
1048 throw new UnsupportedOperationException("This map does not support write-through via an entry");
1049 }
1050
1051 @Override
1052 public int hashCode() {
1053 return Objects.hashCode(key_) ^ Objects.hashCode(value_);
1054 }
1055
1056 @Override
1057 public String toString() {
1058 return key_ + "=" + value_;
1059 }
1060
1061 @Override
1062 public boolean equals(final Object o) {
1063 if (o == this) {
1064 return true;
1065 }
1066
1067 if (o instanceof Map.Entry) {
1068 final Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
1069
1070 if (Objects.equals(key_, e.getKey()) && Objects.equals(value_, e.getValue())) {
1071 return true;
1072 }
1073 }
1074
1075 return false;
1076 }
1077 }
1078 }