/*
 * Copyright © 2018-2019 Apple Inc. and the ServiceTalk project authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.servicetalk.concurrent.api;

import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.function.BiConsumer;
import java.util.function.BiPredicate;
import javax.annotation.Nullable;

import static io.servicetalk.concurrent.api.AsyncContextMapUtils.contextMapToString;
import static java.lang.System.arraycopy;

/**
 * This class provides a Copy-on-Write map behavior is and special cased for cardinality of less than 7 elements. Less
 * than 7 elements was chosen because it is not common to have more than this number of {@link Key}-value pairs in a
 * single {@link AsyncContextMap}. Common {@link Key}-value paris are (tracing, MDC, auth, 3-custom user entries).
 */
final class CopyOnWriteAsyncContextMap implements AsyncContextMap {
    private static final AtomicReferenceFieldUpdater<CopyOnWriteAsyncContextMap, CopyAsyncContextMap> mapUpdater =
            AtomicReferenceFieldUpdater.newUpdater(CopyOnWriteAsyncContextMap.class, CopyAsyncContextMap.class, "map");
    private volatile CopyAsyncContextMap map;

    CopyOnWriteAsyncContextMap() {
        this(EmptyAsyncContextMap.INSTANCE);
    }

    private CopyOnWriteAsyncContextMap(CopyAsyncContextMap map) {
        this.map = map;
    }

    @Nullable
    @Override
    public <T> T get(final Key<T> key) {
        return map.get(key);
    }

    @Override
    public boolean containsKey(final Key<?> key) {
        return map.containsKey(key);
    }

    @Override
    public boolean isEmpty() {
        return map.isEmpty();
    }

    @Override
    public int size() {
        return map.size();
    }

    @Nullable
    @Override
    public <T> T put(final Key<T> key, final T value) {
        return map.put(key, value, this, mapUpdater);
    }

    @Override
    public void putAll(final Map<Key<?>, Object> map) {
        for (;;) {
            CopyAsyncContextMap contextMap = this.map;
            if (mapUpdater.compareAndSet(this, contextMap, contextMap.putAll(map))) {
                break;
            }
        }
    }

    @Nullable
    @Override
    public <T> T remove(final Key<T> key) {
        return map.remove(key, this, mapUpdater);
    }

    @Override
    public boolean removeAll(final Iterable<Key<?>> entries) {
        return map.removeAll(entries, this, mapUpdater);
    }

    @Override
    public void clear() {
        map = EmptyAsyncContextMap.INSTANCE;
    }

    @Nullable
    @Override
    public Key<?> forEach(final BiPredicate<Key<?>, Object> consumer) {
        return map.forEach(consumer);
    }

    @Override
    public AsyncContextMap copy() {
        return new CopyOnWriteAsyncContextMap(map);
    }

    @Override
    public String toString() {
        return contextMapToString(this);
    }

    interface CopyAsyncContextMap {
        @Nullable
        <T> T get(Key<T> key);

        boolean containsKey(Key<?> key);

        boolean isEmpty();

        int size();

        @Nullable
        <T> T put(Key<T> key, T value, CopyOnWriteAsyncContextMap owner,
                  AtomicReferenceFieldUpdater<CopyOnWriteAsyncContextMap, CopyAsyncContextMap> mapUpdater);

        CopyAsyncContextMap putAll(Map<Key<?>, Object> map);

        @Nullable
        <T> T remove(Key<T> key, CopyOnWriteAsyncContextMap owner,
                     AtomicReferenceFieldUpdater<CopyOnWriteAsyncContextMap, CopyAsyncContextMap> mapUpdater);

        boolean removeAll(Iterable<Key<?>> entries, CopyOnWriteAsyncContextMap owner,
                          AtomicReferenceFieldUpdater<CopyOnWriteAsyncContextMap, CopyAsyncContextMap> mapUpdater);

        @Nullable
        Key<?> forEach(BiPredicate<Key<?>, Object> consumer);
    }

    private static final class EmptyAsyncContextMap implements CopyAsyncContextMap {
        static final CopyAsyncContextMap INSTANCE = new EmptyAsyncContextMap();

        private EmptyAsyncContextMap() {
            // singleton
        }

        @Nullable
        @Override
        public <T> T get(final Key<T> key) {
            return null;
        }

        @Override
        public boolean containsKey(final Key<?> key) {
            return false;
        }

        @Override
        public boolean isEmpty() {
            return true;
        }

        @Override
        public int size() {
            return 0;
        }

        @Nullable
        @Override
        public <T> T put(final Key<T> key, final T value,
                         CopyOnWriteAsyncContextMap owner,
                         AtomicReferenceFieldUpdater<CopyOnWriteAsyncContextMap, CopyAsyncContextMap> mapUpdater) {
            return mapUpdater.compareAndSet(owner, this, new OneAsyncContextMap(key, value)) ? null :
                    owner.put(key, value);
        }

        @Override
        public CopyAsyncContextMap putAll(final Map<Key<?>, Object> map) {
            switch (map.size()) {
                case 0:
                    return this;
                case 1: {
                    OneAsyncContextMap newMap = new OneAsyncContextMap();
                    map.forEach(newMap);
                    return newMap;
                }
                case 2: {
                    TwoAsyncContextMap newMap = new TwoAsyncContextMap();
                    map.forEach(newMap);
                    return newMap;
                }
                case 3: {
                    ThreeAsyncContextMap newMap = new ThreeAsyncContextMap();
                    map.forEach(newMap);
                    return newMap;
                }
                case 4: {
                    FourAsyncContextMap newMap = new FourAsyncContextMap();
                    map.forEach(newMap);
                    return newMap;
                }
                case 5: {
                    FiveAsyncContextMap newMap = new FiveAsyncContextMap();
                    map.forEach(newMap);
                    return newMap;
                }
                case 6:
                    SixAsyncContextMap newMap = new SixAsyncContextMap();
                    map.forEach(newMap);
                    return newMap;
                default:
                    return new SevenOrMoreAsyncContextMap().putAll(map);
            }
        }

        @Nullable
        @Override
        public <T> T remove(final Key<T> key,
                            CopyOnWriteAsyncContextMap owner,
                            AtomicReferenceFieldUpdater<CopyOnWriteAsyncContextMap, CopyAsyncContextMap> mapUpdater) {
            return null;
        }

        @Override
        public boolean removeAll(final Iterable<Key<?>> entries,
                                 CopyOnWriteAsyncContextMap owner,
                            AtomicReferenceFieldUpdater<CopyOnWriteAsyncContextMap, CopyAsyncContextMap> mapUpdater) {
            return false;
        }

        @Nullable
        @Override
        public Key<?> forEach(final BiPredicate<Key<?>, Object> consumer) {
            return null;
        }
    }

    private static final class OneAsyncContextMap implements CopyAsyncContextMap, BiConsumer<Key<?>, Object>,
                                                             BiPredicate<Key<?>, Object> {
        @Nullable
        private Key<?> key;
        @Nullable
        private Object value;

        OneAsyncContextMap() {
        }

        OneAsyncContextMap(Key<?> key, @Nullable Object value) {
            this.key = key;
            this.value = value;
        }

        @SuppressWarnings("unchecked")
        @Nullable
        @Override
        public <T> T get(final Key<T> key) {
            assert this.key != null;
            return this.key.equals(key) ? (T) value : null;
        }

        @Override
        public boolean containsKey(final Key<?> key) {
            assert this.key != null;
            return this.key.equals(key);
        }

        @Override
        public boolean isEmpty() {
            return false;
        }

        @Override
        public int size() {
            return 1;
        }

        @SuppressWarnings("unchecked")
        @Override
        public <T> T put(final Key<T> key, final T value, CopyOnWriteAsyncContextMap owner,
                         AtomicReferenceFieldUpdater<CopyOnWriteAsyncContextMap, CopyAsyncContextMap> mapUpdater) {
            assert this.key != null;
            if (this.key.equals(key)) {
                return mapUpdater.compareAndSet(owner, this, new OneAsyncContextMap(this.key, value)) ? (T) this.value :
                        owner.put(key, value);
            }
            return mapUpdater.compareAndSet(owner, this, new TwoAsyncContextMap(this.key, this.value, key, value)) ?
                    null : owner.put(key, value);
        }

        @Override
        public CopyAsyncContextMap putAll(final Map<Key<?>, Object> map) {
            assert key != null;
            switch (map.size()) {
                case 0:
                    return this;
                case 1: {
                    PutTwoBuilder builder = new PutTwoBuilder(key, value);
                    map.forEach(builder);
                    return builder.build();
                }
                case 2: {
                    PutThreeBuilder builder = new PutThreeBuilder(key, value);
                    map.forEach(builder);
                    return builder.build();
                }
                case 3: {
                    PutFourBuilder builder = new PutFourBuilder(key, value);
                    map.forEach(builder);
                    return builder.build();
                }
                case 4: {
                    PutFiveBuilder builder = new PutFiveBuilder(key, value);
                    map.forEach(builder);
                    return builder.build();
                }
                case 5:
                    PutSixBuilder builder = new PutSixBuilder(key, value);
                    map.forEach(builder);
                    return builder.build();
                default:
                    return new SevenOrMoreAsyncContextMap(key, value).putAll(map);
            }
        }

        @SuppressWarnings("unchecked")
        @Nullable
        @Override
        public <T> T remove(final Key<T> key, CopyOnWriteAsyncContextMap owner,
                            AtomicReferenceFieldUpdater<CopyOnWriteAsyncContextMap, CopyAsyncContextMap> mapUpdater) {
            assert this.key != null;
            if (this.key.equals(key)) {
                return mapUpdater.compareAndSet(owner, this, EmptyAsyncContextMap.INSTANCE) ?
                        (T) value : owner.remove(key);
            }
            return null;
        }

        @Override
        public boolean removeAll(final Iterable<Key<?>> entries, CopyOnWriteAsyncContextMap owner,
                             AtomicReferenceFieldUpdater<CopyOnWriteAsyncContextMap, CopyAsyncContextMap> mapUpdater) {
            assert this.key != null;
            MutableInt index = new MutableInt();
            entries.forEach(key -> {
                if (this.key.equals(key)) {
                    index.value = 1;
                }
            });
            if (index.value != 0) {
                return mapUpdater.compareAndSet(owner, this, EmptyAsyncContextMap.INSTANCE) || owner.removeAll(entries);
            }
            return false;
        }

        @Nullable
        @Override
        public Key<?> forEach(final BiPredicate<Key<?>, Object> consumer) {
            return consumer.test(key, value) ? null : key;
        }

        @Override
        public void accept(final Key<?> key, final Object value) {
            test(key, value);
        }

        @Override
        public boolean test(final Key<?> key, final Object o) {
            assert this.key == null;
            this.key = key;
            this.value = o;
            return false;
        }

        private static final class PutTwoBuilder implements BiPredicate<Key<?>, Object>, BiConsumer<Key<?>, Object> {
            private final Key<?> keyOne;
            @Nullable
            private Object valueOne;
            @Nullable
            private Key<?> keyTwo;
            @Nullable
            private Object valueTwo;

            PutTwoBuilder(Key<?> keyOne, @Nullable Object valueOne) {
                this.keyOne = keyOne;
                this.valueOne = valueOne;
            }

            @Override
            public void accept(final Key<?> key, final Object o) {
                test(key, o);
            }

            @Override
            public boolean test(final Key<?> key, final Object o) {
                if (keyOne.equals(key)) {
                    valueOne = o;
                } else {
                    assert keyTwo == null;
                    keyTwo = key;
                    valueTwo = o;
                    return false;
                }
                return true;
            }

            CopyAsyncContextMap build() {
                if (keyTwo == null) {
                    return new OneAsyncContextMap(keyOne, valueOne);
                }
                return new TwoAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo);
            }
        }

        private static final class PutThreeBuilder implements BiPredicate<Key<?>, Object>, BiConsumer<Key<?>, Object> {
            private final Key<?> keyOne;
            @Nullable
            private Object valueOne;
            @Nullable
            private Key<?> keyTwo;
            @Nullable
            private Object valueTwo;
            @Nullable
            private Key<?> keyThree;
            @Nullable
            private Object valueThree;

            PutThreeBuilder(Key<?> keyOne, @Nullable Object valueOne) {
                this.keyOne = keyOne;
                this.valueOne = valueOne;
            }

            @Override
            public void accept(final Key<?> key, final Object o) {
                test(key, o);
            }

            @Override
            public boolean test(final Key<?> key, final Object o) {
                if (keyOne.equals(key)) {
                    valueOne = o;
                } else if (keyTwo == null) {
                    keyTwo = key;
                    valueTwo = o;
                } else {
                    assert keyThree == null;
                    keyThree = key;
                    valueThree = o;
                    return false;
                }
                return true;
            }

