1package org.bouncycastle.asn1;
2
3import java.io.IOException;
4import java.io.OutputStream;
5
6/**
7 * A generator for indefinite-length OCTET STRINGs
8 */
9public class BEROctetStringGenerator
10    extends BERGenerator
11{
12    /**
13     * Use the passed in stream as the target for the generator, writing out the header tag
14     * for a constructed OCTET STRING.
15     *
16     * @param out target stream
17     * @throws IOException if the target stream cannot be written to.
18     */
19    public BEROctetStringGenerator(OutputStream out)
20        throws IOException
21    {
22        super(out);
23
24        writeBERHeader(BERTags.CONSTRUCTED | BERTags.OCTET_STRING);
25    }
26
27    /**
28     * Use the passed in stream as the target for the generator, writing out the header tag
29     * for a tagged constructed OCTET STRING (possibly implicit).
30     *
31     * @param out target stream
32     * @param tagNo the tag number to introduce
33     * @param isExplicit true if this is an explicitly tagged object, false otherwise.
34     * @throws IOException if the target stream cannot be written to.
35     */
36    public BEROctetStringGenerator(
37        OutputStream out,
38        int tagNo,
39        boolean isExplicit)
40        throws IOException
41    {
42        super(out, tagNo, isExplicit);
43
44        writeBERHeader(BERTags.CONSTRUCTED | BERTags.OCTET_STRING);
45    }
46
47    /**
48     * Return a stream representing the content target for this OCTET STRING
49     *
50     * @return an OutputStream which chunks data in blocks of 1000 (CER limit).
51     */
52    public OutputStream getOctetOutputStream()
53    {
54        return getOctetOutputStream(new byte[1000]); // limit for CER encoding.
55    }
56
57    /**
58     * Return a stream representing the content target for this OCTET STRING
59     *
60     * @param buf the buffer to use for chunking the data.
61     * @return an OutputStream which chunks data in blocks of buf length.
62     */
63    public OutputStream getOctetOutputStream(
64        byte[] buf)
65    {
66        return new BufferedBEROctetStream(buf);
67    }
68
69    private class BufferedBEROctetStream
70        extends OutputStream
71    {
72        private byte[] _buf;
73        private int    _off;
74        private DEROutputStream _derOut;
75
76        BufferedBEROctetStream(
77            byte[] buf)
78        {
79            _buf = buf;
80            _off = 0;
81            _derOut = new DEROutputStream(_out);
82        }
83
84        public void write(
85            int b)
86            throws IOException
87        {
88            _buf[_off++] = (byte)b;
89
90            if (_off == _buf.length)
91            {
92                DEROctetString.encode(_derOut, _buf);
93                _off = 0;
94            }
95        }
96
97        public void write(byte[] b, int off, int len) throws IOException
98        {
99            while (len > 0)
100            {
101                int numToCopy = Math.min(len, _buf.length - _off);
102                System.arraycopy(b, off, _buf, _off, numToCopy);
103
104                _off += numToCopy;
105                if (_off < _buf.length)
106                {
107                    break;
108                }
109
110                DEROctetString.encode(_derOut, _buf);
111                _off = 0;
112
113                off += numToCopy;
114                len -= numToCopy;
115            }
116        }
117
118        public void close()
119            throws IOException
120        {
121            if (_off != 0)
122            {
123                byte[] bytes = new byte[_off];
124                System.arraycopy(_buf, 0, bytes, 0, _off);
125
126                DEROctetString.encode(_derOut, bytes);
127            }
128
129             writeBEREnd();
130        }
131    }
132}
133