org.apache.activemq.artemis.utils.AbstractByteBufPool.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.activemq.artemis.utils.AbstractByteBufPool.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF 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
 *
 * 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 org.apache.activemq.artemis.utils;

import io.netty.buffer.ByteBuf;
import io.netty.util.internal.MathUtil;
import io.netty.util.internal.PlatformDependent;

/**
 * Thread-safe {@code <T>} interner.
 * <p>
 * Differently from {@link String#intern()} it contains a fixed amount of entries and
 * when used by concurrent threads it doesn't ensure the uniqueness of the entries ie
 * the same entry could be allocated multiple times by concurrent calls.
 */
public abstract class AbstractByteBufPool<T> {

    public static final int DEFAULT_POOL_CAPACITY = 32;

    private final T[] entries;
    private final int mask;
    private final int shift;

    public AbstractByteBufPool() {
        this(DEFAULT_POOL_CAPACITY);
    }

    public AbstractByteBufPool(final int capacity) {
        entries = (T[]) new Object[MathUtil.findNextPositivePowerOfTwo(capacity)];
        mask = entries.length - 1;
        //log2 of entries.length
        shift = 31 - Integer.numberOfLeadingZeros(entries.length);
    }

    /**
     * Batch hash code implementation that works at its best if {@code bytes}
     * contains a {@link org.apache.activemq.artemis.api.core.SimpleString} encoded.
     */
    private static int hashCode(final ByteBuf bytes, final int offset, final int length) {
        if (PlatformDependent.isUnaligned() && PlatformDependent.hasUnsafe()) {
            //if the platform allows it, the hash code could be computed without bounds checking
            if (bytes.hasArray()) {
                return onHeapHashCode(bytes.array(), bytes.arrayOffset() + offset, length);
            } else if (bytes.hasMemoryAddress()) {
                return offHeapHashCode(bytes.memoryAddress(), offset, length);
            }
        }
        return byteBufHashCode(bytes, offset, length);
    }

    private static int onHeapHashCode(final byte[] bytes, final int offset, final int length) {
        final int intCount = length >>> 1;
        final int byteCount = length & 1;
        int hashCode = 1;
        int arrayIndex = offset;
        for (int i = 0; i < intCount; i++) {
            hashCode = 31 * hashCode + PlatformDependent.getShort(bytes, arrayIndex);
            arrayIndex += 2;
        }
        for (int i = 0; i < byteCount; i++) {
            hashCode = 31 * hashCode + PlatformDependent.getByte(bytes, arrayIndex++);
        }
        return hashCode;
    }

    private static int offHeapHashCode(final long address, final int offset, final int length) {
        final int intCount = length >>> 1;
        final int byteCount = length & 1;
        int hashCode = 1;
        int arrayIndex = offset;
        for (int i = 0; i < intCount; i++) {
            hashCode = 31 * hashCode + PlatformDependent.getShort(address + arrayIndex);
            arrayIndex += 2;
        }
        for (int i = 0; i < byteCount; i++) {
            hashCode = 31 * hashCode + PlatformDependent.getByte(address + arrayIndex++);
        }
        return hashCode;
    }

    private static int byteBufHashCode(final ByteBuf byteBuf, final int offset, final int length) {
        final int intCount = length >>> 1;
        final int byteCount = length & 1;
        int hashCode = 1;
        int arrayIndex = offset;
        for (int i = 0; i < intCount; i++) {
            final short shortLE = byteBuf.getShortLE(arrayIndex);
            final short nativeShort = PlatformDependent.BIG_ENDIAN_NATIVE_ORDER ? Short.reverseBytes(shortLE)
                    : shortLE;
            hashCode = 31 * hashCode + nativeShort;
            arrayIndex += 2;
        }
        for (int i = 0; i < byteCount; i++) {
            hashCode = 31 * hashCode + byteBuf.getByte(arrayIndex++);
        }
        return hashCode;
    }

    /**
     * Returns {@code true} if {@code length}'s {@code byteBuf} content from {@link ByteBuf#readerIndex()} can be pooled,
     * {@code false} otherwise.
     */
    protected abstract boolean canPool(ByteBuf byteBuf, int length);

    /**
     * Create a new entry.
     */
    protected abstract T create(ByteBuf byteBuf, int length);

    /**
     * Returns {@code true} if the {@code entry} content is the same of {@code byteBuf} at the specified {@code offset}
     * and {@code length} {@code false} otherwise.
     */
    protected abstract boolean isEqual(T entry, ByteBuf byteBuf, int offset, int length);

    /**
     * Returns a pooled entry if possible, a new one otherwise.
     * <p>
     * The {@code byteBuf}'s {@link ByteBuf#readerIndex()} is incremented by {@code length} after it.
     */
    public final T getOrCreate(final ByteBuf byteBuf) {
        final int length = byteBuf.readInt();
        if (!canPool(byteBuf, length)) {
            return create(byteBuf, length);
        } else {
            if (!byteBuf.isReadable(length)) {
                throw new IndexOutOfBoundsException();
            }
            final int bytesOffset = byteBuf.readerIndex();
            final int hashCode = hashCode(byteBuf, bytesOffset, length);
            //fast % operation with power of 2 entries.length
            final int firstIndex = hashCode & mask;
            final T firstEntry = entries[firstIndex];
            if (isEqual(firstEntry, byteBuf, bytesOffset, length)) {
                byteBuf.skipBytes(length);
                return firstEntry;
            }
            final int secondIndex = (hashCode >> shift) & mask;
            final T secondEntry = entries[secondIndex];
            if (isEqual(secondEntry, byteBuf, bytesOffset, length)) {
                byteBuf.skipBytes(length);
                return secondEntry;
            }
            final T internedEntry = create(byteBuf, length);
            final int entryIndex = firstEntry == null ? firstIndex : secondIndex;
            entries[entryIndex] = internedEntry;
            return internedEntry;
        }
    }
}