/*
* Copyright 2015 the original author or 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.atomix.catalyst.buffer;
import io.atomix.catalyst.util.reference.ReferenceManager;
import io.atomix.catalyst.buffer.util.HeapMemory;
import io.atomix.catalyst.buffer.util.HeapMemoryAllocator;
import io.atomix.catalyst.buffer.util.Memory;
/**
* Heap byte buffer implementation.
*
* @author <a href="http://github.com/kuujo">Jordan Halterman</a>
*/
public class UnsafeHeapBuffer extends AbstractBuffer {
/**
* Allocates a heap buffer with an initial capacity of {@code 4096} and a maximum capacity of {@link HeapMemory#MAX_SIZE}.
* <p>
* When the buffer is constructed, {@link HeapMemoryAllocator} will be used to allocate
* {@code capacity} bytes of memory on the Java heap. The resulting buffer will be initialized with a capacity of
* {@code 4096} and have a maximum capacity of {@link HeapMemory#MAX_SIZE}. The buffer's {@code capacity} will dynamically
* expand as bytes are written to the buffer. The underlying {@link UnsafeHeapBytes} will be initialized
* to the next power of {@code 2}.
*
* @return The heap buffer.
*
* @see UnsafeHeapBuffer#allocate(long)
* @see UnsafeHeapBuffer#allocate(long, long)
*/
public static UnsafeHeapBuffer allocate() {
return allocate(DEFAULT_INITIAL_CAPACITY, HeapMemory.MAX_SIZE);
}
/**
* Allocates a heap buffer with the given initial capacity.
* <p>
* When the buffer is constructed, {@link HeapMemoryAllocator} will be used to allocate
* {@code capacity} bytes of memory on the Java heap. The resulting buffer will have an initial capacity of {@code capacity}.
* The underlying {@link UnsafeHeapBytes} will be initialized to the next power of {@code 2}.
*
* @param initialCapacity The initial capacity of the buffer to allocate (in bytes).
* @return The heap buffer.
* @throws IllegalArgumentException If {@code capacity} is greater than the maximum allowed capacity for
* an array on the Java heap - {@code Integer.MAX_VALUE - 5}
*
* @see UnsafeHeapBuffer#allocate()
* @see UnsafeHeapBuffer#allocate(long, long)
*/
public static UnsafeHeapBuffer allocate(long initialCapacity) {
return allocate(initialCapacity, HeapMemory.MAX_SIZE);
}
/**
* Allocates a new heap buffer.
* <p>
* When the buffer is constructed, {@link HeapMemoryAllocator} will be used to allocate
* {@code capacity} bytes of memory on the Java heap. The resulting buffer will have an initial capacity of
* {@code initialCapacity} and will be doubled up to {@code maxCapacity} as bytes are written to the buffer. The
* underlying {@link UnsafeHeapBytes} will be initialized to the next power of {@code 2}.
*
* @param initialCapacity The initial capacity of the buffer to allocate (in bytes).
* @param maxCapacity The maximum capacity of the buffer.
* @return The heap buffer.
* @throws IllegalArgumentException If {@code initialCapacity} or {@code maxCapacity} is greater than the
* maximum allowed count for an array on the Java heap - {@code Integer.MAX_VALUE - 5}
*
* @see UnsafeHeapBuffer#allocate()
* @see UnsafeHeapBuffer#allocate(long)
*/
public static UnsafeHeapBuffer allocate(long initialCapacity, long maxCapacity) {
if (initialCapacity > maxCapacity)
throw new IllegalArgumentException("initial capacity cannot be greater than maximum capacity");
if (initialCapacity > HeapMemory.MAX_SIZE)
throw new IllegalArgumentException("initial capacity for HeapBuffer cannot be greater than " + HeapMemory.MAX_SIZE);
if (maxCapacity > HeapMemory.MAX_SIZE)
throw new IllegalArgumentException("maximum capacity for HeapBuffer cannot be greater than " + HeapMemory.MAX_SIZE);
return new UnsafeHeapBuffer(new UnsafeHeapBytes(HeapMemory.allocate(Memory.Util.toPow2(initialCapacity))), 0, initialCapacity, maxCapacity);
}
/**
* Wraps the given bytes in a heap buffer.
* <p>
* The buffer will be created with an initial capacity and maximum capacity equal to the byte array count.
*
* @param bytes The bytes to wrap.
* @return The wrapped bytes.
*/
public static UnsafeHeapBuffer wrap(byte[] bytes) {
return new UnsafeHeapBuffer(UnsafeHeapBytes.wrap(bytes), 0, bytes.length, bytes.length);
}
private final UnsafeHeapBytes bytes;
protected UnsafeHeapBuffer(UnsafeHeapBytes bytes, ReferenceManager<Buffer> referenceManager) {
super(bytes, referenceManager);
this.bytes = bytes;
}
protected UnsafeHeapBuffer(UnsafeHeapBytes bytes, long offset, long initialCapacity, long maxCapacity) {
super(bytes, offset, initialCapacity, maxCapacity, null);
this.bytes = bytes;
}
@Override
protected void compact(long from, long to, long length) {
bytes.memory.unsafe().copyMemory(bytes.memory.array(), bytes.memory.address(from), bytes.memory.array(), bytes.memory.address(to), length);
bytes.memory.unsafe().setMemory(bytes.memory.array(), bytes.memory.address(from), length, (byte) 0);
}
@Override
public boolean hasArray() {
return true;
}
@Override
public byte[] array() {
return bytes.memory.array();
}
/**
* Resets the internal heap array.
*
* @param array The internal array.
* @return The heap buffer.
*/
public UnsafeHeapBuffer reset(byte[] array) {
bytes.memory.reset(array);
clear();
return this;
}
}