apfl/src/encode.c

125 lines
3 KiB
C

#include <stdbool.h>
#include <math.h>
#include <stdint.h>
#include "apfl.h"
#include "format.h"
bool
apfl_encode_u64(struct apfl_io_writer w, uint_least64_t n)
{
unsigned char buf[8] = {
n & 0xFF,
(n & 0xFF00) >> 8,
(n & 0xFF0000) >> 16,
(n & 0xFF000000) >> 24,
(n & 0xFF00000000) >> 32,
(n & 0xFF0000000000) >> 40,
(n & 0xFF000000000000) >> 48,
(n & 0xFF00000000000000) >> 56,
};
return apfl_io_write_string_view(w, (struct apfl_string_view) {
.bytes = buf,
.len = 8,
});
}
bool
apfl_decode_u64(struct apfl_io_reader r, uint_least64_t *n)
{
unsigned char buf[8];
FMT_TRY(apfl_io_read_bytes_exact_size(r, buf, 8));
*n = ((uint_least64_t)buf[0])
| ((uint_least64_t)buf[1] << 8)
| ((uint_least64_t)buf[2] << 16)
| ((uint_least64_t)buf[3] << 24)
| ((uint_least64_t)buf[4] << 32)
| ((uint_least64_t)buf[5] << 40)
| ((uint_least64_t)buf[6] << 48)
| ((uint_least64_t)buf[7] << 56);
return true;
}
static uint_least64_t
double_repr_from_exp_and_frac(uint_least64_t exp, uint_least64_t frac)
{
return ((exp & 0x7FF) << 52) | (frac & 0xFFFFFFFFFFFFF);
}
static uint_least64_t
prepare_unsigned_double_encode(double d)
{
if (d == 0) {
return 0;
} else if (isinf(d)) {
return double_repr_from_exp_and_frac(0x7FF, 0x0);
} else if (isnan(d)) {
return double_repr_from_exp_and_frac(0x7FF, 0x8000000000000);
} else {
int _exp;
double _frac = frexp(d, &_exp);
_frac *= 9007199254740992; // 2^53
uint_least64_t frac = (uint_least64_t)(_frac) & 0xFFFFFFFFFFFFF;
uint_least64_t exp = _exp + 1022;
return double_repr_from_exp_and_frac(exp, frac);
}
}
static double
double_from_exp_and_frac(uint_least64_t exp, uint_least64_t frac)
{
if (exp == 0x7FF) {
if (frac == 0) {
return INFINITY;
} else {
return NAN;
}
} else if (exp == 0) {
if (frac == 0) {
return 0;
} else {
return 0; // TODO: Subnormal numbers
}
} else {
int iexp = (int)exp - 1022;
double dfrac = frac | 0x10000000000000;
dfrac /= 9007199254740992;
return dfrac * pow(2, (double)iexp);
}
}
bool
apfl_encode_double(struct apfl_io_writer w, double d)
{
bool negative = copysign(1, d) < 0;
d = fabs(d);
uint_least64_t out = prepare_unsigned_double_encode(d);
if (negative) {
out |= 0x8000000000000000;
}
return apfl_encode_u64(w, out);
}
bool
apfl_decode_double(struct apfl_io_reader r, double *d)
{
uint_least64_t n;
if (!apfl_decode_u64(r, &n)) {
return false;
}
double sign = (n & ((uint_least64_t)1<<63)) != 0 ? -1 : 1;
n &= 0x7FFFFFFFFFFFFFFF;
uint_least64_t exp = n >> 52;
uint_least64_t frac = n & 0xFFFFFFFFFFFFF;
*d = copysign(double_from_exp_and_frac(exp, frac), sign);
return true;
}