PageRenderTime 97ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/core/com/limegroup/bittorrent/bencoding/BEncoder.java

https://github.com/zootella/learning-bittorrent
Java | 156 lines | 50 code | 18 blank | 88 comment | 13 complexity | 654d947159a19ae5a12ebb46cddd8174 MD5 | raw file
  1. // Commented for the Learning branch
  2. package com.limegroup.bittorrent.bencoding;
  3. import java.io.IOException;
  4. import java.io.OutputStream;
  5. import java.util.Iterator;
  6. import java.util.List;
  7. import java.util.Map;
  8. import java.util.SortedMap;
  9. import java.util.TreeMap;
  10. /**
  11. * The BEncoder class has code that can convert Java objects into bencoded data for BitTorrent.
  12. * Call BEncoder.encode(OutputStream, Object) to bencode a given Object and write the bencoded data to the given OutputStream.
  13. *
  14. * BitTorrent uses a simple and extensible data format called bencoding.
  15. * More information on bencoding is on the Web at:
  16. * http://en.wikipedia.org/wiki/Bencoding
  17. * http://www.bittorrent.org/protocol.html in the section titled "The connectivity is as follows".
  18. *
  19. * Bencoded data is composed of strings, numbers, lists, and dictionaries.
  20. * Strings are prefixed by their length, like "5:hello".
  21. * Numbers are written as text numerals between the letters "i" and "e", like "i87e".
  22. * You can list any number of bencoded pieces of data between "l" for list and "e" for end.
  23. * A dictionary is a list of key and value pairs between "d" and "e".
  24. * The keys have to be strings, and they have to be in alphabetical order.
  25. */
  26. public class BEncoder {
  27. /**
  28. * The BEncoder() constructor is marked private to prevent anyone from making a BEncoder object.
  29. * Just use the public static methods like encodeString() instead.
  30. */
  31. private BEncoder() {}
  32. /**
  33. * Bencode the given byte array to the given OutputStream.
  34. *
  35. * Writes the length, a colon, and then the text.
  36. * For example, the string "hello" becomes the bencoded bytes "5:hello".
  37. *
  38. * @param output An OutputStream for this method to write bencoded data to
  39. * @param b The byte array to bencode and write
  40. */
  41. public static void encodeByteArray(OutputStream output, byte[] b) throws IOException {
  42. // Write the length, a colon, and the byte array, like "5:hello"
  43. String length = String.valueOf(b.length); // Find out how long the data is, and convert that number into a String
  44. output.write(length.getBytes(Token.ASCII)); // Write the text to the given OutputStream
  45. output.write(BEString.COLON); // Write the ":"
  46. output.write(b); // Write the data
  47. }
  48. /**
  49. * Bencode the given number to the given OutputStream.
  50. *
  51. * Writes the base-10 numerals of the number between the letters "i" and "e".
  52. * For example, the if n is 87 encodeInt() will write "i87e" to the OutputStream.
  53. *
  54. * @param output An OutputStream for this method to write bencoded data to
  55. * @param n The number to bencode and write
  56. */
  57. public static void encodeInt(OutputStream output, Number n) throws IOException {
  58. // Write the number between an "i" and "e", like "i87e"
  59. String numerals = String.valueOf(n.longValue()); // Convert the number into a String
  60. output.write(Token.I); // Write the "i"
  61. output.write(numerals.getBytes(Token.ASCII)); // Write the numerals
  62. output.write(Token.E); // Write the "e"
  63. }
  64. /**
  65. * Bencodes the given List to the given OutputStream.
  66. * Pass any Java object that extends java.util.List for the list parameter.
  67. *
  68. * Writes "l" for list, the bencoded-form of each of the given objects, and then "e" for end.
  69. *
  70. * @param output An OutputStream for this method to write bencoded data to
  71. * @param list A Java List object to bencode and write
  72. */
  73. public static void encodeList(OutputStream output, List list) throws IOException {
  74. // Write "l", the bencoded form of each element in the List, and "e"
  75. output.write(Token.L);
  76. for (Iterator iter = list.iterator(); iter.hasNext(); ) encode(output, iter.next()); // Loop for each object, calling encode() on each one
  77. output.write(Token.E);
  78. }
  79. /**
  80. * Bencodes the given Map to the given OutputStream.
  81. * Pass any Java object that extends java.util.Map for the map parameter.
  82. *
  83. * Writes a bencoded dictionary, which is a list of keys and values which looks like this:
  84. *
  85. * d
  86. * 5:color 5:green
  87. * 6:flavor 4:lime
  88. * 5:shape 5:round
  89. * e
  90. *
  91. * The bencoded data starts "d" for dictionary and ends "e" for end.
  92. * In the middle are pairs of bencoded values.
  93. * The keys have to be strings, while the values can be strings, numbers, lists, or more dictionaries.
  94. * The keys have to be in alphabetical order.
  95. *
  96. * @param o An OutputStream for this method to write bencoded data to
  97. * @param map The Java Map object to bencode and write
  98. */
  99. public static void encodeDict(OutputStream output, Map map) throws IOException {
  100. // The BitTorrent specification requires that dictionary keys are sorted in alphanumeric order
  101. SortedMap sorted = new TreeMap(); // A Java TreeMap will automatically sort the items we add to it
  102. for (Iterator iter = map.keySet().iterator(); iter.hasNext(); ) { // Loop for each key and value pair in the given Map
  103. Object key = iter.next();
  104. sorted.put(key.toString(), map.get(key)); // Copy it into the TreeMap, which will put it in sorted order
  105. }
  106. // Write "d", each bencoded key and value, and then "e"
  107. output.write(Token.D);
  108. for (Iterator iter = sorted.keySet().iterator(); iter.hasNext(); ) {
  109. String key = (String)iter.next();
  110. // Write the key, a bencoded string like "4:key1"
  111. encodeByteArray(output, key.getBytes(Token.ASCII));
  112. // Write the value, which can be any kind of bencoded data
  113. encode(output, sorted.get(key));
  114. }
  115. output.write(Token.E);
  116. }
  117. /**
  118. * Describes a given object using bencoding, and writes the bencoded data to the given stream.
  119. *
  120. * @param output An OutputStream for this method to write bencoded data to.
  121. * @param object The Java Object to bencode and write.
  122. * To write a bencoded dictionary, pass a Map object.
  123. * To write a bencoded list, pass a List object.
  124. * To write a bencoded number, pass a Number object.
  125. * To write a bencoded string, pass a String or just a byte array.
  126. * @throws IOException If there was a problem reading from the OutputStream.
  127. * IllegalArgumentException If you pass an object that isn't a Map, List, Number, String, or byte array.
  128. */
  129. private static void encode(OutputStream output, Object object) throws IOException {
  130. // Sort the object by its type, having the matching method bencode it and write it
  131. if (object instanceof Map) encodeDict(output, (Map)object);
  132. else if (object instanceof List) encodeList(output, (List)object);
  133. else if (object instanceof Number) encodeInt(output, (Number)object);
  134. else if (object instanceof String) encodeByteArray(output, ((String)object).getBytes(Token.ASCII));
  135. else if (object instanceof byte[]) encodeByteArray(output, (byte[])object);
  136. else throw new IllegalArgumentException();
  137. }
  138. }