Skip to content

tzvetkoff/hashids.c

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

63 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

hashids

The C port of the Hashids library.

Some Documentation

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/.

Building

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

Usage

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.

API

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.

Preamble

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

hashids_free

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_init3

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_init2

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_init

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 "").

hashids_estimate_encoded_size

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 */

hashids_estimate_encoded_size_v

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 */

hashids_encode

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 */

hashids_encode_v

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 */

hashids_encode_one

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 */

hashids_numbers_count

size_t
hashids_numbers_count(hashids_t *hashids, const char *str);

Returns how many ULONGLONGs 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 */

hashids_decode

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 ULONGLONGs 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 */

hashids_decode_unsafe

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 ULONGLONGs 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 */

hashids_decode_safe

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

hashids_encode_hex

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 */

hashids_decode_hex

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 */

Error checking

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.

Memory allocation

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.

CLI

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.

Issues

Oh, no!

Hacking

Fork the repo, then send me a pull request. Bonus points for topic branches.