All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.undertow.util.SubstringMap Maven / Gradle / Ivy

There is a newer version: 2.3.18.Final
Show newest version
/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2014 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * 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 io.undertow.util;


import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;

/**
 * A string keyed map that can be accessed as a substring, eliminating the need to allocate a new string
 * to do a key comparison against.
 * 

* This class uses linear probing and is thread safe due to copy on write semantics. As such it is not recomended * for data that changes frequently. *

* This class does not actually implement the map interface to avoid implementing unnecessary operations. * * @author Stuart Douglas */ public class SubstringMap { private static final int ALL_BUT_LAST_BIT = ~1; private volatile Object[] table = new Object[16]; private int size; public SubstringMatch get(String key, int length) { return get(key, length, false); } public SubstringMatch get(String key) { return get(key, key.length(), false); } private SubstringMatch get(String key, int length, boolean exact) { if(key.length() < length) { throw new IllegalArgumentException(); } Object[] table = this.table; int hash = hash(key, length); int pos = tablePos(table, hash); int start = pos; while (table[pos] != null) { if(exact) { if(table[pos].equals(key)) { return (SubstringMatch) table[pos + 1]; } } else { if (doEquals((String) table[pos], key, length)) { return (SubstringMatch) table[pos + 1]; } } pos += 2; if(pos >= table.length) { pos = 0; } if(pos == start) { return null; } } return null; } private int tablePos(Object[] table, int hash) { return (hash & (table.length - 1)) & ALL_BUT_LAST_BIT; } private boolean doEquals(String s1, String s2, int length) { if(s1.length() != length || s2.length() < length) { return false; } for(int i = 0; i < length; ++i) { if(s1.charAt(i) != s2.charAt(i)) { return false; } } return true; } public synchronized void put(String key, V value) { if (key == null) { throw new NullPointerException(); } Object[] newTable; if (table.length / (double) size < 4 && table.length != Integer.MAX_VALUE) { newTable = new Object[table.length << 1]; for (int i = 0; i < table.length; i += 2) { if (table[i] != null) { doPut(newTable, (String) table[i], table[i + 1]); } } } else { newTable = new Object[table.length]; System.arraycopy(table, 0, newTable, 0, table.length); } doPut(newTable, key, new SubstringMap.SubstringMatch<>(key, value)); this.table = newTable; size++; } public synchronized V remove(String key) { if (key == null) { throw new NullPointerException(); } //we just assume it is present, and always do a copy //for this maps intended use cases as a path matcher it won't be called when //the value is not present anyway V value = null; Object[] newTable = new Object[table.length]; for (int i = 0; i < table.length; i += 2) { if (table[i] != null && !table[i].equals(key)) { doPut(newTable, (String) table[i], table[i + 1]); } else if (table[i] != null) { value = (V) table[i + 1]; size--; } } this.table = newTable; if(value == null) { return null; } return ((SubstringMatch)value).getValue(); } private void doPut(Object[] newTable, String key, Object value) { int hash = hash(key, key.length()); int pos = tablePos(newTable, hash); while (newTable[pos] != null && !newTable[pos].equals(key)) { pos += 2; if (pos >= newTable.length) { pos = 0; } } newTable[pos] = key; newTable[pos + 1] = value; } public Map toMap() { Map map = new HashMap<>(); Object[] t = this.table; for(int i = 0; i < t.length; i += 2) { if(t[i] != null) { map.put((String)t[i], ((SubstringMatch)t[i+1]).value); } } return map; } public synchronized void clear() { size = 0; table = new Object[16]; } private static int hash(String value, int length) { if (length == 0) { return 0; } int h = 0; for (int i = 0; i < length; i++) { h = 31 * h + value.charAt(i); } return h; } public Iterable keys() { return new Iterable() { @Override public Iterator iterator() { final Object[] tMap = table; int i = 0; while (i < table.length && tMap[i] == null) { i += 2; } final int startPos = i; return new Iterator() { private Object[] map = tMap; private int pos = startPos; @Override public boolean hasNext() { return pos < table.length; } @Override public String next() { if (!hasNext()) { throw new NoSuchElementException(); } String ret = (String) map[pos]; pos += 2; while (pos < table.length && tMap[pos] == null) { pos += 2; } return ret; } @Override public void remove() { throw new UnsupportedOperationException(); } }; } }; } public static final class SubstringMatch { private final String key; private final V value; public SubstringMatch(String key, V value) { this.key = key; this.value = value; } public String getKey() { return key; } public V getValue() { return value; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy