/*

reinhard@finalmedia.de
20260919
PUBLIC DOMAIN

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

using simple, modern and fast 64bit non-cryptographic xxHash64

*/

#include <unistd.h>

#define BUFSIZE 65536

typedef unsigned long long uint64;

// Die 5 speziellen 64-Bit-Konstanten von xxHash
#define PRIME64_1 0x9E3779B185EBCA87ULL
#define PRIME64_2 0x85EBCA77CC90A51DULL
#define PRIME64_3 0xC2B2AE3D27D4EB4FUL
#define PRIME64_4 0x27D4EB2F165667C5ULL
#define PRIME64_5 0x165667B19E3779F9ULL

// Bitweise Links-Rotation (64-Bit)
static inline uint64 xxh_rotl64(uint64 value, int count) {
    return (value << count) | (value >> (64 - count));
}

static inline uint64 xxh64_round(uint64 acc, uint64 input) {
    acc += input * PRIME64_2;
    acc = xxh_rotl64(acc, 31);
    acc *= PRIME64_1;
    return acc;
}

static inline uint64 xxh64_mergeRound(uint64 acc, uint64 val) {
    val = xxh64_round(0, val);
    acc ^= val;
    acc = acc * PRIME64_1 + PRIME64_4;
    return acc;
}

uint64 xxhash64(const char *s, int len) {
    const unsigned char *p = (const unsigned char *)s;
    const unsigned char *const bEnd = p + len;
    uint64 seed = 0;
    uint64 h64;

    // SCHRITT 1: Große Blöcke (>= 32 Byte) in 4 parallelen Tracks verarbeiten
    if (len >= 32) {
        const unsigned char *const limit = bEnd - 32;
        uint64 v1 = seed + PRIME64_1 + PRIME64_2;
        uint64 v2 = seed + PRIME64_2;
        uint64 v3 = seed + 0;
        uint64 v4 = seed - PRIME64_1;

        do {
            uint64 block;

            // Djb-Stil: Manuelles unaligned-sicheres Zusammenbauen eines 64-Bit Integers
            block = (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);
            v1 = xxh64_round(v1, block); p += 8;

            block = (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);
            v2 = xxh64_round(v2, block); p += 8;

            block = (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);
            v3 = xxh64_round(v3, block); p += 8;

            block = (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);
            v4 = xxh64_round(v4, block); p += 8;

        } while (p <= limit);

        h64 = xxh_rotl64(v1, 1) + xxh_rotl64(v2, 7) + xxh_rotl64(v3, 12) + xxh_rotl64(v4, 18);
        h64 = xxh64_mergeRound(h64, v1);
        h64 = xxh64_mergeRound(h64, v2);
        h64 = xxh64_mergeRound(h64, v3);
        h64 = xxh64_mergeRound(h64, v4);
    } else {
        h64 = seed + PRIME64_5;
    }

    h64 += (uint64)len;

    // SCHRITT 2: Reste in 8-Byte Schritten verarbeiten
    while (p + 8 <= bEnd) {
        uint64 val = (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);
        uint64 k1 = xxh64_round(0, val);
        h64 ^= k1;
        h64 = xxh_rotl64(h64, 27) * PRIME64_1 + PRIME64_4;
        p += 8;
    }

    // SCHRITT 3: Reste in 4-Byte Schritten verarbeiten
    if (p + 4 <= bEnd) {
        unsigned int val = (unsigned int)p[0] | ((unsigned int)p[1] << 8) | ((unsigned int)p[2] << 16) | ((unsigned int)p[3] << 24);
        h64 ^= (uint64)val * PRIME64_1;
        h64 = xxh_rotl64(h64, 23) * PRIME64_2 + PRIME64_3;
        p += 4;
    }

    // SCHRITT 4: Letzte einzelne Bytes verarbeiten (Klassischer djb-Loop)
    while (p < bEnd) {
        h64 ^= (*p) * PRIME64_5;
        h64 = xxh_rotl64(h64, 11) * PRIME64_1;
        p++;
    }

    // SCHRITT 5: Finaler Avalanche-Mix
    h64 ^= h64 >> 33;
    h64 *= PRIME64_2;
    h64 ^= h64 >> 29;
    h64 *= PRIME64_3;
    h64 ^= h64 >> 32;

    return h64;
}

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;
    }
}

void put_hex64_buffered(uint64 n) {
    char buf[16];
    static const char hex[] = "0123456789abcdef";
    int i = 16;
    while (i > 0) {
        buf[--i] = hex[n & 15];
        n >>= 4;
    }
    put_buffered(buf, 16);
}

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_hex64_buffered(xxhash64(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_hex64_buffered(xxhash64(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);
}


