Last active
January 3, 2024 09:27
-
-
Save nyteshade/49cef194fae869b269fa312e0d4db3e1 to your computer and use it in GitHub Desktop.
strnumconvert - C89 parseInt, strtoul, itoa with base 62 support
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// strnumconvert.c | |
#include <limits.h> | |
#include <ctype.h> | |
#include <string.h> | |
#include <stdio.h> | |
#include <stddef.h> | |
#include "strnumconvert.h" | |
#define MAX_BASES 6 | |
/* ------------------------ getMaxStrLenForBase -------------------------- */ | |
static const BaseMaxStrLen baseMaxStrLenArray[MAX_BASES] = { | |
{62, MAX_BASE62_STR_LEN + 1}, | |
{36, MAX_BASE36_STR_LEN + 1}, | |
{16, MAX_BASE16_STR_LEN + 1}, | |
{10, MAX_BASE10_STR_LEN + 1}, | |
{7, MAX_BASE7_STR_LEN + 1}, | |
{2, MAX_BASE2_STR_LEN + 1} | |
}; | |
size_t bb_getMaxStrLenForBase(int base) { | |
for (int i = 0; i < MAX_BASES; ++i) { | |
if (baseMaxStrLenArray[i].base == base) { | |
return baseMaxStrLenArray[i].maxStrLen; | |
} | |
} | |
return 0; // Return 0 if base not found | |
} | |
/* --------------------- String to Unsigned Long ------------------------- */ | |
unsigned long bb_strtoul(const char *str, char **endptr, int base) { | |
const char *p = str; | |
unsigned long result = 0; | |
unsigned long max_value; | |
int digit, overflow = 0; | |
int strlength = strlen(str); | |
// Return 0 if string is too long for the given base | |
if (strlength >= bb_getMaxStrLenForBase(base)) { | |
fprintf(stderr, "The supplied string is too long for the requested base"); | |
return 0; | |
} | |
// Skip whitespace | |
while (isspace((unsigned char)*p)) p++; | |
// Handle optional sign | |
int sign = 1; | |
if (*p == '+' || *p == '-') { | |
sign = (*p++ == '-') ? -1 : 1; | |
} | |
if (base < 2 || base > 62) { | |
// Invalid base | |
if (endptr) *endptr = (char *)str; | |
return 0; | |
} | |
// Convert | |
max_value = ULONG_MAX / base; | |
while ((digit = isdigit((unsigned char)*p) ? *p - '0' : | |
islower((unsigned char)*p) ? *p - 'a' + 10 : | |
isupper((unsigned char)*p) ? *p - 'A' + 36 : -1) >= 0 && | |
digit < base) { | |
if (result > max_value || (ULONG_MAX - digit) / base < result) { | |
overflow = 1; | |
} | |
result = result * base + digit; | |
p++; | |
} | |
// Set endptr | |
if (endptr != NULL) { | |
*endptr = (char *)(overflow ? str : p); | |
} | |
// Handle overflow | |
if (overflow) { | |
return ULONG_MAX; | |
} | |
return result * sign; | |
} | |
/* ---------------------------- Reverse ---------------------------------- */ | |
void reverse(char *str, int length) { | |
int start = 0; | |
int end = length - 1; | |
while (start < end) { | |
char temp = str[start]; | |
str[start] = str[end]; | |
str[end] = temp; | |
start++; | |
end--; | |
} | |
} | |
/* --------------------- Integer to ASCII (String) ----------------------- */ | |
char* bb_itoa(unsigned long value, char *str, int base) { | |
int i = 0; | |
int isNegative = 0; | |
// Handle 0 explicitly | |
if (value == 0) { | |
str[i++] = '0'; | |
str[i] = '\0'; | |
return str; | |
} | |
// Handle negative numbers for base 10 | |
if (value < 0 && base == 10) { | |
isNegative = 1; | |
value = -value; | |
} | |
// Convert to string | |
while (value != 0) { | |
int rem = value % base; | |
if (rem < 10) { | |
str[i++] = rem + '0'; | |
} else if (rem < 36) { | |
str[i++] = rem - 10 + 'a'; | |
} else { | |
str[i++] = rem - 36 + 'A'; | |
} | |
value = value / base; | |
} | |
// If number is negative, append '-' | |
if (isNegative) { | |
str[i++] = '-'; | |
} | |
str[i] = '\0'; // Null-terminate string | |
// Reverse the string | |
reverse(str, i); | |
return str; | |
} | |
unsigned long bb_parseInt(const char *str, int base) { | |
switch(base) { | |
case 62: | |
case 36: | |
case 16: | |
case 10: | |
case 7: | |
case 2: | |
return bb_strtoul(str, NULL, base); | |
default: | |
fprintf(stderr, "Cannot parse in that base; only 62, 36, 16, 10, 7 and 2 are supported\n"); | |
return 0; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// strnumconvert.h | |
#ifndef BB_STRNUM_CONVERT_H | |
#define BB_STRNUM_CONVERT_H | |
#include <stddef.h> | |
#define MAX_BASE62_STR_LEN 6 | |
#define MAX_BASE36_STR_LEN 7 | |
#define MAX_BASE16_STR_LEN 8 | |
#define MAX_BASE10_STR_LEN 10 | |
#define MAX_BASE7_STR_LEN 12 | |
#define MAX_BASE2_STR_LEN 32 | |
#define MAX_BASE62_STR "4GFfc3" | |
#define MAX_BASE36_STR "1z141z3" | |
#define MAX_BASE16_STR "ffffffff" | |
#define MAX_BASE10_STR "4294967295" | |
#define MAX_BASE7_STR "211301422353" | |
#define MAX_BASE2_STR "11111111111111111111111111111111" | |
typedef char Base64Str[7]; | |
typedef char Base36Str[8]; | |
typedef char Base16Str[9]; | |
typedef char Base10Str[11]; | |
typedef char Base7Str[13]; | |
typedef char Base2Str[33]; | |
typedef struct { | |
int base; | |
size_t maxStrLen; | |
} BaseMaxStrLen; | |
unsigned long bb_parseInt(const char *str, int base); | |
unsigned long bb_strtoul(const char *str, char **endptr, int base); | |
char* bb_itoa(unsigned long value, char *str, int base); | |
size_t bb_getMaxStrLenForBase(int base); | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// test.c | |
#include <stdio.h> | |
#include "strnumconvert.h" | |
int main(int argc, char **argv) { | |
Base64Str output64; | |
Base36Str output36; | |
Base16Str output16; | |
Base10Str output10; | |
Base7Str output7; | |
Base2Str output2; | |
printf("Base 62 (0-9a-zA-Z)\n"); | |
printf("%ld\n", bb_parseInt(MAX_BASE62_STR, 62)); | |
printf("%s\n\n", bb_itoa(4294967295, output64, 62)); | |
printf("Base 36 (0-9a-z) [case insensitive]\n"); | |
printf("%ld\n", bb_parseInt(MAX_BASE36_STR, 36)); | |
printf("%s\n\n", bb_itoa(4294967295, output36, 36)); | |
printf("Base 16 (0-f)\n"); | |
printf("%ld\n", bb_parseInt(MAX_BASE16_STR, 16)); | |
printf("0x%s\n\n", bb_itoa(4294967295, output16, 16)); | |
printf("Base 10 (n)\n"); | |
printf("%ld\n", bb_parseInt(MAX_BASE10_STR, 10)); | |
printf("0x%s\n\n", bb_itoa(4294967295, output10, 10)); | |
printf("Base 7\n"); | |
printf("%ld\n", bb_parseInt(MAX_BASE7_STR, 7)); | |
printf("0%s\n\n", bb_itoa(4294967295, output7, 7)); | |
printf("Base 2\n"); | |
printf("%ld\n", bb_parseInt(MAX_BASE2_STR, 2)); | |
printf("%s\n\n", bb_itoa(4294967295, output2, 2)); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment