Coverage Report

Created: 2025-11-16 06:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/Zend/zend_smart_str.c
Line
Count
Source
1
/*
2
   +----------------------------------------------------------------------+
3
   | Copyright (c) The PHP Group                                          |
4
   +----------------------------------------------------------------------+
5
   | This source file is subject to version 3.01 of the PHP license,      |
6
   | that is bundled with this package in the file LICENSE, and is        |
7
   | available through the world-wide-web at the following url:           |
8
   | https://www.php.net/license/3_01.txt                                 |
9
   | If you did not receive a copy of the PHP license and are unable to   |
10
   | obtain it through the world-wide-web, please send a note to          |
11
   | license@php.net so we can mail you a copy immediately.               |
12
   +----------------------------------------------------------------------+
13
   | Author: Dmitry Stogov <dmitry@php.net>                               |
14
   +----------------------------------------------------------------------+
15
 */
16
17
#include <zend.h>
18
#include "zend_smart_str.h"
19
#include "zend_smart_string.h"
20
#include "zend_enum.h"
21
22
8.46M
#define SMART_STR_OVERHEAD   (ZEND_MM_OVERHEAD + _ZSTR_HEADER_SIZE + 1)
23
8.25M
#define SMART_STR_START_SIZE 256
24
8.25M
#define SMART_STR_START_LEN  (SMART_STR_START_SIZE - SMART_STR_OVERHEAD)
25
#define SMART_STR_PAGE       4096
26
27
#define SMART_STR_NEW_LEN(len) \
28
4.33M
  (ZEND_MM_ALIGNED_SIZE_EX(len + SMART_STR_OVERHEAD, SMART_STR_PAGE) - SMART_STR_OVERHEAD)
