/* hex2dec.c */
/* by Thomas Zetty, March 13, 2004 */

/* 
ASSUMPTIONS:
1. all input chars are assumed to be in range 0-9 A-F a-f
2. the function handles a range of 0 to 8 hex digits
2. no input error checking is performed
3. no C-lib functions are used, even for I/O; 5 win32 low-level console
   calls are used instead
4. only the base16 to base10 conversion routine is optimized

NOTES:
1. avg. times for 2 billion conversions on a 1.3GHz P3 (inc function overhead):
   4-byte hex number (test case) : 28 seconds
   8-byte hex number (largest case) : 35 seconds
   estimated function stack-frame overhead : 17 seconds
2. the original switch-abusing conversion routine is preserved below
   (old_h2d)
3. a larger, double-byte indexed table was judged to massive to utilize
   (excepting special cases, it could not remain in CPU cache, whereas the
   8 tables here fit nicely)
4. I'm really no fan of pre-processor gymnastics; however the ease of table
   constuction plus the clarity of table declaration outweighed the confusion
   of parsing multi-line nested macros
5. no games were played with function calling procedure or compiler flags

SAMPLE RUN:
TEST! hex 23DA should be decimal 9178 : 23DA = 9178
Enter a base-16 number (8 digits max) = 05311969
Converted to base-10 = 87103849
*/

#include <windows.h> 

typedef unsigned (*h2d_fn)(const char *);

#define SEVEN_ZEROS 0, 0, 0, 0, 0, 0, 0 
#define TWENTY_SIX_ZEROS \ 
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0
#define FOURTY_EIGHT_ZEROS \ 
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0

#define H2D_LOOKUP(x) { FOURTY_EIGHT_ZEROS,\ 
0, 0x1<<x, 0x2<<x, 0x3<<x, 0x4<<x, 0x5<<x, 0x6<<x, 0x7<<x, 0x8<<x, 0x9<<x,\
SEVEN_ZEROS,\
0xa<<x, 0xb<<x, 0xc<<x, 0xd<<x, 0xe<<x, 0xf<<x, TWENTY_SIX_ZEROS,\
0xa<<x, 0xb<<x, 0xc<<x, 0xd<<x, 0xe<<x, 0xf<<x\
}

/* construct lookup tables */
/* note that unifying tables to elimitate zero-space resulted in */
/* slower access - so we'll let the compiler optimize boundries */
static const unsigned h2d_tab0[103] = H2D_LOOKUP(0);
static const unsigned h2d_tab4[103] = H2D_LOOKUP(4);
static const unsigned h2d_tab8[103] = H2D_LOOKUP(8);
static const unsigned h2d_tab12[103] = H2D_LOOKUP(12);
static const unsigned h2d_tab16[103] = H2D_LOOKUP(16);
static const unsigned h2d_tab20[103] = H2D_LOOKUP(20);
static const unsigned h2d_tab24[103] = H2D_LOOKUP(24);
static const unsigned h2d_tab28[103] = H2D_LOOKUP(28);

/* i/o routines not based on the forbidden c-lib */
static void write_string(const char * s);
static void write_decimal(unsigned u);
static char * read_string(char * buf, unsigned maxsize, unsigned * len);

static unsigned h2d_0(const char * s)
{
	return 0;
}

static unsigned h2d_1(const char * s)
{
	return h2d_tab0[*s];
}

static unsigned h2d_2(const char * s)
{
	return h2d_tab4[*s] + h2d_tab0[*(s+1)];
}

static unsigned h2d_3(const char * s)
{
	return
		h2d_tab8[*s] + h2d_tab4[*(s+1)] + h2d_tab0[*(s+2)];
}

static unsigned h2d_4(const char * s)
{
	return
		h2d_tab12[*s]    + h2d_tab8[*(s+1)] +
		h2d_tab4[*(s+2)] + h2d_tab0[*(s+3)];
}

static unsigned h2d_5(const char * s)
{
	return 
		h2d_tab16[*s]    + h2d_tab12[*(s+1)] +
		h2d_tab8[*(s+2)] + h2d_tab4[*(s+3)]  + h2d_tab0[*(s+4)];
}

static unsigned h2d_6(const char * s)
{
	return 
		h2d_tab20[*s]     + h2d_tab16[*(s+1)] +
		h2d_tab12[*(s+2)] + h2d_tab8[*(s+3)]  +
		h2d_tab4[*(s+4)]  + h2d_tab0[*(s+5)];
}

static unsigned h2d_7(const char * s)
{
	return
		h2d_tab24[*s]     + h2d_tab20[*(s+1)] +
		h2d_tab16[*(s+2)] + h2d_tab12[*(s+3)] +
		h2d_tab8[*(s+4)]  + h2d_tab4[*(s+5)]  + h2d_tab0[*(s+6)];
}

static unsigned h2d_8(const char * s)
{
	return 
		h2d_tab28[*s]     + h2d_tab24[*(s+1)] +
		h2d_tab20[*(s+2)] + h2d_tab16[*(s+3)] +
		h2d_tab12[*(s+4)] + h2d_tab8[*(s+5)] +
		h2d_tab4[*(s+6)]  + h2d_tab0[*(s+7)];
}

/* we'll access each of the above conversion fns by length-index */
/* in this array of function pointers */
static h2d_fn h2d[9] = {
	h2d_0, h2d_1, h2d_2, h2d_3, h2d_4, h2d_5, h2d_6, h2d_7, h2d_8
};

int main(void)
{
	char buffer[9];
	unsigned len;

	write_string("TEST! hex 23DA should be decimal 9178 : ");
	write_string("23DA = ");
	write_decimal(h2d[4]("23DA"));
	write_string("\n");

	write_string("Enter a base-16 number (8 digits max) = ");
	read_string(buffer, 8, &len);
	write_string("Converted to base-10 = ");
	write_decimal(h2d[len](buffer));
	write_string("\n");

	return 1;
}

/* replaced with faster array of function pointers
unsigned old_h2d(const char * s, unsigned length)
{
	unsigned total = 0;
	switch (length) {
	case 8: total = h2d_tab28[*s++];
	case 7: total |= h2d_tab24[*s++];
	case 6: total |= h2d_tab20[*s++];
	case 5: total |= h2d_tab16[*s++];
	case 4: total |= h2d_tab12[*s++];
	case 3: total |= h2d_tab8[*s++];
	case 2: total |= h2d_tab4[*s++];
	case 1: total |= h2d_tab0[*s++];
	}
	return total;
}
*/

static void write_string(const char * s)
{
	const char * p = s;
	DWORD count = 0;
	while (*p++ != '\0') ++count;
	WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), s, count, &count, NULL);
}

static void write_decimal(unsigned u)
{
	/* this is big enough to handle numbers up to 9 billion, */
	/* (fine for 32 bit unsigned) */
	static char buf[11];
	char * p = buf + 10;

	if (u == 0) {
		write_string("0");
		return;
	}

	/* store resultant digits in reverse-order in buf */
	*p = '\0';
	do {
		*--p = (u % 10) + '0';
		u /= 10;
	}
	while (u != 0);
	write_string(p);
}

/* reads until end of line, saves up to maxsize chars in buf */
/* reports number of chars saved in *len, returns buf for convenience */
/* unfortunately, tabs are not handled properly */
static char * read_string(char * buf, unsigned maxsize, unsigned * len)
{
	HANDLE hin = GetStdHandle(STD_INPUT_HANDLE);
	HANDLE hout = GetStdHandle(STD_OUTPUT_HANDLE);
	DWORD temp;
	DWORD oldmode;
	char * where = buf;
	char * end = buf + maxsize;
	int done = 0;
	char space = ' ';

	*len = 0;
	GetConsoleMode(hin, &oldmode);
	SetConsoleMode(hin, ENABLE_PROCESSED_INPUT);

	while (done == 0) {
		ReadConsole(hin, where, 1, &temp, NULL);
		/* end on EOL or EOF */
		if ((*where == 13) || (*where == 10) || (*where == 26)) done = 1;
		else {
			/* check for backspace */
			if (*where == 8) {
				if (*len > 0) {
					/* make it destructive - space over last char */
					WriteConsole(hout, where, 1, &temp, NULL);
					WriteConsole(hout, &space, 1, &temp, NULL);
					WriteConsole(hout, where, 1, &temp, NULL);
					--where;
					--(*len);
				}
			}
			else {
				/* treat tabs as spaces >sigh< */
				if (*where == 9) *where = space;
				WriteConsole(hout, where, 1, &temp, NULL);
				if (where != end) {
					++(*len);
					++where;
				}
			}
		}
	}
	SetConsoleMode(hin, oldmode);
	*where = '\0';
	write_string("\n");
	return buf;
}