            CopyAsyncContextMap build() {
                assert keyTwo != null;
                if (keyThree == null) {
                    return new TwoAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo);
                }
                return new ThreeAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree);
            }
        }

        private static final class PutFourBuilder implements BiPredicate<Key<?>, Object>, BiConsumer<Key<?>, Object> {
            private final Key<?> keyOne;
            @Nullable
            private Object valueOne;
            @Nullable
            private Key<?> keyTwo;
            @Nullable
            private Object valueTwo;
            @Nullable
            private Key<?> keyThree;
            @Nullable
            private Object valueThree;
            @Nullable
            private Key<?> keyFour;
            @Nullable
            private Object valueFour;

            PutFourBuilder(Key<?> keyOne, @Nullable Object valueOne) {
                this.keyOne = keyOne;
                this.valueOne = valueOne;
            }

            @Override
            public void accept(final Key<?> key, final Object o) {
                test(key, o);
            }

            @Override
            public boolean test(final Key<?> key, final Object o) {
                if (keyOne.equals(key)) {
                    valueOne = o;
                } else if (keyTwo == null) {
                    keyTwo = key;
                    valueTwo = o;
                } else if (keyThree == null) {
                    keyThree = key;
                    valueThree = o;
                } else {
                    assert keyFour == null;
                    keyFour = key;
                    valueFour = o;
                    return false;
                }
                return true;
            }

            CopyAsyncContextMap build() {
                assert keyTwo != null && keyThree != null;
                if (keyFour == null) {
                    return new ThreeAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree);
                }
                return new FourAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree,
                        keyFour, valueFour);
            }
        }

        private static final class PutFiveBuilder implements BiPredicate<Key<?>, Object>, BiConsumer<Key<?>, Object> {
            private final Key<?> keyOne;
            @Nullable
            private Object valueOne;
            @Nullable
            private Key<?> keyTwo;
            @Nullable
            private Object valueTwo;
            @Nullable
            private Key<?> keyThree;
            @Nullable
            private Object valueThree;
            @Nullable
            private Key<?> keyFour;
            @Nullable
            private Object valueFour;
            @Nullable
            private Key<?> keyFive;
            @Nullable
            private Object valueFive;

            PutFiveBuilder(Key<?> keyOne, @Nullable Object valueOne) {
                this.keyOne = keyOne;
                this.valueOne = valueOne;
            }

            @Override
            public void accept(final Key<?> key, final Object o) {
                test(key, o);
            }

            @Override
            public boolean test(final Key<?> key, final Object o) {
                if (keyOne.equals(key)) {
                    valueOne = o;
                } else if (keyTwo == null) {
                    keyTwo = key;
                    valueTwo = o;
                } else if (keyThree == null) {
                    keyThree = key;
                    valueThree = o;
                } else if (keyFour == null) {
                    keyFour = key;
                    valueFour = o;
                } else {
                    assert keyFive == null;
                    keyFive = key;
                    valueFive = o;
                    return false;
                }
                return true;
            }

            CopyAsyncContextMap build() {
                assert keyTwo != null && keyThree != null && keyFour != null;
                if (keyFive == null) {
                    return new FourAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree,
                            keyFour, valueFour);
                }
                return new FiveAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree,
                        keyFour, valueFour, keyFive, valueFive);
            }
        }

        private static final class PutSixBuilder implements BiPredicate<Key<?>, Object>, BiConsumer<Key<?>, Object> {
            private final Key<?> keyOne;
            @Nullable
            private Object valueOne;
            @Nullable
            private Key<?> keyTwo;
            @Nullable
            private Object valueTwo;
            @Nullable
            private Key<?> keyThree;
            @Nullable
            private Object valueThree;
            @Nullable
            private Key<?> keyFour;
            @Nullable
            private Object valueFour;
            @Nullable
            private Key<?> keyFive;
            @Nullable
            private Object valueFive;
            @Nullable
            private Key<?> keySix;
            @Nullable
            private Object valueSix;

            PutSixBuilder(Key<?> keyOne, @Nullable Object valueOne) {
                this.keyOne = keyOne;
                this.valueOne = valueOne;
            }

            @Override
            public void accept(final Key<?> key, final Object o) {
                test(key, o);
            }

            @Override
            public boolean test(final Key<?> key, final Object o) {
                if (keyOne.equals(key)) {
                    valueOne = o;
                } else if (keyTwo == null) {
                    keyTwo = key;
                    valueTwo = o;
                } else if (keyThree == null) {
                    keyThree = key;
                    valueThree = o;
                } else if (keyFour == null) {
                    keyFour = key;
                    valueFour = o;
                } else if (keyFive == null) {
                    keyFive = key;
                    valueFive = o;
                } else {
                    assert keySix == null;
                    keySix = key;
                    valueSix = o;
                    return false;
                }
                return true;
            }

            CopyAsyncContextMap build() {
                assert keyTwo != null && keyThree != null && keyFour != null && keyFive != null;
                if (keySix == null) {
                    return new FiveAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree,
                            keyFour, valueFour, keyFive, valueFive);
                }
                return new SixAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree,
                        keyFour, valueFour, keyFive, valueFive, keySix, valueSix);
            }
        }
    }

    private static final class TwoAsyncContextMap implements CopyAsyncContextMap, BiConsumer<Key<?>, Object>,
                                                             BiPredicate<Key<?>, Object> {
        @Nullable
        private Key<?> keyOne;
        @Nullable
        private Object valueOne;
        @Nullable
        private Key<?> keyTwo;
        @Nullable
        private Object valueTwo;

        TwoAsyncContextMap() {
        }

        TwoAsyncContextMap(Key<?> keyOne, @Nullable Object valueOne, Key<?> keyTwo, @Nullable Object valueTwo) {
            this.keyOne = keyOne;
            this.valueOne = valueOne;
            this.keyTwo = keyTwo;
            this.valueTwo = valueTwo;
        }

        @SuppressWarnings("unchecked")
        @Nullable
        @Override
        public <T> T get(final Key<T> key) {
            assert keyOne != null && keyTwo != null;
            return keyOne.equals(key) ? (T) valueOne : keyTwo.equals(key) ? (T) valueTwo : null;
        }

        @Override
        public boolean containsKey(final Key<?> key) {
            assert keyOne != null && keyTwo != null;
            return keyOne.equals(key) || keyTwo.equals(key);
        }

        @Override
        public boolean isEmpty() {
            return false;
        }

        @Override
        public int size() {
            return 2;
        }

        @SuppressWarnings("unchecked")
        @Override
        public <T> T put(final Key<T> key, final T value, CopyOnWriteAsyncContextMap owner,
                         AtomicReferenceFieldUpdater<CopyOnWriteAsyncContextMap, CopyAsyncContextMap> mapUpdater) {
            assert keyOne != null && keyTwo != null;
            if (keyOne.equals(key)) {
                return mapUpdater.compareAndSet(owner, this, new TwoAsyncContextMap(keyOne, value, keyTwo, valueTwo)) ?
                        (T) valueOne : owner.put(key, value);
            } else if (keyTwo.equals(key)) {
                return mapUpdater.compareAndSet(owner, this, new TwoAsyncContextMap(keyOne, valueOne, keyTwo, value)) ?
                        (T) valueTwo : owner.put(key, value);
            }
            return mapUpdater.compareAndSet(owner, this,
                    new ThreeAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, key, value)) ?
                    null : owner.put(key, value);
        }

        @Override
        public CopyAsyncContextMap putAll(final Map<Key<?>, Object> map) {
            assert keyOne != null && keyTwo != null;
            switch (map.size()) {
                case 0:
                    return this;
                case 1: {
                    PutThreeBuilder builder = new PutThreeBuilder(keyOne, valueOne, keyTwo, valueTwo);
                    map.forEach(builder);
                    return builder.build();
                }
                case 2: {
                    PutFourBuilder builder = new PutFourBuilder(keyOne, valueOne, keyTwo, valueTwo);
                    map.forEach(builder);
                    return builder.build();
                }
                case 3: {
                    PutFiveBuilder builder = new PutFiveBuilder(keyOne, valueOne, keyTwo, valueTwo);
                    map.forEach(builder);
                    return builder.build();
                }
                case 4: {
                    PutSixBuilder builder = new PutSixBuilder(keyOne, valueOne, keyTwo, valueTwo);
                    map.forEach(builder);
                    return builder.build();
                }
                default:
                    PutSevenBuilder builder = new PutSevenBuilder(keyOne, valueOne, keyTwo, valueTwo, map.size());
                    map.forEach(builder);
                    return builder.build();
            }
        }

        @SuppressWarnings("unchecked")
        @Nullable
        @Override
        public <T> T remove(final Key<T> key, CopyOnWriteAsyncContextMap owner,
                            AtomicReferenceFieldUpdater<CopyOnWriteAsyncContextMap, CopyAsyncContextMap> mapUpdater) {
            assert keyOne != null && keyTwo != null;
            if (keyOne.equals(key)) {
                return mapUpdater.compareAndSet(owner, this, new OneAsyncContextMap(keyTwo, valueTwo)) ?
                        (T) valueOne : owner.remove(key);
            }
            if (keyTwo.equals(key)) {
                return mapUpdater.compareAndSet(owner, this, new OneAsyncContextMap(keyOne, valueOne)) ?
                        (T) valueTwo : owner.remove(key);
            }
            return null;
        }

        @Override
        public boolean removeAll(final Iterable<Key<?>> entries, CopyOnWriteAsyncContextMap owner,
                             AtomicReferenceFieldUpdater<CopyOnWriteAsyncContextMap, CopyAsyncContextMap> mapUpdater) {
            assert keyOne != null && keyTwo != null;
            MutableInt removeIndexMask = new MutableInt();
            entries.forEach(k -> {
                if (keyOne.equals(k)) {
                    removeIndexMask.value |= 0x1;
                } else if (keyTwo.equals(k)) {
                    removeIndexMask.value |= 0x2;
                }
            });

            if ((removeIndexMask.value & 0x3) == 0x3) {
                return mapUpdater.compareAndSet(owner, this, EmptyAsyncContextMap.INSTANCE) || owner.removeAll(entries);
            } else if ((removeIndexMask.value & 0x2) == 0x2) {
                return mapUpdater.compareAndSet(owner, this, new OneAsyncContextMap(keyOne, valueOne)) ||
                        owner.removeAll(entries);
            } else if ((removeIndexMask.value & 0x1) == 0x1) {
                return mapUpdater.compareAndSet(owner, this, new OneAsyncContextMap(keyTwo, valueTwo)) ||
                        owner.removeAll(entries);
            }
            return false;
        }

        @Nullable
        @Override
        public Key<?> forEach(final BiPredicate<Key<?>, Object> consumer) {
            if (!consumer.test(keyOne, valueOne)) {
                return keyOne;
            }
            return consumer.test(keyTwo, valueTwo) ? null : keyTwo;
        }

        @Override
        public void accept(final Key<?> key, final Object value) {
            test(key, value);
        }

        @Override
        public boolean test(final Key<?> key, final Object value) {
            if (keyOne == null) {
                keyOne = key;
                valueOne = value;
            } else {
                assert keyTwo == null;
                keyTwo = key;
                valueTwo = value;
                return false;
            }
            return true;
        }

        private static final class PutThreeBuilder implements BiPredicate<Key<?>, Object>, BiConsumer<Key<?>, Object> {
            private final Key<?> keyOne;
            @Nullable
            private Object valueOne;
            private final Key<?> keyTwo;
            @Nullable
            private Object valueTwo;
            @Nullable
            private Key<?> keyThree;
            @Nullable
            private Object valueThree;

            PutThreeBuilder(Key<?> keyOne, @Nullable Object valueOne, Key<?> keyTwo, @Nullable Object valueTwo) {
                this.keyOne = keyOne;
                this.valueOne = valueOne;
                this.keyTwo = keyTwo;
                this.valueTwo = valueTwo;
            }

            @Override
            public boolean test(final Key<?> key, final Object o) {
                if (keyOne.equals(key)) {
                    valueOne = o;
                } else if (keyTwo.equals(key)) {
                    valueTwo = o;
                } else {
                    assert keyThree == null;
                    keyThree = key;
                    valueThree = o;
                    return false;
                }
                return true;
            }

            CopyAsyncContextMap build() {
                return (keyThree == null) ? new TwoAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo) :
                        new ThreeAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree);
            }

            @Override
            public void accept(final Key<?> key, final Object o) {
                test(key, o);
            }
        }

        private static final class PutFourBuilder implements BiPredicate<Key<?>, Object>, BiConsumer<Key<?>, Object> {
            private final Key<?> keyOne;
            @Nullable
            private Object valueOne;
            private final Key<?> keyTwo;
            @Nullable
            private Object valueTwo;
            @Nullable
            private Key<?> keyThree;
            @Nullable
            private Object valueThree;
            @Nullable
            private Key<?> keyFour;
            @Nullable
            private Object valueFour;

            PutFourBuilder(Key<?> keyOne, @Nullable Object valueOne, Key<?> keyTwo, @Nullable Object valueTwo) {
                this.keyOne = keyOne;
                this.valueOne = valueOne;
                this.keyTwo = keyTwo;
                this.valueTwo = valueTwo;
            }

            @Override
            public boolean test(final Key<?> key, final Object o) {
                if (keyOne.equals(key)) {
                    valueOne = o;
                } else if (keyTwo.equals(key)) {
                    valueTwo = o;
                } else if (keyThree == null) {
                    keyThree = key;
                    valueThree = o;
                } else {
                    assert keyFour == null;
                    keyFour = key;
                    valueFour = o;
                    return false;
                }
                return true;
            }

            CopyAsyncContextMap build() {
                if (keyThree == null) {
                    return new TwoAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo);
                }
                if (keyFour == null) {
                    return new ThreeAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree);
                }
                return new FourAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree,
                        keyFour, valueFour);
            }

            @Override
            public void accept(final Key<?> key, final Object o) {
                test(key, o);
            }
        }

        private static final class PutFiveBuilder implements BiPredicate<Key<?>, Object>, BiConsumer<Key<?>, Object> {
            private final Key<?> keyOne;
            @Nullable
            private Object valueOne;
            private final Key<?> keyTwo;
            @Nullable
            private Object valueTwo;
            @Nullable
            private Key<?> keyThree;
            @Nullable
            private Object valueThree;
            @Nullable
            private Key<?> keyFour;
            @Nullable
            private Object valueFour;
            @Nullable
            private Key<?> keyFive;
            @Nullable
            private Object valueFive;

            PutFiveBuilder(Key<?> keyOne, @Nullable Object valueOne, Key<?> keyTwo, @Nullable Object valueTwo) {
                this.keyOne = keyOne;
                this.valueOne = valueOne;
                this.keyTwo = keyTwo;
                this.valueTwo = valueTwo;
            }

            @Override
            public boolean test(final Key<?> key, final Object o) {
                if (keyOne.equals(key)) {
                    valueOne = o;
                } else if (keyTwo.equals(key)) {
                    valueTwo = o;
                } else if (keyThree == null) {
                    keyThree = key;
                    valueThree = o;
                } else if (keyFour == null) {
                    keyFour = key;
                    valueFour = o;
                } else {
                    assert keyFive == null;
                    keyFive = key;
                    valueFive = o;
                    return false;
                }
                return true;
            }

            CopyAsyncContextMap build() {
                if (keyThree == null) {
                    return new TwoAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo);
                }
                if (keyFour == null) {
                    return new ThreeAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree);
                }
                if (keyFive == null) {
                    return new FourAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree,
                            keyFour, valueFour);
                }
                return new FiveAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree,
                        keyFour, valueFour, keyFive, valueFive);
            }

            @Override
            public void accept(final Key<?> key, final Object o) {
                test(key, o);
            }
        }

        private static final class PutSixBuilder implements BiPredicate<Key<?>, Object>, BiConsumer<Key<?>, Object> {
            private final Key<?> keyOne;
            @Nullable
            private Object valueOne;
            private final Key<?> keyTwo;
            @Nullable
            private Object valueTwo;
            @Nullable
            private Key<?> keyThree;
            @Nullable
            private Object valueThree;
            @Nullable
            private Key<?> keyFour;
            @Nullable
            private Object valueFour;
            @Nullable
            private Key<?> keyFive;
            @Nullable
            private Object valueFive;
            @Nullable
            private Key<?> keySix;
            @Nullable
            private Object valueSix;

            PutSixBuilder(Key<?> keyOne, @Nullable Object valueOne, Key<?> keyTwo, @Nullable Object valueTwo) {
                this.keyOne = keyOne;
                this.valueOne = valueOne;
                this.keyTwo = keyTwo;
                this.valueTwo = valueTwo;
            }

            @Override
            public boolean test(final Key<?> key, final Object o) {
                if (keyOne.equals(key)) {
                    valueOne = o;
                } else if (keyTwo.equals(key)) {
                    valueTwo = o;
                } else if (keyThree == null) {
                    keyThree = key;
                    valueThree = o;
                } else if (keyFour == null) {
                    keyFour = key;
                    valueFour = o;
                } else if (keyFive == null) {
                    keyFive = key;
                    valueFive = o;
                } else {
                    assert keySix == null;
                    keySix = key;
                    valueSix = o;
                    return false;
                }
                return true;
            }

            CopyAsyncContextMap build() {
                if (keyThree == null) {
                    return new TwoAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo);
                }
                if (keyFour == null) {
                    return new ThreeAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree);
                }
                if (keyFive == null) {
                    return new FourAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree,
                            keyFour, valueFour);
                }
                if (keySix == null) {
                    return new FiveAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree,
                            keyFour, valueFour, keyFive, valueFive);
                }
                return new SixAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree,
                        keyFour, valueFour, keyFive, valueFive, keySix, valueSix);
            }

            @Override
            public void accept(final Key<?> key, final Object o) {
                test(key, o);
            }
        }

        private static final class PutSevenBuilder extends AbstractPutSevenBuilder {
            PutSevenBuilder(Key<?> keyOne, @Nullable Object valueOne, Key<?> keyTwo, @Nullable Object valueTwo,
                            int putAllMapSize) {
                super((2 + putAllMapSize) << 1, 4);
                pairs[0] = keyOne;
                pairs[1] = valueOne;
                pairs[2] = keyTwo;
                pairs[3] = valueTwo;
            }

            CopyAsyncContextMap build() {
                if (nextIndex == 4) {
                    return new TwoAsyncContextMap((Key<?>) pairs[0], pairs[1], (Key<?>) pairs[2], pairs[3]);
                }
                if (nextIndex == 6) {
                    return new ThreeAsyncContextMap((Key<?>) pairs[0], pairs[1], (Key<?>) pairs[2], pairs[3],
                            (Key<?>) pairs[4], pairs[5]);
                }
                if (nextIndex == 8) {
                    return new FourAsyncContextMap((Key<?>) pairs[0], pairs[1], (Key<?>) pairs[2], pairs[3],
                            (Key<?>) pairs[4], pairs[5], (Key<?>) pairs[6], pairs[7]);
                }
                if (nextIndex == 10) {
                    return new FiveAsyncContextMap((Key<?>) pairs[0], pairs[1], (Key<?>) pairs[2], pairs[3],
                            (Key<?>) pairs[4], pairs[5], (Key<?>) pairs[6], pairs[7], (Key<?>) pairs[8], pairs[9]);
                }
                if (nextIndex == 12) {
                    return new SixAsyncContextMap((Key<?>) pairs[0], pairs[1], (Key<?>) pairs[2], pairs[3],
                            (Key<?>) pairs[4], pairs[5], (Key<?>) pairs[6], pairs[7], (Key<?>) pairs[8], pairs[9],
                            (Key<?>) pairs[10], pairs[11]);
                }
                if (nextIndex == pairs.length) {
                    return new SevenOrMoreAsyncContextMap(pairs);
                }
                return new SevenOrMoreAsyncContextMap(Arrays.copyOf(pairs, nextIndex));
            }
        }
    }

    private static final class ThreeAsyncContextMap implements CopyAsyncContextMap, BiConsumer<Key<?>, Object>,
                                                               BiPredicate<Key<?>, Object> {
        @Nullable
        private Key<?> keyOne;
        @Nullable
        private Object valueOne;
        @Nullable
        private Key<?> keyTwo;
        @Nullable
        private Object valueTwo;
        @Nullable
        private Key<?> keyThree;
        @Nullable
        private Object valueThree;

        ThreeAsyncContextMap() {
        }

        ThreeAsyncContextMap(Key<?> keyOne, @Nullable Object valueOne, Key<?> keyTwo, @Nullable Object valueTwo,
                             Key<?> keyThree, @Nullable Object valueThree) {
            this.keyOne = keyOne;
            this.valueOne = valueOne;
            this.keyTwo = keyTwo;
            this.valueTwo = valueTwo;
            this.keyThree = keyThree;
            this.valueThree = valueThree;
        }

        @SuppressWarnings("unchecked")
        @Nullable
        @Override
        public <T> T get(final Key<T> key) {
            assert keyOne != null && keyTwo != null && keyThree != null;
            return keyOne.equals(key) ? (T) valueOne : keyTwo.equals(key) ? (T) valueTwo : keyThree.equals(key) ?
                    (T) valueThree : null;
        }

        @Override
        public boolean containsKey(final Key<?> key) {
            assert keyOne != null && keyTwo != null && keyThree != null;
            return keyOne.equals(key) || keyTwo.equals(key) || keyThree.equals(key);
        }

        @Override
        public boolean isEmpty() {
            return false;
        }

        @Override
        public int size() {
            return 3;
        }

        @SuppressWarnings("unchecked")
        @Nullable
        @Override
        public <T> T put(final Key<T> key, final T value, CopyOnWriteAsyncContextMap owner,
                         AtomicReferenceFieldUpdater<CopyOnWriteAsyncContextMap, CopyAsyncContextMap> mapUpdater) {
            assert keyOne != null && keyTwo != null && keyThree != null;
            if (keyOne.equals(key)) {
                return mapUpdater.compareAndSet(owner, this,
                        new ThreeAsyncContextMap(keyOne, value, keyTwo, valueTwo, keyThree, valueThree)) ?
                            (T) valueOne : owner.put(key, value);
            } else if (keyTwo.equals(key)) {
                return mapUpdater.compareAndSet(owner, this,
                        new ThreeAsyncContextMap(keyOne, valueOne, keyTwo, value, keyThree, valueThree)) ?
                            (T) valueTwo : owner.put(key, value);
            } else if (keyThree.equals(key)) {
                return mapUpdater.compareAndSet(owner, this,
                        new ThreeAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, value)) ?
                            (T) valueThree : owner.put(key, value);
            }
            return mapUpdater.compareAndSet(owner, this,
                    new FourAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree, key, value)) ?
                        null : owner.put(key, value);
        }

        @Override
        public CopyAsyncContextMap putAll(final Map<Key<?>, Object> map) {
            assert keyOne != null && keyTwo != null && keyThree != null;
            switch (map.size()) {
                case 0:
                    return this;
                case 1: {
                    PutFourBuilder builder = new PutFourBuilder(keyOne, valueOne, keyTwo, valueTwo,
                            keyThree, valueThree);
                    map.forEach(builder);
                    return builder.build();
                }
                case 2: {
                    PutFiveBuilder builder = new PutFiveBuilder(keyOne, valueOne, keyTwo, valueTwo,
                            keyThree, valueThree);
                    map.forEach(builder);
                    return builder.build();
                }
                case 3: {
                    PutSixBuilder builder = new PutSixBuilder(keyOne, valueOne, keyTwo, valueTwo,
                            keyThree, valueThree);
                    map.forEach(builder);
                    return builder.build();
                }
                default:
                    PutSevenBuilder builder = new PutSevenBuilder(keyOne, valueOne, keyTwo, valueTwo,
                            keyThree, valueThree, map.size());
                    map.forEach(builder);
                    return builder.build();
            }
        }

        @SuppressWarnings("unchecked")
        @Nullable
        @Override
        public <T> T remove(final Key<T> key, CopyOnWriteAsyncContextMap owner,
                            AtomicReferenceFieldUpdater<CopyOnWriteAsyncContextMap, CopyAsyncContextMap> mapUpdater) {
            assert keyOne != null && keyTwo != null && keyThree != null;
            if (keyOne.equals(key)) {
                return mapUpdater.compareAndSet(owner, this,
                        new TwoAsyncContextMap(keyTwo, valueTwo, keyThree, valueThree)) ?
                            (T) valueOne : owner.remove(key);
            }
            if (keyTwo.equals(key)) {
                return mapUpdater.compareAndSet(owner, this,
                        new TwoAsyncContextMap(keyOne, valueOne, keyThree, valueThree)) ?
                            (T) valueTwo : owner.remove(key);
            }
            if (keyThree.equals(key)) {
                return mapUpdater.compareAndSet(owner, this,
                        new TwoAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo)) ?
                        (T) valueThree : owner.remove(key);
            }
            return null;
        }

        @Override
        public boolean removeAll(final Iterable<Key<?>> entries, CopyOnWriteAsyncContextMap owner,
                             AtomicReferenceFieldUpdater<CopyOnWriteAsyncContextMap, CopyAsyncContextMap> mapUpdater) {
            assert keyOne != null && keyTwo != null && keyThree != null;
            MutableInt removeIndexMask = new MutableInt();
            entries.forEach(k -> {
                if (keyOne.equals(k)) {
                    removeIndexMask.value |= 0x1;
                } else if (keyTwo.equals(k)) {
                    removeIndexMask.value |= 0x2;
                } else if (keyThree.equals(k)) {
                    removeIndexMask.value |= 0x4;
                }
            });

            CopyAsyncContextMap newMap = removeAll(removeIndexMask);
            if (newMap == null) {
                return false;
            }
            return mapUpdater.compareAndSet(owner, this, newMap) || owner.removeAll(entries);
        }

        @Nullable
        private CopyAsyncContextMap removeAll(MutableInt removeIndexMask) {
            assert keyOne != null && keyTwo != null && keyThree != null;
            if ((removeIndexMask.value & 0x7) == 0x7) {
                return EmptyAsyncContextMap.INSTANCE;
            } else if ((removeIndexMask.value & 0x4) == 0x4) {
                if ((removeIndexMask.value & 0x2) == 0x2) {
                    return new OneAsyncContextMap(keyOne, valueOne);
                } else if ((removeIndexMask.value & 0x1) == 0x1) {
                    return new OneAsyncContextMap(keyTwo, valueTwo);
                }
                return new TwoAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo);
            } else if ((removeIndexMask.value & 0x2) == 0x2) {
                if ((removeIndexMask.value & 0x1) == 0x1) {
                    return new OneAsyncContextMap(keyThree, valueThree);
                }
                return new TwoAsyncContextMap(keyOne, valueOne, keyThree, valueThree);
            } else if ((removeIndexMask.value & 0x1) == 0x1) {
                return new TwoAsyncContextMap(keyTwo, valueTwo, keyThree, valueThree);
            }
            return null;
        }

        @Nullable
        @Override
        public Key<?> forEach(final BiPredicate<Key<?>, Object> consumer) {
            if (!consumer.test(keyOne, valueOne)) {
                return keyOne;
            }
            if (!consumer.test(keyTwo, valueTwo)) {
                return keyTwo;
            }
            return consumer.test(keyThree, valueThree) ? null : keyThree;
        }

        @Override
        public void accept(final Key<?> key, final Object value) {
            test(key, value);
        }

        @Override
        public boolean test(final Key<?> key, final Object value) {
            if (keyOne == null) {
                keyOne = key;
                valueOne = value;
            } else if (keyTwo == null) {
                keyTwo = key;
                valueTwo = value;
            } else {
                assert keyThree == null;
                keyThree = key;
                valueThree = value;
                return false;
            }
            return true;
        }

        private static final class PutFourBuilder implements BiPredicate<Key<?>, Object>, BiConsumer<Key<?>, Object> {
            private final Key<?> keyOne;
            @Nullable
            private Object valueOne;
            private final Key<?> keyTwo;
            @Nullable
            private Object valueTwo;
            private final Key<?> keyThree;
            @Nullable
            private Object valueThree;
            @Nullable
            private Key<?> keyFour;
            @Nullable
            private Object valueFour;

            PutFourBuilder(Key<?> keyOne, @Nullable Object valueOne, Key<?> keyTwo, @Nullable Object valueTwo,
                           Key<?> keyThree, @Nullable Object valueThree) {
                this.keyOne = keyOne;
                this.valueOne = valueOne;
                this.keyTwo = keyTwo;
                this.valueTwo = valueTwo;
                this.keyThree = keyThree;
                this.valueThree = valueThree;
            }

            @Override
            public boolean test(final Key<?> key, final Object o) {
                if (keyOne.equals(key)) {
                    valueOne = o;
                } else if (keyTwo.equals(key)) {
                    valueTwo = o;
                } else if (keyThree.equals(key)) {
                    valueThree = o;
                } else {
                    assert keyFour == null;
                    keyFour = key;
                    valueFour = o;
                    return false;
                }
                return true;
            }

            @Override
            public void accept(final Key<?> key, final Object o) {
                test(key, o);
            }

            CopyAsyncContextMap build() {
                if (keyFour == null) {
                    return new ThreeAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree);
                }
                return new FourAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree,
                        keyFour, valueFour);
            }
        }

        private static final class PutFiveBuilder implements BiPredicate<Key<?>, Object>, BiConsumer<Key<?>, Object> {
            private final Key<?> keyOne;
            @Nullable
            private Object valueOne;
            private final Key<?> keyTwo;
            @Nullable
            private Object valueTwo;
            private final Key<?> keyThree;
            @Nullable
            private Object valueThree;
            @Nullable
            private Key<?> keyFour;
            @Nullable
            private Object valueFour;
            @Nullable
            private Key<?> keyFive;
            @Nullable
            private Object valueFive;

            PutFiveBuilder(Key<?> keyOne, @Nullable Object valueOne, Key<?> keyTwo, @Nullable Object valueTwo,
                           Key<?> keyThree, @Nullable Object valueThree) {
                this.keyOne = keyOne;
                this.valueOne = valueOne;
                this.keyTwo = keyTwo;
                this.valueTwo = valueTwo;
                this.keyThree = keyThree;
                this.valueThree = valueThree;
            }

            @Override
            public boolean test(final Key<?> key, final Object o) {
                if (keyOne.equals(key)) {
                    valueOne = o;
                } else if (keyTwo.equals(key)) {
                    valueTwo = o;
                } else if (keyThree.equals(key)) {
                    valueThree = o;
                } else if (keyFour == null) {
                    keyFour = key;
                    valueFour = o;
                } else {
                    assert keyFive == null;
                    keyFive = key;
                    valueFive = o;
                    return false;
                }
                return true;
            }

            @Override
            public void accept(final Key<?> key, final Object o) {
                test(key, o);
            }

            CopyAsyncContextMap build() {
                if (keyFour == null) {
                    return new ThreeAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree);
                }
                if (keyFive == null) {
                    return new FourAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree,
                            keyFour, valueFour);
                }
                return new FiveAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree,
                        keyFour, valueFour, keyFive, valueFive);
            }
        }

        private static final class PutSixBuilder implements BiPredicate<Key<?>, Object>, BiConsumer<Key<?>, Object> {
            private final Key<?> keyOne;
            @Nullable
            private Object valueOne;
            private final Key<?> keyTwo;
            @Nullable
            private Object valueTwo;
            private final Key<?> keyThree;
            @Nullable
            private Object valueThree;
            @Nullable
            private Key<?> keyFour;
            @Nullable
            private Object valueFour;
            @Nullable
            private Key<?> keyFive;
            @Nullable
            private Object valueFive;
            @Nullable
            private Key<?> keySix;
            @Nullable
            private Object valueSix;

            PutSixBuilder(Key<?> keyOne, @Nullable Object valueOne, Key<?> keyTwo, @Nullable Object valueTwo,
                          Key<?> keyThree, @Nullable Object valueThree) {
                this.keyOne = keyOne;
                this.valueOne = valueOne;
                this.keyTwo = keyTwo;
                this.valueTwo = valueTwo;
                this.keyThree = keyThree;
                this.valueThree = valueThree;
            }

            @Override
            public boolean test(final Key<?> key, final Object o) {
                if (keyOne.equals(key)) {
                    valueOne = o;
                } else if (keyTwo.equals(key)) {
                    valueTwo = o;
                } else if (keyThree.equals(key)) {
                    valueThree = o;
                } else if (keyFour == null) {
                    keyFour = key;
                    valueFour = o;
                } else if (keyFive == null) {
                    keyFive = key;
                    valueFive = o;
                } else {
                    assert keySix == null;
                    keySix = key;
                    valueSix = o;
                    return false;
                }
                return true;
            }

            @Override
            public void accept(final Key<?> key, final Object o) {
                test(key, o);
            }

            CopyAsyncContextMap build() {
                if (keyFour == null) {
                    return new ThreeAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree);
                }
                if (keyFive == null) {
                    return new FourAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree,
                            keyFour, valueFour);
                }
                if (keySix == null) {
                    return new FiveAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree,
                            keyFour, valueFour, keyFive, valueFive);
                }
                return new SixAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree,
                        keyFour, valueFour, keyFive, valueFive, keySix, valueSix);
            }
        }

        private static final class PutSevenBuilder extends AbstractPutSevenBuilder {
            PutSevenBuilder(Key<?> keyOne, @Nullable Object valueOne, Key<?> keyTwo, @Nullable Object valueTwo,
                            Key<?> keyThree, @Nullable Object valueThree, int putAllMapSize) {
                super((3 + putAllMapSize) << 1, 6);
                pairs[0] = keyOne;
                pairs[1] = valueOne;
                pairs[2] = keyTwo;
                pairs[3] = valueTwo;
                pairs[4] = keyThree;
                pairs[5] = valueThree;
            }

            CopyAsyncContextMap build() {
                if (nextIndex == 6) {
                    return new ThreeAsyncContextMap((Key<?>) pairs[0], pairs[1], (Key<?>) pairs[2], pairs[3],
                            (Key<?>) pairs[4], pairs[5]);
                }
                if (nextIndex == 8) {
                    return new FourAsyncContextMap((Key<?>) pairs[0], pairs[1], (Key<?>) pairs[2], pairs[3],
                            (Key<?>) pairs[4], pairs[5], (Key<?>) pairs[6], pairs[7]);
                }
                if (nextIndex == 10) {
                    return new FiveAsyncContextMap((Key<?>) pairs[0], pairs[1], (Key<?>) pairs[2], pairs[3],
                            (Key<?>) pairs[4], pairs[5], (Key<?>) pairs[6], pairs[7], (Key<?>) pairs[8], pairs[9]);
                }
                if (nextIndex == 12) {
                    return new SixAsyncContextMap((Key<?>) pairs[0], pairs[1], (Key<?>) pairs[2], pairs[3],
                            (Key<?>) pairs[4], pairs[5], (Key<?>) pairs[6], pairs[7], (Key<?>) pairs[8], pairs[9],
                            (Key<?>) pairs[10], pairs[11]);
                }
                if (nextIndex == pairs.length) {
                    return new SevenOrMoreAsyncContextMap(pairs);
                }
                return new SevenOrMoreAsyncContextMap(Arrays.copyOf(pairs, nextIndex));
            }
        }
    }

    private static final class FourAsyncContextMap implements CopyAsyncContextMap, BiConsumer<Key<?>, Object>,
                                                              BiPredicate<Key<?>, Object> {

        @Nullable
        private Key<?> keyOne;
        @Nullable
        private Object valueOne;
        @Nullable
        private Key<?> keyTwo;
        @Nullable
        private Object valueTwo;
        @Nullable
        private Key<?> keyThree;
        @Nullable
        private Object valueThree;
        @Nullable
        private Key<?> keyFour;
        @Nullable
        private Object valueFour;

        FourAsyncContextMap() {
        }

        FourAsyncContextMap(Key<?> keyOne, @Nullable Object valueOne, Key<?> keyTwo, @Nullable Object valueTwo,
                            Key<?> keyThree, @Nullable Object valueThree, Key<?> keyFour, @Nullable Object valueFour) {
            this.keyOne = keyOne;
            this.valueOne = valueOne;
            this.keyTwo = keyTwo;
            this.valueTwo = valueTwo;
            this.keyThree = keyThree;
            this.valueThree = valueThree;
            this.keyFour = keyFour;
            this.valueFour = valueFour;
        }

        @SuppressWarnings("unchecked")
        @Nullable
        @Override
        public <T> T get(final Key<T> key) {
            assert keyOne != null && keyTwo != null && keyThree != null && keyFour != null;
            return keyOne.equals(key) ? (T) valueOne : keyTwo.equals(key) ? (T) valueTwo : keyThree.equals(key) ?
                    (T) valueThree : keyFour.equals(key) ? (T) valueFour : null;
        }

        @Override
        public boolean containsKey(final Key<?> key) {
            assert keyOne != null && keyTwo != null && keyThree != null && keyFour != null;
            return keyOne.equals(key) || keyTwo.equals(key) || keyThree.equals(key) || keyFour.equals(key);
        }

        @Override
        public boolean isEmpty() {
            return false;
        }

        @Override
        public int size() {
            return 4;
        }

        @SuppressWarnings("unchecked")
        @Nullable
        @Override
        public <T> T put(final Key<T> key, final T value, CopyOnWriteAsyncContextMap owner,
                         AtomicReferenceFieldUpdater<CopyOnWriteAsyncContextMap, CopyAsyncContextMap> mapUpdater) {
            assert keyOne != null && keyTwo != null && keyThree != null && keyFour != null;
            if (keyOne.equals(key)) {
                return mapUpdater.compareAndSet(owner, this,
                        new FourAsyncContextMap(keyOne, value, keyTwo, valueTwo, keyThree, valueThree,
                                keyFour, valueFour)) ? (T) valueOne : owner.put(key, value);
            } else if (keyTwo.equals(key)) {
                return mapUpdater.compareAndSet(owner, this,
                        new FourAsyncContextMap(keyOne, valueOne, keyTwo, value, keyThree, valueThree,
                            keyFour, valueFour)) ? (T) valueTwo : owner.put(key, value);
            } else if (keyThree.equals(key)) {
                return mapUpdater.compareAndSet(owner, this,
                        new FourAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, value,
                                keyFour, valueFour)) ? (T) valueThree : owner.put(key, value);
            } else if (keyFour.equals(key)) {
                return mapUpdater.compareAndSet(owner, this,
                        new FourAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree,
                            keyFour, value)) ? (T) valueFour : owner.put(key, value);
            }
            return mapUpdater.compareAndSet(owner, this,
                    new FiveAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree,
                            keyFour, valueFour, key, value)) ? null : owner.put(key, value);
        }

        @Override
        public CopyAsyncContextMap putAll(final Map<Key<?>, Object> map) {
            assert keyOne != null && keyTwo != null && keyThree != null && keyFour != null;
            switch (map.size()) {
                case 0:
                    return this;
                case 1: {
                    PutFiveBuilder builder = new PutFiveBuilder(keyOne, valueOne, keyTwo, valueTwo,
                            keyThree, valueThree, keyFour, valueFour);
                    map.forEach(builder);
                    return builder.build();
                }
                case 2: {
                    PutSixBuilder builder = new PutSixBuilder(keyOne, valueOne, keyTwo, valueTwo,
                            keyThree, valueThree, keyFour, valueFour);
                    map.forEach(builder);
                    return builder.build();
                }
                default:
                    PutSevenBuilder builder = new PutSevenBuilder(keyOne, valueOne, keyTwo, valueTwo,
                            keyThree, valueThree, keyFour, valueFour, map.size());
                    map.forEach(builder);
                    return builder.build();
            }
        }

        @SuppressWarnings("unchecked")
        @Nullable
        @Override
        public <T> T remove(final Key<T> key, CopyOnWriteAsyncContextMap owner,
                            AtomicReferenceFieldUpdater<CopyOnWriteAsyncContextMap, CopyAsyncContextMap> mapUpdater) {
            assert keyOne != null && keyTwo != null && keyThree != null && keyFour != null;
            if (keyOne.equals(key)) {
                return mapUpdater.compareAndSet(owner, this,
                        new ThreeAsyncContextMap(keyTwo, valueTwo, keyThree, valueThree, keyFour, valueFour)) ?
                            (T) valueOne : owner.remove(key);
            }
            if (keyTwo.equals(key)) {
                return mapUpdater.compareAndSet(owner, this,
                        new ThreeAsyncContextMap(keyOne, valueOne, keyThree, valueThree, keyFour, valueFour)) ?
                            (T) valueTwo : owner.remove(key);
            }
            if (keyThree.equals(key)) {
                return mapUpdater.compareAndSet(owner, this,
                        new ThreeAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyFour, valueFour)) ?
                            (T) valueThree : owner.remove(key);
            }
            if (keyFour.equals(key)) {
                return mapUpdater.compareAndSet(owner, this,
                        new ThreeAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree)) ?
                            (T) valueFour : owner.remove(key);
            }
            return null;
        }

        @Override
        public boolean removeAll(final Iterable<Key<?>> entries, CopyOnWriteAsyncContextMap owner,
                             AtomicReferenceFieldUpdater<CopyOnWriteAsyncContextMap, CopyAsyncContextMap> mapUpdater) {
            assert keyOne != null && keyTwo != null && keyThree != null && keyFour != null;
            MutableInt removeIndexMask = new MutableInt();
            entries.forEach(k -> {
                if (keyOne.equals(k)) {
                    removeIndexMask.value |= 0x1;
                } else if (keyTwo.equals(k)) {
                    removeIndexMask.value |= 0x2;
                } else if (keyThree.equals(k)) {
                    removeIndexMask.value |= 0x4;
                } else if (keyFour.equals(k)) {
                    removeIndexMask.value |= 0x8;
                }
            });

            CopyAsyncContextMap newMap = removeAll(removeIndexMask);
            if (newMap == null) {
                return false;
            }
            return mapUpdater.compareAndSet(owner, this, newMap) || owner.removeAll(entries);
        }

        @Nullable
        private CopyAsyncContextMap removeAll(MutableInt removeIndexMask) {
            assert keyOne != null && keyTwo != null && keyThree != null && keyFour != null;
            if ((removeIndexMask.value & 0xf) == 0xf) {
                return EmptyAsyncContextMap.INSTANCE;
            } else if ((removeIndexMask.value & 0x8) == 0x8) {
                if ((removeIndexMask.value & 0x4) == 0x4) {
                    if ((removeIndexMask.value & 0x2) == 0x2) {
                        return new OneAsyncContextMap(keyOne, valueOne);
                    } else if ((removeIndexMask.value & 0x1) == 0x1) {
                        return new OneAsyncContextMap(keyTwo, valueTwo);
                    }
                    return new TwoAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo);
                } else if ((removeIndexMask.value & 0x2) == 0x2) {
                    if ((removeIndexMask.value & 0x1) == 0x1) {
                        return new OneAsyncContextMap(keyThree, valueThree);
                    }
                    return new TwoAsyncContextMap(keyOne, valueOne, keyThree, valueThree);
                } else if ((removeIndexMask.value & 0x1) == 0x1) {
                    return new TwoAsyncContextMap(keyTwo, valueTwo, keyThree, valueThree);
                }
                return new ThreeAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree);
            } else if ((removeIndexMask.value & 0x4) == 0x4) {
                if ((removeIndexMask.value & 0x2) == 0x2) {
                    if ((removeIndexMask.value & 0x1) == 0x1) {
                        return new OneAsyncContextMap(keyFour, valueFour);
                    }
                    return new TwoAsyncContextMap(keyOne, valueOne, keyFour, valueFour);
                } else if ((removeIndexMask.value & 0x1) == 0x1) {
                    return new TwoAsyncContextMap(keyTwo, valueTwo, keyFour, valueFour);
                }
                return new ThreeAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyFour, valueFour);
            } else if ((removeIndexMask.value & 0x2) == 0x2) {
                if ((removeIndexMask.value & 0x1) == 0x1) {
                    return new TwoAsyncContextMap(keyThree, valueThree, keyFour, valueFour);
                }
                return new ThreeAsyncContextMap(keyOne, valueOne, keyThree, valueThree, keyFour, valueFour);
            } else if ((removeIndexMask.value & 0x1) == 0x1) {
                return new ThreeAsyncContextMap(keyTwo, valueTwo, keyThree, valueThree, keyFour, valueFour);
            }
            return null;
        }

        @Nullable
        @Override
        public Key<?> forEach(final BiPredicate<Key<?>, Object> consumer) {
            if (!consumer.test(keyOne, valueOne)) {
                return keyOne;
            }
            if (!consumer.test(keyTwo, valueTwo)) {
                return keyTwo;
            }
            if (!consumer.test(keyThree, valueThree)) {
                return keyThree;
            }
            return consumer.test(keyFour, valueFour) ? null : keyFour;
        }

        @Override
        public void accept(final Key<?> key, final Object value) {
            test(key, value);
        }

        @Override
        public boolean test(final Key<?> key, final Object value) {
            if (keyOne == null) {
                keyOne = key;
                valueOne = value;
            } else if (keyTwo == null) {
                keyTwo = key;
                valueTwo = value;
            } else if (keyThree == null) {
                keyThree = key;
                valueThree = value;
            } else {
                assert keyFour == null;
                keyFour = key;
                valueFour = value;
                return false;
            }
            return true;
        }

        private static final class PutFiveBuilder implements BiPredicate<Key<?>, Object>, BiConsumer<Key<?>, Object> {
            private final Key<?> keyOne;
            @Nullable
            private Object valueOne;
            private final Key<?> keyTwo;
            @Nullable
            private Object valueTwo;
            private final Key<?> keyThree;
            @Nullable
            private Object valueThree;
            private final Key<?> keyFour;
            @Nullable
            private Object valueFour;
            @Nullable
            private Key<?> keyFive;
            @Nullable
            private Object valueFive;

            PutFiveBuilder(Key<?> keyOne, @Nullable Object valueOne, Key<?> keyTwo, @Nullable Object valueTwo,
                           Key<?> keyThree, @Nullable Object valueThree, Key<?> keyFour, @Nullable Object valueFour) {
                this.keyOne = keyOne;
                this.valueOne = valueOne;
                this.keyTwo = keyTwo;
                this.valueTwo = valueTwo;
                this.keyThree = keyThree;
                this.valueThree = valueThree;
                this.keyFour = keyFour;
                this.valueFour = valueFour;
            }

            @Override
            public void accept(final Key<?> key, final Object o) {
                test(key, o);
            }

            @Override
            public boolean test(final Key<?> key, final Object o) {
                if (keyOne.equals(key)) {
                    valueOne = o;
                } else if (keyTwo.equals(key)) {
                    valueTwo = o;
                } else if (keyThree.equals(key)) {
                    valueThree = o;
                } else if (keyFour.equals(key)) {
                    valueFour = o;
                } else {
                    assert keyFive == null;
                    keyFive = key;
                    valueFive = o;
                    return false;
                }
                return true;
            }

            CopyAsyncContextMap build() {
                if (keyFive == null) {
                    return new FourAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree,
                            keyFour, valueFour);
                }
                return new FiveAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree,
                        keyFour, valueFour, keyFive, valueFive);
            }
        }

        private static final class PutSixBuilder implements BiPredicate<Key<?>, Object>, BiConsumer<Key<?>, Object> {
            private final Key<?> keyOne;
            @Nullable
            private Object valueOne;
            private final Key<?> keyTwo;
            @Nullable
            private Object valueTwo;
            private final Key<?> keyThree;
            @Nullable
            private Object valueThree;
            private final Key<?> keyFour;
            @Nullable
            private Object valueFour;
            @Nullable
            private Key<?> keyFive;
            @Nullable
            private Object valueFive;
            @Nullable
            private Key<?> keySix;
            @Nullable
            private Object valueSix;

            PutSixBuilder(Key<?> keyOne, @Nullable Object valueOne, Key<?> keyTwo, @Nullable Object valueTwo,
                          Key<?> keyThree, @Nullable Object valueThree, Key<?> keyFour, @Nullable Object valueFour) {
                this.keyOne = keyOne;
                this.valueOne = valueOne;
                this.keyTwo = keyTwo;
                this.valueTwo = valueTwo;
                this.keyThree = keyThree;
                this.valueThree = valueThree;
                this.keyFour = keyFour;
                this.valueFour = valueFour;
            }

            @Override
            public void accept(final Key<?> key, final Object o) {
                test(key, o);
            }

            @Override
            public boolean test(final Key<?> key, final Object o) {
                if (keyOne.equals(key)) {
                    valueOne = o;
                } else if (keyTwo.equals(key)) {
                    valueTwo = o;
                } else if (keyThree.equals(key)) {
                    valueThree = o;
                } else if (keyFour.equals(key)) {
                    valueFour = o;
                } else if (keyFive == null) {
                    keyFive = key;
                    valueFive = o;
                } else {
                    assert keySix == null;
                    keySix = key;
                    valueSix = o;
                    return false;
                }
                return true;
            }

            CopyAsyncContextMap build() {
                if (keyFive == null) {
                    return new FourAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree,
                            keyFour, valueFour);
                }
                if (keySix == null) {
                    return new FiveAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree,
                            keyFour, valueFour, keyFive, valueFive);
                }
                return new SixAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree,
                        keyFour, valueFour, keyFive, valueFive, keySix, valueSix);
            }
        }

        private static final class PutSevenBuilder extends AbstractPutSevenBuilder {
            PutSevenBuilder(Key<?> keyOne, @Nullable Object valueOne, Key<?> keyTwo, @Nullable Object valueTwo,
                            Key<?> keyThree, @Nullable Object valueThree, Key<?> keyFour, @Nullable Object valueFour,
                            int putAllMapSize) {
                super((4 + putAllMapSize) << 1, 8);
                pairs[0] = keyOne;
                pairs[1] = valueOne;
                pairs[2] = keyTwo;
                pairs[3] = valueTwo;
                pairs[4] = keyThree;
                pairs[5] = valueThree;
                pairs[6] = keyFour;
                pairs[7] = valueFour;
            }

            CopyAsyncContextMap build() {
                if (nextIndex == 8) {
                    return new FourAsyncContextMap((Key<?>) pairs[0], pairs[1], (Key<?>) pairs[2], pairs[3],
                            (Key<?>) pairs[4], pairs[5], (Key<?>) pairs[6], pairs[7]);
                }
                if (nextIndex == 10) {
                    return new FiveAsyncContextMap((Key<?>) pairs[0], pairs[1], (Key<?>) pairs[2], pairs[3],
                            (Key<?>) pairs[4], pairs[5], (Key<?>) pairs[6], pairs[7], (Key<?>) pairs[8], pairs[9]);
                }
                if (nextIndex == 12) {
                    return new SixAsyncContextMap((Key<?>) pairs[0], pairs[1], (Key<?>) pairs[2], pairs[3],
                            (Key<?>) pairs[4], pairs[5], (Key<?>) pairs[6], pairs[7], (Key<?>) pairs[8], pairs[9],
                            (Key<?>) pairs[10], pairs[11]);
                }
                if (nextIndex == pairs.length) {
                    return new SevenOrMoreAsyncContextMap(pairs);
                }
                return new SevenOrMoreAsyncContextMap(Arrays.copyOf(pairs, nextIndex));
            }
        }
    }

    private static final class FiveAsyncContextMap implements CopyAsyncContextMap, BiConsumer<Key<?>, Object>,
                                                              BiPredicate<Key<?>, Object> {

        @Nullable
        private Key<?> keyOne;
        @Nullable
        private Object valueOne;
        @Nullable
        private Key<?> keyTwo;
        @Nullable
        private Object valueTwo;
        @Nullable
        private Key<?> keyThree;
        @Nullable
        private Object valueThree;
        @Nullable
        private Key<?> keyFour;
        @Nullable
        private Object valueFour;
        @Nullable
        private Key<?> keyFive;
        @Nullable
        private Object valueFive;

        FiveAsyncContextMap() {
        }

        FiveAsyncContextMap(Key<?> keyOne, @Nullable Object valueOne, Key<?> keyTwo, @Nullable Object valueTwo,
                            Key<?> keyThree, @Nullable Object valueThree, Key<?> keyFour, @Nullable Object valueFour,
                            Key<?> keyFive, @Nullable Object valueFive) {
            this.keyOne = keyOne;
            this.valueOne = valueOne;
            this.keyTwo = keyTwo;
            this.valueTwo = valueTwo;
            this.keyThree = keyThree;
            this.valueThree = valueThree;
            this.keyFour = keyFour;
            this.valueFour = valueFour;
            this.keyFive = keyFive;
            this.valueFive = valueFive;
        }

        @SuppressWarnings("unchecked")
        @Nullable
        @Override
        public <T> T get(final Key<T> key) {
            assert keyOne != null && keyTwo != null && keyThree != null && keyFour != null && keyFive != null;
            return keyOne.equals(key) ? (T) valueOne : keyTwo.equals(key) ? (T) valueTwo : keyThree.equals(key) ?
                    (T) valueThree : keyFour.equals(key) ? (T) valueFour : keyFive.equals(key) ? (T) valueFive : null;
        }

        @Override
        public boolean containsKey(final Key<?> key) {
            assert keyOne != null && keyTwo != null && keyThree != null && keyFour != null && keyFive != null;
            return keyOne.equals(key) || keyTwo.equals(key) || keyThree.equals(key) || keyFour.equals(key) ||
                    keyFive.equals(key);
        }

        @Override
        public boolean isEmpty() {
            return false;
        }

        @Override
        public int size() {
            return 5;
        }

        @SuppressWarnings("unchecked")
        @Nullable
        @Override
        public <T> T put(final Key<T> key, final T value, CopyOnWriteAsyncContextMap owner,
                         AtomicReferenceFieldUpdater<CopyOnWriteAsyncContextMap, CopyAsyncContextMap> mapUpdater) {
            assert keyOne != null && keyTwo != null && keyThree != null && keyFour != null && keyFive != null;
            if (keyOne.equals(key)) {
                return mapUpdater.compareAndSet(owner, this,
                        new FiveAsyncContextMap(keyOne, value, keyTwo, valueTwo, keyThree, valueThree,
                        keyFour, valueFour, keyFive, valueFive)) ? (T) valueOne : owner.put(key, value);
            } else if (keyTwo.equals(key)) {
                return mapUpdater.compareAndSet(owner, this,
                        new FiveAsyncContextMap(keyOne, valueOne, keyTwo, value, keyThree, valueThree,
                        keyFour, valueFour, keyFive, valueFive)) ? (T) valueTwo : owner.put(key, value);
            } else if (keyThree.equals(key)) {
                return mapUpdater.compareAndSet(owner, this,
                        new FiveAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, value, keyFour, valueFour,
                        keyFive, valueFive)) ? (T) valueThree : owner.put(key, value);
            } else if (keyFour.equals(key)) {
                return mapUpdater.compareAndSet(owner, this, new FiveAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo,
                        keyThree, valueThree, keyFour, value, keyFive, valueFive)) ?
                        (T) valueFour : owner.put(key, value);
            } else if (keyFive.equals(key)) {
                return mapUpdater.compareAndSet(owner, this,
                        new FiveAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree,
                        keyFour, valueFour, keyFive, value)) ? (T) valueFive : owner.put(key, value);
            }
            return mapUpdater.compareAndSet(owner, this,
                    new SixAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree, keyFour, valueFour,
                    keyFive, valueFive, key, value)) ? null : owner.put(key, value);
        }

        @Override
        public CopyAsyncContextMap putAll(final Map<Key<?>, Object> map) {
            assert keyOne != null && keyTwo != null && keyThree != null && keyFour != null && keyFive != null;
            switch (map.size()) {
                case 0:
                    return this;
                case 1: {
                    PutSixBuilder builder = new PutSixBuilder(keyOne, valueOne, keyTwo, valueTwo,
                            keyThree, valueThree, keyFour, valueFour, keyFive, valueFive);
                    map.forEach(builder);
                    return builder.build();
                }
                default:
                    PutSevenBuilder builder = new PutSevenBuilder(keyOne, valueOne, keyTwo, valueTwo,
                            keyThree, valueThree, keyFour, valueFour, keyFive, valueFive, map.size());
                    map.forEach(builder);
                    return builder.build();
            }
        }

        @SuppressWarnings("unchecked")
        @Nullable
        @Override
        public <T> T remove(final Key<T> key, CopyOnWriteAsyncContextMap owner,
                            AtomicReferenceFieldUpdater<CopyOnWriteAsyncContextMap, CopyAsyncContextMap> mapUpdater) {
            assert keyOne != null && keyTwo != null && keyThree != null && keyFour != null && keyFive != null;
            if (keyOne.equals(key)) {
                return mapUpdater.compareAndSet(owner, this, new FourAsyncContextMap(keyTwo, valueTwo, keyThree,
                        valueThree, keyFour, valueFour, keyFive, valueFive)) ? (T) valueOne : owner.remove(key);
            }
            if (keyTwo.equals(key)) {
                return mapUpdater.compareAndSet(owner, this, new FourAsyncContextMap(keyOne, valueOne, keyThree,
                        valueThree, keyFour, valueFour, keyFive, valueFive)) ? (T) valueTwo : owner.remove(key);
            }
            if (keyThree.equals(key)) {
                return mapUpdater.compareAndSet(owner, this, new FourAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo,
                        keyFour, valueFour, keyFive, valueFive)) ? (T) valueThree : owner.remove(key);
            }
            if (keyFour.equals(key)) {
                return mapUpdater.compareAndSet(owner, this, new FourAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo,
                        keyThree, valueThree, keyFive, valueFive)) ? (T) valueFour : owner.remove(key);
            }
            if (keyFive.equals(key)) {
                return mapUpdater.compareAndSet(owner, this, new FourAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo,
                        keyThree, valueThree, keyFour, valueFour)) ? (T) valueFive : owner.remove(key);
            }
            return null;
        }

        @Override
        public boolean removeAll(final Iterable<Key<?>> entries, CopyOnWriteAsyncContextMap owner,
                             AtomicReferenceFieldUpdater<CopyOnWriteAsyncContextMap, CopyAsyncContextMap> mapUpdater) {
            assert keyOne != null && keyTwo != null && keyThree != null && keyFour != null && keyFive != null;
            MutableInt removeIndexMask = new MutableInt();
            entries.forEach(k -> {
                if (keyOne.equals(k)) {
                    removeIndexMask.value |= 0x1;
                } else if (keyTwo.equals(k)) {
                    removeIndexMask.value |= 0x2;
                } else if (keyThree.equals(k)) {
                    removeIndexMask.value |= 0x4;
                } else if (keyFour.equals(k)) {
                    removeIndexMask.value |= 0x8;
                } else if (keyFive.equals(k)) {
                    removeIndexMask.value |= 0x10;
                }
            });

            CopyAsyncContextMap newMap = removeAll(removeIndexMask);
            if (newMap == null) {
                return false;
            }
            return mapUpdater.compareAndSet(owner, this, newMap) || owner.removeAll(entries);
        }

        @Nullable
        private CopyAsyncContextMap removeAll(MutableInt removeIndexMask) {
            assert keyOne != null && keyTwo != null && keyThree != null && keyFour != null && keyFive != null;
            if ((removeIndexMask.value & 0x1f) == 0x1f) {
                return EmptyAsyncContextMap.INSTANCE;
            } else if ((removeIndexMask.value & 0x10) == 0x10) {
                if ((removeIndexMask.value & 0x8) == 0x8) {
                    if ((removeIndexMask.value & 0x4) == 0x4) {
                        if ((removeIndexMask.value & 0x2) == 0x2) {
                            return new OneAsyncContextMap(keyOne, valueOne);
                        } else if ((removeIndexMask.value & 0x1) == 0x1) {
                            return new OneAsyncContextMap(keyTwo, valueTwo);
                        }
                        return new TwoAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo);
                    } else if ((removeIndexMask.value & 0x2) == 0x2) {
                        if ((removeIndexMask.value & 0x1) == 0x1) {
                            return new OneAsyncContextMap(keyThree, valueThree);
                        }
                        return new TwoAsyncContextMap(keyOne, valueOne, keyThree, valueThree);
                    } else if ((removeIndexMask.value & 0x1) == 0x1) {
                        return new TwoAsyncContextMap(keyTwo, valueTwo, keyThree, valueThree);
                    }
                    return new ThreeAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree);
                } else if ((removeIndexMask.value & 0x4) == 0x4) {
                    if ((removeIndexMask.value & 0x2) == 0x2) {
                        if ((removeIndexMask.value & 0x1) == 0x1) {
                            return new OneAsyncContextMap(keyFour, valueFour);
                        }
                        return new TwoAsyncContextMap(keyOne, valueOne, keyFour, valueFour);
                    } else if ((removeIndexMask.value & 0x1) == 0x1) {
                        return new TwoAsyncContextMap(keyTwo, valueTwo, keyFour, valueFour);
                    }
                    return new ThreeAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyFour, valueFour);
                } else if ((removeIndexMask.value & 0x2) == 0x2) {
                    if ((removeIndexMask.value & 0x1) == 0x1) {
                        return new TwoAsyncContextMap(keyThree, valueThree, keyFour, valueFour);
                    }
                    return new ThreeAsyncContextMap(keyOne, valueOne, keyThree, valueThree, keyFour, valueFour);
                } else if ((removeIndexMask.value & 0x1) == 0x1) {
                    return new ThreeAsyncContextMap(keyTwo, valueTwo, keyThree, valueThree, keyFour, valueFour);
                }
                return new FourAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree,
                        keyFour, valueFour);
            } else if ((removeIndexMask.value & 0x8) == 0x8) {
                if ((removeIndexMask.value & 0x4) == 0x4) {
                    if ((removeIndexMask.value & 0x2) == 0x2) {
                        if ((removeIndexMask.value & 0x1) == 0x1) {
                            return new OneAsyncContextMap(keyFive, valueFive);
                        }
                        return new TwoAsyncContextMap(keyOne, valueOne, keyFive, valueFive);
                    } else if ((removeIndexMask.value & 0x1) == 0x1) {
                        return new TwoAsyncContextMap(keyTwo, valueTwo, keyFive, valueFive);
                    }
                    return new ThreeAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyFive, valueFive);
                } else if ((removeIndexMask.value & 0x2) == 0x2) {
                    if ((removeIndexMask.value & 0x1) == 0x1) {
                        return new TwoAsyncContextMap(keyThree, valueThree, keyFive, valueFive);
                    }
                    return new ThreeAsyncContextMap(keyOne, valueOne, keyThree, valueThree, keyFive, valueFive);
                } else if ((removeIndexMask.value & 0x1) == 0x1) {
                    return new ThreeAsyncContextMap(keyTwo, valueTwo, keyThree, valueThree, keyFive, valueFive);
                }
                return new FourAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree,
                        keyFive, valueFive);
            } else if ((removeIndexMask.value & 0x4) == 0x4) {
                if ((removeIndexMask.value & 0x2) == 0x2) {
                    if ((removeIndexMask.value & 0x1) == 0x1) {
                        return new TwoAsyncContextMap(keyFour, valueFour, keyFive, valueFive);
                    }
                    return new ThreeAsyncContextMap(keyOne, valueOne, keyFour, valueFour, keyFive, valueFive);
                } else if ((removeIndexMask.value & 0x1) == 0x1) {
                    return new ThreeAsyncContextMap(keyTwo, valueTwo, keyFour, valueFour, keyFive, valueFive);
                }
                return new FourAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyFour, valueFour,
                        keyFive, valueFive);
            } else if ((removeIndexMask.value & 0x2) == 0x2) {
                if ((removeIndexMask.value & 0x1) == 0x1) {
                    return new ThreeAsyncContextMap(keyThree, valueThree, keyFour, valueFour,
                            keyFive, valueFive);
                }
                return new FourAsyncContextMap(keyOne, valueOne, keyThree, valueThree, keyFour, valueFour,
                        keyFive, valueFive);
            } else if ((removeIndexMask.value & 0x1) == 0x1) {
                return new FourAsyncContextMap(keyTwo, valueTwo, keyThree, valueThree, keyFour, valueFour,
                        keyFive, valueFive);
            }
            return null;
        }

        @Nullable
        @Override
        public Key<?> forEach(final BiPredicate<Key<?>, Object> consumer) {
            if (!consumer.test(keyOne, valueOne)) {
                return keyOne;
            }
            if (!consumer.test(keyTwo, valueTwo)) {
                return keyTwo;
            }
            if (!consumer.test(keyThree, valueThree)) {
                return keyThree;
            }
            if (!consumer.test(keyFour, valueFour)) {
                return keyFour;
            }
            return consumer.test(keyFive, valueFive) ? null : keyFive;
        }

        @Override
        public void accept(final Key<?> key, final Object value) {
            test(key, value);
        }

        @Override
        public boolean test(final Key<?> key, final Object value) {
            if (keyOne == null) {
                keyOne = key;
                valueOne = value;
            } else if (keyTwo == null) {
                keyTwo = key;
                valueTwo = value;
            } else if (keyThree == null) {
                keyThree = key;
                valueThree = value;
            } else if (keyFour == null) {
                keyFour = key;
                valueFour = value;
            } else {
                assert keyFive == null;
                keyFive = key;
                valueFive = value;
                return false;
            }
            return true;
        }

        private static final class PutSixBuilder implements BiPredicate<Key<?>, Object>, BiConsumer<Key<?>, Object> {
            private final Key<?> keyOne;
            @Nullable
            private Object valueOne;
            private final Key<?> keyTwo;
            @Nullable
            private Object valueTwo;
            private final Key<?> keyThree;
            @Nullable
            private Object valueThree;
            private final Key<?> keyFour;
            @Nullable
            private Object valueFour;
            private final Key<?> keyFive;
            @Nullable
            private Object valueFive;
            @Nullable
            private Key<?> keySix;
            @Nullable
            private Object valueSix;

            PutSixBuilder(Key<?> keyOne, @Nullable Object valueOne, Key<?> keyTwo, @Nullable Object valueTwo,
                          Key<?> keyThree, @Nullable Object valueThree, Key<?> keyFour, @Nullable Object valueFour,
                          Key<?> keyFive, @Nullable Object valueFive) {
                this.keyOne = keyOne;
                this.valueOne = valueOne;
                this.keyTwo = keyTwo;
                this.valueTwo = valueTwo;
                this.keyThree = keyThree;
                this.valueThree = valueThree;
                this.keyFour = keyFour;
                this.valueFour = valueFour;
                this.keyFive = keyFive;
                this.valueFive = valueFive;
            }

            @Override
            public void accept(final Key<?> key, final Object o) {
                test(key, o);
            }

            @Override
            public boolean test(final Key<?> key, final Object o) {
                if (keyOne.equals(key)) {
                    valueOne = o;
                } else if (keyTwo.equals(key)) {
                    valueTwo = o;
                } else if (keyThree.equals(key)) {
                    valueThree = o;
                } else if (keyFour.equals(key)) {
                    valueFour = o;
                } else if (keyFive.equals(key)) {
                    valueFive = o;
                } else {
                    assert keySix == null;
                    keySix = key;
                    valueSix = o;
                    return false;
                }
                return true;
            }

            CopyAsyncContextMap build() {
                if (keySix == null) {
                    return new FiveAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree,
                            keyFour, valueFour, keyFive, valueFive);
                }
                return new SixAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree,
                        keyFour, valueFour, keyFive, valueFive, keySix, valueSix);
            }
        }

        private static final class PutSevenBuilder extends AbstractPutSevenBuilder {
            PutSevenBuilder(Key<?> keyOne, @Nullable Object valueOne, Key<?> keyTwo, @Nullable Object valueTwo,
                            Key<?> keyThree, @Nullable Object valueThree, Key<?> keyFour, @Nullable Object valueFour,
                            Key<?> keyFive, @Nullable Object valueFive, int putAllMapSize) {
                super((5 + putAllMapSize) << 1, 10);
                pairs[0] = keyOne;
                pairs[1] = valueOne;
                pairs[2] = keyTwo;
                pairs[3] = valueTwo;
                pairs[4] = keyThree;
                pairs[5] = valueThree;
                pairs[6] = keyFour;
                pairs[7] = valueFour;
                pairs[8] = keyFive;
                pairs[9] = valueFive;
            }

            CopyAsyncContextMap build() {
                if (nextIndex == 10) {
                    return new FiveAsyncContextMap((Key<?>) pairs[0], pairs[1], (Key<?>) pairs[2], pairs[3],
                            (Key<?>) pairs[4], pairs[5], (Key<?>) pairs[6], pairs[7], (Key<?>) pairs[8], pairs[9]);
                }
                if (nextIndex == 12) {
                    return new SixAsyncContextMap((Key<?>) pairs[0], pairs[1], (Key<?>) pairs[2], pairs[3],
                            (Key<?>) pairs[4], pairs[5], (Key<?>) pairs[6], pairs[7], (Key<?>) pairs[8], pairs[9],
                            (Key<?>) pairs[10], pairs[11]);
                }
                if (nextIndex == pairs.length) {
                    return new SevenOrMoreAsyncContextMap(pairs);
                }
                return new SevenOrMoreAsyncContextMap(Arrays.copyOf(pairs, nextIndex));
            }
        }
    }

    private static final class SixAsyncContextMap implements CopyAsyncContextMap, BiConsumer<Key<?>, Object>,
                                                             BiPredicate<Key<?>, Object> {

        @Nullable
        private Key<?> keyOne;
        @Nullable
        private Object valueOne;
        @Nullable
        private Key<?> keyTwo;
        @Nullable
        private Object valueTwo;
        @Nullable
        private Key<?> keyThree;
        @Nullable
        private Object valueThree;
        @Nullable
        private Key<?> keyFour;
        @Nullable
        private Object valueFour;
        @Nullable
        private Key<?> keyFive;
        @Nullable
        private Object valueFive;
        @Nullable
        private Key<?> keySix;
        @Nullable
        private Object valueSix;

        SixAsyncContextMap() {
        }

        SixAsyncContextMap(Key<?> keyOne, @Nullable Object valueOne, Key<?> keyTwo, @Nullable Object valueTwo,
                           Key<?> keyThree, @Nullable Object valueThree, Key<?> keyFour, @Nullable Object valueFour,
                           Key<?> keyFive, @Nullable Object valueFive, Key<?> keySix, @Nullable Object valueSix) {
            this.keyOne = keyOne;
            this.valueOne = valueOne;
            this.keyTwo = keyTwo;
            this.valueTwo = valueTwo;
            this.keyThree = keyThree;
            this.valueThree = valueThree;
            this.keyFour = keyFour;
            this.valueFour = valueFour;
            this.keyFive = keyFive;
            this.valueFive = valueFive;
            this.keySix = keySix;
            this.valueSix = valueSix;
        }

        @SuppressWarnings("unchecked")
        @Nullable
        @Override
        public <T> T get(final Key<T> key) {
            assert keyOne != null && keyTwo != null && keyThree != null && keyFour != null && keyFive != null &&
                    keySix != null;
            return keyOne.equals(key) ? (T) valueOne : keyTwo.equals(key) ? (T) valueTwo : keyThree.equals(key) ?
                    (T) valueThree : keyFour.equals(key) ? (T) valueFour : keyFive.equals(key) ? (T) valueFive :
                    keySix.equals(key) ? (T) valueSix : null;
        }

        @Override
        public boolean containsKey(final Key<?> key) {
            assert keyOne != null && keyTwo != null && keyThree != null && keyFour != null && keyFive != null &&
                    keySix != null;
            return keyOne.equals(key) || keyTwo.equals(key) || keyThree.equals(key) || keyFour.equals(key) ||
                    keyFive.equals(key) || keySix.equals(key);
        }

        @Override
        public boolean isEmpty() {
            return false;
        }

        @Override
        public int size() {
            return 6;
        }

        @SuppressWarnings("unchecked")
        @Nullable
        @Override
        public <T> T put(final Key<T> key, final T value, CopyOnWriteAsyncContextMap owner,
                         AtomicReferenceFieldUpdater<CopyOnWriteAsyncContextMap, CopyAsyncContextMap> mapUpdater) {
            assert keyOne != null && keyTwo != null && keyThree != null && keyFour != null && keyFive != null &&
                    keySix != null;
            if (keyOne.equals(key)) {
                return mapUpdater.compareAndSet(owner, this, new SixAsyncContextMap(keyOne, value, keyTwo, valueTwo,
                        keyThree, valueThree, keyFour, valueFour, keyFive, valueFive, keySix, valueSix)) ?
                            (T) valueOne : owner.put(key, value);
            } else if (keyTwo.equals(key)) {
                return mapUpdater.compareAndSet(owner, this, new SixAsyncContextMap(keyOne, valueOne, keyTwo, value,
                        keyThree, valueThree, keyFour, valueFour, keyFive, valueFive, keySix, valueSix)) ?
                            (T) valueTwo : owner.put(key, value);
            } else if (keyThree.equals(key)) {
                return mapUpdater.compareAndSet(owner, this, new SixAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo,
                        keyThree, value, keyFour, valueFour, keyFive, valueFive, keySix, valueSix)) ?
                            (T) valueThree : owner.put(key, value);
            } else if (keyFour.equals(key)) {
                return mapUpdater.compareAndSet(owner, this, new SixAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo,
                        keyThree, valueThree, keyFour, value, keyFive, valueFive, keySix, valueSix)) ?
                            (T) valueFour : owner.put(key, value);
            } else if (keyFive.equals(key)) {
                return mapUpdater.compareAndSet(owner, this, new SixAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo,
                        keyThree, valueThree, keyFour, valueFour, keyFive, value, keySix, valueSix)) ?
                            (T) valueFive : owner.put(key, value);
            } else if (keySix.equals(key)) {
                return mapUpdater.compareAndSet(owner, this, new SixAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo,
                        keyThree, valueThree, keyFour, valueFour, keyFive, valueFive, keySix, value)) ?
                            (T) valueSix : owner.put(key, value);
            }
            return mapUpdater.compareAndSet(owner, this, new SevenOrMoreAsyncContextMap(keyOne, valueOne, keyTwo,
                    valueTwo, keyThree, valueThree, keyFour, valueFour, keyFive, valueFive, keySix, valueSix,
                    key, value)) ? null : owner.put(key, value);
        }

        @Override
        public CopyAsyncContextMap putAll(final Map<Key<?>, Object> map) {
            assert keyOne != null && keyTwo != null && keyThree != null && keyFour != null && keyFive != null &&
                    keySix != null;
            if (map.isEmpty()) {
                return this;
            }
            PutSevenBuilder builder = new PutSevenBuilder(keyOne, valueOne, keyTwo, valueTwo,
                    keyThree, valueThree, keyFour, valueFour, keyFive, valueFive, keySix, valueSix, map.size());
            map.forEach(builder);
            return builder.build();
        }

        @SuppressWarnings("unchecked")
        @Nullable
        @Override
        public <T> T remove(final Key<T> key, CopyOnWriteAsyncContextMap owner,
                            AtomicReferenceFieldUpdater<CopyOnWriteAsyncContextMap, CopyAsyncContextMap> mapUpdater) {
            assert keyOne != null && keyTwo != null && keyThree != null && keyFour != null && keyFive != null &&
                    keySix != null;
            if (keyOne.equals(key)) {
                return mapUpdater.compareAndSet(owner, this, new FiveAsyncContextMap(keyTwo, valueTwo, keyThree,
                        valueThree, keyFour, valueFour, keyFive, valueFive, keySix, valueSix)) ?
                            (T) valueOne : owner.remove(key);
            }
            if (keyTwo.equals(key)) {
                return mapUpdater.compareAndSet(owner, this, new FiveAsyncContextMap(keyOne, valueOne, keyThree,
                        valueThree, keyFour, valueFour, keyFive, valueFive, keySix, valueSix)) ?
                            (T) valueTwo : owner.remove(key);
            }
            if (keyThree.equals(key)) {
                return mapUpdater.compareAndSet(owner, this, new FiveAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo,
                        keyFour, valueFour, keyFive, valueFive, keySix, valueSix)) ? (T) valueThree : owner.remove(key);
            }
            if (keyFour.equals(key)) {
                return mapUpdater.compareAndSet(owner, this, new FiveAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo,
                    keyThree, valueThree, keyFive, valueFive, keySix, valueSix)) ? (T) valueFour : owner.remove(key);
            }
            if (keyFive.equals(key)) {
                return mapUpdater.compareAndSet(owner, this, new FiveAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo,
                    keyThree, valueThree, keyFour, valueFour, keySix, valueSix)) ? (T) valueFive : owner.remove(key);
            }
            if (keySix.equals(key)) {
                return mapUpdater.compareAndSet(owner, this, new FiveAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo,
                    keyThree, valueThree, keyFour, valueFour, keyFive, valueFive)) ? (T) valueSix : owner.remove(key);
            }
            return null;
        }

        @Override
        public boolean removeAll(final Iterable<Key<?>> entries, CopyOnWriteAsyncContextMap owner,
                             AtomicReferenceFieldUpdater<CopyOnWriteAsyncContextMap, CopyAsyncContextMap> mapUpdater) {
            assert keyOne != null && keyTwo != null && keyThree != null && keyFour != null && keyFive != null &&
                    keySix != null;
            MutableInt removeIndexMask = new MutableInt();
            entries.forEach(k -> {
                if (keyOne.equals(k)) {
                    removeIndexMask.value |= 0x1;
                } else if (keyTwo.equals(k)) {
                    removeIndexMask.value |= 0x2;
                } else if (keyThree.equals(k)) {
                    removeIndexMask.value |= 0x4;
                } else if (keyFour.equals(k)) {
                    removeIndexMask.value |= 0x8;
                } else if (keyFive.equals(k)) {
                    removeIndexMask.value |= 0x10;
                } else if (keySix.equals(k)) {
                    removeIndexMask.value |= 0x20;
                }
            });

            CopyAsyncContextMap newMap = removeAll(removeIndexMask);
            if (newMap == null) {
                return false;
            }
            return mapUpdater.compareAndSet(owner, this, newMap) || owner.removeAll(entries);
        }

        @Nullable
        private CopyAsyncContextMap removeAll(MutableInt removeIndexMask) {
            assert keyOne != null && keyTwo != null && keyThree != null && keyFour != null && keyFive != null &&
                    keySix != null;
            if ((removeIndexMask.value & 0x3f) == 0x3f) {
                return EmptyAsyncContextMap.INSTANCE;
            } else if ((removeIndexMask.value & 0x20) == 0x20) {
                if ((removeIndexMask.value & 0x10) == 0x10) {
                    if ((removeIndexMask.value & 0x8) == 0x8) {
                        if ((removeIndexMask.value & 0x4) == 0x4) {
                            if ((removeIndexMask.value & 0x2) == 0x2) {
                                return new OneAsyncContextMap(keyOne, valueOne);
                            } else if ((removeIndexMask.value & 0x1) == 0x1) {
                                return new OneAsyncContextMap(keyTwo, valueTwo);
                            }
                            return new TwoAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo);
                        } else if ((removeIndexMask.value & 0x2) == 0x2) {
                            if ((removeIndexMask.value & 0x1) == 0x1) {
                                return new OneAsyncContextMap(keyThree, valueThree);
                            }
                            return new TwoAsyncContextMap(keyOne, valueOne, keyThree, valueThree);
                        } else if ((removeIndexMask.value & 0x1) == 0x1) {
                            return new TwoAsyncContextMap(keyTwo, valueTwo, keyThree, valueThree);
                        }
                        return new ThreeAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree);
                    } else if ((removeIndexMask.value & 0x4) == 0x4) {
                        if ((removeIndexMask.value & 0x2) == 0x2) {
                            if ((removeIndexMask.value & 0x1) == 0x1) {
                                return new OneAsyncContextMap(keyFour, valueFour);
                            }
                            return new TwoAsyncContextMap(keyOne, valueOne, keyFour, valueFour);
                        } else if ((removeIndexMask.value & 0x1) == 0x1) {
                            return new TwoAsyncContextMap(keyTwo, valueTwo, keyFour, valueFour);
                        }
                        return new ThreeAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyFour, valueFour);
                    } else if ((removeIndexMask.value & 0x2) == 0x2) {
                        if ((removeIndexMask.value & 0x1) == 0x1) {
                            return new TwoAsyncContextMap(keyThree, valueThree, keyFour, valueFour);
                        }
                        return new ThreeAsyncContextMap(keyOne, valueOne, keyThree, valueThree, keyFour, valueFour);
                    } else if ((removeIndexMask.value & 0x1) == 0x1) {
                        return new ThreeAsyncContextMap(keyTwo, valueTwo, keyThree, valueThree, keyFour, valueFour);
                    }
                    return new FourAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree,
                            keyFour, valueFour);
                } else if ((removeIndexMask.value & 0x8) == 0x8) {
                    if ((removeIndexMask.value & 0x4) == 0x4) {
                        if ((removeIndexMask.value & 0x2) == 0x2) {
                            if ((removeIndexMask.value & 0x1) == 0x1) {
                                return new OneAsyncContextMap(keyFive, valueFive);
                            }
                            return new TwoAsyncContextMap(keyOne, valueOne, keyFive, valueFive);
                        } else if ((removeIndexMask.value & 0x1) == 0x1) {
                            return new TwoAsyncContextMap(keyTwo, valueTwo, keyFive, valueFive);
                        }
                        return new ThreeAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyFive, valueFive);
                    } else if ((removeIndexMask.value & 0x2) == 0x2) {
                        if ((removeIndexMask.value & 0x1) == 0x1) {
                            return new TwoAsyncContextMap(keyThree, valueThree, keyFive, valueFive);
                        }
                        return new ThreeAsyncContextMap(keyOne, valueOne, keyThree, valueThree, keyFive, valueFive);
                    } else if ((removeIndexMask.value & 0x1) == 0x1) {
                        return new ThreeAsyncContextMap(keyTwo, valueTwo, keyThree, valueThree, keyFive, valueFive);
                    }
                    return new FourAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree,
                            keyFive, valueFive);
                } else if ((removeIndexMask.value & 0x4) == 0x4) {
                    if ((removeIndexMask.value & 0x2) == 0x2) {
                        if ((removeIndexMask.value & 0x1) == 0x1) {
                            return new TwoAsyncContextMap(keyFour, valueFour, keyFive, valueFive);
                        }
                        return new ThreeAsyncContextMap(keyOne, valueOne, keyFour, valueFour, keyFive, valueFive);
                    } else if ((removeIndexMask.value & 0x1) == 0x1) {
                        return new ThreeAsyncContextMap(keyTwo, valueTwo, keyFour, valueFour, keyFive, valueFive);
                    }
                    return new FourAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyFour, valueFour,
                            keyFive, valueFive);
                } else if ((removeIndexMask.value & 0x2) == 0x2) {
                    if ((removeIndexMask.value & 0x1) == 0x1) {
                        return new ThreeAsyncContextMap(keyThree, valueThree, keyFour, valueFour, keyFive, valueFive);
                    }
                    return new FourAsyncContextMap(keyOne, valueOne, keyThree, valueThree, keyFour, valueFour,
                            keyFive, valueFive);
                } else if ((removeIndexMask.value & 0x1) == 0x1) {
                    return new FourAsyncContextMap(keyTwo, valueTwo, keyThree, valueThree, keyFour, valueFour,
                            keyFive, valueFive);
                }
                return new FiveAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree,
                        keyFour, valueFour, keyFive, valueFive);
            } else if ((removeIndexMask.value & 0x10) == 0x10) {
                if ((removeIndexMask.value & 0x8) == 0x8) {
                    if ((removeIndexMask.value & 0x4) == 0x4) {
                        if ((removeIndexMask.value & 0x2) == 0x2) {
                            if ((removeIndexMask.value & 0x1) == 0x1) {
                                return new OneAsyncContextMap(keySix, valueSix);
                            }
                            return new TwoAsyncContextMap(keyOne, valueOne, keySix, valueSix);
                        } else if ((removeIndexMask.value & 0x1) == 0x1) {
                            return new TwoAsyncContextMap(keyTwo, valueTwo, keySix, valueSix);
                        }
                        return new ThreeAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keySix, valueSix);
                    } else if ((removeIndexMask.value & 0x2) == 0x2) {
                        if ((removeIndexMask.value & 0x1) == 0x1) {
                            return new TwoAsyncContextMap(keyThree, valueThree, keySix, valueSix);
                        }
                        return new ThreeAsyncContextMap(keyOne, valueOne, keyThree, valueThree, keySix, valueSix);
                    } else if ((removeIndexMask.value & 0x1) == 0x1) {
                        return new ThreeAsyncContextMap(keyTwo, valueTwo, keyThree, valueThree, keySix, valueSix);
                    }
                    return new FourAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree,
                            keySix, valueSix);
                } else if ((removeIndexMask.value & 0x4) == 0x4) {
                    if ((removeIndexMask.value & 0x2) == 0x2) {
                        if ((removeIndexMask.value & 0x1) == 0x1) {
                            return new TwoAsyncContextMap(keyFour, valueFour, keySix, valueSix);
                        }
                        return new ThreeAsyncContextMap(keyOne, valueOne, keyFour, valueFour, keySix, valueSix);
                    } else if ((removeIndexMask.value & 0x1) == 0x1) {
                        return new ThreeAsyncContextMap(keyTwo, valueTwo, keyFour, valueFour, keySix, valueSix);
                    }
                    return new FourAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyFour, valueFour,
                            keySix, valueSix);
                } else if ((removeIndexMask.value & 0x2) == 0x2) {
                    if ((removeIndexMask.value & 0x1) == 0x1) {
                        return new ThreeAsyncContextMap(keyThree, valueThree, keyFour, valueFour, keySix, valueSix);
                    }
                    return new FourAsyncContextMap(keyOne, valueOne, keyThree, valueThree, keyFour, valueFour,
                            keySix, valueSix);
                } else if ((removeIndexMask.value & 0x1) == 0x1) {
                    return new FourAsyncContextMap(keyTwo, valueTwo, keyThree, valueThree, keyFour, valueFour,
                            keySix, valueSix);
                }
                return new FiveAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree,
                        keyFour, valueFour, keySix, valueSix);
            } else if ((removeIndexMask.value & 0x8) == 0x8) {
                if ((removeIndexMask.value & 0x4) == 0x4) {
                    if ((removeIndexMask.value & 0x2) == 0x2) {
                        if ((removeIndexMask.value & 0x1) == 0x1) {
                            return new TwoAsyncContextMap(keyFive, valueFive, keySix, valueSix);
                        }
                        return new ThreeAsyncContextMap(keyOne, valueOne, keyFive, valueFive, keySix, valueSix);
                    } else if ((removeIndexMask.value & 0x1) == 0x1) {
                        return new ThreeAsyncContextMap(keyTwo, valueTwo, keyFive, valueFive, keySix, valueSix);
                    }
                    return new FourAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyFive, valueFive,
                            keySix, valueSix);
                } else if ((removeIndexMask.value & 0x2) == 0x2) {
                    if ((removeIndexMask.value & 0x1) == 0x1) {
                        return new ThreeAsyncContextMap(keyThree, valueThree, keyFive, valueFive, keySix, valueSix);
                    }
                    return new FourAsyncContextMap(keyOne, valueOne, keyThree, valueThree, keyFive, valueFive,
                            keySix, valueSix);
                } else if ((removeIndexMask.value & 0x1) == 0x1) {
                    return new FourAsyncContextMap(keyTwo, valueTwo, keyThree, valueThree, keyFive, valueFive,
                            keySix, valueSix);
                }
                return new FiveAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyThree, valueThree,
                        keyFive, valueFive, keySix, valueSix);
            } else if ((removeIndexMask.value & 0x4) == 0x4) {
                if ((removeIndexMask.value & 0x2) == 0x2) {
                    if ((removeIndexMask.value & 0x1) == 0x1) {
                        return new ThreeAsyncContextMap(keyFour, valueFour, keyFive, valueFive, keySix, valueSix);
                    }
                    return new FourAsyncContextMap(keyOne, valueOne, keyFour, valueFour, keyFive, valueFive,
                            keySix, valueSix);
                } else if ((removeIndexMask.value & 0x1) == 0x1) {
                    return new FourAsyncContextMap(keyTwo, valueTwo, keyFour, valueFour, keyFive, valueFive,
                            keySix, valueSix);
                }
                return new FiveAsyncContextMap(keyOne, valueOne, keyTwo, valueTwo, keyFour, valueFour,
                        keyFive, valueFive, keySix, valueSix);
            } else if ((removeIndexMask.value & 0x2) == 0x2) {
                if ((removeIndexMask.value & 0x1) == 0x1) {
                    return new FourAsyncContextMap(keyThree, valueThree, keyFour, valueFour, keyFive, valueFive,
                            keySix, valueSix);
                }
                return new FiveAsyncContextMap(keyOne, valueOne, keyThree, valueThree, keyFour, valueFour,
                        keyFive, valueFive, keySix, valueSix);
            } else if ((removeIndexMask.value & 0x1) == 0x1) {
                return new FiveAsyncContextMap(keyTwo, valueTwo, keyThree, valueThree, keyFour, valueFour,
                        keyFive, valueFive, keySix, valueSix);
            }
            return null;
        }

        @Nullable
        @Override
        public Key<?> forEach(final BiPredicate<Key<?>, Object> consumer) {
            if (!consumer.test(keyOne, valueOne)) {
                return keyOne;
            }
            if (!consumer.test(keyTwo, valueTwo)) {
                return keyTwo;
            }
            if (!consumer.test(keyThree, valueThree)) {
                return keyThree;
            }
            if (!consumer.test(keyFour, valueFour)) {
                return keyFour;
            }
            if (!consumer.test(keyFive, valueFive)) {
                return keyFive;
            }
            return consumer.test(keySix, valueSix) ? null : keySix;
        }

        @Override
        public void accept(final Key<?> key, final Object value) {
            test(key, value);
        }

        @Override
        public boolean test(final Key<?> key, final Object value) {
            if (keyOne == null) {
                keyOne = key;
                valueOne = value;
            } else if (keyTwo == null) {
                keyTwo = key;
                valueTwo = value;
            } else if (keyThree == null) {
                keyThree = key;
                valueThree = value;
            } else if (keyFour == null) {
                keyFour = key;
                valueFour = value;
            } else if (keyFive == null) {
                keyFive = key;
                valueFive = value;
            } else {
                assert keySix == null;
                keySix = key;
                valueSix = value;
                return false;
            }
            return true;
        }

        private static final class PutSevenBuilder extends AbstractPutSevenBuilder {
            PutSevenBuilder(Key<?> keyOne, @Nullable Object valueOne, Key<?> keyTwo, @Nullable Object valueTwo,
                            Key<?> keyThree, @Nullable Object valueThree, Key<?> keyFour, @Nullable Object valueFour,
                            Key<?> keyFive, @Nullable Object valueFive, Key<?> keySix, @Nullable Object valueSix,
                            int putAllMapSize) {
                super((6 + putAllMapSize) << 1, 12);
                pairs[0] = keyOne;
                pairs[1] = valueOne;
                pairs[2] = keyTwo;
                pairs[3] = valueTwo;
                pairs[4] = keyThree;
                pairs[5] = valueThree;
                pairs[6] = keyFour;
                pairs[7] = valueFour;
                pairs[8] = keyFive;
                pairs[9] = valueFive;
                pairs[10] = keySix;
                pairs[11] = valueSix;
            }

            CopyAsyncContextMap build() {
                if (nextIndex == 12) {
                    return new SixAsyncContextMap((Key<?>) pairs[0], pairs[1], (Key<?>) pairs[2], pairs[3],
                            (Key<?>) pairs[4], pairs[5], (Key<?>) pairs[6], pairs[7], (Key<?>) pairs[8], pairs[9],
                            (Key<?>) pairs[10], pairs[11]);
                }
                if (nextIndex == pairs.length) {
                    return new SevenOrMoreAsyncContextMap(pairs);
                }
                return new SevenOrMoreAsyncContextMap(Arrays.copyOf(pairs, nextIndex));
            }
        }
    }

    private static final class SevenOrMoreAsyncContextMap implements CopyAsyncContextMap {
        /**
         * Array of <[i] = key, [i+1] = value> pairs.
         */
        private final Object[] context;

        SevenOrMoreAsyncContextMap(Object... context) {
            this.context = context;
        }

        @Override
        public boolean isEmpty() {
            return false;
        }

        @Override
        public int size() {
            return context.length >>> 1;
        }

        @SuppressWarnings("unchecked")
        @Override
        public <T> T get(Key<T> key) {
            int i = findIndex(key);
            return i < 0 ? null : (T) context[i + 1];
        }

        @Override
        public boolean containsKey(Key<?> key) {
            return findIndex(key) >= 0;
        }

        @SuppressWarnings("unchecked")
        @Nullable
        @Override
        public <T> T put(Key<T> key, @Nullable T value, CopyOnWriteAsyncContextMap owner,
                         AtomicReferenceFieldUpdater<CopyOnWriteAsyncContextMap, CopyAsyncContextMap> mapUpdater) {
            int i = findIndex(Objects.requireNonNull(key));
            final Object[] context;
            if (i < 0) {
                context = new Object[this.context.length + 2];
                arraycopy(this.context, 0, context, 0, this.context.length);
                context[this.context.length] = key;
                context[this.context.length + 1] = value;
            } else {
                context = new Object[this.context.length];
                arraycopy(this.context, 0, context, 0, i + 1);
                context[i + 1] = value;
                if (i + 2 < context.length) {
                    arraycopy(this.context, i + 2, context, i + 2, context.length - i - 2);
                }
            }
            return mapUpdater.compareAndSet(owner, this, new SevenOrMoreAsyncContextMap(context)) ?
                    (T) this.context[i + 1] : null;
        }

        @Override
        public CopyAsyncContextMap putAll(Map<Key<?>, Object> map) {
            PutAllConsumer consumer = new PutAllConsumer(map.size());
            // First pass is to see how many new entries there are to get the correct size of the new context array
            map.forEach(consumer);

            Object[] context = new Object[this.context.length + (consumer.newItems << 1)];
            arraycopy(this.context, 0, context, 0, this.context.length);

            // Second pass is to fill entries where the keys overlap, or append new entries to the end
            PutAllPopulateConsumer populateConsumer =
                    new PutAllPopulateConsumer(consumer.keyIndexes, this.context, context);
            map.forEach(populateConsumer);
            return new SevenOrMoreAsyncContextMap(context);
        }

        @SuppressWarnings("unchecked")
        @Nullable
        @Override
        public <T> T remove(Key<T> key, CopyOnWriteAsyncContextMap owner,
                            AtomicReferenceFieldUpdater<CopyOnWriteAsyncContextMap, CopyAsyncContextMap> mapUpdater) {
            int i = findIndex(key);
            if (i < 0) {
                return null;
            }
            if (size() == 7) {
                return mapUpdater.compareAndSet(owner, this, removeBelowSeven(i)) ? (T) this.context[i + 1] :
                        owner.remove(key);
            }
            Object[] context = new Object[this.context.length - 2];
            arraycopy(this.context, 0, context, 0, i);
            arraycopy(this.context, i + 2, context, i, this.context.length - i - 2);
            return mapUpdater.compareAndSet(owner, this, new SevenOrMoreAsyncContextMap(context)) ?
                    (T) this.context[i + 1] : owner.remove(key);
        }

        @Override
        public boolean removeAll(Iterable<Key<?>> entries, CopyOnWriteAsyncContextMap owner,
                             AtomicReferenceFieldUpdater<CopyOnWriteAsyncContextMap, CopyAsyncContextMap> mapUpdater) {
            GrowableIntArray indexesToRemove = new GrowableIntArray(3);
            entries.forEach(key -> {
                int keyIndex = findIndex(key);
                if (keyIndex >= 0) {
                    indexesToRemove.add(keyIndex);
                }
            });

            CopyAsyncContextMap newMap = removeAll(indexesToRemove);
            if (newMap == null) {
                return false;
            }
            return mapUpdater.compareAndSet(owner, this, newMap) || owner.removeAll(entries);
        }

        private int findIndex(Key<?> key) {
            for (int i = 0; i < context.length; i += 2) {
                if (key.equals(context[i])) {
                    return i;
                }
            }
            return -1;
        }

        @Override
        public Key<?> forEach(BiPredicate<Key<?>, Object> consumer) {
            for (int i = 0; i < context.length; i += 2) {
                final Key<?> key = (Key<?>) context[i];
                if (!consumer.test(key, context[i + 1])) {
                    return key;
                }
            }
            return null;
        }

        @Nullable
        private CopyAsyncContextMap removeAll(GrowableIntArray indexesToRemove) {
            if (size() == indexesToRemove.size) {
                return EmptyAsyncContextMap.INSTANCE;
            } else if (indexesToRemove.size == 0) {
                return null;
            } else if (size() - indexesToRemove.size < 7) {
                return removeBelowSeven(indexesToRemove);
            }

            Object[] context = new Object[this.context.length - (indexesToRemove.size << 1)];

            // Preserve entries that were not found in the first pass
            int newContextIndex = 0;
            for (int i = 0; i < this.context.length; i += 2) {
                if (indexesToRemove.isValueAbsent(i)) {
                    context[newContextIndex] = this.context[i];
                    context[newContextIndex + 1] = this.context[i + 1];
                    newContextIndex += 2;
                }
            }
            return new SevenOrMoreAsyncContextMap(context);
        }

        private CopyAsyncContextMap removeBelowSeven(int i) {
            switch (i) {
                case 0:
                    return new SixAsyncContextMap((Key<?>) context[2], context[3],
                            (Key<?>) context[4], context[5],
                            (Key<?>) context[6], context[7],
                            (Key<?>) context[8], context[9],
                            (Key<?>) context[10], context[11],
                            (Key<?>) context[12], context[13]);
                case 2:
                    return new SixAsyncContextMap((Key<?>) context[0], context[1],
                            (Key<?>) context[4], context[5],
                            (Key<?>) context[6], context[7],
                            (Key<?>) context[8], context[9],
                            (Key<?>) context[10], context[11],
                            (Key<?>) context[12], context[13]);
                case 4:
                    return new SixAsyncContextMap((Key<?>) context[0], context[1],
                            (Key<?>) context[2], context[3],
                            (Key<?>) context[6], context[7],
                            (Key<?>) context[8], context[9],
                            (Key<?>) context[10], context[11],
                            (Key<?>) context[12], context[13]);
                case 6:
                    return new SixAsyncContextMap((Key<?>) context[0], context[1],
                            (Key<?>) context[2], context[3],
                            (Key<?>) context[4], context[5],
                            (Key<?>) context[8], context[9],
                            (Key<?>) context[10], context[11],
                            (Key<?>) context[12], context[13]);
                case 8:
                    return new SixAsyncContextMap((Key<?>) context[0], context[1],
                            (Key<?>) context[2], context[3],
                            (Key<?>) context[4], context[5],
                            (Key<?>) context[6], context[7],
                            (Key<?>) context[10], context[11],
                            (Key<?>) context[12], context[13]);
                case 10:
                    return new SixAsyncContextMap((Key<?>) context[0], context[1],
                            (Key<?>) context[2], context[3],
                            (Key<?>) context[4], context[5],
                            (Key<?>) context[6], context[7],
                            (Key<?>) context[8], context[9],
                            (Key<?>) context[12], context[13]);
                case 12:
                    return new SixAsyncContextMap((Key<?>) context[0], context[1],
                            (Key<?>) context[2], context[3],
                            (Key<?>) context[4], context[5],
                            (Key<?>) context[6], context[7],
                            (Key<?>) context[8], context[9],
                            (Key<?>) context[10], context[11]);
                default:
                    throw new RuntimeException("programming error. unable to remove i: " + i);
            }
        }

        private CopyAsyncContextMap removeBelowSeven(GrowableIntArray indexesToRemove) {
            switch (size() - indexesToRemove.size) {
                case 1:
                    for (int i = 0; i < this.context.length; i += 2) {
                        if (indexesToRemove.isValueAbsent(i)) {
                            return new OneAsyncContextMap((Key<?>) context[i], context[i + 1]);
                        }
                    }
                    break;
                case 2: {
                    int keepI1 = -1;
                    for (int i = 0; i < this.context.length; i += 2) {
                        if (indexesToRemove.isValueAbsent(i)) {
                            if (keepI1 < 0) {
                                keepI1 = i;
                            } else {
                                return new TwoAsyncContextMap((Key<?>) context[keepI1], context[keepI1 + 1],
                                        (Key<?>) context[i], context[i + 1]);
                            }