/* * JaamSim Discrete Event Simulation * Copyright (C) 2013 Ausenco Engineering Canada 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.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.util.zip.CRC32; public class BlockWriter { private static long getBlockDataSize(DataBlock b) { long size = 0; for (DataBlock child : b.getChildren()) { size += getBlockTotalSize(child); } size += b.getDataSize(); return size; } private static long getBlockTotalSize(DataBlock b) { try { byte[] nameBytes = b.getName().getBytes("UTF-8"); return 29 + nameBytes.length + getBlockDataSize(b); } catch (UnsupportedEncodingException e) { throw new DataBlock.Error(e.getMessage()); } } /** * Write the block and sub blocks to the output stream * Can throw a DataBlock.Error * @param out * @param d */ public static void writeBlock(OutputStream out, DataBlock b) { try { // Write out the block header byte[] nameBytes = b.getName().getBytes("UTF-8"); if (nameBytes.length > 128) { throw new DataBlock.Error("Block name too large"); } long payloadSize = getBlockDataSize(b); byte[] sizeBytes = new byte[8]; BlockUtils.longToBytes(payloadSize, sizeBytes, 0); byte[] numChildrenBytes = new byte[4]; BlockUtils.intToBytes(b.getChildren().size(), numChildrenBytes, 0); CRC32 headerCRC = new CRC32(); headerCRC.update(nameBytes); headerCRC.update(0); headerCRC.update(numChildrenBytes); headerCRC.update(sizeBytes); byte[] crcBytes = new byte[4]; BlockUtils.intToBytes((int)headerCRC.getValue(), crcBytes, 0); // header is ready, so start writing out.write(BlockUtils.header); out.write(crcBytes); out.write(nameBytes); out.write(0); out.write(numChildrenBytes); out.write(sizeBytes); // Now wrap the output stream to get a total payload CRC, while still being recursive BlockUtils.CRCOutputStream crcStream = new BlockUtils.CRCOutputStream(out); for(DataBlock child : b.getChildren()) { writeBlock(crcStream, child); } // Write the data section crcStream.write(b.getData(), 0, b.getDataSize()); // re-use the CRC bytes array BlockUtils.intToBytes((int)crcStream.getCRC(), crcBytes, 0); out.write(crcBytes); out.write(BlockUtils.footer); } catch (IOException e) { throw new DataBlock.Error(e.getMessage()); } } }