/* * 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.model.constraints.NotNull; import sun.misc.Unsafe; import java.io.IOException; import java.lang.reflect.Field; /** * Created by Rob Austin */ public class ChronicleUnsafe { public static final Unsafe UNSAFE; static { try { @SuppressWarnings("ALL") Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); UNSAFE = (Unsafe) theUnsafe.get(null); } catch (Exception e) { throw new AssertionError(e); } } private final MappedFile mappedFile; private final long mask; private MappedMemory mappedMemory = null; private long chunkSize; private long offset; private long last = -1; /** * @param mappedFile a until that able to map block of memory to a file * @throws IllegalStateException if the block size is not a power of 2 */ public ChronicleUnsafe(@NotNull MappedFile mappedFile) { long blockSize = mappedFile.blockSize(); if (((blockSize & -blockSize) != blockSize)) throw new IllegalStateException("the block size has to be a power of 2"); this.mappedFile = mappedFile; this.chunkSize = mappedFile.blockSize(); long shift = (int) (Math.log(blockSize) / Math.log(2)); mask = ~((1L << shift) - 1L); } public long toAddress(long address) { return (mask & address ^ this.last) == 0 ? address + offset : toAddress0(address); } public long toAddress0(long address) { int index = (int) ((address / chunkSize)); long remainder = address - (((long) index) * chunkSize); // index == 0 is the header, so we wont reference count the header if (mappedMemory != null && mappedMemory.index() != 0) mappedFile.release(mappedMemory); try { this.mappedMemory = mappedFile.acquire(index); } catch (IOException e) { throw new RuntimeException(e); } long result = mappedMemory.bytes().address() + remainder; this.offset = result - address; this.last = mask & address; return result; } public long toRemainingInChunk(long address) { int chunk = (int) ((address / chunkSize)); long remainder = address - (((long) chunk) * chunkSize); return mappedMemory.bytes().capacity() - remainder; } public int arrayBaseOffset(Class<?> aClass) { return UNSAFE.arrayBaseOffset(aClass); } public int pageSize() { throw new UnsupportedOperationException("todo (pageSize)"); } public long allocateMemory(int aVoid) { throw new UnsupportedOperationException("todo (allocateMemory)"); } public long getLong(byte[] bytes, long address) { return UNSAFE.getLong(bytes, toAddress(address)); } public long getLong(Object object, long address) { return UNSAFE.getLong(object, toAddress(address)); } public void setMemory(long startAddress, long len, byte defaultValue) { long remaining = len; while (remaining > 0) { long address = toAddress(startAddress); long remainingInChunk = toRemainingInChunk(startAddress); if (remainingInChunk > remaining) remainingInChunk = remaining; UNSAFE.setMemory(address, remainingInChunk, defaultValue); startAddress += remainingInChunk; remaining -= remainingInChunk; } } public byte getByte(long address) { return UNSAFE.getByte(toAddress(address)); } public void putByte(long address, byte value) { UNSAFE.putByte(toAddress(address), value); } public void putLong(long address, long value) { UNSAFE.putLong(toAddress(address), value); } public long getLong(long address) { return UNSAFE.getLong(toAddress(address)); } public void copyMemory(Object o, long positionAddr, Object bytes, long i, long len2) { throw new UnsupportedOperationException("todo (copyMemory)"); } public short getShort(long address) { return UNSAFE.getShort(toAddress(address)); } public char getChar(long address) { return UNSAFE.getChar(toAddress(address)); } public int getInt(long address) { return UNSAFE.getInt(toAddress(address)); } public int getIntVolatile(Object o, long address) { return UNSAFE.getIntVolatile(o, toAddress(address)); } public long getLongVolatile(Object o, long address) { return UNSAFE.getLongVolatile(o, toAddress(address)); } public float getFloat(long address) { return UNSAFE.getFloat(toAddress(address)); } public double getDouble(long address) { return UNSAFE.getDouble(toAddress(address)); } public void putShort(long address, short v) { UNSAFE.putShort(toAddress(address), v); } public void putChar(long address, char v) { UNSAFE.putChar(toAddress(address), v); } public void putInt(long address, int v) { UNSAFE.putInt(toAddress(address), v); } public void putOrderedInt(Object o, long address, int v) { UNSAFE.putOrderedInt(o, toAddress(address), v); } public boolean compareAndSwapInt(Object o, long address, int expected, int v) { return UNSAFE.compareAndSwapInt(o, toAddress(address), expected, v); } public void putOrderedLong(Object o, long address, long v) { UNSAFE.putOrderedLong(o, toAddress(address), v); } public boolean compareAndSwapLong(Object o, long address, long expected, long v) { return UNSAFE.compareAndSwapLong(o, toAddress(address), expected, v); } public void putFloat(long address, float v) { UNSAFE.putFloat(toAddress(address), v); } public void putDouble(long address, double v) { UNSAFE.putDouble(toAddress(address), v); } public void putLong(Object o, long address, long aLong) { UNSAFE.putLong(o, toAddress(address), aLong); } public void putByte(Object o, long address, byte aByte) { UNSAFE.putByte(o, toAddress(address), aByte); } public byte getByte(Object o, long address) { return UNSAFE.getByte(o, toAddress(address)); } public void copyMemory(long l, long positionAddr, long length) { throw new UnsupportedOperationException("todo (copyMemory)"); } }