Mega Code Archive

 
Categories / Java / File Input Output
 

Introduce a protocol for reading arbitrary length data in a uniform way

/*  * Licensed to the Apache Software Foundation (ASF) under one or more  * contributor license agreements.  See the NOTICE file distributed with  * this work for additional information regarding copyright ownership.  * The ASF licenses this file to You under the Apache License, Version 2.0  * (the "License"); you may not use this file except in compliance with  * the License.  You may obtain a copy of the License at  *   *      http://www.apache.org/licenses/LICENSE-2.0  *   * Unless required by applicable law or agreed to in writing, software  * distributed under the License is distributed on an "AS IS" BASIS,  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  * See the License for the specific language governing permissions and  * limitations under the License.  */ import java.io.DataInputStream; import java.io.FilterInputStream; import java.io.InputStream; import java.io.IOException; /**  * This input stream works in conjunction with the WrappedOutputStream  * to introduce a protocol for reading arbitrary length data in a  * uniform way.  * <p>  * <strong>Note:</strong> See the javadoc for WrappedOutputStream for  * more information.  *  * @see WrappedOutputStream  *  * @author Andy Clark, IBM  *  * @version $Id: WrappedInputStream.java 447688 2006-09-19 02:39:49Z mrglavas $  */ public class WrappedInputStream     extends FilterInputStream {     //     // Data     //     /** Bytes left on input stream for current packet. */     protected int fPacketCount;     /**       * Data input stream. This stream is used to input the block sizes      * from the data stream that are written by the WrappedOutputStream.      * <p>      * <strong>Note:</strong> The data input stream is only used for      * reading the byte count for performance reasons. We avoid the      * method indirection for reading the byte data.      */     protected DataInputStream fDataInputStream;     /** To mark that the stream is "closed". */     protected boolean fClosed;     //     // Constructors     //     /** Constructs a wrapper for the given an input stream. */     public WrappedInputStream(InputStream stream) {         super(stream);         fDataInputStream = new DataInputStream(stream);     } // <init>(InputStream)     //     // InputStream methods     //     /** Reads a single byte. */     public int read() throws IOException {         // ignore, if already closed         if (fClosed) {             return -1;         }         // read packet header         if (fPacketCount == 0) {             fPacketCount = fDataInputStream.readInt() & 0x7FFFFFFF;             if (fPacketCount == 0) {                 fClosed = true;                 return -1;             }         }         // read a byte from the packet         fPacketCount--;         return super.in.read();     } // read():int     /**       * Reads a block of bytes and returns the total number of bytes read.       */     public int read(byte[] b, int offset, int length) throws IOException {         // ignore, if already closed         if (fClosed) {             return -1;         }         // read packet header         if (fPacketCount == 0) {             fPacketCount = fDataInputStream.readInt() & 0x7FFFFFFF;             if (fPacketCount == 0) {                 fClosed = true;                 return -1;             }         }         // read bytes from packet         if (length > fPacketCount) {             length = fPacketCount;         }         int count = super.in.read(b, offset, length);         if (count == -1) {             // NOTE: This condition should not happen. The end of              //       the stream should always be designated by a              //       byte count header of 0. -Ac             fClosed = true;             return -1;         }         fPacketCount -= count;         // return total bytes read         return count;     } // read(byte[],int,int):int     /** Skips the specified number of bytes from the input stream. */     public long skip(long n) throws IOException {         if (!fClosed) {             // NOTE: This should be rewritten to be more efficient. -Ac             for (long i = 0; i < n; i++) {                 int b = read();                 if (b == -1) {                     return i + 1;                 }             }             return n;         }         return 0;     } // skip(long):long     /**       * Closes the input stream. This method will search for the end of      * the wrapped input, positioning the stream at after the end packet.      * <p>      * <strong>Note:</strong> This method does not close the underlying      * input stream.      */     public void close() throws IOException {         if (!fClosed) {             fClosed = true;             do {                 super.in.skip(fPacketCount);                 fPacketCount = fDataInputStream.readInt() & 0x7FFFFFFF;             } while (fPacketCount > 0);         }     } // close() } // class WrappedInputStream /*  * Licensed to the Apache Software Foundation (ASF) under one or more  * contributor license agreements.  See the NOTICE file distributed with  * this work for additional information regarding copyright ownership.  * The ASF licenses this file to You under the Apache License, Version 2.0  * (the "License"); you may not use this file except in compliance with  * the License.  You may obtain a copy of the License at  *   *      http://www.apache.org/licenses/LICENSE-2.0  *   * Unless required by applicable law or agreed to in writing, software  * distributed under the License is distributed on an "AS IS" BASIS,  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  * See the License for the specific language governing permissions and  * limitations under the License.  */ import java.io.DataOutputStream; import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; /**  * This output stream works in conjunction with the WrappedInputStream  * to introduce a protocol for sending arbitrary length data in a  * uniform way. This output stream allows variable length data to be  * inserted into an existing output stream so that it can be read by  * an input stream without reading too many bytes (in case of buffering  * by the input stream).  * <p>  * This output stream is used like any normal output stream. The protocol  * is introduced by the WrappedOutputStream and does not need to be known  * by the user of this class. However, for those that are interested, the  * method is described below.  * <p>  * The output stream writes the requested bytes as packets of binary  * information. The packet consists of a header and payload. The header  * is two bytes of a single unsigned short (written in network order)   * that specifies the length of bytes in the payload. A header value of  * 0 indicates that the stream is "closed".  * <p>  * <strong>Note:</strong> For this wrapped output stream to be used,  * the application <strong>must</strong> call <code>close()</code>  * to end the output.  *  * @see WrappedInputStream  *  * @author Andy Clark, IBM  *  * @version $Id: WrappedOutputStream.java 447688 2006-09-19 02:39:49Z mrglavas $  */ public class WrappedOutputStream     extends FilterOutputStream {     //     // Constants     //     /** Default buffer size (1024). */     public static final int DEFAULT_BUFFER_SIZE = 1024;     //     // Data     //     /** Buffer. */     protected byte[] fBuffer;     /** Buffer position. */     protected int fPosition;     /**       * Data output stream. This stream is used to output the block sizes      * into the data stream that are read by the WrappedInputStream.      * <p>      * <strong>Note:</strong> The data output stream is only used for      * writing the byte count for performance reasons. We avoid the      * method indirection for writing the byte data.      */     protected DataOutputStream fDataOutputStream;     //     // Constructors     //     /** Constructs a wrapper for the given output stream. */     public WrappedOutputStream(OutputStream stream) {         this(stream, DEFAULT_BUFFER_SIZE);     } // <init>(OutputStream)     /**       * Constructs a wrapper for the given output stream with the      * given buffer size.      */     public WrappedOutputStream(OutputStream stream, int bufferSize) {         super(stream);         fBuffer = new byte[bufferSize];         fDataOutputStream = new DataOutputStream(stream);     } // <init>(OutputStream)     //     // OutputStream methods     //     /**       * Writes a single byte to the output.       * <p>      * <strong>Note:</strong> Single bytes written to the output stream      * will be buffered      */     public void write(int b) throws IOException {         fBuffer[fPosition++] = (byte)b;         if (fPosition == fBuffer.length) {             fPosition = 0;             fDataOutputStream.writeInt(fBuffer.length);             super.out.write(fBuffer, 0, fBuffer.length);         }     } // write(int)     /** Writes an array of bytes to the output. */     public void write(byte[] b, int offset, int length)          throws IOException {         // flush existing buffer         if (fPosition > 0) {             flush0();         }         // write header followed by actual bytes         fDataOutputStream.writeInt(length);         super.out.write(b, offset, length);     } // write(byte[])     /**       * Flushes the output buffer, writing all bytes currently in      * the buffer to the output.      */     public void flush() throws IOException {         flush0();         super.out.flush();     } // flush()     /**       * Closes the output stream. This method <strong>must</strong> be      * called when done writing all data to the output stream.      * <p>      * <strong>Note:</strong> This method does <em>not</em> close the      * actual output stream, only makes the input stream see the stream      * closed. Do not write bytes after closing the output stream.      */     public void close() throws IOException {         flush0();         fDataOutputStream.writeInt(0);         super.out.flush();     } // close()     //     // Protected methods     //     /**       * Flushes the output buffer, writing all bytes currently in      * the buffer to the output. This method does not call the      * flush() method of the output stream; it merely writes the      * remaining bytes in the buffer.      */     public void flush0() throws IOException {         int length = fPosition;         fPosition = 0;         if (length > 0) {             fDataOutputStream.writeInt(length);             super.out.write(fBuffer, 0, length);         }     } // flush0() } // class WrappedOutputStream