Skip to content

Instantly share code, notes, and snippets.

@nyteshade
Last active January 3, 2024 09:27
Show Gist options
  • Save nyteshade/49cef194fae869b269fa312e0d4db3e1 to your computer and use it in GitHub Desktop.
Save nyteshade/49cef194fae869b269fa312e0d4db3e1 to your computer and use it in GitHub Desktop.
strnumconvert - C89 parseInt, strtoul, itoa with base 62 support
// 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;
}
}
// 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
// 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