29
30
ZEND_API void ZEND_FASTCALL smart_str_erealloc(smart_str *str, size_t len)
31
4.33M
{
32
4.33M
  if (UNEXPECTED(!str->s)) {
33
4.12M
    str->a = len <= SMART_STR_START_LEN
34
4.12M
        ? SMART_STR_START_LEN
35
4.12M
        : SMART_STR_NEW_LEN(len);
36
4.12M
    str->s = zend_string_alloc(str->a, 0);
37
4.12M
    ZSTR_LEN(str->s) = 0;
38
4.12M
  } else {
39
201k
    str->a = SMART_STR_NEW_LEN(len);
40
201k
    str->s = (zend_string *) erealloc2(str->s, str->a + _ZSTR_HEADER_SIZE + 1, _ZSTR_HEADER_SIZE + ZSTR_LEN(str->s));
41
201k
  }
42
4.33M
}
43
44
ZEND_API void ZEND_FASTCALL smart_str_realloc(smart_str *str, size_t len)
45
0
{
46
0
  if (UNEXPECTED(!str->s)) {
47
0
    str->a = len <= SMART_STR_START_LEN
48
0
        ? SMART_STR_START_LEN
49
0
        : SMART_STR_NEW_LEN(len);
50
0
    str->s = zend_string_alloc(str->a, 1);
51
0
    ZSTR_LEN(str->s) = 0;
52
0
  } else {
53
0
    str->a = SMART_STR_NEW_LEN(len);
54
0
    str->s = (zend_string *) perealloc(str->s, str->a + _ZSTR_HEADER_SIZE + 1, 1);
55
0
  }
56
0
}
57
58
/* Windows uses VK_ESCAPE instead of \e */
59
#ifndef VK_ESCAPE
60
376k
#define VK_ESCAPE '\e'
61
#endif
62
63
21.1k
static size_t zend_compute_escaped_string_len(const char *s, size_t l) {
64
21.1k
  size_t i, len = l;
65
400k
  for (i = 0; i < l; ++i) {
66
378k
    char c = s[i];
67
378k
    if (c == '\n' || c == '\r' || c == '\t' ||
68
376k
      c == '\f' || c == '\v' || c == '\\' || c == VK_ESCAPE) {
69
4.30k
      len += 1;
70
374k
    } else if (c < 32 || c > 126) {
71
57.7k
      len += 3;
72
57.7k
    }
73
378k
  }
74
21.1k
  return len;
75
21.1k
}
76
77
21.1k
ZEND_API void ZEND_FASTCALL smart_str_append_escaped(smart_str *str, const char *s, size_t l) {
78
21.1k
  char *res;
79
21.1k
  size_t i, len = zend_compute_escaped_string_len(s, l);
80
81
21.1k
  smart_str_alloc(str, len, 0);
82
21.1k
  res = &ZSTR_VAL(str->s)[ZSTR_LEN(str->s)];
83
21.1k
  ZSTR_LEN(str->s) += len;
84
85
400k
  for (i = 0; i < l; ++i) {
86
378k
    unsigned char c = s[i];
87
378k
    if (c < 32 || c == '\\' || c > 126) {
88
62.0k
      *res++ = '\\';
89
62.0k
      switch (c) {
90
927
        case '\n': *res++ = 'n'; break;
91
735
        case '\r': *res++ = 'r'; break;
92
653
        case '\t': *res++ = 't'; break;
93
221
        case '\f': *res++ = 'f'; break;
94
367
        case '\v': *res++ = 'v'; break;
95
483
        case '\\': *res++ = '\\'; break;
96
919
        case VK_ESCAPE: *res++ = 'e'; break;
97
57.7k
        default:
98
57.7k
          *res++ = 'x';
99
57.7k
          if ((c >> 4) < 10) {
100
39.7k
            *res++ = (c >> 4) + '0';
101
39.7k
          } else {
102
18.0k
            *res++ = (c >> 4) + 'A' - 10;
103
18.0k
          }
104
57.7k
          if ((c & 0xf) < 10) {
105
40.9k
            *res++ = (c & 0xf) + '0';
106
40.9k
          } else {
107
16.7k
            *res++ = (c & 0xf) + 'A' - 10;
108
16.7k
          }
109
62.0k
      }
110
316k
    } else {
111
316k
      *res++ = c;
112
316k
    }
113
378k
  }
114
21.1k
}
115
116
ZEND_API void ZEND_FASTCALL smart_str_append_double(
117
63.8k
    smart_str *str, double num, int precision, bool zero_fraction) {
118
63.8k
  char buf[ZEND_DOUBLE_MAX_LENGTH];
119
  /* Model snprintf precision behavior. */
120
63.8k
  zend_gcvt(num, precision ? precision : 1, '.', 'E', buf);
121
63.8k
  smart_str_appends(str, buf);
122
63.8k
  if (zero_fraction && zend_finite(num) && !strchr(buf, '.')) {
123
35.1k
    smart_str_appendl(str, ".0", 2);
124
35.1k
  }
125
63.8k
}
126
127
4.61k
ZEND_API void smart_str_append_printf(smart_str *dest, const char *format, ...) {
128
4.61k
  va_list arg;
129
4.61k
  va_start(arg, format);
130
4.61k
  zend_printf_to_smart_str(dest, format, arg);
131
4.61k
  va_end(arg);
132
4.61k
}
133
134
0
ZEND_API void smart_string_append_printf(smart_string *dest, const char *format, ...) {
135
0
  va_list arg;
136
0
  va_start(arg, format);
137
0
  zend_printf_to_smart_string(dest, format, arg);
138
0
  va_end(arg);
139
0
}
140
141
31.1M
#define SMART_STRING_OVERHEAD   (ZEND_MM_OVERHEAD + 1)
142
31.1M
#define SMART_STRING_START_SIZE 256
143
31.1M
#define SMART_STRING_START_LEN  (SMART_STRING_START_SIZE - SMART_STRING_OVERHEAD)
144
#define SMART_STRING_PAGE       4096
145
146
ZEND_API void ZEND_FASTCALL _smart_string_alloc_persistent(smart_string *str, size_t len)
147
0
{
148
0
  if (!str->c) {
149
0
    str->len = 0;
150
0
    if (len <= SMART_STRING_START_LEN) {
151
0
      str->a = SMART_STRING_START_LEN;
152
0
    } else {
153
0
      str->a = ZEND_MM_ALIGNED_SIZE_EX(len + SMART_STRING_OVERHEAD, SMART_STRING_PAGE) - SMART_STRING_OVERHEAD;
154
0
    }
155
0
    str->c = pemalloc(str->a + 1, 1);
156
0
  } else {
157
0
    if (UNEXPECTED((size_t) len > SIZE_MAX - str->len)) {
158
0
      zend_error_noreturn(E_ERROR, "String size overflow");
159
0
    }
160
0
    len += str->len;
161
0
    str->a = ZEND_MM_ALIGNED_SIZE_EX(len + SMART_STRING_OVERHEAD, SMART_STRING_PAGE) - SMART_STRING_OVERHEAD;
162
0
    str->c = perealloc(str->c, str->a + 1, 1);
163
0
  }
164
0
}
165
166
ZEND_API void ZEND_FASTCALL _smart_string_alloc(smart_string *str, size_t len)
167
15.5M
{
168
15.5M
  if (!str->c) {
169
15.5M
    str->len = 0;
170
15.5M
    if (len <= SMART_STRING_START_LEN) {
171
15.5M
      str->a = SMART_STRING_START_LEN;
172
15.5M
      str->c = emalloc(SMART_STRING_START_LEN + 1);
173
15.5M
    } else {
174
679
      str->a = ZEND_MM_ALIGNED_SIZE_EX(len + SMART_STRING_OVERHEAD, SMART_STRING_PAGE) - SMART_STRING_OVERHEAD;
175
679
      if (EXPECTED(str->a < (ZEND_MM_CHUNK_SIZE - SMART_STRING_OVERHEAD))) {
176
679
        str->c = emalloc_large(str->a + 1);
177
679
      } else {
178
        /* allocate a huge chunk */
179
0
        str->c = emalloc(str->a + 1);
180
0
      }
181
679
    }
182
15.5M
  } else {
183
8.57k
    if (UNEXPECTED((size_t) len > SIZE_MAX - str->len)) {
184
0
      zend_error_noreturn(E_ERROR, "String size overflow");
185
0
    }
186
8.57k
    len += str->len;
187
8.57k
    str->a = ZEND_MM_ALIGNED_SIZE_EX(len + SMART_STRING_OVERHEAD, SMART_STRING_PAGE) - SMART_STRING_OVERHEAD;
188
8.57k
    str->c = erealloc2(str->c, str->a + 1, str->len);
189
8.57k
  }
190
15.5M
}
191
192
ZEND_API void ZEND_FASTCALL smart_str_append_escaped_truncated(smart_str *str, const zend_string *value, size_t length)
193
20.8k
{
194
20.8k
  smart_str_append_escaped(str, ZSTR_VAL(value), MIN(length, ZSTR_LEN(value)));
195
196
20.8k
  if (ZSTR_LEN(value) > length) {
197
2.91k
    smart_str_appendl(str, "...", sizeof("...")-1);
198
2.91k
  }
199
20.8k
}
200
201
23.2k
ZEND_API void ZEND_FASTCALL smart_str_append_scalar(smart_str *dest, const zval *value, size_t truncate) {
202
23.2k
  ZEND_ASSERT(Z_TYPE_P(value) <= IS_STRING);
203
204
23.2k
  switch (Z_TYPE_P(value)) {
205
5
    case IS_UNDEF:
206
2.05k
    case IS_NULL:
207
2.05k
      smart_str_appendl(dest, "NULL", sizeof("NULL")-1);
208
2.05k
    break;
209
210
28
    case IS_TRUE:
211
28
      smart_str_appendl(dest, "true", sizeof("true")-1);
212
28
    break;
213
214
30
    case IS_FALSE:
215
30
      smart_str_appendl(dest, "false", sizeof("false")-1);
216
30
    break;
217
218
21
    case IS_DOUBLE:
219
21
      smart_str_append_double(dest, Z_DVAL_P(value), (int) EG(precision), true);
220
21
    break;
221
222
262
    case IS_LONG:
223
262
      smart_str_append_long(dest, Z_LVAL_P(value));
224
262
    break;
225
226
20.8k
    case IS_STRING:
227
20.8k
      smart_str_appendc(dest, '\'');
228
20.8k
      smart_str_append_escaped_truncated(dest, Z_STR_P(value), truncate);
229
20.8k
      smart_str_appendc(dest, '\'');
230
20.8k
    break;
231
232
0
    EMPTY_SWITCH_DEFAULT_CASE();
233
23.2k
  }
234
23.2k
}
235
236
ZEND_API zend_result ZEND_FASTCALL smart_str_append_zval(smart_str *dest, const zval *value, size_t truncate)
237
25.1k
{
238
25.1k
  if (Z_TYPE_P(value) <= IS_STRING) {
239
23.2k
    smart_str_append_scalar(dest, value, truncate);
240
23.2k
  } else if (Z_TYPE_P(value) == IS_OBJECT && (Z_OBJCE_P(value)->ce_flags & ZEND_ACC_ENUM)) {
241
6
    smart_str_append(dest, Z_OBJCE_P(value)->name);
242
6
    smart_str_appends(dest, "::");
243
6
    smart_str_append(dest, Z_STR_P(zend_enum_fetch_case_name(Z_OBJ_P(value))));
244
1.94k
  } else {
245
1.94k
    return FAILURE;
246
1.94k
  }
247
23.2k
  return SUCCESS;
248
25.1k
}