/* ** 2015 December 03 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. */ package info.ata4.junity.serialize; import info.ata4.io.DataWriter; import info.ata4.junity.serialize.objectinfo.ObjectInfo; import info.ata4.log.LogUtils; import info.ata4.util.io.DataBlock; import java.io.Closeable; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.logging.Level; import java.util.logging.Logger; /** * * @author Nico Bergemann <barracuda415 at yahoo.de> */ public class SerializedFileWriter implements Closeable { private static final Logger L = LogUtils.getLogger(); private static final int META_PADDING = 4096; private static final int META_ALIGN = 16; private final DataWriter out; private SerializedFile serialized; public SerializedFileWriter(DataWriter out) { this.out = out; } public void write(SerializedFile serialized) throws IOException { this.serialized = serialized; // header is always big endian out.order(ByteOrder.BIG_ENDIAN); writeHeader(out); SerializedFileHeader header = serialized.header(); // newer formats use little endian for the rest of the file if (header.version() > 5) { out.order(ByteOrder.LITTLE_ENDIAN); } // older formats store the object data before the structure data if (header.version() < 9) { header.dataOffset(0); writeObjects(out); out.writeUnsignedByte(header.version() > 5 ? 0 : 1); writeMetadata(out); out.writeUnsignedByte(0); } else { writeMetadata(out); long dataOffset = out.position(); // calculate padding if (dataOffset < META_PADDING) { dataOffset = META_PADDING; } else { dataOffset += META_ALIGN - (dataOffset % META_ALIGN); } header.dataOffset(dataOffset); out.position(dataOffset); writeObjects(out); // write updated path table out.position(serialized.metadata().objectInfoBlock().offset()); out.writeStruct(serialized.metadata().objectInfoTable()); } // update header header.fileSize(out.size()); // FIXME: the metadata size is slightly off in comparison to original files int metadataOffset = header.version() < 9 ? 2 : 1; header.metadataSize(serialized.metadataBlock().length() + metadataOffset); // write updated header out.order(ByteOrder.BIG_ENDIAN); out.position(serialized.headerBlock().offset()); out.writeStruct(header); } private void writeHeader(DataWriter out) throws IOException { DataBlock headerBlock = serialized.headerBlock(); headerBlock.markBegin(out); out.writeStruct(serialized.header()); headerBlock.markEnd(out); L.log(Level.FINER, "headerBlock: {0}", headerBlock); } private void writeMetadata(DataWriter out) throws IOException { SerializedFileMetadata metadata = serialized.metadata(); SerializedFileHeader header = serialized.header(); DataBlock metadataBlock = serialized.metadataBlock(); metadataBlock.markBegin(out); metadata.version(header.version()); out.writeStruct(metadata); metadataBlock.markEnd(out); L.log(Level.FINER, "metadataBlock: {0}", metadataBlock); } private void writeObjects(DataWriter out) throws IOException { long ofsMin = Long.MAX_VALUE; long ofsMax = Long.MIN_VALUE; for (SerializedObjectData data : serialized.objectData()) { ByteBuffer bb = data.buffer(); bb.rewind(); out.align(8); ofsMin = Math.min(ofsMin, out.position()); ofsMax = Math.max(ofsMax, out.position() + bb.remaining()); ObjectInfo info = data.info(); info.offset(out.position() - serialized.header().dataOffset()); info.length(bb.remaining()); out.writeBuffer(bb); } DataBlock objectDataBlock = serialized.objectDataBlock(); objectDataBlock.offset(ofsMin); objectDataBlock.endOffset(ofsMax); L.log(Level.FINER, "objectDataBlock: {0}", objectDataBlock); } @Override public void close() throws IOException { out.close(); } }