org.apache.activemq.artemis.utils.AbstractByteBufPool Maven / Gradle / Ivy
/**
* 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 } interner.
*
* 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 {
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.
*
* 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;
}
}
}