Coverage Report

Created: 2026-06-13 07:01

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 © The PHP Group and Contributors.                          |
4
   +----------------------------------------------------------------------+
5
   | This source file is subject to the Modified BSD License that is      |
6
   | bundled with this package in the file LICENSE, and is available      |
7
   | through the World Wide Web at <https://www.php.net/license/>.        |
8
   |                                                                      |
9
   | SPDX-License-Identifier: BSD-3-Clause                                |
10
   +----------------------------------------------------------------------+
11
   | Author: Dmitry Stogov <dmitry@php.net>                               |
12
   +----------------------------------------------------------------------+
13
 */
14
15
#include <zend.h>
16
#include "zend_smart_str.h"
17
#include "zend_smart_string.h"
18
#include "zend_enum.h"
19
20
11.2M
#define SMART_STR_OVERHEAD   (ZEND_MM_OVERHEAD + _ZSTR_HEADER_SIZE + 1)
21
10.5M
#define SMART_STR_START_SIZE 256
22
10.5M
#define SMART_STR_START_LEN  (SMART_STR_START_SIZE - SMART_STR_OVERHEAD)
23
#define SMART_STR_PAGE       4096
24
25
#define SMART_STR_NEW_LEN(len) \
26
6.00M
  (ZEND_MM_ALIGNED_SIZE_EX(len + SMART_STR_OVERHEAD, SMART_STR_PAGE) - SMART_STR_OVERHEAD)
