package com.xiaomi.infra.galaxy.api.io; import com.xiaomi.infra.galaxy.io.thrift.Compression; import com.xiaomi.infra.galaxy.io.thrift.RSFileConstants; import com.xiaomi.infra.galaxy.io.thrift.RSFileHeader; import com.xiaomi.infra.galaxy.io.thrift.Record; import libthrift091.TException; import libthrift091.protocol.TCompactProtocol; import libthrift091.protocol.TProtocol; import libthrift091.transport.TIOStreamTransport; import java.io.IOException; import java.io.OutputStream; /** * This class is not thread safe */ public class ByteArrayRecordWriter implements RecordWriter<byte[]> { private static final int VERSION = 1; private RSFileHeader header; private OutputStream outputStream; private TProtocol protocol; private boolean headerWritten = false; private long count = -1; private long counter = 0; private boolean sealed = false; public ByteArrayRecordWriter(OutputStream outputStream, RSFileHeader header) { this.outputStream = outputStream; this.header = header.setVersion(VERSION).setMagic(RSFileConstants.MAGIC); } private boolean tryWriteHeader() throws IOException { if (!headerWritten) { this.protocol = new TCompactProtocol(new TIOStreamTransport(outputStream)); try { this.header.write(this.protocol); } catch (TException te) { throw new IOException("Failed to write file header", te); } headerWritten = true; Compression compression = header.getCompression(); outputStream = CompressionStreamAdaptor.getOutputStream(outputStream, compression); this.protocol = new TCompactProtocol(new TIOStreamTransport(outputStream)); return true; } return false; } @Override public void append(byte[] record) throws IOException { if (sealed) { throw new IOException("Can not append to sealed file"); } tryWriteHeader(); Record rec = new Record().setData(record); try { rec.write(this.protocol); } catch (TException te) { throw new IOException("Failed to append record", te); } if (count >= 0) { ++counter; } } @Override public void seal() throws IOException { if (!sealed) { sealed = true; tryWriteHeader(); Record rec = new Record().setEof(true); try { rec.write(this.protocol); } catch (TException te) { throw new IOException("Failed to seal file", te); } outputStream.flush(); // flush snappy buffer if (count >= 0 && count != counter) { throw new IllegalArgumentException( String.format("Record count mismatch: %d != %d", counter, count)); } } } @Override public void close() throws IOException { this.seal(); this.outputStream.close(); } }