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

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

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 34.0.0.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 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