27
28
ZEND_API void ZEND_FASTCALL smart_str_erealloc(smart_str *str, size_t len)
29
6.00M
{
30
6.00M
  if (UNEXPECTED(!str->s)) {
31
5.28M
    str->a = len <= SMART_STR_START_LEN
32
5.28M
        ? SMART_STR_START_LEN
33
5.28M
        : SMART_STR_NEW_LEN(len);
34
5.28M
    str->s = zend_string_alloc(str->a, 0);
35
5.28M
    ZSTR_LEN(str->s) = 0;
36
5.28M
  } else {
37
721k
    str->a = SMART_STR_NEW_LEN(len);
38
721k
    str->s = (zend_string *) erealloc2(str->s, str->a + _ZSTR_HEADER_SIZE + 1, _ZSTR_HEADER_SIZE + ZSTR_LEN(str->s));
39
721k
  }
40
6.00M
}
41
42
ZEND_API void ZEND_FASTCALL smart_str_realloc(smart_str *str, size_t len)
43
0
{
44
0
  if (UNEXPECTED(!str->s)) {
45
0
    str->a = len <= SMART_STR_START_LEN
46
0
        ? SMART_STR_START_LEN
47
0
        : SMART_STR_NEW_LEN(len);
48
0
    str->s = zend_string_alloc(str->a, 1);
49
0
    ZSTR_LEN(str->s) = 0;
50
0
  } else {
51
0
    str->a = SMART_STR_NEW_LEN(len);
52
0
    str->s = (zend_string *) perealloc(str->s, str->a + _ZSTR_HEADER_SIZE + 1, 1);
53
0
  }
54
0
}
55
56
/* Windows uses VK_ESCAPE instead of \e */
57
#ifndef VK_ESCAPE
58
397k
#define VK_ESCAPE '\e'
59
#endif
60
61
20.9k
static size_t zend_compute_escaped_string_len(const char *s, size_t l) {
62
20.9k
  size_t i, len = l;
63
422k
  for (i = 0; i < l; ++i) {
64
401k
    char c = s[i];
65
401k
    if (c == '\n' || c == '\r' || c == '\t' ||
66
398k
      c == '\f' || c == '\v' || c == '\\' || c == VK_ESCAPE) {
67
4.99k
      len += 1;
68
396k
    } else if (c < 32 || c > 126) {
69
77.0k
      len += 3;
70
77.0k
    }
71
401k
  }
72
20.9k
  return len;
73
20.9k
}
74
75
20.9k
ZEND_API void ZEND_FASTCALL smart_str_append_escaped(smart_str *str, const char *s, size_t l) {
76
20.9k
  char *res;
77
20.9k
  size_t i, len = zend_compute_escaped_string_len(s, l);
78
79
20.9k
  smart_str_alloc(str, len, 0);
80
20.9k
  res = &ZSTR_VAL(str->s)[ZSTR_LEN(str->s)];
81
20.9k
  ZSTR_LEN(str->s) += len;
82
83
422k
  for (i = 0; i < l; ++i) {
84
401k
    unsigned char c = s[i];
85
401k
    if (c < 32 || c == '\\' || c > 126) {
86
82.0k
      *res++ = '\\';
87
82.0k
      switch (c) {
88
1.03k
        case '\n': *res++ = 'n'; break;
89
1.01k
        case '\r': *res++ = 'r'; break;
90
826
        case '\t': *res++ = 't'; break;
91
344
        case '\f': *res++ = 'f'; break;
92
577
        case '\v': *res++ = 'v'; break;
93
532
        case '\\': *res++ = '\\'; break;
94
666
        case VK_ESCAPE: *res++ = 'e'; break;
95
77.0k
        default:
96
77.0k
          *res++ = 'x';
97
77.0k
          if ((c >> 4) < 10) {
98
54.3k
            *res++ = (c >> 4) + '0';
99
54.3k
          } else {
100
22.7k
            *res++ = (c >> 4) + 'A' - 10;
101
22.7k
          }
102
77.0k
          if ((c & 0xf) < 10) {
103
49.9k
            *res++ = (c & 0xf) + '0';
104
49.9k
          } else {
105
27.1k
            *res++ = (c & 0xf) + 'A' - 10;
106
27.1k
          }
107
82.0k
      }
108
319k
    } else {
109
319k
      *res++ = c;
110
319k
    }
111
401k
  }
112
20.9k
}
113
114
ZEND_API void ZEND_FASTCALL smart_str_append_double(
115
154k
    smart_str *str, double num, int precision, bool zero_fraction) {
116
154k
  char buf[ZEND_DOUBLE_MAX_LENGTH];
117
  /* Model snprintf precision behavior. */
118
154k
  zend_gcvt(num, precision ? precision : 1, '.', 'E', buf);
119
154k
  smart_str_appends(str, buf);
120
154k
  if (zero_fraction && zend_finite(num) && !strchr(buf, '.')) {
121
107k
    smart_str_appendl(str, ".0", 2);
122
107k
  }
123
154k
}
124
125
17.0k
ZEND_API void smart_str_append_printf(smart_str *dest, const char *format, ...) {
126
17.0k
  va_list arg;
127
17.0k
  va_start(arg, format);
128
17.0k
  zend_printf_to_smart_str(dest, format, arg);
129
17.0k
  va_end(arg);
130
17.0k
}
131
132
0
ZEND_API void smart_string_append_printf(smart_string *dest, const char *format, ...) {
133
0
  va_list arg;
134
0
  va_start(arg, format);
135
0
  zend_printf_to_smart_string(dest, format, arg);
136
0
  va_end(arg);
137
0
}
138
139
32.6M
#define SMART_STRING_OVERHEAD   (ZEND_MM_OVERHEAD + 1)
140
32.6M
#define SMART_STRING_START_SIZE 256
141
32.6M
#define SMART_STRING_START_LEN  (SMART_STRING_START_SIZE - SMART_STRING_OVERHEAD)
142
#define SMART_STRING_PAGE       4096
143
144
ZEND_API void ZEND_FASTCALL _smart_string_alloc_persistent(smart_string *str, size_t len)
145
0
{
146
0
  if (!str->c) {
147
0
    str->len = 0;
148
0
    if (len <= SMART_STRING_START_LEN) {
149
0
      str->a = SMART_STRING_START_LEN;
150
0
    } else {
151
0
      str->a = ZEND_MM_ALIGNED_SIZE_EX(len + SMART_STRING_OVERHEAD, SMART_STRING_PAGE) - SMART_STRING_OVERHEAD;
152
0
    }
153
0
    str->c = pemalloc(str->a + 1, 1);
154
0
  } else {
155
0
    if (UNEXPECTED((size_t) len > SIZE_MAX - str->len)) {
156
0
      zend_error_noreturn(E_ERROR, "String size overflow");
157
0
    }
158
0
    len += str->len;
159
0
    str->a = ZEND_MM_ALIGNED_SIZE_EX(len + SMART_STRING_OVERHEAD, SMART_STRING_PAGE) - SMART_STRING_OVERHEAD;
160
0
    str->c = perealloc(str->c, str->a + 1, 1);
161
0
  }
162
0
}
163
164
ZEND_API void ZEND_FASTCALL _smart_string_alloc(smart_string *str, size_t len)
165
16.3M
{
166
16.3M
  if (!str->c) {
167
16.3M
    str->len = 0;
168
16.3M
    if (len <= SMART_STRING_START_LEN) {
169
16.3M
      str->a = SMART_STRING_START_LEN;
170
16.3M
      str->c = emalloc(SMART_STRING_START_LEN + 1);
171
16.3M
    } else {
172
562
      str->a = ZEND_MM_ALIGNED_SIZE_EX(len + SMART_STRING_OVERHEAD, SMART_STRING_PAGE) - SMART_STRING_OVERHEAD;
173
562
      if (EXPECTED(str->a < (ZEND_MM_CHUNK_SIZE - SMART_STRING_OVERHEAD))) {
174
562
        str->c = emalloc_large(str->a + 1);
175
562
      } else {
176
        /* allocate a huge chunk */
177
0
        str->c = emalloc(str->a + 1);
178
0
      }
179
562
    }
180
16.3M
  } else {
181
9.69k
    if (UNEXPECTED((size_t) len > SIZE_MAX - str->len)) {
182
0
      zend_error_noreturn(E_ERROR, "String size overflow");
183
0
    }
184
9.69k
    len += str->len;
185
9.69k
    str->a = ZEND_MM_ALIGNED_SIZE_EX(len + SMART_STRING_OVERHEAD, SMART_STRING_PAGE) - SMART_STRING_OVERHEAD;
186
9.69k
    str->c = erealloc2(str->c, str->a + 1, str->len);
187
9.69k
  }
188
16.3M
}
189
190
ZEND_API void ZEND_FASTCALL smart_str_append_escaped_truncated(smart_str *str, const zend_string *value, size_t length)
191
20.3k
{
192
20.3k
  smart_str_append_escaped(str, ZSTR_VAL(value), MIN(length, ZSTR_LEN(value)));
193
194
20.3k
  if (ZSTR_LEN(value) > length) {
195
2.82k
    smart_str_appendl(str, "...", sizeof("...")-1);
196
2.82k
  }
197
20.3k
}
198
199
22.9k
ZEND_API void ZEND_FASTCALL smart_str_append_scalar(smart_str *dest, const zval *value, size_t truncate) {
200
22.9k
  ZEND_ASSERT(Z_TYPE_P(value) <= IS_STRING);
201
202
22.9k
  switch (Z_TYPE_P(value)) {
203
13
    case IS_UNDEF:
204
2.06k
    case IS_NULL:
205
2.06k
      smart_str_appendl(dest, "NULL", sizeof("NULL")-1);
206
2.06k
    break;
207
208
41
    case IS_TRUE:
209
41
      smart_str_appendl(dest, "true", sizeof("true")-1);
210
41
    break;
211
212
29
    case IS_FALSE:
213
29
      smart_str_appendl(dest, "false", sizeof("false")-1);
214
29
    break;
215
216
16
    case IS_DOUBLE:
217
16
      smart_str_append_double(dest, Z_DVAL_P(value), (int) EG(precision), true);
218
16
    break;
219
220
392
    case IS_LONG:
221
392
      smart_str_append_long(dest, Z_LVAL_P(value));
222
392
    break;
223
224
20.3k
    case IS_STRING:
225
20.3k
      smart_str_appendc(dest, '\'');
226
20.3k
      smart_str_append_escaped_truncated(dest, Z_STR_P(value), truncate);
227
20.3k
      smart_str_appendc(dest, '\'');
228
20.3k
    break;
229
230
0
    default: ZEND_UNREACHABLE();
231
22.9k
  }
232
22.9k
}
233
234
ZEND_API zend_result ZEND_FASTCALL smart_str_append_zval(smart_str *dest, const zval *value, size_t truncate)
235
25.0k
{
236
25.0k
  if (Z_TYPE_P(value) <= IS_STRING) {
237
22.9k
    smart_str_append_scalar(dest, value, truncate);
238
22.9k
  } else if (Z_TYPE_P(value) == IS_OBJECT && (Z_OBJCE_P(value)->ce_flags & ZEND_ACC_ENUM)) {
239
11
    smart_str_append(dest, Z_OBJCE_P(value)->name);
240
11
    smart_str_appends(dest, "::");
241
11
    smart_str_append(dest, Z_STR_P(zend_enum_fetch_case_name(Z_OBJ_P(value))));
242
2.07k
  } else {
243
2.07k
    return FAILURE;
244
2.07k
  }
245
22.9k
  return SUCCESS;
246
25.0k
}