Skip to content

Commit d742754

Browse files
committed
Merge pull request mongodb#97 from sjoerdmulder/binary_fix
JAVA-733 JSON.parse doesn't handle $binary object correctly
2 parents 8c6562a + f883379 commit d742754

File tree

4 files changed

+216
-166
lines changed

4 files changed

+216
-166
lines changed
Lines changed: 91 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11

22
/**
33
* Copyright (C) 2012 10gen Inc.
4-
*
4+
*
55
* Licensed under the Apache License, Version 2.0 (the "License");
66
* you may not use this file except in compliance with the License.
77
* You may obtain a copy of the License at
8-
*
8+
*
99
* http://www.apache.org/licenses/LICENSE-2.0
10-
*
10+
*
1111
* Unless required by applicable law or agreed to in writing, software
1212
* distributed under the License is distributed on an "AS IS" BASIS,
1313
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -35,58 +35,101 @@
3535

3636
/**
3737
* Provides Base64 encoding and decoding </a>.
38-
*
38+
* <p/>
3939
* <p>
4040
* This class implements Base64 encoding
41-
*
42-
* Thanks to Apache Commons project. This class refactored from org.apache.commons.codec.binary
43-
*
41+
* <p/>
42+
* Thanks to Apache Commons project. This class refactored from org.apache.commons.codec.binary
43+
* <p/>
4444
* Original Thanks to "commons" project in ws.apache.org for this code.
4545
* http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
4646
* </p>
47-
*
4847
*/
4948
public class Base64Codec {
50-
51-
private static int BYTES_PER_UNENCODED_BLOCK = 3;
52-
private static int BYTES_PER_ENCODED_BLOCK = 4;
53-
54-
/** Mask used to extract 6 bits, used when encoding */
55-
private static final int SixBitMask = 0x3f;
56-
57-
/** padding char */
49+
50+
private static final int BYTES_PER_UNENCODED_BLOCK = 3;
51+
private static final int BYTES_PER_ENCODED_BLOCK = 4;
52+
53+
/**
54+
* Mask used to extract 6 bits, used when encoding
55+
*/
56+
private static final int SixBitMask = 0x3f;
57+
58+
/**
59+
* padding char
60+
*/
5861
private static final byte PAD = '=';
59-
62+
6063
/**
6164
* This array is a lookup table that translates 6-bit positive integer index values into their "Base64 Alphabet"
6265
* equivalents as specified in Table 1 of RFC 2045.
63-
*
6466
*/
65-
private static final byte[] EncodeTable = { 'A', 'B', 'C', 'D', 'E', 'F',
67+
private static final byte[] EncodeTable = {'A', 'B', 'C', 'D', 'E', 'F',
6668
'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
6769
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
6870
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
6971
't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5',
70-
'6', '7', '8', '9', '+', '/' };
71-
72-
72+
'6', '7', '8', '9', '+', '/'};
73+
74+
private static final int[] DecodeTable = new int[128];
75+
76+
static {
77+
for (int i = 0; i < EncodeTable.length; i++) {
78+
DecodeTable[EncodeTable[i]] = i;
79+
}
80+
}
81+
82+
/**
83+
* Translates the specified Base64 string into a byte array.
84+
*
85+
* @param s the Base64 string (not null)
86+
* @return the byte array (not null)
87+
*/
88+
public byte[] decode(String s) {
89+
int delta = s.endsWith("==") ? 2 : s.endsWith("=") ? 1 : 0;
90+
byte[] buffer = new byte[s.length() * BYTES_PER_UNENCODED_BLOCK / BYTES_PER_ENCODED_BLOCK - delta];
91+
int mask = 0xFF;
92+
int pos = 0;
93+
for (int i = 0; i < s.length(); i += BYTES_PER_ENCODED_BLOCK) {
94+
int c0 = DecodeTable[s.charAt(i)];
95+
int c1 = DecodeTable[s.charAt(i + 1)];
96+
buffer[pos++] = (byte) (((c0 << 2) | (c1 >> 4)) & mask);
97+
if (pos >= buffer.length) {
98+
return buffer;
99+
}
100+
int c2 = DecodeTable[s.charAt(i + 2)];
101+
buffer[pos++] = (byte) (((c1 << 4) | (c2 >> 2)) & mask);
102+
if (pos >= buffer.length) {
103+
return buffer;
104+
}
105+
int c3 = DecodeTable[s.charAt(i + 3)];
106+
buffer[pos++] = (byte) (((c2 << 6) | c3) & mask);
107+
}
108+
return buffer;
109+
}
110+
111+
/**
112+
* Translates the specified byte array into Base64 string.
113+
*
114+
* @param in the byte array (not null)
115+
* @return the translated Base64 string (not null)
116+
*/
73117
public String encode(byte[] in) {
74-
118+
75119
int modulus = 0;
76120
int bitWorkArea = 0;
77-
int numEncodedBytes = (in.length/BYTES_PER_UNENCODED_BLOCK)*BYTES_PER_ENCODED_BLOCK
78-
+ ((in.length%BYTES_PER_UNENCODED_BLOCK == 0 )?0:4);
121+
int numEncodedBytes = (in.length / BYTES_PER_UNENCODED_BLOCK) * BYTES_PER_ENCODED_BLOCK
122+
+ ((in.length % BYTES_PER_UNENCODED_BLOCK == 0) ? 0 : 4);
79123

80-
byte[] buffer = new byte[numEncodedBytes];
124+
byte[] buffer = new byte[numEncodedBytes];
81125
int pos = 0;
82-
83-
for (int i = 0; i < in.length; i++) {
84-
modulus = (modulus+1) % BYTES_PER_UNENCODED_BLOCK;
85-
int b = in[i];
86-
87-
if (b < 0)
126+
127+
for (int b : in) {
128+
modulus = (modulus + 1) % BYTES_PER_UNENCODED_BLOCK;
129+
130+
if (b < 0)
88131
b += 256;
89-
132+
90133
bitWorkArea = (bitWorkArea << 8) + b; // BITS_PER_BYTE
91134
if (0 == modulus) { // 3 bytes = 24 bits = 4 * 6 bits to extract
92135
buffer[pos++] = EncodeTable[(bitWorkArea >> 18) & SixBitMask];
@@ -95,23 +138,23 @@ public String encode(byte[] in) {
95138
buffer[pos++] = EncodeTable[bitWorkArea & SixBitMask];
96139
}
97140
}
98-
141+
99142
switch (modulus) { // 0-2
100-
case 1 : // 8 bits = 6 + 2
101-
buffer[pos++] = EncodeTable[(bitWorkArea >> 2) & SixBitMask]; // top 6 bits
102-
buffer[pos++] = EncodeTable[(bitWorkArea << 4) & SixBitMask]; // remaining 2
103-
buffer[pos++] = PAD;
104-
buffer[pos++] = PAD;
105-
break;
106-
107-
case 2 : // 16 bits = 6 + 6 + 4
108-
buffer[pos++] = EncodeTable[(bitWorkArea >> 10) & SixBitMask];
109-
buffer[pos++] = EncodeTable[(bitWorkArea >> 4) & SixBitMask];
110-
buffer[pos++] = EncodeTable[(bitWorkArea << 2) & SixBitMask];
111-
buffer[pos++] = PAD;
112-
break;
143+
case 1: // 8 bits = 6 + 2
144+
buffer[pos++] = EncodeTable[(bitWorkArea >> 2) & SixBitMask]; // top 6 bits
145+
buffer[pos++] = EncodeTable[(bitWorkArea << 4) & SixBitMask]; // remaining 2
146+
buffer[pos++] = PAD;
147+
buffer[pos] = PAD; // Last entry no need to ++
148+
break;
149+
150+
case 2: // 16 bits = 6 + 6 + 4
151+
buffer[pos++] = EncodeTable[(bitWorkArea >> 10) & SixBitMask];
152+
buffer[pos++] = EncodeTable[(bitWorkArea >> 4) & SixBitMask];
153+
buffer[pos++] = EncodeTable[(bitWorkArea << 2) & SixBitMask];
154+
buffer[pos] = PAD; // Last entry no need to ++
155+
break;
113156
}
114-
157+
115158
return new String(buffer);
116159
}
117160
}

0 commit comments

Comments
 (0)