Skip to content

Instantly share code, notes, and snippets.

@j0sh
Created March 14, 2012 18:52
Show Gist options
  • Select an option

  • Save j0sh/2038636 to your computer and use it in GitHub Desktop.

Select an option

Save j0sh/2038636 to your computer and use it in GitHub Desktop.
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2007 Josh Tynjala
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//
////////////////////////////////////////////////////////////////////////////////
package com.joshtynjala.bittorrent
{
/**
* Encodes and decodes data to and from the Bencode format used by BitTorrent.
*/
public class Bencoder
{
/**
* Encodes generic data into the Bencode format used by BitTorrent. If
* the data is of an unsupported ActionScript class, an empty string is returned.
*/
public static function encode(unencodedData:*):String
{
var encodedData:String = "";
if(unencodedData is String)
{
encodedData = encodeString(unencodedData);
}
else if(unencodedData is int)
{
encodedData = encodeInteger(unencodedData);
}
else if(unencodedData is Array)
{
encodedData = encodeList(unencodedData);
}
else if(unencodedData is Object)
{
encodedData = encodeDictionary(unencodedData);
}
return encodedData;
}
/**
* Decodes data that is stored in the Bencode format used by BitTorrent.
*/
public static function decode(encodedData:String):*
{
var result:BencoderResult = decodeNext(encodedData);
return result.item;
}
/**
* @private
*/
private static function decodeNext(bencodedData:String):BencoderResult
{
var type:String = bencodedData.charAt(0);
switch(type)
{
case "d":
return decodeDictionary(bencodedData);
case "l":
return decodeList(bencodedData);
case "i":
return decodeInteger(bencodedData);
default:
return decodeString(bencodedData);
}
}
/**
* @private
* Encodes an ActionScript string to a Bencoded string.
*/
private static function encodeString(data:String):String
{
return data.length + ":" + data;
}
/**
* @private
* Decodes a Bencoded string to an ActionScript string.
*/
private static function decodeString(bencodedData:String):BencoderResult
{
var index:int = bencodedData.indexOf(":");
if(index == -1)
{
return new BencoderResult("", 0);
}
var length:int = int(bencodedData.substr(0, index));
var value:String = bencodedData.substr(index + 1, length);
return new BencoderResult(value, index + length + 1);
}
/**
* @private
* Encodes an ActionScript int to a Bencoded integer.
*/
private static function encodeInteger(data:int):String
{
return "i" + data.toString() + "e";
}
/**
* @private
* Decodes a Bencoded integer to an ActionScript int.
*/
private static function decodeInteger(bencodedData:String):BencoderResult
{
var eIndex:int = bencodedData.indexOf("e");
var value:int = int(bencodedData.substr(1, eIndex - 1));
return new BencoderResult(value, eIndex + 1);
}
/**
* @private
* Encodes am ActionScript Array to a Bencoded list.
*/
private static function encodeList(data:Array):String
{
var encodedList:String = "l";
for(var i:int = 0; i < data.length; i++)
{
encodedList += encode(data[i]);
}
encodedList += "e";
return encodedList;
}
/**
* @private
* Decodes a Bencoded list to an ActionScript Array.
*/
private static function decodeList(bencodedData:String):BencoderResult
{
var decodedList:Array = [];
var index:int = 1;
while(bencodedData.indexOf("e", index) > index)
{
var result:BencoderResult = decodeNext(bencodedData.substr(index));
decodedList.push(result.item);
index += result.index;
}
return new BencoderResult(decodedList, index + 1);
}
/**
* @private
* Encodes an ActionScript Object to a Bencoded dictionary.
*/
private static function encodeDictionary(data:Object):String
{
var encodedDictionary:String = "d";
for(var name:String in data)
{
encodedDictionary += encodeString(name);
encodedDictionary += encode(data[name]);
}
encodedDictionary += "e";
return encodedDictionary;
}
/**
* @private
* Decodes a Bencoded dictionary to an ActionScript Object.
*/
private static function decodeDictionary(bencodedData:String):BencoderResult
{
var decodedDictionary:Object = new Object();
var currentState:String = bencodedData;
var index:int = 1;
while(bencodedData.indexOf("e", index) > index)
{
var nameResult:BencoderResult = decodeString(bencodedData.substr(index));
index += nameResult.index;
var valueResult:BencoderResult = decodeNext(bencodedData.substr(index));
index += valueResult.index;
decodedDictionary[nameResult.item] = valueResult.item;
}
return new BencoderResult(decodedDictionary, index + 1);
}
}
}
class BencoderResult
{
/**
* The decoded object.
*/
public var item:*;
/**
* The new index to use to read from the encoded data.
*/
public var index:int;
/**
* Constructor.
*/
public function BencoderResult(item:* = null, index:int = 0)
{
this.item = item;
this.index = index;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment