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

org.xbib.helianthus.internal.DefaultAttributeMap Maven / Gradle / Ivy

package org.xbib.helianthus.internal;

import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
import io.netty.util.AttributeMap;

import java.util.Collections;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicReferenceArray;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

/**
 * Default {@link AttributeMap} implementation which use simple synchronization per bucket to keep the memory overhead
 * as low as possible.
 * Note: This class has been forked from {@link io.netty.util.DefaultAttributeMap}.
 */
public class DefaultAttributeMap implements AttributeMap {

    @SuppressWarnings({"rawtypes", "AtomicFieldUpdaterIssues"})
    private static final AtomicReferenceFieldUpdater updater =
            AtomicReferenceFieldUpdater.newUpdater(DefaultAttributeMap.class,
                    AtomicReferenceArray.class, "attributes");

    private static final int BUCKET_SIZE = 4;
    private static final int MASK = BUCKET_SIZE - 1;

    // Initialize lazily to reduce memory consumption; updated by AtomicReferenceFieldUpdater above.
    @SuppressWarnings("UnusedDeclaration")
    volatile AtomicReferenceArray> attributes;

    private static int index(AttributeKey key) {
        return key.id() & MASK;
    }

    @SuppressWarnings("unchecked")
    @Override
    public  Attribute attr(AttributeKey key) {
        if (key == null) {
            throw new NullPointerException("key");
        }
        AtomicReferenceArray> attributes = this.attributes;
        if (attributes == null) {
            // Not using ConcurrentHashMap due to high memory consumption.
            attributes = new AtomicReferenceArray<>(BUCKET_SIZE);

            if (!updater.compareAndSet(this, null, attributes)) {
                attributes = this.attributes;
            }
        }

        int i = index(key);
        DefaultAttribute head = attributes.get(i);
        if (head == null) {
            // No head exists yet which means we may be able to add the attribute without synchronization and just
            // use compare and set. At worst we need to fallback to synchronization
            head = new DefaultAttribute<>(key);
            if (attributes.compareAndSet(i, null, head)) {
                // we were able to add it so return the head right away
                return (Attribute) head;
            } else {
                head = attributes.get(i);
            }
        }

        synchronized (head) {
            DefaultAttribute curr = head;
            for (; ; ) {
                if (!curr.removed && curr.key == key) {
                    return (Attribute) curr;
                }

                DefaultAttribute next = curr.next;
                if (next == null) {
                    DefaultAttribute attr = new DefaultAttribute<>(head, key);
                    curr.next = attr;
                    attr.prev = curr;
                    return attr;
                } else {
                    curr = next;
                }
            }
        }
    }

    @Override
    public  boolean hasAttr(AttributeKey key) {
        if (key == null) {
            throw new NullPointerException("key");
        }
        AtomicReferenceArray> attributes = this.attributes;
        if (attributes == null) {
            // no attribute exists
            return false;
        }

        int i = index(key);
        DefaultAttribute head = attributes.get(i);
        if (head == null) {
            // No attribute exists which point to the bucket in which the head should be located
            return false;
        }

        // check on the head can be done without synchronization
        if (head.key == key && !head.removed) {
            return true;
        }

        synchronized (head) {
            // we need to synchronize on the head
            DefaultAttribute curr = head.next;
            while (curr != null) {
                if (!curr.removed && curr.key == key) {
                    return true;
                }
                curr = curr.next;
            }
            return false;
        }
    }

    public Iterator> attrs() {
        final AtomicReferenceArray> attributes = this.attributes;
        if (attributes == null) {
            return Collections.emptyIterator();
        }

        return new IteratorImpl(attributes);
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        for (Iterator> i = attrs(); i.hasNext(); ) {
            final Attribute a = i.next();
            if (sb.length() > 0) {
                sb.append(',');
            }
            sb.append(a.key().name()).append('=').append(a.get());
        }
        return sb.toString();
    }

    @SuppressWarnings("serial")
    private static final class DefaultAttribute extends AtomicReference implements Attribute {

        private static final long serialVersionUID = -2661411462200283011L;

        // The head of the linked-list this attribute belongs to, which may be itself
        private final DefaultAttribute head;
        private final AttributeKey key;

        // Double-linked list to prev and next node to allow fast removal
        private DefaultAttribute prev;
        private DefaultAttribute next;

        // Will be set to true one the attribute is removed via getAndRemove() or remove()
        private volatile boolean removed;

        DefaultAttribute(DefaultAttribute head, AttributeKey key) {
            this.head = head;
            this.key = key;
        }

        DefaultAttribute(AttributeKey key) {
            head = this;
            this.key = key;
        }

        @Override
        public AttributeKey key() {
            return key;
        }

        @Override
        public T setIfAbsent(T value) {
            while (!compareAndSet(null, value)) {
                T old = get();
                if (old != null) {
                    return old;
                }
            }
            return null;
        }

        @SuppressWarnings("deprecation")
        @Override
        public T getAndRemove() {
            removed = true;
            T oldValue = getAndSet(null);
            remove0();
            return oldValue;
        }

        @SuppressWarnings("deprecation")
        @Override
        public void remove() {
            removed = true;
            set(null);
            remove0();
        }

        private void remove0() {
            synchronized (head) {
                // We only update the linked-list structure if prev != null because if it is null this
                // DefaultAttribute acts also as head. The head must never be removed completely and just be
                // marked as removed as all synchronization is done on the head itself for each bucket.
                // The head itself will be GC'ed once the DefaultAttributeMap is GC'ed. So at most 5 heads will
                // be removed lazy as the array size is 5.
                if (prev != null) {
                    prev.next = next;

                    if (next != null) {
                        next.prev = prev;
                    }

                    // Null out prev and next - this will guard against multiple remove0() calls which may corrupt
                    // the linked list for the bucket.
                    prev = null;
                    next = null;
                }
            }
        }
    }

    private static final class IteratorImpl implements Iterator> {

        private final AtomicReferenceArray> attributes;

        private int i = -1;
        private DefaultAttribute next;

        IteratorImpl(AtomicReferenceArray> attributes) {
            this.attributes = attributes;
            next = findNext(null);
        }

        @Override
        public boolean hasNext() {
            return next != null;
        }

        @Override
        public Attribute next() {
            if (next == null) {
                throw new NoSuchElementException();
            }
            final DefaultAttribute next = this.next;
            this.next = findNext(next.next);
            return next;
        }

        private DefaultAttribute findNext(DefaultAttribute next) {
            while (true) {
                if (next == null) {
                    for (i++; i < attributes.length(); i++) {
                        final DefaultAttribute attr = attributes.get(i);
                        if (attr != null) {
                            next = attr;
                            break;
                        }
                    }
                    if (i == attributes.length()) {
                        return null;
                    }
                }
                boolean found = false;
                while (next != null && next.removed) {
                    if ((next = next.next) == null) {
                        found = true;
                        break;
                    }
                }
                if (!found) {
                    return next;
                }
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy