/* [ʞ] iaia.c * ~ lexi hale * # include "iaia.c" * # define _IAIA_FN_{ATOI,ITOA,ASCTOI,ITOASC} * : typedef iaia_word_type, * iaia_error_type * : enum iaia_e_domain, * iaia_e_base, * iaia_e_overflow * ? arbitrary-base integer conversion functions */ #ifndef _IAIA_FN_ATOI # define _IAIA_FN_ATOI atoi #endif #ifndef _IAIA_FN_ITOA # define _IAIA_FN_ITOA itoa #endif #ifndef _IAIA_FN_ASCTOI # define _IAIA_FN_ASCTOI asctoi #endif #ifndef _IAIA_FN_ITOASC # define _IAIA_FN_ITOASC itoasc #endif enum /* constants */ { /* ascii address space */ numspace = (0x39 - 0x30) + 1, /* 10 */ alphaspace = (0x5a - 0x41) + 1, /* 26 */ smallalphaspace = (0x7a - 0x61) + 1, /* 26 */ /* base representations */ imaxbase = numspace + alphaspace, /* 36 */ maxbase = imaxbase + smallalphaspace /* 62 */ }; /* -- string to integer converters -- */ iaia_error_type _IAIA_FN_ASCTOI(const char* s, iaia_word_type* ret) { iaia_word_type val = 0; enum { base = 128 }; for (;*s!=null;++s) { uint8_t v = *s; if (v > base) return iaia_e_domain; val *= base; val += v; } *ret = val; return ok; } iaia_error_type _IAIA_FN_ATOI(iaia_word_type base, const char* s, iaia_word_type* ret) { /* s must be a null-terminated ASCII numeral string */ if (base > maxbase) return iaia_e_base; /* override the default base if it's a basèd literal */ if (s[0] == '@' || base == 0) return _IAIA_FN_ASCTOI(s + (s[0]=='@'),ret); else if (s[0] == '0' && s[1] == 'x') base = 16, s += 2; else if (s[0] == '0' && s[1] == 'd') base = 10, s += 2; else if (s[0] == '0' && s[1] == 'b') base = 2, s += 2; else if (s[0] == '0' && s[1] == 't') base = 3, s += 2; else if (s[0] == '0') base = 8, s += 1; bool insens = (base <= imaxbase); iaia_word_type val = 0; for (;*s!=null;++s) { uint8_t v = *s; if(v >= 0x30 && v <= 0x39) v -= 0x30; else { if(v >= 0x61 && v <= 0x7a) { if (insens) v -= 0x20; else { v = numspace + alphaspace + (v - 0x61); goto checkval; } } if(v >= 0x41 && v <= 0x5a) v = numspace + (v - 0x41); else return iaia_e_domain; } checkval: if (v >= base) return iaia_e_domain; val *= base; val += v; } *ret = val; return iaia_e_ok; } /* -- integer to string converters -- */ /* needed for efficiency's sake, but really sucky - * this table needs to be kept in sync with the * itoa algorithm by hand. unfortunately, given C's * abject lack of metaprogramming, we have to do this * by hand. */ const char iaia_ref_table[] = /* numerals[10] */ "0123456789" /* bigalpha[26] */ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" /* smallalpha[26] */ "abcdefghijklmnopqrstuvwxyz"; _Static_assert (sizeof iaia_ref_table - 1 == maxbase, "tables out of sync"); iaia_error_type _IAIA_FN_ITOASC(iaia_word_type val, const char* buf_start, char* buf_end, char** newbuf) { char* ptr = buf_end; *ptr-- = 0; while(val > 0) { if (ptr < buf_start) return iaia_e_overflow; iaia_word_type rem = val % 128; val /= 128; *ptr-- = (char)rem; } if (newbuf != null) *newbuf = ptr + 1; return ok; } iaia_error_type _IAIA_FN_ITOA(iaia_word_type base, iaia_word_type val, const char* buf_start, char* buf_end, char** newbuf, bool lowercase) { char* ptr = buf_end; if (base > maxbase) return iaia_e_base; if (base == 0) return _IAIA_FN_ITOASC(val, buf_start, buf_end, newbuf); *ptr-- = 0; if (val == 0) *ptr-- = '0'; else while(val > 0) { if (ptr < buf_start) return iaia_e_overflow; iaia_word_type rem = val % base; val /= base; char out = iaia_ref_table[rem]; if (lowercase && base <= imaxbase) if (out >= 'A' && out <= 'Z') out += ('a' - 'A'); *ptr-- = out; } if (newbuf != null) *newbuf = ptr + 1; return iaia_e_ok; }