104 lines
2.8 KiB
C
104 lines
2.8 KiB
C
|
|
/*
|
||
|
|
* aksl_strhash_djb2 -- TODO.md section 1.6.
|
||
|
|
*
|
||
|
|
* The expected values are the canonical djb2 ones: h = 5381, then
|
||
|
|
* h = h * 33 + byte for each of len bytes, truncated to 32 bits. They were
|
||
|
|
* computed independently of this implementation.
|
||
|
|
*
|
||
|
|
* Every vector here is 7-bit ASCII, where signed and unsigned char agree. The
|
||
|
|
* high-bit case ("\xff\xfe") is the sign-extension defect in TODO.md 2.2.6 and
|
||
|
|
* is left for a test that can be registered as a known failure.
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include "aksl_capture.h"
|
||
|
|
|
||
|
|
static int test_empty_string_is_the_djb2_seed(void)
|
||
|
|
{
|
||
|
|
uint32_t h = 0;
|
||
|
|
|
||
|
|
AKSL_CHECK_OK(aksl_strhash_djb2("", 0, &h));
|
||
|
|
AKSL_CHECK(h == 5381);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* len drives the loop, so a zero length ignores the contents entirely. */
|
||
|
|
static int test_zero_length_ignores_the_buffer(void)
|
||
|
|
{
|
||
|
|
char buf[] = "ignored";
|
||
|
|
uint32_t h = 0;
|
||
|
|
|
||
|
|
AKSL_CHECK_OK(aksl_strhash_djb2(buf, 0, &h));
|
||
|
|
AKSL_CHECK(h == 5381);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int test_known_answer_vectors(void)
|
||
|
|
{
|
||
|
|
char hello[] = "hello";
|
||
|
|
char libname[] = "libakstdlib";
|
||
|
|
uint32_t h = 0;
|
||
|
|
|
||
|
|
AKSL_CHECK_OK(aksl_strhash_djb2(hello, 5, &h));
|
||
|
|
AKSL_CHECK(h == 261238937u);
|
||
|
|
|
||
|
|
AKSL_CHECK_OK(aksl_strhash_djb2(libname, 11, &h));
|
||
|
|
AKSL_CHECK(h == 884285482u);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* The function is length-driven, not NUL-driven: an embedded NUL is hashed. */
|
||
|
|
static int test_embedded_nul_is_hashed(void)
|
||
|
|
{
|
||
|
|
char buf[3] = { 'a', '\0', 'b' };
|
||
|
|
uint32_t whole = 0;
|
||
|
|
uint32_t prefix = 0;
|
||
|
|
|
||
|
|
AKSL_CHECK_OK(aksl_strhash_djb2(buf, sizeof(buf), &whole));
|
||
|
|
AKSL_CHECK(whole == 193482728u);
|
||
|
|
|
||
|
|
/* Stopping at the NUL would give the one-byte hash instead. */
|
||
|
|
AKSL_CHECK_OK(aksl_strhash_djb2(buf, 1, &prefix));
|
||
|
|
AKSL_CHECK(prefix != whole);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int test_hash_is_stable_across_calls(void)
|
||
|
|
{
|
||
|
|
char buf[] = "repeatable";
|
||
|
|
uint32_t first = 0;
|
||
|
|
uint32_t second = 0;
|
||
|
|
|
||
|
|
AKSL_CHECK_OK(aksl_strhash_djb2(buf, sizeof(buf) - 1, &first));
|
||
|
|
AKSL_CHECK_OK(aksl_strhash_djb2(buf, sizeof(buf) - 1, &second));
|
||
|
|
AKSL_CHECK(first == second);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int test_rejects_null_arguments(void)
|
||
|
|
{
|
||
|
|
char buf[] = "x";
|
||
|
|
uint32_t h = 0;
|
||
|
|
|
||
|
|
AKSL_CHECK_STATUS_MSG_CONTAINS(aksl_strhash_djb2(NULL, 1, &h),
|
||
|
|
AKERR_NULLPOINTER, "str");
|
||
|
|
AKSL_CHECK_STATUS_MSG_CONTAINS(aksl_strhash_djb2(buf, 1, NULL),
|
||
|
|
AKERR_NULLPOINTER, "hashval");
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
int main(void)
|
||
|
|
{
|
||
|
|
int failures = 0;
|
||
|
|
|
||
|
|
akerr_init();
|
||
|
|
|
||
|
|
AKSL_RUN(failures, test_empty_string_is_the_djb2_seed);
|
||
|
|
AKSL_RUN(failures, test_zero_length_ignores_the_buffer);
|
||
|
|
AKSL_RUN(failures, test_known_answer_vectors);
|
||
|
|
AKSL_RUN(failures, test_embedded_nul_is_hashed);
|
||
|
|
AKSL_RUN(failures, test_hash_is_stable_across_calls);
|
||
|
|
AKSL_RUN(failures, test_rejects_null_arguments);
|
||
|
|
|
||
|
|
AKSL_REPORT(failures);
|
||
|
|
}
|