All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.graphhopper.storage.UnsafeDataAccess Maven / Gradle / Ivy

Go to download

GraphHopper is a fast and memory efficient Java road routing engine working seamlessly with OpenStreetMap data.

There is a newer version: 0.7.0
Show newest version
/*
 *  Licensed to GraphHopper and Peter Karich under one or more contributor
 *  license agreements. See the NOTICE file distributed with this work for 
 *  additional information regarding copyright ownership.
 * 
 *  GraphHopper 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 com.graphhopper.storage;

import static com.graphhopper.storage.AbstractDataAccess.HEADER_OFFSET;
import com.graphhopper.util.NotThreadSafe;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.reflect.Field;
import java.nio.ByteOrder;

/**
 * This is a data structure which uses an unsafe access to native memory. The speed up compared to
 * RAMDataAccess is roughly 10% due to index calculations and BitUtil overhead in RAMDataAccess.
 * Notes:
 * 

* 1. Highly experimental. Still some bugs and access through file/MMAP should work at some point *

* 2. Compared to MMAP no syncDAWrapper is need to make it read and write safe from multiple threads *

* 3. Cannot be used on Android as no memory allocation methods are available there *

* @author Peter Karich */ @NotThreadSafe public class UnsafeDataAccess extends AbstractDataAccess { @SuppressWarnings("all") static final sun.misc.Unsafe UNSAFE; static { try { // On Android getting Unsafe fails as the field is named THE_ONE but Android has no memory allocation methods so it won't work nevertheless. // On Android we need JNI+malloc https://github.com/libgdx/libgdx/blob/5945211a88570ced7eafce95c68f6f1f7124cd23/gdx/src/com/badlogic/gdx/utils/BufferUtils.java#L287 @SuppressWarnings("all") Field field = sun.misc.Unsafe.class.getDeclaredField("theUnsafe"); field.setAccessible(true); UNSAFE = (sun.misc.Unsafe) field.get(null); } catch (Exception e) { throw new AssertionError(e); } } private long address; private long capacity; UnsafeDataAccess( String name, String location, ByteOrder order ) { super(name, location, order); } @Override public UnsafeDataAccess create( long bytes ) { // TODO use unsafe.pageSize() instead segmentSizeInBytes? // e.g. on my system pageSize is only 4096 setSegmentSize(segmentSizeInBytes); incCapacity(bytes); return this; } @Override public final boolean incCapacity( long bytes ) { return ensureCapacity(bytes, true); } final boolean ensureCapacity( long bytes, boolean clearNewMem ) { long oldCap = getCapacity(); long todoBytes = bytes - oldCap; if (todoBytes <= 0) return false; // avoid frequent increase of allocation area, instead increase by segment size int allSegments = (int) (bytes / segmentSizeInBytes); if (bytes % segmentSizeInBytes != 0) allSegments++; capacity = allSegments * segmentSizeInBytes; try { address = UNSAFE.reallocateMemory(address, capacity); } catch (OutOfMemoryError err) { throw new OutOfMemoryError(err.getMessage() + " - problem when allocating new memory. Old capacity: " + oldCap + ", new bytes:" + todoBytes + ", segmentSizeIntsPower:" + segmentSizePower); } if (clearNewMem) UNSAFE.setMemory(address + oldCap, capacity - oldCap, (byte) 0); return true; } @Override public DataAccess copyTo( DataAccess da ) { if (da instanceof UnsafeDataAccess) { // TODO // unsafe.copyMemory(address, da.address, capacity); // return this; } return super.copyTo(da); } @Override public boolean loadExisting() { if (isClosed()) throw new IllegalStateException("already closed"); File file = new File(getFullName()); if (!file.exists() || file.length() == 0) return false; try { RandomAccessFile raFile = new RandomAccessFile(getFullName(), "r"); try { long byteCount = readHeader(raFile) - HEADER_OFFSET; if (byteCount < 0) return false; raFile.seek(HEADER_OFFSET); int segmentCount = (int) (byteCount / segmentSizeInBytes); if (byteCount % segmentSizeInBytes != 0) segmentCount++; ensureCapacity(byteCount, false); byte[] bytes = new byte[segmentSizeInBytes]; for (int s = 0; s < segmentCount; s++) { int read = raFile.read(bytes); if (read <= 0) throw new IllegalStateException("segment " + s + " is empty? " + toString()); // is there a faster method? setBytes(s * segmentSizeInBytes, bytes, segmentSizeInBytes); } return true; } finally { raFile.close(); } } catch (IOException ex) { throw new RuntimeException("Problem while loading " + getFullName(), ex); } } @Override public void flush() { if (isClosed()) throw new IllegalStateException("already closed"); try { RandomAccessFile raFile = new RandomAccessFile(getFullName(), "rw"); try { long len = getCapacity(); writeHeader(raFile, len, segmentSizeInBytes); raFile.seek(HEADER_OFFSET); byte bytes[] = new byte[segmentSizeInBytes]; int segs = getSegments(); for (int s = 0; s < segs; s++) { getBytes(s * segmentSizeInBytes, bytes, segmentSizeInBytes); raFile.write(bytes); } } finally { raFile.close(); } } catch (Exception ex) { throw new RuntimeException("Couldn't store bytes to " + toString(), ex); } } @Override public void close() { super.close(); UNSAFE.freeMemory(address); } @Override public final void setInt( long bytePos, int value ) { UNSAFE.putInt(address + bytePos, value); } @Override public final int getInt( long bytePos ) { return UNSAFE.getInt(address + bytePos); } @Override public short getShort( long bytePos ) { return UNSAFE.getShort(address + bytePos); } @Override public void setShort( long bytePos, short value ) { UNSAFE.putShort(address + bytePos, value); } @Override public final void setBytes( long bytePos, byte[] values, int length ) { for (int offset = 0; offset < length; offset++) { UNSAFE.putByte(address + bytePos + offset, values[offset]); } } @Override public final void getBytes( long bytePos, byte[] values, int length ) { assert length <= segmentSizeInBytes : "the length has to be smaller or equal to the segment size: " + length + " vs. " + segmentSizeInBytes; for (int offset = 0; offset < length; offset++) { values[offset] = UNSAFE.getByte(address + bytePos + offset); } } @Override public final long getCapacity() { return capacity; } @Override public final int getSegments() { return (int) (capacity / segmentSizeInBytes); } @Override public final void trimTo( long bytes ) { if (bytes > this.capacity) throw new IllegalStateException("Use ensureCapacity to increase capacity!"); int allSegments = (int) (bytes / segmentSizeInBytes); if (bytes % segmentSizeInBytes != 0) allSegments++; if (allSegments <= 0) allSegments = 1; capacity = allSegments * segmentSizeInBytes; } @Override public DAType getType() { return DAType.UNSAFE_STORE; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy