Base 57
Posted 03-16-2021 at 09:49 PM by Skaperen
Base 57 is the smallest base that can represent 64 bits in just 11 characters. many programmers use higher base values to do this. that forces the use of a larger set of characters that must be used. if you choose base 62 instead of base 64 you avoid the use of special characters. so you can use just letters and number for your 11 encoded digits. with base 57 you can remove 5 characters that look similar to others. for example you might use just one character of 0, O and o instead of all 3 that would be more necessary with a higher base.
Total Comments 4
Comments
-
Annnnd? ...
I'm unclear where you're going with this.Posted 03-17-2021 at 08:13 AM by jr_bob_dobbs -
i want to use a smaller set of characters with no special characters. around this scale it seems everyone jumps to base 64 when base 57 works fine and avoids the special characters. which base do you think is best, and why?
Posted 03-21-2021 at 02:07 PM by Skaperen -
Now I get it. Paper backup of data that one can later type in with less likelihood of error, with an expansion ratio nearly as good as the usual 3:4 of base 64.
Posted 05-01-2021 at 10:52 PM by jr_bob_dobbs -
You got me curious, so, years later, I wrote it. At first I had some tricky code that required it to be running on a x86_64, since it has eight-byte registers, but I've since rewritten it for portability.
I hope Cloudflare will let me post this.
I hope the code tag works today.
Code:/* cc -o base_57 base_57.c */ /* version 2025.03.01 */ #include <stdio.h> #include <stdlib.h> #include <string.h> #define OKDOKEY 0 #define DOOPS 20 #define neq != #define eq == /* globals */ char e_table[] = "123456789ACDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; /* It's the set of characters from base 58, but with the uppercase "B" taken out because it looks lot like an "8". */ unsigned char reverse_lookup[256]; /* forward references */ void usage_exit(void); void populate_reverse_lookup(void); int read_chars_as_ints(int *, int, FILE *); int write_ints_as_chars(int *, int, FILE *); void mult57_by_16(int *); void n57_plus_16(int *, int); void mult256_by_57(int *); void n256_plus_57(int *, int); void block_8_to_11(int *, int *); void block_11_to_8(int *, int *); int encode_and_write_block(FILE *, int *, int *); int encode(FILE *, FILE *); int decode(FILE *, FILE *); /* actual program */ void usage_exit(void) { fprintf(stderr, "base_57 <-e|-d> <infile> <outfile>\n\ where \"-\" means stdin or stdout\n"); exit(DOOPS); } void populate_reverse_lookup(void) { int x; memset(reverse_lookup, 255, 256); /* fill with "invalid" (255) */ for (x = 0; x < 57; x++) { reverse_lookup[e_table[x]] = x; } } int read_chars_as_ints(b, len, f) int *b; int len; FILE *f; { int x, r; for (x = 0; x < len; x++) { if (EOF eq (b[x] = fgetc(f))) break; } r = x; while (x < len) { b[x++] = 0; /* pad a partially-read block with zeros */ } return(r); } int write_ints_as_chars(b, len, f) int *b; int len; FILE *f; { int x; for (x = 0; x < len; x++) { if (EOF eq fputc(b[x], f)) break; } return(x); } void mult57_by_16(b57) int *b57; { int x, carry; for (x = 10, carry = 0; x >= 0; x--) { b57[x] = (16 * b57[x]) + carry; if (b57[x] >= 57) { carry = b57[x] / 57; b57[x] = b57[x] % 57; } else { carry = 0; } } } void n57_plus_16(b57, addin) int *b57; int addin; /* a 0 - 15 value *only* (no we don't check that here) */ { int x, carry; for (x = 10, carry = addin; x >= 0; x--) { b57[x] += carry; if (b57[x] >= 57) { carry = b57[x] / 57; b57[x] = b57[x] % 57; } else { carry = 0; } if (0 eq carry) break; } } void mult256_by_57(b256) int *b256; { int x, carry; for (x = 7, carry = 0; x >= 0; x--) { b256[x] = (57 * b256[x]) + carry; if (b256[x] > 255) { carry = b256[x] / 256; b256[x] = b256[x] % 256; } else { carry = 0; } } } void n256_plus_57(b256, addin) int *b256; int addin; /* a 0 - 56 value *only* (no we don't check that here) */ { int x, carry; for (x = 7, carry = addin; x >= 0; x--) { b256[x] += carry; if (b256[x] > 255) { carry = b256[x] / 256; b256[x] = b256[x] % 256; } else { carry = 0; } if (0 eq carry) break; } } void block_8_to_11(in8, out11) int *in8; int *out11; { int x, nibble; for (x = 0; x < 11; x++) out11[x] = 0; /* zero the base 57 number */ for (x = 0; x < 8; x++) { nibble = in8[x] / 16; /* get high nibble */ mult57_by_16(out11); n57_plus_16(out11, nibble); nibble = in8[x] % 16; /* get low nibble */ mult57_by_16(out11); n57_plus_16(out11, nibble); } /* mapping the 0-56 values to readable characters happens elsewhere */ } void block_11_to_8(in11, out8) int *in11; int *out8; { /* mapping from readable characters to 0-56 values was done already, elsewhere */ int x; for (x = 0; x < 8; x++) out8[x] = 0; /* zero the base 256 number */ for (x = 0; x < 11; x++) { mult256_by_57(out8); n256_plus_57(out8, in11[x]); } } int encode_and_write_block(f, base_256_in, base_57_out) FILE *f; int *base_256_in; int *base_57_out; { int x; /* convert base */ block_8_to_11(base_256_in, base_57_out); /* convert to readable characters */ for (x = 0; x neq 11; x++) { base_57_out[x] = e_table[base_57_out[x]]; } /* write it */ if (11 neq write_ints_as_chars(base_57_out, 11, f)) { return(DOOPS); } /* done */ return(OKDOKEY); } int encode(f1, f2) FILE *f1; FILE *f2; { int base_256_in[8]; int base_57_out[11]; int count, columns; /* read encode and write, a block at a time */ columns = 0; while (0 < (count = read_chars_as_ints(base_256_in, 8, f1))) { if (OKDOKEY neq encode_and_write_block(f2, base_256_in, base_57_out)) { fprintf(stderr, "encode: file write error 1\n"); return(DOOPS); } columns += 11; if (columns > 65) { columns = 0; if (EOF eq fputc('\n', f2)) { fprintf(stderr, "encode: file write error 2\n"); return(DOOPS); } } } if (EOF eq fputc('\n', f2)) { fprintf(stderr, "encode: file write error 3\n"); return(DOOPS); } return(OKDOKEY); } int decode(f1, f2) FILE *f1; FILE *f2; { int base_57_in[11]; int base_256_out[8]; int c, x, time_to_go; for (time_to_go = 0; time_to_go eq 0;) { /* read characters, skipping invalid characters, until there are eleven characters */ x = 0; while (x < 11) { c = fgetc(f1); if (EOF eq c) break; /* end of file, so a partial block */ /* If we reached this point, there was not an end of file. */ c = (int) reverse_lookup[c]; /* decode single character/digit */ if (255 eq c) continue; /* 255 means an invalid character, which is not usually an error, just a newline or space or some such. */ base_57_in[x++] = c; /* put translated (back to a 0-56 range) number into array */ } if (0 eq x) break; /* end of file on a clean block size break */ if (x < 11) { /* end of file, partial block size */ time_to_go = 1; for (; x < 11; x++) base_57_in[x] = 0; /* pad with base 57 zeros */ } /* decode the 11 digit base 57 block into an 8 digit base 256 block */ block_11_to_8(base_57_in, base_256_out); /* write decoded raw base 265 block */ if (8 neq write_ints_as_chars(base_256_out, 8, f2)) { fprintf(stderr, "decode: file write error\n"); return(DOOPS); } } /* done */ return(OKDOKEY); } main(argc, argv) int argc; char *argv[]; { FILE *f1, *f2; int status, file_in, file_out; if (argc < 4) usage_exit(); populate_reverse_lookup(); /* set up input */ if (strcmp("-", argv[2])) { f1 = fopen(argv[2], "r"); if (NULL eq f1) { fprintf(stderr, "error opening input file \"%s\"\n", argv[2]); exit(DOOPS); } file_in = 1; } else { f1 = stdin; file_in = 0; } /* set up output */ if (strcmp("-", argv[3])) { f2 = fopen(argv[3], "w"); if (NULL eq f2) { fprintf(stderr, "error creating output file \"%s\"\n", argv[3]); exit(DOOPS); } file_out = 1; } else { f2 = stdout; file_out = 0; } if (0 eq strcmp("-e", argv[1])) { status = encode(f1, f2); } else { if (0 eq strcmp("-d", argv[1])) { status = decode(f1, f2); } else { fprintf(stderr, "invalid operation: must be one of -e or -d, only\n"); usage_exit(); } } if (status neq OKDOKEY) { fprintf(stderr, "error during file processing\n"); exit(DOOPS); } if (file_out) fclose(f2); if (file_in) fclose(f1); exit(OKDOKEY); } /* actual end of this file */
Posted 03-08-2025 at 07:42 AM by jr_bob_dobbs