#include "unity/unity.h" #define PCRE2_CODE_UNIT_WIDTH 8 #include "pcre2.h" /* We need internal ECLASS tokens/types and eclass_op_info. */ #include "pcre2_compile.h" #include #include /* The wrapper is defined in the module, but is not declared in a header. */ extern BOOL test_compile_class_binary_tight(); void setUp(void) { /* nothing */ } void tearDown(void) { /* nothing */ } /* Helper: assert bitmap is all ones (0xFF..). */ static void assert_bits_all_ones(const eclass_op_info *op) { for (int i = 0; i < 8; i++) { TEST_ASSERT_EQUAL_HEX32(0xFFFFFFFFu, op->bits.classwords[i]); } } /* Helper: assert bitmap is all zeros. */ static void assert_bits_all_zeros(const eclass_op_info *op) { for (int i = 0; i < 8; i++) { TEST_ASSERT_EQUAL_HEX32(0u, op->bits.classwords[i]); } } /* Case 1: single operand (META_CLASS_EMPTY_NOT) -> ECL_ANY with all-ones bitmap. */ void test_compile_class_binary_tight_single_any(void) { /* Tokens: [META_CLASS_EMPTY_NOT] then end. */ uint32_t tokens[] = { META_CLASS_EMPTY_NOT, META_CLASS_END }; uint32_t *ptr = tokens; uint32_t *pptr = ptr; PCRE2_UCHAR codebuf[16]; PCRE2_UCHAR *code = codebuf; eclass_op_info op_info; memset(&op_info, 0, sizeof(op_info)); BOOL ok = test_compile_class_binary_tight(NULL, /*negated=*/FALSE, &pptr, &code, &op_info, /*lengthptr=*/NULL); TEST_ASSERT_TRUE(ok); /* One byte written: ECL_ANY. */ TEST_ASSERT_EQUAL_PTR(codebuf + 1, code); TEST_ASSERT_EQUAL_UINT8(ECL_ANY, codebuf[0]); TEST_ASSERT_EQUAL_UINT8(ECL_ANY, op_info.op_single_type); TEST_ASSERT_EQUAL_UINT(1, op_info.length); assert_bits_all_ones(&op_info); /* pptr advanced to META_CLASS_END */ TEST_ASSERT_EQUAL_PTR(&tokens[1], pptr); TEST_ASSERT_EQUAL_UINT(META_CLASS_END, *pptr); } /* Case 2: ANY && ANY -> ANY (no operator emitted; RHS dropped). */ void test_compile_class_binary_tight_and_any_any(void) { uint32_t tokens[] = { META_CLASS_EMPTY_NOT, META_ECLASS_AND, META_CLASS_EMPTY_NOT, META_CLASS_END }; uint32_t *pptr = tokens; PCRE2_UCHAR codebuf[16]; PCRE2_UCHAR *code = codebuf; eclass_op_info op_info; memset(&op_info, 0, sizeof(op_info)); BOOL ok = test_compile_class_binary_tight(NULL, FALSE, &pptr, &code, &op_info, NULL); TEST_ASSERT_TRUE(ok); /* Still a single byte: ECL_ANY. */ TEST_ASSERT_EQUAL_PTR(codebuf + 1, code); TEST_ASSERT_EQUAL_UINT8(ECL_ANY, codebuf[0]); TEST_ASSERT_EQUAL_UINT8(ECL_ANY, op_info.op_single_type); TEST_ASSERT_EQUAL_UINT(1, op_info.length); assert_bits_all_ones(&op_info); /* pptr should now point to META_CLASS_END */ TEST_ASSERT_EQUAL_PTR(&tokens[3], pptr); TEST_ASSERT_EQUAL_UINT(META_CLASS_END, *pptr); } /* Case 3: NONE && ANY -> NONE. Use META_CLASS_EMPTY (NONE) and META_CLASS_EMPTY_NOT (ANY). */ void test_compile_class_binary_tight_and_none_any(void) { uint32_t tokens[] = { META_CLASS_EMPTY, META_ECLASS_AND, META_CLASS_EMPTY_NOT, META_CLASS_END }; uint32_t *pptr = tokens; PCRE2_UCHAR codebuf[16]; PCRE2_UCHAR *code = codebuf; eclass_op_info op_info; memset(&op_info, 0, sizeof(op_info)); BOOL ok = test_compile_class_binary_tight(NULL, FALSE, &pptr, &code, &op_info, NULL); TEST_ASSERT_TRUE(ok); TEST_ASSERT_EQUAL_PTR(codebuf + 1, code); TEST_ASSERT_EQUAL_UINT8(ECL_NONE, codebuf[0]); TEST_ASSERT_EQUAL_UINT8(ECL_NONE, op_info.op_single_type); TEST_ASSERT_EQUAL_UINT(1, op_info.length); assert_bits_all_zeros(&op_info); TEST_ASSERT_EQUAL_PTR(&tokens[3], pptr); TEST_ASSERT_EQUAL_UINT(META_CLASS_END, *pptr); } /* Case 4: negated context flips AND to OR semantics. Input: EMPTY && EMPTY, negated=true EMPTY -> NONE when un-negated; with negated=true, each operand becomes ANY. ANY || ANY -> ANY. */ void test_compile_class_binary_tight_negated_transforms_and_to_or(void) { uint32_t tokens[] = { META_CLASS_EMPTY, META_ECLASS_AND, META_CLASS_EMPTY, META_CLASS_END }; uint32_t *pptr = tokens; PCRE2_UCHAR codebuf[16]; PCRE2_UCHAR *code = codebuf; eclass_op_info op_info; memset(&op_info, 0, sizeof(op_info)); BOOL ok = test_compile_class_binary_tight(NULL, /*negated=*/TRUE, &pptr, &code, &op_info, NULL); TEST_ASSERT_TRUE(ok); TEST_ASSERT_EQUAL_PTR(codebuf + 1, code); TEST_ASSERT_EQUAL_UINT8(ECL_ANY, codebuf[0]); TEST_ASSERT_EQUAL_UINT8(ECL_ANY, op_info.op_single_type); TEST_ASSERT_EQUAL_UINT(1, op_info.length); assert_bits_all_ones(&op_info); TEST_ASSERT_EQUAL_PTR(&tokens[3], pptr); TEST_ASSERT_EQUAL_UINT(META_CLASS_END, *pptr); } /* Case 5: length-only mode. Ensure *lengthptr increases by operand bytes and no code is written. */ void test_compile_class_binary_tight_length_only_mode(void) { uint32_t tokens[] = { META_CLASS_EMPTY_NOT, META_ECLASS_AND, META_CLASS_EMPTY_NOT, META_CLASS_END }; uint32_t *pptr = tokens; PCRE2_UCHAR codebuf[16]; memset(codebuf, 0xA5, sizeof(codebuf)); PCRE2_UCHAR *code = codebuf; /* Should remain unchanged in length-only mode. */ eclass_op_info op_info; memset(&op_info, 0, sizeof(op_info)); PCRE2_SIZE length = 0; BOOL ok = test_compile_class_binary_tight(NULL, FALSE, &pptr, &code, &op_info, &length); TEST_ASSERT_TRUE(ok); /* Two operands each contribute 1 unit; no operator emitted after folding. */ TEST_ASSERT_EQUAL_UINT(2, length); /* Code pointer should be unchanged when lengthptr != NULL. */ TEST_ASSERT_EQUAL_PTR(codebuf, code); /* op_info still reflects the folded result (ANY, all ones). */ TEST_ASSERT_EQUAL_UINT8(ECL_ANY, op_info.op_single_type); assert_bits_all_ones(&op_info); TEST_ASSERT_EQUAL_PTR(&tokens[3], pptr); TEST_ASSERT_EQUAL_UINT(META_CLASS_END, *pptr); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_compile_class_binary_tight_single_any); RUN_TEST(test_compile_class_binary_tight_and_any_any); RUN_TEST(test_compile_class_binary_tight_and_none_any); RUN_TEST(test_compile_class_binary_tight_negated_transforms_and_to_or); RUN_TEST(test_compile_class_binary_tight_length_only_mode); return UNITY_END(); }