Browse Source

Add bencode2json

master
JustAnotherArchivist 1 year ago
parent
commit
8d267c7f46
1 changed files with 92 additions and 0 deletions
  1. +92
    -0
      bencode2json

+ 92
- 0
bencode2json View File

@@ -0,0 +1,92 @@
#!/usr/bin/env python3
try:
from contextlib import nullcontext
except ImportError:
# Python 3.6 and below
# One-liner because I can.
nullcontext = type("nc", (object,), {"__init__": (lambda self, v: setattr(self, "v", v)), "__enter__": (lambda self: self.v), "__exit__": (lambda self, *args, **kwargs: None)})
import json
import sys


def parse_dict(f):
d = {}
while True:
c = f.read(1)
if c == b'e':
return d
elif b'0' <= c <= b'9':
key = parse_str(f, c)
value = parse_any(f)
d[key] = value
else:
raise ValueError(f'invalid bencode value: {c} instead of expected e or digit')


def parse_list(f):
l = []
while True:
c = f.read(1)
if c == b'e':
return l
l.append(parse_any(f, c))


def _parse_int_bytes(f, stopChar):
# Read until encountering a non-decimal character; if it isn't stopChar, raise a ValueError
v = b''
# Python 3.8+ (walrus)
#while b'0' <= (c := f.read(1)) <= b'9':
# v += c
while True:
c = f.read(1)
if b'0' <= c <= b'9':
v += c
else:
break
if c == stopChar:
return v
raise ValueError(f'invalid bencode value: {c} instead of expected {stopChar}')


def parse_int(f):
return int(_parse_int_bytes(f, b'e'))


def parse_str(f, c):
v = _parse_int_bytes(f, b':')
length = int(c + v)
read = 0
buf = []
while read < length:
d = f.read(length - read)
buf.append(d)
read += len(d)
buf = b''.join(buf)
try:
return buf.decode('utf-8', 'strict')
except UnicodeDecodeError:
return buf.decode('iso-8859-1')


def parse_any(f, c = None):
if c is None:
c = f.read(1)
if c == b'd':
return parse_dict(f)
elif c == b'l':
return parse_list(f)
elif c == b'i':
return parse_int(f)
elif b'0' <= c <= b'9':
return parse_str(f, c)
else:
raise ValueError(f'invalid bencode value: {c} instead of expected d, l, i, or digit')


if sys.argv[1:]:
c = open(sys.argv[1], 'rb')
else:
c = nullcontext(sys.stdin.buffer)
with c as fp:
print(json.dumps(parse_any(fp)))

Loading…
Cancel
Save