io.undertow.util.HeaderMap Maven / Gradle / Ivy
/*
* 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 static org.wildfly.common.Assert.checkNotNullParam;
import java.util.AbstractCollection;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* An optimized array-backed header map.
*
* @author David M. Lloyd
* @author Flavia Rainone
*/
public final class HeaderMap implements Iterable {
private Object[] table;
private int size;
private Collection headerNames;
public HeaderMap() {
table = new Object[16];
}
private HeaderValues getEntry(final HttpString headerName) {
if (headerName == null) {
return null;
}
final int hc = headerName.hashCode();
final int idx = hc & (table.length - 1);
final Object o = table[idx];
if (o == null) {
return null;
}
HeaderValues headerValues;
if (o instanceof HeaderValues) {
headerValues = (HeaderValues) o;
if (! headerName.equals(headerValues.key)) {
return null;
}
return headerValues;
} else {
final HeaderValues[] row = (HeaderValues[]) o;
for (int i = 0; i < row.length; i++) {
headerValues = row[i];
if (headerValues != null && headerName.equals(headerValues.key)) {
return headerValues;
}
}
return null;
}
}
private HeaderValues getEntry(final String headerName) {
if (headerName == null) {
return null;
}
final int hc = HttpString.hashCodeOf(headerName);
final int idx = hc & (table.length - 1);
final Object o = table[idx];
if (o == null) {
return null;
}
HeaderValues headerValues;
if (o instanceof HeaderValues) {
headerValues = (HeaderValues) o;
if (! headerValues.key.equalToString(headerName)) {
return null;
}
return headerValues;
} else {
final HeaderValues[] row = (HeaderValues[]) o;
for (int i = 0; i < row.length; i++) {
headerValues = row[i];
if (headerValues != null && headerValues.key.equalToString(headerName)) {
return headerValues;
}
}
return null;
}
}
private HeaderValues removeEntry(final HttpString headerName) {
if (headerName == null) {
return null;
}
final int hc = headerName.hashCode();
final Object[] table = this.table;
final int idx = hc & (table.length - 1);
final Object o = table[idx];
if (o == null) {
return null;
}
HeaderValues headerValues;
if (o instanceof HeaderValues) {
headerValues = (HeaderValues) o;
if (! headerName.equals(headerValues.key)) {
return null;
}
table[idx] = null;
size --;
return headerValues;
} else {
final HeaderValues[] row = (HeaderValues[]) o;
for (int i = 0; i < row.length; i++) {
headerValues = row[i];
if (headerValues != null && headerName.equals(headerValues.key)) {
row[i] = null;
size --;
return headerValues;
}
}
return null;
}
}
private HeaderValues removeEntry(final String headerName) {
if (headerName == null) {
return null;
}
final int hc = HttpString.hashCodeOf(headerName);
final Object[] table = this.table;
final int idx = hc & (table.length - 1);
final Object o = table[idx];
if (o == null) {
return null;
}
HeaderValues headerValues;
if (o instanceof HeaderValues) {
headerValues = (HeaderValues) o;
if (! headerValues.key.equalToString(headerName)) {
return null;
}
table[idx] = null;
size --;
return headerValues;
} else {
final HeaderValues[] row = (HeaderValues[]) o;
for (int i = 0; i < row.length; i++) {
headerValues = row[i];
if (headerValues != null && headerValues.key.equalToString(headerName)) {
row[i] = null;
size --;
return headerValues;
}
}
return null;
}
}
private void resize() {
final int oldLen = table.length;
if (oldLen == 0x40000000) {
return;
}
assert Integer.bitCount(oldLen) == 1;
Object[] newTable = Arrays.copyOf(table, oldLen << 1);
table = newTable;
for (int i = 0; i < oldLen; i ++) {
if (newTable[i] == null) {
continue;
}
if (newTable[i] instanceof HeaderValues) {
HeaderValues e = (HeaderValues) newTable[i];
if ((e.key.hashCode() & oldLen) != 0) {
newTable[i] = null;
newTable[i + oldLen] = e;
}
continue;
}
HeaderValues[] oldRow = (HeaderValues[]) newTable[i];
HeaderValues[] newRow = oldRow.clone();
int rowLen = oldRow.length;
newTable[i + oldLen] = newRow;
HeaderValues item;
for (int j = 0; j < rowLen; j ++) {
item = oldRow[j];
if (item != null) {
if ((item.key.hashCode() & oldLen) != 0) {
oldRow[j] = null;
} else {
newRow[j] = null;
}
}
}
}
}
private HeaderValues getOrCreateEntry(final HttpString headerName) {
if (headerName == null) {
return null;
}
final int hc = headerName.hashCode();
final Object[] table = this.table;
final int length = table.length;
final int idx = hc & (length - 1);
final Object o = table[idx];
HeaderValues headerValues;
if (o == null) {
if (size >= length >> 1) {
resize();
return getOrCreateEntry(headerName);
}
headerValues = new HeaderValues(headerName);
table[idx] = headerValues;
size++;
return headerValues;
}
return getOrCreateNonEmpty(headerName, table, length, idx, o);
}
private HeaderValues getOrCreateNonEmpty(HttpString headerName, Object[] table, int length, int idx, Object o) {
HeaderValues headerValues;
if (o instanceof HeaderValues) {
headerValues = (HeaderValues) o;
if (! headerName.equals(headerValues.key)) {
if (size >= length >> 1) {
resize();
return getOrCreateEntry(headerName);
}
size++;
final HeaderValues[] row = { headerValues, new HeaderValues(headerName), null, null };
table[idx] = row;
return row[1];
}
return headerValues;
} else {
final HeaderValues[] row = (HeaderValues[]) o;
int empty = -1;
for (int i = 0; i < row.length; i++) {
headerValues = row[i];
if (headerValues != null) {
if (headerName.equals(headerValues.key)) {
return headerValues;
}
} else if (empty == -1) {
empty = i;
}
}
if (size >= length >> 1) {
resize();
return getOrCreateEntry(headerName);
}
size++;
headerValues = new HeaderValues(headerName);
if (empty != -1) {
row[empty] = headerValues;
} else {
if (row.length >= 16) {
throw new SecurityException("Excessive collisions");
}
final HeaderValues[] newRow = Arrays.copyOf(row, row.length + 3);
newRow[row.length] = headerValues;
table[idx] = newRow;
}
return headerValues;
}
}
// get
public HeaderValues get(final HttpString headerName) {
return getEntry(headerName);
}
public HeaderValues get(final String headerName) {
return getEntry(headerName);
}
public String getFirst(HttpString headerName) {
HeaderValues headerValues = getEntry(headerName);
if (headerValues == null) return null;
return headerValues.getFirst();
}
public String getFirst(String headerName) {
HeaderValues headerValues = getEntry(headerName);
if (headerValues == null) return null;
return headerValues.getFirst();
}
public String get(HttpString headerName, int index) throws IndexOutOfBoundsException {
if (headerName == null) {
return null;
}
final HeaderValues headerValues = getEntry(headerName);
if (headerValues == null) {
return null;
}
return headerValues.get(index);
}
public String get(String headerName, int index) throws IndexOutOfBoundsException {
if (headerName == null) {
return null;
}
final HeaderValues headerValues = getEntry(headerName);
if (headerValues == null) {
return null;
}
return headerValues.get(index);
}
public String getLast(HttpString headerName) {
if (headerName == null) {
return null;
}
HeaderValues headerValues = getEntry(headerName);
if (headerValues == null) return null;
return headerValues.getLast();
}
public String getLast(String headerName) {
if (headerName == null) {
return null;
}
HeaderValues headerValues = getEntry(headerName);
if (headerValues == null) return null;
return headerValues.getLast();
}
// count
public int count(HttpString headerName) {
if (headerName == null) {
return 0;
}
final HeaderValues headerValues = getEntry(headerName);
if (headerValues == null) {
return 0;
}
return headerValues.size();
}
public int count(String headerName) {
if (headerName == null) {
return 0;
}
final HeaderValues headerValues = getEntry(headerName);
if (headerValues == null) {
return 0;
}
return headerValues.size();
}
public int size() {
return size;
}
// iterate
/**
* Do a fast iteration of this header map without creating any objects.
*
* @return an opaque iterating cookie, or -1 if no iteration is possible
*
* @see #fiNext(long)
* @see #fiCurrent(long)
*/
public long fastIterate() {
final Object[] table = this.table;
final int len = table.length;
int ri = 0;
int ci;
while (ri < len) {
final Object item = table[ri];
if (item != null) {
if (item instanceof HeaderValues) {
return (long)ri << 32L;
} else {
final HeaderValues[] row = (HeaderValues[]) item;
ci = 0;
final int rowLen = row.length;
while (ci < rowLen) {
if (row[ci] != null) {
return (long)ri << 32L | (ci & 0xffffffffL);
}
ci ++;
}
}
}
ri++;
}
return -1L;
}
/**
* Do a fast iteration of this header map without creating any objects, only considering non-empty header values.
*
* @return an opaque iterating cookie, or -1 if no iteration is possible
*/
public long fastIterateNonEmpty() {
final Object[] table = this.table;
final int len = table.length;
int ri = 0;
int ci;
while (ri < len) {
final Object item = table[ri];
if (item != null) {
if (item instanceof HeaderValues) {
if(!((HeaderValues) item).isEmpty()) {
return (long) ri << 32L;
}
} else {
final HeaderValues[] row = (HeaderValues[]) item;
ci = 0;
final int rowLen = row.length;
while (ci < rowLen) {
if (row[ci] != null && !row[ci].isEmpty()) {
return (long)ri << 32L | (ci & 0xffffffffL);
}
ci ++;
}
}
}
ri++;
}
return -1L;
}
/**
* Find the next index in a fast iteration.
*
* @param cookie the previous cookie value
* @return the next cookie value, or -1L if iteration is done
*/
public long fiNext(long cookie) {
if (cookie == -1L) return -1L;
final Object[] table = this.table;
final int len = table.length;
int ri = (int) (cookie >> 32);
int ci = (int) cookie;
Object item = table[ri];
if (item instanceof HeaderValues[]) {
final HeaderValues[] row = (HeaderValues[]) item;
final int rowLen = row.length;
if (++ci >= rowLen) {
ri ++; ci = 0;
} else if (row[ci] != null) {
return (long)ri << 32L | (ci & 0xffffffffL);
}
} else {
ri ++; ci = 0;
}
while (ri < len) {
item = table[ri];
if (item instanceof HeaderValues) {
return (long)ri << 32L;
} else if (item instanceof HeaderValues[]) {
final HeaderValues[] row = (HeaderValues[]) item;
final int rowLen = row.length;
while (ci < rowLen) {
if (row[ci] != null) {
return (long)ri << 32L | (ci & 0xffffffffL);
}
ci ++;
}
}
ci = 0;
ri ++;
}
return -1L;
}
/**
* Find the next non-empty index in a fast iteration.
*
* @param cookie the previous cookie value
* @return the next cookie value, or -1L if iteration is done
*/
public long fiNextNonEmpty(long cookie) {
if (cookie == -1L) return -1L;
final Object[] table = this.table;
final int len = table.length;
int ri = (int) (cookie >> 32);
int ci = (int) cookie;
Object item = table[ri];
if (item instanceof HeaderValues[]) {
final HeaderValues[] row = (HeaderValues[]) item;
final int rowLen = row.length;
if (++ci >= rowLen) {
ri ++; ci = 0;
} else if (row[ci] != null && !row[ci].isEmpty()) {
return (long)ri << 32L | (ci & 0xffffffffL);
}
} else {
ri ++; ci = 0;
}
while (ri < len) {
item = table[ri];
if (item instanceof HeaderValues && !((HeaderValues) item).isEmpty()) {
return (long)ri << 32L;
} else if (item instanceof HeaderValues[]) {
final HeaderValues[] row = (HeaderValues[]) item;
final int rowLen = row.length;
while (ci < rowLen) {
if (row[ci] != null && !row[ci].isEmpty()) {
return (long)ri << 32L | (ci & 0xffffffffL);
}
ci ++;
}
}
ci = 0;
ri ++;
}
return -1L;
}
/**
* Return the value at the current index in a fast iteration.
*
* @param cookie the iteration cookie value
* @return the values object at this position
* @throws NoSuchElementException if the cookie value is invalid
*/
public HeaderValues fiCurrent(long cookie) {
try {
final Object[] table = this.table;
int ri = (int) (cookie >> 32);
int ci = (int) cookie;
final Object item = table[ri];
if (item instanceof HeaderValues[]) {
return ((HeaderValues[])item)[ci];
} else if (ci == 0) {
return (HeaderValues) item;
} else {
throw new NoSuchElementException();
}
} catch (RuntimeException e) {
throw new NoSuchElementException();
}
}
public Iterable eachValue(final HttpString headerName) {
if (headerName == null) {
return Collections.emptyList();
}
final HeaderValues entry = getEntry(headerName);
if (entry == null) {
return Collections.emptyList();
}
return entry;
}
public Iterator iterator() {
return new Iterator() {
final Object[] table = HeaderMap.this.table;
boolean consumed;
int ri, ci;
private HeaderValues _next() {
for (;;) {
if (ri >= table.length) {
return null;
}
final Object o = table[ri];
if (o == null) {
// zero-entry row
ri++;
ci = 0;
consumed = false;
continue;
}
if (o instanceof HeaderValues) {
// one-entry row
if (ci > 0 || consumed) {
ri++;
ci = 0;
consumed = false;
continue;
}
return (HeaderValues) o;
}
final HeaderValues[] row = (HeaderValues[]) o;
final int len = row.length;
if (ci >= len) {
ri ++;
ci = 0;
consumed = false;
continue;
}
if (consumed) {
ci++;
consumed = false;
continue;
}
final HeaderValues headerValues = row[ci];
if (headerValues == null) {
ci ++;
continue;
}
return headerValues;
}
}
public boolean hasNext() {
return _next() != null;
}
public HeaderValues next() {
final HeaderValues next = _next();
if (next == null) {
throw new NoSuchElementException();
}
consumed = true;
return next;
}
public void remove() {
}
};
}
public Collection getHeaderNames() {
if (headerNames != null) {
return headerNames;
}
return headerNames = new AbstractCollection() {
public boolean contains(final Object o) {
return o instanceof HttpString && getEntry((HttpString) o) != null;
}
public boolean add(final HttpString httpString) {
getOrCreateEntry(httpString);
return true;
}
public boolean remove(final Object o) {
if (! (o instanceof HttpString)) return false;
HttpString s = (HttpString) o;
HeaderValues entry = getEntry(s);
if (entry == null) {
return false;
}
entry.clear();
return true;
}
public void clear() {
HeaderMap.this.clear();
}
public Iterator iterator() {
final Iterator iterator = HeaderMap.this.iterator();
return new Iterator() {
public boolean hasNext() {
return iterator.hasNext();
}
public HttpString next() {
return iterator.next().getHeaderName();
}
public void remove() {
iterator.remove();
}
};
}
public int size() {
return HeaderMap.this.size();
}
};
}
// add
public HeaderMap add(HttpString headerName, String headerValue) {
addLast(headerName, headerValue);
return this;
}
public HeaderMap addFirst(final HttpString headerName, final String headerValue) {
checkNotNullParam("headerName", headerName);
if (headerValue == null) {
return this;
}
getOrCreateEntry(headerName).addFirst(headerValue);
return this;
}
public HeaderMap addLast(final HttpString headerName, final String headerValue) {
checkNotNullParam("headerName", headerName);
if (headerValue == null) {
return this;
}
getOrCreateEntry(headerName).addLast(headerValue);
return this;
}
public HeaderMap add(HttpString headerName, long headerValue) {
add(headerName, Long.toString(headerValue));
return this;
}
public HeaderMap addAll(HttpString headerName, Collection headerValues) {
checkNotNullParam("headerName", headerName);
if (headerValues == null || headerValues.isEmpty()) {
return this;
}
getOrCreateEntry(headerName).addAll(headerValues);
return this;
}
// put
public HeaderMap put(HttpString headerName, String headerValue) {
checkNotNullParam("headerName", headerName);
if (headerValue == null) {
remove(headerName);
return this;
}
final HeaderValues headerValues = getOrCreateEntry(headerName);
headerValues.clear();
headerValues.add(headerValue);
return this;
}
public HeaderMap put(HttpString headerName, long headerValue) {
checkNotNullParam("headerName", headerName);
final HeaderValues entry = getOrCreateEntry(headerName);
entry.clear();
entry.add(Long.toString(headerValue));
return this;
}
public HeaderMap putAll(HttpString headerName, Collection headerValues) {
checkNotNullParam("headerName", headerName);
if (headerValues == null || headerValues.isEmpty()) {
remove(headerName);
return this;
}
final HeaderValues entry = getOrCreateEntry(headerName);
entry.clear();
entry.addAll(headerValues);
return this;
}
public HeaderMap putAll(HeaderMap headerMap) {
checkNotNullParam("headerMap", headerMap);
final Iterator iterator = headerMap.iterator();
while (iterator.hasNext()) {
final HeaderValues headerValues = iterator.next();
putAll(headerValues.getHeaderName(), headerValues);
}
return this;
}
// clear
public HeaderMap clear() {
Arrays.fill(table, null);
size = 0;
return this;
}
// remove
public Collection remove(HttpString headerName) {
if (headerName == null) {
return Collections.emptyList();
}
final Collection values = removeEntry(headerName);
return values != null ? values : Collections.emptyList();
}
public Collection remove(String headerName) {
if (headerName == null) {
return Collections.emptyList();
}
final Collection values = removeEntry(headerName);
return values != null ? values : Collections.emptyList();
}
// contains
public boolean contains(HttpString headerName) {
final HeaderValues headerValues = getEntry(headerName);
if (headerValues == null || headerValues.size == 0) {
return false;
}
final Object v = headerValues.value;
if (v instanceof String) {
return true;
}
final String[] list = (String[]) v;
for (int i = 0; i < list.length; i++) {
if (list[i] != null) {
return true;
}
}
return false;
}
public boolean contains(String headerName) {
final HeaderValues headerValues = getEntry(headerName);
if (headerValues == null || headerValues.size == 0) {
return false;
}
final Object v = headerValues.value;
if (v instanceof String) {
return true;
}
final String[] list = (String[]) v;
for (int i = 0; i < list.length; i++) {
if (list[i] != null) {
return true;
}
}
return false;
}
// compare
@Override
public boolean equals(final Object o) {
return o == this;
}
@Override
public int hashCode() {
return super.hashCode();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("{");
boolean first = true;
for(HttpString name : getHeaderNames()) {
if(first) {
first = false;
} else {
sb.append(", ");
}
sb.append(name);
sb.append("=[");
boolean f = true;
for(String val : get(name)) {
if(f) {
f = false;
} else {
sb.append(", ");
}
sb.append(val);
}
sb.append("]");
}
sb.append("}");
return sb.toString();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy