// 
// Decompiled by Procyon v0.6.0
// 

package io.netty.handler.codec;

import java.util.NoSuchElementException;
import java.util.Arrays;
import java.util.Map;
import java.util.LinkedHashSet;
import java.util.Collections;
import java.util.Set;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import io.netty.util.internal.MathUtil;
import io.netty.util.internal.ObjectUtil;
import io.netty.util.HashingStrategy;

public class DefaultHeaders<K, V, T extends Headers<K, V, T>> implements Headers<K, V, T>
{
    static final int HASH_CODE_SEED = -1028477387;
    private final HeaderEntry<K, V>[] entries;
    protected final HeaderEntry<K, V> head;
    private final byte hashMask;
    private final ValueConverter<V> valueConverter;
    private final NameValidator<K> nameValidator;
    private final ValueValidator<V> valueValidator;
    private final HashingStrategy<K> hashingStrategy;
    int size;
    
    public DefaultHeaders(final ValueConverter<V> valueConverter) {
        this(HashingStrategy.JAVA_HASHER, valueConverter);
    }
    
    public DefaultHeaders(final ValueConverter<V> valueConverter, final NameValidator<K> nameValidator) {
        this(HashingStrategy.JAVA_HASHER, valueConverter, nameValidator);
    }
    
    public DefaultHeaders(final HashingStrategy<K> nameHashingStrategy, final ValueConverter<V> valueConverter) {
        this(nameHashingStrategy, valueConverter, NameValidator.NOT_NULL);
    }
    
    public DefaultHeaders(final HashingStrategy<K> nameHashingStrategy, final ValueConverter<V> valueConverter, final NameValidator<K> nameValidator) {
        this(nameHashingStrategy, valueConverter, nameValidator, 16);
    }
    
    public DefaultHeaders(final HashingStrategy<K> nameHashingStrategy, final ValueConverter<V> valueConverter, final NameValidator<K> nameValidator, final int arraySizeHint) {
        this(nameHashingStrategy, valueConverter, nameValidator, arraySizeHint, ValueValidator.NO_VALIDATION);
    }
    
    public DefaultHeaders(final HashingStrategy<K> nameHashingStrategy, final ValueConverter<V> valueConverter, final NameValidator<K> nameValidator, final int arraySizeHint, final ValueValidator<V> valueValidator) {
        this.valueConverter = ObjectUtil.checkNotNull(valueConverter, "valueConverter");
        this.nameValidator = ObjectUtil.checkNotNull(nameValidator, "nameValidator");
        this.hashingStrategy = ObjectUtil.checkNotNull(nameHashingStrategy, "nameHashingStrategy");
        this.valueValidator = ObjectUtil.checkNotNull(valueValidator, "valueValidator");
        this.entries = new HeaderEntry[MathUtil.findNextPositivePowerOfTwo(Math.max(2, Math.min(arraySizeHint, 128)))];
        this.hashMask = (byte)(this.entries.length - 1);
        this.head = new HeaderEntry<K, V>();
    }
    
    @Override
    public V get(final K name) {
        ObjectUtil.checkNotNull(name, "name");
        final int h = this.hashingStrategy.hashCode(name);
        final int i = this.index(h);
        HeaderEntry<K, V> e = this.entries[i];
        V value = null;
        while (e != null) {
            if (e.hash == h && this.hashingStrategy.equals(name, e.key)) {
                value = e.value;
            }
            e = e.next;
        }
        return value;
    }
    
    @Override
    public V get(final K name, final V defaultValue) {
        final V value = this.get(name);
        if (value == null) {
            return defaultValue;
        }
        return value;
    }
    
    @Override
    public V getAndRemove(final K name) {
        final int h = this.hashingStrategy.hashCode(name);
        return this.remove0(h, this.index(h), ObjectUtil.checkNotNull(name, "name"));
    }
    
    @Override
    public V getAndRemove(final K name, final V defaultValue) {
        final V value = this.getAndRemove(name);
        if (value == null) {
            return defaultValue;
        }
        return value;
    }
    
    @Override
    public List<V> getAll(final K name) {
        ObjectUtil.checkNotNull(name, "name");
        final LinkedList<V> values = new LinkedList<V>();
        final int h = this.hashingStrategy.hashCode(name);
        final int i = this.index(h);
        for (HeaderEntry<K, V> e = this.entries[i]; e != null; e = e.next) {
            if (e.hash == h && this.hashingStrategy.equals(name, e.key)) {
                values.addFirst(e.getValue());
            }
        }
        return values;
    }
    
    public Iterator<V> valueIterator(final K name) {
        return new ValueIterator(name);
    }
    
    @Override
    public List<V> getAllAndRemove(final K name) {
        final List<V> all = this.getAll(name);
        this.remove(name);
        return all;
    }
    
    @Override
    public boolean contains(final K name) {
        return this.get(name) != null;
    }
    
    @Override
    public boolean containsObject(final K name, final Object value) {
        return this.contains(name, this.fromObject(name, value));
    }
    
    @Override
    public boolean containsBoolean(final K name, final boolean value) {
        return this.contains(name, this.fromBoolean(name, value));
    }
    
    @Override
    public boolean containsByte(final K name, final byte value) {
        return this.contains(name, this.fromByte(name, value));
    }
    
    @Override
    public boolean containsChar(final K name, final char value) {
        return this.contains(name, this.fromChar(name, value));
    }
    
    @Override
    public boolean containsShort(final K name, final short value) {
        return this.contains(name, this.fromShort(name, value));
    }
    
    @Override
    public boolean containsInt(final K name, final int value) {
        return this.contains(name, this.fromInt(name, value));
    }
    
    @Override
    public boolean containsLong(final K name, final long value) {
        return this.contains(name, this.fromLong(name, value));
    }
    
    @Override
    public boolean containsFloat(final K name, final float value) {
        return this.contains(name, this.fromFloat(name, value));
    }
    
    @Override
    public boolean containsDouble(final K name, final double value) {
        return this.contains(name, this.fromDouble(name, value));
    }
    
    @Override
    public boolean containsTimeMillis(final K name, final long value) {
        return this.contains(name, this.fromTimeMillis(name, value));
    }
    
    @Override
    public boolean contains(final K name, final V value) {
        return this.contains(name, value, HashingStrategy.JAVA_HASHER);
    }
    
    public final boolean contains(final K name, final V value, final HashingStrategy<? super V> valueHashingStrategy) {
        ObjectUtil.checkNotNull(name, "name");
        final int h = this.hashingStrategy.hashCode(name);
        final int i = this.index(h);
        for (HeaderEntry<K, V> e = this.entries[i]; e != null; e = e.next) {
            if (e.hash == h && this.hashingStrategy.equals(name, e.key) && valueHashingStrategy.equals((Object)value, (Object)e.value)) {
                return true;
            }
        }
        return false;
    }
    
    @Override
    public int size() {
        return this.size;
    }
    
    @Override
    public boolean isEmpty() {
        return this.head == this.head.after;
    }
    
    @Override
    public Set<K> names() {
        if (this.isEmpty()) {
            return Collections.emptySet();
        }
        final Set<K> names = new LinkedHashSet<K>(this.size());
        for (HeaderEntry<K, V> e = this.head.after; e != this.head; e = e.after) {
            names.add(e.getKey());
        }
        return names;
    }
    
    @Override
    public T add(final K name, final V value) {
        this.validateName(this.nameValidator, true, name);
        this.validateValue(this.valueValidator, name, value);
        ObjectUtil.checkNotNull(value, "value");
        final int h = this.hashingStrategy.hashCode(name);
        final int i = this.index(h);
        this.add0(h, i, name, value);
        return this.thisT();
    }
    
    @Override
    public T add(final K name, final Iterable<? extends V> values) {
        this.validateName(this.nameValidator, true, name);
        final int h = this.hashingStrategy.hashCode(name);
        final int i = this.index(h);
        for (final V v : values) {
            this.validateValue(this.valueValidator, name, v);
            this.add0(h, i, name, v);
        }
        return this.thisT();
    }
    
    @Override
    public T add(final K name, final V... values) {
        this.validateName(this.nameValidator, true, name);
        final int h = this.hashingStrategy.hashCode(name);
        final int i = this.index(h);
        for (final V v : values) {
            this.validateValue(this.valueValidator, name, v);
            this.add0(h, i, name, v);
        }
        return this.thisT();
    }
    
    @Override
    public T addObject(final K name, final Object value) {
        return this.add(name, this.fromObject(name, value));
    }
    
    @Override
    public T addObject(final K name, final Iterable<?> values) {
        for (final Object value : values) {
            this.addObject(name, value);
        }
        return this.thisT();
    }
    
    @Override
    public T addObject(final K name, final Object... values) {
        for (final Object value : values) {
            this.addObject(name, value);
        }
        return this.thisT();
    }
    
    @Override
    public T addInt(final K name, final int value) {
        return this.add(name, this.fromInt(name, value));
    }
    
    @Override
    public T addLong(final K name, final long value) {
        return this.add(name, this.fromLong(name, value));
    }
    
    @Override
    public T addDouble(final K name, final double value) {
        return this.add(name, this.fromDouble(name, value));
    }
    
    @Override
    public T addTimeMillis(final K name, final long value) {
        return this.add(name, this.fromTimeMillis(name, value));
    }
    
    @Override
    public T addChar(final K name, final char value) {
        return this.add(name, this.fromChar(name, value));
    }
    
    @Override
    public T addBoolean(final K name, final boolean value) {
        return this.add(name, this.fromBoolean(name, value));
    }
    
    @Override
    public T addFloat(final K name, final float value) {
        return this.add(name, this.fromFloat(name, value));
    }
    
    @Override
    public T addByte(final K name, final byte value) {
        return this.add(name, this.fromByte(name, value));
    }
    
    @Override
    public T addShort(final K name, final short value) {
        return this.add(name, this.fromShort(name, value));
    }
    
    @Override
    public T add(final Headers<? extends K, ? extends V, ?> headers) {
        if (headers == this) {
            throw new IllegalArgumentException("can't add to itself.");
        }
        this.addImpl(headers);
        return this.thisT();
    }
    
    protected void addImpl(final Headers<? extends K, ? extends V, ?> headers) {
        if (headers instanceof DefaultHeaders) {
            final DefaultHeaders<? extends K, ? extends V, T> defaultHeaders = (DefaultHeaders)headers;
            HeaderEntry<? extends K, ? extends V> e = defaultHeaders.head.after;
            if (defaultHeaders.hashingStrategy == this.hashingStrategy && defaultHeaders.nameValidator == this.nameValidator) {
                while (e != defaultHeaders.head) {
                    this.add0(e.hash, this.index(e.hash), e.key, e.value);
                    e = e.after;
                }
            }
            else {
                while (e != defaultHeaders.head) {
                    this.add(e.key, e.value);
                    e = e.after;
                }
            }
        }
        else {
            for (final Map.Entry<? extends K, ? extends V> header : headers) {
                this.add(header.getKey(), header.getValue());
            }
        }
    }
    
    @Override
    public T set(final K name, final V value) {
        this.validateName(this.nameValidator, false, name);
        this.validateValue(this.valueValidator, name, value);
        ObjectUtil.checkNotNull(value, "value");
        final int h = this.hashingStrategy.hashCode(name);
        final int i = this.index(h);
        this.remove0(h, i, name);
        this.add0(h, i, name, value);
        return this.thisT();
    }
    
    @Override
    public T set(final K name, final Iterable<? extends V> values) {
        this.validateName(this.nameValidator, false, name);
        ObjectUtil.checkNotNull(values, "values");
        final int h = this.hashingStrategy.hashCode(name);
        final int i = this.index(h);
        this.remove0(h, i, name);
        for (final V v : values) {
            if (v == null) {
                break;
            }
            this.validateValue(this.valueValidator, name, v);
            this.add0(h, i, name, v);
        }
        return this.thisT();
    }
    
    @Override
    public T set(final K name, final V... values) {
        this.validateName(this.nameValidator, false, name);
        ObjectUtil.checkNotNull(values, "values");
        final int h = this.hashingStrategy.hashCode(name);
        final int i = this.index(h);
        this.remove0(h, i, name);
        for (final V v : values) {
            if (v == null) {
                break;
            }
            this.validateValue(this.valueValidator, name, v);
            this.add0(h, i, name, v);
        }
        return this.thisT();
    }
    
    @Override
    public T setObject(final K name, final Object value) {
        final V convertedValue = ObjectUtil.checkNotNull(this.fromObject(name, value), "convertedValue");
        return this.set(name, convertedValue);
    }
    
    @Override
    public T setObject(final K name, final Iterable<?> values) {
        this.validateName(this.nameValidator, false, name);
        final int h = this.hashingStrategy.hashCode(name);
        final int i = this.index(h);
        this.remove0(h, i, name);
        for (final Object v : values) {
            if (v == null) {
                break;
            }
            final V converted = this.fromObject(name, v);
            this.validateValue(this.valueValidator, name, converted);
            this.add0(h, i, name, converted);
        }
        return this.thisT();
    }
    
    @Override
    public T setObject(final K name, final Object... values) {
        this.validateName(this.nameValidator, false, name);
        final int h = this.hashingStrategy.hashCode(name);
        final int i = this.index(h);
        this.remove0(h, i, name);
        for (final Object v : values) {
            if (v == null) {
                break;
            }
            final V converted = this.fromObject(name, v);
            this.validateValue(this.valueValidator, name, converted);
            this.add0(h, i, name, converted);
        }
        return this.thisT();
    }
    
    @Override
    public T setInt(final K name, final int value) {
        return this.set(name, this.fromInt(name, value));
    }
    
    @Override
    public T setLong(final K name, final long value) {
        return this.set(name, this.fromLong(name, value));
    }
    
    @Override
    public T setDouble(final K name, final double value) {
        return this.set(name, this.fromDouble(name, value));
    }
    
    @Override
    public T setTimeMillis(final K name, final long value) {
        return this.set(name, this.fromTimeMillis(name, value));
    }
    
    @Override
    public T setFloat(final K name, final float value) {
        return this.set(name, this.fromFloat(name, value));
    }
    
    @Override
    public T setChar(final K name, final char value) {
        return this.set(name, this.fromChar(name, value));
    }
    
    @Override
    public T setBoolean(final K name, final boolean value) {
        return this.set(name, this.fromBoolean(name, value));
    }
    
    @Override
    public T setByte(final K name, final byte value) {
        return this.set(name, this.fromByte(name, value));
    }
    
    @Override
    public T setShort(final K name, final short value) {
        return this.set(name, this.fromShort(name, value));
    }
    
    @Override
    public T set(final Headers<? extends K, ? extends V, ?> headers) {
        if (headers != this) {
            this.clear();
            this.addImpl(headers);
        }
        return this.thisT();
    }
    
    @Override
    public T setAll(final Headers<? extends K, ? extends V, ?> headers) {
        if (headers != this) {
            for (final K key : headers.names()) {
                this.remove(key);
            }
            this.addImpl(headers);
        }
        return this.thisT();
    }
    
    @Override
    public boolean remove(final K name) {
        return this.getAndRemove(name) != null;
    }
    
    @Override
    public T clear() {
        Arrays.fill(this.entries, null);
        final HeaderEntry<K, V> head = this.head;
        final HeaderEntry<K, V> head2 = this.head;
        final HeaderEntry<K, V> head3 = this.head;
        head2.after = head3;
        head.before = head3;
        this.size = 0;
        return this.thisT();
    }
    
    @Override
    public Iterator<Map.Entry<K, V>> iterator() {
        return new HeaderIterator();
    }
    
    @Override
    public Boolean getBoolean(final K name) {
        final V v = this.get(name);
        try {
            return (v != null) ? Boolean.valueOf(this.toBoolean(name, v)) : null;
        }
        catch (final RuntimeException ignore) {
            return null;
        }
    }
    
    @Override
    public boolean getBoolean(final K name, final boolean defaultValue) {
        final Boolean v = this.getBoolean(name);
        return (v != null) ? v : defaultValue;
    }
    
    @Override
    public Byte getByte(final K name) {
        final V v = this.get(name);
        try {
            return (v != null) ? Byte.valueOf(this.toByte(name, v)) : null;
        }
        catch (final RuntimeException ignore) {
            return null;
        }
    }
    
    @Override
    public byte getByte(final K name, final byte defaultValue) {
        final Byte v = this.getByte(name);
        return (v != null) ? v : defaultValue;
    }
    
    @Override
    public Character getChar(final K name) {
        final V v = this.get(name);
        try {
            return (v != null) ? Character.valueOf(this.toChar(name, v)) : null;
        }
        catch (final RuntimeException ignore) {
            return null;
        }
    }
    
    @Override
    public char getChar(final K name, final char defaultValue) {
        final Character v = this.getChar(name);
        return (v != null) ? v : defaultValue;
    }
    
    @Override
    public Short getShort(final K name) {
        final V v = this.get(name);
        try {
            return (v != null) ? Short.valueOf(this.toShort(name, v)) : null;
        }
        catch (final RuntimeException ignore) {
            return null;
        }
    }
    
    @Override
    public short getShort(final K name, final short defaultValue) {
        final Short v = this.getShort(name);
        return (v != null) ? v : defaultValue;
    }
    
    @Override
    public Integer getInt(final K name) {
        final V v = this.get(name);
        try {
            return (v != null) ? Integer.valueOf(this.toInt(name, v)) : null;
        }
        catch (final RuntimeException ignore) {
            return null;
        }
    }
    
    @Override
    public int getInt(final K name, final int defaultValue) {
        final Integer v = this.getInt(name);
        return (v != null) ? v : defaultValue;
    }
    
    @Override
    public Long getLong(final K name) {
        final V v = this.get(name);
        try {
            return (v != null) ? Long.valueOf(this.toLong(name, v)) : null;
        }
        catch (final RuntimeException ignore) {
            return null;
        }
    }
    
    @Override
    public long getLong(final K name, final long defaultValue) {
        final Long v = this.getLong(name);
        return (v != null) ? v : defaultValue;
    }
    
    @Override
    public Float getFloat(final K name) {
        final V v = this.get(name);
        try {
            return (v != null) ? Float.valueOf(this.toFloat(name, v)) : null;
        }
        catch (final RuntimeException ignore) {
            return null;
        }
    }
    
    @Override
    public float getFloat(final K name, final float defaultValue) {
        final Float v = this.getFloat(name);
        return (v != null) ? v : defaultValue;
    }
    
    @Override
    public Double getDouble(final K name) {
        final V v = this.get(name);
        try {
            return (v != null) ? Double.valueOf(this.toDouble(name, v)) : null;
        }
        catch (final RuntimeException ignore) {
            return null;
        }
    }
    
    @Override
    public double getDouble(final K name, final double defaultValue) {
        final Double v = this.getDouble(name);
        return (v != null) ? v : defaultValue;
    }
    
    @Override
    public Long getTimeMillis(final K name) {
        final V v = this.get(name);
        try {
            return (v != null) ? Long.valueOf(this.toTimeMillis(name, v)) : null;
        }
        catch (final RuntimeException ignore) {
            return null;
        }
    }
    
    @Override
    public long getTimeMillis(final K name, final long defaultValue) {
        final Long v = this.getTimeMillis(name);
        return (v != null) ? v : defaultValue;
    }
    
    @Override
    public Boolean getBooleanAndRemove(final K name) {
        final V v = this.getAndRemove(name);
        try {
            return (v != null) ? Boolean.valueOf(this.toBoolean(name, v)) : null;
        }
        catch (final RuntimeException ignore) {
            return null;
        }
    }
    
    @Override
    public boolean getBooleanAndRemove(final K name, final boolean defaultValue) {
        final Boolean v = this.getBooleanAndRemove(name);
        return (v != null) ? v : defaultValue;
    }
    
    @Override
    public Byte getByteAndRemove(final K name) {
        final V v = this.getAndRemove(name);
        try {
            return (v != null) ? Byte.valueOf(this.toByte(name, v)) : null;
        }
        catch (final RuntimeException ignore) {
            return null;
        }
    }
    
    @Override
    public byte getByteAndRemove(final K name, final byte defaultValue) {
        final Byte v = this.getByteAndRemove(name);
        return (v != null) ? v : defaultValue;
    }
    
    @Override
    public Character getCharAndRemove(final K name) {
        final V v = this.getAndRemove(name);
        try {
            return (v != null) ? Character.valueOf(this.toChar(name, v)) : null;
        }
        catch (final RuntimeException ignore) {
            return null;
        }
    }
    
    @Override
    public char getCharAndRemove(final K name, final char defaultValue) {
        final Character v = this.getCharAndRemove(name);
        return (v != null) ? v : defaultValue;
    }
    
    @Override
    public Short getShortAndRemove(final K name) {
        final V v = this.getAndRemove(name);
        try {
            return (v != null) ? Short.valueOf(this.toShort(name, v)) : null;
        }
        catch (final RuntimeException ignore) {
            return null;
        }
    }
    
    @Override
    public short getShortAndRemove(final K name, final short defaultValue) {
        final Short v = this.getShortAndRemove(name);
        return (v != null) ? v : defaultValue;
    }
    
    @Override
    public Integer getIntAndRemove(final K name) {
        final V v = this.getAndRemove(name);
        try {
            return (v != null) ? Integer.valueOf(this.toInt(name, v)) : null;
        }
        catch (final RuntimeException ignore) {
            return null;
        }
    }
    
    @Override
    public int getIntAndRemove(final K name, final int defaultValue) {
        final Integer v = this.getIntAndRemove(name);
        return (v != null) ? v : defaultValue;
    }
    
    @Override
    public Long getLongAndRemove(final K name) {
        final V v = this.getAndRemove(name);
        try {
            return (v != null) ? Long.valueOf(this.toLong(name, v)) : null;
        }
        catch (final RuntimeException ignore) {
            return null;
        }
    }
    
    @Override
    public long getLongAndRemove(final K name, final long defaultValue) {
        final Long v = this.getLongAndRemove(name);
        return (v != null) ? v : defaultValue;
    }
    
    @Override
    public Float getFloatAndRemove(final K name) {
        final V v = this.getAndRemove(name);
        try {
            return (v != null) ? Float.valueOf(this.toFloat(name, v)) : null;
        }
        catch (final RuntimeException ignore) {
            return null;
        }
    }
    
    @Override
    public float getFloatAndRemove(final K name, final float defaultValue) {
        final Float v = this.getFloatAndRemove(name);
        return (v != null) ? v : defaultValue;
    }
    
    @Override
    public Double getDoubleAndRemove(final K name) {
        final V v = this.getAndRemove(name);
        try {
            return (v != null) ? Double.valueOf(this.toDouble(name, v)) : null;
        }
        catch (final RuntimeException ignore) {
            return null;
        }
    }
    
    @Override
    public double getDoubleAndRemove(final K name, final double defaultValue) {
        final Double v = this.getDoubleAndRemove(name);
        return (v != null) ? v : defaultValue;
    }
    
    @Override
    public Long getTimeMillisAndRemove(final K name) {
        final V v = this.getAndRemove(name);
        try {
            return (v != null) ? Long.valueOf(this.toTimeMillis(name, v)) : null;
        }
        catch (final RuntimeException ignore) {
            return null;
        }
    }
    
    @Override
    public long getTimeMillisAndRemove(final K name, final long defaultValue) {
        final Long v = this.getTimeMillisAndRemove(name);
        return (v != null) ? v : defaultValue;
    }
    
    @Override
    public boolean equals(final Object o) {
        return o instanceof Headers && this.equals((Headers<K, V, ?>)o, HashingStrategy.JAVA_HASHER);
    }
    
    @Override
    public int hashCode() {
        return this.hashCode(HashingStrategy.JAVA_HASHER);
    }
    
    public final boolean equals(final Headers<K, V, ?> h2, final HashingStrategy<V> valueHashingStrategy) {
        if (h2.size() != this.size()) {
            return false;
        }
        if (this == h2) {
            return true;
        }
        for (final K name : this.names()) {
            final List<V> otherValues = h2.getAll(name);
            final List<V> values = this.getAll(name);
            if (otherValues.size() != values.size()) {
                return false;
            }
            for (int i = 0; i < otherValues.size(); ++i) {
                if (!valueHashingStrategy.equals(otherValues.get(i), values.get(i))) {
                    return false;
                }
            }
        }
        return true;
    }
    
    public final int hashCode(final HashingStrategy<V> valueHashingStrategy) {
        int result = -1028477387;
        for (final K name : this.names()) {
            result = 31 * result + this.hashingStrategy.hashCode(name);
            final List<V> values = this.getAll(name);
            for (int i = 0; i < values.size(); ++i) {
                result = 31 * result + valueHashingStrategy.hashCode(values.get(i));
            }
        }
        return result;
    }
    
    @Override
    public String toString() {
        return HeadersUtils.toString(this.getClass(), this.iterator(), this.size());
    }
    
    protected void validateName(final NameValidator<K> validator, final boolean forAdd, final K name) {
        validator.validateName(name);
    }
    
    protected void validateValue(final ValueValidator<V> validator, final K name, final V value) {
        try {
            validator.validate(value);
        }
        catch (final IllegalArgumentException e) {
            throw new IllegalArgumentException("Validation failed for header '" + name + "'", e);
        }
    }
    
    protected HeaderEntry<K, V> newHeaderEntry(final int h, final K name, final V value, final HeaderEntry<K, V> next) {
        return new HeaderEntry<K, V>(h, name, value, next, this.head);
    }
    
    protected ValueConverter<V> valueConverter() {
        return this.valueConverter;
    }
    
    protected NameValidator<K> nameValidator() {
        return this.nameValidator;
    }
    
    protected ValueValidator<V> valueValidator() {
        return this.valueValidator;
    }
    
    private int index(final int hash) {
        return hash & this.hashMask;
    }
    
    private void add0(final int h, final int i, final K name, final V value) {
        this.entries[i] = this.newHeaderEntry(h, name, value, this.entries[i]);
        ++this.size;
    }
    
    private V remove0(final int h, final int i, final K name) {
        HeaderEntry<K, V> e = this.entries[i];
        if (e == null) {
            return null;
        }
        V value = null;
        for (HeaderEntry<K, V> next = e.next; next != null; next = e.next) {
            if (next.hash == h && this.hashingStrategy.equals(name, next.key)) {
                value = next.value;
                e.next = next.next;
                next.remove();
                --this.size;
            }
            else {
                e = next;
            }
        }
        e = this.entries[i];
        if (e.hash == h && this.hashingStrategy.equals(name, e.key)) {
            if (value == null) {
                value = e.value;
            }
            this.entries[i] = e.next;
            e.remove();
            --this.size;
        }
        return value;
    }
    
    HeaderEntry<K, V> remove0(final HeaderEntry<K, V> entry, HeaderEntry<K, V> previous) {
        final int i = this.index(entry.hash);
        final HeaderEntry<K, V> firstEntry = this.entries[i];
        if (firstEntry == entry) {
            this.entries[i] = entry.next;
            previous = this.entries[i];
        }
        else if (previous == null) {
            previous = firstEntry;
            HeaderEntry<K, V> next;
            for (next = firstEntry.next; next != null && next != entry; next = next.next) {
                previous = next;
            }
            assert next != null : "Entry not found in its hash bucket: " + entry;
            previous.next = entry.next;
        }
        else {
            previous.next = entry.next;
        }
        entry.remove();
        --this.size;
        return previous;
    }
    
    private T thisT() {
        return (T)this;
    }
    
    private V fromObject(final K name, final Object value) {
        try {
            return this.valueConverter.convertObject(ObjectUtil.checkNotNull(value, "value"));
        }
        catch (final IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert object value for header '" + name + '\'', e);
        }
    }
    
    private V fromBoolean(final K name, final boolean value) {
        try {
            return this.valueConverter.convertBoolean(value);
        }
        catch (final IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert boolean value for header '" + name + '\'', e);
        }
    }
    
    private V fromByte(final K name, final byte value) {
        try {
            return this.valueConverter.convertByte(value);
        }
        catch (final IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert byte value for header '" + name + '\'', e);
        }
    }
    
    private V fromChar(final K name, final char value) {
        try {
            return this.valueConverter.convertChar(value);
        }
        catch (final IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert char value for header '" + name + '\'', e);
        }
    }
    
    private V fromShort(final K name, final short value) {
        try {
            return this.valueConverter.convertShort(value);
        }
        catch (final IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert short value for header '" + name + '\'', e);
        }
    }
    
    private V fromInt(final K name, final int value) {
        try {
            return this.valueConverter.convertInt(value);
        }
        catch (final IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert int value for header '" + name + '\'', e);
        }
    }
    
    private V fromLong(final K name, final long value) {
        try {
            return this.valueConverter.convertLong(value);
        }
        catch (final IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert long value for header '" + name + '\'', e);
        }
    }
    
    private V fromFloat(final K name, final float value) {
        try {
            return this.valueConverter.convertFloat(value);
        }
        catch (final IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert float value for header '" + name + '\'', e);
        }
    }
    
    private V fromDouble(final K name, final double value) {
        try {
            return this.valueConverter.convertDouble(value);
        }
        catch (final IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert double value for header '" + name + '\'', e);
        }
    }
    
    private V fromTimeMillis(final K name, final long value) {
        try {
            return this.valueConverter.convertTimeMillis(value);
        }
        catch (final IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert millsecond value for header '" + name + '\'', e);
        }
    }
    
    private boolean toBoolean(final K name, final V value) {
        try {
            return this.valueConverter.convertToBoolean(value);
        }
        catch (final IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert header value to boolean for header '" + name + '\'');
        }
    }
    
    private byte toByte(final K name, final V value) {
        try {
            return this.valueConverter.convertToByte(value);
        }
        catch (final IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert header value to byte for header '" + name + '\'');
        }
    }
    
    private char toChar(final K name, final V value) {
        try {
            return this.valueConverter.convertToChar(value);
        }
        catch (final IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert header value to char for header '" + name + '\'');
        }
    }
    
    private short toShort(final K name, final V value) {
        try {
            return this.valueConverter.convertToShort(value);
        }
        catch (final IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert header value to short for header '" + name + '\'');
        }
    }
    
    private int toInt(final K name, final V value) {
        try {
            return this.valueConverter.convertToInt(value);
        }
        catch (final IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert header value to int for header '" + name + '\'');
        }
    }
    
    private long toLong(final K name, final V value) {
        try {
            return this.valueConverter.convertToLong(value);
        }
        catch (final IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert header value to long for header '" + name + '\'');
        }
    }
    
    private float toFloat(final K name, final V value) {
        try {
            return this.valueConverter.convertToFloat(value);
        }
        catch (final IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert header value to float for header '" + name + '\'');
        }
    }
    
    private double toDouble(final K name, final V value) {
        try {
            return this.valueConverter.convertToDouble(value);
        }
        catch (final IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert header value to double for header '" + name + '\'');
        }
    }
    
    private long toTimeMillis(final K name, final V value) {
        try {
            return this.valueConverter.convertToTimeMillis(value);
        }
        catch (final IllegalArgumentException e) {
            throw new IllegalArgumentException("Failed to convert header value to millsecond for header '" + name + '\'');
        }
    }
    
    public DefaultHeaders<K, V, T> copy() {
        final DefaultHeaders<K, V, T> copy = new DefaultHeaders<K, V, T>(this.hashingStrategy, this.valueConverter, this.nameValidator, this.entries.length);
        copy.addImpl((Headers<? extends K, ? extends V, ?>)this);
        return copy;
    }
    
    public interface NameValidator<K>
    {
        public static final NameValidator NOT_NULL = new NameValidator() {
            @Override
            public void validateName(final Object name) {
                ObjectUtil.checkNotNull(name, "name");
            }
        };
        
        void validateName(final K p0);
    }
    
    public interface ValueValidator<V>
    {
        public static final ValueValidator<?> NO_VALIDATION = new ValueValidator<Object>() {
            @Override
            public void validate(final Object value) {
            }
        };
        
        void validate(final V p0);
    }
    
    private final class HeaderIterator implements Iterator<Map.Entry<K, V>>
    {
        private HeaderEntry<K, V> current;
        
        private HeaderIterator() {
            this.current = DefaultHeaders.this.head;
        }
        
        @Override
        public boolean hasNext() {
            return this.current.after != DefaultHeaders.this.head;
        }
        
        @Override
        public Map.Entry<K, V> next() {
            this.current = this.current.after;
            if (this.current == DefaultHeaders.this.head) {
                throw new NoSuchElementException();
            }
            return this.current;
        }
        
        @Override
        public void remove() {
            throw new UnsupportedOperationException("read only");
        }
    }
    
    private final class ValueIterator implements Iterator<V>
    {
        private final K name;
        private final int hash;
        private HeaderEntry<K, V> removalPrevious;
        private HeaderEntry<K, V> previous;
        private HeaderEntry<K, V> next;
        
        ValueIterator(final K name) {
            this.name = ObjectUtil.checkNotNull(name, "name");
            this.hash = DefaultHeaders.this.hashingStrategy.hashCode(name);
            this.calculateNext(DefaultHeaders.this.entries[DefaultHeaders.this.index(this.hash)]);
        }
        
        @Override
        public boolean hasNext() {
            return this.next != null;
        }
        
        @Override
        public V next() {
            if (!this.hasNext()) {
                throw new NoSuchElementException();
            }
            if (this.previous != null) {
                this.removalPrevious = this.previous;
            }
            this.previous = this.next;
            this.calculateNext(this.next.next);
            return this.previous.value;
        }
        
        @Override
        public void remove() {
            if (this.previous == null) {
                throw new IllegalStateException();
            }
            this.removalPrevious = DefaultHeaders.this.remove0(this.previous, this.removalPrevious);
            this.previous = null;
        }
        
        private void calculateNext(HeaderEntry<K, V> entry) {
            while (entry != null) {
                if (entry.hash == this.hash && DefaultHeaders.this.hashingStrategy.equals(this.name, entry.key)) {
                    this.next = entry;
                    return;
                }
                entry = entry.next;
            }
            this.next = null;
        }
    }
    
    protected static class HeaderEntry<K, V> implements Map.Entry<K, V>
    {
        protected final int hash;
        protected final K key;
        protected V value;
        protected HeaderEntry<K, V> next;
        protected HeaderEntry<K, V> before;
        protected HeaderEntry<K, V> after;
        
        protected HeaderEntry(final int hash, final K key) {
            this.hash = hash;
            this.key = key;
        }
        
        HeaderEntry(final int hash, final K key, final V value, final HeaderEntry<K, V> next, final HeaderEntry<K, V> head) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
            this.after = head;
            this.before = head.before;
            this.pointNeighborsToThis();
        }
        
        HeaderEntry() {
            this.hash = -1;
            this.key = null;
            this.after = this;
            this.before = this;
        }
        
        protected final void pointNeighborsToThis() {
            this.before.after = this;
            this.after.before = this;
        }
        
        public final HeaderEntry<K, V> before() {
            return this.before;
        }
        
        public final HeaderEntry<K, V> after() {
            return this.after;
        }
        
        protected void remove() {
            this.before.after = this.after;
            this.after.before = this.before;
        }
        
        @Override
        public final K getKey() {
            return this.key;
        }
        
        @Override
        public final V getValue() {
            return this.value;
        }
        
        @Override
        public final V setValue(final V value) {
            ObjectUtil.checkNotNull(value, "value");
            final V oldValue = this.value;
            this.value = value;
            return oldValue;
        }
        
        @Override
        public final String toString() {
            return this.key.toString() + '=' + this.value.toString();
        }
        
        @Override
        public boolean equals(final Object o) {
            if (!(o instanceof Map.Entry)) {
                return false;
            }
            final Map.Entry<?, ?> other = (Map.Entry<?, ?>)o;
            if (this.getKey() == null) {
                if (other.getKey() != null) {
                    return false;
                }
            }
            else if (!this.getKey().equals(other.getKey())) {
                return false;
            }
            if ((this.getValue() != null) ? this.getValue().equals(other.getValue()) : (other.getValue() == null)) {
                return true;
            }
            return false;
        }
        
        @Override
        public int hashCode() {
            return ((this.key == null) ? 0 : this.key.hashCode()) ^ ((this.value == null) ? 0 : this.value.hashCode());
        }
    }
}
