/torrent/src/main/java/org/torrent/bencoding/BEncoder.java

https://github.com/gurkerl83/millipede · Java · 157 lines · 119 code · 14 blank · 24 comment · 29 complexity · 6e8a2689eef4a89230699f4acbc6c43c MD5 · raw file

  1. package org.torrent.bencoding;
  2. import java.io.ByteArrayOutputStream;
  3. import java.io.IOException;
  4. import java.io.OutputStream;
  5. import java.io.UnsupportedEncodingException;
  6. import java.math.BigInteger;
  7. import java.util.ArrayList;
  8. import java.util.Collection;
  9. import java.util.Collections;
  10. import java.util.Comparator;
  11. import java.util.List;
  12. import java.util.Map;
  13. import org.torrent.internal.util.Validator;
  14. /**
  15. * Helper class to "BEncode" data.
  16. *
  17. * @author Dennis "Bytekeeper" Waldherr
  18. *
  19. */
  20. public final class BEncoder {
  21. static final Comparator<String> BYTE_COMPARATOR = new Comparator<String>() {
  22. @Override
  23. public int compare(String o1, String o2) {
  24. byte b1[] = bytesOf(o1);
  25. byte b2[] = bytesOf(o2);
  26. int len = Math.min(b1.length, b2.length);
  27. for (int i = 0; i < len; i++) {
  28. if (b1[i] > b2[i]) {
  29. return 1;
  30. }
  31. if (b1[i] < b2[i]) {
  32. return -1;
  33. }
  34. }
  35. return b1.length - b2.length;
  36. }
  37. };
  38. /**
  39. * Converts a string into the raw byte array representation used to store
  40. * into bencoded data.
  41. *
  42. * @param s
  43. * @return
  44. */
  45. public static byte[] bytesOf(String s) {
  46. try {
  47. return s.getBytes("UTF-8");
  48. } catch (UnsupportedEncodingException e) {
  49. throw new Error(e);
  50. }
  51. }
  52. private BEncoder() {
  53. }
  54. /**
  55. * @param obj
  56. * @return
  57. */
  58. public static byte[] bencode(Object obj) {
  59. Validator.notNull(obj, "Object to encode is null!");
  60. ByteArrayOutputStream out = new ByteArrayOutputStream();
  61. try {
  62. bencode(out, obj);
  63. } catch (IOException e) {
  64. throw new RuntimeException(e);
  65. }
  66. return out.toByteArray();
  67. }
  68. /**
  69. * BEncodes data and outputs it to an OutputStream.
  70. *
  71. * @param out
  72. * @param obj
  73. * @throws IOException
  74. */
  75. @SuppressWarnings("unchecked")
  76. public static void bencode(OutputStream out, Object obj) throws IOException {
  77. Validator.notNull(obj, "Object to encode is null!");
  78. if (obj instanceof String) {
  79. bencodeString(out, (String) obj);
  80. } else if (obj instanceof byte[]) {
  81. bencodeString(out, (byte[]) obj);
  82. } else if (obj instanceof Integer) {
  83. bencodeInteger(out, BigInteger.valueOf((Integer) obj));
  84. } else if (obj instanceof Long) {
  85. bencodeInteger(out, BigInteger.valueOf((Long) obj));
  86. } else if (obj instanceof BigInteger) {
  87. bencodeInteger(out, (BigInteger) obj);
  88. } else if (obj instanceof Collection) {
  89. bencodeList(out, (Collection<?>) obj);
  90. } else if (obj instanceof Map) {
  91. bencodeDictionary(out, (Map<String, ?>) obj);
  92. } else {
  93. throw new IllegalArgumentException("Type " + obj.getClass()
  94. + " is not bencodable.");
  95. }
  96. }
  97. private static void bencodeDictionary(OutputStream out, Map<String, ?> dict)
  98. throws IOException {
  99. assert out != null;
  100. assert dict != null;
  101. out.write('d');
  102. List<String> sorted = new ArrayList<String>(dict.keySet());
  103. Collections.sort(sorted, BYTE_COMPARATOR);
  104. for (String key : sorted) {
  105. bencode(out, key);
  106. bencode(out, dict.get(key));
  107. }
  108. out.write('e');
  109. }
  110. private static void bencodeList(OutputStream out, Collection<?> list)
  111. throws IOException {
  112. assert out != null;
  113. assert list != null;
  114. out.write('l');
  115. for (Object child : list) {
  116. bencode(out, child);
  117. }
  118. out.write('e');
  119. }
  120. private static void bencodeInteger(OutputStream out, BigInteger integer)
  121. throws IOException {
  122. assert out != null;
  123. assert integer != null;
  124. out.write('i');
  125. out.write(bytesOf(integer.toString()));
  126. out.write('e');
  127. }
  128. private static void bencodeString(OutputStream out, String string)
  129. throws IOException {
  130. assert out != null;
  131. assert string != null;
  132. byte[] bytes = bytesOf(string);
  133. bencodeString(out, bytes);
  134. }
  135. private static void bencodeString(OutputStream out, byte[] data)
  136. throws IOException {
  137. assert out != null;
  138. assert data != null;
  139. out.write(bytesOf(Integer.toString(data.length)));
  140. out.write(':');
  141. out.write(data);
  142. }
  143. }