/* * JaamSim Discrete Event Simulation * Copyright (C) 2013 Ausenco Engineering Canada Inc. * Copyright (C) 2015 JaamSim Software Inc. * * 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 com.jaamsim.MeshFiles; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import com.jaamsim.math.Mat4d; /** * DataBlock is the base unit of data for the renderer binary format IO system * Blocks can be written to disk and can contain binary data, other blocks or a mixture of the two * API includes utility for serializing primitive types and strings. * @author matt.chudleigh * */ public class DataBlock { public static class Error extends RuntimeException { public Error(String errStr) { super(errStr); } } private final String name; private final byte[] data; private int dataSize = 0; private int readPos = 0; private final ArrayList<DataBlock> children; /** Create a new DataBlock with room for 'bufferSize' bytes of binary data * this method is intended for data that is being dynamically generated, eg by an exporter */ public DataBlock(String name, int bufferSize) { this.name = name; data = new byte[bufferSize]; children = new ArrayList<>(); } /** * Create a DataBlock explicitly, this method is meant to be used by a file reader * @param name * @param data * @param children */ public DataBlock(String name, byte[] data, ArrayList<DataBlock> children) { this.name = name; this.data = data; this.children = children; dataSize = data.length; } public int getDataSize() { return dataSize; } public void setReadPosition(int pos) { if (pos > dataSize) { throw new Error("Read set past end of block"); } readPos = pos; } public int getReadPosition() { return readPos; } public boolean atEnd() { return readPos == dataSize; } public byte[] getData() { return data; } public ArrayList<DataBlock> getChildren() { return children; } public void addChildBlock(DataBlock child) { children.add(child); } public String getName() { return name; } private void checkWriteSize(int newSize) { if (dataSize + newSize > data.length) { throw new Error("DataBlock write too large"); } } private void checkReadSize(int newSize) { if (readPos + newSize > dataSize) { throw new Error("DataBlock read too large"); } } // Add all of a byte array to the data public void writeData(byte[] d) { checkWriteSize(d.length); System.arraycopy(d, 0, data, dataSize, d.length); dataSize += d.length; } public void writeByte(byte b) { checkWriteSize(1); data[dataSize++] = b; } public void writeDouble(double d) { writeLong(Double.doubleToLongBits(d)); } public void writeLong(long l) { checkWriteSize(8); BlockUtils.longToBytes(l, data, dataSize); dataSize += 8; } public void writeFloat(float f) { writeInt(Float.floatToIntBits(f)); } public void writeInt(int i) { checkWriteSize(4); BlockUtils.intToBytes(i, data, dataSize); dataSize += 4; } public void writeString(String s) { try { byte[] utf8 = s.getBytes("UTF-8"); checkWriteSize(s.length() + 1); // Room for the string and null terminator System.arraycopy(utf8, 0, data, dataSize, utf8.length); dataSize += utf8.length; data[dataSize++] = 0; // Add the null terminator } catch (UnsupportedEncodingException e) { throw new Error(e.getMessage()); } } public void writeMat4d(Mat4d mat) { checkWriteSize(8*16); writeDouble(mat.d00); writeDouble(mat.d01); writeDouble(mat.d02); writeDouble(mat.d03); writeDouble(mat.d10); writeDouble(mat.d11); writeDouble(mat.d12); writeDouble(mat.d13); writeDouble(mat.d20); writeDouble(mat.d21); writeDouble(mat.d22); writeDouble(mat.d23); writeDouble(mat.d30); writeDouble(mat.d31); writeDouble(mat.d32); writeDouble(mat.d33); } public byte readByte() { checkReadSize(1); return data[readPos++]; } public int readInt() { checkReadSize(4); int ret = BlockUtils.intFromBytes(data, readPos); readPos += 4; return ret; } public float readFloat() { int bits = readInt(); return Float.intBitsToFloat(bits); } public long readLong() { checkReadSize(8); long ret = BlockUtils.longFromBytes(data, readPos); readPos += 8; return ret; } public double readDouble() { long bits = readLong(); return Double.longBitsToDouble(bits); } public String readString() { // Find the next null terminator int startPos = readPos; while (readPos++ < dataSize) { if (data[readPos] == 0) break; } if (readPos == dataSize) { throw new Error("Read string past end of block"); } int size = readPos - startPos; readPos++; // Skip the null byte byte[] bytes = new byte[size]; System.arraycopy(data, startPos, bytes, 0, size); try { return new String(bytes, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new Error(e.getMessage()); } } public Mat4d readMat4d() { Mat4d ret = new Mat4d(); ret.d00 = readDouble(); ret.d01 = readDouble(); ret.d02 = readDouble(); ret.d03 = readDouble(); ret.d10 = readDouble(); ret.d11 = readDouble(); ret.d12 = readDouble(); ret.d13 = readDouble(); ret.d20 = readDouble(); ret.d21 = readDouble(); ret.d22 = readDouble(); ret.d23 = readDouble(); ret.d30 = readDouble(); ret.d31 = readDouble(); ret.d32 = readDouble(); ret.d33 = readDouble(); return ret; } /** * Returns the first child with a matching name. Utility * @param name * @return */ public DataBlock findChildByName(String name) { for (DataBlock b : children) { if (b.name.equals(name)) { return b; } } return null; } @Override public String toString() { return name; } }