Coverage Report

Created: 2026-08-03 06:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/spdk/include/spdk_internal/utf.h
Line
Count
Source
1
/*   SPDX-License-Identifier: BSD-3-Clause
2
 *   Copyright (C) 2016 Intel Corporation.
3
 *   All rights reserved.
4
 */
5
6
#ifndef SPDK_UTF_H_
7
#define SPDK_UTF_H_
8
9
#include "spdk/stdinc.h"
10
11
#include "spdk/endian.h"
12
#include "spdk/likely.h"
13
#include "spdk/string.h"
14
15
static inline bool
16
utf8_tail(uint8_t c)
17
22.5k
{
18
  /* c >= 0x80 && c <= 0xBF, or binary 01xxxxxx */
19
22.5k
  return (c & 0xC0) == 0x80;
20
22.5k
}
21
22
/*
23
 * Check for a valid UTF-8 encoding of a single codepoint.
24
 *
25
 * \return Length of valid UTF-8 byte sequence, or negative if invalid.
26
 */
27
static inline int
28
utf8_valid(const uint8_t *start, const uint8_t *end)
29
1.10M
{
30
1.10M
  const uint8_t *p = start;
31
1.10M
  uint8_t b0, b1, b2, b3;
32
33
1.10M
  if (p == end) {
34
0
    return 0;
35
0
  }
36
37
1.10M
  b0 = *p;
38
39
1.10M
  if (b0 <= 0x7F) {
40
1.09M
    return 1;
41
1.09M
  }
42
43
11.5k
  if (b0 <= 0xC1) {
44
    /* Invalid start byte */
45
20
    return -1;
46
20
  }
47
48
11.5k
  if (++p == end) {
49
    /* Not enough bytes left */
50
27
    return -1;
51
27
  }
52
11.5k
  b1 = *p;
53
54
11.5k
  if (b0 <= 0xDF) {
55
    /* C2..DF 80..BF */
56
1.16k
    if (!utf8_tail(b1)) {
57
11
      return -1;
58
11
    }
59
1.15k
    return 2;
60
1.16k
  }
61
62
10.3k
  if (++p == end) {
63
    /* Not enough bytes left */
64
5
    return -1;
65
5
  }
66
10.3k
  b2 = *p;
67
68
10.3k
  if (b0 == 0xE0) {
69
    /* E0 A0..BF 80..BF */
70
229
    if (b1 < 0xA0 || b1 > 0xBF || !utf8_tail(b2)) {
71
22
      return -1;
72
22
    }
73
207
    return 3;
74
10.1k
  } else if (b0 == 0xED && b1 >= 0xA0) {
75
    /*
76
     * UTF-16 surrogate pairs use U+D800..U+DFFF, which would be encoded as
77
     * ED A0..BF 80..BF in UTF-8; however, surrogate pairs are not allowed in UTF-8.
78
     */
79
4
    return -1;
80
10.1k
  } else if (b0 <= 0xEF) {
81
    /* E1..EF 80..BF 80..BF */
82
8.63k
    if (!utf8_tail(b1) || !utf8_tail(b2)) {
83
30
      return -1;
84
30
    }
85
8.60k
    return 3;
86
8.63k
  }
87
88
1.47k
  if (++p == end) {
89
    /* Not enough bytes left */
90
3
    return -1;
91
3
  }
92
1.47k
  b3 = *p;
93
94
1.47k
  if (b0 == 0xF0) {
95
    /* F0 90..BF 80..BF 80..BF */
96
235
    if (b1 < 0x90 || b1 > 0xBF || !utf8_tail(b2) || !utf8_tail(b3)) {
97
28
      return -1;
98
28
    }
99
207
    return 4;
100
1.23k
  } else if (b0 <= 0xF3) {
101
    /* F1..F3 80..BF 80..BF 80..BF */
102
1.01k
    if (!utf8_tail(b1) || !utf8_tail(b2) || !utf8_tail(b3)) {
103
19
      return -1;
104
19
    }
105
995
    return 4;
106
1.01k
  } else if (b0 == 0xF4) {
107
    /* F4 80..8F 80..BF 80..BF */
108
219
    if (b1 < 0x80 || b1 > 0x8F || !utf8_tail(b2) || !utf8_tail(b3)) {
109
19
      return -1;
110
19
    }
111
200
    return 4;
112
219
  }
113
114
6
  return -1;
115
1.47k
}
116
117
static inline uint32_t
118
utf8_decode_unsafe_1(const uint8_t *data)
119
0
{
120
0
  return data[0];
121
0
}
122
123
static inline uint32_t
124
utf8_decode_unsafe_2(const uint8_t *data)
125
0
{
126
0
  uint32_t codepoint;
127
0
128
0
  codepoint = ((data[0] & 0x1F) << 6);
129
0
  codepoint |= (data[1] & 0x3F);
130
0
131
0
  return codepoint;
132
0
}
133
134
static inline uint32_t
135
utf8_decode_unsafe_3(const uint8_t *data)
136
0
{
137
0
  uint32_t codepoint;
138
0
139
0
  codepoint = ((data[0] & 0x0F) << 12);
140
0
  codepoint |= (data[1] & 0x3F) << 6;
141
0
  codepoint |= (data[2] & 0x3F);
142
0
143
0
  return codepoint;
144
0
}
145
146
static inline uint32_t
147
utf8_decode_unsafe_4(const uint8_t *data)
148
0
{
149
0
  uint32_t codepoint;
150
0
151
0
  codepoint = ((data[0] & 0x07) << 18);
152
0
  codepoint |= (data[1] & 0x3F) << 12;
153
0
  codepoint |= (data[2] & 0x3F) << 6;
154
0
  codepoint |= (data[3] & 0x3F);
155
0
156
0
  return codepoint;
157
0
}
158
159
/*
160
 * Encode a single Unicode codepoint as UTF-8.
161
 *
162
 * buf must have at least 4 bytes of space available (hence unsafe).
163
 *
164
 * \return Number of bytes appended to buf, or negative if encoding failed.
165
 */
166
static inline int
167
utf8_encode_unsafe(uint8_t *buf, uint32_t c)
168
0
{
169
0
  if (c <= 0x7F) {
170
0
    buf[0] = c;
171
0
    return 1;
172
0
  } else if (c <= 0x7FF) {
173
0
    buf[0] = 0xC0 | (c >> 6);
174
0
    buf[1] = 0x80 | (c & 0x3F);
175
0
    return 2;
176
0
  } else if (c >= 0xD800 && c <= 0xDFFF) {
177
    /* UTF-16 surrogate pairs - invalid in UTF-8 */
178
0
    return -1;
179
0
  } else if (c <= 0xFFFF) {
180
0
    buf[0] = 0xE0 | (c >> 12);
181
0
    buf[1] = 0x80 | ((c >> 6) & 0x3F);
182
0
    buf[2] = 0x80 | (c & 0x3F);
183
0
    return 3;
184
0
  } else if (c <= 0x10FFFF) {
185
0
    buf[0] = 0xF0 | (c >> 18);
186
0
    buf[1] = 0x80 | ((c >> 12) & 0x3F);
187
0
    buf[2] = 0x80 | ((c >> 6) & 0x3F);
188
0
    buf[3] = 0x80 | (c & 0x3F);
189
0
    return 4;
190
0
  }
191
0
  return -1;
192
0
}
193
194
static inline int
195
utf8_codepoint_len(uint32_t c)
196
1.46k
{
197
1.46k
  if (c <= 0x7F) {
198
292
    return 1;
199
1.17k
  } else if (c <= 0x7FF) {
200
291
    return 2;
201
885
  } else if (c >= 0xD800 && c <= 0xDFFF) {
202
    /* UTF-16 surrogate pairs - invalid in UTF-8 */
203
0
    return -1;
204
885
  } else if (c <= 0xFFFF) {
205
606
    return 3;
206
606
  } else if (c <= 0x10FFFF) {
207
279
    return 4;
208
279
  }
209
0
  return -1;
210
1.46k
}
211
212
static inline bool
213
utf16_valid_surrogate_high(uint32_t val)
214
1.52k
{
215
1.52k
  return val >= 0xD800 && val <= 0xDBFF;
216
1.52k
}
217
218
static inline bool
219
utf16_valid_surrogate_low(uint32_t val)
220
1.51k
{
221
1.51k
  return val >= 0xDC00 && val <= 0xDFFF;
222
1.51k
}
223
224
/*
225
 * Check for a valid UTF-16LE encoding of a single codepoint.
226
 *
227
 * \return Length of valid UTF-16LE sequence in 16-bit code units, or negative if invalid.
228
 */
229
static inline int
230
utf16le_valid(const uint16_t *start, const uint16_t *end)
231
0
{
232
0
  const uint16_t *p = start;
233
0
  uint16_t high, low;
234
0
235
0
  if (p == end) {
236
0
    return 0;
237
0
  }
238
0
239
0
  high = from_le16(p);
240
0
241
0
  if (high <= 0xD7FF || high >= 0xE000) {
242
0
    /* Single code unit in BMP */
243
0
    return 1;
244
0
  }
245
0
246
0
  if (high >= 0xDC00) {
247
0
    /* Low surrogate in first code unit - invalid */
248
0
    return -1;
249
0
  }
250
0
251
0
  assert(utf16_valid_surrogate_high(high));
252
0
253
0
  if (++p == end) {
254
0
    /* Not enough code units left */
255
0
    return -1;
256
0
  }
257
0
  low = from_le16(p);
258
0
259
0
  if (!utf16_valid_surrogate_low(low)) {
260
0
    return -1;
261
0
  }
262
0
263
0
  /* Valid surrogate pair */
264
0
  return 2;
265
0
}
266
267
static inline uint32_t
268
utf16_decode_surrogate_pair(uint32_t high, uint32_t low)
269
279
{
270
279
  uint32_t codepoint;
271
272
279
  assert(utf16_valid_surrogate_high(high));
273
279
  assert(utf16_valid_surrogate_low(low));
274
275
279
  codepoint = low;
276
279
  codepoint &= 0x3FF;
277
279
  codepoint |= ((high & 0x3FF) << 10);
278
279
  codepoint += 0x10000;
279
280
279
  return codepoint;
281
279
}
282
283
static inline void
284
utf16_encode_surrogate_pair(uint32_t codepoint, uint16_t *high, uint16_t *low)
285
0
{
286
0
  assert(codepoint >= 0x10000);
287
0
  assert(codepoint <= 0x10FFFF);
288
0
289
0
  codepoint -= 0x10000;
290
0
  *high = 0xD800 | (codepoint >> 10);
291
0
  *low = 0xDC00 | (codepoint & 0x3FF);
292
0
293
0
  assert(utf16_valid_surrogate_high(*high));
294
0
  assert(utf16_valid_surrogate_low(*low));
295
0
}
296
297
#endif