Skip to content

Commit

Permalink
Fix for #5
Browse files Browse the repository at this point in the history
Add useBytes parameter to allow no coercion of dictionary values.
Clean up javadoc.
  • Loading branch information
dampcake committed Jul 21, 2019
1 parent a1b4524 commit 377578b
Show file tree
Hide file tree
Showing 6 changed files with 378 additions and 66 deletions.
57 changes: 50 additions & 7 deletions src/main/java/com/dampcake/bencode/Bencode.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,25 +47,59 @@ public final class Bencode {
static final char SEPARATOR = ':';

private final Charset charset;
private final boolean useBytes;

/**
* Create a new Bencoder using the default {@link Charset} (UTF-8)
* Create a new Bencoder using the default {@link Charset} (UTF-8) and useBytes as false.
*
* @see #Bencode(Charset, boolean)
*/
public Bencode() {
this.charset = DEFAULT_CHARSET;
this(DEFAULT_CHARSET);
}

/**
* Creates a new Bencoder using the {@link Charset} passed for encoding/decoding.
* Creates a new Bencoder using the {@link Charset} passed for encoding/decoding and useBytes as false.
*
* @param charset the {@link Charset} to use
*
* @throws NullPointerException if the {@link Charset} passed is null
*
* @see #Bencode(Charset, boolean)
*/
public Bencode(final Charset charset) {
this(charset, false);
}

/**
* Creates a new Bencoder using the boolean passed to control String parsing.
*
* @param useBytes {@link #Bencode(Charset, boolean)}
*
* @since 1.3
*/
public Bencode(final boolean useBytes) {
this(DEFAULT_CHARSET, useBytes);
}

/**
* Creates a new Bencoder using the {@link Charset} passed for encoding/decoding and boolean passed to control String parsing.
*
* If useBytes is false, then dictionary values that contain byte string data will be coerced to a {@link String}.
* if useBytes is true, then dictionary values that contain byte string data will be coerced to a {@link java.nio.ByteBuffer}.
*
* @param charset the {@link Charset} to use
* @param useBytes true to have dictionary byte data to stay as bytes
*
* @throws NullPointerException if the {@link Charset} passed is null
*
* @since 1.3
*/
public Bencode(final Charset charset, final boolean useBytes) {
if (charset == null) throw new NullPointerException("charset cannot be null");

this.charset = charset;
this.useBytes = useBytes;
}

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

BencodeInputStream in = new BencodeInputStream(new ByteArrayInputStream(bytes), charset);
BencodeInputStream in = new BencodeInputStream(new ByteArrayInputStream(bytes), charset, useBytes);

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

BencodeInputStream in = new BencodeInputStream(new ByteArrayInputStream(bytes), charset);
BencodeInputStream in = new BencodeInputStream(new ByteArrayInputStream(bytes), charset, useBytes);

try {
if (type == Type.NUMBER)
Expand All @@ -137,6 +172,8 @@ public <T> T decode(final byte[] bytes, final Type<T> type) {
*
* @param s the {@link String} to encode
*
* @return the encoded bytes
*
* @throws NullPointerException if the {@link String} is null
* @throws BencodeException if an error occurs during encoding
*/
Expand All @@ -153,6 +190,8 @@ public byte[] encode(final String s) {
*
* @param n the {@link Number} to encode
*
* @return the encoded bytes
*
* @throws NullPointerException if the {@link Number} is null
* @throws BencodeException if an error occurs during encoding
*/
Expand All @@ -169,7 +208,9 @@ public byte[] encode(final Number n) {
* any {@link Number} as a Number, any {@link Map} as a Dictionary and any other {@link Object} is written as a String
* calling the {@link Object#toString()} method.
*
* @param l the List to encode
* @param l the {@link Iterable} to encode
*
* @return the encoded bytes
*
* @throws NullPointerException if the List is null
* @throws BencodeException if an error occurs during encoding
Expand All @@ -187,7 +228,9 @@ public byte[] encode(final Iterable<?> l) {
* any {@link Number} as a Number, any {@link Map} as a Dictionary and any other {@link Object} is written as a String
* calling the {@link Object#toString()} method.
*
* @param m the Map to encode
* @param m the {@link Map} to encode
*
* @return the encoded bytes
*
* @throws NullPointerException if the Map is null
* @throws BencodeException if an error occurs during encoding
Expand Down
67 changes: 58 additions & 9 deletions src/main/java/com/dampcake/bencode/BencodeInputStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.io.InputStream;
import java.io.InvalidObjectException;
import java.io.PushbackInputStream;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.LinkedHashMap;
Expand All @@ -38,28 +39,55 @@ public class BencodeInputStream extends FilterInputStream {
private static final int EOF = -1;

private final Charset charset;
private final boolean useBytes;
private final PushbackInputStream in;

/**
* Creates a new BencodeInputStream that reads from the {@link InputStream} passed and uses the {@link Charset} passed for decoding the data.
* Creates a new BencodeInputStream that reads from the {@link InputStream} passed and uses the {@link Charset} passed for decoding the data
* and boolean passed to control String parsing.
*
* @param in the {@link InputStream} to read from
* @param charset the {@link Charset} to use
* If useBytes is false, then dictionary values that contain byte string data will be coerced to a {@link String}.
* if useBytes is true, then dictionary values that contain byte string data will be coerced to a {@link ByteBuffer}.
*
* @param in the {@link InputStream} to read from
* @param charset the {@link Charset} to use
* @param useBytes controls coercion of dictionary values
*
* @throws NullPointerException if the {@link Charset} passed is null
*
* @since 1.3
*/
public BencodeInputStream(final InputStream in, final Charset charset) {
public BencodeInputStream(final InputStream in, final Charset charset, boolean useBytes) {
super(new PushbackInputStream(in));
this.in = (PushbackInputStream) super.in;

if (charset == null) throw new NullPointerException("charset cannot be null");
this.charset = charset;
this.useBytes = useBytes;
}

/**
* Creates a new BencodeInputStream that reads from the {@link InputStream} passed and uses the UTF-8 {@link Charset} for decoding the data.
* Creates a new BencodeInputStream that reads from the {@link InputStream} passed and uses the {@link Charset} passed for decoding the data
* and coerces dictionary values to a {@link String}.
*
* @param in the {@link InputStream} to read from
* @param charset the {@link Charset} to use
*
* @throws NullPointerException if the {@link Charset} passed is null
*
* @see #BencodeInputStream(InputStream, Charset, boolean)
*/
public BencodeInputStream(final InputStream in, final Charset charset) {
this(in, charset, false);
}

/**
* Creates a new BencodeInputStream that reads from the {@link InputStream} passed and uses the UTF-8 {@link Charset} for decoding the data
* and coerces dictionary values to a {@link String}.
*
* @param in the {@link InputStream} to read from
*
* @see #BencodeInputStream(InputStream, Charset, boolean)
*/
public BencodeInputStream(final InputStream in) {
this(in, Bencode.DEFAULT_CHARSET);
Expand Down Expand Up @@ -105,15 +133,34 @@ private Type typeForToken(int token) {
}

/**
* Reads a String from the stream.
* Reads a {@link String} from the stream.
*
* @return the String read from the stream
* @return the {@link String} read from the stream
*
* @throws IOException if the underlying stream throws
* @throws EOFException if the end of the stream has been reached
* @throws InvalidObjectException if the next type in the stream is not a String
*/
public String readString() throws IOException {
return new String(readStringBytesInternal(), getCharset());
}

/**
* Reads a Byte String from the stream.
*
* @return the {@link ByteBuffer} read from the stream
*
* @throws IOException if the underlying stream throws
* @throws EOFException if the end of the stream has been reached
* @throws InvalidObjectException if the next type in the stream is not a String
*
* @since 1.3
*/
public ByteBuffer readStringBytes() throws IOException {
return ByteBuffer.wrap(readStringBytesInternal());
}

private byte[] readStringBytesInternal() throws IOException {
int token = in.read();
validateToken(token, Type.STRING);

Expand All @@ -128,7 +175,7 @@ public String readString() throws IOException {
int length = Integer.parseInt(buffer.toString());
byte[] bytes = new byte[length];
read(bytes);
return new String(bytes, getCharset());
return bytes;
}

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

Type type = typeForToken(token);

if (type == Type.STRING)
if (type == Type.STRING && !useBytes)
return readString();
if (type == Type.STRING && useBytes)
return readStringBytes();
if (type == Type.NUMBER)
return readNumber();
if (type == Type.LIST)
Expand Down
Loading

0 comments on commit 377578b

Please sign in to comment.