Tests and benchmark for isc_ascii
The test is to verify basic functionality. The benchmark compares a number of alternative tolower() implementations on large and small strings.
This commit is contained in:
@@ -1579,9 +1579,10 @@ AC_CONFIG_FILES([doc/Makefile
|
||||
doc/man/Makefile
|
||||
doc/misc/Makefile])
|
||||
|
||||
# Unit Tests
|
||||
# Unit tests and benchmarks
|
||||
|
||||
AC_CONFIG_FILES([tests/Makefile
|
||||
tests/bench/Makefile
|
||||
tests/isc/Makefile
|
||||
tests/dns/Makefile
|
||||
tests/ns/Makefile
|
||||
|
||||
@@ -12,5 +12,6 @@ LDADD += \
|
||||
$(LIBDNS_LIBS) \
|
||||
$(LIBNS_LIBS)
|
||||
|
||||
SUBDIRS = libtest isc dns ns isccfg irs
|
||||
SUBDIRS = libtest isc dns ns isccfg irs bench
|
||||
|
||||
check_PROGRAMS =
|
||||
|
||||
1
tests/bench/.gitignore
vendored
Normal file
1
tests/bench/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
ascii
|
||||
10
tests/bench/Makefile.am
Normal file
10
tests/bench/Makefile.am
Normal file
@@ -0,0 +1,10 @@
|
||||
include $(top_srcdir)/Makefile.top
|
||||
|
||||
AM_CPPFLAGS += \
|
||||
$(LIBISC_CFLAGS)
|
||||
|
||||
LDADD += \
|
||||
$(LIBISC_LIBS)
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
ascii
|
||||
223
tests/bench/ascii.c
Normal file
223
tests/bench/ascii.c
Normal file
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See the COPYRIGHT file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <isc/ascii.h>
|
||||
#include <isc/random.h>
|
||||
#include <isc/time.h>
|
||||
|
||||
#define SIZE (1024 * 1024)
|
||||
|
||||
typedef void
|
||||
copy_fn(void *a, void *b, unsigned len);
|
||||
|
||||
static void
|
||||
time_it(copy_fn *copier, void *a, void *b, const char *name) {
|
||||
isc_time_t start;
|
||||
isc_time_now_hires(&start);
|
||||
|
||||
copier(a, b, SIZE);
|
||||
|
||||
isc_time_t finish;
|
||||
isc_time_now_hires(&finish);
|
||||
|
||||
uint64_t microseconds = isc_time_microdiff(&finish, &start);
|
||||
printf("%f for %s\n", (double)microseconds / 1000000.0, name);
|
||||
}
|
||||
|
||||
static void
|
||||
copy_raw(void *a, void *b, unsigned size) {
|
||||
memmove(a, b, size);
|
||||
}
|
||||
|
||||
static void
|
||||
copy_toupper(void *va, void *vb, unsigned size) {
|
||||
uint8_t *a = va, *b = vb;
|
||||
while (size-- > 0) {
|
||||
*a++ = isc_ascii_toupper(*b++);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
copy_tolower8(void *a, void *b, unsigned size) {
|
||||
isc_ascii_lowercopy(a, b, size);
|
||||
}
|
||||
|
||||
#define TOLOWER(c) ((c) + ('a' - 'A') * (((c) >= 'A') ^ ((c) > 'Z')))
|
||||
|
||||
static void
|
||||
copy_tolower1(void *va, void *vb, unsigned size) {
|
||||
for (uint8_t *a = va, *b = vb; size-- > 0; a++, b++) {
|
||||
*a = TOLOWER(*b);
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
cmp_tolower1(void *va, void *vb, unsigned size) {
|
||||
for (uint8_t *a = va, *b = vb; size-- > 0; a++, b++) {
|
||||
if (TOLOWER(*a) != TOLOWER(*b)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool oldskool_result;
|
||||
|
||||
static void
|
||||
cmp_oldskool(void *va, void *vb, unsigned size) {
|
||||
uint8_t *a = va, *b = vb, c;
|
||||
|
||||
while (size > 3) {
|
||||
c = isc_ascii_tolower(a[0]);
|
||||
if (c != isc_ascii_tolower(b[0])) {
|
||||
goto diff;
|
||||
}
|
||||
c = isc_ascii_tolower(a[1]);
|
||||
if (c != isc_ascii_tolower(b[1])) {
|
||||
goto diff;
|
||||
}
|
||||
c = isc_ascii_tolower(a[2]);
|
||||
if (c != isc_ascii_tolower(b[2])) {
|
||||
goto diff;
|
||||
}
|
||||
c = isc_ascii_tolower(a[3]);
|
||||
if (c != isc_ascii_tolower(b[3])) {
|
||||
goto diff;
|
||||
}
|
||||
size -= 4;
|
||||
a += 4;
|
||||
b += 4;
|
||||
}
|
||||
while (size-- > 0) {
|
||||
c = isc_ascii_tolower(*a++);
|
||||
if (c != isc_ascii_tolower(*b++)) {
|
||||
goto diff;
|
||||
}
|
||||
}
|
||||
oldskool_result = true;
|
||||
return;
|
||||
diff:
|
||||
oldskool_result = false;
|
||||
return;
|
||||
}
|
||||
|
||||
static bool tolower1_result;
|
||||
|
||||
static void
|
||||
vcmp_tolower1(void *a, void *b, unsigned size) {
|
||||
tolower1_result = cmp_tolower1(a, b, size);
|
||||
}
|
||||
|
||||
static bool swar_result;
|
||||
|
||||
static void
|
||||
cmp_swar(void *a, void *b, unsigned size) {
|
||||
swar_result = isc_ascii_lowerequal(a, b, size);
|
||||
}
|
||||
|
||||
static bool chunk_result;
|
||||
static unsigned chunk_size;
|
||||
|
||||
static void
|
||||
cmp_chunks1(void *va, void *vb, unsigned size) {
|
||||
uint8_t *a = va, *b = vb;
|
||||
|
||||
chunk_result = false;
|
||||
while (size >= chunk_size) {
|
||||
if (!cmp_tolower1(a, b, chunk_size)) {
|
||||
return;
|
||||
}
|
||||
size -= chunk_size;
|
||||
a += chunk_size;
|
||||
b += chunk_size;
|
||||
}
|
||||
chunk_result = cmp_tolower1(a, b, size);
|
||||
}
|
||||
|
||||
static void
|
||||
cmp_chunks8(void *va, void *vb, unsigned size) {
|
||||
uint8_t *a = va, *b = vb;
|
||||
|
||||
while (size >= chunk_size) {
|
||||
if (!isc_ascii_lowerequal(a, b, chunk_size)) {
|
||||
goto diff;
|
||||
}
|
||||
size -= chunk_size;
|
||||
a += chunk_size;
|
||||
b += chunk_size;
|
||||
}
|
||||
chunk_result = isc_ascii_lowerequal(a, b, size);
|
||||
return;
|
||||
diff:
|
||||
chunk_result = false;
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
cmp_oldchunks(void *va, void *vb, unsigned size) {
|
||||
uint8_t *a = va, *b = vb;
|
||||
|
||||
while (size >= chunk_size) {
|
||||
cmp_oldskool(a, b, chunk_size);
|
||||
if (!oldskool_result) {
|
||||
return;
|
||||
}
|
||||
size -= chunk_size;
|
||||
a += chunk_size;
|
||||
b += chunk_size;
|
||||
}
|
||||
cmp_oldskool(a, b, size);
|
||||
}
|
||||
|
||||
int
|
||||
main(void) {
|
||||
static uint8_t bytes[SIZE];
|
||||
|
||||
isc_random_buf(bytes, SIZE);
|
||||
|
||||
static uint8_t raw_dest[SIZE];
|
||||
time_it(copy_raw, raw_dest, bytes, "memmove");
|
||||
|
||||
static uint8_t toupper_dest[SIZE];
|
||||
time_it(copy_toupper, toupper_dest, bytes, "toupper");
|
||||
|
||||
static uint8_t tolower1_dest[SIZE];
|
||||
time_it(copy_tolower1, tolower1_dest, bytes, "tolower1");
|
||||
|
||||
static uint8_t tolower8_dest[SIZE];
|
||||
time_it(copy_tolower8, tolower8_dest, bytes, "tolower8");
|
||||
|
||||
time_it(cmp_oldskool, toupper_dest, tolower1_dest, "oldskool");
|
||||
printf("-> %s\n", oldskool_result ? "same" : "WAT");
|
||||
|
||||
time_it(vcmp_tolower1, tolower1_dest, tolower8_dest, "tolower1");
|
||||
printf("-> %s\n", tolower1_result ? "same" : "WAT");
|
||||
|
||||
time_it(cmp_swar, toupper_dest, tolower8_dest, "swar");
|
||||
printf("-> %s\n", swar_result ? "same" : "WAT");
|
||||
|
||||
for (chunk_size = 3; chunk_size <= 15; chunk_size += 2) {
|
||||
time_it(cmp_chunks1, toupper_dest, raw_dest, "chunks1");
|
||||
printf("%u -> %s\n", chunk_size, chunk_result ? "same" : "WAT");
|
||||
time_it(cmp_chunks8, toupper_dest, raw_dest, "chunks8");
|
||||
printf("%u -> %s\n", chunk_size, chunk_result ? "same" : "WAT");
|
||||
time_it(cmp_oldchunks, toupper_dest, raw_dest, "oldchunks");
|
||||
printf("%u -> %s\n", chunk_size,
|
||||
oldskool_result ? "same" : "WAT");
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ LDADD += \
|
||||
$(LIBUV_LIBS)
|
||||
|
||||
check_PROGRAMS = \
|
||||
ascii_test \
|
||||
aes_test \
|
||||
async_test \
|
||||
buffer_test \
|
||||
|
||||
179
tests/isc/ascii_test.c
Normal file
179
tests/isc/ascii_test.c
Normal file
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See the COPYRIGHT file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <sched.h> /* IWYU pragma: keep */
|
||||
#include <setjmp.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define UNIT_TESTING
|
||||
#include <cmocka.h>
|
||||
|
||||
#include <isc/ascii.h>
|
||||
|
||||
#include <tests/isc.h>
|
||||
|
||||
const char *same[][2] = {
|
||||
{
|
||||
"AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz",
|
||||
"aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz",
|
||||
},
|
||||
{
|
||||
"aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz",
|
||||
"AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ",
|
||||
},
|
||||
{
|
||||
"AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ",
|
||||
"aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz",
|
||||
},
|
||||
{
|
||||
"aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ",
|
||||
"aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz",
|
||||
},
|
||||
{
|
||||
"aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVxXyYzZ",
|
||||
"aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvxxyyzz",
|
||||
},
|
||||
{
|
||||
"WwW.ExAmPlE.OrG",
|
||||
"wWw.eXaMpLe.oRg",
|
||||
},
|
||||
{
|
||||
"_SIP.tcp.example.org",
|
||||
"_sip.TCP.example.org",
|
||||
},
|
||||
{
|
||||
"bind-USERS.lists.example.org",
|
||||
"bind-users.lists.example.org",
|
||||
},
|
||||
{
|
||||
"a0123456789.example.org",
|
||||
"A0123456789.example.org",
|
||||
},
|
||||
{
|
||||
"\\000.example.org",
|
||||
"\\000.example.org",
|
||||
},
|
||||
{
|
||||
"wWw.\\000.isc.org",
|
||||
"www.\\000.isc.org",
|
||||
},
|
||||
{
|
||||
"\255.example.org",
|
||||
"\255.example.ORG",
|
||||
}
|
||||
};
|
||||
|
||||
struct {
|
||||
const char *a, *b;
|
||||
int cmp;
|
||||
} diff[] = {
|
||||
{ "foo", "bar", +1 },
|
||||
{ "bar", "foo", -1 },
|
||||
{ "foosuffix", "barsuffix", +1 },
|
||||
{ "barsuffix", "foosuffix", -1 },
|
||||
{ "prefixfoo", "prefixbar", +1 },
|
||||
{ "prefixbar", "prefixfoo", -1 },
|
||||
};
|
||||
|
||||
ISC_RUN_TEST_IMPL(upperlower) {
|
||||
UNUSED(state);
|
||||
|
||||
for (size_t n = 0; n < ARRAY_SIZE(same); n++) {
|
||||
const char *a = same[n][0];
|
||||
const char *b = same[n][1];
|
||||
for (size_t i = 0; a[i] != '\0' && b[i] != '\0'; i++) {
|
||||
assert_true(isc_ascii_toupper(a[i]) == (uint8_t)a[i] ||
|
||||
isc_ascii_tolower(a[i]) == (uint8_t)a[i]);
|
||||
assert_true(isc_ascii_toupper(b[i]) == (uint8_t)b[i] ||
|
||||
isc_ascii_tolower(b[i]) == (uint8_t)b[i]);
|
||||
assert_true(isc_ascii_toupper(a[i]) ==
|
||||
isc_ascii_toupper(b[i]));
|
||||
assert_true(isc_ascii_tolower(a[i]) ==
|
||||
isc_ascii_tolower(b[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ISC_RUN_TEST_IMPL(lowerequal) {
|
||||
for (size_t n = 0; n < ARRAY_SIZE(same); n++) {
|
||||
const uint8_t *a = (void *)same[n][0];
|
||||
const uint8_t *b = (void *)same[n][1];
|
||||
unsigned len = (unsigned)strlen(same[n][0]);
|
||||
assert_true(isc_ascii_lowerequal(a, b, len));
|
||||
}
|
||||
for (size_t n = 0; n < ARRAY_SIZE(diff); n++) {
|
||||
const uint8_t *a = (void *)diff[n].a;
|
||||
const uint8_t *b = (void *)diff[n].b;
|
||||
unsigned len = (unsigned)strlen(diff[n].a);
|
||||
assert_true(!isc_ascii_lowerequal(a, b, len));
|
||||
}
|
||||
}
|
||||
|
||||
ISC_RUN_TEST_IMPL(lowercmp) {
|
||||
for (size_t n = 0; n < ARRAY_SIZE(same); n++) {
|
||||
const uint8_t *a = (void *)same[n][0];
|
||||
const uint8_t *b = (void *)same[n][1];
|
||||
unsigned len = (unsigned)strlen(same[n][0]);
|
||||
assert_true(isc_ascii_lowercmp(a, b, len) == 0);
|
||||
}
|
||||
for (size_t n = 0; n < ARRAY_SIZE(diff); n++) {
|
||||
const uint8_t *a = (void *)diff[n].a;
|
||||
const uint8_t *b = (void *)diff[n].b;
|
||||
unsigned len = (unsigned)strlen(diff[n].a);
|
||||
assert_true(isc_ascii_lowercmp(a, b, len) == diff[n].cmp);
|
||||
}
|
||||
}
|
||||
|
||||
ISC_RUN_TEST_IMPL(exhaustive) {
|
||||
for (uint64_t ab = 0; ab < (1 << 16); ab++) {
|
||||
uint8_t a = ab >> 8;
|
||||
uint8_t b = ab & 0xFF;
|
||||
uint64_t abc = tolower(a) << 8 | tolower(b);
|
||||
uint64_t abi = isc_ascii_tolower(a) << 8 | isc_ascii_tolower(b);
|
||||
uint64_t ab1 = isc__ascii_tolower1(a) << 8 |
|
||||
isc__ascii_tolower1(b);
|
||||
uint64_t ab8 = isc__ascii_tolower8(ab);
|
||||
/* each byte individually matches ctype.h */
|
||||
assert_int_equal(tolower(a), isc_ascii_tolower(a));
|
||||
assert_int_equal(tolower(a), isc__ascii_tolower1(a));
|
||||
assert_int_equal(tolower(a), isc__ascii_tolower8(a));
|
||||
assert_int_equal(tolower(b), isc_ascii_tolower(b));
|
||||
assert_int_equal(tolower(b), isc__ascii_tolower1(b));
|
||||
assert_int_equal(tolower(b), isc__ascii_tolower8(b));
|
||||
/* two lanes of SWAR match other implementations */
|
||||
assert_int_equal(ab8, abc);
|
||||
assert_int_equal(ab8, abi);
|
||||
assert_int_equal(ab8, ab1);
|
||||
/* check lack of overflow */
|
||||
assert_int_equal(ab8 >> 16, 0);
|
||||
/* all lanes of SWAR work */
|
||||
assert_int_equal(isc__ascii_tolower8(ab << 8), abc << 8);
|
||||
assert_int_equal(isc__ascii_tolower8(ab << 16), abc << 16);
|
||||
assert_int_equal(isc__ascii_tolower8(ab << 24), abc << 24);
|
||||
assert_int_equal(isc__ascii_tolower8(ab << 32), abc << 32);
|
||||
assert_int_equal(isc__ascii_tolower8(ab << 40), abc << 40);
|
||||
assert_int_equal(isc__ascii_tolower8(ab << 48), abc << 48);
|
||||
}
|
||||
}
|
||||
|
||||
ISC_TEST_LIST_START
|
||||
ISC_TEST_ENTRY(upperlower)
|
||||
ISC_TEST_ENTRY(lowerequal)
|
||||
ISC_TEST_ENTRY(lowercmp)
|
||||
ISC_TEST_ENTRY(exhaustive)
|
||||
ISC_TEST_LIST_END
|
||||
|
||||
ISC_TEST_MAIN
|
||||
Reference in New Issue
Block a user