Skip to content

Instantly share code, notes, and snippets.

@Davenchy
Last active December 20, 2020 22:47
Show Gist options
  • Save Davenchy/a5f2c2f9931b07f7e83f1bd1a132dba7 to your computer and use it in GitHub Desktop.
Save Davenchy/a5f2c2f9931b07f7e83f1bd1a132dba7 to your computer and use it in GitHub Desktop.
BEncode class that encodes and decodes bencoded data
import 'dart:developer';
class BEncode {
static const int ssp = 58;
static const int esp = 101;
static const int isp = 105;
static const int dsp = 100;
static const int lsp = 108;
BEncode._();
static List<int> encode(dynamic item) {
if (item is String)
return [...item.length.toString().codeUnits, ssp, ...item.codeUnits];
else if (item is int)
return [isp, ...item.toString().codeUnits, esp];
else if (item is List)
return [lsp, for (var val in item) ...encode(val), esp];
else if (item is Map<String, dynamic>)
return [
dsp,
...[
for (var val in item.entries) ...[
...encode(val.key),
...encode(val.value)
]
],
esp
];
else
throw UnimplementedError(
'value data type (${item.runtimeType}) is not supported');
}
static dynamic decode(List<int> row, [bool first = true]) {
assert(row != null);
final List items = [];
for (int i = 0; i < row.length; i++) {
final int c = row[i];
if (c == lsp) {
final endIndex = _findEnd(row, i);
final List<int> listRow = row.sublist(i + 1, endIndex);
final List listItems = decode(listRow, false);
if (first) return listItems;
items.add(listItems);
i = endIndex;
} else if (c == isp) {
final int endIndex = row.indexOf(esp, i);
final List<int> numberRow = row.sublist(i + 1, endIndex);
final String numberStr = String.fromCharCodes(numberRow);
final int number = int.tryParse(numberStr) ?? null;
if (first) return number;
items.add(number);
i = endIndex;
} else if (c == dsp) {
final endIndex = _findEnd(row, i);
final List<int> dictRow = row.sublist(i + 1, endIndex);
final List dictItems = decode(dictRow, false);
final Map<String, dynamic> dict = {};
for (int a = 0; a < dictItems.length; a++)
dict[dictItems[a]] = dictItems[++a];
if (first) return dict;
items.add(dict);
i = endIndex;
} else if (c >= 48 && c <= 57) {
final endIndex = row.indexOf(ssp, i);
final lenRow = row.sublist(i, endIndex);
final lenStr = String.fromCharCodes(lenRow);
final len = int.tryParse(lenStr) ?? null;
if (len == null)
throw UnimplementedError('failed to pick string length');
final start = i + lenStr.length + 1;
final end = start + len;
final strRow = row.sublist(start, end);
final str = String.fromCharCodes(strRow);
if (first) return str;
items.add(str);
i = endIndex + len;
} else
throw UnimplementedError('unsupported b_decoded row of data');
}
return items;
}
static _findEnd(List<int> row, int index) {
int ends = 0;
int ignore = 0;
const List<int> sps = const [dsp, lsp, isp];
for (index; index < row.length; index++) {
if (ignore > 0) {
ignore--;
continue;
}
int c = row[index];
if (sps.contains(c)) {
ends++;
if (c == isp) {
final endIndex = row.indexOf(esp, index);
index = endIndex - 1;
}
} else if (c == esp)
ends--;
else if (c >= 48 && c <= 57) {
final endIndex = row.indexOf(ssp, index);
final nearestEnd = row.indexOf(esp, index);
if (endIndex > nearestEnd) continue;
final lenRow = row.sublist(index, endIndex);
final lenStr = String.fromCharCodes(lenRow);
final len = int.tryParse(lenStr) ?? null;
if (len == null)
throw UnimplementedError('failed to pick string length');
ignore = len;
index = endIndex;
}
if (ends == 0) return index;
}
return -1;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment