[6152] | 1 | /* |
---|
| 2 | Copyright 2009 by Sean Luke |
---|
| 3 | Licensed under the Academic Free License version 3.0 |
---|
| 4 | See the file "LICENSE" for more information |
---|
| 5 | */ |
---|
| 6 | |
---|
| 7 | |
---|
| 8 | package ec.util; |
---|
| 9 | import java.io.*; |
---|
| 10 | |
---|
| 11 | /* |
---|
| 12 | DataPipe is a mechanism which allows you to pipe data from an OutputStream to an InputStream within a single thread. |
---|
| 13 | This differs from a PipedInputStream/PipedOutputSteam pair in that it permits a single thread (in fact requires it |
---|
| 14 | due to lack of synchronization for speed). To do this, the DataPipe maintains an extensible buffer which gets as large |
---|
| 15 | as necessary when filled with data from the InputStream, then expels it out the OutputStream when called. The |
---|
| 16 | default Input and Output streams of a DataPipe are DataInputStream and DataOutputStream, since reading and writing |
---|
| 17 | binary objects is its primary function. |
---|
| 18 | |
---|
| 19 | <p>The procedure is as follows: create the DataPipe, then start writing to its DataInputStream. When you are done, |
---|
| 20 | start reading from its DataOutputStream. DataPipe is meant to be one-shot: write a bunch of stuff, then read <i>all</i> of it. |
---|
| 21 | You shouldn't read and write piecemeal as the DataPipe is not a true circular buffer and will grow without bound with wasted |
---|
| 22 | space equal to the amount you've read so far. |
---|
| 23 | |
---|
| 24 | <p>You <i>can</i>, however, reuse the DataPipe by calling reset() on it. |
---|
| 25 | Note that this retains the current buffer however large it has grown to. |
---|
| 26 | */ |
---|
| 27 | |
---|
| 28 | public class DataPipe |
---|
| 29 | { |
---|
| 30 | // an extensible array which can be pushed into, then pulled out of |
---|
| 31 | byte[] buffer = new byte[8192]; |
---|
| 32 | |
---|
| 33 | // the number of bytes in the array -- in reality this is the number of bytes written to the array so far |
---|
| 34 | int size = 0; |
---|
| 35 | |
---|
| 36 | // the number of bytes read from the array. pull will always trail size |
---|
| 37 | int pull = 0; |
---|
| 38 | |
---|
| 39 | // Double the size of the buffer |
---|
| 40 | void resize() |
---|
| 41 | { |
---|
| 42 | byte[] newbuffer = new byte[buffer.length * 2]; |
---|
| 43 | System.arraycopy(buffer, 0, newbuffer, 0, buffer.length); |
---|
| 44 | buffer = newbuffer; |
---|
| 45 | } |
---|
| 46 | |
---|
| 47 | // Add a byte to the buffer |
---|
| 48 | void push(byte b) |
---|
| 49 | { |
---|
| 50 | if (size >= buffer.length) resize(); |
---|
| 51 | buffer[size++] = b; |
---|
| 52 | } |
---|
| 53 | |
---|
| 54 | // Add bytes to the buffer, read from b[offset]... b[offset+length-1] |
---|
| 55 | void push(byte[] b, int offset, int length) |
---|
| 56 | { |
---|
| 57 | if (size + length > buffer.length) resize(); |
---|
| 58 | System.arraycopy(b, offset, buffer, size, length); |
---|
| 59 | size += length; |
---|
| 60 | } |
---|
| 61 | |
---|
| 62 | // Return an unsigned byte read from the buffer. |
---|
| 63 | // If there are no bytes left to read, -1 is returned. |
---|
| 64 | int pull() |
---|
| 65 | { |
---|
| 66 | if (pull==size) return -1; // EOF |
---|
| 67 | byte b = buffer[pull++]; |
---|
| 68 | if (b < 0) return b + 256; |
---|
| 69 | return b; |
---|
| 70 | } |
---|
| 71 | |
---|
| 72 | // Provide up to *length* bytes from the buffer. They are |
---|
| 73 | // placed into b[offset] ... b[offset + length - 1]. |
---|
| 74 | // If there aren't *length* bytes in the buffer, some number |
---|
| 75 | // less than that is actually provided. The actual number |
---|
| 76 | // of bytes provided is returned. |
---|
| 77 | // If there are no bytes left to read at all, -1 is returned. |
---|
| 78 | int pull(byte[] b, int offset, int length) |
---|
| 79 | { |
---|
| 80 | if (pull==size) return -1; |
---|
| 81 | if (length > size-pull) length = size-pull; |
---|
| 82 | System.arraycopy(buffer, pull, b, offset, length); |
---|
| 83 | pull += length; |
---|
| 84 | return length; |
---|
| 85 | } |
---|
| 86 | |
---|
| 87 | /** The input stream */ |
---|
| 88 | public DataInputStream input; |
---|
| 89 | /** The output stream */ |
---|
| 90 | public DataOutputStream output; |
---|
| 91 | |
---|
| 92 | public DataPipe() |
---|
| 93 | { |
---|
| 94 | OutputStream outStream = new OutputStream() |
---|
| 95 | { |
---|
| 96 | public void write(int b) throws IOException { push((byte)b); } |
---|
| 97 | public void write(byte[] b, int off, int len) throws IOException { push(b, off, len); } |
---|
| 98 | public void write(byte[] b) throws IOException { push(b, 0, b.length); } |
---|
| 99 | }; |
---|
| 100 | output = new DataOutputStream(outStream); |
---|
| 101 | |
---|
| 102 | InputStream inStream = new InputStream() |
---|
| 103 | { |
---|
| 104 | public int read() throws IOException { return pull(); } |
---|
| 105 | public int read(byte[] b, int off, int len) throws IOException { return pull(b, off, len); } |
---|
| 106 | public int read(byte[] b) throws IOException { return pull(b, 0, b.length); } |
---|
| 107 | }; |
---|
| 108 | |
---|
| 109 | input = new DataInputStream(inStream); |
---|
| 110 | } |
---|
| 111 | |
---|
| 112 | /** Reset the buffer. Does not resize it back to a smaller size -- if it has ballooned it will |
---|
| 113 | stay large, though it will no longer have wasted space in it. If you wish to make the buffer |
---|
| 114 | a more manageable size, create a new DataPipe instead. */ |
---|
| 115 | public void reset() |
---|
| 116 | { |
---|
| 117 | pull = size = 0; |
---|
| 118 | } |
---|
| 119 | |
---|
| 120 | /** Returns the total size of the buffer. */ |
---|
| 121 | public int size() |
---|
| 122 | { |
---|
| 123 | return buffer.length; |
---|
| 124 | } |
---|
| 125 | |
---|
| 126 | /** Returns the number of elements written to the buffer so far (after the last reset()). */ |
---|
| 127 | public int numWritten() |
---|
| 128 | { |
---|
| 129 | return size; |
---|
| 130 | } |
---|
| 131 | |
---|
| 132 | /** Returns the number of elements read from the buffer so far (after the last reset()). */ |
---|
| 133 | public int numRead() |
---|
| 134 | { |
---|
| 135 | return pull; |
---|
| 136 | } |
---|
| 137 | |
---|
| 138 | /** A poor-man's clone for serializable but not cloneable objects: |
---|
| 139 | serializes an object to the pipe, then deserializes it. */ |
---|
| 140 | public static Object copy(Serializable obj) throws IOException, ClassNotFoundException |
---|
| 141 | { |
---|
| 142 | DataPipe pipe = new DataPipe(); |
---|
| 143 | ObjectOutputStream s = new ObjectOutputStream(pipe.output); |
---|
| 144 | ObjectInputStream u = new ObjectInputStream(pipe.input); |
---|
| 145 | s.writeObject(obj); |
---|
| 146 | return u.readObject(); |
---|
| 147 | } |
---|
| 148 | |
---|
| 149 | public String toString() { return "DataPipe(" + numWritten() + ", " + numRead() + ", " + size() + ")"; } |
---|
| 150 | } |
---|