/*
* Copyright 2016 higherfrequencytrading.com
*
* 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 net.openhft.lang.io;
import net.openhft.lang.io.serialization.BytesMarshallableSerializer;
import net.openhft.lang.io.serialization.BytesMarshallerFactory;
import net.openhft.lang.io.serialization.JDKZObjectSerializer;
import net.openhft.lang.io.serialization.ObjectSerializer;
import net.openhft.lang.io.serialization.impl.VanillaBytesMarshallerFactory;
import net.openhft.lang.model.constraints.NotNull;
import sun.misc.Cleaner;
import java.io.File;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author peter.lawrey
*/
public class DirectStore implements BytesStore, AutoCloseable {
private final ObjectSerializer objectSerializer;
private final Cleaner cleaner;
private final Deallocator deallocator;
private final AtomicInteger refCount = new AtomicInteger(1);
private long address;
private long size;
public DirectStore(long size) {
this(new VanillaBytesMarshallerFactory(), size);
}
private DirectStore(BytesMarshallerFactory bytesMarshallerFactory, long size) {
this(bytesMarshallerFactory, size, true);
}
private DirectStore(BytesMarshallerFactory bytesMarshallerFactory, long size, boolean zeroOut) {
this(BytesMarshallableSerializer.create(bytesMarshallerFactory, JDKZObjectSerializer.INSTANCE), size, zeroOut);
}
public DirectStore(ObjectSerializer objectSerializer, long size, boolean zeroOut) {
this.objectSerializer = objectSerializer;
address = NativeBytes.UNSAFE.allocateMemory(size);
// System.out.println("old value " + Integer.toHexString(NativeBytes.UNSAFE.getInt(null, address)));
if (zeroOut) {
NativeBytes.UNSAFE.setMemory(address, size, (byte) 0);
NativeBytes.UNSAFE.putLongVolatile(null, address, 0L);
}
this.size = size;
deallocator = new Deallocator(address);
cleaner = Cleaner.create(this, deallocator);
}
@NotNull
public static DirectStore allocate(long size) {
return new DirectStore(new VanillaBytesMarshallerFactory(), size);
}
@NotNull
public static DirectStore allocateLazy(long size) {
return new DirectStore(new VanillaBytesMarshallerFactory(), size, false);
}
public static BytesStore allocateLazy(long sizeInBytes, ObjectSerializer objectSerializer) {
return new DirectStore(objectSerializer, sizeInBytes, false);
}
@Override
public ObjectSerializer objectSerializer() {
return objectSerializer;
}
/**
* Resizes this {@code DirectStore} to the {@code newSize}.
*
* <p>If {@code zeroOut} is {@code false}, the memory past the old size is not zeroed out and
* will generally be garbage.
*
* <p>{@code DirectStore} don't keep track of the child {@code DirectBytes} instances, so after
* the resize they might point to the wrong memory. Use at your own risk.
*
* @param newSize new size of this {@code DirectStore}
* @param zeroOut if the memory past the old size should be zeroed out on increasing resize
* @throws IllegalArgumentException if the {@code newSize} is not positive
*/
public void resize(long newSize, boolean zeroOut) {
if (newSize <= 0)
throw new IllegalArgumentException("Given newSize is " + newSize +
" but should be positive");
address = deallocator.address = NativeBytes.UNSAFE.reallocateMemory(address, newSize);
if (zeroOut && newSize > size) {
NativeBytes.UNSAFE.setMemory(address + size, newSize - size, (byte) 0);
}
size = newSize;
}
@SuppressWarnings("ConstantConditions")
@NotNull
public DirectBytes bytes() {
boolean debug = false;
assert debug = true;
return debug ? new BoundsCheckingDirectBytes(this, refCount) : new DirectBytes(this, refCount);
}
@NotNull
public DirectBytes bytes(long offset, long length) {
return new DirectBytes(this, refCount, offset, length);
}
@Override
public long address() {
return address;
}
public void free() {
cleaner.clean();
}
public long size() {
return size;
}
@Override
public File file() {
return null;
}
/**
* calls free
*/
@Override
public void close() {
free();
}
/**
* Static nested class instead of anonymous because the latter would hold a strong reference to
* this DirectStore preventing it from becoming phantom-reachable.
*/
private static class Deallocator implements Runnable {
private volatile long address;
Deallocator(long address) {
assert address != 0;
this.address = address;
}
@Override
public void run() {
if (address == 0)
return;
NativeBytes.UNSAFE.freeMemory(address);
address = 0;
}
}
}