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

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

/*
 *  Licensed to GraphHopper GmbH under one or more contributor
 *  license agreements. See the NOTICE file distributed with this work for
 *  additional information regarding copyright ownership.
 *
 *  GraphHopper GmbH 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 com.graphhopper.util.BitUtil;
import com.graphhopper.util.Helper;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteOrder;

/**
 * @author Peter Karich
 */
public abstract class AbstractDataAccess implements DataAccess {
    protected static final int SEGMENT_SIZE_MIN = 1 << 7;
    // reserve some space for downstream usage (in classes using/extending this)
    protected static final int HEADER_OFFSET = 20 * 4 + 20;
    protected static final byte[] EMPTY = new byte[1024];
    private static final int SEGMENT_SIZE_DEFAULT = 1 << 20;
    protected final ByteOrder byteOrder;
    protected final BitUtil bitUtil;
    private final String location;
    protected int header[] = new int[(HEADER_OFFSET - 20) / 4];
    protected String name;
    protected int segmentSizeInBytes = SEGMENT_SIZE_DEFAULT;
    protected int segmentSizePower;
    protected int indexDivisor;
    protected boolean closed = false;

    public AbstractDataAccess(String name, String location, ByteOrder order) {
        byteOrder = order;
        bitUtil = BitUtil.get(order);
        this.name = name;
        if (!Helper.isEmpty(location) && !location.endsWith("/"))
            throw new IllegalArgumentException("Create DataAccess object via its corresponding Directory!");

        this.location = location;
    }

    @Override
    public String getName() {
        return name;
    }

    protected String getFullName() {
        return location + name;
    }

    @Override
    public void close() {
        closed = true;
    }

    @Override
    public boolean isClosed() {
        return closed;
    }

    @Override
    public void setHeader(int bytePos, int value) {
        bytePos >>= 2;
        header[bytePos] = value;
    }

    @Override
    public int getHeader(int bytePos) {
        bytePos >>= 2;
        return header[bytePos];
    }

    /**
     * Writes some internal data into the beginning of the specified file.
     */
    protected void writeHeader(RandomAccessFile file, long length, int segmentSize) throws IOException {
        file.seek(0);
        file.writeUTF("GH");
        file.writeLong(length);
        file.writeInt(segmentSize);
        for (int i = 0; i < header.length; i++) {
            file.writeInt(header[i]);
        }
    }

    protected long readHeader(RandomAccessFile raFile) throws IOException {
        raFile.seek(0);
        if (raFile.length() == 0)
            return -1;

        String versionHint = raFile.readUTF();
        if (!"GH".equals(versionHint))
            throw new IllegalArgumentException("Not a GraphHopper file! Expected 'GH' as file marker but was " + versionHint);

        long bytes = raFile.readLong();
        setSegmentSize(raFile.readInt());
        for (int i = 0; i < header.length; i++) {
            header[i] = raFile.readInt();
        }
        return bytes;
    }

    protected void copyHeader(DataAccess da) {
        for (int h = 0; h < header.length * 4; h += 4) {
            da.setHeader(h, getHeader(h));
        }
    }

    @Override
    public DataAccess copyTo(DataAccess da) {
        copyHeader(da);
        da.ensureCapacity(getCapacity());
        long cap = getCapacity();
        // currently get/setBytes does not support copying more bytes then segmentSize
        int segSize = Math.min(da.getSegmentSize(), getSegmentSize());
        byte[] bytes = new byte[segSize];
        boolean externalIntBased = ((AbstractDataAccess) da).isIntBased();
        for (long bytePos = 0; bytePos < cap; bytePos += segSize) {
            // read
            if (isIntBased()) {
                for (int offset = 0; offset < segSize; offset += 4) {
                    bitUtil.fromInt(bytes, getInt(bytePos + offset), offset);
                }
            } else {
                getBytes(bytePos, bytes, segSize);
            }

            // write
            if (externalIntBased) {
                for (int offset = 0; offset < segSize; offset += 4) {
                    da.setInt(bytePos + offset, bitUtil.toInt(bytes, offset));
                }
            } else {
                da.setBytes(bytePos, bytes, segSize);
            }
        }
        return da;
    }

    @Override
    public DataAccess setSegmentSize(int bytes) {
        if (bytes > 0) {
            // segment size should be a power of 2
            int tmp = (int) (Math.log(bytes) / Math.log(2));
            segmentSizeInBytes = Math.max((int) Math.pow(2, tmp), SEGMENT_SIZE_MIN);
        }
        segmentSizePower = (int) (Math.log(segmentSizeInBytes) / Math.log(2));
        indexDivisor = segmentSizeInBytes - 1;
        return this;
    }

    @Override
    public int getSegmentSize() {
        return segmentSizeInBytes;
    }

    @Override
    public String toString() {
        return getFullName();
    }

    @Override
    public void rename(String newName) {
        File file = new File(location + name);
        if (file.exists()) {
            try {
                if (!file.renameTo(new File(location + newName))) {
                    throw new IllegalStateException("Couldn't rename this " + getType() + " object to " + newName);
                }
                name = newName;
            } catch (Exception ex) {
                throw new IllegalStateException("Couldn't rename this " + getType() + " object!", ex);
            }
        } else {
            throw new IllegalStateException("File does not exist!? " + getFullName()
                    + " Make sure that you flushed before renaming. Otherwise it could make problems"
                    + " for memory mapped DataAccess objects");
        }
    }

    protected boolean checkBeforeRename(String newName) {
        if (Helper.isEmpty(newName))
            throw new IllegalArgumentException("newName mustn't be empty!");

        if (newName.equals(name))
            return false;

        if (isStoring() && new File(location + newName).exists())
            throw new IllegalArgumentException("file newName already exists!");

        return true;
    }

    public boolean isStoring() {
        return true;
    }

    protected boolean isIntBased() {
        return false;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy