diff options
Diffstat (limited to 'src/de/lmu/ifi/dbs/elki/persistent/OnDiskUpperTriangleMatrix.java')
-rw-r--r-- | src/de/lmu/ifi/dbs/elki/persistent/OnDiskUpperTriangleMatrix.java | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/src/de/lmu/ifi/dbs/elki/persistent/OnDiskUpperTriangleMatrix.java b/src/de/lmu/ifi/dbs/elki/persistent/OnDiskUpperTriangleMatrix.java new file mode 100644 index 00000000..cc1880c7 --- /dev/null +++ b/src/de/lmu/ifi/dbs/elki/persistent/OnDiskUpperTriangleMatrix.java @@ -0,0 +1,172 @@ +package de.lmu.ifi.dbs.elki.persistent; +/* +This file is part of ELKI: +Environment for Developing KDD-Applications Supported by Index-Structures + +Copyright (C) 2011 +Ludwig-Maximilians-Universität München +Lehr- und Forschungseinheit für Datenbanksysteme +ELKI Development Team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero 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 Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +import java.io.File; +import java.io.IOException; +import java.nio.ByteBuffer; + +/** + * Class representing an upper triangle matrix backed by an on-disk array of + * O((n+1)*n/2) size + * + * @apiviz.composedOf OnDiskArray + * + * @author Erich Schubert + */ +public class OnDiskUpperTriangleMatrix { + /** + * Serial number, also used for generating a magic + */ + private static final long serialVersionUID = -4489942156357634702L; + + /** + * Size of this class' header + */ + private static final int TRIANGLE_HEADER_SIZE = 4; + + /** + * Size of the matrix + */ + private int matrixsize; + + /** + * Data storage + */ + private OnDiskArray array; + + /** + * Constructor to access an existing array. + * + * @param filename File name + * @param magicseed Magic number + * @param extraheadersize Size of extra header data + * @param recordsize Record size + * @param writable flag to open writable + * @throws IOException on IO errors + */ + public OnDiskUpperTriangleMatrix(File filename, int magicseed, int extraheadersize, int recordsize, boolean writable) throws IOException { + array = new OnDiskArray(filename, OnDiskArray.mixMagic((int) serialVersionUID, magicseed), extraheadersize + TRIANGLE_HEADER_SIZE, recordsize, writable); + ByteBuffer header = array.getExtraHeader(); + this.matrixsize = header.getInt(); + if(arraysize(matrixsize) != array.getNumRecords()) { + throw new IOException("Matrix file size doesn't match specified dimensions: " + matrixsize + "->" + arraysize(matrixsize) + " vs. " + array.getNumRecords()); + } + } + + /** + * Constructor to access a new array. + * + * @param filename File name + * @param magicseed Magic number + * @param extraheadersize Size of extra header data + * @param recordsize Record size + * @param matrixsize Size of matrix to store + * @throws IOException on IO errors + */ + public OnDiskUpperTriangleMatrix(File filename, int magicseed, int extraheadersize, int recordsize, int matrixsize) throws IOException { + if(matrixsize >= 0xFFFF) { + throw new RuntimeException("Matrix size is too big and will overflow the integer datatype."); + } + this.matrixsize = matrixsize; + array = new OnDiskArray(filename, OnDiskArray.mixMagic((int) serialVersionUID, magicseed), extraheadersize + TRIANGLE_HEADER_SIZE, recordsize, arraysize(matrixsize)); + ByteBuffer header = array.getExtraHeader(); + header.putInt(this.matrixsize); + } + + /** + * Resize the matrix to cover newsize x newsize. + * + * @param newsize New matrix size. + * @throws IOException on IO errors + */ + public synchronized void resizeMatrix(int newsize) throws IOException { + if(newsize >= 0xFFFF) { + throw new RuntimeException("Matrix size is too big and will overflow the integer datatype."); + } + if(!array.isWritable()) { + throw new IOException("Can't resize a read-only array."); + } + array.resizeFile(arraysize(newsize)); + this.matrixsize = newsize; + ByteBuffer header = array.getExtraHeader(); + header.putInt(this.matrixsize); + } + + /** + * Compute the size of the needed backing array from the matrix dimensions. + * + * @param matrixsize size of the matrix + * @return size of the array + */ + private static int arraysize(int matrixsize) { + return matrixsize * (matrixsize + 1) / 2; + } + + /** + * Compute the offset within the file. + * + * @param x First coordinate + * @param y Second coordinate + * @return Linear offset + */ + private int computeOffset(int x, int y) { + if(y > x) { + return computeOffset(y, x); + } + return (x * (x + 1)) / 2 + y; + } + + /** + * Get a record buffer + * + * @param x First coordinate + * @param y Second coordinate + * @return Byte buffer for the record + * @throws IOException on IO errors + */ + public synchronized ByteBuffer getRecordBuffer(int x, int y) throws IOException { + if(x >= matrixsize || y >= matrixsize) { + throw new ArrayIndexOutOfBoundsException(); + } + return array.getRecordBuffer(computeOffset(x, y)); + } + + /** + * Close the matrix file. + * + * @throws IOException on IO errors + */ + public synchronized void close() throws IOException { + array.close(); + } + + /** + * Query the size of the matrix. + * + * @return size of the matrix + */ + public int getMatrixSize() { + return matrixsize; + } +} |