Last active
December 20, 2020 22:47
-
-
Save Davenchy/a5f2c2f9931b07f7e83f1bd1a132dba7 to your computer and use it in GitHub Desktop.
BEncode class that encodes and decodes bencoded data
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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