Bencode encoder/decoder

Module for encoding/decoding Bencoded data

Versão de: 12/10/2015. Veja: a última versão.

Este script não deve ser instalado diretamente. Este script é uma biblioteca de outros scripts para incluir com o diretório meta // @require https://update.greasyfork.org/scripts/13016/79775/Bencode%20encoderdecoder.js

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

// ==UserScript==
// @name        Bencode encoder/decoder
// @description Module for encoding/decoding Bencoded data
// @version     1.0
// ==/UserScript==

// Copyright: https://github.com/nutbread/t2m

var bencode = (function() {
	"use strict";

	// Encoding functions
	var encode = function (value) {
		// Type
		var t = typeof(value);

		// Number
		if (t == "number") return encode_int(Math.floor(value));
		// String
		if (t == "string") return encode_string(value);
		// Array
		if (Array.isArray(value)) return encode_list(value);
		// Dict
		return encode_dict(value);
	};

	var encode_int = function (value) {
		return "i" + value + "e";
	};
	var encode_string = function (value) {
		return "" + value.length + ":" + value;
	};
	var encode_list = function (value) {
		var str = [ "l" ],
			i;

		// List values
		for (i = 0; i < value.length; ++i) {
			str.push(encode(value[i]));
		}

		// End
		str.push("e");
		return str.join("");
	};
	var encode_dict = function (value) {
		var str = [ "d" ],
			keys = [],
			i;

		// Get and sort keys
		for (i in value) keys.push(i);
		keys.sort();

		// Push values
		for (i = 0; i < keys.length; ++i) {
			str.push(encode_string(keys[i]));
			str.push(encode(value[keys[i]]));
		}

		// End
		str.push("e");
		return str.join("");
	};



	// Decoding class
	var Decoder = function () {
		this.pos = 0;
	};

	Decoder.prototype = {
		constructor: Decoder,

		decode: function (str) {
			// Errors
			var k = str[this.pos];
			if (!(k in decode_generic)) throw "Invalid format";

			// Call
			return decode_generic[k].call(this, str);
		},
		decode_int: function (str) {
			// Skip the "i" prefix
			++this.pos;

			var end = str.indexOf("e", this.pos),
				value;

			// No end
			if (end < 0) throw "Invalid format";

			// Assume proper number format
			value = parseInt(str.substr(this.pos, end - this.pos), 10);

			// Done
			this.pos = end + 1;
			return value;
		},
		decode_string: function (str) {
			var delim = str.indexOf(":", this.pos),
				length, value;

			// No end
			if (delim < 0) throw "Invalid format";

			// Assume proper number format
			length = parseInt(str.substr(this.pos, delim - this.pos), 10);
			value = str.substr(delim + 1, length);

			// Done
			this.pos = delim + length + 1;
			return value;
		},
		decode_list: function (str) {
			// Skip the "l" prefix
			++this.pos;

			// Read list
			var list = [],
				value;

			// Loop until end or exception
			while (str[this.pos] != "e") {
				value = this.decode(str); // this throws errors if str[this.pos] is out of bounds
				list.push(value);
			}

			// Done; skip "e" suffix
			++this.pos;
			return list;
		},
		decode_dict: function (str) {
			// Skip the "d" prefix
			++this.pos;

			// Read dict
			var dict = {},
				key, value;

			// Loop until end or exception
			while (str[this.pos] != "e") {
				key = this.decode_string(str);
				value = this.decode(str); // this throws errors if str[this.pos] is out of bounds
				dict[key] = value;
			}

			// Done; skip "e" suffix
			++this.pos;
			return dict;
		}
	};

	// Generic decode functions
	var decode_generic = {
			"l": Decoder.prototype.decode_list,
			"d": Decoder.prototype.decode_dict,
			"i": Decoder.prototype.decode_int,
		},
		i;
	for (i = 0; i < 10; ++i) decode_generic[i.toString()] = Decoder.prototype.decode_string;



	// Encode/decode functions
	return {
		/**
			encode: function (obj)
			Encodes an object into a Bencode'd string
			@param obj
				The object to encode
				This should only be one of the following:
					string
					number (floats are floor'd to integers)
					array (containing things only from this list)
					object (containing things only from this list)
				Strings should be encoded in some way such that each character is in the range [0,255]
			@return
				A string representing the object
		*/
		encode: encode,
		/**
			decode: function (str)
			Decodes a Bencode'd string back into its original type
			@param str
				The string to decode
			@return
				The original object represented by str
			@throws
				Any one of the following self-explanatory strings
				"Invalid format"
				"Invalid string"
				"Invalid int"
		*/
		decode: function (str) {
			// Create a decoder and call
			return (new Decoder()).decode(str);
		},
	};

})();