#include "unity/unity.h" #define PCRE2_CODE_UNIT_WIDTH 8 #include "pcre2.h" #include #include #include /* Pull in internal tokens/types needed for building meta streams and inspecting results. */ #include "pcre2_compile.h" /* The target has a wrapper compiled in the module. We declare it with an old-style prototype to avoid needing the internal typedef for eclass_context. */ extern BOOL test_compile_class_binary_loose(); static int bits_all_ones(const eclass_op_info *info) { for (int i = 0; i < 8; i++) { if (info->bits.classwords[i] != 0xffffffffu) return 0; } return 1; } static int bits_all_zero(const eclass_op_info *info) { for (int i = 0; i < 8; i++) { if (info->bits.classwords[i] != 0u) return 0; } return 1; } void setUp(void) { /* no-op */ } void tearDown(void) { /* no-op */ } /* Helper to invoke the wrapper safely */ static BOOL call_loose(BOOL negated, uint32_t *tokens, PCRE2_UCHAR *code_buf, eclass_op_info *out_info, PCRE2_SIZE *lengthptr, uint32_t **pp_after) { uint32_t *pp = tokens; PCRE2_UCHAR *pc = code_buf; BOOL rc = ((BOOL (*)(void*, BOOL, uint32_t**, PCRE2_UCHAR**, eclass_op_info*, PCRE2_SIZE*)) test_compile_class_binary_loose)(NULL, negated, &pp, &pc, out_info, lengthptr); if (pp_after) *pp_after = pp; return rc; } void test_compile_class_binary_loose_single_operand_any(void) { uint32_t tokens[] = { META_CLASS_EMPTY, META_CLASS_END }; PCRE2_UCHAR code[16] = {0}; eclass_op_info info; uint32_t *pp_after = NULL; BOOL ok = call_loose(FALSE, tokens, code, &info, NULL, &pp_after); TEST_ASSERT_TRUE(ok); TEST_ASSERT_EQUAL_PTR(&tokens[1], pp_after); /* advanced to META_CLASS_END */ TEST_ASSERT_EQUAL_INT(ECL_ANY, info.op_single_type); TEST_ASSERT_EQUAL_UINT32(1, info.length); TEST_ASSERT_TRUE(bits_all_ones(&info)); TEST_ASSERT_EQUAL_UINT8(ECL_ANY, info.code_start[0]); } void test_compile_class_binary_loose_single_operand_negated(void) { uint32_t tokens[] = { META_CLASS_EMPTY, META_CLASS_END }; PCRE2_UCHAR code[16] = {0}; eclass_op_info info; uint32_t *pp_after = NULL; BOOL ok = call_loose(TRUE, tokens, code, &info, NULL, &pp_after); TEST_ASSERT_TRUE(ok); TEST_ASSERT_EQUAL_PTR(&tokens[1], pp_after); TEST_ASSERT_EQUAL_INT(ECL_NONE, info.op_single_type); TEST_ASSERT_EQUAL_UINT32(1, info.length); TEST_ASSERT_TRUE(bits_all_zero(&info)); TEST_ASSERT_EQUAL_UINT8(ECL_NONE, info.code_start[0]); } void test_compile_class_binary_loose_unary_not_operand(void) { uint32_t tokens[] = { META_ECLASS_NOT, META_CLASS_EMPTY, META_CLASS_END }; PCRE2_UCHAR code[16] = {0}; eclass_op_info info; uint32_t *pp_after = NULL; BOOL ok = call_loose(FALSE, tokens, code, &info, NULL, &pp_after); TEST_ASSERT_TRUE(ok); TEST_ASSERT_EQUAL_PTR(&tokens[2], pp_after); TEST_ASSERT_EQUAL_INT(ECL_NONE, info.op_single_type); TEST_ASSERT_TRUE(bits_all_zero(&info)); TEST_ASSERT_EQUAL_UINT8(ECL_NONE, info.code_start[0]); } void test_compile_class_binary_loose_double_not_operand(void) { uint32_t tokens[] = { META_ECLASS_NOT, META_ECLASS_NOT, META_CLASS_EMPTY, META_CLASS_END }; PCRE2_UCHAR code[16] = {0}; eclass_op_info info; uint32_t *pp_after = NULL; BOOL ok = call_loose(FALSE, tokens, code, &info, NULL, &pp_after); TEST_ASSERT_TRUE(ok); TEST_ASSERT_EQUAL_PTR(&tokens[3], pp_after); TEST_ASSERT_EQUAL_INT(ECL_ANY, info.op_single_type); TEST_ASSERT_TRUE(bits_all_ones(&info)); TEST_ASSERT_EQUAL_UINT8(ECL_ANY, info.code_start[0]); } void test_compile_class_binary_loose_or_simplify(void) { uint32_t tokens[] = { META_CLASS_EMPTY, META_ECLASS_OR, META_CLASS_EMPTY, META_CLASS_END }; PCRE2_UCHAR code[16] = {0}; eclass_op_info info; uint32_t *pp_after = NULL; BOOL ok = call_loose(FALSE, tokens, code, &info, NULL, &pp_after); TEST_ASSERT_TRUE(ok); TEST_ASSERT_EQUAL_PTR(&tokens[3], pp_after); TEST_ASSERT_EQUAL_INT(ECL_ANY, info.op_single_type); TEST_ASSERT_TRUE(bits_all_ones(&info)); TEST_ASSERT_EQUAL_UINT32(1, info.length); /* simplified to single op */ TEST_ASSERT_EQUAL_UINT8(ECL_ANY, info.code_start[0]); } void test_compile_class_binary_loose_or_length_only_mode(void) { uint32_t tokens[] = { META_CLASS_EMPTY, META_ECLASS_OR, META_CLASS_EMPTY, META_CLASS_END }; PCRE2_UCHAR code[16] = {0}; eclass_op_info info; PCRE2_SIZE len = 0; PCRE2_UCHAR *code_before = code; uint32_t *pp_after = NULL; BOOL ok = call_loose(FALSE, tokens, code, &info, &len, &pp_after); TEST_ASSERT_TRUE(ok); TEST_ASSERT_EQUAL_PTR(code_before, code_before); /* code pointer we pass doesn't change */ TEST_ASSERT_EQUAL_PTR(&tokens[3], pp_after); /* Two operands were encountered; even if simplified, the function accounts for their individual writes in lengthptr mode. */ TEST_ASSERT_EQUAL_UINT32(2, len); } void test_compile_class_binary_loose_sub_mapping(void) { uint32_t tokens[] = { META_CLASS_EMPTY, META_ECLASS_SUB, META_CLASS_EMPTY, META_CLASS_END }; PCRE2_UCHAR code[16] = {0}; eclass_op_info info; uint32_t *pp_after = NULL; BOOL ok = call_loose(FALSE, tokens, code, &info, NULL, &pp_after); TEST_ASSERT_TRUE(ok); TEST_ASSERT_EQUAL_PTR(&tokens[3], pp_after); TEST_ASSERT_EQUAL_INT(ECL_NONE, info.op_single_type); TEST_ASSERT_TRUE(bits_all_zero(&info)); TEST_ASSERT_EQUAL_UINT32(1, info.length); } void test_compile_class_binary_loose_xor_basic(void) { uint32_t tokens[] = { META_CLASS_EMPTY, META_ECLASS_XOR, META_CLASS_EMPTY, META_CLASS_END }; PCRE2_UCHAR code[16] = {0}; eclass_op_info info; uint32_t *pp_after = NULL; BOOL ok = call_loose(FALSE, tokens, code, &info, NULL, &pp_after); TEST_ASSERT_TRUE(ok); TEST_ASSERT_EQUAL_PTR(&tokens[3], pp_after); TEST_ASSERT_EQUAL_INT(ECL_NONE, info.op_single_type); TEST_ASSERT_TRUE(bits_all_zero(&info)); TEST_ASSERT_EQUAL_UINT32(1, info.length); } void test_compile_class_binary_loose_negated_or_demorgan(void) { uint32_t tokens[] = { META_CLASS_EMPTY, META_ECLASS_OR, META_CLASS_EMPTY, META_CLASS_END }; PCRE2_UCHAR code[16] = {0}; eclass_op_info info; uint32_t *pp_after = NULL; BOOL ok = call_loose(TRUE, tokens, code, &info, NULL, &pp_after); TEST_ASSERT_TRUE(ok); TEST_ASSERT_EQUAL_PTR(&tokens[3], pp_after); /* !(ANY || ANY) -> NONE */ TEST_ASSERT_EQUAL_INT(ECL_NONE, info.op_single_type); TEST_ASSERT_TRUE(bits_all_zero(&info)); } void test_compile_class_binary_loose_negated_xor_becomes_xnor(void) { uint32_t tokens[] = { META_CLASS_EMPTY_NOT, META_ECLASS_XOR, META_CLASS_EMPTY_NOT, META_CLASS_END }; PCRE2_UCHAR code[16] = {0}; eclass_op_info info; uint32_t *pp_after = NULL; BOOL ok = call_loose(TRUE, tokens, code, &info, NULL, &pp_after); TEST_ASSERT_TRUE(ok); TEST_ASSERT_EQUAL_PTR(&tokens[3], pp_after); /* With outer negation, XOR gets complemented (XNOR); with both operands effectively NONE under inner folding, XNOR -> ANY. */ TEST_ASSERT_EQUAL_INT(ECL_ANY, info.op_single_type); TEST_ASSERT_TRUE(bits_all_ones(&info)); } void test_compile_class_binary_loose_precedence_and_before_or(void) { /* Expression: !EMPTY && EMPTY || !EMPTY Expected: (!ANY && ANY) || !ANY -> (NONE && ANY) || NONE -> NONE || NONE -> NONE */ uint32_t tokens[] = { META_ECLASS_NOT, META_CLASS_EMPTY, META_ECLASS_AND, META_CLASS_EMPTY, META_ECLASS_OR, META_ECLASS_NOT, META_CLASS_EMPTY, META_CLASS_END }; PCRE2_UCHAR code[32] = {0}; eclass_op_info info; uint32_t *pp_after = NULL; BOOL ok = call_loose(FALSE, tokens, code, &info, NULL, &pp_after); TEST_ASSERT_TRUE(ok); TEST_ASSERT_EQUAL_PTR(&tokens[7], pp_after); TEST_ASSERT_EQUAL_INT(ECL_NONE, info.op_single_type); TEST_ASSERT_TRUE(bits_all_zero(&info)); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_compile_class_binary_loose_single_operand_any); RUN_TEST(test_compile_class_binary_loose_single_operand_negated); RUN_TEST(test_compile_class_binary_loose_unary_not_operand); RUN_TEST(test_compile_class_binary_loose_double_not_operand); RUN_TEST(test_compile_class_binary_loose_or_simplify); RUN_TEST(test_compile_class_binary_loose_or_length_only_mode); RUN_TEST(test_compile_class_binary_loose_sub_mapping); RUN_TEST(test_compile_class_binary_loose_xor_basic); RUN_TEST(test_compile_class_binary_loose_negated_or_demorgan); RUN_TEST(test_compile_class_binary_loose_negated_xor_becomes_xnor); RUN_TEST(test_compile_class_binary_loose_precedence_and_before_or); return UNITY_END(); }