/** * Copyright 2013 Oak Ridge National Laboratory * Author: James Horey <horeyjl@ornl.gov> * * 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 gov.ornl.keva.table; /** * Java libs. **/ import java.io.File; import java.io.IOException; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.nio.channels.FileChannel; import java.nio.ByteBuffer; /** * Used for data that is stored locally in a file. */ public class TableExternal extends TableValue { private ByteBuffer data; // Current chunk of data loaded. private long start; // Start index of current chunk private long offset; // Offset into the file. private long chunkSize; // Size of current chunk private long totalSize; // Total size of stream/file private String path; // Path to external file. private long maxChunkSize; // Max chunk size. private FileChannel fc; /** * @param path Path of the external file being referenced. */ public TableExternal(String path) { super(TableValue.STREAM); start = 0; offset = 0; chunkSize = 0; totalSize = 0; data = null; maxChunkSize = 32000; // 32 KB. this.path = path; // Path to the file // Set some attributes. setAttributes(new TableAttributes("Stream")); // Read the file contents. readFile(path); } /** * Set the total size of the file contents. **/ protected void setTotalSize(long s) { totalSize = s; } /** * Offsets are used so that we can continue referring to * the 1st, 2nd, etc. index in the middle of the file. **/ protected void setOffset(long o) { offset = o; } /** * Create a file handle if the path is valid. */ private boolean readFile(String path) { File f = new File(path); try { if(f.exists() && !f.isDirectory()) { totalSize = f.length(); fc = FileChannel.open(Paths.get(path), StandardOpenOption.READ); return true; } } catch(IOException e) { e.printStackTrace(); } return false; } /** * The chunk size defines how large of an in-memory buffer * to use when reading data. * * @return Chunk size */ public long getChunkSize() { return chunkSize; } /** * The chunk start index defines where in the file the * data is initially read from. * * @return Chunk index */ public long getChunkStart() { return start; } /** * Get all the data associated with this value. Since the * data resides in a file, the content must first be read * into an in-memory buffer. This method must be called * multiple times to read the entire file if the file size * exceeds the user-defined chunk size. * * @return Table value data */ @Override public byte[] getData() { chunkSize = totalSize - offset; if(fc != null) { // Make sure we have a file handle. data = ByteBuffer.allocate((int)chunkSize); // Create the byte buffer try { fc.position(offset); // Move the file to the right position. fc.read(data); // Read into bytebuffer. return data.array(); } catch(IOException e) { e.printStackTrace(); } } return null; } /** * Serialize the table value. * * @return Serialized table value */ @Override public byte[] getBytes() { return null; } /** * Create a new external table value that is a subset of the current value. * * @param index Start of the file * @param size Size of the file * @return A new external value */ public TableValue subset(long index, long size) { // Create a new table stream that // remembers the index offset and size. TableExternal subset = new TableExternal(path); subset.setTotalSize(size); subset.setOffset(index); subset.setAttributes(getAttributes()); subset.setFlags(getFlags()); subset.setClock(getClock()); subset.setKey(getKey()); return subset; } /** * Get the size of the data. * * @return Size of the data in bytes */ @Override public long size() { return totalSize; } /** * Get the memory usage of the data. This is usually * the same as the "size", but not always. * * @return Bytes used */ @Override public long memory() { return totalSize; } }