/* ************************************************************************ # # DivConq # # http://divconq.com/ # # Copyright: # Copyright 2014 eTimeline, LLC. All rights reserved. # # License: # See the license.txt file in the project's top-level directory for details. # # Authors: # * Andy White # ************************************************************************ */ package divconq.ctp.stream; import java.util.ArrayList; import java.util.List; import io.netty.buffer.ByteBuf; import divconq.ctp.f.FileDescriptor; import divconq.script.StackEntry; import divconq.util.FileUtil; import divconq.util.StringUtil; import divconq.xml.XElement; public class SplitStream extends BaseStream implements IStreamSource { protected int seqnum = 1; protected int size = 10 * 1024 * 1024; protected String template = "file-%seq%.bin"; protected int currchunk = 0; protected FileDescriptor sfile = null; protected List<FileDescriptor> outlist = new ArrayList<>(); protected List<ByteBuf> outbuf = new ArrayList<>(); public SplitStream() { } @Override public void init(StackEntry stack, XElement el) { this.seqnum = (int) stack.intFromElement(el, "StartAt", this.seqnum); String size = stack.stringFromElement(el, "Size", "10MB"); this.size = (int) FileUtil.parseFileSize(size); String temp = stack.stringFromElement(el, "Template"); if (StringUtil.isNotEmpty(temp)) this.template = temp; } // make sure we don't return without first releasing the file reference content @Override public ReturnOption handle(FileDescriptor file, ByteBuf data) { if (file == FileDescriptor.FINAL) return this.downstream.handle(file, data); ByteBuf in = data; if (in != null) { while (in.isReadable()) { int amt = Math.min(in.readableBytes(), this.size - this.currchunk); ByteBuf out = in.copy(in.readerIndex(), amt); in.skipBytes(amt); this.currchunk += amt; boolean eof = (this.currchunk == this.size) || (!in.isReadable() && file.isEof()); this.nextMessage(out, file, eof); if (eof) { this.seqnum++; this.currchunk = 0; } } in.release(); } else if (file.isEof()) { this.nextMessage(null, file, false); } // write all messages in the queue while (this.outlist.size() > 0) { ReturnOption ret = this.downstream.handle(this.outlist.remove(0), this.outbuf.remove(0)); if (ret != ReturnOption.CONTINUE) return ret; } return ReturnOption.CONTINUE; } public void nextMessage(ByteBuf out, FileDescriptor curr, boolean eof) { // create the output message FileDescriptor blk = new FileDescriptor(); blk.setModTime(System.currentTimeMillis()); // keep the path, just vary the name to the template blk.setPath(curr.path().resolvePeer("/" + this.template.replace("%seq%", this.seqnum + ""))); blk.setEof(eof); if (eof) blk.setSize(this.currchunk); else blk.setSize(0); // don't know yet this.outlist.add(blk); this.outbuf.add(out); } @Override public void read() { // write all messages in the queue while (this.outlist.size() > 0) { ReturnOption ret = this.downstream.handle(this.outlist.remove(0), this.outbuf.remove(0)); if (ret != ReturnOption.CONTINUE) return; } this.upstream.read(); } }