/* * Copyright (C) 2012 The Guava Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.common.base; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.CharMatcher.FastMatcher; /** * An immutable small version of CharMatcher that uses an efficient hash table implementation, with * non-power-of-2 sizing to try to use no reprobing, if possible. * * @author Christopher Swenson */ @GwtCompatible(emulated = true) final class SmallCharMatcher extends FastMatcher { static final int MAX_SIZE = 63; static final int MAX_TABLE_SIZE = 128; private final boolean reprobe; private final char[] table; private final boolean containsZero; final long filter; private SmallCharMatcher(char[] table, long filter, boolean containsZero, boolean reprobe, String description) { super(description); this.table = table; this.filter = filter; this.containsZero = containsZero; this.reprobe = reprobe; } private boolean checkFilter(int c) { return 1 == (1 & (filter >> c)); } @VisibleForTesting static char[] buildTable(int modulus, char[] charArray, boolean reprobe) { char[] table = new char[modulus]; for (char c : charArray) { int index = c % modulus; if (index < 0) { index += modulus; } if ((table[index] != 0) && !reprobe) { return null; } else if (reprobe) { while (table[index] != 0) { index = (index + 1) % modulus; } } table[index] = c; } return table; } static CharMatcher from(char[] chars, String description) { int size = chars.length; boolean containsZero = chars[0] == 0; boolean reprobe = false; // Compute the filter. long filter = 0; for (char c : chars) { filter |= 1L << c; } char[] table = null; for (int i = size; table == null && i < MAX_TABLE_SIZE; i++) { table = buildTable(i, chars, false); } // Compute the hash table. if (table == null) { table = buildTable(MAX_TABLE_SIZE, chars, true); reprobe = true; } return new SmallCharMatcher(table, filter, containsZero, reprobe, description); } @Override public boolean matches(char c) { if (c == 0) { return containsZero; } if (!checkFilter(c)) { return false; } int index = c % table.length; if (index < 0) { index += table.length; } while (true) { // Check for empty. if (table[index] == 0) { return false; } else if (table[index] == c) { return true; } else if (reprobe) { // Linear probing will terminate eventually. index = (index + 1) % table.length; } else { return false; } } } }