1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.google.common.base;
18
19 import com.google.common.annotations.GwtIncompatible;
20 import com.google.common.annotations.VisibleForTesting;
21 import com.google.common.base.CharMatcher.FastMatcher;
22
23 import java.util.BitSet;
24
25
26
27
28
29
30
31 @GwtIncompatible("no precomputation is done in GWT")
32 final class SmallCharMatcher extends FastMatcher {
33 static final int MAX_SIZE = 1023;
34 private final char[] table;
35 private final boolean containsZero;
36 private final long filter;
37
38 private SmallCharMatcher(char[] table, long filter, boolean containsZero,
39 String description) {
40 super(description);
41 this.table = table;
42 this.filter = filter;
43 this.containsZero = containsZero;
44 }
45
46 private static final int C1 = 0xcc9e2d51;
47 private static final int C2 = 0x1b873593;
48
49
50
51
52
53
54
55
56
57 static int smear(int hashCode) {
58 return C2 * Integer.rotateLeft(hashCode * C1, 15);
59 }
60
61 private boolean checkFilter(int c) {
62 return 1 == (1 & (filter >> c));
63 }
64
65
66
67
68
69 private static final double DESIRED_LOAD_FACTOR = 0.5;
70
71
72
73
74
75
76
77 @VisibleForTesting static int chooseTableSize(int setSize) {
78 if (setSize == 1) {
79 return 2;
80 }
81
82
83 int tableSize = Integer.highestOneBit(setSize - 1) << 1;
84 while (tableSize * DESIRED_LOAD_FACTOR < setSize) {
85 tableSize <<= 1;
86 }
87 return tableSize;
88 }
89
90 static CharMatcher from(BitSet chars, String description) {
91
92 long filter = 0;
93 int size = chars.cardinality();
94 boolean containsZero = chars.get(0);
95
96 char[] table = new char[chooseTableSize(size)];
97 int mask = table.length - 1;
98 for (int c = chars.nextSetBit(0); c != -1; c = chars.nextSetBit(c + 1)) {
99
100 filter |= 1L << c;
101 int index = smear(c) & mask;
102 while (true) {
103
104 if (table[index] == 0) {
105 table[index] = (char) c;
106 break;
107 }
108
109 index = (index + 1) & mask;
110 }
111 }
112 return new SmallCharMatcher(table, filter, containsZero, description);
113 }
114
115 @Override
116 public boolean matches(char c) {
117 if (c == 0) {
118 return containsZero;
119 }
120 if (!checkFilter(c)) {
121 return false;
122 }
123 int mask = table.length - 1;
124 int startingIndex = smear(c) & mask;
125 int index = startingIndex;
126 do {
127
128 if (table[index] == 0) {
129 return false;
130
131 } else if (table[index] == c) {
132 return true;
133 } else {
134
135 index = (index + 1) & mask;
136 }
137
138 } while (index != startingIndex);
139 return false;
140 }
141
142 @Override
143 void setBits(BitSet table) {
144 if (containsZero) {
145 table.set(0);
146 }
147 for (char c : this.table) {
148 if (c != 0) {
149 table.set(c);
150 }
151 }
152 }
153 }