diff options
Diffstat (limited to 'src/libmp/port/mpfmt.c')
-rw-r--r-- | src/libmp/port/mpfmt.c | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/src/libmp/port/mpfmt.c b/src/libmp/port/mpfmt.c new file mode 100644 index 00000000..f7c42a7b --- /dev/null +++ b/src/libmp/port/mpfmt.c @@ -0,0 +1,193 @@ +#include "os.h" +#include <mp.h> +#include <libsec.h> +#include "dat.h" + +static int +to64(mpint *b, char *buf, int len) +{ + uchar *p; + int n, rv; + + p = nil; + n = mptobe(b, nil, 0, &p); + if(n < 0) + return -1; + rv = enc64(buf, len, p, n); + free(p); + return rv; +} + +static int +to32(mpint *b, char *buf, int len) +{ + uchar *p; + int n, rv; + + // leave room for a multiple of 5 buffer size + n = b->top*Dbytes + 5; + p = malloc(n); + if(p == nil) + return -1; + n = mptobe(b, p, n, nil); + if(n < 0) + return -1; + + // round up buffer size, enc32 only accepts a multiple of 5 + if(n%5) + n += 5 - (n%5); + rv = enc32(buf, len, p, n); + free(p); + return rv; +} + +static char set16[] = "0123456789ABCDEF"; + +static int +to16(mpint *b, char *buf, int len) +{ + mpdigit *p, x; + int i, j; + char *out, *eout; + + if(len < 1) + return -1; + + out = buf; + eout = buf+len; + for(p = &b->p[b->top-1]; p >= b->p; p--){ + x = *p; + for(i = Dbits-4; i >= 0; i -= 4){ + j = 0xf & (x>>i); + if(j != 0 || out != buf){ + if(out >= eout) + return -1; + *out++ = set16[j]; + } + } + } + if(out == buf) + *out++ = '0'; + if(out >= eout) + return -1; + *out = 0; + return 0; +} + +static char* +modbillion(int rem, ulong r, char *out, char *buf) +{ + ulong rr; + int i; + + for(i = 0; i < 9; i++){ + rr = r%10; + r /= 10; + if(out <= buf) + return nil; + *--out = '0' + rr; + if(rem == 0 && r == 0) + break; + } + return out; +} + +static int +to10(mpint *b, char *buf, int len) +{ + mpint *d, *r, *billion; + char *out; + + if(len < 1) + return -1; + + d = mpcopy(b); + r = mpnew(0); + billion = uitomp(1000000000, nil); + out = buf+len; + *--out = 0; + do { + mpdiv(d, billion, d, r); + out = modbillion(d->top, r->p[0], out, buf); + if(out == nil) + break; + } while(d->top != 0); + mpfree(d); + mpfree(r); + mpfree(billion); + + if(out == nil) + return -1; + len -= out-buf; + if(out != buf) + memmove(buf, out, len); + return 0; +} + +int +mpfmt(Fmt *fmt) +{ + mpint *b; + char *p; + + b = va_arg(fmt->args, mpint*); + if(b == nil) + return fmtstrcpy(fmt, "*"); + + p = mptoa(b, fmt->prec, nil, 0); + fmt->flags &= ~FmtPrec; + + if(p == nil) + return fmtstrcpy(fmt, "*"); + else{ + fmtstrcpy(fmt, p); + free(p); + return 0; + } +} + +char* +mptoa(mpint *b, int base, char *buf, int len) +{ + char *out; + int rv, alloced; + + alloced = 0; + if(buf == nil){ + len = ((b->top+1)*Dbits+2)/3 + 1; + buf = malloc(len); + if(buf == nil) + return nil; + alloced = 1; + } + + if(len < 2) + return nil; + + out = buf; + if(b->sign < 0){ + *out++ = '-'; + len--; + } + switch(base){ + case 64: + rv = to64(b, out, len); + break; + case 32: + rv = to32(b, out, len); + break; + default: + case 16: + rv = to16(b, out, len); + break; + case 10: + rv = to10(b, out, len); + break; + } + if(rv < 0){ + if(alloced) + free(buf); + return nil; + } + return buf; +} |