package kan;
/*
* BeDecoder.java
*
* Created on May 30, 2003, 2:44 PM
* Copyright (C) 2003, 2004, 2005, 2006 Aelitis, All Rights Reserved.
*
* 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
* of the License, 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.
*
* AELITIS, SAS au capital de 46,603.30 euros
* 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
*/
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* A set of utility methods to decode a bencoded array of byte into a Map.
* integer are represented as Long, String as byte[], dictionnaries as Map, and
* list as List.
*
* @author TdC_VgA
*
*/
@SuppressWarnings(value={"rawtypes", "unchecked", "unused"})
public class BDecoder {
public static Charset BYTE_CHARSET = Charset.forName("UTF-8");;
public static Charset DEFAULT_CHARSET = Charset.forName("UTF-8");;
private boolean recovery_mode;
public static Map decode(byte[] data) throws IOException {
return (new BDecoder().decodeByteArray(data));
}
public static Map decode(BufferedInputStream is) throws IOException {
return (new BDecoder().decodeStream(is));
}
public BDecoder() { }
public Map decodeByteArray(byte[] data) throws IOException {
return (decode(new ByteArrayInputStream(data)));
}
public Map decodeStream(BufferedInputStream data) throws IOException {
Object res = decodeInputStream(data, 0);
if (res == null) {
throw (new IOException("BDecoder: zero length file"));
} else if (!(res instanceof Map)) {
throw (new IOException("BDecoder: top level isn't a Map"));
}
return ((Map) res);
}
private Map decode(ByteArrayInputStream data) throws IOException {
Object res = decodeInputStream(data, 0);
if (res == null) {
throw (new IOException("BDecoder: zero length file"));
} else if (!(res instanceof Map)) {
throw (new IOException("BDecoder: top level isn't a Map"));
}
return ((Map) res);
}
private Object decodeInputStream(InputStream bais, int nesting)
throws IOException {
if (nesting == 0 && !bais.markSupported()) {
throw new IOException("InputStream must support the mark() method");
}
// set a mark
bais.mark(Integer.MAX_VALUE);
// read a byte
int tempByte = bais.read();
// decide what to do
switch (tempByte) {
case 'd':
// create a new dictionary object
Map tempMap = new HashMap();
try {
// get the key
byte[] tempByteArray = null;
while ((tempByteArray =
(byte[]) decodeInputStream(bais,nesting + 1)) != null) {
// decode some more
Object value = decodeInputStream(bais, nesting + 1);
// add the value to the map
CharBuffer cb =
BYTE_CHARSET.decode(ByteBuffer.wrap(tempByteArray));
String key = new String(cb.array(), 0, cb.limit());
tempMap.put(key, value);
}
bais.mark(Integer.MAX_VALUE);
tempByte = bais.read();
bais.reset();
if (nesting > 0 && tempByte == -1) {
throw (new IOException(
"BDecoder: invalid input data, 'e' missing from end of dictionary"));
}
} catch (Throwable e) {
if (!recovery_mode) {
if (e instanceof IOException) {
throw ((IOException) e);
}
throw (new IOException(e.toString()));
}
}
// return the map
return tempMap;
case 'l':
// create the list
List tempList = new ArrayList();
try {
// create the key
Object tempElement = null;
while ((tempElement = decodeInputStream(bais, nesting + 1)) != null) {
// add the element
tempList.add(tempElement);
}
bais.mark(Integer.MAX_VALUE);
tempByte = bais.read();
bais.reset();
if (nesting > 0 && tempByte == -1) {
throw (new IOException(
"BDecoder: invalid input data, 'e' missing from end of list"));
}
} catch (Throwable e) {
if (!recovery_mode) {
if (e instanceof IOException) {
throw ((IOException) e);
}
throw (new IOException(e.toString()));
}
}
// return the list
return tempList;
case 'e':
case -1:
return null;
case 'i':
return new Long(getNumberFromStream(bais, 'e'));
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
// move back one
bais.reset();
// get the string
return getByteArrayFromStream(bais);
default: {
int rem_len = bais.available();
if (rem_len > 256) {
rem_len = 256;
}
byte[] rem_data = new byte[rem_len];
bais.read(rem_data);
throw (new IOException("BDecoder: unknown command '" + tempByte
+ ", remainder = " + new String(rem_data, DEFAULT_CHARSET)));
}
}
}
private long getNumberFromStream(
InputStream bais, char parseChar)throws IOException {
StringBuffer sb = new StringBuffer(3);
int tempByte = bais.read();
while ((tempByte != parseChar) && (tempByte >= 0)) {
sb.append((char) tempByte);
tempByte = bais.read();
}
// are we at the end of the stream?
if (tempByte < 0) {
return -1;
}
return Long.parseLong(sb.toString());
}
// This one causes lots of "Query Information" calls to the filesystem
private long getNumberFromStreamOld(
InputStream bais, char parseChar) throws IOException {
int length = 0;
// place a mark
bais.mark(Integer.MAX_VALUE);
int tempByte = bais.read();
while ((tempByte != parseChar) && (tempByte >= 0)) {
tempByte = bais.read();
length++;
}
// are we at the end of the stream?
if (tempByte < 0) {
return -1;
}
// reset the mark
bais.reset();
// get the length
byte[] tempArray = new byte[length];
int count = 0;
int len = 0;
// get the string
while (count != length
&& (len = bais.read(tempArray, count, length - count)) > 0) {
count += len;
}
// jump ahead in the stream to compensate for the :
bais.skip(1);
// return the value
CharBuffer cb =
DEFAULT_CHARSET.decode(ByteBuffer.wrap(tempArray));
String str_value = new String(cb.array(), 0, cb.limit());
return Long.parseLong(str_value);
}
private byte[] getByteArrayFromStream(
InputStream bais) throws IOException {
int length = (int) getNumberFromStream(bais, ':');
if (length < 0) {
return null;
}
// note that torrent hashes can be big (consider a 55GB file with 2MB
// pieces
// this generates a pieces hash of 1/2 meg
if (length > 8 * 1024 * 1024) {
throw new IOException("Byte array length too large (" + length + ")");
}
byte[] tempArray = new byte[length];
int count = 0;
int len = 0;
// get the string
while (count != length
&& (len = bais.read(tempArray, count, length - count)) > 0) {
count += len;
}
if (count != tempArray.length) {
throw (new IOException(
"BDecoder::getByteArrayFromStream: truncated"));
}
return tempArray;
}
public void setRecoveryMode(boolean r) {
recovery_mode = r;
}
private void print(PrintWriter writer, Object obj) {
print(writer, obj, "", false);
}
private void print(
PrintWriter writer, Object obj, String indent, boolean skip_indent) {
String use_indent = skip_indent ? "" : indent;
if (obj instanceof Long) {
writer.println(use_indent + obj);
} else if (obj instanceof byte[]) {
byte[] b = (byte[]) obj;
if (b.length == 20) {
writer.println(use_indent + " { " + b + " }");
} else if (b.length < 64) {
writer.println(new String(b, DEFAULT_CHARSET));
} else {
writer.println("[byte array length " + b.length);
}
} else if (obj instanceof String) {
writer.println(use_indent + obj);
} else if (obj instanceof List) {
List l = (List) obj;
writer.println(use_indent + "[");
for (int i = 0; i < l.size(); i++) {
writer.print(indent + " (" + i + ") ");
print(writer, l.get(i), indent + " ", true);
}
writer.println(indent + "]");
} else {
Map m = (Map) obj;
Iterator it = m.keySet().iterator();
while (it.hasNext()) {
String key = (String) it.next();
if (key.length() > 256) {
writer.print(indent + key.substring(0, 256) + "... = ");
} else {
writer.print(indent + key + " = ");
}
print(writer, m.get(key), indent + " ", true);
}
}
}
private static void print(File f, File output) {
try {
BDecoder decoder = new BDecoder();
decoder.setRecoveryMode(false);
PrintWriter pw = new PrintWriter(new FileWriter(output));
decoder.print(pw,
decoder.decodeStream(
new BufferedInputStream(new FileInputStream(f))));
pw.flush();
} catch (Throwable e) {
}
}
public static void main(String[] args) {
// print(new File(
// "C:\\Temp\\8565658FA6C187A602A5360A69F11933624DD9B5.dat.bak"),
// new File("C:\\Temp\\bdecoder.log"));
//print(new File("D:/Users/Desktop/WorkSpace/torrent/1.torrent"), new File("D:/Users/kanlianhui689/Desktop/WorkSpace/torrent/1.text"));
print(new File("D:/Users/Desktop/WorkSpace/torrent/1.torrent"), new File("D:/Users/kanlianhui689/Desktop/WorkSpace/torrent/1.text"));
}
}
转载于:https://my.oschina.net/kanlianhui/blog/168397