The C port of the Hashids library.
A small C library to generate Youtube-like IDs from one or many numbers. Use Hashids when you don't want to expose your database IDs to the user.
Read more about the library at http://hashids.org/c/.
The C port has become (since version 1.1.0) a regular autotools-built library.
You can build it easily, provided you have autoconf
, automake
and other autotools
stuff installed:
./bootstrap
./configure
make
# make install-strip
The C usage differs marginally than JavaScript's or Ruby's in the matter that nothing is done (automagically) for you.
You'll have to manually allocate and free all memory you need for encoding/decoding.
The library itself will only allocate the hashids_t
structure (the handle) on its own.
If you want to roll your own allocator, look here.
The C version of Hashids works (presumably, and only) with unsigned long long
arguments.
Negative (signed) integers are, by design, treated as very big unsigned ones.
Note: The API has changed a bit since version 1.1.0
. hashids_t
is now a type alias of struct hashids_t
.
Note: Since version 1.2.0
hashids_decode
has a 4th parameter for bounds checking.
A small header (hashids.h
) is installed in ${PREFIX}/include
.
All further examples (unless explicitly shown otherwise) use empty salt for initializations.
#include <hashids.h>
hashids_t hashids;
hashids = hashids_init(NULL);
void
hashids_free(hashids_t *hashids);
The 'destructor'. This function disposes what you can allocate with the following 3 functions. You'll definetely need to call this function when you're done (un)hashing.
hashids_t *
hashids_init3(const char *salt, size_t min_hash_length, const char *alphabet);
The most common initializer.
Example:
hashids_t *hashids;
hashids = hashids_init3("this is my salt", 0, HASHIDS_DEFAULT_ALPHABET);
hashids_t *
hashids_init2(const char *salt, size_t min_hash_length);
The same as hashids_init3
but without the alpabet
parameter.
Will use HASHIDS_DEFAULT_ALPHABET
as alphabet.
hashids_t *
hashids_init(const char *salt);
The same as hashids_init2
but using 0
as min_hash_length
.
If you pass NULL
for salt
the HASHIDS_DEFAULT_SALT
will be used (currently ""
).
size_t
hashids_estimate_encoded_size(hashids_t *hashids, size_t numbers_count, unsigned long long *numbers);
Since we have no idea how much bytes an encoded ULONGLONG
will take, there's this (pessimistic) function:
Example:
unsigned long long numbers[] = {1ull, 2ull, 3ull, 4ull, 5ull};
size_t bytes_needed;
bytes_needed = hashids_estimate_encoded_size(hashids, sizeof(numbers) / sizeof(unsigned long long), numbers);
/* bytes_needed => 12 */
size_t
hashids_estimate_encoded_size_v(hashids_t *hashids, size_t numbers_count, ...);
The variadic variant of the hashids_estimate_encoded_size
function.
Example:
bytes_needed = hashids_estimate_encoded_size_v(hashids, 5, 1ull, 2ull, 3ull, 4ull, 5ull);
/* bytes_needed => 12 */
size_t
hashids_encode(hashids_t *hashids, char *buffer, size_t numbers_count, unsigned long long *numbers);
The common encoding encoder.
Encodes an array of ULONGLONG
numbers.
The returned value is the count of bytes encoded - if 0, something went south...
Example:
size_t bytes_encoded;
char hash[512];
unsigned long long numbers[] = {1ull};
bytes_encoded = hashids_encode(hashids, hash, sizeof(numbers) / sizeof(unsigned long long), numbers);
/* hash => "jR", bytes_encoded => 2 */
unsigned long long numbers2[] = {1ull, 2ull, 3ull, 4ull, 5ull};
bytes_encoded = hashids_encode(hashids, hash, sizeof(numbers2) / sizeof(unsigned long long), numbers2);
/* hash => "ADf9h9i0sQ", bytes_encoded => 10 */
size_t
hashids_encode_v(hashids_t *hashids, char *buffer, size_t numbers_count, ...);
The variadic variant of hashids_encode
.
Example:
bytes_encoded = hashids_encode_v(hashids, hash, 1, 1ull);
/* hash => "jR", bytes_encoded => 2 */
bytes_encoded = hashids_encode_v(hashids, hash, 5, 1ull, 2ull, 3ull, 4ull, 5ull);
/* hash => "ADf9h9i0sQ", bytes_encoded => 10 */
size_t
hashids_encode_one(hashids_t *hashids, char *buffer, unsigned long long number);
A shorthand function encoding just one ULONGLONG
.
Example:
bytes_encoded = hashids_encode_one(hashids, hash, 12345);
/* hash => "j0gW", bytes_encoded => 4 */
size_t
hashids_numbers_count(hashids_t *hashids, const char *str);
Returns how many ULONGLONG
s are encoded in a string.
If the function returns 0
, the hash is probably hashed with a different salt/alphabet.
It's up to you to allocate result * sizeof(unsigned long long)
memory yourself.
Example:
size_t numbers_count = hashids_numbers_count(hashids, "ADf9h9i0sQ");
/* numbers_count => 5 */
size_t
hashids_decode(hashids_t *hashids, const char *str, unsigned long long *numbers, size_t numbers_max);
The common decoding decoder.
Will decode a string hash to an array of ULONGLONG
s with a limit check.
If you pass NULL
as output or 0
as limit, the number count will be returned.
If the function returns 0
, the hash is probably hashed with a different salt/alphabet.
Example:
unsigned long long numbers[5];
result = hashids_decode(hashids, "QkoW1vt955nxCVVjZDt5VD2PTgBP72", numbers, 5);
/* numbers = {21979508, 35563591, 57543099, 93106690, 150649789}, result => 5 */
size_t
hashids_decode_unsafe(hashids_t *hashids, const char *str, unsigned long long *numbers);
The unsafe decoding decoder. Kept mostly for the ease of use.
Will decode a string hash to an array of ULONGLONG
s with no boundary checks.
Example:
unsigned long long numbers[5];
result = hashids_decode_unsafe(hashids, "QkoW1vt955nxCVVjZDt5VD2PTgBP72", numbers);
/* numbers = {21979508, 35563591, 57543099, 93106690, 150649789}, result => 5 */
A "safe" decoding decoder. Checks if the decoded numbers are encoded into the exact same string.
Example:
hashids_t hashids;
hashids = hashids_init3(HASHIDS_DEFAULT_SALT, 64, HASHIDS_DEFAULT_ALPHABET);
unsigned long long numbers[5];
result = hashids_decode_safe(hashids, "ky16BrZ5DYW4q3oOaQkoW1vt955nxCVVjZDt5VD2PTgBP72dKAMmwRnQXlEgGJ2p", numbers, 5);
/* result => 0, hashids_errno = HASHIDS_ERROR_INVALID_HASH (the correct last letter is "P") */
XXXXXX
size_t
hashids_encode_hex(hashids_t *hashids, char *buffer, const char *hex_str);
Encodes a hex string rather than a number.
Example:
bytes_encoded = hashids_encode_hex(hashids, hash, "C0FFEE");
/* hash => "k7AVov", result => 6 */
size_t
hashids_decode_hex(hashids_t *hashids, char *str, char *output);
Decodes a hash to a hex string rather than to a number.
Example:
char str[18]; /* sizeof(unsigned long long) * 2 + 2 */
result = hashids_decode_hex(hashids, "k7AVov", str);
/* str => "C0FFEE", result => 1 */
The library uses its own extern int hashids_errno
for error handling, thus it does not mangle the system-wide errno
.
hashids_errno
definitions:
CONSTANT | Code | Description |
---|---|---|
HASHIDS_ERROR_OK | 0 | Happy, do nothing! |
HASHIDS_ERROR_ALLOC | -1 | Memory allocation error |
HASHIDS_ERROR_ALPHABET_LENGTH | -2 | The alphabet is shorter than HASHIDS_MIN_ALPHABET_LENGTH (16 chars) |
HASHIDS_ERROR_ALPHABET_SPACE | -3 | The alphabet contains a space (tab NOT included) |
HASHIDS_ERROR_INVALID_HASH | -4 | An invalid hash has been passed to hashids_decode() |
HASHIDS_ERROR_INVALID_NUMBER | -5 | An invalid hex string has been passed to hashids_encode_hex() |
Since v1.1.5
hashids_errno
is thread-safe.
Since the hashids_init*
(and some of the *_v
) functions are memory-dependent, this library is trying to be allocator-agnostic.
If you roll your own allocator, or for some reason you don't like external libraries calling malloc
/calloc
, you can redefine the memory handling functions:
void *(*_hashids_alloc)(size_t size) = hashids_alloc_f;
void (*_hashids_free)(void *ptr) = hashids_free_f;
Please note that the hashids_init*
functions (most likely) rely on zero-initialized memory.
The library also has a command line utility providing all the functionality in the shell.
The usage is not much different. If you have any trouble, just run the command without any arguments and read the help lines.
./hashids 1
# => jR
./hashids -d jR
# => 1
You can also consult the source (main.c
) for further help on using hashids
.
Fork the repo, then send me a pull request. Bonus points for topic branches.