/* * BDecoder - Converts an InputStream to BEValues. Copyright (C) 2003 Mark J. * Wielaard * * This file is part of Snark. * * 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 2, 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, write to the Free Software Foundation, Inc., 59 Temple * Place - Suite 330, Boston, MA 02111-1307, USA. * * Revised by Stephen L. Reed, Dec 22, 2009. * Reformatted, fixed Checkstyle, Findbugs and PMD violations, and substituted Log4J logger * for consistency with the Texai project. */ package org.texai.torrent.bencode; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; /** Converts an InputStream to BEValues. */ public final class BEncoder { /** Constructs a new BEncoder instance. Unused because all methods are static. */ private BEncoder() { } /** Bencodes the given object. * * @param object the given object * @return the encoded bytes */ public static byte[] bencode(final Object object) { try { final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); bencode(object, byteArrayOutputStream); return byteArrayOutputStream.toByteArray(); } catch (IOException ioe) { throw new InternalError(ioe.toString()); // NOPMD } } /** Bencodes the given object onto the given output stream. * * @param obj the given object * @param outputStream the given output stream * @throws IOException when an input/output error occurs */ @SuppressWarnings("unchecked") public static void bencode(final Object obj, final OutputStream outputStream) throws IOException { if (obj instanceof String) { bencode((String) obj, outputStream); } else if (obj instanceof byte[]) { bencode((byte[]) obj, outputStream); } else if (obj instanceof Number) { bencode((Number) obj, outputStream); } else if (obj instanceof List<?>) { bencode((List<?>) obj, outputStream); } else if (obj instanceof Map<?, ?>) { bencode((Map<Object, Object>) obj, outputStream); } else { throw new IllegalArgumentException("Cannot bencode: " + obj.getClass()); } } /** Bencodes the given string. * * @param string the given string * @return the encoded bytes */ public static byte[] bencode(final String string) { try { final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); bencode(string, byteArrayOutputStream); return byteArrayOutputStream.toByteArray(); } catch (IOException ioe) { throw new InternalError(ioe.toString()); // NOPMD } } /** Bencodes the given string onto the given output stream. * * @param string the given string * @param outputStream the given output stream * @throws IOException when an input/output error occurs */ public static void bencode(final String string, final OutputStream outputStream) throws IOException { bencode(string.getBytes("UTF-8"), outputStream); } /** Bencodes the given number. * * @param number the given number * @return the encoded bytes */ public static byte[] bencode(final Number number) { try { final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); bencode(number, byteArrayOutputStream); return byteArrayOutputStream.toByteArray(); } catch (IOException ioe) { throw new InternalError(ioe.toString()); // NOPMD } } /** Bencodes the given number onto the given output stream. * * @param number the given number * @param outputStream the given output stream * @throws IOException when an input/output error occurs */ public static void bencode(final Number number, final OutputStream outputStream) throws IOException { outputStream.write('i'); outputStream.write(number.toString().getBytes("UTF-8")); outputStream.write('e'); } /** Bencodes the given list. * * @param list the given list * @return the encoded bytes */ public static byte[] bencode(final List<?> list) { try { final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); bencode(list, byteArrayOutputStream); return byteArrayOutputStream.toByteArray(); } catch (IOException ioe) { throw new InternalError(ioe.toString()); // NOPMD } } /** Bencodes the given list onto the given output stream. * * @param list the given list * @param outputStream the given output stream * @throws IOException when an input/output error occurs */ public static void bencode(final List<?> list, final OutputStream outputStream) throws IOException { outputStream.write('l'); final Iterator<?> list_iter = list.iterator(); while (list_iter.hasNext()) { bencode(list_iter.next(), outputStream); } outputStream.write('e'); } /** Bencodes the given bytes. * * @param bytes the bytes supplied * @return the encoded bytes */ public static byte[] bencode(final byte[] bytes) { try { final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); bencode(bytes, byteArrayOutputStream); return byteArrayOutputStream.toByteArray(); } catch (IOException ioe) { throw new InternalError(ioe.toString()); // NOPMD } } /** Bencodes the given bytes onto the given output stream. * * @param bytes the given bytes * @param outputStream the given output stream * @throws IOException when an input/output error occurs */ public static void bencode(final byte[] bytes, final OutputStream outputStream) throws IOException { outputStream.write(Integer.toString(bytes.length).getBytes("UTF-8")); outputStream.write(':'); outputStream.write(bytes); } /** Bencodes the given map. * * @param map the given map * @return the encoded bytes */ public static byte[] bencode(final Map<Object, Object> map) { try { final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); bencode(map, byteArrayOutputStream); return byteArrayOutputStream.toByteArray(); } catch (IOException ioe) { throw new InternalError(ioe.toString()); // NOPMD } } /** Bencodes the given map onto the given output stream. * * @param map the given map * @param outputStream the given output stream * @throws IOException when an input/output error occurs */ public static void bencode(final Map<Object, Object> map, final OutputStream outputStream) throws IOException { outputStream.write('d'); // if the keys are all strings then sort them boolean areKeysAllStrings = true; for (final Object key : map.keySet()) { if (!(key instanceof String)) { areKeysAllStrings = false; break; } } final List<Object> keys = new ArrayList<>(map.keySet()); if (areKeysAllStrings) { final List<String> stringKeys = new ArrayList<>(keys.size()); for (final Object key : keys) { stringKeys.add((String) key); } Collections.sort(stringKeys); keys.clear(); keys.addAll(stringKeys); } for (final Object key : keys) { final Object value = map.get(key); bencode(key, outputStream); bencode(value, outputStream); } outputStream.write('e'); } }