hash usage

The turbo::Hash<T, Tag> module provides a set of functions and classes for hashing. The library consists of the following modules:

  • turbo::Hash<T, Tag>, A concrete hash functor object. which you can use out of the box.

  • turbo::Mixer<R,Tag>, A concrete mixer functor object for small keys.

  • turbo::hash_engine, A hash engine for hashing a sequence of bytes. like xxhash, murmurhash, etc.

This library is designed to be used as a replacement for the standard library’s hash functions, like std::hash. The standard library’s hash functions are not guaranteed to be stable across different. It provides several advantages over them:

  • It can hash objects of almost any standard type, including std::pair, std::tuple, and most standard containers.

  • It can be extended to support user-defined types. Our goal is that if it makes sense to hash an object of type Foo, then Turbo::Hash<Foo> will just work. These extensions are easy to write and efficient to execute.

  • The underlying hash algorithm can be changed without modifying user code, which allows us to improve it over time. For example, to improve performance and to defend against some hash-flooding attacks.

  • Turbo can easily switch hash algorithms, such as city hash murmur3, xxhash, etc.

Using Turbo::Hash

The turbo::Hash module is designed to be used as a replacement for the standard library’s hash functions, like std::hash.

It can be used as a hash functor object, like this:

#include <turbo/hash/hash.h>
#include <iostream>
#include <string>

int main()
{
    std::unordered_map<std::string, int, turbo::Hash<std::string>> m;
    m["foo"] = 1;
    m["bar"] = 2;
    m["baz"] = 3;
    for (auto& p : m) {
        std::cout << p.first << " => " << p.second << '\n';
    }
}

The turbo::Hash module can also be used as a base class for user-defined hash functions. For example, if you have a class Foo that you want to hash, you can write:

#include <turbo/hash/hash.h>
#include <iostream>
#include <string>

struct Foo {
    int x;
    int y;
};

struct FooHash : turbo::Hash<Foo> {
    size_t operator()(const Foo& foo) const {
        return turbo::Hash<Foo>::operator()(foo.x, foo.y);
    }
};

int main()
{
    std::unordered_map<Foo, int, FooHash> m;
    m[Foo{1, 2}] = 1;
    m[Foo{3, 4}] = 2;
    m[Foo{5, 6}] = 3;
    for (auto& p : m) {
        std::cout << p.first.x << ',' << p.first.y << " => " << p.second << '\n';
    }
}

manage hash algorithm

The turbo::Hash module can easily switch hash algorithms, such as city hash murmur3, xxhash, etc.

#include <turbo/hash/hash.h>
#include <iostream>
#include <string>

int main()
{
    std::unordered_map<std::string, int, turbo::Hash<std::string, turbo::xx_hash_tag>> m;
    m["foo"] = 1;
    m["bar"] = 2;
    m["baz"] = 3;
    for (auto& p : m) {
        std::cout << p.first << " => " << p.second << '\n';
    }
}

you can list all supported hash algorithms by turbo::turbo::supported_hash_engines variable. and find the in turbo/hash/xx/xx.h and other files like **_hash_tag.

manage default hash algorithm

The turbo::Hash module can easily switch default hash algorithms, such as city hash murmur3, xxhash, etc.

The Macro TURBO_HASH_DEFAULT_ENGINE can be used to set the default hash algorithm. The default hash algorithm is turbo::bytes_hash_engine. You can set it to turbo::xx_hash_engine or other hash algorithm.

#define TURBO_HASH_DEFAULT_ENGINE turbo::xx_hash_engine
#include <turbo/hash/hash.h>
#include <iostream>
#include <string>

int main()
{
    std::unordered_map<std::string, int, turbo::Hash<std::string>> m;
    m["foo"] = 1;
    m["bar"] = 2;
    m["baz"] = 3;
    for (auto& p : m) {
        std::cout << p.first << " => " << p.second << '\n';
    }
}

make your defined type hashable

If you want to make your defined type hashable by turbo::Hash, you need to define a hash function hash_value in your type. The overload should combine state with the existing hash state (denoted as H in the template below), and your class must provide an equality operator.

#include <turbo/hash/hash.h>
#include <iostream>
#include <string>

struct Foo {
    int x;
    std::string y;

};

bool operator==(const Foo& lhs, const Foo& rhs) {
    return lhs.x == rhs.x && lhs.y == rhs.y;
}

template <typename H>
void hash_value(H& h, const Foo& foo) {
    return H::combine(std::move(h), m.v, m.str, m.b);
}

Note

The hash_value function should be defined in the same namespace as your type. If you can’t do that, you can define a specialization of turbo::hash instead.

compatible with std::hash

The turbo::Hash module is compatible with std::hash. You can use std::hash to hash your type, and use turbo::Hash to hash your type. The following example shows how to use std::hash to hash your type.

#include <turbo/hash/hash.h>
#include <iostream>
#include <string>

struct Foo {
    int x;
    std::string y;

};

bool operator==(const Foo& lhs, const Foo& rhs) {
    return lhs.x == rhs.x && lhs.y == rhs.y;
}

namespace std {
    template <>
    struct hash<Foo> {
        size_t operator()(const Foo& foo) const {
            return x + std::hash<std::string>()(y);
        }
    };
}

int main()
{
    std::unordered_map<Foo, int, turbo::Hash<Foo>> m;
    m[Foo{1, "foo"}] = 1;
    m[Foo{2, "bar"}] = 2;
    m[Foo{3, "baz"}] = 3;
    for (auto& p : m) {
        std::cout << p.first.x << ',' << p.first.y << " => " << p.second << '\n';
    }
}

This will also work. But we recommend that you use turbo::Hash to hash your type.

hash select

Sometimes you have defined hash_value for your type, but also defined std::hash for your type. at this case, turbo::Hash will use select hash_value to hash your type if you use turbo::Hash.

#include <turbo/hash/hash.h>
#include <iostream>
#include <string>

struct Bar {
    int x;
};

bool operator==(const Bar& lhs, const Bar& rhs) {
    return lhs.x == rhs.x;
}

template <typename H>
void hash_value(H& h, const Bar& bar) {
    return H::combine(std::move(h), bar.x);
}

namespace std {
    template <>
    struct hash<Bar> {
        size_t operator()(const Bar& bar) const {
            return bar.x;
        }
    };
}

int main()
{
    std::cout << turbo::Hash<Bar>()(Bar{1}) << std::endl;
    std::cout << std::hash<Bar>()(Bar{1}) << std::endl;
}

The output are different. turbo::Hash will use hash_value to hash your type. std::hash will use std::hash to hash your type.

Note

If you want to use std::hash to hash your type, you need to define std::hash in std namespace. and use std::hash to hash your type. If you want to use turbo::Hash to hash and use std defined algorithm to hash your type, you should not define hash_value for your type.

tools for hash

we provide some tools for hash. you can use it to hash your type. After you have installed turbo, there is a command line tool named turbo in your install directory. you can use it to hash your type. and debug some things.

> turbo hash --help
 Usage: turbo hash [OPTIONS] [ARGS]...
 ...
> turbo hash -s "hello world"
 +----------+---------------------+
 | engine   | bytes_hash          |
 +----------+---------------------+
 | original | hello world         |
 +----------+---------------------+
 | hash     | 7051363063550010326 |
 +----------+---------------------+
 > turbo hash -s "hello world" -e xx
 +----------+---------------------+
 | engine   | xx_hash             |
 +----------+---------------------+
 | original | hello world         |
 +----------+---------------------+
 | hash     | 8962184958574551130 |
 +----------+---------------------+

advanced usage