/* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2025 Oxide Computer Company */ /* * Test various aspects of the libc stdbit(3HEAD) interfaces. This does not test * the generic interfaces so that way this can be built and run by compilers * that don't support C23 and we also want to explicitly test the various type * specific values. * * This test is built 32-bit and 64-bit. The width of a long varies between an * ILP32 and LP64 environment and therefore will end up getting back different * values. Hence the ifdefs. */ #include #include #include #include #include #include typedef enum { STDBIT_TEST_U8 = 1 << 0, STDBIT_TEST_U16 = 1 << 1, STDBIT_TEST_U32 = 1 << 2, STDBIT_TEST_U64 = 1 << 3 } stdbit_test_type_t; #define STDBIT_TEST_64P (STDBIT_TEST_U64) #define STDBIT_TEST_32P (STDBIT_TEST_U32 | STDBIT_TEST_64P) #define STDBIT_TEST_16P (STDBIT_TEST_U16 | STDBIT_TEST_32P) #define STDBIT_TEST_ALL (STDBIT_TEST_U8 | STDBIT_TEST_16P) typedef struct { const char *so_name; unsigned int (*so_uc)(unsigned char); unsigned int (*so_us)(unsigned short); unsigned int (*so_ui)(unsigned int); unsigned int (*so_ul)(unsigned long); unsigned int (*so_ull)(unsigned long long); int32_t so_delta[3]; } stdbit_ops_t; typedef struct { stdbit_test_type_t st_types; uint64_t st_val; uint64_t st_res; } stdbit_test_t; /* * Count Leading Zeros tests. As the integer increases in size, there are a * bunch of leading zeros added, hence the delta values in this entry. */ static const stdbit_ops_t stdbit_clz_ops = { .so_name = "Count Leading Zeros", .so_uc = stdc_leading_zeros_uc, .so_us = stdc_leading_zeros_us, .so_ui = stdc_leading_zeros_ui, .so_ul = stdc_leading_zeros_ul, .so_ull = stdc_leading_zeros_ull, .so_delta = { 8, 16, 32 } }; static const stdbit_test_t stdbit_clz_tests[] = { { .st_types = STDBIT_TEST_ALL, .st_val = 0, .st_res = 8 }, { .st_types = STDBIT_TEST_ALL, .st_val = UINT8_MAX, .st_res = 0 }, { .st_types = STDBIT_TEST_ALL, .st_val = 0x42, .st_res = 1 }, { .st_types = STDBIT_TEST_ALL, .st_val = 1, .st_res = 7 }, { .st_types = STDBIT_TEST_16P, .st_val = UINT16_MAX, .st_res = 0 }, { .st_types = STDBIT_TEST_16P, .st_val = 0x7777, .st_res = 1 }, { .st_types = STDBIT_TEST_16P, .st_val = 0x800, .st_res = 4 }, { .st_types = STDBIT_TEST_16P, .st_val = 0x080, .st_res = 8 }, { .st_types = STDBIT_TEST_16P, .st_val = 0x008, .st_res = 12 }, { .st_types = STDBIT_TEST_32P, .st_val = UINT32_MAX, .st_res = 0 }, { .st_types = STDBIT_TEST_32P, .st_val = 0x23000000, .st_res = 2 }, { .st_types = STDBIT_TEST_32P, .st_val = 0x23000032, .st_res = 2 }, { .st_types = STDBIT_TEST_64P, .st_val = 0x400000000, .st_res = 29 }, { .st_types = STDBIT_TEST_64P, .st_val = UINT64_MAX, .st_res = 0 } }; /* * Unlike count leading zeros, when we take a value and hand it to a larger * function, it will always go to a value of zero. As a result, we don't test * many of this suite across everything. */ static const stdbit_ops_t stdbit_clo_ops = { .so_name = "Count Leading Ones", .so_uc = stdc_leading_ones_uc, .so_us = stdc_leading_ones_us, .so_ui = stdc_leading_ones_ui, .so_ul = stdc_leading_ones_ul, .so_ull = stdc_leading_ones_ull, }; static const stdbit_test_t stdbit_clo_tests[] = { { .st_types = STDBIT_TEST_U8, .st_val = UINT8_MAX, .st_res = 8 }, { .st_types = STDBIT_TEST_ALL, .st_val = 0, .st_res = 0 }, { .st_types = STDBIT_TEST_ALL, .st_val = 0x42, .st_res = 0 }, { .st_types = STDBIT_TEST_U8, .st_val = 0xe0, .st_res = 3 }, { .st_types = STDBIT_TEST_U8, .st_val = 0xfc, .st_res = 6 }, { .st_types = STDBIT_TEST_16P, .st_val = UINT8_MAX, .st_res = 0 }, { .st_types = STDBIT_TEST_16P, .st_val = 0x142, .st_res = 0 }, { .st_types = STDBIT_TEST_U16, .st_val = UINT16_MAX, .st_res = 16 }, { .st_types = STDBIT_TEST_U16, .st_val = 0xc0ff, .st_res = 2 }, { .st_types = STDBIT_TEST_U16, .st_val = 0xf88f, .st_res = 5 }, { .st_types = STDBIT_TEST_U32, .st_val = 0x12345678, .st_res = 0 }, { .st_types = STDBIT_TEST_U32, .st_val = UINT32_MAX, .st_res = 32 }, { .st_types = STDBIT_TEST_U32, .st_val = 0x87654321, .st_res = 1 }, { .st_types = STDBIT_TEST_U32, .st_val = 0xff7ff7ff, .st_res = 8 }, { .st_types = STDBIT_TEST_U32, .st_val = 0xfffffeee, .st_res = 23 }, { .st_types = STDBIT_TEST_U64, .st_val = UINT64_MAX, .st_res = 64 }, { .st_types = STDBIT_TEST_U64, .st_val = 0x8000000000000000, .st_res = 1 }, { .st_types = STDBIT_TEST_U64, .st_val = 0xffffffff80000000, .st_res = 33 }, { .st_types = STDBIT_TEST_U64, .st_val = 0xffffffffffff9999, .st_res = 49 } }; /* * The results for zero is the only special case that occurs with this * particular case. */ static const stdbit_ops_t stdbit_ctz_ops = { .so_name = "Count Trailing Zeros", .so_uc = stdc_trailing_zeros_uc, .so_us = stdc_trailing_zeros_us, .so_ui = stdc_trailing_zeros_ui, .so_ul = stdc_trailing_zeros_ul, .so_ull = stdc_trailing_zeros_ull, }; static const stdbit_test_t stdbit_ctz_tests[] = { { .st_types = STDBIT_TEST_U8, .st_val = 0, .st_res = 8 }, { .st_types = STDBIT_TEST_U16, .st_val = 0, .st_res = 16 }, { .st_types = STDBIT_TEST_U32, .st_val = 0, .st_res = 32 }, { .st_types = STDBIT_TEST_U64, .st_val = 0, .st_res = 64 }, { .st_types = STDBIT_TEST_ALL, .st_val = UINT8_MAX, .st_res = 0 }, { .st_types = STDBIT_TEST_ALL, .st_val = 0x1, .st_res = 0 }, { .st_types = STDBIT_TEST_ALL, .st_val = 0x4, .st_res = 2 }, { .st_types = STDBIT_TEST_ALL, .st_val = 0x80, .st_res = 7 }, { .st_types = STDBIT_TEST_16P, .st_val = 0xff60, .st_res = 5 }, { .st_types = STDBIT_TEST_16P, .st_val = 0x8ad0, .st_res = 4 }, { .st_types = STDBIT_TEST_16P, .st_val = 0x2300, .st_res = 8 }, { .st_types = STDBIT_TEST_32P, .st_val = 0x42000000, .st_res = 25 }, { .st_types = STDBIT_TEST_32P, .st_val = 0x99887700, .st_res = 8 }, { .st_types = STDBIT_TEST_32P, .st_val = UINT32_MAX, .st_res = 0 }, { .st_types = STDBIT_TEST_64P, .st_val = 0xaa00000000000000, .st_res = 57 }, { .st_types = STDBIT_TEST_64P, .st_val = 0xbadcaf0000000000, .st_res = 40 }, { .st_types = STDBIT_TEST_64P, .st_val = UINT64_MAX, .st_res = 0 } }; /* * Count Trailing Ones Tests */ static const stdbit_ops_t stdbit_cto_ops = { .so_name = "Count Trailing Ones", .so_uc = stdc_trailing_ones_uc, .so_us = stdc_trailing_ones_us, .so_ui = stdc_trailing_ones_ui, .so_ul = stdc_trailing_ones_ul, .so_ull = stdc_trailing_ones_ull, }; static const stdbit_test_t stdbit_cto_tests[] = { { .st_types = STDBIT_TEST_ALL, .st_val = UINT8_MAX, .st_res = 8 }, { .st_types = STDBIT_TEST_ALL, .st_val = 0, .st_res = 0 }, { .st_types = STDBIT_TEST_ALL, .st_val = 3, .st_res = 2 }, { .st_types = STDBIT_TEST_ALL, .st_val = 0x7e, .st_res = 0 }, { .st_types = STDBIT_TEST_ALL, .st_val = 0x7f, .st_res = 7 }, { .st_types = STDBIT_TEST_16P, .st_val = UINT16_MAX, .st_res = 16 }, { .st_types = STDBIT_TEST_16P, .st_val = 0x8765, .st_res = 1 }, { .st_types = STDBIT_TEST_16P, .st_val = 0xcdef, .st_res = 4 }, { .st_types = STDBIT_TEST_16P, .st_val = 0x9fff, .st_res = 13 }, { .st_types = STDBIT_TEST_32P, .st_val = UINT32_MAX, .st_res = 32 }, { .st_types = STDBIT_TEST_32P, .st_val = 0x85ab91ff, .st_res = 9 }, { .st_types = STDBIT_TEST_32P, .st_val = 0x7fffffff, .st_res = 31 }, { .st_types = STDBIT_TEST_64P, .st_val = UINT64_MAX, .st_res = 64 }, { .st_types = STDBIT_TEST_64P, .st_val = 0x1bffffffffffffff, .st_res = 58 }, { .st_types = STDBIT_TEST_64P, .st_val = 0x9abe83cff6ff7ff8, .st_res = 0 } }; /* * See the manual. The C23 definition for "most-significant" bit is * counter-intuitive. Basically bit 0 is considered the most significant bit. So * for a uint8_t bit 0 is considered index 7 and bit 7 is index 0. The results * always have 1 added to them. */ static const stdbit_ops_t stdbit_flz_ops = { .so_name = "First Leading Zero", .so_uc = stdc_first_leading_zero_uc, .so_us = stdc_first_leading_zero_us, .so_ui = stdc_first_leading_zero_ui, .so_ul = stdc_first_leading_zero_ul, .so_ull = stdc_first_leading_zero_ull, }; static const stdbit_test_t stdbit_flz_tests[] = { { .st_types = STDBIT_TEST_ALL, .st_val = 0, .st_res = 1 }, { .st_types = STDBIT_TEST_ALL, .st_val = 0x3, .st_res = 1 }, { .st_types = STDBIT_TEST_U8, .st_val = 0xf0, .st_res = 5 }, { .st_types = STDBIT_TEST_U8, .st_val = 0xef, .st_res = 4 }, { .st_types = STDBIT_TEST_U8, .st_val = 0xc4, .st_res = 3 }, { .st_types = STDBIT_TEST_U8, .st_val = UINT8_MAX, .st_res = 0 }, { .st_types = STDBIT_TEST_16P, .st_val = UINT8_MAX, .st_res = 1 }, { .st_types = STDBIT_TEST_U16, .st_val = UINT16_MAX, .st_res = 0 }, { .st_types = STDBIT_TEST_U16, .st_val = 0xfabc, .st_res = 6 }, { .st_types = STDBIT_TEST_U16, .st_val = 0xcbaf, .st_res = 3 }, { .st_types = STDBIT_TEST_32P, .st_val = UINT16_MAX, .st_res = 1 }, { .st_types = STDBIT_TEST_U32, .st_val = UINT32_MAX, .st_res = 0 }, { .st_types = STDBIT_TEST_U32, .st_val = 0xff7ff623, .st_res = 9 }, { .st_types = STDBIT_TEST_U32, .st_val = 0xfffff623, .st_res = 21 }, { . st_types = STDBIT_TEST_U32, .st_val = 0xffffff95, .st_res = 26 }, { .st_types = STDBIT_TEST_64P, .st_val = UINT32_MAX, .st_res = 1 }, { .st_types = STDBIT_TEST_U64, .st_val = UINT64_MAX, .st_res = 0 }, { .st_types = STDBIT_TEST_U64, .st_val = 0xfffffffffffffffe, .st_res = 64 }, { .st_types = STDBIT_TEST_U64, .st_val = 0xffff2b9542fffffe, .st_res = 17 } }; /* * See the note on the flz tests for the oddities with calculating this. Due to * the nature of how these are counted, the larger the number gets, the more the * first 1 increases in its "most-significant" value. However, we have to * special case 0 in our logic because it will stay consistent across all the * values. */ static const stdbit_ops_t stdbit_flo_ops = { .so_name = "First Leading One", .so_uc = stdc_first_leading_one_uc, .so_us = stdc_first_leading_one_us, .so_ui = stdc_first_leading_one_ui, .so_ul = stdc_first_leading_one_ul, .so_ull = stdc_first_leading_one_ull, .so_delta = { 8, 16, 32 } }; static const stdbit_test_t stdbit_flo_tests[] = { { .st_types = STDBIT_TEST_U8, .st_val = 0, .st_res = 0 }, { .st_types = STDBIT_TEST_U16, .st_val = 0, .st_res = 0 }, { .st_types = STDBIT_TEST_U32, .st_val = 0, .st_res = 0 }, { .st_types = STDBIT_TEST_U64, .st_val = 0, .st_res = 0 }, { .st_types = STDBIT_TEST_ALL, .st_val = 0x1, .st_res = 8 }, { .st_types = STDBIT_TEST_ALL, .st_val = 0xf, .st_res = 5 }, { .st_types = STDBIT_TEST_ALL, .st_val = 0xfe, .st_res = 1 }, { .st_types = STDBIT_TEST_ALL, .st_val = 0x7f, .st_res = 2 }, { .st_types = STDBIT_TEST_ALL, .st_val = UINT8_MAX, .st_res = 1 }, { .st_types = STDBIT_TEST_16P, .st_val = UINT16_MAX, .st_res = 1 }, { .st_types = STDBIT_TEST_16P, .st_val = 0xfeed, .st_res = 1 }, { .st_types = STDBIT_TEST_16P, .st_val = 0x1aff, .st_res = 4 }, { .st_types = STDBIT_TEST_16P, .st_val = 0x02b0, .st_res = 7 }, { .st_types = STDBIT_TEST_32P, .st_val = UINT32_MAX, .st_res = 1 }, { .st_types = STDBIT_TEST_32P, .st_val = 0x00001234, .st_res = 20 }, { .st_types = STDBIT_TEST_32P, .st_val = 0x2bb22bb2, .st_res = 3 }, { .st_types = STDBIT_TEST_32P, .st_val = 0x00420000, .st_res = 10 }, { .st_types = STDBIT_TEST_64P, .st_val = UINT64_MAX, .st_res = 1 }, { .st_types = STDBIT_TEST_64P, .st_val = 0x000000000c000000, .st_res = 37 }, { .st_types = STDBIT_TEST_64P, .st_val = 0x000fedcba9abcdef, .st_res = 13 }, { .st_types = STDBIT_TEST_64P, .st_val = 0x000001992aa3bb4c, .st_res = 24 }, { .st_types = STDBIT_TEST_64P, .st_val = 0x0706050403020100, .st_res = 6 } }; /* * First Trailing Zero. This numbers indexes in the way that someone expects * where the bit 0 is least significant index zero, which returns a value of 1. * When there are no zeros this returns 0. There is no reliable increment * pattern here. */ static const stdbit_ops_t stdbit_ftz_ops = { .so_name = "First Trailing Zero", .so_uc = stdc_first_trailing_zero_uc, .so_us = stdc_first_trailing_zero_us, .so_ui = stdc_first_trailing_zero_ui, .so_ul = stdc_first_trailing_zero_ul, .so_ull = stdc_first_trailing_zero_ull, }; static const stdbit_test_t stdbit_ftz_tests[] = { { .st_types = STDBIT_TEST_ALL, .st_val = 0, .st_res = 1 }, { .st_types = STDBIT_TEST_U8, .st_val = UINT8_MAX, .st_res = 0 }, { .st_types = STDBIT_TEST_U8, .st_val = 0xfe, .st_res = 1 }, { .st_types = STDBIT_TEST_U8, .st_val = 0xef, .st_res = 5 }, { .st_types = STDBIT_TEST_U8, .st_val = 0x7f, .st_res = 8 }, { .st_types = STDBIT_TEST_U16, .st_val = UINT8_MAX, .st_res = 9 }, { .st_types = STDBIT_TEST_U16, .st_val = UINT16_MAX, .st_res = 0 }, { .st_types = STDBIT_TEST_U16, .st_val = 0xfffe, .st_res = 1 }, { .st_types = STDBIT_TEST_U16, .st_val = 0xefff, .st_res = 13 }, { .st_types = STDBIT_TEST_U16, .st_val = 0x07ff, .st_res = 12 }, { .st_types = STDBIT_TEST_U32, .st_val = UINT16_MAX, .st_res = 17 }, { .st_types = STDBIT_TEST_U32, .st_val = UINT32_MAX, .st_res = 0 }, { .st_types = STDBIT_TEST_U32, .st_val = 0xcaffffff, .st_res = 25 }, { .st_types = STDBIT_TEST_U32, .st_val = 0xcabfffff, .st_res = 23 }, { .st_types = STDBIT_TEST_U64, .st_val = UINT32_MAX, .st_res = 33 }, { .st_types = STDBIT_TEST_U64, .st_val = UINT64_MAX, .st_res = 0 }, { .st_types = STDBIT_TEST_U64, .st_val = 0xface2bface95a2ff, .st_res = 9 }, { .st_types = STDBIT_TEST_U64, .st_val = 0x7777777777777777, .st_res = 4 } }; /* * First Trailing One. This numbers indexes in the way that someone expects * where the bit 0 is least significant index zero, which returns a value of 1. * When there are no zeros this returns 0. This is classical ffs(). */ static const stdbit_ops_t stdbit_fto_ops = { .so_name = "First Trailing One", .so_uc = stdc_first_trailing_one_uc, .so_us = stdc_first_trailing_one_us, .so_ui = stdc_first_trailing_one_ui, .so_ul = stdc_first_trailing_one_ul, .so_ull = stdc_first_trailing_one_ull, }; static const stdbit_test_t stdbit_fto_tests[] = { { .st_types = STDBIT_TEST_ALL, .st_val = 0, .st_res = 0 }, { .st_types = STDBIT_TEST_ALL, .st_val = UINT8_MAX, .st_res = 1 }, { .st_types = STDBIT_TEST_ALL, .st_val = 0xf7, .st_res = 1 }, { .st_types = STDBIT_TEST_ALL, .st_val = 0xf8, .st_res = 4 }, { .st_types = STDBIT_TEST_ALL, .st_val = 0x6d, .st_res = 1 }, { .st_types = STDBIT_TEST_ALL, .st_val = 0xd6, .st_res = 2 }, { .st_types = STDBIT_TEST_ALL, .st_val = 0x40, .st_res = 7 }, { .st_types = STDBIT_TEST_16P, .st_val = UINT16_MAX, .st_res = 1 }, { .st_types = STDBIT_TEST_16P, .st_val = 0xf840, .st_res = 7 }, { .st_types = STDBIT_TEST_16P, .st_val = 0x0a00, .st_res = 10 }, { .st_types = STDBIT_TEST_16P, .st_val = 0x8000, .st_res = 16 }, { .st_types = STDBIT_TEST_32P, .st_val = UINT32_MAX, .st_res = 1 }, { .st_types = STDBIT_TEST_32P, .st_val = 0xb0000000, .st_res = 29 }, { .st_types = STDBIT_TEST_32P, .st_val = 0xf9c00000, .st_res = 23 }, { .st_types = STDBIT_TEST_32P, .st_val = 0xfed81500, .st_res = 9 }, { .st_types = STDBIT_TEST_64P, .st_val = UINT64_MAX, .st_res = 1 }, { .st_types = STDBIT_TEST_64P, .st_val = 0xfed80d0000000000, .st_res = 41 }, { .st_types = STDBIT_TEST_64P, .st_val = 0xff70000000000000, .st_res = 53 } }; /* * Count Zeros. */ static const stdbit_ops_t stdbit_cz_ops = { .so_name = "Count Zeros", .so_uc = stdc_count_zeros_uc, .so_us = stdc_count_zeros_us, .so_ui = stdc_count_zeros_ui, .so_ul = stdc_count_zeros_ul, .so_ull = stdc_count_zeros_ull, .so_delta = { 8, 16, 32 } }; static const stdbit_test_t stdbit_cz_tests[] = { { .st_types = STDBIT_TEST_ALL, .st_val = 0, .st_res = 8 }, { .st_types = STDBIT_TEST_ALL, .st_val = UINT8_MAX, .st_res = 0 }, { .st_types = STDBIT_TEST_ALL, .st_val = 0x77, .st_res = 2 }, { .st_types = STDBIT_TEST_ALL, .st_val = 0x88, .st_res = 6 }, { .st_types = STDBIT_TEST_ALL, .st_val = 0x5, .st_res = 6 }, { .st_types = STDBIT_TEST_ALL, .st_val = 0x1f, .st_res = 3 }, { .st_types = STDBIT_TEST_16P, .st_val = UINT16_MAX, .st_res = 0 }, { .st_types = STDBIT_TEST_16P, .st_val = 0x1234, .st_res = 11 }, { .st_types = STDBIT_TEST_16P, .st_val = 0x4321, .st_res = 11 }, { .st_types = STDBIT_TEST_16P, .st_val = 0x2ba2, .st_res = 9 }, { .st_types = STDBIT_TEST_32P, .st_val = UINT32_MAX, .st_res = 0 }, { .st_types = STDBIT_TEST_32P, .st_val = 0xdeadbeef, .st_res = 8 }, { .st_types = STDBIT_TEST_32P, .st_val = 0x12345678, .st_res = 19 }, { .st_types = STDBIT_TEST_64P, .st_val = UINT64_MAX, .st_res = 0 }, { .st_types = STDBIT_TEST_64P, .st_val = 0xabbabccbcddcdeed, .st_res = 22 }, { .st_types = STDBIT_TEST_64P, .st_val = 0x1221244248848008, .st_res = 50 }, { .st_types = STDBIT_TEST_64P, .st_val = 0xfffffffeefffffff, .st_res = 2 } }; /* * Count Ones. */ static const stdbit_ops_t stdbit_co_ops = { .so_name = "Count Ones", .so_uc = stdc_count_ones_uc, .so_us = stdc_count_ones_us, .so_ui = stdc_count_ones_ui, .so_ul = stdc_count_ones_ul, .so_ull = stdc_count_ones_ull, }; static const stdbit_test_t stdbit_co_tests[] = { { .st_types = STDBIT_TEST_ALL, .st_val = 0, .st_res = 0 }, { .st_types = STDBIT_TEST_ALL, .st_val = UINT8_MAX, .st_res = 8 }, { .st_types = STDBIT_TEST_ALL, .st_val = 0x77, .st_res = 6 }, { .st_types = STDBIT_TEST_ALL, .st_val = 0x88, .st_res = 2 }, { .st_types = STDBIT_TEST_ALL, .st_val = 0x5, .st_res = 2 }, { .st_types = STDBIT_TEST_ALL, .st_val = 0x1f, .st_res = 5 }, { .st_types = STDBIT_TEST_16P, .st_val = UINT16_MAX, .st_res = 16 }, { .st_types = STDBIT_TEST_16P, .st_val = 0x1234, .st_res = 5 }, { .st_types = STDBIT_TEST_16P, .st_val = 0x4321, .st_res = 5 }, { .st_types = STDBIT_TEST_16P, .st_val = 0x2ba2, .st_res = 7 }, { .st_types = STDBIT_TEST_32P, .st_val = UINT32_MAX, .st_res = 32 }, { .st_types = STDBIT_TEST_32P, .st_val = 0xdeadbeef, .st_res = 24 }, { .st_types = STDBIT_TEST_32P, .st_val = 0x12345678, .st_res = 13 }, { .st_types = STDBIT_TEST_64P, .st_val = UINT64_MAX, .st_res = 64 }, { .st_types = STDBIT_TEST_64P, .st_val = 0xabbabccbcddcdeed, .st_res = 42 }, { .st_types = STDBIT_TEST_64P, .st_val = 0x1221244248848008, .st_res = 14 }, { .st_types = STDBIT_TEST_64P, .st_val = 0xfffffffeefffffff, .st_res = 62 } }; /* * Bit width tests. These values should stay the same as we increase integer * sizes as values are only adding zeros. */ static const stdbit_ops_t stdbit_bw_ops = { .so_name = "Bit Width", .so_uc = stdc_bit_width_uc, .so_us = stdc_bit_width_us, .so_ui = stdc_bit_width_ui, .so_ul = stdc_bit_width_ul, .so_ull = stdc_bit_width_ull, }; static const stdbit_test_t stdbit_bw_tests[] = { { .st_types = STDBIT_TEST_ALL, .st_val = 0, .st_res = 0 }, { .st_types = STDBIT_TEST_ALL, .st_val = UINT8_MAX, .st_res = 8 }, { .st_types = STDBIT_TEST_ALL, .st_val = 0x80, .st_res = 8 }, { .st_types = STDBIT_TEST_ALL, .st_val = 0x08, .st_res = 4 }, { .st_types = STDBIT_TEST_ALL, .st_val = 0x17, .st_res = 5 }, { .st_types = STDBIT_TEST_16P, .st_val = UINT16_MAX, .st_res = 16 }, { .st_types = STDBIT_TEST_16P, .st_val = 0x7777, .st_res = 15 }, { .st_types = STDBIT_TEST_16P, .st_val = 0x2bb2, .st_res = 14 }, { .st_types = STDBIT_TEST_16P, .st_val = 0x0230, .st_res = 10 }, { .st_types = STDBIT_TEST_32P, .st_val = UINT32_MAX, .st_res = 32 }, { .st_types = STDBIT_TEST_32P, .st_val = 0xfedc4000, .st_res = 32 }, { .st_types = STDBIT_TEST_32P, .st_val = 0x0004cedf, .st_res = 19 }, { .st_types = STDBIT_TEST_32P, .st_val = 0x001ee100, .st_res = 21 }, { .st_types = STDBIT_TEST_64P, .st_val = 0x8000000000000000, .st_res = 64 }, { .st_types = STDBIT_TEST_64P, .st_val = 0x00ff11ee22dd33cc, .st_res = 56 }, { .st_types = STDBIT_TEST_64P, .st_val = UINT64_MAX, .st_res = 64 } }; static void stdbit_print_pass(stdbit_test_type_t types, uint64_t val, const char *cat) { bool first = true; (void) printf("TEST PASSED: %s (0x%" PRIx64 ") [", cat, val); if ((types & STDBIT_TEST_U8) != 0) { (void) printf("8"); first = false; } if ((types & STDBIT_TEST_U16) != 0) { (void) printf("%s16", first ? "" : ","); first = false; } if ((types & STDBIT_TEST_U32) != 0) { (void) printf("%s32", first ? "" : ","); first = false; } if ((types & STDBIT_TEST_U64) != 0) { (void) printf("%s64", first ? "" : ","); first = false; } (void) printf("]\n"); } static bool stdbit_test_one(const stdbit_test_t *test, const stdbit_ops_t *ops) { bool ret = true; uint64_t comp = test->st_res; VERIFY3U(test->st_types, !=, 0); if ((test->st_types & STDBIT_TEST_U8) != 0) { unsigned res = ops->so_uc(test->st_val); if (res != comp) { warnx("TEST FAILED: %s (0x%" PRIx64 ") 8-bit (uchar) " "returned 0x%x, expected 0x%" PRIx64, ops->so_name, test->st_val, res, comp); ret = false; } comp += ops->so_delta[0]; } if ((test->st_types & STDBIT_TEST_U16) != 0) { unsigned res = ops->so_us(test->st_val); if (res != comp) { warnx("TEST FAILED: %s (0x%" PRIx64 ") 16-bit (ushort) " "returned 0x%x, expected 0x%" PRIx64, ops->so_name, test->st_val, res, comp); ret = false; } comp += ops->so_delta[1]; } if ((test->st_types & STDBIT_TEST_U32) != 0) { unsigned res = ops->so_ui(test->st_val); if (res != comp) { warnx("TEST FAILED: %s (0x%" PRIx64 ") 32-bit (uint) " "returned 0x%x, expected 0x%" PRIx64, ops->so_name, test->st_val, res, comp); ret = false; } #ifdef _ILP32 res = ops->so_ul(test->st_val); if (res != comp) { warnx("TEST FAILED: %s (0x%" PRIx64 ") 32-bit (ulong) " "returned 0x%x, expected 0x%" PRIx64, ops->so_name, test->st_val, res, comp); ret = false; } #endif /* _ILP32 */ comp += ops->so_delta[2]; } if ((test->st_types & STDBIT_TEST_U64) != 0) { unsigned res; #ifdef _LP64 res = ops->so_ul(test->st_val); if (res != comp) { warnx("TEST FAILED: %s (0x%" PRIx64 ") 64-bit (ulong) " "returned 0x%x, expected 0x%" PRIx64, ops->so_name, test->st_val, res, comp); ret = false; } #endif /* _LP64 */ res = ops->so_ull(test->st_val); if (res != comp) { warnx("TEST FAILED: %s (0x%" PRIx64 ") 64-bit (ulong " "long) returned 0x%x, expected 0x%" PRIx64, ops->so_name, test->st_val, res, comp); ret = false; } } if (ret) { stdbit_print_pass(test->st_types, test->st_val, ops->so_name); } return (ret); } /* * This is used for all the functions that can return unsigned. */ typedef struct { const stdbit_ops_t *sg_ops; const stdbit_test_t *sg_tests; size_t sg_ntests; } stdbit_std_group_t; static const stdbit_std_group_t stdbit_groups[] = { { &stdbit_clz_ops, stdbit_clz_tests, ARRAY_SIZE(stdbit_clz_tests) }, { &stdbit_clo_ops, stdbit_clo_tests, ARRAY_SIZE(stdbit_clo_tests) }, { &stdbit_ctz_ops, stdbit_ctz_tests, ARRAY_SIZE(stdbit_ctz_tests) }, { &stdbit_cto_ops, stdbit_cto_tests, ARRAY_SIZE(stdbit_cto_tests) }, { &stdbit_flz_ops, stdbit_flz_tests, ARRAY_SIZE(stdbit_flz_tests) }, { &stdbit_flo_ops, stdbit_flo_tests, ARRAY_SIZE(stdbit_flo_tests) }, { &stdbit_ftz_ops, stdbit_ftz_tests, ARRAY_SIZE(stdbit_ftz_tests) }, { &stdbit_fto_ops, stdbit_fto_tests, ARRAY_SIZE(stdbit_fto_tests) }, { &stdbit_cz_ops, stdbit_cz_tests, ARRAY_SIZE(stdbit_cz_tests) }, { &stdbit_co_ops, stdbit_co_tests, ARRAY_SIZE(stdbit_co_tests) }, { &stdbit_bw_ops, stdbit_bw_tests, ARRAY_SIZE(stdbit_bw_tests) }, }; /* * Tests for is a single bit set. These should be the same regardless of integer * size. */ static const stdbit_test_t stdbit_1b_tests[] = { { .st_types = STDBIT_TEST_ALL, .st_val = 0, .st_res = false }, { .st_types = STDBIT_TEST_ALL, .st_val = UINT8_MAX, .st_res = false }, { .st_types = STDBIT_TEST_ALL, .st_val = 0x40, .st_res = true }, { .st_types = STDBIT_TEST_ALL, .st_val = 0x23, .st_res = false }, { .st_types = STDBIT_TEST_ALL, .st_val = 0x81, .st_res = false }, { .st_types = STDBIT_TEST_ALL, .st_val = 0x08, .st_res = true }, { .st_types = STDBIT_TEST_16P, .st_val = UINT16_MAX, .st_res = false }, { .st_types = STDBIT_TEST_16P, .st_val = 0x0100, .st_res = true }, { .st_types = STDBIT_TEST_16P, .st_val = 0x7777, .st_res = false }, { .st_types = STDBIT_TEST_16P, .st_val = 0x8000, .st_res = true }, { .st_types = STDBIT_TEST_16P, .st_val = 0x0400, .st_res = true }, { .st_types = STDBIT_TEST_16P, .st_val = 0x0020, .st_res = true }, { .st_types = STDBIT_TEST_16P, .st_val = 0x0001, .st_res = true }, { .st_types = STDBIT_TEST_32P, .st_val = UINT32_MAX, .st_res = false }, { .st_types = STDBIT_TEST_32P, .st_val = 0x00200000, .st_res = true }, { .st_types = STDBIT_TEST_32P, .st_val = 0xbaddcafe, .st_res = false }, { .st_types = STDBIT_TEST_32P, .st_val = 0x80000000, .st_res = true }, { .st_types = STDBIT_TEST_64P, .st_val = UINT64_MAX, .st_res = false }, { .st_types = STDBIT_TEST_64P, .st_val = 0x8000000000000000, .st_res = true }, { .st_types = STDBIT_TEST_64P, .st_val = 0x0010000000000000, .st_res = true } }; /* * The single bit set tests require a slightly different runner because they * return a boolean. */ static bool stdbit_1b_test_one(const stdbit_test_t *test) { bool ret = true, comp; VERIFY(test->st_res == 0 || test->st_res == 1); comp = (bool)test->st_res; VERIFY3U(test->st_types, !=, 0); if ((test->st_types & STDBIT_TEST_U8) != 0) { bool res = stdc_has_single_bit_uc(test->st_val); if (res != comp) { warnx("TEST FAILED: Single-bit (0x%" PRIx64 ") 8-bit " "(uchar) returned %s, expected %s", test->st_val, res ? "true" : "false", comp ? "true" : "false"); ret = false; } } if ((test->st_types & STDBIT_TEST_U16) != 0) { bool res = stdc_has_single_bit_us(test->st_val); if (res != comp) { warnx("TEST FAILED: Single-bit (0x%" PRIx64 ") 16-bit " "(ushort) returned %s, expected %s", test->st_val, res ? "true" : "false", comp ? "true" : "false"); ret = false; } } if ((test->st_types & STDBIT_TEST_U32) != 0) { bool res = stdc_has_single_bit_ui(test->st_val); if (res != comp) { warnx("TEST FAILED: Single-bit (0x%" PRIx64 ") 32-bit " "(uint) returned %s, expected %s", test->st_val, res ? "true" : "false", comp ? "true" : "false"); ret = false; } #ifdef _ILP32 res = stdc_has_single_bit_ul(test->st_val); if (res != comp) { warnx("TEST FAILED: Single-bit (0x%" PRIx64 ") 32-bit " "(ulong) returned %s, expected %s", test->st_val, res ? "true" : "false", comp ? "true" : "false"); ret = false; } #endif /* _ILP32 */ } if ((test->st_types & STDBIT_TEST_U64) != 0) { bool res; #ifdef _LP64 res = stdc_has_single_bit_ul(test->st_val); if (res != comp) { warnx("TEST FAILED: Single-bit (0x%" PRIx64 ") 64-bit " "(ulong) returned %s, expected %s", test->st_val, res ? "true" : "false", comp ? "true" : "false"); ret = false; } #endif /* _LP64 */ res = stdc_has_single_bit_ull(test->st_val); if (res != comp) { warnx("TEST FAILED: Single-bit (0x%" PRIx64 ") 64-bit " "(ulong long) returned %s, expected %s", test->st_val, res ? "true" : "false", comp ? "true" : "false"); ret = false; } } if (ret) { stdbit_print_pass(test->st_types, test->st_val, "Single-bit"); } return (ret); } /* * We use a different test structure for the floor and ceiling tests and check * both at each stop. */ typedef struct { stdbit_test_type_t sfc_types; uint64_t sfc_val; uint64_t sfc_floor; uint64_t sfc_ceil; } stdbit_fc_test_t; /* * Bit floor and ceiling tests. Note, a bit ceiling test can fail and return 0 * if the value would overlap the type it's in. In those cases we don't use all * tests. This happens when the most significant bit in a given integer is set. * It will work at the next size up. All others should always pass all tests. */ static const stdbit_fc_test_t stdbit_fc_tests[] = { { .sfc_types = STDBIT_TEST_ALL, .sfc_val = 0, .sfc_floor = 0, .sfc_ceil = 1 }, { .sfc_types = STDBIT_TEST_U8, .sfc_val = UINT8_MAX, .sfc_floor = 1ULL << 7, .sfc_ceil = 0 }, { .sfc_types = STDBIT_TEST_ALL, .sfc_val = 0x23, .sfc_floor = 1ULL << 5, .sfc_ceil = 1ULL << 6 }, { .sfc_types = STDBIT_TEST_ALL, .sfc_val = 0x06, .sfc_floor = 1ULL << 2, .sfc_ceil = 1ULL << 3 }, { .sfc_types = STDBIT_TEST_ALL, .sfc_val = 0x18, .sfc_floor = 1ULL << 4, .sfc_ceil = 1ULL << 5 }, { .sfc_types = STDBIT_TEST_U8, .sfc_val = 0x81, .sfc_floor = 1ULL << 7, .sfc_ceil = 0 }, { .sfc_types = STDBIT_TEST_16P, .sfc_val = UINT8_MAX, .sfc_floor = 1ULL << 7, .sfc_ceil = 1ULL << 8 }, { .sfc_types = STDBIT_TEST_16P, .sfc_val = 0x0ff7, .sfc_floor = 1ULL << 11, .sfc_ceil = 1ULL << 12 }, { .sfc_types = STDBIT_TEST_16P, .sfc_val = 0x20a4, .sfc_floor = 1ULL << 13, .sfc_ceil = 1ULL << 14 }, { .sfc_types = STDBIT_TEST_U16, .sfc_val = 0x8ab1, .sfc_floor = 1ULL << 15, .sfc_ceil = 0 }, { .sfc_types = STDBIT_TEST_U16, .sfc_val = UINT16_MAX, .sfc_floor = 1ULL << 15, .sfc_ceil = 0 }, { .sfc_types = STDBIT_TEST_32P, .sfc_val = UINT16_MAX, .sfc_floor = 1ULL << 15, .sfc_ceil = 1ULL << 16 }, { .sfc_types = STDBIT_TEST_32P, .sfc_val = 0x000271ab, .sfc_floor = 1ULL << 17, .sfc_ceil = 1ULL << 18 }, { .sfc_types = STDBIT_TEST_32P, .sfc_val = 0x01000009, .sfc_floor = 1ULL << 24, .sfc_ceil = 1ULL << 25 }, { .sfc_types = STDBIT_TEST_32P, .sfc_val = 0x02000000, .sfc_floor = 1ULL << 25, .sfc_ceil = 1ULL << 25 }, { .sfc_types = STDBIT_TEST_32P, .sfc_val = 0x1cabf917, .sfc_floor = 1ULL << 28, .sfc_ceil = 1ULL << 29 }, { .sfc_types = STDBIT_TEST_U32, .sfc_val = 0x800a9b03, .sfc_floor = 1ULL << 31, .sfc_ceil = 0 }, { .sfc_types = STDBIT_TEST_U32, .sfc_val = UINT32_MAX, .sfc_floor = 1ULL << 31, .sfc_ceil = 0 }, { .sfc_types = STDBIT_TEST_64P, .sfc_val = UINT32_MAX, .sfc_floor = 1ULL << 31, .sfc_ceil = 1ULL << 32 }, { .sfc_types = STDBIT_TEST_U64, .sfc_val = 0x0089a23b1389ba87, .sfc_floor = 1ULL << 55, .sfc_ceil = 1ULL << 56 }, { .sfc_types = STDBIT_TEST_U64, .sfc_val = 0x499aff6eb12e7777, .sfc_floor = 1ULL << 62, .sfc_ceil = 1ULL << 63 }, { .sfc_types = STDBIT_TEST_U64, .sfc_val = 0xc00123481980ab87, .sfc_floor = 1ULL << 63, .sfc_ceil = 0 }, { .sfc_types = STDBIT_TEST_U64, .sfc_val = UINT64_MAX, .sfc_floor = 1ULL << 63, .sfc_ceil = 0 } }; static bool stdbit_fc_test_one(const stdbit_fc_test_t *test) { bool ret = true; VERIFY3U(test->sfc_types, !=, 0); if ((test->sfc_types & STDBIT_TEST_U8) != 0) { uint64_t res = stdc_bit_floor_uc(test->sfc_val); if (res != test->sfc_floor) { warnx("TEST FAILED: Bit Floor (0x%" PRIx64 ") 8-bit " "(uchar) returned 0x%" PRIx64 ", expected 0x%" PRIx64, test->sfc_val, res, test->sfc_floor); ret = false; } res = stdc_bit_ceil_uc(test->sfc_val); if (res != test->sfc_ceil) { warnx("TEST FAILED: Bit Ceiling (0x%" PRIx64 ") 8-bit " "(uchar) returned 0x%" PRIx64 ", expected 0x%" PRIx64, test->sfc_val, res, test->sfc_ceil); ret = false; } } if ((test->sfc_types & STDBIT_TEST_U16) != 0) { uint64_t res = stdc_bit_floor_us(test->sfc_val); if (res != test->sfc_floor) { warnx("TEST FAILED: Bit Floor (0x%" PRIx64 ") 16-bit " "(ushort) returned 0x%" PRIx64 ", expected 0x%" PRIx64, test->sfc_val, res, test->sfc_floor); ret = false; } res = stdc_bit_ceil_us(test->sfc_val); if (res != test->sfc_ceil) { warnx("TEST FAILED: Bit Ceiling (0x%" PRIx64 ") 16-bit " "(ushort) returned 0x%" PRIx64 ", expected 0x%" PRIx64, test->sfc_val, res, test->sfc_ceil); ret = false; } } if ((test->sfc_types & STDBIT_TEST_U32) != 0) { uint64_t res = stdc_bit_floor_ui(test->sfc_val); if (res != test->sfc_floor) { warnx("TEST FAILED: Bit Floor (0x%" PRIx64 ") 32-bit " "(uint) returned 0x%" PRIx64 ", expected 0x%" PRIx64, test->sfc_val, res, test->sfc_floor); ret = false; } res = stdc_bit_ceil_ui(test->sfc_val); if (res != test->sfc_ceil) { warnx("TEST FAILED: Bit Ceiling (0x%" PRIx64 ") 32-bit " "(uint) returned 0x%" PRIx64 ", expected 0x%" PRIx64, test->sfc_val, res, test->sfc_ceil); ret = false; } #ifdef _ILP32 res = stdc_bit_floor_ul(test->sfc_val); if (res != test->sfc_floor) { warnx("TEST FAILED: Bit Floor (0x%" PRIx64 ") 32-bit " "(ulong) returned 0x%" PRIx64 ", expected 0x%" PRIx64, test->sfc_val, res, test->sfc_floor); ret = false; } res = stdc_bit_ceil_ul(test->sfc_val); if (res != test->sfc_ceil) { warnx("TEST FAILED: Bit Ceiling (0x%" PRIx64 ") 32-bit " "(ulong) returned 0x%" PRIx64 ", expected 0x%" PRIx64, test->sfc_val, res, test->sfc_ceil); ret = false; } #endif /* _ILP32 */ } if ((test->sfc_types & STDBIT_TEST_U64) != 0) { uint64_t res; #ifdef _LP64 res = stdc_bit_floor_ul(test->sfc_val); if (res != test->sfc_floor) { warnx("TEST FAILED: Bit Floor (0x%" PRIx64 ") 64-bit " "(ulong) returned 0x%" PRIx64 ", expected 0x%" PRIx64, test->sfc_val, res, test->sfc_floor); ret = false; } res = stdc_bit_ceil_ul(test->sfc_val); if (res != test->sfc_ceil) { warnx("TEST FAILED: Bit Ceiling (0x%" PRIx64 ") 64-bit " "(ulong) returned 0x%" PRIx64 ", expected 0x%" PRIx64, test->sfc_val, res, test->sfc_ceil); ret = false; } #endif /* _LP64 */ res = stdc_bit_floor_ull(test->sfc_val); if (res != test->sfc_floor) { warnx("TEST FAILED: Bit Floor (0x%" PRIx64 ") 64-bit " "(ulong long) returned 0x%" PRIx64 ", expected 0x%" PRIx64, test->sfc_val, res, test->sfc_floor); ret = false; } res = stdc_bit_ceil_ull(test->sfc_val); if (res != test->sfc_ceil) { warnx("TEST FAILED: Bit Ceiling (0x%" PRIx64 ") 64-bit " "(ulong long) returned 0x%" PRIx64 ", expected 0x%" PRIx64, test->sfc_val, res, test->sfc_ceil); ret = false; } } if (ret) { stdbit_print_pass(test->sfc_types, test->sfc_val, "Bit Floor/Ceiling"); } return (ret); } int main(void) { int ret = EXIT_SUCCESS; for (size_t i = 0; i < ARRAY_SIZE(stdbit_groups); i++) { for (size_t t = 0; t < stdbit_groups[i].sg_ntests; t++) { if (!stdbit_test_one(&stdbit_groups[i].sg_tests[t], stdbit_groups[i].sg_ops)) { ret = EXIT_FAILURE; } } } for (size_t i = 0; i < ARRAY_SIZE(stdbit_1b_tests); i++) { if (!stdbit_1b_test_one(&stdbit_1b_tests[i])) { ret = EXIT_FAILURE; } } for (size_t i = 0; i < ARRAY_SIZE(stdbit_fc_tests); i++) { if (!stdbit_fc_test_one(&stdbit_fc_tests[i])) { ret = EXIT_FAILURE; } } if (ret == EXIT_SUCCESS) { (void) printf("All tests passed successfully\n"); } return (ret); }