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

openllet.aterm.pure.binary.BinaryReader Maven / Gradle / Ivy

There is a newer version: 2.6.5
Show newest version
/*
 * Copyright (c) 2002-2007, CWI and INRIA
 *
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the University of California, Berkeley nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package openllet.aterm.pure.binary;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.List;

import openllet.aterm.AFun;
import openllet.aterm.ATerm;
import openllet.aterm.ATermList;
import openllet.aterm.pure.PureFactory;
import openllet.atom.OpenError;

/**
 * Reconstructs an ATerm from the given (series of) buffer(s). It can be retrieved when the
 * construction of the term is done / when _isDone() returns true.
 *
 * For example (yes I know this code is crappy, but it's simple):
* *
 * ByteBuffer buffer = ByteBuffer.allocate(8192);
 * BinaryWriter bw = new BinaryWriter(openllet.aterm);
 * while (!bw.isDone())
 * {
 * 	int bytesRead = channel.read(buffer); // Read the next chunk of data from the stream.
 * 	if (!buffer.hasRemaining() || bytesRead == -1)
 * 	{
 * 		bw.serialize(buffer);
 * 		buffer.clear();
 * 	}
 * }
 * 
* *
* * @author Arnold Lankamp */ public class BinaryReader { private final static int ISSHAREDFLAG = 0x00000080; private final static int TYPEMASK = 0x0000000f; private final static int ISFUNSHARED = 0x00000040; private final static int APPLQUOTED = 0x00000020; private final static int INITIALSHAREDTERMSARRAYSIZE = 1024; private final static int STACKSIZE = 256; private final PureFactory _factory; private int _sharedTermIndex; private ATerm[] _sharedTerms; private final List _applSignatures; private ATermConstruct[] _stack; private int _stackPosition; private int _tempType = -1; private byte[] _tempBytes = null; private int _tempBytesIndex = 0; private int _tempArity = -1; private boolean _tempIsQuoted = false; private ByteBuffer _currentBuffer; private boolean _isDone = false; /** * Constructor. * * @param factory to use for reconstruction of the ATerm. */ public BinaryReader(final PureFactory factory) { super(); _factory = factory; _sharedTerms = new ATerm[INITIALSHAREDTERMSARRAYSIZE]; _applSignatures = new ArrayList<>(); _sharedTermIndex = 0; _stack = new ATermConstruct[STACKSIZE]; _stackPosition = -1; } /** * Resizes the openllet.shared.hash terms array when needed. When we're running low on space the capacity * will be doubled. */ private void ensureSharedTermsCapacity() { final int sharedTermsArraySize = _sharedTerms.length; if (_sharedTermIndex + 1 >= sharedTermsArraySize) { final ATerm[] newSharedTermsArray = new ATerm[sharedTermsArraySize << 1]; System.arraycopy(_sharedTerms, 0, newSharedTermsArray, 0, sharedTermsArraySize); _sharedTerms = newSharedTermsArray; } } /** * Constructs (a part of) the ATerm from the binary representation present in the given buffer. * This method will 'remember' where it was left. * * @param buffer * The buffer that contains (a part of) the binary representation of the ATerm. */ public void deserialize(final ByteBuffer buffer) { _currentBuffer = buffer; if (_tempType != -1) readData(); while (buffer.hasRemaining()) { final byte header = buffer.get(); if ((header & ISSHAREDFLAG) == ISSHAREDFLAG) { final int index = readInt(); final ATerm term = _sharedTerms[index]; _stackPosition++; linkTerm(term); } else { final int type = header & TYPEMASK; final ATermConstruct ac = new ATermConstruct(type, _sharedTermIndex++); ensureSharedTermsCapacity(); _stack[++_stackPosition] = ac; TYPECHECK: switch (type) { case ATerm.APPL: touchAppl(header); break TYPECHECK; case ATerm.LIST: touchList(); break TYPECHECK; case ATerm.INT: touchInt(); break TYPECHECK; case ATerm.REAL: touchReal(); break TYPECHECK; case ATerm.LONG: touchLong(); break TYPECHECK; case ATerm.BLOB: touchBlob(); break TYPECHECK; case ATerm.PLACEHOLDER: touchPlaceholder(); break TYPECHECK; default: throw new OpenError("Unknown type id: " + type + ". Current buffer position: " + _currentBuffer.position()); } } // Make sure the _stack remains large enough ensureStackCapacity(); } } /** * Resizes the _stack when needed. When we're running low on _stack space the capacity will be * doubled. */ private void ensureStackCapacity() { final int stackSize = _stack.length; if (_stackPosition + 1 >= stackSize) { final ATermConstruct[] newStack = new ATermConstruct[stackSize << 1]; System.arraycopy(_stack, 0, newStack, 0, _stack.length); _stack = newStack; } } /** * Checks if we are done serializing. * * @return True if we are done; false otherwise. */ public boolean isDone() { return _isDone; } /** * Returns the reconstructed ATerm. A RuntimeException will be thrown when we are not yet done * with the reconstruction of the ATerm. * * @return The reconstructed ATerm. */ public ATerm getRoot() { if (!_isDone) throw new OpenError("Can't retrieve the root of the tree while it's still being constructed."); return _sharedTerms[0]; } /** * Resets the temporary data. We don't want to hold it if it's not necessary. */ private void resetTemp() { _tempType = -1; _tempBytes = null; _tempBytesIndex = 0; } /** * Reads a series of bytes from the buffer. When the nessecary amount of bytes is read a term * of the corresponding type will be constructed and (if possible) linked with it's parent. */ private void readData() { final int length = _tempBytes.length; int bytesToRead = length - _tempBytesIndex; final int remaining = _currentBuffer.remaining(); if (remaining < bytesToRead) bytesToRead = remaining; _currentBuffer.get(_tempBytes, _tempBytesIndex, bytesToRead); _tempBytesIndex += bytesToRead; if (_tempBytesIndex == length) { if (_tempType == ATerm.APPL) { final AFun fun = _factory.makeAFun(new String(_tempBytes), _tempArity, _tempIsQuoted); _applSignatures.add(fun); final ATermConstruct ac = _stack[_stackPosition]; if (_tempArity == 0) { final ATerm term = _factory.makeAppl(fun); _sharedTerms[ac.termIndex] = term; linkTerm(term); } else { ac.tempTerm = fun; ac.subTerms = new ATerm[_tempArity]; } } else if (_tempType == ATerm.BLOB) { final ATermConstruct ac = _stack[_stackPosition]; final ATerm term = _factory.makeBlob(_tempBytes); _sharedTerms[ac.termIndex] = term; linkTerm(term); } else throw new OpenError("Unsupported chunkified type: " + _tempType); resetTemp(); } } /** * Starts the deserialization process of a appl. * * @param header * The header of the appl. */ private void touchAppl(final byte header) { if ((header & ISFUNSHARED) == ISFUNSHARED) { final int key = readInt(); final AFun fun = _applSignatures.get(key); final int arity = fun.getArity(); final ATermConstruct ac = _stack[_stackPosition]; if (arity == 0) { final ATerm term = _factory.makeAppl(fun); _sharedTerms[ac.termIndex] = term; linkTerm(term); } else { ac.tempTerm = fun; ac.subTerms = new ATerm[arity]; } } else { _tempIsQuoted = (header & APPLQUOTED) == APPLQUOTED; _tempArity = readInt(); final int nameLength = readInt(); _tempType = ATerm.APPL; _tempBytes = new byte[nameLength]; _tempBytesIndex = 0; readData(); } } /** * Deserialializes a list. */ private void touchList() { final int size = readInt(); final ATermConstruct ac = _stack[_stackPosition]; ac.subTerms = new ATerm[size]; if (size == 0) { final ATerm term = _factory.makeList(); _sharedTerms[ac.termIndex] = term; linkTerm(term); } } /** * Deserialializes an int. */ private void touchInt() { final int value = readInt(); final ATermConstruct ac = _stack[_stackPosition]; final ATerm term = _factory.makeInt(value); _sharedTerms[ac.termIndex] = term; linkTerm(term); } /** * Deserialializes a real. */ private void touchReal() { final double value = readDouble(); final ATermConstruct ac = _stack[_stackPosition]; final ATerm term = _factory.makeReal(value); _sharedTerms[ac.termIndex] = term; linkTerm(term); } /** * Deserialializes a long. */ private void touchLong() { final long value = readLong(); final ATermConstruct ac = _stack[_stackPosition]; final ATerm term = _factory.makeLong(value); _sharedTerms[ac.termIndex] = term; linkTerm(term); } /** * Starts the deserialization process for a BLOB. */ private void touchBlob() { final int length = readInt(); _tempType = ATerm.BLOB; _tempBytes = new byte[length]; _tempBytesIndex = 0; readData(); } /** * Deserialializes a placeholder. */ private void touchPlaceholder() { // A placeholder doesn't have content final ATermConstruct ac = _stack[_stackPosition]; ac.subTerms = new ATerm[1]; } /** * Constructs a term from the given structure. * * @param ac * A structure that contains all the nessecary data to contruct the associated term. * @return The constructed openllet.aterm. */ private ATerm buildTerm(final ATermConstruct ac) { ATerm constructedTerm; final ATerm[] subTerms = ac.subTerms; final int type = ac.type; if (type == ATerm.APPL) { final AFun fun = (AFun) ac.tempTerm; constructedTerm = _factory.makeAppl(fun, subTerms); } else if (type == ATerm.LIST) { ATermList list = _factory.makeList(); for (int i = subTerms.length - 1; i >= 0; i--) list = _factory.makeList(subTerms[i], list); constructedTerm = list; } else if (type == ATerm.PLACEHOLDER) { final ATerm placeholder = _factory.makePlaceholder(subTerms[0]); constructedTerm = placeholder; } else throw new OpenError("Unable to construct term.\n"); return constructedTerm; } /** * Links the given term with it's parent. * * @param aTerm * The term that needs to be linked. */ private void linkTerm(final ATerm aTerm) { ATerm term = aTerm; while (_stackPosition != 0) { final ATermConstruct parent = _stack[--_stackPosition]; final ATerm[] subTerms = parent.subTerms; if (subTerms != null && subTerms.length > parent.subTermIndex) { subTerms[parent.subTermIndex++] = term; if (parent.subTerms.length != parent.subTermIndex) return; } else throw new OpenError("Encountered a term that didn't fit anywhere. Type: " + term.getType()); term = buildTerm(parent); _sharedTerms[parent.termIndex] = term; } if (_stackPosition == 0) _isDone = true; } private final static int SEVENBITS = 0x0000007f; private final static int SIGNBIT = 0x00000080; private final static int BYTEMASK = 0x000000ff; private final static int BYTEBITS = 8; private final static int LONGBITS = 8; /** * Reconstructs an integer from the following 1 to 5 bytes in the buffer (depending on how many * we used to represent the value). See the documentation of * openllet.aterm.binary.BinaryWriter#writeInt(int) for more information. * * @return The reconstructed integer. */ private int readInt() { byte part = _currentBuffer.get(); int result = part & SEVENBITS; if ((part & SIGNBIT) == 0) return result; part = _currentBuffer.get(); result |= (part & SEVENBITS) << 7; if ((part & SIGNBIT) == 0) return result; part = _currentBuffer.get(); result |= (part & SEVENBITS) << 14; if ((part & SIGNBIT) == 0) return result; part = _currentBuffer.get(); result |= (part & SEVENBITS) << 21; if ((part & SIGNBIT) == 0) return result; part = _currentBuffer.get(); result |= (part & SEVENBITS) << 28; return result; } /** * Reconstructs a double from the following 8 bytes in the buffer. * * @return The reconstructed double. */ private double readDouble() { final long result = readLong(); return Double.longBitsToDouble(result); } /** * Reconstructs a long from the following 8 bytes in the buffer. * * @return The reconstructed long. */ private long readLong() { long result = 0; for (int i = 0; i < LONGBITS; i++) result |= ((long) _currentBuffer.get() & BYTEMASK) << i * BYTEBITS; return result; } /** * Reads the ATerm from the given SAF encoded file. * * @param pureFactory * The factory to use. * @param file * The file that contains the SAF encoded term. * @return The constructed ATerm. * @throws IOException Thrown when an error occurs while reading the given file. */ public static ATerm readTermFromSAFFile(final PureFactory pureFactory, final File file) throws IOException { final BinaryReader binaryReader = new BinaryReader(pureFactory); final ByteBuffer byteBuffer = ByteBuffer.allocate(65536); final ByteBuffer sizeBuffer = ByteBuffer.allocate(2); try (FileInputStream fis = new FileInputStream(file)) { try (FileChannel fc = fis.getChannel()) { // Consume the SAF identification token. byteBuffer.limit(1); int bytesRead = fc.read(byteBuffer); if (bytesRead != 1) throw new IOException("Unable to read SAF identification token.\n"); do { sizeBuffer.clear(); bytesRead = fc.read(sizeBuffer); if (bytesRead <= 0) break; else if (bytesRead != 2) throw new IOException("Unable to read block size bytes from file: " + bytesRead + ".\n"); sizeBuffer.flip(); int blockSize = (sizeBuffer.get() & 0x000000ff) + ((sizeBuffer.get() & 0x000000ff) << 8); if (blockSize == 0) blockSize = 65536; byteBuffer.clear(); byteBuffer.limit(blockSize); bytesRead = fc.read(byteBuffer); byteBuffer.flip(); if (bytesRead != blockSize) throw new IOException("Unable to read bytes from file " + bytesRead + " vs " + blockSize + "."); binaryReader.deserialize(byteBuffer); } while (bytesRead > 0); if (!binaryReader.isDone()) throw new OpenError("Term incomplete, missing data.\n"); } } return binaryReader.getRoot(); } /** * Reads the ATerm from the given SAF encoded data. * * @param pureFactory * The factory to use. * @param data * The SAF encoded data. * @return The constructed ATerm. */ public static ATerm readTermFromSAFString(final PureFactory pureFactory, final byte[] data) { final BinaryReader binaryReader = new BinaryReader(pureFactory); final int length = data.length; int position = 0; do { int blockSize = data[position++] & 0x000000ff; blockSize += (data[position++] & 0x000000ff) << 8; if (blockSize == 0) blockSize = 65536; final ByteBuffer byteBuffer = ByteBuffer.allocate(blockSize); byteBuffer.put(data, position, blockSize); position += blockSize; byteBuffer.flip(); binaryReader.deserialize(byteBuffer); } while (position < length); if (!binaryReader.isDone()) throw new OpenError("Term incomplete, missing data.\n"); return binaryReader.getRoot(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy