#ifndef _QS2_XXHASH_MODULE_H
#define _QS2_XXHASH_MODULE_H

#include "io_common.h"

#include <stdexcept>

// XXH3_createState() mallocs and can return NULL; matches the checked_zstd_*
// helpers in zstd_module.h
inline XXH3_state_t * checked_xxhash_state(XXH3_state_t * const state) {
    if(state == nullptr) {
        throw std::runtime_error("Failed to create xxHash state");
    }
    return state;
}

struct xxHashEnv {
    XXH3_state_t* state;
    xxHashEnv() : state(checked_xxhash_state(XXH3_createState())) {
        XXH3_64bits_reset(state);
    }
    ~xxHashEnv() {
        XXH3_freeState(state);
    }
    void reset() {
        XXH3_64bits_reset(state);
    }
    void update(const void * const buf, const uint64_t length) {
        XXH3_64bits_update(state, buf, length);
    }
    template <typename POD>
    void update(const POD & value) {
        XXH3_64bits_update(state, &value, sizeof(POD));
    }
    uint64_t digest() {
        uint64_t hash = XXH3_64bits_digest(state);
        // there is a 1/2^64 chance that the hash is zero
        // but we use it as a sentinel value so we need to make sure it's not zero
        if(hash == 0) {
            hash = 1;
        }
        return hash;
    }
};

// do nothing and return zero
struct noHashEnv {
    noHashEnv() {}
    void reset() {}
    void update(const void * const buf, const uint64_t length) {
        // do nothing
    }
    template <typename POD>
    void update(const POD & value) {
        // do nothing
    }
    uint64_t digest() {
        return 0;
    }
};

#endif
