HunterHuang0X7C7
2023-08-12 1c7c28f03215f03e97387d7e6b45ae752c396dcb
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
package com.github.hunter0x7c7.sync.utils;
 
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
 
public class Base64Decoder extends FilterInputStream {
 
    private static final char[] chars = {'A', 'B', 'C', 'D', 'E', 'F', 'G',
            'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
            'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
            'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
            'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6',
            '7', '8', '9', '+', '/'};
 
    // A mapping between char values and six-bit integers
    private static final int[] ints = new int[128];
 
    static {
        for (int i = 0; i < 64; i++) {
            ints[chars[i]] = i;
        }
    }
 
    private int charCount;
    private int carryOver;
 
    /***
     * Constructs a new Base64 decoder that reads input from the given
     * InputStream.
     *
     * @param in
     *            the input stream
     */
    public Base64Decoder(InputStream in) {
        super(in);
    }
 
    /***
     * Returns the next decoded character from the stream, or -1 if end of
     * stream was reached.
     *
     * @return the decoded character, or -1 if the end of the input stream is
     *         reached
     * @exception IOException
     *                if an I/O error occurs
     */
    public int read() throws IOException {
        // Read the next non-whitespace character
        int x;
        do {
            x = in.read();
            if (x == -1) {
                return -1;
            }
        } while (Character.isWhitespace((char) x));
        charCount++;
 
        // The '=' sign is just padding
        if (x == '=') {
            return -1; // effective end of stream
        }
 
        // Convert from raw form to 6-bit form
        x = ints[x];
 
        // Calculate which character we're decoding now
        int mode = (charCount - 1) % 4;
 
        // First char save all six bits, go for another
        if (mode == 0) {
            carryOver = x & 63;
            return read();
        }
        // Second char use previous six bits and first two new bits,
        // save last four bits
        else if (mode == 1) {
            int decoded = ((carryOver << 2) + (x >> 4)) & 255;
            carryOver = x & 15;
            return decoded;
        }
        // Third char use previous four bits and first four new bits,
        // save last two bits
        else if (mode == 2) {
            int decoded = ((carryOver << 4) + (x >> 2)) & 255;
            carryOver = x & 3;
            return decoded;
        }
        // Fourth char use previous two bits and all six new bits
        else if (mode == 3) {
            return ((carryOver << 6) + x) & 255;
        }
        return -1; // can't actually reach this line
    }
 
    /***
     * Reads decoded data into an array of bytes and returns the actual number
     * of bytes read, or -1 if end of stream was reached.
     *
     * @param buf
     *            the buffer into which the data is read
     * @param off
     *            the start offset of the data
     * @param len
     *            the maximum number of bytes to read
     * @return the actual number of bytes read, or -1 if the end of the input
     *         stream is reached
     * @exception IOException
     *                if an I/O error occurs
     */
    public int read(byte[] buf, int off, int len) throws IOException {
        if (buf.length < (len + off - 1)) {
            throw new IOException("The input buffer is too small: " + len
                    + " bytes requested starting at offset " + off
                    + " while the buffer " + " is only " + buf.length
                    + " bytes long.");
        }
 
        // This could of course be optimized
        int i;
        for (i = 0; i < len; i++) {
            int x = read();
            if (x == -1 && i == 0) { // an immediate -1 returns -1
                return -1;
            } else if (x == -1) { // a later -1 returns the chars read so far
                break;
            }
            buf[off + i] = (byte) x;
        }
        return i;
    }
 
    /***
     * Returns the decoded form of the given encoded string, as a String. Note
     * that not all binary data can be represented as a String, so this method
     * should only be used for encoded String data. Use decodeToBytes()
     * otherwise.
     *
     * @param encoded
     *            the string to decode
     * @return the decoded form of the encoded string
     */
    public static String decode(String encoded) {
        return new String(decodeToBytes(encoded));
    }
 
    /***
     * Returns the decoded form of the given encoded string, as bytes.
     *
     * @param encoded the string to decode
     * @return the decoded form of the encoded string
     */
    public static byte[] decodeToBytes(String encoded) {
        byte[] bytes = null;
        try {
            bytes = encoded.getBytes("UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (bytes != null) {
            Base64Decoder in = new Base64Decoder(new ByteArrayInputStream(bytes));
            ByteArrayOutputStream out = new ByteArrayOutputStream((int) (bytes.length * 0.67));
 
            try {
                byte[] buf = new byte[4 * 1024]; // 4K buffer
                int bytesRead;
                while ((bytesRead = in.read(buf)) != -1) {
                    out.write(buf, 0, bytesRead);
                }
                out.close();
 
                return out.toByteArray();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }
 
    public static void main(String[] args) throws Exception {
        if (args.length != 1) {
            System.err.println("Usage: java Base64Decoder fileToDecode");
            return;
        }
 
        Base64Decoder decoder = null;
        try {
            decoder = new Base64Decoder(new BufferedInputStream(new FileInputStream(args[0])));
            byte[] buf = new byte[4 * 1024]; // 4K buffer
            int bytesRead;
            while ((bytesRead = decoder.read(buf)) != -1) {
                System.out.write(buf, 0, bytesRead);
            }
        } finally {
            if (decoder != null)
                decoder.close();
        }
    }
}