/* [ʞ] iaia.c
* ~ lexi hale <lexi@hale.su>
* # include "iaia.c"
* # define _IAIA_FN_{ATOI,ITOA,ASCTOI,ITOASC}
* _IAIA_EXP_ASCFORM
* : 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
#ifndef _IAIA_EXP_ASCFORM
/* this is an expression that will be evaluated to
* determine whether to compress ascii to a 7-bit
* representation or to store it in 8-bit space.
* 1 = 7-bit (compressed)
* 0 = 8-bit (uncompressed) */
# define _IAIA_EXP_ASCFORM (0)
#endif
#ifndef NULL
# define NULL ((void*)0)
#endif
enum /* constants */ {
base7bit = 128,
/* 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 */
};
#ifndef _IAIA_EXTERNAL_TYPES
typedef enum iaia_error_type {
iaia_e_ok,
iaia_e_domain,
iaia_e_base,
iaia_e_overflow,
} iaia_error_type;
typedef unsigned long long iaia_word_type;
#endif
/* -- string to integer converters -- */
iaia_error_type
_IAIA_FN_ASCTOI(const char* s, iaia_word_type* ret) {
iaia_word_type val = 0;
for (;*s!=0;++s) {
uint8_t v = *s;
if (v > base7bit) return iaia_e_domain;
if (_IAIA_EXP_ASCFORM)
val *= base7bit;
else val <<= 8;
val += v;
}
*ret = val;
return iaia_e_ok;
}
iaia_error_type
_IAIA_FN_ATOI(iaia_word_type base, const char* s, iaia_word_type* ret) {
/* s must be a NUL-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!=0;++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 manually. 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;
if (_IAIA_EXP_ASCFORM)
val /= base7bit;
else val >>= 8;
*ptr-- = (char)rem;
}
if (newbuf != NULL) *newbuf = ptr + 1;
return iaia_e_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;
}