• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OISF / suricata / 22550902417

01 Mar 2026 07:32PM UTC coverage: 68.401% (-5.3%) from 73.687%
22550902417

Pull #14922

github

web-flow
github-actions: bump actions/upload-artifact from 6.0.0 to 7.0.0

Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6.0.0 to 7.0.0.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #14922: github-actions: bump actions/upload-artifact from 6.0.0 to 7.0.0

218243 of 319063 relevant lines covered (68.4%)

3284926.58 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.08
/rust/src/jsonbuilder.rs
1
/* Copyright (C) 2020-2024 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17

18
//! Module for building JSON documents.
19

20
#![allow(clippy::missing_safety_doc)]
21

22
use base64::{engine::general_purpose::STANDARD, Engine};
23
use num_traits::Unsigned;
24
use std::cmp::max;
25
use std::collections::TryReserveError;
26
use std::ffi::CStr;
27
use std::os::raw::c_char;
28
use std::str::Utf8Error;
29

30
const INIT_SIZE: usize = 4096;
31

32
#[derive(Debug, PartialEq, Eq)]
33
pub enum JsonError {
34
    InvalidState,
35
    Utf8Error(Utf8Error),
36
    Memory,
37
}
38

39
impl std::error::Error for JsonError {}
40

41
impl std::fmt::Display for JsonError {
42
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
×
43
        match self {
×
44
            JsonError::InvalidState => write!(f, "invalid state"),
×
45
            JsonError::Utf8Error(ref e) => e.fmt(f),
×
46
            JsonError::Memory => write!(f, "memory error"),
×
47
        }
48
    }
×
49
}
50

51
impl From<TryReserveError> for JsonError {
52
    fn from(_: TryReserveError) -> Self {
×
53
        JsonError::Memory
×
54
    }
×
55
}
56

57
impl From<Utf8Error> for JsonError {
58
    fn from(e: Utf8Error) -> Self {
×
59
        JsonError::Utf8Error(e)
×
60
    }
×
61
}
62

63
#[derive(Clone, Debug)]
64
enum Type {
65
    Object,
66
    Array,
67
}
68

69
#[derive(Debug, Clone, Copy, PartialEq)]
70
#[repr(C)]
71
enum State {
72
    None = 0,
73
    ObjectFirst,
74
    ObjectNth,
75
    ArrayFirst,
76
    ArrayNth,
77
}
78

79
impl State {
80
    fn from_u64(v: u64) -> Result<State, JsonError> {
623✔
81
        let s = match v {
623✔
82
            0 => State::None,
×
83
            1 => State::ObjectFirst,
×
84
            2 => State::ObjectNth,
623✔
85
            3 => State::ArrayFirst,
×
86
            4 => State::ArrayNth,
×
87
            _ => {
88
                return Err(JsonError::InvalidState);
×
89
            }
90
        };
91
        Ok(s)
623✔
92
    }
623✔
93
}
94

95
/// A "mark" or saved state for a JsonBuilder object.
96
///
97
/// The name is full, and the types are u64 as this object is used
98
/// directly in C as well.
99
#[repr(C)]
100
pub struct JsonBuilderMark {
101
    position: u64,
102
    state_index: u64,
103
    state: u64,
104
}
105

106
#[derive(Debug, Clone)]
107
pub struct JsonBuilder {
108
    buf: String,
109
    state: Vec<State>,
110
    init_type: Type,
111
}
112

113
impl JsonBuilder {
114
    /// Returns a new JsonBuilder in object state.
115
    pub fn try_new_object() -> Result<Self, JsonError> {
29,874✔
116
        Self::try_new_object_with_capacity(INIT_SIZE)
29,874✔
117
    }
29,874✔
118

119
    pub fn try_new_object_with_capacity(capacity: usize) -> Result<Self, JsonError> {
29,875✔
120
        let mut buf = String::new();
29,875✔
121
        buf.try_reserve(capacity)?;
29,875✔
122
        buf.push('{');
29,875✔
123
        let mut state = Vec::new();
29,875✔
124
        state.try_reserve(32)?;
29,875✔
125
        state.extend_from_slice(&[State::None, State::ObjectFirst]);
29,875✔
126
        Ok(Self {
29,875✔
127
            buf,
29,875✔
128
            state,
29,875✔
129
            init_type: Type::Object,
29,875✔
130
        })
29,875✔
131
    }
29,875✔
132

133
    /// Returns a new JsonBuilder in array state.
134
    pub fn try_new_array() -> Result<Self, JsonError> {
6,249✔
135
        Self::try_new_array_with_capacity(INIT_SIZE)
6,249✔
136
    }
6,249✔
137

138
    pub fn try_new_array_with_capacity(capacity: usize) -> Result<Self, JsonError> {
6,249✔
139
        let mut buf = String::new();
6,249✔
140
        buf.try_reserve(capacity)?;
6,249✔
141
        buf.push('[');
6,249✔
142
        let mut state = Vec::new();
6,249✔
143
        state.try_reserve(32)?;
6,249✔
144
        state.extend_from_slice(&[State::None, State::ArrayFirst]);
6,249✔
145
        Ok(Self {
6,249✔
146
            buf,
6,249✔
147
            state,
6,249✔
148
            init_type: Type::Array,
6,249✔
149
        })
6,249✔
150
    }
6,249✔
151

152
    /// A wrapper around String::push that pre-allocates data return
153
    /// an error if unable to.
154
    pub fn push(&mut self, ch: char) -> Result<&mut Self, JsonError> {
1,206,425✔
155
        if self.buf.capacity() == self.buf.len() {
1,206,425✔
156
            self.buf.try_reserve(INIT_SIZE)?;
1✔
157
        }
1,206,424✔
158
        self.buf.push(ch);
1,206,425✔
159
        Ok(self)
1,206,425✔
160
    }
1,206,425✔
161

162
    /// A wrapper around String::push_str that pre-allocates data
163
    /// return an error if unable to.
164
    pub fn push_str(&mut self, s: &str) -> Result<&mut Self, JsonError> {
1,818,093✔
165
        if self.buf.capacity() < self.buf.len() + s.len() {
1,818,093✔
166
            self.buf.try_reserve(max(INIT_SIZE, s.len()))?;
23✔
167
        }
1,818,070✔
168
        self.buf.push_str(s);
1,818,093✔
169
        Ok(self)
1,818,093✔
170
    }
1,818,093✔
171

172
    // Reset the builder to its initial state, without losing
173
    // the current capacity.
174
    pub fn reset(&mut self) {
12✔
175
        self.buf.truncate(0);
12✔
176
        self.state.clear();
12✔
177
        match self.init_type {
12✔
178
            Type::Array => {
×
179
                self.buf.push('[');
×
180
                self.state
×
181
                    .extend_from_slice(&[State::None, State::ArrayFirst]);
×
182
            }
×
183
            Type::Object => {
12✔
184
                self.buf.push('{');
12✔
185
                self.state
12✔
186
                    .extend_from_slice(&[State::None, State::ObjectFirst]);
12✔
187
            }
12✔
188
        }
189
    }
12✔
190

191
    // Closes the currently open datatype (object or array).
192
    pub fn close(&mut self) -> Result<&mut Self, JsonError> {
100,972✔
193
        match self.current_state() {
100,972✔
194
            State::ObjectFirst | State::ObjectNth => {
195
                self.push('}')?;
82,636✔
196
                self.pop_state();
82,636✔
197
                Ok(self)
82,636✔
198
            }
199
            State::ArrayFirst | State::ArrayNth => {
200
                self.push(']')?;
18,336✔
201
                self.pop_state();
18,336✔
202
                Ok(self)
18,336✔
203
            }
204
            State::None => {
205
                debug_validate_fail!("invalid state");
206
                Err(JsonError::InvalidState)
×
207
            }
208
        }
209
    }
100,972✔
210

211
    // Return the current state of the JsonBuilder.
212
    fn current_state(&self) -> State {
765,479✔
213
        if self.state.is_empty() {
765,479✔
214
            State::None
×
215
        } else {
216
            self.state[self.state.len() - 1]
765,479✔
217
        }
218
    }
765,479✔
219

220
    /// Move to a new state.
221
    fn push_state(&mut self, state: State) -> Result<(), JsonError> {
66,537✔
222
        if self.state.len() == self.state.capacity() {
66,537✔
223
            self.state.try_reserve(32)?;
×
224
        }
66,537✔
225
        self.state.push(state);
66,537✔
226
        Ok(())
66,537✔
227
    }
66,537✔
228

229
    /// Go back to the previous state.
230
    fn pop_state(&mut self) {
100,972✔
231
        self.state.pop();
100,972✔
232
    }
100,972✔
233

234
    /// Change the current state.
235
    fn set_state(&mut self, state: State) {
120,825✔
236
        let n = self.state.len() - 1;
120,825✔
237
        self.state[n] = state;
120,825✔
238
    }
120,825✔
239

240
    pub fn get_mark(&self) -> JsonBuilderMark {
1,949✔
241
        JsonBuilderMark {
1,949✔
242
            position: self.buf.len() as u64,
1,949✔
243
            state: self.current_state() as u64,
1,949✔
244
            state_index: self.state.len() as u64,
1,949✔
245
        }
1,949✔
246
    }
1,949✔
247

248
    pub fn restore_mark(&mut self, mark: &JsonBuilderMark) -> Result<(), JsonError> {
623✔
249
        let state = State::from_u64(mark.state)?;
623✔
250
        if mark.position < (self.buf.len() as u64) && mark.state_index < (self.state.len() as u64) {
623✔
251
            self.buf.truncate(mark.position as usize);
616✔
252
            self.state.truncate(mark.state_index as usize);
616✔
253
            self.state[(mark.state_index as usize) - 1] = state;
616✔
254
        }
616✔
255
        Ok(())
623✔
256
    }
623✔
257

258
    /// Open an object under the given key.
259
    ///
260
    /// For example:
261
    ///     Before: {
262
    ///     After:  {"key": {
263
    pub fn open_object(&mut self, key: &str) -> Result<&mut Self, JsonError> {
40,531✔
264
        match self.current_state() {
40,531✔
265
            State::ObjectFirst => {
266
                self.push('"')?;
246✔
267
                self.set_state(State::ObjectNth);
246✔
268
            }
269
            State::ObjectNth => {
270
                self.push_str(",\"")?;
40,285✔
271
            }
272
            _ => {
273
                debug_validate_fail!("invalid state");
274
                return Err(JsonError::InvalidState);
×
275
            }
276
        }
277
        self.push_str(key)?;
40,531✔
278
        self.push_str("\":{")?;
40,531✔
279
        self.push_state(State::ObjectFirst)?;
40,531✔
280
        Ok(self)
40,531✔
281
    }
40,531✔
282

283
    /// Start an object.
284
    ///
285
    /// Like open_object but does not create the object under a key. An
286
    /// error will be returned if starting an object does not make
287
    /// sense for the current state.
288
    pub fn start_object(&mut self) -> Result<&mut Self, JsonError> {
12,692✔
289
        match self.current_state() {
12,692✔
290
            State::ArrayFirst => {}
7,123✔
291
            State::ArrayNth => {
292
                self.push(',')?;
5,569✔
293
            }
294
            _ => {
295
                debug_validate_fail!("invalid state");
296
                return Err(JsonError::InvalidState);
×
297
            }
298
        }
299
        self.push('{')?;
12,692✔
300
        self.set_state(State::ArrayNth);
12,692✔
301
        self.push_state(State::ObjectFirst)?;
12,692✔
302
        Ok(self)
12,692✔
303
    }
12,692✔
304

305
    /// Open an array under the given key.
306
    ///
307
    /// For example:
308
    ///     Before: {
309
    ///     After:  {"key": [
310
    pub fn open_array(&mut self, key: &str) -> Result<&mut Self, JsonError> {
13,314✔
311
        match self.current_state() {
13,314✔
312
            State::ObjectFirst => {}
63✔
313
            State::ObjectNth => {
314
                self.push(',')?;
13,251✔
315
            }
316
            _ => {
317
                debug_validate_fail!("invalid state");
318
                return Err(JsonError::InvalidState);
×
319
            }
320
        }
321
        self.push('"')?;
13,314✔
322
        self.push_str(key)?;
13,314✔
323
        self.push_str("\":[")?;
13,314✔
324
        self.set_state(State::ObjectNth);
13,314✔
325
        self.push_state(State::ArrayFirst)?;
13,314✔
326
        Ok(self)
13,314✔
327
    }
13,314✔
328

329
    /// Add a string to an array.
330
    pub fn append_string(&mut self, val: &str) -> Result<&mut Self, JsonError> {
20,808✔
331
        match self.current_state() {
20,808✔
332
            State::ArrayFirst => {
333
                self.encode_string(val)?;
7,112✔
334
                self.set_state(State::ArrayNth);
7,112✔
335
                Ok(self)
7,112✔
336
            }
337
            State::ArrayNth => {
338
                self.push(',')?;
13,695✔
339
                self.encode_string(val)?;
13,695✔
340
                Ok(self)
13,695✔
341
            }
342
            _ => {
343
                debug_validate_fail!("invalid state");
344
                Err(JsonError::InvalidState)
1✔
345
            }
346
        }
347
    }
20,808✔
348

349
    pub fn append_string_from_bytes(&mut self, val: &[u8]) -> Result<&mut Self, JsonError> {
11,770✔
350
        match std::str::from_utf8(val) {
11,770✔
351
            Ok(s) => self.append_string(s),
11,766✔
352
            Err(_) => self.append_string(&try_string_from_bytes(val)?),
4✔
353
        }
354
    }
11,770✔
355

356
    /// Add a string to an array.
357
    pub fn append_base64(&mut self, val: &[u8]) -> Result<&mut Self, JsonError> {
×
358
        match self.current_state() {
×
359
            State::ArrayFirst => {
360
                self.push('"')?;
×
361
                self.encode_base64(val)?;
×
362
                self.push('"')?;
×
363
                self.set_state(State::ArrayNth);
×
364
                Ok(self)
×
365
            }
366
            State::ArrayNth => {
367
                self.push(',')?;
×
368
                self.push('"')?;
×
369
                self.encode_base64(val)?;
×
370
                self.push('"')?;
×
371
                Ok(self)
×
372
            }
373
            _ => {
374
                debug_validate_fail!("invalid state");
375
                Err(JsonError::InvalidState)
×
376
            }
377
        }
378
    }
×
379

380
    /// Add a byte array to a JSON array encoded as hex.
381
    pub fn append_hex(&mut self, val: &[u8]) -> Result<&mut Self, JsonError> {
×
382
        match self.current_state() {
×
383
            State::ArrayFirst => {
384
                self.push('"')?;
×
385
                for i in 0..val.len() {
×
386
                    self.push(HEX[(val[i] >> 4) as usize] as char)?;
×
387
                    self.push(HEX[(val[i] & 0xf) as usize] as char)?;
×
388
                }
389
                self.push('"')?;
×
390
                self.set_state(State::ArrayNth);
×
391
                Ok(self)
×
392
            }
393
            State::ArrayNth => {
394
                self.push(',')?;
×
395
                self.push('"')?;
×
396
                for i in 0..val.len() {
×
397
                    self.push(HEX[(val[i] >> 4) as usize] as char)?;
×
398
                    self.push(HEX[(val[i] & 0xf) as usize] as char)?;
×
399
                }
400
                self.push('"')?;
×
401
                Ok(self)
×
402
            }
403
            _ => {
404
                debug_validate_fail!("invalid state");
405
                Err(JsonError::InvalidState)
×
406
            }
407
        }
408
    }
×
409

410
    /// Add an unsigned integer to an array.
411
    pub fn append_uint(&mut self, val: u64) -> Result<&mut Self, JsonError> {
3,745✔
412
        match self.current_state() {
3,745✔
413
            State::ArrayFirst => {
3,733✔
414
                self.set_state(State::ArrayNth);
3,733✔
415
            }
3,733✔
416
            State::ArrayNth => {
417
                self.push(',')?;
12✔
418
            }
419
            _ => {
420
                debug_validate_fail!("invalid state");
421
                return Err(JsonError::InvalidState);
×
422
            }
423
        }
424
        self.push_str(&val.to_string())
3,745✔
425
    }
3,745✔
426

427
    pub fn append_float(&mut self, val: f64) -> Result<&mut Self, JsonError> {
5✔
428
        match self.current_state() {
5✔
429
            State::ArrayFirst => {
4✔
430
                self.set_state(State::ArrayNth);
4✔
431
            }
4✔
432
            State::ArrayNth => {
433
                self.push(',')?;
1✔
434
            }
435
            _ => {
436
                debug_validate_fail!("invalid state");
437
                return Err(JsonError::InvalidState);
×
438
            }
439
        }
440
        self.push_float(val)?;
5✔
441
        Ok(self)
5✔
442
    }
5✔
443

444
    pub fn set_object(&mut self, key: &str, js: &JsonBuilder) -> Result<&mut Self, JsonError> {
9,216✔
445
        match self.current_state() {
9,216✔
446
            State::ObjectNth => {
447
                self.push(',')?;
4,837✔
448
            }
449
            State::ObjectFirst => {
4,379✔
450
                self.set_state(State::ObjectNth);
4,379✔
451
            }
4,379✔
452
            _ => {
453
                debug_validate_fail!("invalid state");
454
                return Err(JsonError::InvalidState);
×
455
            }
456
        }
457
        self.push('"')?;
9,216✔
458
        self.push_str(key)?;
9,216✔
459
        self.push_str("\":")?;
9,216✔
460
        self.push_str(&js.buf)?;
9,216✔
461
        Ok(self)
9,216✔
462
    }
9,216✔
463

464
    /// Append an object onto this array.
465
    ///
466
    /// '[' -> '[{...}'
467
    /// '[{...}' -> '[{...},{...}'
468
    pub fn append_object(&mut self, js: &JsonBuilder) -> Result<&mut Self, JsonError> {
3,140✔
469
        match self.current_state() {
3,140✔
470
            State::ArrayFirst => {
1,324✔
471
                self.set_state(State::ArrayNth);
1,324✔
472
            }
1,324✔
473
            State::ArrayNth => {
474
                self.push(',')?;
1,816✔
475
            }
476
            _ => {
477
                debug_validate_fail!("invalid state");
478
                return Err(JsonError::InvalidState);
×
479
            }
480
        }
481
        self.push_str(&js.buf)?;
3,140✔
482
        Ok(self)
3,140✔
483
    }
3,140✔
484

485
    /// Set a key and string value type on an object.
486
    #[inline(always)]
487
    pub fn set_string(&mut self, key: &str, val: &str) -> Result<&mut Self, JsonError> {
287,647✔
488
        match self.current_state() {
287,647✔
489
            State::ObjectNth => {
490
                self.push(',')?;
231,806✔
491
            }
492
            State::ObjectFirst => {
55,841✔
493
                self.set_state(State::ObjectNth);
55,841✔
494
            }
55,841✔
495
            _ => {
496
                debug_validate_fail!("invalid state");
497
                return Err(JsonError::InvalidState);
×
498
            }
499
        }
500
        self.push('"')?;
287,647✔
501
        self.push_str(key)?;
287,647✔
502
        self.push_str("\":")?;
287,647✔
503
        self.encode_string(val)?;
287,647✔
504
        Ok(self)
287,647✔
505
    }
287,647✔
506

507
    /// Set a key and a string value on an object, with a limited size
508
    pub fn set_string_limited(
145✔
509
        &mut self, key: &str, val: &str, limit: usize,
145✔
510
    ) -> Result<&mut Self, JsonError> {
145✔
511
        if val.len() > limit {
145✔
512
            // Gracefully handle splitting UTF-8 strings at arbitrary locations.
513
            // Strings in Rust are UTF-8; and a UTF-8 code point is max 4 bytes.
514
            // Hence we will find a suitable boundary within 4 bytes of any byte
515
            // position, in any direction with sufficiently long strings left.
516
            // This is an approach similar to Rust's (currently nightly unstable
517
            // only) "floor_char_boundary" str method:
518
            // https://doc.rust-lang.org/std/primitive.str.html#method.floor_char_boundary
519
            for i in 0..=std::cmp::min(limit, 4) {
19✔
520
                // We first try the requested boundary. In the expected
521
                // ("happy") case the limit is at a code point boundary so the
522
                // slice will succeed immediately. If not, we successively try
523
                // earlier positions in the string until we find a suitable
524
                // position.
525
                if let Some(valtrunc) = val.get(0..limit - i) {
19✔
526
                    let additional_bytes = val.len() - limit;
9✔
527
                    let outstr = format!(
9✔
528
                        "{valtrunc}[truncated {additional_bytes} additional byte{}]",
9✔
529
                        if additional_bytes != 1 { "s" } else { "" }
9✔
530
                    );
531
                    self.set_string(key, &outstr)?;
9✔
532
                    break;
9✔
533
                }
10✔
534
            }
535
        } else {
536
            self.set_string(key, val)?;
136✔
537
        }
538
        Ok(self)
145✔
539
    }
145✔
540

541
    pub fn set_formatted(&mut self, formatted: &str) -> Result<&mut Self, JsonError> {
25,069✔
542
        match self.current_state() {
25,069✔
543
            State::ObjectNth => {
544
                self.push(',')?;
24,974✔
545
            }
546
            State::ObjectFirst => {
95✔
547
                self.set_state(State::ObjectNth);
95✔
548
            }
95✔
549
            _ => {
550
                debug_validate_fail!("invalid state");
551
                return Err(JsonError::InvalidState);
×
552
            }
553
        }
554
        self.push_str(formatted)?;
25,069✔
555
        Ok(self)
25,069✔
556
    }
25,069✔
557

558
    /// Set a key and a string value (from bytes) on an object.
559
    pub fn set_string_from_bytes(&mut self, key: &str, val: &[u8]) -> Result<&mut Self, JsonError> {
46,602✔
560
        match std::str::from_utf8(val) {
46,602✔
561
            Ok(s) => self.set_string(key, s),
46,577✔
562
            Err(_) => self.set_string(key, &try_string_from_bytes(val)?),
25✔
563
        }
564
    }
46,602✔
565

566
    /// Set a key with a string value taking only ascii-printable bytes.
567
    /// Non-printable characters are replaced by a dot `.`, except
568
    /// CR and LF which are escaped the regular json way \r and \n
569
    pub fn set_print_ascii(&mut self, key: &str, val: &[u8]) -> Result<&mut Self, JsonError> {
×
570
        match self.current_state() {
×
571
            State::ObjectNth => {
572
                self.push(',')?;
×
573
            }
574
            State::ObjectFirst => {
×
575
                self.set_state(State::ObjectNth);
×
576
            }
×
577
            _ => {
578
                debug_validate_fail!("invalid state");
579
                return Err(JsonError::InvalidState);
×
580
            }
581
        }
582
        self.push('"')?;
×
583
        self.push_str(key)?;
×
584
        self.push_str("\":\"")?;
×
585
        for &x in val.iter() {
×
586
            match x {
×
587
                b'\r' => {
588
                    self.push_str("\\r")?;
×
589
                }
590
                b'\n'=> {
591
                    self.push_str("\\n")?;
×
592
                }
593
                b'"'=> {
594
                    self.push_str("\\\"")?;
×
595
                }
596
                b'\\'=> {
597
                    self.push_str("\\\\")?;
×
598
                }
599
                _ => {
600
                    if !x.is_ascii() || x.is_ascii_control()  {
×
601
                        self.push('.')?;
×
602
                    } else {
603
                        self.push(x as char)?;
×
604
                    }
605
                }
606
            }
607
        }
608
        self.push('"')?;
×
609
        Ok(self)
×
610
    }
×
611

612
    /// Set a key and a string value (from bytes) on an object, with a limited size
613
    pub fn set_string_from_bytes_limited(
103✔
614
        &mut self, key: &str, val: &[u8], limit: usize,
103✔
615
    ) -> Result<&mut Self, JsonError> {
103✔
616
        let mut valtrunc = Vec::new();
103✔
617
        let val = if val.len() > limit {
103✔
618
            let additional_bytes = val.len() - limit;
7✔
619
            valtrunc.extend_from_slice(&val[..limit]);
7✔
620
            valtrunc.extend_from_slice(
7✔
621
                format!(
7✔
622
                    "[truncated {additional_bytes} additional byte{}]",
7✔
623
                    if additional_bytes != 1 { "s" } else { "" }
7✔
624
                )
625
                .as_bytes(),
7✔
626
            );
7✔
627
            &valtrunc
7✔
628
        } else {
629
            val
96✔
630
        };
631
        match std::str::from_utf8(val) {
103✔
632
            Ok(s) => self.set_string(key, s),
100✔
633
            Err(_) => self.set_string(key, &try_string_from_bytes(val)?),
3✔
634
        }
635
    }
103✔
636

637
    /// Set a key and a string field as the base64 encoded string of the value.
638
    pub fn set_base64(&mut self, key: &str, val: &[u8]) -> Result<&mut Self, JsonError> {
×
639
        match self.current_state() {
×
640
            State::ObjectNth => {
641
                self.push(',')?;
×
642
            }
643
            State::ObjectFirst => {
×
644
                self.set_state(State::ObjectNth);
×
645
            }
×
646
            _ => {
647
                debug_validate_fail!("invalid state");
648
                return Err(JsonError::InvalidState);
×
649
            }
650
        }
651
        self.push('"')?;
×
652
        self.push_str(key)?;
×
653
        self.push_str("\":\"")?;
×
654
        self.encode_base64(val)?;
×
655
        self.push('"')?;
×
656

657
        Ok(self)
×
658
    }
×
659

660
    /// Set a key and a string field as the hex encoded string of the value.
661
    pub fn set_hex(&mut self, key: &str, val: &[u8]) -> Result<&mut Self, JsonError> {
170✔
662
        match self.current_state() {
170✔
663
            State::ObjectNth => {
664
                self.push(',')?;
140✔
665
            }
666
            State::ObjectFirst => {
30✔
667
                self.set_state(State::ObjectNth);
30✔
668
            }
30✔
669
            _ => {
670
                debug_validate_fail!("invalid state");
671
                return Err(JsonError::InvalidState);
×
672
            }
673
        }
674
        self.push('"')?;
170✔
675
        self.push_str(key)?;
170✔
676
        self.push_str("\":\"")?;
170✔
677
        for i in 0..val.len() {
7,774✔
678
            self.push(HEX[(val[i] >> 4) as usize] as char)?;
7,774✔
679
            self.push(HEX[(val[i] & 0xf) as usize] as char)?;
7,774✔
680
        }
681
        self.push('"')?;
170✔
682

683
        Ok(self)
170✔
684
    }
170✔
685

686
    /// Set a key and an unsigned integer type on an object.
687
    pub fn set_uint<T>(&mut self, key: &str, val: T) -> Result<&mut Self, JsonError>
231,033✔
688
    where
231,033✔
689
        T: Unsigned + Into<u64>,
231,033✔
690
    {
231,033✔
691
        let val: u64 = val.into();
231,033✔
692
        match self.current_state() {
231,033✔
693
            State::ObjectNth => {
694
                self.push(',')?;
212,381✔
695
            }
696
            State::ObjectFirst => {
18,652✔
697
                self.set_state(State::ObjectNth);
18,652✔
698
            }
18,652✔
699
            _ => {
700
                debug_validate_fail!("invalid state");
701
                return Err(JsonError::InvalidState);
×
702
            }
703
        }
704
        self.push('"')?;
231,033✔
705
        self.push_str(key)?;
231,033✔
706
        self.push_str("\":")?;
231,033✔
707
        self.push_str(&val.to_string())?;
231,033✔
708
        Ok(self)
231,033✔
709
    }
231,033✔
710

711
    /// Set a key and a signed integer type on an object.
712
    pub fn set_int(&mut self, key: &str, val: i64) -> Result<&mut Self, JsonError> {
2,981✔
713
        match self.current_state() {
2,981✔
714
            State::ObjectNth => {
715
                self.push(',')?;
×
716
            }
717
            State::ObjectFirst => {
2,981✔
718
                self.set_state(State::ObjectNth);
2,981✔
719
            }
2,981✔
720
            _ => {
721
                debug_validate_fail!("invalid state");
722
                return Err(JsonError::InvalidState);
×
723
            }
724
        }
725
        self.push('"')?;
2,981✔
726
        self.push_str(key)?;
2,981✔
727
        self.push_str("\":")?;
2,981✔
728
        self.push_str(&val.to_string())?;
2,981✔
729
        Ok(self)
2,981✔
730
    }
2,981✔
731

732
    pub fn set_float(&mut self, key: &str, val: f64) -> Result<&mut Self, JsonError> {
5✔
733
        match self.current_state() {
5✔
734
            State::ObjectNth => {
735
                self.push(',')?;
1✔
736
            }
737
            State::ObjectFirst => {
4✔
738
                self.set_state(State::ObjectNth);
4✔
739
            }
4✔
740
            _ => {
741
                debug_validate_fail!("invalid state");
742
                return Err(JsonError::InvalidState);
×
743
            }
744
        }
745
        self.push('"')?;
5✔
746
        self.push_str(key)?;
5✔
747
        self.push_str("\":")?;
5✔
748
        self.push_float(val)?;
5✔
749
        Ok(self)
5✔
750
    }
5✔
751

752
    pub fn set_bool(&mut self, key: &str, val: bool) -> Result<&mut Self, JsonError> {
12,183✔
753
        match self.current_state() {
12,183✔
754
            State::ObjectNth => {
755
                self.push(',')?;
11,765✔
756
            }
757
            State::ObjectFirst => {
418✔
758
                self.set_state(State::ObjectNth);
418✔
759
            }
418✔
760
            _ => {
761
                debug_validate_fail!("invalid state");
762
                return Err(JsonError::InvalidState);
×
763
            }
764
        }
765
        self.push('"')?;
12,183✔
766
        self.push_str(key)?;
12,183✔
767
        if val {
12,183✔
768
            self.push_str("\":true")?;
4,633✔
769
        } else {
770
            self.push_str("\":false")?;
7,550✔
771
        }
772
        Ok(self)
12,183✔
773
    }
12,183✔
774

775
    pub fn capacity(&self) -> usize {
5✔
776
        self.buf.capacity()
5✔
777
    }
5✔
778

779
    fn push_float(&mut self, val: f64) -> Result<(), JsonError> {
10✔
780
        if val.is_nan() || val.is_infinite() {
10✔
781
            self.push_str("null")?;
6✔
782
        } else {
783
            self.push_str(&val.to_string())?;
4✔
784
        }
785
        Ok(())
10✔
786
    }
10✔
787

788
    /// Encode a string into the buffer, escaping as needed.
789
    ///
790
    /// The string is encoded into an intermediate vector as its faster
791
    /// than building onto the buffer.
792
    ///
793
    /// TODO: Revisit this, would be nice to build directly onto the
794
    ///    existing buffer.
795
    #[inline(always)]
796
    fn encode_string(&mut self, val: &str) -> Result<(), JsonError> {
308,454✔
797
        let mut buf = Vec::new();
308,454✔
798

308,454✔
799
        // Start by allocating a reasonable size buffer, it will be
308,454✔
800
        // grown if needed.
308,454✔
801
        buf.try_reserve(val.len() * 2 + 2)?;
308,454✔
802
        buf.resize(val.len() * 2 + 2, 0);
308,454✔
803

308,454✔
804
        let mut offset = 0;
308,454✔
805
        let bytes = val.as_bytes();
308,454✔
806
        buf[offset] = b'"';
308,454✔
807
        offset += 1;
308,454✔
808
        for &x in bytes.iter() {
5,297,197✔
809
            if offset + 7 >= buf.capacity() {
5,297,197✔
810
                // We could be smarter, but just double the buffer size.
811
                buf.try_reserve(buf.capacity())?;
85,913✔
812
                buf.resize(buf.capacity(), 0);
85,913✔
813
            }
5,211,284✔
814
            let escape = ESCAPED[x as usize];
5,297,197✔
815
            if escape == 0 {
5,297,197✔
816
                buf[offset] = x;
5,291,159✔
817
                offset += 1;
5,291,159✔
818
            } else if escape == b'u' {
5,291,159✔
819
                buf[offset] = b'\\';
1,630✔
820
                offset += 1;
1,630✔
821
                buf[offset] = b'u';
1,630✔
822
                offset += 1;
1,630✔
823
                buf[offset] = b'0';
1,630✔
824
                offset += 1;
1,630✔
825
                buf[offset] = b'0';
1,630✔
826
                offset += 1;
1,630✔
827
                buf[offset] = HEX[((x >> 4) & 0xf) as usize];
1,630✔
828
                offset += 1;
1,630✔
829
                buf[offset] = HEX[(x & 0xf) as usize];
1,630✔
830
                offset += 1;
1,630✔
831
            } else {
4,411✔
832
                buf[offset] = b'\\';
4,408✔
833
                offset += 1;
4,408✔
834
                buf[offset] = escape;
4,408✔
835
                offset += 1;
4,408✔
836
            }
4,408✔
837
        }
838
        buf[offset] = b'"';
308,454✔
839
        offset += 1;
308,454✔
840
        match std::str::from_utf8(&buf[0..offset]) {
308,454✔
841
            Ok(s) => {
308,454✔
842
                self.push_str(s)?;
308,454✔
843
            }
844
            Err(err) => {
×
845
                let error = format!(
×
846
                    "\"UTF8-ERROR: what=[escaped string] error={} output={:02x?} input={:02x?}\"",
×
847
                    err,
×
848
                    &buf[0..offset],
×
849
                    val.as_bytes(),
×
850
                );
×
851
                self.push_str(&error)?;
×
852
            }
853
        }
854
        Ok(())
308,454✔
855
    }
308,454✔
856

857
    fn encode_base64(&mut self, val: &[u8]) -> Result<&mut Self, JsonError> {
×
858
        let encoded_len = 4 * val.len().div_ceil(3);
×
859
        if self.buf.capacity() < self.buf.len() + encoded_len {
×
860
            self.buf.try_reserve(encoded_len)?;
×
861
        }
×
862
        STANDARD.encode_string(val, &mut self.buf);
×
863
        Ok(self)
×
864
    }
×
865
}
866

867
/// A Suricata specific function to create a string from bytes when UTF-8 decoding fails.
868
///
869
/// For bytes over 0x0f, we encode as hex like "\xf2".
870
fn try_string_from_bytes(input: &[u8]) -> Result<String, JsonError> {
32✔
871
    let mut out = String::new();
32✔
872

32✔
873
    // Allocate enough data to handle the worst case scenario of every
32✔
874
    // byte needing to be presented as a byte.
32✔
875
    out.try_reserve(input.len() * 4)?;
32✔
876

877
    for b in input.iter() {
3,990✔
878
        if *b < 128 {
3,990✔
879
            out.push(*b as char);
3,141✔
880
        } else {
3,147✔
881
            out.push_str(&format!("\\x{:02x}", *b));
849✔
882
        }
849✔
883
    }
884
    return Ok(out);
32✔
885
}
32✔
886

887
#[no_mangle]
888
pub extern "C" fn SCJbNewObject() -> *mut JsonBuilder {
26,625✔
889
    match JsonBuilder::try_new_object() {
26,625✔
890
        Ok(js) => {
26,625✔
891
            let boxed = Box::new(js);
26,625✔
892
            Box::into_raw(boxed)
26,625✔
893
        }
894
        Err(_) => std::ptr::null_mut(),
×
895
    }
896
}
26,625✔
897

898
#[no_mangle]
899
pub extern "C" fn SCJbNewArray() -> *mut JsonBuilder {
4,053✔
900
    match JsonBuilder::try_new_array() {
4,053✔
901
        Ok(js) => {
4,053✔
902
            let boxed = Box::new(js);
4,053✔
903
            Box::into_raw(boxed)
4,053✔
904
        }
905
        Err(_) => std::ptr::null_mut(),
×
906
    }
907
}
4,053✔
908

909
#[no_mangle]
910
pub extern "C" fn SCJbClone(js: &mut JsonBuilder) -> *mut JsonBuilder {
×
911
    let clone = Box::new(js.clone());
×
912
    Box::into_raw(clone)
×
913
}
×
914

915
#[no_mangle]
916
pub unsafe extern "C" fn SCJbFree(js: &mut JsonBuilder) {
30,678✔
917
    let _ = Box::from_raw(js);
30,678✔
918
}
30,678✔
919

920
#[no_mangle]
921
pub extern "C" fn SCJbCapacity(jb: &mut JsonBuilder) -> usize {
×
922
    jb.capacity()
×
923
}
×
924

925
#[no_mangle]
926
pub extern "C" fn SCJbReset(jb: &mut JsonBuilder) {
×
927
    jb.reset();
×
928
}
×
929

930
#[no_mangle]
931
pub unsafe extern "C" fn SCJbOpenObject(js: &mut JsonBuilder, key: *const c_char) -> bool {
26,546✔
932
    if let Ok(s) = CStr::from_ptr(key).to_str() {
26,546✔
933
        js.open_object(s).is_ok()
26,546✔
934
    } else {
935
        false
×
936
    }
937
}
26,546✔
938

939
#[no_mangle]
940
pub unsafe extern "C" fn SCJbStartObject(js: &mut JsonBuilder) -> bool {
415✔
941
    js.start_object().is_ok()
415✔
942
}
415✔
943

944
#[no_mangle]
945
pub unsafe extern "C" fn SCJbOpenArray(js: &mut JsonBuilder, key: *const c_char) -> bool {
4,535✔
946
    if let Ok(s) = CStr::from_ptr(key).to_str() {
4,535✔
947
        js.open_array(s).is_ok()
4,535✔
948
    } else {
949
        false
×
950
    }
951
}
4,535✔
952

953
#[no_mangle]
954
pub unsafe extern "C" fn SCJbSetString(
170,769✔
955
    js: &mut JsonBuilder, key: *const c_char, val: *const c_char,
170,769✔
956
) -> bool {
170,769✔
957
    if val.is_null() {
170,769✔
958
        return false;
32✔
959
    }
170,737✔
960
    if let Ok(key) = CStr::from_ptr(key).to_str() {
170,737✔
961
        if let Ok(val) = CStr::from_ptr(val).to_str() {
170,737✔
962
            return js.set_string(key, val).is_ok();
170,737✔
963
        }
×
964
    }
×
965
    return false;
×
966
}
170,769✔
967

968
#[no_mangle]
969
pub unsafe extern "C" fn SCJbSetStringFromBytes(
30,678✔
970
    js: &mut JsonBuilder, key: *const c_char, bytes: *const u8, len: u32,
30,678✔
971
) -> bool {
30,678✔
972
    if bytes.is_null() || len == 0 {
30,678✔
973
        return false;
115✔
974
    }
30,563✔
975
    if let Ok(key) = CStr::from_ptr(key).to_str() {
30,563✔
976
        let val = std::slice::from_raw_parts(bytes, len as usize);
30,563✔
977
        return js.set_string_from_bytes(key, val).is_ok();
30,563✔
978
    }
×
979
    return false;
×
980
}
30,678✔
981

982
#[no_mangle]
983
pub unsafe extern "C" fn SCJbSetPrintAsciiString(
×
984
    js: &mut JsonBuilder, key: *const c_char, bytes: *const u8, len: u32,
×
985
) -> bool {
×
986
    if bytes.is_null() || len == 0 {
×
987
        return false;
×
988
    }
×
989
    if let Ok(key) = CStr::from_ptr(key).to_str() {
×
990
        let val = std::slice::from_raw_parts(bytes, len as usize);
×
991
        return js.set_print_ascii(key, val).is_ok();
×
992
    }
×
993
    return false;
×
994
}
×
995

996
#[no_mangle]
997
pub unsafe extern "C" fn SCJbSetBase64(
×
998
    js: &mut JsonBuilder, key: *const c_char, bytes: *const u8, len: u32,
×
999
) -> bool {
×
1000
    if bytes.is_null() || len == 0 {
×
1001
        return false;
×
1002
    }
×
1003
    if let Ok(key) = CStr::from_ptr(key).to_str() {
×
1004
        let val = std::slice::from_raw_parts(bytes, len as usize);
×
1005
        return js.set_base64(key, val).is_ok();
×
1006
    }
×
1007
    return false;
×
1008
}
×
1009

1010
#[no_mangle]
1011
pub unsafe extern "C" fn SCJbSetHex(
×
1012
    js: &mut JsonBuilder, key: *const c_char, bytes: *const u8, len: u32,
×
1013
) -> bool {
×
1014
    if bytes.is_null() || len == 0 {
×
1015
        return false;
×
1016
    }
×
1017
    if let Ok(key) = CStr::from_ptr(key).to_str() {
×
1018
        let val = std::slice::from_raw_parts(bytes, len as usize);
×
1019
        return js.set_hex(key, val).is_ok();
×
1020
    }
×
1021
    return false;
×
1022
}
×
1023

1024
#[no_mangle]
1025
pub unsafe extern "C" fn SCJbSetFormatted(js: &mut JsonBuilder, formatted: *const c_char) -> bool {
25,067✔
1026
    if let Ok(formatted) = CStr::from_ptr(formatted).to_str() {
25,067✔
1027
        return js.set_formatted(formatted).is_ok();
25,067✔
1028
    }
×
1029
    return false;
×
1030
}
25,067✔
1031

1032
#[no_mangle]
1033
pub unsafe extern "C" fn SCJbAppendObject(jb: &mut JsonBuilder, obj: &JsonBuilder) -> bool {
×
1034
    jb.append_object(obj).is_ok()
×
1035
}
×
1036

1037
#[no_mangle]
1038
pub unsafe extern "C" fn SCJbSetObject(
7,937✔
1039
    js: &mut JsonBuilder, key: *const c_char, val: &mut JsonBuilder,
7,937✔
1040
) -> bool {
7,937✔
1041
    if let Ok(key) = CStr::from_ptr(key).to_str() {
7,937✔
1042
        return js.set_object(key, val).is_ok();
7,937✔
1043
    }
×
1044
    return false;
×
1045
}
7,937✔
1046

1047
#[no_mangle]
1048
pub unsafe extern "C" fn SCJbAppendString(js: &mut JsonBuilder, val: *const c_char) -> bool {
7,094✔
1049
    if val.is_null() {
7,094✔
1050
        return false;
×
1051
    }
7,094✔
1052
    if let Ok(val) = CStr::from_ptr(val).to_str() {
7,094✔
1053
        return js.append_string(val).is_ok();
7,094✔
1054
    }
×
1055
    return false;
×
1056
}
7,094✔
1057

1058
#[no_mangle]
1059
pub unsafe extern "C" fn SCJbAppendStringFromBytes(
4,032✔
1060
    js: &mut JsonBuilder, bytes: *const u8, len: u32,
4,032✔
1061
) -> bool {
4,032✔
1062
    if bytes.is_null() || len == 0 {
4,032✔
1063
        return false;
×
1064
    }
4,032✔
1065
    let val = std::slice::from_raw_parts(bytes, len as usize);
4,032✔
1066
    return js.append_string_from_bytes(val).is_ok();
4,032✔
1067
}
4,032✔
1068

1069
#[no_mangle]
1070
pub unsafe extern "C" fn SCJbAppendBase64(
×
1071
    js: &mut JsonBuilder, bytes: *const u8, len: u32,
×
1072
) -> bool {
×
1073
    if bytes.is_null() || len == 0 {
×
1074
        return false;
×
1075
    }
×
1076
    let val = std::slice::from_raw_parts(bytes, len as usize);
×
1077
    return js.append_base64(val).is_ok();
×
1078
}
×
1079

1080
#[no_mangle]
1081
pub unsafe extern "C" fn SCJbAppendUint(js: &mut JsonBuilder, val: u64) -> bool {
3,718✔
1082
    return js.append_uint(val).is_ok();
3,718✔
1083
}
3,718✔
1084

1085
#[no_mangle]
1086
pub unsafe extern "C" fn SCJbAppendFloat(js: &mut JsonBuilder, val: f64) -> bool {
×
1087
    return js.append_float(val).is_ok();
×
1088
}
×
1089

1090
#[no_mangle]
1091
pub unsafe extern "C" fn SCJbSetUint(js: &mut JsonBuilder, key: *const c_char, val: u64) -> bool {
168,716✔
1092
    if let Ok(key) = CStr::from_ptr(key).to_str() {
168,716✔
1093
        return js.set_uint(key, val).is_ok();
168,716✔
1094
    }
×
1095
    return false;
×
1096
}
168,716✔
1097

1098
#[no_mangle]
1099
pub unsafe extern "C" fn SCJbSetInt(js: &mut JsonBuilder, key: *const c_char, val: i64) -> bool {
×
1100
    if let Ok(key) = CStr::from_ptr(key).to_str() {
×
1101
        return js.set_int(key, val).is_ok();
×
1102
    }
×
1103
    return false;
×
1104
}
×
1105

1106
#[no_mangle]
1107
pub unsafe extern "C" fn SCJbSetFloat(js: &mut JsonBuilder, key: *const c_char, val: f64) -> bool {
×
1108
    if let Ok(key) = CStr::from_ptr(key).to_str() {
×
1109
        return js.set_float(key, val).is_ok();
×
1110
    }
×
1111
    return false;
×
1112
}
×
1113

1114
#[no_mangle]
1115
pub unsafe extern "C" fn SCJbSetBool(js: &mut JsonBuilder, key: *const c_char, val: bool) -> bool {
7,058✔
1116
    if let Ok(key) = CStr::from_ptr(key).to_str() {
7,058✔
1117
        return js.set_bool(key, val).is_ok();
7,058✔
1118
    }
×
1119
    return false;
×
1120
}
7,058✔
1121

1122
#[no_mangle]
1123
pub unsafe extern "C" fn SCJbClose(js: &mut JsonBuilder) -> bool {
62,077✔
1124
    js.close().is_ok()
62,077✔
1125
}
62,077✔
1126

1127
#[no_mangle]
1128
pub unsafe extern "C" fn SCJbLen(js: &JsonBuilder) -> usize {
22,681✔
1129
    js.buf.len()
22,681✔
1130
}
22,681✔
1131

1132
#[no_mangle]
1133
pub unsafe extern "C" fn SCJbPtr(js: &mut JsonBuilder) -> *const u8 {
22,681✔
1134
    js.buf.as_ptr()
22,681✔
1135
}
22,681✔
1136

1137
#[no_mangle]
1138
pub unsafe extern "C" fn SCJbGetMark(js: &mut JsonBuilder, mark: &mut JsonBuilderMark) {
775✔
1139
    let m = js.get_mark();
775✔
1140
    mark.position = m.position;
775✔
1141
    mark.state_index = m.state_index;
775✔
1142
    mark.state = m.state;
775✔
1143
}
775✔
1144

1145
#[no_mangle]
1146
pub unsafe extern "C" fn SCJbRestoreMark(js: &mut JsonBuilder, mark: &mut JsonBuilderMark) -> bool {
44✔
1147
    js.restore_mark(mark).is_ok()
44✔
1148
}
44✔
1149

1150
#[cfg(test)]
1151
mod test {
1152
    use super::*;
1153

1154
    #[test]
1155
    fn test_try_reserve() {
1✔
1156
        // Just a sanity check that try_reserve works as I expect.
1✔
1157
        let mut buf = String::new();
1✔
1158
        assert_eq!(buf.len(), 0);
1✔
1159
        assert_eq!(buf.capacity(), 0);
1✔
1160

1161
        buf.try_reserve(1).unwrap();
1✔
1162
        assert_eq!(buf.len(), 0);
1✔
1163
        assert!(buf.capacity() >= 1);
1✔
1164
    }
1✔
1165

1166
    #[test]
1167
    fn test_set_bool() {
1✔
1168
        let mut jb = JsonBuilder::try_new_object().unwrap();
1✔
1169
        jb.set_bool("first", true).unwrap();
1✔
1170
        assert_eq!(jb.buf, r#"{"first":true"#);
1✔
1171
        jb.set_bool("second", false).unwrap();
1✔
1172
        assert_eq!(jb.buf, r#"{"first":true,"second":false"#);
1✔
1173

1174
        let mut jb = JsonBuilder::try_new_object().unwrap();
1✔
1175
        jb.set_bool("first", false).unwrap();
1✔
1176
        assert_eq!(jb.buf, r#"{"first":false"#);
1✔
1177
        jb.set_bool("second", true).unwrap();
1✔
1178
        assert_eq!(jb.buf, r#"{"first":false,"second":true"#);
1✔
1179
    }
1✔
1180

1181
    #[test]
1182
    fn test_object_in_object() -> Result<(), JsonError> {
1✔
1183
        let mut js = JsonBuilder::try_new_object().unwrap();
1✔
1184

1✔
1185
        js.open_object("object")?;
1✔
1186
        assert_eq!(js.current_state(), State::ObjectFirst);
1✔
1187
        assert_eq!(js.buf, r#"{"object":{"#);
1✔
1188

1189
        js.set_string("one", "one")?;
1✔
1190
        assert_eq!(js.current_state(), State::ObjectNth);
1✔
1191
        assert_eq!(js.buf, r#"{"object":{"one":"one""#);
1✔
1192

1193
        js.close()?;
1✔
1194
        assert_eq!(js.current_state(), State::ObjectNth);
1✔
1195
        assert_eq!(js.buf, r#"{"object":{"one":"one"}"#);
1✔
1196

1197
        js.close()?;
1✔
1198
        assert_eq!(js.current_state(), State::None);
1✔
1199
        assert_eq!(js.buf, r#"{"object":{"one":"one"}}"#);
1✔
1200

1201
        Ok(())
1✔
1202
    }
1✔
1203

1204
    #[test]
1205
    fn test_empty_array_in_object() -> Result<(), JsonError> {
1✔
1206
        let mut js = JsonBuilder::try_new_object().unwrap();
1✔
1207

1✔
1208
        js.open_array("array")?;
1✔
1209
        assert_eq!(js.current_state(), State::ArrayFirst);
1✔
1210

1211
        js.close()?;
1✔
1212
        assert_eq!(js.current_state(), State::ObjectNth);
1✔
1213
        assert_eq!(js.buf, r#"{"array":[]"#);
1✔
1214

1215
        js.close()?;
1✔
1216
        assert_eq!(js.buf, r#"{"array":[]}"#);
1✔
1217

1218
        Ok(())
1✔
1219
    }
1✔
1220

1221
    #[test]
1222
    #[cfg(not(feature = "debug-validate"))]
1223
    fn test_array_in_object() -> Result<(), JsonError> {
1✔
1224
        let mut js = JsonBuilder::try_new_object().unwrap();
1✔
1225

1✔
1226
        // Attempt to add an item, should fail.
1✔
1227
        assert_eq!(
1✔
1228
            js.append_string("will fail").err().unwrap(),
1✔
1229
            JsonError::InvalidState
1✔
1230
        );
1✔
1231

1232
        js.open_array("array")?;
1✔
1233
        assert_eq!(js.current_state(), State::ArrayFirst);
1✔
1234

1235
        js.append_string("one")?;
1✔
1236
        assert_eq!(js.current_state(), State::ArrayNth);
1✔
1237
        assert_eq!(js.buf, r#"{"array":["one""#);
1✔
1238

1239
        js.append_string("two")?;
1✔
1240
        assert_eq!(js.current_state(), State::ArrayNth);
1✔
1241
        assert_eq!(js.buf, r#"{"array":["one","two""#);
1✔
1242

1243
        js.append_uint(3)?;
1✔
1244
        assert_eq!(js.current_state(), State::ArrayNth);
1✔
1245
        assert_eq!(js.buf, r#"{"array":["one","two",3"#);
1✔
1246

1247
        js.close()?;
1✔
1248
        assert_eq!(js.current_state(), State::ObjectNth);
1✔
1249
        assert_eq!(js.buf, r#"{"array":["one","two",3]"#);
1✔
1250

1251
        js.close()?;
1✔
1252
        assert_eq!(js.current_state(), State::None);
1✔
1253
        assert_eq!(js.buf, r#"{"array":["one","two",3]}"#);
1✔
1254

1255
        Ok(())
1✔
1256
    }
1✔
1257

1258
    #[test]
1259
    fn basic_test() -> Result<(), JsonError> {
1✔
1260
        let mut js = JsonBuilder::try_new_object().unwrap();
1✔
1261
        assert_eq!(js.current_state(), State::ObjectFirst);
1✔
1262
        assert_eq!(js.buf, "{");
1✔
1263

1264
        js.set_string("one", "one")?;
1✔
1265
        assert_eq!(js.current_state(), State::ObjectNth);
1✔
1266
        assert_eq!(js.buf, r#"{"one":"one""#);
1✔
1267

1268
        js.set_string("two", "two")?;
1✔
1269
        assert_eq!(js.current_state(), State::ObjectNth);
1✔
1270
        assert_eq!(js.buf, r#"{"one":"one","two":"two""#);
1✔
1271

1272
        js.set_uint("three", 3u8)?;
1✔
1273

1274
        js.close()?;
1✔
1275
        assert_eq!(js.current_state(), State::None);
1✔
1276
        assert_eq!(js.buf, r#"{"one":"one","two":"two","three":3}"#);
1✔
1277

1278
        Ok(())
1✔
1279
    }
1✔
1280

1281
    #[test]
1282
    fn test_combine() -> Result<(), JsonError> {
1✔
1283
        let mut main = JsonBuilder::try_new_object().unwrap();
1✔
1284
        let mut obj = JsonBuilder::try_new_object().unwrap();
1✔
1285
        obj.close()?;
1✔
1286

1287
        let mut array = JsonBuilder::try_new_array().unwrap();
1✔
1288
        array.append_string("one")?;
1✔
1289
        array.append_uint(2)?;
1✔
1290
        array.close()?;
1✔
1291
        main.set_object("object", &obj)?;
1✔
1292
        main.set_object("array", &array)?;
1✔
1293
        main.close()?;
1✔
1294

1295
        assert_eq!(main.buf, r#"{"object":{},"array":["one",2]}"#);
1✔
1296

1297
        Ok(())
1✔
1298
    }
1✔
1299

1300
    #[test]
1301
    fn test_objects_in_array() -> Result<(), JsonError> {
1✔
1302
        let mut js = JsonBuilder::try_new_array()?;
1✔
1303
        assert_eq!(js.buf, r#"["#);
1✔
1304

1305
        js.start_object()?;
1✔
1306
        assert_eq!(js.buf, r#"[{"#);
1✔
1307

1308
        js.set_string("uid", "0")?;
1✔
1309
        assert_eq!(js.buf, r#"[{"uid":"0""#);
1✔
1310

1311
        js.close()?;
1✔
1312
        assert_eq!(js.buf, r#"[{"uid":"0"}"#);
1✔
1313

1314
        js.start_object()?;
1✔
1315
        assert_eq!(js.buf, r#"[{"uid":"0"},{"#);
1✔
1316

1317
        js.set_string("username", "root")?;
1✔
1318
        assert_eq!(js.buf, r#"[{"uid":"0"},{"username":"root""#);
1✔
1319

1320
        js.close()?;
1✔
1321
        assert_eq!(js.buf, r#"[{"uid":"0"},{"username":"root"}"#);
1✔
1322

1323
        js.close()?;
1✔
1324
        assert_eq!(js.buf, r#"[{"uid":"0"},{"username":"root"}]"#);
1✔
1325

1326
        Ok(())
1✔
1327
    }
1✔
1328

1329
    #[test]
1330
    fn test_grow() -> Result<(), JsonError> {
1✔
1331
        let mut jb = JsonBuilder::try_new_object_with_capacity(1).unwrap();
1✔
1332

1✔
1333
        // For efficiency reasons, more capacity may be allocated than
1✔
1334
        // requested.
1✔
1335
        assert!(jb.capacity() > 0);
1✔
1336
        let capacity = jb.capacity();
1✔
1337

1✔
1338
        let mut value = String::new();
1✔
1339
        for i in 0..capacity {
8✔
1340
            value.push_str((i % 10).to_string().as_str());
8✔
1341
        }
8✔
1342
        jb.set_string("foo", &value)?;
1✔
1343
        assert!(jb.capacity() > capacity);
1✔
1344
        Ok(())
1✔
1345
    }
1✔
1346

1347
    #[test]
1348
    fn test_reset() -> Result<(), JsonError> {
1✔
1349
        let mut jb = JsonBuilder::try_new_object().unwrap();
1✔
1350
        assert_eq!(jb.buf, "{");
1✔
1351
        jb.set_string("foo", "bar")?;
1✔
1352
        let cap = jb.capacity();
1✔
1353
        jb.reset();
1✔
1354
        assert_eq!(jb.buf, "{");
1✔
1355
        assert_eq!(jb.capacity(), cap);
1✔
1356
        Ok(())
1✔
1357
    }
1✔
1358

1359
    #[test]
1360
    fn test_append_string_from_bytes() -> Result<(), JsonError> {
1✔
1361
        let mut jb = JsonBuilder::try_new_array().unwrap();
1✔
1362
        let s = &[0x41, 0x41, 0x41, 0x00];
1✔
1363
        jb.append_string_from_bytes(s)?;
1✔
1364
        assert_eq!(jb.buf, r#"["AAA\u0000""#);
1✔
1365

1366
        let s = &[0x00, 0x01, 0x02, 0x03];
1✔
1367
        let mut jb = JsonBuilder::try_new_array().unwrap();
1✔
1368
        jb.append_string_from_bytes(s)?;
1✔
1369
        assert_eq!(jb.buf, r#"["\u0000\u0001\u0002\u0003""#);
1✔
1370

1371
        Ok(())
1✔
1372
    }
1✔
1373

1374
    #[test]
1375
    fn test_set_string_from_bytes() {
1✔
1376
        let mut jb = JsonBuilder::try_new_object().unwrap();
1✔
1377
        jb.set_string_from_bytes("first", &[]).unwrap();
1✔
1378
        assert_eq!(jb.buf, r#"{"first":"""#);
1✔
1379
        jb.set_string_from_bytes("second", &[]).unwrap();
1✔
1380
        assert_eq!(jb.buf, r#"{"first":"","second":"""#);
1✔
1381
    }
1✔
1382

1383
    #[test]
1384
    fn test_append_string_from_bytes_grow() -> Result<(), JsonError> {
1✔
1385
        let s = &[0x00, 0x01, 0x02, 0x03];
1✔
1386
        let mut jb = JsonBuilder::try_new_array().unwrap();
1✔
1387
        jb.append_string_from_bytes(s)?;
1✔
1388

1389
        for i in 1..1000 {
1,000✔
1390
            let mut s = Vec::new();
999✔
1391
            s.resize(i, 0x41);
999✔
1392
            let mut jb = JsonBuilder::try_new_array().unwrap();
999✔
1393
            jb.append_string_from_bytes(&s)?;
999✔
1394
        }
1395

1396
        Ok(())
1✔
1397
    }
1✔
1398

1399
    #[test]
1400
    fn test_set_string_limited() {
1✔
1401
        let mut jb = JsonBuilder::try_new_object().unwrap();
1✔
1402
        jb.set_string_limited("val", "foobar", 10).unwrap();
1✔
1403
        assert_eq!(jb.buf, r#"{"val":"foobar""#);
1✔
1404
        jb.reset();
1✔
1405
        jb.set_string_limited("val", "foobar", 2).unwrap();
1✔
1406
        assert_eq!(jb.buf, r#"{"val":"fo[truncated 4 additional bytes]""#);
1✔
1407
        jb.reset();
1✔
1408
        jb.set_string_limited("val", "foobar", 0).unwrap();
1✔
1409
        assert_eq!(jb.buf, r#"{"val":"[truncated 6 additional bytes]""#);
1✔
1410
        jb.reset();
1✔
1411
        let unicode_str = "Hello, 世界! 👋😊";
1✔
1412
        // invalid unicode boundary, naive access should panic
1✔
1413
        let result = std::panic::catch_unwind(|| _ = unicode_str[..9]);
1✔
1414
        assert!(result.is_err());
1✔
1415
        // our code should just skip the incomplete character
1416
        jb.set_string_limited("val", unicode_str, 9).unwrap();
1✔
1417
        assert_eq!(jb.buf, r#"{"val":"Hello, [truncated 14 additional bytes]""#);
1✔
1418
        jb.reset();
1✔
1419
        // valid unicode boundary, naive access should not panic
1✔
1420
        let result = std::panic::catch_unwind(|| _ = unicode_str[..10]);
1✔
1421
        assert!(result.is_ok());
1✔
1422
        jb.set_string_limited("val", unicode_str, 10).unwrap();
1✔
1423
        assert_eq!(
1✔
1424
            jb.buf,
1✔
1425
            r#"{"val":"Hello, 世[truncated 13 additional bytes]""#
1✔
1426
        );
1✔
1427
        jb.reset();
1✔
1428
        let unicode_str2 = "世";
1✔
1429
        // this character has three UTF-8 bytes
1✔
1430
        assert_eq!(
1✔
1431
            unicode_str2,
1✔
1432
            std::str::from_utf8(&[0xE4, 0xB8, 0x96]).unwrap()
1✔
1433
        );
1✔
1434
        let result = std::panic::catch_unwind(|| _ = unicode_str2[..1]);
1✔
1435
        assert!(result.is_err());
1✔
1436
        jb.set_string_limited("val", unicode_str2, 1).unwrap();
1✔
1437
        assert_eq!(jb.buf, r#"{"val":"[truncated 2 additional bytes]""#);
1✔
1438
        jb.reset();
1✔
1439
        jb.set_string_limited("val", unicode_str2, 2).unwrap();
1✔
1440
        assert_eq!(jb.buf, r#"{"val":"[truncated 1 additional byte]""#);
1✔
1441
        jb.reset();
1✔
1442
        // with limit 3 or more we should include it in the log
1✔
1443
        jb.set_string_limited("val", unicode_str2, 3).unwrap();
1✔
1444
        assert_eq!(jb.buf, r#"{"val":"世""#);
1✔
1445
        jb.reset();
1✔
1446
        jb.set_string_limited("val", unicode_str2, 4).unwrap();
1✔
1447
        assert_eq!(jb.buf, r#"{"val":"世""#);
1✔
1448
        jb.reset();
1✔
1449
        jb.set_string_limited("val", unicode_str2, 0).unwrap();
1✔
1450
        assert_eq!(jb.buf, r#"{"val":"[truncated 3 additional bytes]""#);
1✔
1451
        let unicode_str3 = "🏴󠁧󠁢󠁷󠁬󠁳󠁿";
1✔
1452
        // this character consists of multiple code points
1✔
1453
        jb.reset();
1✔
1454
        jb.set_string_limited("val", unicode_str3, 7).unwrap();
1✔
1455
        assert_eq!(jb.buf, r#"{"val":"🏴[truncated 21 additional bytes]""#);
1✔
1456
        jb.reset();
1✔
1457
        jb.set_string_limited("val", unicode_str3, 2).unwrap();
1✔
1458
        assert_eq!(jb.buf, r#"{"val":"[truncated 26 additional bytes]""#);
1✔
1459
    }
1✔
1460

1461
    #[test]
1462
    fn test_set_string_from_bytes_limited() {
1✔
1463
        let mut jb = JsonBuilder::try_new_object().unwrap();
1✔
1464
        jb.set_string_from_bytes_limited("first", b"foobar", 10)
1✔
1465
            .unwrap();
1✔
1466
        assert_eq!(jb.buf, r#"{"first":"foobar""#);
1✔
1467
        jb.set_string_from_bytes_limited("second", b"foobar", 2)
1✔
1468
            .unwrap();
1✔
1469
        assert_eq!(
1✔
1470
            jb.buf,
1✔
1471
            r#"{"first":"foobar","second":"fo[truncated 4 additional bytes]""#
1✔
1472
        );
1✔
1473
        jb.set_string_from_bytes_limited("third", b"foobar", 0)
1✔
1474
            .unwrap();
1✔
1475
        assert_eq!(
1✔
1476
            jb.buf,
1✔
1477
            r#"{"first":"foobar","second":"fo[truncated 4 additional bytes]","third":"[truncated 6 additional bytes]""#
1✔
1478
        );
1✔
1479
    }
1✔
1480

1481
    #[test]
1482
    fn test_invalid_utf8() {
1✔
1483
        let mut jb = JsonBuilder::try_new_object().unwrap();
1✔
1484
        jb.set_string_from_bytes("invalid", &[0xf0, 0xf1, 0xf2])
1✔
1485
            .unwrap();
1✔
1486
        assert_eq!(jb.buf, r#"{"invalid":"\\xf0\\xf1\\xf2""#);
1✔
1487

1488
        let mut jb = JsonBuilder::try_new_array().unwrap();
1✔
1489
        jb.append_string_from_bytes(&[0xf0, 0xf1, 0xf2]).unwrap();
1✔
1490
        assert_eq!(jb.buf, r#"["\\xf0\\xf1\\xf2""#);
1✔
1491
    }
1✔
1492

1493
    #[test]
1494
    fn test_marks() {
1✔
1495
        let mut jb = JsonBuilder::try_new_object().unwrap();
1✔
1496
        jb.set_string("foo", "bar").unwrap();
1✔
1497
        assert_eq!(jb.buf, r#"{"foo":"bar""#);
1✔
1498
        assert_eq!(jb.current_state(), State::ObjectNth);
1✔
1499
        assert_eq!(jb.state.len(), 2);
1✔
1500
        let mark = jb.get_mark();
1✔
1501

1✔
1502
        // Mutate such that states are transitioned.
1✔
1503
        jb.open_array("bar").unwrap();
1✔
1504
        jb.start_object().unwrap();
1✔
1505
        assert_eq!(jb.buf, r#"{"foo":"bar","bar":[{"#);
1✔
1506
        assert_eq!(jb.current_state(), State::ObjectFirst);
1✔
1507
        assert_eq!(jb.state.len(), 4);
1✔
1508

1509
        // Restore to mark.
1510
        jb.restore_mark(&mark).unwrap();
1✔
1511
        assert_eq!(jb.buf, r#"{"foo":"bar""#);
1✔
1512
        assert_eq!(jb.current_state(), State::ObjectNth);
1✔
1513
        assert_eq!(jb.state.len(), 2);
1✔
1514
    }
1✔
1515

1516
    #[test]
1517
    fn test_set_formatted() {
1✔
1518
        let mut jb = JsonBuilder::try_new_object().unwrap();
1✔
1519
        jb.set_formatted("\"foo\":\"bar\"").unwrap();
1✔
1520
        assert_eq!(jb.buf, r#"{"foo":"bar""#);
1✔
1521
        jb.set_formatted("\"bar\":\"foo\"").unwrap();
1✔
1522
        assert_eq!(jb.buf, r#"{"foo":"bar","bar":"foo""#);
1✔
1523
        jb.close().unwrap();
1✔
1524
        assert_eq!(jb.buf, r#"{"foo":"bar","bar":"foo"}"#);
1✔
1525
    }
1✔
1526

1527
    #[test]
1528
    fn test_set_float() {
1✔
1529
        let mut jb = JsonBuilder::try_new_object().unwrap();
1✔
1530
        jb.set_float("one", 1.1).unwrap();
1✔
1531
        jb.set_float("two", 2.2).unwrap();
1✔
1532
        jb.close().unwrap();
1✔
1533
        assert_eq!(jb.buf, r#"{"one":1.1,"two":2.2}"#);
1✔
1534
    }
1✔
1535

1536
    #[test]
1537
    fn test_append_float() {
1✔
1538
        let mut jb = JsonBuilder::try_new_array().unwrap();
1✔
1539
        jb.append_float(1.1).unwrap();
1✔
1540
        jb.append_float(2.2).unwrap();
1✔
1541
        jb.close().unwrap();
1✔
1542
        assert_eq!(jb.buf, r#"[1.1,2.2]"#);
1✔
1543
    }
1✔
1544

1545
    #[test]
1546
    fn test_set_nan() {
1✔
1547
        let mut jb = JsonBuilder::try_new_object().unwrap();
1✔
1548
        jb.set_float("nan", f64::NAN).unwrap();
1✔
1549
        jb.close().unwrap();
1✔
1550
        assert_eq!(jb.buf, r#"{"nan":null}"#);
1✔
1551
    }
1✔
1552

1553
    #[test]
1554
    fn test_append_nan() {
1✔
1555
        let mut jb = JsonBuilder::try_new_array().unwrap();
1✔
1556
        jb.append_float(f64::NAN).unwrap();
1✔
1557
        jb.close().unwrap();
1✔
1558
        assert_eq!(jb.buf, r#"[null]"#);
1✔
1559
    }
1✔
1560

1561
    #[test]
1562
    fn test_set_inf() {
1✔
1563
        let mut jb = JsonBuilder::try_new_object().unwrap();
1✔
1564
        jb.set_float("inf", f64::INFINITY).unwrap();
1✔
1565
        jb.close().unwrap();
1✔
1566
        assert_eq!(jb.buf, r#"{"inf":null}"#);
1✔
1567

1568
        let mut jb = JsonBuilder::try_new_object().unwrap();
1✔
1569
        jb.set_float("inf", f64::NEG_INFINITY).unwrap();
1✔
1570
        jb.close().unwrap();
1✔
1571
        assert_eq!(jb.buf, r#"{"inf":null}"#);
1✔
1572
    }
1✔
1573

1574
    #[test]
1575
    fn test_append_inf() {
1✔
1576
        let mut jb = JsonBuilder::try_new_array().unwrap();
1✔
1577
        jb.append_float(f64::INFINITY).unwrap();
1✔
1578
        jb.close().unwrap();
1✔
1579
        assert_eq!(jb.buf, r#"[null]"#);
1✔
1580

1581
        let mut jb = JsonBuilder::try_new_array().unwrap();
1✔
1582
        jb.append_float(f64::NEG_INFINITY).unwrap();
1✔
1583
        jb.close().unwrap();
1✔
1584
        assert_eq!(jb.buf, r#"[null]"#);
1✔
1585
    }
1✔
1586
}
1587

1588
// Escape table as seen in serde-json (MIT/Apache license)
1589

1590
const QU: u8 = b'"';
1591
const BS: u8 = b'\\';
1592
const BB: u8 = b'b';
1593
const TT: u8 = b't';
1594
const NN: u8 = b'n';
1595
const FF: u8 = b'f';
1596
const RR: u8 = b'r';
1597
const UU: u8 = b'u';
1598
const __: u8 = 0;
1599

1600
// Look up table for characters that need escaping in a product string
1601
static ESCAPED: [u8; 256] = [
1602
    // 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
1603
    UU, UU, UU, UU, UU, UU, UU, UU, BB, TT, NN, UU, FF, RR, UU, UU, // 0
1604
    UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, // 1
1605
    __, __, QU, __, __, __, __, __, __, __, __, __, __, __, __, __, // 2
1606
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 3
1607
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 4
1608
    __, __, __, __, __, __, __, __, __, __, __, __, BS, __, __, __, // 5
1609
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 6
1610
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 7
1611
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 8
1612
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 9
1613
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // A
1614
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // B
1615
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // C
1616
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // D
1617
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // E
1618
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // F
1619
];
1620

1621
pub static HEX: [u8; 16] = [
1622
    b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'a', b'b', b'c', b'd', b'e', b'f',
1623
];
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc