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

org.oscim.tiling.source.mapfile.IndexCache Maven / Gradle / Ivy

/*
 * Copyright 2010, 2011, 2012 mapsforge.org
 *
 * This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
 *
 * This program is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with
 * this program. If not, see .
 */
package org.oscim.tiling.source.mapfile;

import org.oscim.tiling.source.mapfile.header.SubFileParameter;
import org.oscim.utils.LRUCache;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Collections;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * A cache for database index blocks with a fixed size and LRU policy.
 */
class IndexCache {
    /**
     * Number of index entries that one index block consists of.
     */
    private static final int INDEX_ENTRIES_PER_BLOCK = 128;

    private static final Logger LOG = Logger.getLogger(IndexCache.class.getName());

    /**
     * Maximum size in bytes of one index block.
     */
    private static final int SIZE_OF_INDEX_BLOCK = INDEX_ENTRIES_PER_BLOCK
            * SubFileParameter.BYTES_PER_INDEX_ENTRY;

    private final Map map;
    private final RandomAccessFile randomAccessFile;

    /**
     * @param randomAccessFile the map file from which the index should be read and cached.
     * @param capacity         the maximum number of entries in the cache.
     * @throws IllegalArgumentException if the capacity is negative.
     */
    IndexCache(RandomAccessFile randomAccessFile, int capacity) {
        this.randomAccessFile = randomAccessFile;
        this.map = Collections.synchronizedMap(new LRUCache(capacity));
    }

    /**
     * Destroy the cache at the end of its lifetime.
     */
    void destroy() {
        this.map.clear();
    }

    /**
     * Returns the index entry of a block in the given map file. If the required
     * index entry is not cached, it will be
     * read from the map file index and put in the cache.
     *
     * @param subFileParameter the parameters of the map file for which the index entry is
     *                         needed.
     * @param blockNumber      the number of the block in the map file.
     * @return the index entry or -1 if the block number is invalid.
     */
    synchronized long getIndexEntry(SubFileParameter subFileParameter, long blockNumber) {
        try {
            // check if the block number is out of bounds
            if (blockNumber >= subFileParameter.numberOfBlocks) {
                return -1;
            }

            // calculate the index block number
            long indexBlockNumber = blockNumber / INDEX_ENTRIES_PER_BLOCK;

            // create the cache entry key for this request
            IndexCacheEntryKey indexCacheEntryKey = new IndexCacheEntryKey(subFileParameter,
                    indexBlockNumber);

            // check for cached index block
            byte[] indexBlock = this.map.get(indexCacheEntryKey);
            if (indexBlock == null) {
                // cache miss, seek to the correct index block in the file and read it
                long indexBlockPosition = subFileParameter.indexStartAddress + indexBlockNumber
                        * SIZE_OF_INDEX_BLOCK;

                int remainingIndexSize = (int) (subFileParameter.indexEndAddress - indexBlockPosition);
                int indexBlockSize = Math.min(SIZE_OF_INDEX_BLOCK, remainingIndexSize);
                indexBlock = new byte[indexBlockSize];

                this.randomAccessFile.seek(indexBlockPosition);
                if (this.randomAccessFile.read(indexBlock, 0, indexBlockSize) != indexBlockSize) {
                    LOG.warning("reading the current index block has failed");
                    return -1;
                }

                // put the index block in the map
                this.map.put(indexCacheEntryKey, indexBlock);
            }

            // calculate the address of the index entry inside the index block
            long indexEntryInBlock = blockNumber % INDEX_ENTRIES_PER_BLOCK;
            int addressInIndexBlock = (int) (indexEntryInBlock * SubFileParameter.BYTES_PER_INDEX_ENTRY);

            // return the real index entry
            return Deserializer.getFiveBytesLong(indexBlock, addressInIndexBlock);
        } catch (IOException e) {
            LOG.log(Level.SEVERE, null, e);
            return -1;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy