📄 datastore.java
字号:
/* Sesame - Storage and Querying architecture for RDF and RDF Schema * Copyright (C) 2001-2005 Aduna * * Contact: * Aduna * Prinses Julianaplein 14 b * 3817 CS Amersfoort * The Netherlands * tel. +33 (0)33 465 99 87 * fax. +33 (0)33 465 99 87 * * http://aduna.biz/ * http://www.openrdf.org/ * * This library 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 2.1 of the License, or (at your option) any later version. * * This library 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 library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */package org.openrdf.sesame.sailimpl.nativerdf.datastore;import java.io.File;import java.io.IOException;import java.util.Arrays;import java.util.LinkedList;import java.util.Iterator;import java.util.zip.CRC32;import org.openrdf.util.ByteArrayUtil;/** * Class that provides indexed storage and retrieval of RDF values. **/public class DataStore {/*-------------+| Constants |+-------------*/ private static final int MRU_CACHE_SIZE = 512;/*-------------+| Variables |+-------------*/ private DataFile _dataFile; private IDFile _idFile; private HashFile _hashFile; /** * A most-recently used cache for DataItems, to be used during transactions. **/ //FIXME: replace LinkedList with LinkedHashMap? private LinkedList _mruCache; private CRC32 _crc32 = new CRC32();/*-------------+| Constructors |+-------------*/ public DataStore(File dataDir, String filePrefix) throws IOException { _dataFile = new DataFile(new File(dataDir, filePrefix+".dat")); _idFile = new IDFile(new File(dataDir, filePrefix+".id")); _hashFile = new HashFile(new File(dataDir, filePrefix+".hash")); _mruCache = new LinkedList(); }/*----------+| Methods |+----------*/ /** * Gets the value for the specified ID. * * @param id A value ID. * @return The value for the ID, or <tt>null</tt> no such value could be * found. * @exception IOException If an I/O error occurred. **/ public byte[] getData(int id) throws IOException { return getData(id, false); } /** * Gets the value for the specified ID, optionally searching the * non-commited data. * * @param id A value ID. * @param dirtyReads Flag indicating whether the non-commited data should * be searched. * @return The value for the ID, or <tt>null</tt> no such value could be * found. * @exception IOException If an I/O error occurred. **/ public byte[] getData(int id, boolean dirtyReads) throws IOException { long offset = _idFile.getOffset(id, dirtyReads); if (offset != 0L) { byte[] combinedData = _dataFile.getData(offset, dirtyReads); return _getDataFromCombinedArray(combinedData); } return null; } /** * Gets the ID for the specified value. * * @param queryData A value. * @return The ID for the specified value, or <tt>0</tt> if no such ID could * be found. * @exception IOException If an I/O error occurred. **/ public int getID(byte[] queryData) throws IOException { return getID(queryData, false); } /** * Gets the ID for the specified value, optionally search the non-committed * data. * * @param queryData A value. * @param dirtyReads Flag indicating whether the non-commited data should * be searched. * @return The ID for the specified value, or <tt>0</tt> if no such ID could * be found. * @exception IOException If an I/O error occurred. **/ public int getID(byte[] queryData, boolean dirtyReads) throws IOException { int hash = _getHash(queryData); HashFile.OffsetIterator iter = _hashFile.getOffsetIterator(hash, dirtyReads); long offset = iter.next(); while (offset >= 0L) { byte[] combinedData = _dataFile.getData(offset, dirtyReads); // First 8 bytes in combinedData form the count and ID, the rest of the bytes should match if (queryData.length + 8 == combinedData.length && ByteArrayUtil.regionMatches(queryData, combinedData, 8)) { // Stored data matches, return this data's ID return _getIdFromCombinedArray(combinedData); } // Data doesn't match, try next offset offset = iter.next(); } // Value was not found return 0; } /** * Returns the maximum ID value. **/ public int getMaxID() throws IOException { return _idFile.getMaxID(); } /** * Starts a transaction. This prepares the DataStore for upcoming updates * to the stored data. * * @exception IOException If an I/O error occurred. **/ public void startTransaction() throws IOException { _dataFile.startTransaction(false); _idFile.startTransaction(false); _hashFile.startTransaction(false); } /** * Commits a transaction, applying all updates that have been performed * during the transaction. * * @exception IOException If an I/O error occurred. **/ public void commitTransaction() throws IOException { // Sync any dirty cached items to file synchronized (_mruCache) { Iterator iter = _mruCache.iterator(); while (iter.hasNext()) { DataItem di = (DataItem)iter.next(); if (di.isDirty) { _dataFile.updateData(di.offset, _createCombinedArray(di.count, di.id, di.data)); di.isDirty = false; } } } _hashFile.commitTransaction(); _idFile.commitTransaction(); _dataFile.commitTransaction(); } /** * Rolls back all updates that have been performed in the current * transaction and closes it. * * @exception IOException If an I/O error occurred. **/ public void rollbackTransaction() throws IOException { // Clear the cached data items synchronized (_mruCache) { _mruCache.clear(); } _hashFile.rollbackTransaction(); _idFile.rollbackTransaction(); _dataFile.rollbackTransaction(); } /** * Stores the supplied value and returns the ID that has been assigned to * it. In case the data to store is already present, the ID of this existing * data is returned. This method can only be called as part of a * transaction. * * @param data The data to store. * @return The ID that has been assigned to the value. * @exception IOException If an I/O error occurred. * @see #startTransaction * @see #commitTransaction **/ public int storeData(byte[] data) throws IOException { // Check MRU cache DataItem dataItem = _getCachedDataItem(data); if (dataItem != null) { // Cache hit, increase reference count by 1 dataItem.increaseCount(1); return dataItem.id; } // Value not found in cache, check file int hash = _getHash(data); HashFile.OffsetIterator iter = _hashFile.getOffsetIterator(hash, true); long offset = iter.next(); while (offset >= 0L) { byte[] combinedData = _dataFile.getData(offset, true); // First 8 bytes in combinedData form the count and ID, the rest of the bytes should match if (data.length + 8 == combinedData.length && ByteArrayUtil.regionMatches(data, combinedData, 8)) { // Stored data matches, increase its reference count by 1 and store it in the cache int count = _getCountFromCombinedArray(combinedData); int id = _getIdFromCombinedArray(combinedData); byte[] storedData = _getDataFromCombinedArray(combinedData); dataItem = new DataItem(count, id, storedData, offset); dataItem.increaseCount(1); _cacheDataItem(dataItem); return id; } // Data doesn't match, try next offset offset = iter.next(); } // Data not stored yet, store it under a new ID. int id = _idFile.getNewID(); byte[] combinedData = _createCombinedArray(1, id, data); offset = _dataFile.storeData(combinedData); _hashFile.storeOffset(hash, offset); _idFile.storeOffset(id, offset); // Store the new data in the cache _cacheDataItem(new DataItem(1, id, data, offset)); return id; } /** * Changes the reference count for the data with the specified ID with the
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -