package com.intellij.flex.uiDesigner.io; import com.intellij.openapi.application.ApplicationManager; import gnu.trove.TLinkable; import gnu.trove.TLinkedList; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.*; import java.nio.channels.WritableByteChannel; public class BlockDataOutputStream extends AbstractByteArrayOutputStream implements WritableByteChannel { private static final int SERVICE_DATA_SIZE = 8; private int lastBlockBegin; private OutputStream out; private final TLinkedList<Marker> markers = new TLinkedList<>(); private int messageCounter; public BlockDataOutputStream() { this(64 * 1024); } public BlockDataOutputStream(int size) { super(size); count = SERVICE_DATA_SIZE; } @SuppressWarnings("ConstantConditions") public void setOut(@NotNull OutputStream out) { String debugFilename = System.getProperty("fud.socket.dump"); DebugOutput debugOut; if (debugFilename != null) { try { debugOut = new DebugOutput(out, new File(debugFilename)); } catch (FileNotFoundException e) { throw new RuntimeException(e); } this.out = debugOut; } else if (true || ApplicationManager.getApplication().isUnitTestMode()) { this.out = new AuditorOutput(out); } else { this.out = out; } } public void reset() { count = SERVICE_DATA_SIZE; lastBlockBegin = 0; markers.clear(); out = null; } private void writeHeader() { IOUtil.writeInt(count - lastBlockBegin - SERVICE_DATA_SIZE, buffer, lastBlockBegin); IOUtil.writeInt(messageCounter++, buffer, lastBlockBegin + 4); } private void flushBuffer() throws IOException { if (markers.isEmpty()) { out.write(buffer, 0, count); } else { writeMarkered(); } lastBlockBegin = 0; count = SERVICE_DATA_SIZE; } public void assertStart() { assert count - lastBlockBegin == SERVICE_DATA_SIZE; } public void rollback() { count = lastBlockBegin + SERVICE_DATA_SIZE; if (!markers.isEmpty()) { markers.clear(); } } public void end() throws IOException { if ((count - lastBlockBegin) == SERVICE_DATA_SIZE) { return; } writeHeader(); if (!markers.isEmpty() || (count >= 8192 && out != null)) { flushBuffer(); } else { lastBlockBegin = count; count += SERVICE_DATA_SIZE; } } private void writeMarkered() throws IOException { int lastEnd = 0; AuditorOutput auditorOutput = null; if (out instanceof AuditorOutput) { auditorOutput = (AuditorOutput)out; auditorOutput.written = 0; } Marker marker = markers.getFirst(); do { int length = marker.getStart() - lastEnd; // may be < 0 if nested if (length >= 0) { if (length > 0) { out.write(buffer, lastEnd, length); } lastEnd = marker.getEnd(); } if (marker instanceof ByteRangePointer) { writeDataRange(((ByteRangePointer)marker).getDataRange()); } } while ((marker = (Marker)marker.getNext()) != null); int tailLength = count - lastEnd; if (tailLength > 0) { out.write(buffer, lastEnd, tailLength); } if (auditorOutput != null) { //assert auditorOutput.written == (count - (lastBlockBegin + SERVICE_DATA_SIZE)); auditorOutput.written = -1; } markers.clear(); } private void writeDataRange(ByteRange dataRange) throws IOException { ByteRange possibleChild = dataRange; int start = dataRange.getStart(); final int ownEnd = dataRange.getEnd(); while (true) { TLinkable next = possibleChild.getNext(); while (next != null && !(next instanceof ByteRange)) { next = next.getNext(); } possibleChild = (ByteRange)next; if (possibleChild == null || possibleChild.getEnd() > ownEnd) { out.write(buffer, start, ownEnd - start); break; } else { int length = possibleChild.getStart() - start; if (length > -1) { if (length != 0) { out.write(buffer, start, length); } start = possibleChild.getEnd(); } } } } public static int getDataRangeOwnLength(ByteRange dataRange) { if (dataRange.getOwnLength() != -1) { return dataRange.getOwnLength(); } Marker possibleChild = dataRange; int start = dataRange.getStart(); final int ownEnd = dataRange.getEnd(); int ownLength = 0; while (true) { possibleChild = (Marker)possibleChild.getNext(); if (possibleChild == null || possibleChild.getEnd() > ownEnd) { ownLength += ownEnd - start; break; } else { int length = possibleChild.getStart() - start; if (length > -1) { ownLength += length; start = possibleChild.getEnd(); } } } dataRange.setOwnLength(ownLength); return ownLength; } @Override public void flush() throws IOException { if (out == null) { end(); return; } if (lastBlockBegin != (count - SERVICE_DATA_SIZE)) { writeHeader(); flushBuffer(); } out.flush(); } @Override public boolean isOpen() { return out != null; } @Override public void close() throws IOException { if (out != null) { out.close(); } } @Nullable public Marker getLastMarker() { return markers.getLast(); } public ByteRange startRange() { return startRange(count); } public ByteRange startRange(int start) { ByteRange byteRange = new ByteRange(start); markers.addLast(byteRange); return byteRange; } public ByteRange startRange(int start, @Nullable Marker after) { ByteRange byteRange = new ByteRange(start); if (after == null) { markers.addFirst(byteRange); } else { markers.addBefore((Marker)after.getNext(), byteRange); } return byteRange; } public void endRange(ByteRange range) { range.setEnd(count); } public void removeLastMarkerAndAssert(ByteRange dataRange) { Marker removed = markers.removeLast(); assert removed == dataRange; } public void insert(final int position, final ByteRange dataRange) { markers.addFirst(new ByteRangePointer(position, dataRange)); } public void append(ByteRange dataRange) { markers.addLast(new ByteRangePointer(count, dataRange)); } private static class DebugOutput extends AuditorOutput { private final FileOutputStream fileOut; private DebugOutput(OutputStream out, File file) throws FileNotFoundException { super(out); fileOut = new FileOutputStream(file); } @Override public void write(int b) throws IOException { fileOut.write(b); super.write(b); } @Override public void write(byte[] b) throws IOException { fileOut.write(b); super.write(b); } @Override public void write(byte[] b, int off, int len) throws IOException { fileOut.write(b, off, len); super.write(b, off, len); } @Override public void flush() throws IOException { fileOut.flush(); super.flush(); } @Override public void close() throws IOException { fileOut.close(); super.close(); } } }