/*

reinhard@finalmedia.de
20260915
PUBLIC DOMAIN

musl-gcc -O3 -static-pie -fPIE -fstack-protector-strong -o hashline128 hashline128.c

using fast 128bit non-cryptographic MurmurHash3 2x64

*/

#include <unistd.h>

#define BUFSIZE 65536

typedef unsigned long long uint64;

// Ein 128-Bit Container
typedef struct {
    uint64 h1;
    uint64 h2;
} uint128;

// Bitweise Links-Rotation (64-Bit)
static inline uint64 rotl64(uint64 x, int r) {
    return (x << r) | (x >> (64 - r));
}

// 128-Bit MurmurHash3 (x64-Variante) optimiert im djb-Stil
uint128 murmur3_128(const char *s, int len) {
    const unsigned char *p = (const unsigned char *)s;
    const int nblocks = len / 16;

    uint64 h1 = 0; // Seed
    uint64 h2 = 0; // Seed

    const uint64 c1 = 0x87c37b91114253d5ULL;
    const uint64 c2 = 0x4cf5ad432745937fULL;

    // SCHRITT 1: 16-Byte Blocks verarbeiten im djb-Stil via Bit-Shifts
    int i;
    for (i = 0; i < nblocks; i++) {
        // Erste 8 Bytes auslesen
        uint64 k1 = (uint64)p[0] | ((uint64)p[1] << 8) | ((uint64)p[2] << 16) | ((uint64)p[3] << 24) |
                    ((uint64)p[4] << 32) | ((uint64)p[5] << 40) | ((uint64)p[6] << 48) | ((uint64)p[7] << 56);
        p += 8;

        // Zweite 8 Bytes auslesen
        uint64 k2 = (uint64)p[0] | ((uint64)p[1] << 8) | ((uint64)p[2] << 16) | ((uint64)p[3] << 24) |
                    ((uint64)p[4] << 32) | ((uint64)p[5] << 40) | ((uint64)p[6] << 48) | ((uint64)p[7] << 56);
        p += 8;

        k1 *= c1; k1 = rotl64(k1, 31); k1 *= c2; h1 ^= k1;
        h1 = rotl64(h1, 27); h1 += h2; h1 = h1 * 5 + 0x52dce729;

        k2 *= c2; k2 = rotl64(k2, 33); k2 *= c1; h2 ^= k2;
        h2 = rotl64(h2, 31); h2 += h1; h2 = h2 * 5 + 0x38495ab5;
    }

    // SCHRITT 2: Reste verarbeiten (0 bis 15 Bytes)
    uint64 k1 = 0;
    uint64 k2 = 0;
    int tail = len & 15;

    switch (tail) {
        case 15: k2 ^= (uint64)p[14] << 48;
        case 14: k2 ^= (uint64)p[13] << 40;
        case 13: k2 ^= (uint64)p[12] << 32;
        case 12: k2 ^= (uint64)p[11] << 24;
        case 11: k2 ^= (uint64)p[10] << 16;
        case 10: k2 ^= (uint64)p[9] << 8;
        case  9: k2 ^= (uint64)p[8];
                 k2 *= c2; k2 = rotl64(k2, 33); k2 *= c1; h2 ^= k2;

        case  8: k1 ^= (uint64)p[7] << 56;
        case  7: k1 ^= (uint64)p[6] << 48;
        case  6: k1 ^= (uint64)p[5] << 40;
        case  5: k1 ^= (uint64)p[4] << 32;
        case  4: k1 ^= (uint64)p[3] << 24;
        case  3: k1 ^= (uint64)p[2] << 16;
        case  2: k1 ^= (uint64)p[1] << 8;
        case  1: k1 ^= (uint64)p[0];
                 k1 *= c1; k1 = rotl64(k1, 31); k1 *= c2; h1 ^= k1;
    };

    // SCHRITT 3: Finale "Avalanche"-Mischung
    h1 ^= len; h2 ^= len;

    h1 += h2; h2 += h1;

    h1 ^= h1 >> 33; h1 *= 0xff51afd7ed558ccdULL; h1 ^= h1 >> 33; h1 *= 0xc4ceb9fe1a85ec53ULL; h1 ^= h1 >> 33;
    h2 ^= h2 >> 33; h2 *= 0xff51afd7ed558ccdULL; h2 ^= h2 >> 33; h2 *= 0xc4ceb9fe1a85ec53ULL; h2 ^= h2 >> 33;

    h1 += h2; h2 += h1;

    uint128 result;
    result.h1 = h1;
    result.h2 = h2;
    return result;
}

char out_buf[BUFSIZE];
int out_pos = 0;

void flush_out(void) {
    if (out_pos == 0) return;
    int pos = 0;
    while (pos < out_pos) {
        int w = write(1, out_buf + pos, out_pos - pos);
        if (w <= 0) _exit(111);
        pos += w;
    }
    out_pos = 0;
}

void put_buffered(const char *s, int len) {
    while (len > 0) {
        int space = BUFSIZE - out_pos;
        if (space == 0) {
            flush_out();
            space = BUFSIZE;
        }
        int chunk = (len < space) ? len : space;
        int i;
        for (i = 0; i < chunk; i++) {
            out_buf[out_pos++] = *s++;
        }
        len -= chunk;
    }
}

// Gibt die 32 Hex-Zeichen des 128-Bit Hashes aus
void put_hex128_buffered(uint128 n) {
    char buf[32];
    static const char hex[] = "0123456789abcdef";
    int i = 32;

    // Untere 64-Bit verarbeiten
    uint64 part2 = n.h2;
    while (i > 16) {
        buf[--i] = hex[part2 & 15];
        part2 >>= 4;
    }

    // Obere 64-Bit verarbeiten
    uint64 part1 = n.h1;
    while (i > 0) {
        buf[--i] = hex[part1 & 15];
        part1 >>= 4;
    }

    put_buffered(buf, 32);
}

int main(void) {
    char in_buf[BUFSIZE];
    int rlen = 0;
    int pos = 0;

    while (1) {
        if (pos >= rlen) {
            int n = read(0, in_buf, BUFSIZE);
            if (n <= 0) break;
            rlen = n;
            pos = 0;
        }

        int start = pos;
        while (pos < rlen && in_buf[pos] != '\n') {
            pos++;
        }

        if (pos < rlen && in_buf[pos] == '\n') {
            pos++;
            put_hex128_buffered(murmur3_128(in_buf + start, pos - start));
            put_buffered(" ", 1);
            put_buffered(in_buf + start, pos - start);
        } else {
            int left = rlen - start;
            if (left > 0 && start > 0) {
                int i;
                for (i = 0; i < left; i++) {
                    in_buf[i] = in_buf[start + i];
                }
            }
            int n = read(0, in_buf + left, BUFSIZE - left);
            if (n <= 0) {
                if (left > 0) {
                    put_hex128_buffered(murmur3_128(in_buf, left));
                    put_buffered(" ", 1);
                    put_buffered(in_buf, left);
                    if (in_buf[left - 1] != '\n') put_buffered("\n", 1);
                }
                break;
            }
            rlen = left + n;
            pos = 0;
        }
    }

    flush_out();
    _exit(0);
}


