seria/src/benben.php
2022-07-03 23:44:11 +00:00

147 lines
4.2 KiB
PHP

<?php
interface BEncodeSerializable {
function bencodeSerialize(): mixed;
}
// todo: if $depth < 1 return unencoded, or just get mad i dunno yet
function bdecode(mixed $input, bool $dictAsObject = false, int $depth = 512): mixed {
if(is_string($input)) {
$file = fopen('php://temp', 'rb+');
fwrite($file, $input);
fseek($file, 0, SEEK_SET);
$output = bdecode($file, $dictAsObject, $depth);
fclose($file);
return $output;
} elseif(!is_resource($input))
return null;
$char = fgetc($input);
if($char === false)
die('bdecode: unexpected eof');
switch($char) {
case 'i':
$number = '';
for(;;) {
$char = fgetc($input);
if($char === false)
die('bdecode: integer unexpected eof');
if($char === 'e')
break;
if($char === '-' && $number !== '')
die('bdecode: integer unexpected minus');
if($char === '0' && $number === '-')
die('bdecode: integer negative zero');
if($char === '0' && $number === '0')
die('bdecode: integer double zero');
if(!ctype_digit($char))
die('bdecode: integer unexpected char');
$number .= $char;
}
return intval($number);
case 'l':
$list = [];
for(;;) {
$char = fgetc($input);
if($char === false)
die('bdecode: list unexpected eof');
if($char === 'e')
break;
fseek($input, -1, SEEK_CUR);
$list[] = bdecode($input, $dictAsObject, $depth - 1);
}
return $list;
case 'd':
$dict = [];
for(;;) {
$char = fgetc($input);
if($char === false)
die('bdecode: dict unexpected eof');
if($char === 'e')
break;
if(!ctype_digit($char))
die('bdecode: dict expecting string');
fseek($input, -1, SEEK_CUR);
$dict[bdecode($input)] = bdecode($input, $dictAsObject, $depth - 1);
}
if($dictAsObject)
$dict = (object)$dict;
return $dict;
default:
if(!ctype_digit($char))
die('bdecode: unexpected char');
$length = $char;
for(;;) {
$char = fgetc($input);
if($char === false)
die('bdecode: string unexpected eof');
if($char === ':')
break;
if($char === '0' && $length === '0')
die('bdecode: string double zero');
if(!ctype_digit($char))
die('bdecode: string unexpected char');
$length .= $char;
}
return fread($input, intval($length));
}
return null;
}
function bencode(mixed $input): string {
switch(gettype($input)) {
case 'string':
return sprintf('%d:%s', strlen($input), $input);
case 'integer':
return sprintf('i%de', $input);
case 'array':
if(array_is_list($input)) {
$output = 'l';
foreach($input as $item)
$output .= bencode($item);
} else {
$output = 'd';
foreach($input as $key => $value) {
$output .= bencode(strval($key));
$output .= bencode($value);
}
}
return $output . 'e';
case 'object':
if($input instanceof BEncodeSerializable)
return bencode($input->bencodeSerialize());
$input = get_object_vars($input);
$output = 'd';
foreach($input as $key => $value) {
$output .= bencode(strval($key));
$output .= bencode($value);
}
return $output . 'e';
default:
return '';
}
}