/*
**  OSSP uuid - Universally Unique Identifier
**  Copyright (c) 2002-2005 Ralf S. Engelschall <rse@engelschall.com>
**  Copyright (c) 2002-2005 The OSSP Project <http://www.ossp.org/>
**
**  This file is part of OSSP uuid, a library for the generation
**  of UUIDs which can found at http://www.ossp.org/pkg/lib/uuid/
**
**  Permission to use, copy, modify, and distribute this software for
**  any purpose with or without fee is hereby granted, provided that
**  the above copyright notice and this permission notice appear in all
**  copies.
**
**  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
**  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
**  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
**  IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
**  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
**  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
**  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
**  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
**  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
**  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
**  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
**  SUCH DAMAGE.
*/

#ifndef __UI128_H__
#define __UI128_H__

#include <ctype.h>
#include <string.h>


#define UI128_BASE 256  /* 2^8 */
typedef struct {
	uint8_t x[16]; /* 8*16 = 128 bit */
} ui128_t;


static ui128_t strtou128(const char * str, size_t strmax, const char ** end);
static void u128tostr(ui128_t x, char * str, size_t len);
static ui128_t ui128_addn(ui128_t x, int y, int * ov);
static ui128_t ui128_muln(ui128_t x, int y, int * ov);
static ui128_t ui128_divn(ui128_t x, int y, int * ov);
static int ui128_len(ui128_t x);

/* decode decimal digits to a 16-byte array */
static ui128_t strtou128(const char * str, size_t strmax, const char ** end) {
	ui128_t z       = {};
	const char * cp = str;
	for(int carry; cp != str + strmax && *cp >= '0' && *cp <= '9'; ++cp) {
		z = ui128_muln(z, 10, &carry);
		if(carry)
			break;

		z = ui128_addn(z, *cp - '0', &carry);
		if(carry)
			break;
	}
	*end = cp;
	return z;
}

/* convert 16-byte array to decimal digits */
static void u128tostr(ui128_t x, char * str, size_t len) {
	int n = ui128_len(x);
	int i = 0;
	do {
		int r;
		x        = ui128_divn(x, 10, &r);
		str[i++] = '0' + r;
		while(n > 1 && x.x[n - 1] == 0)
			n--;
	} while(i < ((int)len - 1) && (n > 1 || x.x[0] != 0));
	str[i] = '\0';
	for(int j = 0; j < --i; ++j) {
		char c = str[j];
		str[j] = str[i];
		str[i] = c;
	}
}

/* addition of an ui128_t and a single digit */
static ui128_t ui128_addn(ui128_t x, int y, int * ov) {
	ui128_t z;
	for(size_t i = 0; i < sizeof(x.x); ++i) {
		y += x.x[i];
		z.x[i] = (y % UI128_BASE);
		y /= UI128_BASE;
	}
	*ov = y;
	return z;
}

static ui128_t ui128_muln(ui128_t x, int y, int * ov) {
	ui128_t z;
	int carry = 0;
	for(size_t i = 0; i < sizeof(x.x); ++i) {
		carry += (x.x[i] * y);
		z.x[i] = (carry % UI128_BASE);
		carry /= UI128_BASE;
	}
	*ov = carry;
	return z;
}

static ui128_t ui128_divn(ui128_t x, int y, int * ov) {
	ui128_t z;
	unsigned carry = 0;
	for(int i = (sizeof(x.x) - 1); i >= 0; --i) {
		carry  = (carry * UI128_BASE) + x.x[i];
		z.x[i] = (carry / y);
		carry %= y;
	}
	*ov = carry;
	return z;
}

static int ui128_len(ui128_t x) {
	int i;
	for(i = sizeof(x.x); i > 1 && x.x[i - 1] == 0; --i)
		;
	return i;
}


#endif
