shithub: femtolisp

ref: 4dc8cff4fd04e8536f5577522ecb95032064f295
dir: /3rd/mp/mpfmt.c/

View raw version
#include "platform.h"

static int
topow2(mpint *b, char *buf, int len, int s)
{
	mpdigit *p, x;
	int i, j, sn;
	char *out, *eout;

	if(len < 1)
		return -1;

	sn = 1<<s;
	out = buf;
	eout = buf+len;
	for(p = b->p+b->top-1; p >= b->p; p--){
		x = *p;
		for(i = Dbits-s; i >= 0; i -= s){
			j = x >> i & sn - 1;
			if(j != 0 || out != buf){
				if(out >= eout)
					return -1;
				*out++ = enc16chr(j);
			}
		}
	}
	if(out == buf)
		*out++ = '0';
	if(out >= eout)
		return -1;
	*out = 0;
	return 0;
}

static char*
modbillion(int rem, uint32_t r, char *out, char *buf)
{
	uint32_t 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);
	d->flags &= ~MPtimesafe;
	mpnorm(d);
	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;
}

static int
to8(mpint *b, char *buf, int len)
{
	mpdigit x, y;
	char *out;
	uint32_t j;
	int i;

	if(len < 2)
		return -1;

	out = buf+len;
	*--out = 0;

	i = j = 0;
	x = y = 0;
	while(j < b->top){
		y = b->p[j++];
		if(i > 0)
			x |= y << i;
		else
			x = y;
		i += Dbits;
		while(i >= 3){
Digout:
			i -= 3;
			if(out > buf)
				out--;
			else if(x != 0)
				return -1;
			*out = '0' + (x & 7);
			x = y >> (Dbits-i);
		}
	}
	if(i > 0)
		goto Digout;

	while(*out == '0') out++;
	if(*out == '\0')
		*--out = '0';

	len -= out-buf;
	if(out != buf)
		memmove(buf, out, len);
	return 0;
}

char*
mptoa(mpint *b, int base, char *buf, int len)
{
	char *out;
	int rv, alloced;

	if(base == 0)
		base = 16;	/* default */
	alloced = 0;
	if(buf == nil){
		/* rv <= log₂(base) */
		for(rv=1; (base >> rv) > 1; rv++)
			;
		len = 10 + (b->top*Dbits / rv);
		buf = MEM_ALLOC(len);
		if(buf == nil)
			return nil;
		alloced = 1;
	}

	if(len < 2)
		return nil;

	out = buf;
	if(b->sign < 0){
		*out++ = '-';
		len--;
	}
	switch(base){
	case 16:
		rv = topow2(b, out, len, 4);
		break;
	case 10:
		rv = to10(b, out, len);
		break;
	case 8:
		rv = to8(b, out, len);
		break;
	case 4:
		rv = topow2(b, out, len, 2);
		break;
	case 2:
		rv = topow2(b, out, len, 1);
		break;
	default:
		fprintf(stderr, "mptoa: invalid base: %d\n", base);
		exit(2);
	}
	if(rv < 0){
		if(alloced)
			MEM_FREE(buf);
		return nil;
	}
	return buf;
}