001: package org.bouncycastle.bcpg;
002:
003: import java.io.*;
004:
005: /**
006: * Basic output stream.
007: */
008: public class BCPGOutputStream extends OutputStream implements
009: PacketTags, CompressionAlgorithmTags {
010: OutputStream out;
011: private byte[] partialBuffer;
012: private int partialBufferLength;
013: private int partialPower;
014: private int partialOffset;
015:
016: private static final int BUF_SIZE_POWER = 16; // 2^16 size buffer on long files
017:
018: public BCPGOutputStream(OutputStream out) {
019: this .out = out;
020: }
021:
022: /**
023: * Create a stream representing an old style partial object.
024: *
025: * @param tag the packet tag for the object.
026: */
027: public BCPGOutputStream(OutputStream out, int tag)
028: throws IOException {
029: this .out = out;
030: this .writeHeader(tag, true, true, 0);
031: }
032:
033: /**
034: * Create a stream representing a general packet.
035: *
036: * @param out
037: * @param tag
038: * @param length
039: * @param oldFormat
040: * @throws IOException
041: */
042: public BCPGOutputStream(OutputStream out, int tag, long length,
043: boolean oldFormat) throws IOException {
044: this .out = out;
045:
046: if (length > 0xFFFFFFFFL) {
047: this .writeHeader(tag, false, true, 0);
048: this .partialBufferLength = 1 << BUF_SIZE_POWER;
049: this .partialBuffer = new byte[partialBufferLength];
050: this .partialOffset = 0;
051: } else {
052: this .writeHeader(tag, oldFormat, false, length);
053: }
054: }
055:
056: /**
057: *
058: * @param tag
059: * @param length
060: * @throws IOException
061: */
062: public BCPGOutputStream(OutputStream out, int tag, long length)
063: throws IOException {
064: this .out = out;
065:
066: this .writeHeader(tag, false, false, length);
067: }
068:
069: /**
070: * Create a new style partial input stream buffered into chunks.
071: *
072: * @param out output stream to write to.
073: * @param tag packet tag.
074: * @param buffer size of chunks making up the packet.
075: * @throws IOException
076: */
077: public BCPGOutputStream(OutputStream out, int tag, byte[] buffer)
078: throws IOException {
079: this .out = out;
080: this .writeHeader(tag, false, true, 0);
081:
082: this .partialBuffer = buffer;
083:
084: int length = partialBuffer.length;
085:
086: for (partialPower = 0; length != 1; partialPower++) {
087: length >>>= 1;
088: }
089:
090: if (partialPower > 30) {
091: throw new IOException(
092: "Buffer cannot be greater than 2^30 in length.");
093: }
094:
095: this .partialBufferLength = 1 << partialPower;
096: this .partialOffset = 0;
097: }
098:
099: private void writeNewPacketLength(long bodyLen) throws IOException {
100: if (bodyLen < 192) {
101: out.write((byte) bodyLen);
102: } else if (bodyLen <= 8383) {
103: bodyLen -= 192;
104:
105: out.write((byte) (((bodyLen >> 8) & 0xff) + 192));
106: out.write((byte) bodyLen);
107: } else {
108: out.write(0xff);
109: out.write((byte) (bodyLen >> 24));
110: out.write((byte) (bodyLen >> 16));
111: out.write((byte) (bodyLen >> 8));
112: out.write((byte) bodyLen);
113: }
114: }
115:
116: private void writeHeader(int tag, boolean oldPackets,
117: boolean partial, long bodyLen) throws IOException {
118: int hdr = 0x80;
119:
120: if (partialBuffer != null) {
121: partialFlush(true);
122: partialBuffer = null;
123: }
124:
125: if (oldPackets) {
126: hdr |= tag << 2;
127:
128: if (partial) {
129: this .write(hdr | 0x03);
130: } else {
131: if (bodyLen <= 0xff) {
132: this .write(hdr);
133: this .write((byte) bodyLen);
134: } else if (bodyLen <= 0xffff) {
135: this .write(hdr | 0x01);
136: this .write((byte) (bodyLen >> 8));
137: this .write((byte) (bodyLen));
138: } else {
139: this .write(hdr | 0x02);
140: this .write((byte) (bodyLen >> 24));
141: this .write((byte) (bodyLen >> 16));
142: this .write((byte) (bodyLen >> 8));
143: this .write((byte) bodyLen);
144: }
145: }
146: } else {
147: hdr |= 0x40 | tag;
148: this .write(hdr);
149:
150: if (partial) {
151: partialOffset = 0;
152: } else {
153: this .writeNewPacketLength(bodyLen);
154: }
155: }
156: }
157:
158: private void partialFlush(boolean isLast) throws IOException {
159: if (isLast) {
160: writeNewPacketLength(partialOffset);
161: out.write(partialBuffer, 0, partialOffset);
162: } else {
163: out.write(0xE0 | partialPower);
164: out.write(partialBuffer, 0, partialBufferLength);
165: }
166:
167: partialOffset = 0;
168: }
169:
170: private void writePartial(byte b) throws IOException {
171: if (partialOffset == partialBufferLength) {
172: partialFlush(false);
173: }
174:
175: partialBuffer[partialOffset++] = b;
176: }
177:
178: private void writePartial(byte[] buf, int off, int len)
179: throws IOException {
180: if (partialOffset == partialBufferLength) {
181: partialFlush(false);
182: }
183:
184: if (len <= (partialBufferLength - partialOffset)) {
185: System.arraycopy(buf, off, partialBuffer, partialOffset,
186: len);
187: partialOffset += len;
188: } else {
189: System.arraycopy(buf, off, partialBuffer, partialOffset,
190: partialBufferLength - partialOffset);
191: off += partialBufferLength - partialOffset;
192: len -= partialBufferLength - partialOffset;
193: partialFlush(false);
194:
195: while (len > partialBufferLength) {
196: System.arraycopy(buf, off, partialBuffer, 0,
197: partialBufferLength);
198: off += partialBufferLength;
199: len -= partialBufferLength;
200: partialFlush(false);
201: }
202:
203: System.arraycopy(buf, off, partialBuffer, 0, len);
204: partialOffset += len;
205: }
206: }
207:
208: public void write(int b) throws IOException {
209: if (partialBuffer != null) {
210: writePartial((byte) b);
211: } else {
212: out.write(b);
213: }
214: }
215:
216: public void write(byte[] bytes, int off, int len)
217: throws IOException {
218: if (partialBuffer != null) {
219: writePartial(bytes, off, len);
220: } else {
221: out.write(bytes, off, len);
222: }
223: }
224:
225: public void writePacket(ContainedPacket p) throws IOException {
226: p.encode(this );
227: }
228:
229: void writePacket(int tag, byte[] body, boolean oldFormat)
230: throws IOException {
231: this .writeHeader(tag, oldFormat, false, body.length);
232: this .write(body);
233: }
234:
235: public void writeObject(BCPGObject o) throws IOException {
236: o.encode(this );
237: }
238:
239: /**
240: * Flush the underlying stream.
241: */
242: public void flush() throws IOException {
243: out.flush();
244: }
245:
246: /**
247: * Finish writing out the current packet without closing the underlying stream.
248: */
249: public void finish() throws IOException {
250: if (partialBuffer != null) {
251: partialFlush(true);
252: partialBuffer = null;
253: }
254: }
255:
256: public void close() throws IOException {
257: this.finish();
258: out.flush();
259: out.close();
260: }
261: }
|