zwevendekommagetallen
This commit is contained in:
parent
312ffc8fac
commit
a76a22f990
3 changed files with 121 additions and 21 deletions
|
@ -8,12 +8,14 @@ use DateTimeImmutable;
|
||||||
use DateTimeZone;
|
use DateTimeZone;
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
|
|
||||||
|
// TODO: IMPLEMENT TYPE_TIMESPAN
|
||||||
|
|
||||||
class FWIF {
|
class FWIF {
|
||||||
public const CONTENT_TYPE = 'text/plain; charset=us-ascii'; // TODO: come up with a mime type
|
public const CONTENT_TYPE = 'text/plain; charset=us-ascii'; // TODO: come up with a mime type
|
||||||
|
|
||||||
public const DEFAULT = 0;
|
public const DEFAULT = 0; // Default behaviour
|
||||||
public const DISCARD_MILLISECONDS = 0x01; // Always exclude the millisecond component from DateTime
|
public const DISCARD_MILLISECONDS = 0x01; // Always exclude the millisecond component from DateTime
|
||||||
public const EXCLUDE_VERSION = 0x02; // Exclude version byte at the start of the stream
|
public const EXCLUDE_VERSION = 0x02; // Exclude version byte at the start of the stream
|
||||||
|
|
||||||
public const TYPE_NULL = 0; // NULL, no data
|
public const TYPE_NULL = 0; // NULL, no data
|
||||||
public const TYPE_INTEGER = 0x01; // LEB128, implicit length
|
public const TYPE_INTEGER = 0x01; // LEB128, implicit length
|
||||||
|
@ -23,20 +25,22 @@ class FWIF {
|
||||||
public const TYPE_ARRAY = 0x05; // List of values, terminated with TRAILER
|
public const TYPE_ARRAY = 0x05; // List of values, terminated with TRAILER
|
||||||
public const TYPE_OBJECT = 0x06; // List of values with ASCII names, terminated with TRAILER
|
public const TYPE_OBJECT = 0x06; // List of values with ASCII names, terminated with TRAILER
|
||||||
public const TYPE_DATETIME = 0x07; // A gregorian year, month and day as well as an hour, minute, seconds and millisecond component, variable ranging from 4 to 7 bytes
|
public const TYPE_DATETIME = 0x07; // A gregorian year, month and day as well as an hour, minute, seconds and millisecond component, variable ranging from 4 to 7 bytes
|
||||||
|
public const TYPE_TIMESPAN = 0x08; // A period of time, follows the same format DATETIME but uses an additional flag to indicate negative periods
|
||||||
|
|
||||||
public const TRAILER = 0xFF; // Termination byte
|
public const TRAILER = 0xFF; // Termination byte
|
||||||
|
|
||||||
public const VERSION = 0x01; // min 1, max 254
|
public const VERSION = 0x01; // min 1, max 254
|
||||||
|
|
||||||
private const CODECS = [
|
private const CODECS = [
|
||||||
self::TYPE_NULL => 'Null',
|
self::TYPE_NULL => 'Null',
|
||||||
self::TYPE_INTEGER => 'Integer',
|
self::TYPE_INTEGER => 'Integer',
|
||||||
self::TYPE_FLOAT => 'Float',
|
self::TYPE_FLOAT => 'Float',
|
||||||
self::TYPE_STRING => 'String',
|
self::TYPE_STRING => 'String',
|
||||||
self::TYPE_ARRAY => 'Array',
|
self::TYPE_ARRAY => 'Array',
|
||||||
self::TYPE_OBJECT => 'Object',
|
self::TYPE_OBJECT => 'Object',
|
||||||
self::TYPE_BUFFER => 'Buffer',
|
self::TYPE_BUFFER => 'Buffer',
|
||||||
self::TYPE_DATETIME => 'DateTime',
|
self::TYPE_DATETIME => 'DateTime',
|
||||||
|
self::TYPE_TIMESPAN => 'TimeSpan',
|
||||||
];
|
];
|
||||||
|
|
||||||
private const UTF8 = '%^(?:' // https://www.w3.org/International/questions/qa-forms-utf-8.en
|
private const UTF8 = '%^(?:' // https://www.w3.org/International/questions/qa-forms-utf-8.en
|
||||||
|
@ -75,6 +79,8 @@ class FWIF {
|
||||||
if(is_object($data) || self::isAssocArray($data)) {
|
if(is_object($data) || self::isAssocArray($data)) {
|
||||||
if($data instanceof DateTimeInterface)
|
if($data instanceof DateTimeInterface)
|
||||||
return self::TYPE_DATETIME;
|
return self::TYPE_DATETIME;
|
||||||
|
if($data instanceof DateInterval)
|
||||||
|
return self::TYPE_TIMESPAN;
|
||||||
return self::TYPE_OBJECT;
|
return self::TYPE_OBJECT;
|
||||||
}
|
}
|
||||||
if(is_array($data))
|
if(is_array($data))
|
||||||
|
@ -155,12 +161,72 @@ class FWIF {
|
||||||
return $number;
|
return $number;
|
||||||
}
|
}
|
||||||
|
|
||||||
// I still don't like these
|
private const FLOAT_MZ = 0x04;
|
||||||
|
private const FLOAT_EZ = 0x08;
|
||||||
|
private const FLOAT_ZERO = self::FLOAT_MZ | self::FLOAT_EZ;
|
||||||
|
private const FLOAT_NAN = 0x20;
|
||||||
|
private const FLOAT_INF = 0x40;
|
||||||
|
private const FLOAT_SIGN = 0x80;
|
||||||
|
private const FLOAT_NINF = self::FLOAT_INF | self::FLOAT_SIGN;
|
||||||
|
|
||||||
|
private const FLOAT_DIV = 0x7FFFFFFF;
|
||||||
|
|
||||||
private static function encodeFloat(float $number, int $flags): string {
|
private static function encodeFloat(float $number, int $flags): string {
|
||||||
return pack('E', $number);
|
if(is_nan($number))
|
||||||
|
$packed = chr(self::FLOAT_NAN);
|
||||||
|
elseif($number === INF)
|
||||||
|
$packed = chr(self::FLOAT_INF);
|
||||||
|
elseif($number === -INF)
|
||||||
|
$packed = chr(self::FLOAT_NINF);
|
||||||
|
else {
|
||||||
|
$byte1 = 0; $packed = '';
|
||||||
|
|
||||||
|
if($number < 0.0 || ($number ** -1 === -INF))
|
||||||
|
$byte1 |= self::FLOAT_SIGN;
|
||||||
|
|
||||||
|
if($number === 0.0)
|
||||||
|
$byte1 |= self::FLOAT_ZERO;
|
||||||
|
else {
|
||||||
|
$numAbs = abs($number);
|
||||||
|
$exp = (int)(floor(log($numAbs, 2)) + 1);
|
||||||
|
$man = (int)(($numAbs * (2 ** -$exp)) * self::FLOAT_DIV);
|
||||||
|
if($exp != 0)
|
||||||
|
$packed .= self::encodeInteger($exp, $flags);
|
||||||
|
else
|
||||||
|
$byte1 |= self::FLOAT_EZ;
|
||||||
|
if($man > 0)
|
||||||
|
$packed .= pack('N', $man);
|
||||||
|
else
|
||||||
|
$byte1 |= self::FLOAT_MZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
$packed = chr($byte1) . $packed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $packed;
|
||||||
}
|
}
|
||||||
private static function decodeFloat($data, int $flags): float {
|
private static function decodeFloat($data, int $flags): float {
|
||||||
return unpack('E', fread($data, 8))[1];
|
$byte1 = ord(fgetc($data));
|
||||||
|
|
||||||
|
if($byte1 & self::FLOAT_NAN)
|
||||||
|
return NAN;
|
||||||
|
if($byte1 & self::FLOAT_INF)
|
||||||
|
return ($byte1 & self::FLOAT_SIGN) ? -INF : INF;
|
||||||
|
if(($byte1 & self::FLOAT_ZERO) === self::FLOAT_ZERO)
|
||||||
|
return ($byte1 & self::FLOAT_SIGN) ? -0.0 : 0.0;
|
||||||
|
|
||||||
|
$exp = 0; $man = 0;
|
||||||
|
|
||||||
|
if(!($byte1 & self::FLOAT_EZ))
|
||||||
|
$exp = self::decodeInteger($data, $flags);
|
||||||
|
if(!($byte1 & self::FLOAT_MZ))
|
||||||
|
$man = ((float)unpack('N', fread($data, 4))[1]) / self::FLOAT_DIV;
|
||||||
|
|
||||||
|
$number = $man * (2 ** $exp);
|
||||||
|
if($byte1 & self::FLOAT_SIGN)
|
||||||
|
$number *= -1;
|
||||||
|
|
||||||
|
return $number;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function encodeString(string $string, int $flags): string {
|
private static function encodeString(string $string, int $flags): string {
|
||||||
|
@ -238,8 +304,9 @@ class FWIF {
|
||||||
return fread($data, self::decodeInteger($data, $flags));
|
return fread($data, self::decodeInteger($data, $flags));
|
||||||
}
|
}
|
||||||
|
|
||||||
private const DATETIME_FLAG_TIME = 0x40;
|
private const DATETIME_FLAG_NEGA = 0x20;
|
||||||
private const DATETIME_FLAG_MILLI = 0x4000;
|
private const DATETIME_FLAG_TIME = 0x40;
|
||||||
|
private const DATETIME_FLAG_MILLI = 0x4000;
|
||||||
|
|
||||||
private const DATETIME_YEAR_SIGN = 0x40000000;
|
private const DATETIME_YEAR_SIGN = 0x40000000;
|
||||||
private const DATETIME_YEAR_MASK = 0x3FFF;
|
private const DATETIME_YEAR_MASK = 0x3FFF;
|
||||||
|
@ -263,12 +330,12 @@ class FWIF {
|
||||||
private const DATETIME_MILLI_HI_SHIFT = 8; // >>
|
private const DATETIME_MILLI_HI_SHIFT = 8; // >>
|
||||||
private const DATETIME_MILLI_LO_MASK = 0x0FF;
|
private const DATETIME_MILLI_LO_MASK = 0x0FF;
|
||||||
|
|
||||||
/* +--------+--------+
|
/* +--------+--------+ Y - Signed 15-bit year W - w enable flag
|
||||||
* |.YYYYYYY|YYYYYYYY|
|
* |.YYYYYYY|YYYYYYYY| M - Unsigned 4-bit month m - unsigned 6-bit minutes
|
||||||
* |MMMMDDDD|DT.HHHHH|
|
* |MMMMDDDD|DTNHHHHH| D - Unsigned 5-bit Day S - unsigned 6-bit seconds
|
||||||
* |.Wmmmmmm|SSSSSSww|
|
* |.Wmmmmmm|SSSSSSww| T - WmSw enable flag w - unsigned 10-bit millisecs
|
||||||
* |wwwwwwww| |
|
* |wwwwwwww| | N - Negative flag (for TYPE_PERIOD)
|
||||||
* +--------+--------+
|
* +--------+--------+ H - Unsigned 5-bit hours
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private static function encodeDateTime(DateTimeInterface $dt, int $flags): string {
|
private static function encodeDateTime(DateTimeInterface $dt, int $flags): string {
|
||||||
|
@ -349,4 +416,11 @@ class FWIF {
|
||||||
|
|
||||||
return new DateTimeImmutable($dt);
|
return new DateTimeImmutable($dt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static function encodeTimeSpan(DateInterval $di, int $flags): string {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
private static function decodeTimeSpan($data, int $flags): DateInterval {
|
||||||
|
return new \DateInterval('P1Y');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,6 @@ if($request->match('GET', '/packages')) {
|
||||||
|
|
||||||
$jsonEncoded = json_encode($packages, JSON_INVALID_UTF8_SUBSTITUTE);
|
$jsonEncoded = json_encode($packages, JSON_INVALID_UTF8_SUBSTITUTE);
|
||||||
echo 'JSON ' . strlen($jsonEncoded) . ' bytes ' . $jsonEncoded;
|
echo 'JSON ' . strlen($jsonEncoded) . ' bytes ' . $jsonEncoded;
|
||||||
|
|
||||||
echo "\r\n\r\n--------------------\r\n\r\n";
|
echo "\r\n\r\n--------------------\r\n\r\n";
|
||||||
|
|
||||||
$hexdump = bin2hex($encoded); $hexdumpSect = 8; $hexdumpSize = 32;
|
$hexdump = bin2hex($encoded); $hexdumpSect = 8; $hexdumpSize = 32;
|
||||||
|
|
|
@ -36,10 +36,37 @@ class DummyPackage implements IPackage, \JsonSerializable {
|
||||||
'neg32' => -12345678,
|
'neg32' => -12345678,
|
||||||
'neg64' => -1234567890987654,
|
'neg64' => -1234567890987654,
|
||||||
'float' => 12345.6789,
|
'float' => 12345.6789,
|
||||||
|
'float2' => 8.0,
|
||||||
|
'floatNeg' => -12345.6789,
|
||||||
|
'floatPi' => M_PI,
|
||||||
|
'floatE' => M_E,
|
||||||
|
'floatLog2E' => M_LOG2E,
|
||||||
|
'floatLog10E' => M_LOG10E,
|
||||||
|
'floatLn2' => M_LN2,
|
||||||
|
'floatLn10' => M_LN10,
|
||||||
|
'floatPi2' => M_PI_2,
|
||||||
|
'floatPi4' => M_PI_4,
|
||||||
|
'floatM1Pi' => M_1_PI,
|
||||||
|
'floatM2Pi' => M_2_PI,
|
||||||
|
'floatSqrtPi' => M_SQRTPI,
|
||||||
|
'float2SqrtPi' => M_2_SQRTPI,
|
||||||
|
'floatSqrt2' => M_SQRT2,
|
||||||
|
'floatSqrt3' => M_SQRT3,
|
||||||
|
'floatSqrt12' => M_SQRT1_2,
|
||||||
|
'floatLnPi' => M_LNPI,
|
||||||
|
'floatEuler' => M_EULER,
|
||||||
|
//'floatNaN' => NAN,
|
||||||
|
//'floatInf' => INF,
|
||||||
|
//'floatNegInf' => -INF,
|
||||||
|
'floatZero' => 0.0,
|
||||||
|
'floatNegZero' => -0.0,
|
||||||
'invalid' => "\xFF\x25\x25\x02\xFF蠕。蝮F鄒守清\xFF\xFF\xFF",
|
'invalid' => "\xFF\x25\x25\x02\xFF蠕。蝮F鄒守清\xFF\xFF\xFF",
|
||||||
'datetime' => new \DateTime('2013-01-27 23:14:44 CET'),
|
'datetime' => new \DateTime('2013-01-27 23:14:44 CET'),
|
||||||
'datetimeNegative' => new \DateTime('-2013-01-27 23:14:44 CET'),
|
'datetimeNegative' => new \DateTime('-2013-01-27 23:14:44 CET'),
|
||||||
'datetimeNow' => new \DateTime(),
|
'datetimeNow' => new \DateTime(),
|
||||||
|
'period' => (new \DateTime())->diff(new \DateTime('2013-01-27 23:14:44 CET')),
|
||||||
|
'periodNegative' => (new \DateTime('2013-01-27 23:14:44 CET'))->diff(new \DateTime()),
|
||||||
|
'periodZero' => (new \DateTime())->diff(new \DateTime()),
|
||||||
'array' => ['e', 'a', 0x55],
|
'array' => ['e', 'a', 0x55],
|
||||||
'object' => new \stdClass,
|
'object' => new \stdClass,
|
||||||
'misaka' => '御坂 美琴',
|
'misaka' => '御坂 美琴',
|
||||||
|
|
Reference in a new issue