File size: 6,103 Bytes
864071c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
#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 <string.h>
#include <stdint.h>

/* 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();
}