/* * libfyaml-test-private-id.c - libfyaml id allocation and handling * * Copyright (c) 2023 Pantelis Antoniou * * SPDX-License-Identifier: MIT */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #ifdef HAVE_ALLOCA_H #include #endif #include #include #include "fy-utils.h" #include "fy-id.h" #include "fy-check.h" /* check ffs works */ START_TEST(id_ffs) { static const struct { fy_id_bits v; int r; } ffs_check[] = { { .v = 0, .r = -1 }, { .v = ~(fy_id_bits_non_atomic)0, .r = 0 }, { .v = ((fy_id_bits_non_atomic)1 << 0), .r = 0 }, { .v = ((fy_id_bits_non_atomic)1 << (FY_ID_BITS_BITS - 1)), .r = (FY_ID_BITS_BITS - 1) }, { .v = ((fy_id_bits_non_atomic)1 << 0) | ((fy_id_bits_non_atomic)1 << (FY_ID_BITS_BITS - 1)), .r = 0 }, { .v = ((fy_id_bits_non_atomic)1 << (FY_ID_BITS_BITS / 2)), .r = (FY_ID_BITS_BITS / 2), }, }; unsigned int i; fy_id_bits v; int r; for (i = 0; i < ARRAY_SIZE(ffs_check); i++) { fy_atomic_store(&v, ffs_check[i].v); r = fy_id_ffs(v); ck_assert_int_eq(r, ffs_check[i].r); } } END_TEST /* a random bit number for the following tests, not a power of 2 */ #define BA_BITS 67 #define BA_DECL \ size_t ba_count = FY_ID_BITS_ARRAY_COUNT(BA_BITS); \ fy_id_bits *ba = alloca(ba_count * sizeof(*ba)) /* verify that reset clears everything */ START_TEST(id_reset) { BA_DECL; unsigned int i; fy_id_reset(ba, ba_count); for (i = 0; i < ba_count; i++) ck_assert_int_eq(ba[i], 0); } END_TEST /* verify that allocation to the full works, and then fails */ START_TEST(id_alloc_full) { BA_DECL; unsigned int ba_bits_actual = FY_ID_BITS_ARRAY_COUNT_BITS(BA_BITS); unsigned int i; int id, expected_id; fy_id_reset(ba, ba_count); /* allocate all, verify that we get numbers in sequence */ expected_id = 0; for (i = 0; i < ba_bits_actual; i++) { id = fy_id_alloc(ba, ba_count); ck_assert_int_ne(id, -1); ck_assert_int_eq(id, expected_id); expected_id++; } /* full, it must fail now */ id = fy_id_alloc(ba, ba_count); ck_assert_int_eq(id, -1); } END_TEST /* verify that allocation when almost full works, and then fails */ START_TEST(id_alloc_almost_full) { BA_DECL; unsigned int ba_bits_actual = FY_ID_BITS_ARRAY_COUNT_BITS(BA_BITS); unsigned int i; int id, expected_id; /* fill the array */ fy_id_reset(ba, ba_count); for (i = 0; i < ba_bits_actual; i++) fy_id_set_used(ba, ba_count, i); /* allocate all, verify that we get numbers in sequence */ expected_id = 0; for (i = 0; i < ba_bits_actual; i++) { /* free one, and allocate, it must succeed at the exact spot */ fy_id_free(ba, ba_count, i); id = fy_id_alloc(ba, ba_count); ck_assert_int_eq(id, expected_id); expected_id++; /* now it must fail */ id = fy_id_alloc(ba, ba_count); ck_assert_int_eq(id, -1); } } END_TEST /* verify that allocation of even bits works */ START_TEST(id_alloc_even) { BA_DECL; unsigned int ba_bits_actual = FY_ID_BITS_ARRAY_COUNT_BITS(BA_BITS); unsigned int i, j; int id, expected_id; /* fill the array */ fy_id_reset(ba, ba_count); for (i = 0; i < ba_bits_actual; i++) fy_id_set_used(ba, ba_count, i); /* free the even ids */ j = ba_bits_actual / 2; for (i = 0; i < j; i++) fy_id_free(ba, ba_count, i * 2); /* allocate all, verify that we get numbers in sequence */ expected_id = 0; for (i = 0; i < j; i++) { id = fy_id_alloc(ba, ba_count); ck_assert_int_eq(id, expected_id); expected_id += 2; } } END_TEST /* verify that allocation of odd bits works */ START_TEST(id_alloc_odd) { BA_DECL; unsigned int ba_bits_actual = FY_ID_BITS_ARRAY_COUNT_BITS(BA_BITS); unsigned int i, j; int id, expected_id; /* fill the array */ fy_id_reset(ba, ba_count); for (i = 0; i < ba_bits_actual; i++) fy_id_set_used(ba, ba_count, i); /* free the even ids */ j = ba_bits_actual / 2; for (i = 0; i < j; i++) fy_id_free(ba, ba_count, i * 2 + 1); /* allocate all, verify that we get numbers in sequence */ expected_id = 1; for (i = 0; i < j; i++) { id = fy_id_alloc(ba, ba_count); ck_assert_int_eq(id, expected_id); expected_id += 2; } } END_TEST /* verify that a specific allocation sequence works */ START_TEST(id_alloc_seq) { BA_DECL; unsigned int ba_bits_actual = FY_ID_BITS_ARRAY_COUNT_BITS(BA_BITS); unsigned int i; int id, expected_id; int check_ids[] = { 0, ba_bits_actual / 2 - 1, ba_bits_actual / 2, ba_bits_actual / 2 + 1, ba_bits_actual - 1 }; /* fill the array */ fy_id_reset(ba, ba_count); for (i = 0; i < ba_bits_actual; i++) fy_id_set_used(ba, ba_count, i); /* free those specific bits */ for (i = 0; i < ARRAY_SIZE(check_ids); i++) fy_id_free(ba, ba_count, check_ids[i]); /* now allocate in sequence */ expected_id = 0; for (i = 0; i < ARRAY_SIZE(check_ids); i++) { expected_id = check_ids[i]; id = fy_id_alloc(ba, ba_count); ck_assert_int_eq(id, expected_id); } } END_TEST /* verify that a iterator works for a single bit in array */ START_TEST(id_iter_single) { BA_DECL; unsigned int ba_bits_actual = FY_ID_BITS_ARRAY_COUNT_BITS(BA_BITS); unsigned int i; int id, expected_id, found_id; struct fy_id_iter iter; for (i = 0; i < ba_bits_actual; i++) { fy_id_reset(ba, ba_count); fy_id_set_used(ba, ba_count, i); expected_id = i; found_id = -1; fy_id_iter_begin(ba, ba_count, &iter); while ((id = fy_id_iter_next(ba, ba_count, &iter)) >= 0) { /* must find a single one */ ck_assert_int_lt(found_id, 0); found_id = id; ck_assert_int_eq(id, expected_id); } fy_id_iter_end(ba, ba_count, &iter); } } END_TEST /* verify that a iterator works for a full array */ START_TEST(id_iter_full) { BA_DECL; unsigned int ba_bits_actual = FY_ID_BITS_ARRAY_COUNT_BITS(BA_BITS); unsigned int i; int id, expected_id; struct fy_id_iter iter; fy_id_reset(ba, ba_count); for (i = 0; i < ba_bits_actual; i++) fy_id_set_used(ba, ba_count, i); expected_id = 0; fy_id_iter_begin(ba, ba_count, &iter); while ((id = fy_id_iter_next(ba, ba_count, &iter)) >= 0) { ck_assert_int_eq(id, expected_id); expected_id++; } /* we must have run through the whole array */ ck_assert_int_eq(expected_id, ba_bits_actual); fy_id_iter_end(ba, ba_count, &iter); } END_TEST /* verify that a iterator works for sequences up to 3 bits */ START_TEST(id_iter_seq) { BA_DECL; unsigned int ba_bits_actual = FY_ID_BITS_ARRAY_COUNT_BITS(BA_BITS); int idtab[][3] = { { 0, ba_bits_actual - 1, -1 }, { 0, 1, -1 }, { ba_bits_actual - 2, ba_bits_actual - 1, -1 }, { 0, FY_ID_BITS_BITS - 1, -1 }, { 0, FY_ID_BITS_BITS, -1 }, { FY_ID_BITS_BITS - 1, FY_ID_BITS_BITS, -1 }, { FY_ID_BITS_BITS, FY_ID_BITS_BITS + 1, -1 }, { 0, 1, 2 }, { 0, ba_bits_actual - 2, ba_bits_actual - 1 }, { ba_bits_actual - 3, ba_bits_actual - 2, ba_bits_actual - 1 }, }; unsigned int i; int id, expected_id; int p0, p1, p2; struct fy_id_iter iter; for (i = 0; i < ARRAY_SIZE(idtab); i++) { p0 = idtab[i][0]; p1 = idtab[i][1]; p2 = idtab[i][2]; fy_id_reset(ba, ba_count); if (p0 >= 0) fy_id_set_used(ba, ba_count, p0); if (p1 >= 0) fy_id_set_used(ba, ba_count, p1); if (p2 >= 0) fy_id_set_used(ba, ba_count, p2); fy_id_iter_begin(ba, ba_count, &iter); if (p0 >= 0) { id = fy_id_iter_next(ba, ba_count, &iter); expected_id = p0; ck_assert_int_eq(id, expected_id); fy_id_set_free(ba, ba_count, expected_id); } if (p1 >= 0) { id = fy_id_iter_next(ba, ba_count, &iter); expected_id = p1; ck_assert_int_eq(id, expected_id); fy_id_set_free(ba, ba_count, expected_id); } if (p2 >= 0) { id = fy_id_iter_next(ba, ba_count, &iter); expected_id = p2; ck_assert_int_eq(id, expected_id); fy_id_set_free(ba, ba_count, expected_id); } fy_id_iter_end(ba, ba_count, &iter); /* and now it should be empty */ fy_id_iter_begin(ba, ba_count, &iter); id = fy_id_iter_next(ba, ba_count, &iter); ck_assert_int_eq(id, -1); fy_id_iter_end(ba, ba_count, &iter); } } END_TEST void libfyaml_case_private_id(struct fy_check_suite *cs) { struct fy_check_testcase *ctc; ctc = fy_check_suite_add_test_case(cs, "private-id"); fy_check_testcase_add_test(ctc, id_ffs); fy_check_testcase_add_test(ctc, id_reset); fy_check_testcase_add_test(ctc, id_alloc_full); fy_check_testcase_add_test(ctc, id_alloc_almost_full); fy_check_testcase_add_test(ctc, id_alloc_even); fy_check_testcase_add_test(ctc, id_alloc_odd); fy_check_testcase_add_test(ctc, id_alloc_seq); fy_check_testcase_add_test(ctc, id_iter_single); fy_check_testcase_add_test(ctc, id_iter_full); fy_check_testcase_add_test(ctc, id_iter_seq); }