aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/experimental/BitInputStream.java
blob: 111e0b3661e087ee1a02489a000b7da932cfd274 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/*
 * Copyright (C) Andreas Jakl
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.sufficientlysecure.keychain.experimental;

import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;

/**
 * The BitInputStream allows reading individual bits from a
 * general Java InputStream.
 * Like the various Stream-classes from Java, the BitInputStream
 * has to be created based on another Input stream. It provides
 * a function to read the next bit from the stream, as well as to read multiple
 * bits at once and write the resulting data into an integer value.
 * <p/>
 * source: http://developer.nokia.com/Community/Wiki/Bit_Input/Output_Stream_utility_classes_for_efficient_data_transfer
 */
public class BitInputStream {
    /**
     * The Java InputStream this class is working on.
     */
    private InputStream iIs;

    /**
     * The buffer containing the currently processed
     * byte of the input stream.
     */
    private int iBuffer;

    /**
     * Next bit of the current byte value that the user will
     * get. If it's 8, the next bit will be read from the
     * next byte of the InputStream.
     */
    private int iNextBit = 8;

    /**
     * Create a new bit input stream based on an existing Java InputStream.
     *
     * @param aIs the input stream this class should read the bits from.
     */
    public BitInputStream(InputStream aIs) {
        iIs = aIs;
    }

    /**
     * Read a specified number of bits and return them combined as
     * an integer value. The bits are written to the integer
     * starting at the highest bit ( << aNumberOfBits ), going down
     * to the lowest bit ( << 0 )
     *
     * @param aNumberOfBits defines how many bits to read from the stream.
     * @return integer value containing the bits read from the stream.
     * @throws IOException
     */
    synchronized public int readBits(final int aNumberOfBits, boolean stuffIfEnd)
            throws IOException {
        int value = 0;
        for (int i = aNumberOfBits - 1; i >= 0; i--) {
            value |= (readBit(stuffIfEnd) << i);
        }
        return value;
    }

    synchronized public int available() {
        try {
            return (8 - iNextBit) + iIs.available() * 8; // bytestream to bitstream available
        } catch (Exception e) {
            return 0;
        }
    }

    /**
     * Read the next bit from the stream.
     *
     * @return 0 if the bit is 0, 1 if the bit is 1.
     * @throws IOException
     */
    synchronized public int readBit(boolean stuffIfEnd) throws IOException {
        if (iIs == null)
            throw new IOException("Already closed");

        if (iNextBit == 8) {
            iBuffer = iIs.read();

            if (iBuffer == -1) {
                if (stuffIfEnd) {
                    return 1;
                } else {
                    throw new EOFException();
                }
            }

            iNextBit = 0;
        }

        int bit = iBuffer & (1 << iNextBit);
        iNextBit++;

        bit = (bit == 0) ? 0 : 1;

        return bit;
    }

    /**
     * Close the underlying input stream.
     *
     * @throws IOException
     */
    public void close() throws IOException {
        iIs.close();
        iIs = null;
    }
}