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

io.netty.util.DefaultAttributeMap Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2012 The Netty Project
 *
 * The Netty Project licenses this file to you 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:
 *
 *   https://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.netty.util;

import io.netty.util.internal.ObjectUtil;

import java.util.Arrays;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

/**
 * Default {@link AttributeMap} implementation which not exibit any blocking behaviour on attribute lookup while using a
 * copy-on-write approach on the modify path.
Attributes lookup and remove exibit {@code O(logn)} time worst-case * complexity, hence {@code attribute::set(null)} is to be preferred to {@code remove}. */ public class DefaultAttributeMap implements AttributeMap { private static final AtomicReferenceFieldUpdater ATTRIBUTES_UPDATER = AtomicReferenceFieldUpdater.newUpdater(DefaultAttributeMap.class, DefaultAttribute[].class, "attributes"); private static final DefaultAttribute[] EMPTY_ATTRIBUTES = new DefaultAttribute[0]; /** * Similarly to {@code Arrays::binarySearch} it perform a binary search optimized for this use case, in order to * save polymorphic calls (on comparator side) and unnecessary class checks. */ private static int searchAttributeByKey(DefaultAttribute[] sortedAttributes, AttributeKey key) { int low = 0; int high = sortedAttributes.length - 1; while (low <= high) { int mid = low + high >>> 1; DefaultAttribute midVal = sortedAttributes[mid]; AttributeKey midValKey = midVal.key; if (midValKey == key) { return mid; } int midValKeyId = midValKey.id(); int keyId = key.id(); assert midValKeyId != keyId; boolean searchRight = midValKeyId < keyId; if (searchRight) { low = mid + 1; } else { high = mid - 1; } } return -(low + 1); } private static void orderedCopyOnInsert(DefaultAttribute[] sortedSrc, int srcLength, DefaultAttribute[] copy, DefaultAttribute toInsert) { // let's walk backward, because as a rule of thumb, toInsert.key.id() tends to be higher for new keys final int id = toInsert.key.id(); int i; for (i = srcLength - 1; i >= 0; i--) { DefaultAttribute attribute = sortedSrc[i]; assert attribute.key.id() != id; if (attribute.key.id() < id) { break; } copy[i + 1] = sortedSrc[i]; } copy[i + 1] = toInsert; final int toCopy = i + 1; if (toCopy > 0) { System.arraycopy(sortedSrc, 0, copy, 0, toCopy); } } private volatile DefaultAttribute[] attributes = EMPTY_ATTRIBUTES; @SuppressWarnings("unchecked") @Override public Attribute attr(AttributeKey key) { ObjectUtil.checkNotNull(key, "key"); DefaultAttribute newAttribute = null; for (;;) { final DefaultAttribute[] attributes = this.attributes; final int index = searchAttributeByKey(attributes, key); final DefaultAttribute[] newAttributes; if (index >= 0) { final DefaultAttribute attribute = attributes[index]; assert attribute.key() == key; if (!attribute.isRemoved()) { return attribute; } // let's try replace the removed attribute with a new one if (newAttribute == null) { newAttribute = new DefaultAttribute(this, key); } final int count = attributes.length; newAttributes = Arrays.copyOf(attributes, count); newAttributes[index] = newAttribute; } else { if (newAttribute == null) { newAttribute = new DefaultAttribute(this, key); } final int count = attributes.length; newAttributes = new DefaultAttribute[count + 1]; orderedCopyOnInsert(attributes, count, newAttributes, newAttribute); } if (ATTRIBUTES_UPDATER.compareAndSet(this, attributes, newAttributes)) { return newAttribute; } } } @Override public boolean hasAttr(AttributeKey key) { ObjectUtil.checkNotNull(key, "key"); return searchAttributeByKey(attributes, key) >= 0; } private void removeAttributeIfMatch(AttributeKey key, DefaultAttribute value) { for (;;) { final DefaultAttribute[] attributes = this.attributes; final int index = searchAttributeByKey(attributes, key); if (index < 0) { return; } final DefaultAttribute attribute = attributes[index]; assert attribute.key() == key; if (attribute != value) { return; } final int count = attributes.length; final int newCount = count - 1; final DefaultAttribute[] newAttributes = newCount == 0? EMPTY_ATTRIBUTES : new DefaultAttribute[newCount]; // perform 2 bulk copies System.arraycopy(attributes, 0, newAttributes, 0, index); final int remaining = count - index - 1; if (remaining > 0) { System.arraycopy(attributes, index + 1, newAttributes, index, remaining); } if (ATTRIBUTES_UPDATER.compareAndSet(this, attributes, newAttributes)) { return; } } } @SuppressWarnings("serial") private static final class DefaultAttribute extends AtomicReference implements Attribute { private static final AtomicReferenceFieldUpdater MAP_UPDATER = AtomicReferenceFieldUpdater.newUpdater(DefaultAttribute.class, DefaultAttributeMap.class, "attributeMap"); private static final long serialVersionUID = -2661411462200283011L; private volatile DefaultAttributeMap attributeMap; private final AttributeKey key; DefaultAttribute(DefaultAttributeMap attributeMap, AttributeKey key) { this.attributeMap = attributeMap; this.key = key; } @Override public AttributeKey key() { return key; } private boolean isRemoved() { return attributeMap == null; } @Override public T setIfAbsent(T value) { while (!compareAndSet(null, value)) { T old = get(); if (old != null) { return old; } } return null; } @Override public T getAndRemove() { final DefaultAttributeMap attributeMap = this.attributeMap; final boolean removed = attributeMap != null && MAP_UPDATER.compareAndSet(this, attributeMap, null); T oldValue = getAndSet(null); if (removed) { attributeMap.removeAttributeIfMatch(key, this); } return oldValue; } @Override public void remove() { final DefaultAttributeMap attributeMap = this.attributeMap; final boolean removed = attributeMap != null && MAP_UPDATER.compareAndSet(this, attributeMap, null); set(null); if (removed) { attributeMap.removeAttributeIfMatch(key, this); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy