Base24: Skillnad mellan sidversioner

Från Täpp-Anders
Hoppa till navigeringHoppa till sök
Märke: Ersättning
Ingen redigeringssammanfattning
 
(2 mellanliggande sidversioner av samma användare visas inte)
Rad 1: Rad 1:
[[category:c]]
[[category:programmering]]
[[category:algoritmer]]
= Bakgrund =  
= Bakgrund =  


Rad 4: Rad 8:


= Implementation =
= Implementation =
<pre>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
const char ALPHABET[] = "BCDFGHJKMNPQRTVWXY2346789";
#define BASE 24
#define MAX_LINE 70
#define CHARS_PER_BLOCK 6 // 6 tecken för data täcker 24 bitar (24^6 > 2^24)
int get_index(char c) {
    for (int i = 0; i < BASE; i++) if (ALPHABET[i] == c) return i;
    return -1;
}
void encode_block(const uint8_t *in, int len, char *out_buf) {
    uint32_t val = 0;
    // Packa indata till en 24-bitars integer
    if (len >= 1) val |= ((uint32_t)in[0] << 16);
    if (len >= 2) val |= ((uint32_t)in[1] << 8);
    if (len >= 3) val |= ((uint32_t)in[2]);
    out_buf[0] = ALPHABET[len]; // Header: längd
   
    // Konvertera val till bas 24 över 6 tecken (position 1 till 6)
    uint32_t temp = val;
    for (int i = CHARS_PER_BLOCK; i >= 1; i--) {
        out_buf[i] = ALPHABET[temp % BASE];
        temp /= BASE;
    }
}
int decode_block(const char *in_buf, uint8_t *out_three) {
    int len = get_index(in_buf[0]);
    if (len < 0 || len > 3) return -1;
    uint32_t val = 0;
    for (int i = 1; i <= CHARS_PER_BLOCK; i++) {
        int idx = get_index(in_buf[i]);
        if (idx == -1) return -1;
        // Vi bygger värdet baserat på positionell vikt
        val = val * BASE + idx;
    }
    if (len >= 1) out_three[0] = (uint8_t)((val >> 16) & 0xFF);
    if (len >= 2) out_three[1] = (uint8_t)((val >> 8) & 0xFF);
    if (len >= 3) out_three[2] = (uint8_t)(val & 0xFF);
   
    return len;
}
void encode_stream() {
    printf("--- BEGIN BASE24 ENCODED DATA ---\n");
    uint8_t in[3];
    char out[CHARS_PER_BLOCK + 1];
    int bytes_read, line_pos = 0;
    while ((bytes_read = fread(in, 1, 3, stdin)) > 0) {
        encode_block(in, bytes_read, out);
        for (int i = 0; i < CHARS_PER_BLOCK + 1; i++) {
            putchar(out[i]);
            if (++line_pos >= MAX_LINE) {
                putchar('\n');
                line_pos = 0;
            }
        }
    }
    if (line_pos > 0) putchar('\n');
    printf("--- END BASE24 ENCODED DATA ---\n");
}
void decode_stream() {
    int ch, count = 0;
    char block[CHARS_PER_BLOCK + 1];
    uint8_t out[3];
    while ((ch = getchar()) != EOF) {
        if (ch == '-' || ch == '\n' || ch == '\r' || ch == ' ' || ch == '\t') {
            if (ch == '-') while ((ch = getchar()) != '\n' && ch != EOF);
            continue;
        }
        block[count++] = (char)ch;
        if (count == CHARS_PER_BLOCK + 1) {
            int decoded = decode_block(block, out);
            if (decoded > 0) fwrite(out, 1, decoded, stdout);
            count = 0;
        }
    }
}
void run_test_case(const char *name, uint8_t *orig, int len) {
    printf("Testar: %s (%d bytes)\n", name, len);
   
    int num_blocks = (len + 2) / 3;
    char *encoded = malloc(num_blocks * (CHARS_PER_BLOCK + 1));
    for (int i = 0; i < num_blocks; i++) {
        int chunk = (len - i * 3 > 3) ? 3 : (len - i * 3);
        encode_block(&orig[i * 3], chunk, &encoded[i * (CHARS_PER_BLOCK + 1)]);
    }
    uint8_t *reconstructed = malloc(num_blocks * 3);
    int total_decoded = 0;
    for (int i = 0; i < num_blocks; i++) {
        int res = decode_block(&encoded[i * (CHARS_PER_BLOCK + 1)], &reconstructed[total_decoded]);
        if (res > 0) total_decoded += res;
    }
    if (total_decoded == len && memcmp(orig, reconstructed, len) == 0) {
        printf("  [PASS] Data matchar perfekt.\n");
    } else {
        printf("  [FAIL] Korruption! Längd: %d/%d\n", total_decoded, len);
    }
    free(encoded);
    free(reconstructed);
}
void run_tests() {
    printf("BASE24 SJÄLVTEST - FINAL VERSION\n================================\n");
    uint8_t test1[13];
    for(int i=0; i<13; i++) test1[i] = (uint8_t)(rand() % 256);
    run_test_case("13 bytes slumpdata (inkl. höga bitvärden)", test1, 13);
    uint8_t test2[12] = {0};
    run_test_case("12 bytes nollor", test2, 12);
    uint8_t test3[] = "Klockrent, Täpp-Anders!";
    run_test_case("Sträng med UTF-8", test3, strlen((char*)test3));
}
int main(int argc, char *argv[]) {
    if (argc < 2) return 1;
    if (strcmp(argv[1], "--test") == 0) run_tests();
    else if (strcmp(argv[1], "--encode") == 0 || strcmp(argv[1], "-e") == 0) encode_stream();
    else if (strcmp(argv[1], "--decode") == 0 || strcmp(argv[1], "-d") == 0) decode_stream();
    return 0;
}
</pre>

Nuvarande version från 24 april 2026 kl. 14.36


Bakgrund

Base24 är inte en standardkodning och den är lite mer komplex än de som baseras på ett jämt antal oktetter som base32 samt base64 men det har en fördel också, vi använder bara stora bokstäver A-X.

Implementation

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

const char ALPHABET[] = "BCDFGHJKMNPQRTVWXY2346789";
#define BASE 24
#define MAX_LINE 70
#define CHARS_PER_BLOCK 6 // 6 tecken för data täcker 24 bitar (24^6 > 2^24)

int get_index(char c) {
    for (int i = 0; i < BASE; i++) if (ALPHABET[i] == c) return i;
    return -1;
}

void encode_block(const uint8_t *in, int len, char *out_buf) {
    uint32_t val = 0;
    // Packa indata till en 24-bitars integer
    if (len >= 1) val |= ((uint32_t)in[0] << 16);
    if (len >= 2) val |= ((uint32_t)in[1] << 8);
    if (len >= 3) val |= ((uint32_t)in[2]);

    out_buf[0] = ALPHABET[len]; // Header: längd
    
    // Konvertera val till bas 24 över 6 tecken (position 1 till 6)
    uint32_t temp = val;
    for (int i = CHARS_PER_BLOCK; i >= 1; i--) {
        out_buf[i] = ALPHABET[temp % BASE];
        temp /= BASE;
    }
}

int decode_block(const char *in_buf, uint8_t *out_three) {
    int len = get_index(in_buf[0]);
    if (len < 0 || len > 3) return -1;

    uint32_t val = 0;
    for (int i = 1; i <= CHARS_PER_BLOCK; i++) {
        int idx = get_index(in_buf[i]);
        if (idx == -1) return -1;
        // Vi bygger värdet baserat på positionell vikt
        val = val * BASE + idx;
    }

    if (len >= 1) out_three[0] = (uint8_t)((val >> 16) & 0xFF);
    if (len >= 2) out_three[1] = (uint8_t)((val >> 8) & 0xFF);
    if (len >= 3) out_three[2] = (uint8_t)(val & 0xFF);
    
    return len;
}

void encode_stream() {
    printf("--- BEGIN BASE24 ENCODED DATA ---\n");
    uint8_t in[3];
    char out[CHARS_PER_BLOCK + 1];
    int bytes_read, line_pos = 0;
    while ((bytes_read = fread(in, 1, 3, stdin)) > 0) {
        encode_block(in, bytes_read, out);
        for (int i = 0; i < CHARS_PER_BLOCK + 1; i++) {
            putchar(out[i]);
            if (++line_pos >= MAX_LINE) {
                putchar('\n');
                line_pos = 0;
            }
        }
    }
    if (line_pos > 0) putchar('\n');
    printf("--- END BASE24 ENCODED DATA ---\n");
}

void decode_stream() {
    int ch, count = 0;
    char block[CHARS_PER_BLOCK + 1];
    uint8_t out[3];

    while ((ch = getchar()) != EOF) {
        if (ch == '-' || ch == '\n' || ch == '\r' || ch == ' ' || ch == '\t') {
            if (ch == '-') while ((ch = getchar()) != '\n' && ch != EOF);
            continue;
        }
        block[count++] = (char)ch;
        if (count == CHARS_PER_BLOCK + 1) {
            int decoded = decode_block(block, out);
            if (decoded > 0) fwrite(out, 1, decoded, stdout);
            count = 0;
        }
    }
}

void run_test_case(const char *name, uint8_t *orig, int len) {
    printf("Testar: %s (%d bytes)\n", name, len);
    
    int num_blocks = (len + 2) / 3;
    char *encoded = malloc(num_blocks * (CHARS_PER_BLOCK + 1));
    for (int i = 0; i < num_blocks; i++) {
        int chunk = (len - i * 3 > 3) ? 3 : (len - i * 3);
        encode_block(&orig[i * 3], chunk, &encoded[i * (CHARS_PER_BLOCK + 1)]);
    }

    uint8_t *reconstructed = malloc(num_blocks * 3);
    int total_decoded = 0;
    for (int i = 0; i < num_blocks; i++) {
        int res = decode_block(&encoded[i * (CHARS_PER_BLOCK + 1)], &reconstructed[total_decoded]);
        if (res > 0) total_decoded += res;
    }

    if (total_decoded == len && memcmp(orig, reconstructed, len) == 0) {
        printf("  [PASS] Data matchar perfekt.\n");
    } else {
        printf("  [FAIL] Korruption! Längd: %d/%d\n", total_decoded, len);
    }

    free(encoded);
    free(reconstructed);
}

void run_tests() {
    printf("BASE24 SJÄLVTEST - FINAL VERSION\n================================\n");
    uint8_t test1[13];
    for(int i=0; i<13; i++) test1[i] = (uint8_t)(rand() % 256);
    run_test_case("13 bytes slumpdata (inkl. höga bitvärden)", test1, 13);

    uint8_t test2[12] = {0};
    run_test_case("12 bytes nollor", test2, 12);

    uint8_t test3[] = "Klockrent, Täpp-Anders!";
    run_test_case("Sträng med UTF-8", test3, strlen((char*)test3));
}

int main(int argc, char *argv[]) {
    if (argc < 2) return 1;
    if (strcmp(argv[1], "--test") == 0) run_tests();
    else if (strcmp(argv[1], "--encode") == 0 || strcmp(argv[1], "-e") == 0) encode_stream();
    else if (strcmp(argv[1], "--decode") == 0 || strcmp(argv[1], "-d") == 0) decode_stream();
    return 0;
}