Skip to content

Commit 377578b

Browse files
committed
Fix for #5
Add useBytes parameter to allow no coercion of dictionary values. Clean up javadoc.
1 parent a1b4524 commit 377578b

File tree

6 files changed

+378
-66
lines changed

6 files changed

+378
-66
lines changed

src/main/java/com/dampcake/bencode/Bencode.java

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,25 +47,59 @@ public final class Bencode {
4747
static final char SEPARATOR = ':';
4848

4949
private final Charset charset;
50+
private final boolean useBytes;
5051

5152
/**
52-
* Create a new Bencoder using the default {@link Charset} (UTF-8)
53+
* Create a new Bencoder using the default {@link Charset} (UTF-8) and useBytes as false.
54+
*
55+
* @see #Bencode(Charset, boolean)
5356
*/
5457
public Bencode() {
55-
this.charset = DEFAULT_CHARSET;
58+
this(DEFAULT_CHARSET);
5659
}
5760

5861
/**
59-
* Creates a new Bencoder using the {@link Charset} passed for encoding/decoding.
62+
* Creates a new Bencoder using the {@link Charset} passed for encoding/decoding and useBytes as false.
6063
*
6164
* @param charset the {@link Charset} to use
6265
*
6366
* @throws NullPointerException if the {@link Charset} passed is null
67+
*
68+
* @see #Bencode(Charset, boolean)
6469
*/
6570
public Bencode(final Charset charset) {
71+
this(charset, false);
72+
}
73+
74+
/**
75+
* Creates a new Bencoder using the boolean passed to control String parsing.
76+
*
77+
* @param useBytes {@link #Bencode(Charset, boolean)}
78+
*
79+
* @since 1.3
80+
*/
81+
public Bencode(final boolean useBytes) {
82+
this(DEFAULT_CHARSET, useBytes);
83+
}
84+
85+
/**
86+
* Creates a new Bencoder using the {@link Charset} passed for encoding/decoding and boolean passed to control String parsing.
87+
*
88+
* If useBytes is false, then dictionary values that contain byte string data will be coerced to a {@link String}.
89+
* if useBytes is true, then dictionary values that contain byte string data will be coerced to a {@link java.nio.ByteBuffer}.
90+
*
91+
* @param charset the {@link Charset} to use
92+
* @param useBytes true to have dictionary byte data to stay as bytes
93+
*
94+
* @throws NullPointerException if the {@link Charset} passed is null
95+
*
96+
* @since 1.3
97+
*/
98+
public Bencode(final Charset charset, final boolean useBytes) {
6699
if (charset == null) throw new NullPointerException("charset cannot be null");
67100

68101
this.charset = charset;
102+
this.useBytes = useBytes;
69103
}
70104

71105
/**
@@ -90,7 +124,7 @@ public Charset getCharset() {
90124
public Type type(final byte[] bytes) {
91125
if (bytes == null) throw new NullPointerException("bytes cannot be null");
92126

93-
BencodeInputStream in = new BencodeInputStream(new ByteArrayInputStream(bytes), charset);
127+
BencodeInputStream in = new BencodeInputStream(new ByteArrayInputStream(bytes), charset, useBytes);
94128

95129
try {
96130
return in.nextType();
@@ -102,6 +136,7 @@ public Type type(final byte[] bytes) {
102136
/**
103137
* Decodes a bencode encoded byte array.
104138
*
139+
* @param <T> inferred from the {@link Type} parameter
105140
* @param bytes the bytes to decode
106141
* @param type the {@link Type} to decode as
107142
*
@@ -117,7 +152,7 @@ public <T> T decode(final byte[] bytes, final Type<T> type) {
117152
if (type == null) throw new NullPointerException("type cannot be null");
118153
if (type == Type.UNKNOWN) throw new IllegalArgumentException("type cannot be UNKNOWN");
119154

120-
BencodeInputStream in = new BencodeInputStream(new ByteArrayInputStream(bytes), charset);
155+
BencodeInputStream in = new BencodeInputStream(new ByteArrayInputStream(bytes), charset, useBytes);
121156

122157
try {
123158
if (type == Type.NUMBER)
@@ -137,6 +172,8 @@ public <T> T decode(final byte[] bytes, final Type<T> type) {
137172
*
138173
* @param s the {@link String} to encode
139174
*
175+
* @return the encoded bytes
176+
*
140177
* @throws NullPointerException if the {@link String} is null
141178
* @throws BencodeException if an error occurs during encoding
142179
*/
@@ -153,6 +190,8 @@ public byte[] encode(final String s) {
153190
*
154191
* @param n the {@link Number} to encode
155192
*
193+
* @return the encoded bytes
194+
*
156195
* @throws NullPointerException if the {@link Number} is null
157196
* @throws BencodeException if an error occurs during encoding
158197
*/
@@ -169,7 +208,9 @@ public byte[] encode(final Number n) {
169208
* any {@link Number} as a Number, any {@link Map} as a Dictionary and any other {@link Object} is written as a String
170209
* calling the {@link Object#toString()} method.
171210
*
172-
* @param l the List to encode
211+
* @param l the {@link Iterable} to encode
212+
*
213+
* @return the encoded bytes
173214
*
174215
* @throws NullPointerException if the List is null
175216
* @throws BencodeException if an error occurs during encoding
@@ -187,7 +228,9 @@ public byte[] encode(final Iterable<?> l) {
187228
* any {@link Number} as a Number, any {@link Map} as a Dictionary and any other {@link Object} is written as a String
188229
* calling the {@link Object#toString()} method.
189230
*
190-
* @param m the Map to encode
231+
* @param m the {@link Map} to encode
232+
*
233+
* @return the encoded bytes
191234
*
192235
* @throws NullPointerException if the Map is null
193236
* @throws BencodeException if an error occurs during encoding

src/main/java/com/dampcake/bencode/BencodeInputStream.java

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.io.InputStream;
2222
import java.io.InvalidObjectException;
2323
import java.io.PushbackInputStream;
24+
import java.nio.ByteBuffer;
2425
import java.nio.charset.Charset;
2526
import java.util.ArrayList;
2627
import java.util.LinkedHashMap;
@@ -38,28 +39,55 @@ public class BencodeInputStream extends FilterInputStream {
3839
private static final int EOF = -1;
3940

4041
private final Charset charset;
42+
private final boolean useBytes;
4143
private final PushbackInputStream in;
4244

4345
/**
44-
* Creates a new BencodeInputStream that reads from the {@link InputStream} passed and uses the {@link Charset} passed for decoding the data.
46+
* Creates a new BencodeInputStream that reads from the {@link InputStream} passed and uses the {@link Charset} passed for decoding the data
47+
* and boolean passed to control String parsing.
4548
*
46-
* @param in the {@link InputStream} to read from
47-
* @param charset the {@link Charset} to use
49+
* If useBytes is false, then dictionary values that contain byte string data will be coerced to a {@link String}.
50+
* if useBytes is true, then dictionary values that contain byte string data will be coerced to a {@link ByteBuffer}.
51+
*
52+
* @param in the {@link InputStream} to read from
53+
* @param charset the {@link Charset} to use
54+
* @param useBytes controls coercion of dictionary values
4855
*
4956
* @throws NullPointerException if the {@link Charset} passed is null
57+
*
58+
* @since 1.3
5059
*/
51-
public BencodeInputStream(final InputStream in, final Charset charset) {
60+
public BencodeInputStream(final InputStream in, final Charset charset, boolean useBytes) {
5261
super(new PushbackInputStream(in));
5362
this.in = (PushbackInputStream) super.in;
5463

5564
if (charset == null) throw new NullPointerException("charset cannot be null");
5665
this.charset = charset;
66+
this.useBytes = useBytes;
5767
}
5868

5969
/**
60-
* Creates a new BencodeInputStream that reads from the {@link InputStream} passed and uses the UTF-8 {@link Charset} for decoding the data.
70+
* Creates a new BencodeInputStream that reads from the {@link InputStream} passed and uses the {@link Charset} passed for decoding the data
71+
* and coerces dictionary values to a {@link String}.
72+
*
73+
* @param in the {@link InputStream} to read from
74+
* @param charset the {@link Charset} to use
75+
*
76+
* @throws NullPointerException if the {@link Charset} passed is null
77+
*
78+
* @see #BencodeInputStream(InputStream, Charset, boolean)
79+
*/
80+
public BencodeInputStream(final InputStream in, final Charset charset) {
81+
this(in, charset, false);
82+
}
83+
84+
/**
85+
* Creates a new BencodeInputStream that reads from the {@link InputStream} passed and uses the UTF-8 {@link Charset} for decoding the data
86+
* and coerces dictionary values to a {@link String}.
6187
*
6288
* @param in the {@link InputStream} to read from
89+
*
90+
* @see #BencodeInputStream(InputStream, Charset, boolean)
6391
*/
6492
public BencodeInputStream(final InputStream in) {
6593
this(in, Bencode.DEFAULT_CHARSET);
@@ -105,15 +133,34 @@ private Type typeForToken(int token) {
105133
}
106134

107135
/**
108-
* Reads a String from the stream.
136+
* Reads a {@link String} from the stream.
109137
*
110-
* @return the String read from the stream
138+
* @return the {@link String} read from the stream
111139
*
112140
* @throws IOException if the underlying stream throws
113141
* @throws EOFException if the end of the stream has been reached
114142
* @throws InvalidObjectException if the next type in the stream is not a String
115143
*/
116144
public String readString() throws IOException {
145+
return new String(readStringBytesInternal(), getCharset());
146+
}
147+
148+
/**
149+
* Reads a Byte String from the stream.
150+
*
151+
* @return the {@link ByteBuffer} read from the stream
152+
*
153+
* @throws IOException if the underlying stream throws
154+
* @throws EOFException if the end of the stream has been reached
155+
* @throws InvalidObjectException if the next type in the stream is not a String
156+
*
157+
* @since 1.3
158+
*/
159+
public ByteBuffer readStringBytes() throws IOException {
160+
return ByteBuffer.wrap(readStringBytesInternal());
161+
}
162+
163+
private byte[] readStringBytesInternal() throws IOException {
117164
int token = in.read();
118165
validateToken(token, Type.STRING);
119166

@@ -128,7 +175,7 @@ public String readString() throws IOException {
128175
int length = Integer.parseInt(buffer.toString());
129176
byte[] bytes = new byte[length];
130177
read(bytes);
131-
return new String(bytes, getCharset());
178+
return bytes;
132179
}
133180

134181
/**
@@ -206,8 +253,10 @@ private Object readObject(final int token) throws IOException {
206253

207254
Type type = typeForToken(token);
208255

209-
if (type == Type.STRING)
256+
if (type == Type.STRING && !useBytes)
210257
return readString();
258+
if (type == Type.STRING && useBytes)
259+
return readStringBytes();
211260
if (type == Type.NUMBER)
212261
return readNumber();
213262
if (type == Type.LIST)

0 commit comments

Comments
 (0)