<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
	<title type="html"><![CDATA[Серый форум &mdash; JavaScript: Bencode - формат хранения и передачи данных BitTorrent]]></title>
	<link rel="self" href="https://forum.script-coding.com/extern.php?action=feed&amp;tid=7602&amp;type=atom" />
	<updated>2012-09-20T19:29:55Z</updated>
	<generator>PunBB</generator>
	<id>https://forum.script-coding.com/viewtopic.php?id=7602</id>
		<entry>
			<title type="html"><![CDATA[JavaScript: Bencode - формат хранения и передачи данных BitTorrent]]></title>
			<link rel="alternate" href="https://forum.script-coding.com/viewtopic.php?pid=64045#p64045" />
			<content type="html"><![CDATA[<p>на досуге написал небольшой скрипт разбора и упаковки данных в Bencode - <a href="http://ru.wikipedia.org/wiki/Bencode">формат, используемый p2p системой распространения файлов BitTorrent для хранения и передачи свободно структурированных данных</a>. Когда начинал писать были какие-то мысли на счет его использования. Однако со временем актуальность потерялась, а код остался. Некоторое время спустя возникло желание завершить написанное и поделиться с общественностью. Возможно кому-нибудь пригодится. Насколько помню, проблема упирается в корректную трансляцию порции бинарных данных. Полностью решить не удалось. <br />.<br /><strong>Ссылки</strong><br />Подробно о формате данных можно почитать по следующим ссылкам:<br /><a href="http://ru.wikipedia.org/wiki/Bencode">http://ru.wikipedia.org/wiki/Bencode</a> - русская википедия<br /><a href="http://en.wikipedia.org/wiki/Bencode">http://en.wikipedia.org/wiki/Bencode</a> - английская википедия<br /><a href="http://wiki.theory.org/BitTorrentSpecification">http://wiki.theory.org/BitTorrentSpecification</a> - спецификация от разработчиков</p><p>На этих страницах можно найти и другие реализации с использованием различных языков программирования (также и JScript/JavaScript). <br />.<br /><strong>Описание библиотеки</strong><br /><em>Bencode.stringify(value)</em><br />Конвертирует объект в строку в формате bencode. </p><p><em>Bencode.parse(value)</em><br />Разбирает bencode строку в объект JavaScript. В случае некорректно сформированной строки бросает исключение RangeError. Это самая основная функция для разбора данных торрент-файла. </p><p><em>Bencode.PATH_DELIMITER = &#039;\\&#039;;</em><br />Разделитель путей, по умолчанию. Это значение может быть изменено до вызова Bencode.torrentInfo(). </p><p><em>Bencode.torrentInfo(value, key)</em><br />Рассматривает входной объект как торрент-объект и возвращает информацию, заданную параметром key. Если входное значение - строка, то она первоначально будет разобрана в торрент-объект. </p><p>Есть несколько специально заданных ключей для упрощения доступа к данных:<br />&#039;name&#039;<br />название торрента</p><p>&#039;piece length&#039;<br />количество байт в каждом фрагменте</p><p>&#039;pieces&#039;<br />строка, состоящая из 20-ибайтых SHA1-кодированных хешей. До сих пор эта информация у меня не вызывает доверия в силу того, что JScript не умеет корректно работать с байтовыми строками без привязки к кодировке. Возможно в NodeJS этих проблем не будет. Я не проверял. </p><p>&#039;creation date&#039;<br />дата создания торрент-файла. Эта информация конвертируется в формат типа Date. </p><p>&#039;announce-list&#039;<br />упрощенный список (массив) анонсов (в отличие от используемого для внутренних целей списка списков строк)</p><p>&#039;file-names&#039;<br />&#039;file-sizes&#039;<br />всегда (даже если файл один) список имен и список размеров всех файлов в торренте<br />.<br /><strong>Исходный код</strong><br />Полная и актуальная версия файла находится по адресу <a href="http://code.google.com/p/jsxt/source/browse/trunk/js/Bencode.js">http://code.google.com/p/jsxt/source/br … Bencode.js</a><br /></p><div class="codebox"><pre><code>
var Bencode = Bencode || {};

(function()
{

var toString = Object.prototype.toString;

var stringify = function(value)
{
	var typeOf = toString.call(value);

	if ( typeOf == &#039;[object Number]&#039; ) {
		return &#039;i&#039; + parseInt(value) + &#039;e&#039;;
	}
	if ( typeOf == &#039;[object String]&#039; ) {
		return value.length + &#039;:&#039; + value.toString();
	}
	if ( typeOf == &#039;[object Date]&#039; ) {
		return &#039;i&#039; + Math.floor(value.getTime() / 1000) + &#039;e&#039;;
	}

	var result = [];

	if ( typeOf == &#039;[object Array]&#039; ) {
		for (var i = 0; i &lt; value.length; i++) {
			result.push(stringify(value[i]));
		}
		return &#039;l&#039; + result.join(&#039;&#039;) + &#039;e&#039;;
	}

	for (var p in value) {
		if ( ! value.hasOwnProperty(p) ) {
			continue;
		}
		result.push(stringify(String(p)) + stringify(value[p]));
	}
	return &#039;d&#039; + result.join(&#039;&#039;) + &#039;e&#039;;
};


var i = 0;
var text;

var parser = function()
{
	var c = text.charAt(i);

	var err;

	switch (c) {
	case &#039;i&#039;:
		err = &#039;integer&#039;;

		var matches = text.slice(i).match(/^i(-?\d+)e/);
		if ( ! matches ) {
			break;
		}

		i += matches.lastIndex;
		return Number(matches[1]);

	case &#039;l&#039;:
		err = &#039;list&#039;;

		i++;

		var result = [];
		while ( i &lt; text.length &amp;&amp; text.charAt(i) != &#039;e&#039; ) {
			result.push(parser());
		}

		if ( text.charAt(i) != &#039;e&#039; ) {
			break;
		}

		i++;
		return result;

	case &#039;d&#039;:
		err = &#039;dictionary&#039;;

		i++;

		var result = {};
		while ( i &lt; text.length &amp;&amp; text.charAt(i) != &#039;e&#039; ) {
			var k = parser();
			var v = parser();
			result[k] = v;
		}

		if ( text.charAt(i) != &#039;e&#039; ) {
			break;
		}

		i++;
		return result;

	default:
		err = &#039;string&#039;;

		var matches = text.slice(i).match(/^(\d+):/);
		if ( ! matches ) {
			break;
		}

		var len = Number(matches[1]);
		var a = i + matches.lastIndex;
		var b = a + len;

		var result = text.slice(a, b);
		if ( result.length != len ) {
			break;
		}

		i = b;
		return result;
	}

	// Here is abnormal ending
	throw new RangeError(&#039;Bencode.parse: Illegal &#039; + err + &#039; at &#039; + i + &#039; (0x&#039; + i.toString(16).toUpperCase() + &#039;)&#039;);
};


var torrentInfo = function(value, key)
{
	switch ( key ) {
	case &#039;name&#039;: 
	case &#039;piece length&#039;: 
	case &#039;pieces&#039;: 
		return value.info[key];
	case &#039;creation date&#039;: 
		// convert date to the standard Date object
		return new Date(value[key] * 1000);
	case &#039;announce-list&#039;: 
		var input = value[&#039;announce-list&#039;];
		var result = [];
		for (var i = 0; i &lt; input.length; i++) {
			// convert a list of lists of strings to 
			// the simple list of strings
			result.push.apply(result, input[i]);
		}
		return result;
	case &#039;file-names&#039;:
		var input = value.info.files;
		if ( ! input ) {
			// torrent contains the single file
			return [value.info.name];
		}
		var result = [];
		for (var i = 0; i &lt; input.length; i++) {
			result.push(input[i].path.join(Bencode.PATH_DELIMITER));
		}
		return result;
	case &#039;file-sizes&#039;: 
		var input = value.info.files;
		if ( ! input ) {
			// torrent contains the single file
			return [value.info.length];
		}
		var result = [];
		for (var i = 0; i &lt; input.length; i++) {
			result.push(input[i].length);
		}
		return result;
	default:
		return value[key];
	}
};

Bencode.PATH_DELIMITER = &#039;\\&#039;;

Bencode.stringify = stringify;

Bencode.parse = function(value)
{
	i = 0;
	text = (value || &#039;&#039;).toString();
	return parser();
};

Bencode.torrentInfo = function(value, key)
{
	if ( toString.call(value) == &#039;[object String]&#039; ) {
		value = Bencode.parse(value);
	}

	if ( ! value || ! value.info ) {
		return;
	}

	return torrentInfo(value, key);
};

})();</code></pre></div><p>.<br /><strong>Пример использования</strong><br />В качестве функции чтения торрент-файла использовалось стороннее решение <a href="http://demon.tw/my-work/javascript-bencode.html">http://demon.tw/my-work/javascript-bencode.html</a>. Насколько я разобрался, это самый корректный из известных мне способов прочитать бинарные данные в JScript. <br /></p><div class="codebox"><pre><code>
// http://demon.tw/my-work/javascript-bencode.html
function read(path) {
    var cp1252Chars = [/\u20AC/g,/\u201A/g,/\u0192/g,/\u201E/g,/\u2026/g,/\u2020/g,/\u2021/g,/\u02C6/g,/\u2030/g,/\u0160/g,/\u2039/g,/\u0152/g,/\u017D/g,/\u2018/g,/\u2019/g,/\u201C/g,/\u201D/g,/\u2022/g,/\u2013/g,/\u2014/g,/\u02DC/g,/\u2122/g,/\u0161/g,/\u203A/g,/\u0153/g,/\u017E/g,/\u0178/g];
    var latin1Chars = [&quot;\u0080&quot;,&quot;\u0082&quot;,&quot;\u0083&quot;,&quot;\u0084&quot;,&quot;\u0085&quot;,&quot;\u0086&quot;,&quot;\u0087&quot;,&quot;\u0088&quot;,&quot;\u0089&quot;,&quot;\u008A&quot;,&quot;\u008B&quot;,&quot;\u008C&quot;,&quot;\u008E&quot;,&quot;\u0091&quot;,&quot;\u0092&quot;,&quot;\u0093&quot;,&quot;\u0094&quot;,&quot;\u0095&quot;,&quot;\u0096&quot;,&quot;\u0097&quot;,&quot;\u0098&quot;,&quot;\u0099&quot;,&quot;\u009A&quot;,&quot;\u009B&quot;,&quot;\u009C&quot;,&quot;\u009E&quot;,&quot;\u009F&quot;];
    var binstream = new ActiveXObject(&quot;ADODB.Stream&quot;);
    binstream.Type = 2;
    binstream.Charset = &quot;iso-8859-1&quot;;
    binstream.Open();
    binstream.LoadFromFile(path);
    var s = binstream.ReadText();
    for (var i = 0; i &lt; 27; i++)
        s = s.replace(cp1252Chars[i], latin1Chars[i]);
    return s;
}

var filename = &#039;foo.torrent&#039;;

var text = read(filename);
var torrent = Bencode.parse(text);

WScript.Echo( Bencode.torrentInfo(torrent, &#039;name&#039;) );
WScript.Echo( Bencode.torrentInfo(torrent, &#039;creation date&#039;) );
WScript.Echo( Bencode.torrentInfo(torrent, &#039;file-names&#039;) );</code></pre></div>]]></content>
			<author>
				<name><![CDATA[Rumata]]></name>
				<uri>https://forum.script-coding.com/profile.php?id=24846</uri>
			</author>
			<updated>2012-09-20T19:29:55Z</updated>
			<id>https://forum.script-coding.com/viewtopic.php?pid=64045#p64045</id>
		</entry>
</feed>
