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

//! Module for building JSON documents.

#![allow(clippy::missing_safety_doc)]

use base64::{engine::general_purpose::STANDARD, Engine};
use num_traits::Unsigned;
use std::cmp::max;
use std::collections::TryReserveError;
use std::ffi::CStr;
use std::os::raw::c_char;
use std::str::Utf8Error;

const INIT_SIZE: usize = 4096;

#[derive(Debug, PartialEq, Eq)]
pub enum JsonError {
    InvalidState,
    Utf8Error(Utf8Error),
    Memory,
}

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

impl std::fmt::Display for JsonError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            JsonError::InvalidState => write!(f, "invalid state"),
            JsonError::Utf8Error(ref e) => e.fmt(f),
            JsonError::Memory => write!(f, "memory error"),
        }
    }
}

impl From<TryReserveError> for JsonError {
    fn from(_: TryReserveError) -> Self {
        JsonError::Memory
    }
}

impl From<Utf8Error> for JsonError {
    fn from(e: Utf8Error) -> Self {
        JsonError::Utf8Error(e)
    }
}

#[derive(Clone, Debug)]
enum Type {
    Object,
    Array,
}

#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(C)]
enum State {
    None = 0,
    ObjectFirst,
    ObjectNth,
    ArrayFirst,
    ArrayNth,
}

impl State {
    fn from_u64(v: u64) -> Result<State, JsonError> {
        let s = match v {
            0 => State::None,
            1 => State::ObjectFirst,
            2 => State::ObjectNth,
            3 => State::ArrayFirst,
            4 => State::ArrayNth,
            _ => {
                return Err(JsonError::InvalidState);
            }
        };
        Ok(s)
    }
}

/// A "mark" or saved state for a JsonBuilder object.
///
/// The name is full, and the types are u64 as this object is used
/// directly in C as well.
#[repr(C)]
pub struct JsonBuilderMark {
    position: u64,
    state_index: u64,
    state: u64,
}

#[derive(Debug, Clone)]
pub struct JsonBuilder {
    buf: String,
    state: Vec<State>,
    init_type: Type,
}

impl JsonBuilder {
    /// Returns a new JsonBuilder in object state.
    pub fn try_new_object() -> Result<Self, JsonError> {
        Self::try_new_object_with_capacity(INIT_SIZE)
    }

    pub fn try_new_object_with_capacity(capacity: usize) -> Result<Self, JsonError> {
        let mut buf = String::new();
        buf.try_reserve(capacity)?;
        buf.push('{');
        let mut state = Vec::new();
        state.try_reserve(32)?;
        state.extend_from_slice(&[State::None, State::ObjectFirst]);
        Ok(Self {
            buf,
            state,
            init_type: Type::Object,
        })
    }

    /// Returns a new JsonBuilder in array state.
    pub fn try_new_array() -> Result<Self, JsonError> {
        Self::try_new_array_with_capacity(INIT_SIZE)
    }

    pub fn try_new_array_with_capacity(capacity: usize) -> Result<Self, JsonError> {
        let mut buf = String::new();
        buf.try_reserve(capacity)?;
        buf.push('[');
        let mut state = Vec::new();
        state.try_reserve(32)?;
        state.extend_from_slice(&[State::None, State::ArrayFirst]);
        Ok(Self {
            buf,
            state,
            init_type: Type::Array,
        })
    }

    /// A wrapper around String::push that pre-allocates data return
    /// an error if unable to.
    pub fn push(&mut self, ch: char) -> Result<&mut Self, JsonError> {
        if self.buf.capacity() == self.buf.len() {
            self.buf.try_reserve(INIT_SIZE)?;
        }
        self.buf.push(ch);
        Ok(self)
    }

    /// A wrapper around String::push_str that pre-allocates data
    /// return an error if unable to.
    pub fn push_str(&mut self, s: &str) -> Result<&mut Self, JsonError> {
        if self.buf.capacity() < self.buf.len() + s.len() {
            self.buf.try_reserve(max(INIT_SIZE, s.len()))?;
        }
        self.buf.push_str(s);
        Ok(self)
    }

    // Reset the builder to its initial state, without losing
    // the current capacity.
    pub fn reset(&mut self) {
        self.buf.truncate(0);
        self.state.clear();
        match self.init_type {
            Type::Array => {
                self.buf.push('[');
                self.state
                    .extend_from_slice(&[State::None, State::ArrayFirst]);
            }
            Type::Object => {
                self.buf.push('{');
                self.state
                    .extend_from_slice(&[State::None, State::ObjectFirst]);
            }
        }
    }

    // Closes the currently open datatype (object or array).
    pub fn close(&mut self) -> Result<&mut Self, JsonError> {
        match self.current_state() {
            State::ObjectFirst | State::ObjectNth => {
                self.push('}')?;
                self.pop_state();
                Ok(self)
            }
            State::ArrayFirst | State::ArrayNth => {
                self.push(']')?;
                self.pop_state();
                Ok(self)
            }
            State::None => {
                debug_validate_fail!("invalid state");
                Err(JsonError::InvalidState)
            }
        }
    }

    // Return the current state of the JsonBuilder.
    fn current_state(&self) -> State {
        if self.state.is_empty() {
            State::None
        } else {
            self.state[self.state.len() - 1]
        }
    }

    /// Move to a new state.
    fn push_state(&mut self, state: State) -> Result<(), JsonError> {
        if self.state.len() == self.state.capacity() {
            self.state.try_reserve(32)?;
        }
        self.state.push(state);
        Ok(())
    }

    /// Go back to the previous state.
    fn pop_state(&mut self) {
        self.state.pop();
    }

    /// Change the current state.
    fn set_state(&mut self, state: State) {
        let n = self.state.len() - 1;
        self.state[n] = state;
    }

    pub fn get_mark(&self) -> JsonBuilderMark {
        JsonBuilderMark {
            position: self.buf.len() as u64,
            state: self.current_state() as u64,
            state_index: self.state.len() as u64,
        }
    }

    pub fn restore_mark(&mut self, mark: &JsonBuilderMark) -> Result<(), JsonError> {
        let state = State::from_u64(mark.state)?;
        if mark.position < (self.buf.len() as u64) && mark.state_index < (self.state.len() as u64) {
            self.buf.truncate(mark.position as usize);
            self.state.truncate(mark.state_index as usize);
            self.state[(mark.state_index as usize) - 1] = state;
        }
        Ok(())
    }

    /// Open an object under the given key.
    ///
    /// For example:
    ///     Before: {
    ///     After:  {"key": {
    pub fn open_object(&mut self, key: &str) -> Result<&mut Self, JsonError> {
        match self.current_state() {
            State::ObjectFirst => {
                self.push('"')?;
                self.set_state(State::ObjectNth);
            }
            State::ObjectNth => {
                self.push_str(",\"")?;
            }
            _ => {
                debug_validate_fail!("invalid state");
                return Err(JsonError::InvalidState);
            }
        }
        self.push_str(key)?;
        self.push_str("\":{")?;
        self.push_state(State::ObjectFirst)?;
        Ok(self)
    }

    /// Start an object.
    ///
    /// Like open_object but does not create the object under a key. An
    /// error will be returned if starting an object does not make
    /// sense for the current state.
    pub fn start_object(&mut self) -> Result<&mut Self, JsonError> {
        match self.current_state() {
            State::ArrayFirst => {}
            State::ArrayNth => {
                self.push(',')?;
            }
            _ => {
                debug_validate_fail!("invalid state");
                return Err(JsonError::InvalidState);
            }
        }
        self.push('{')?;
        self.set_state(State::ArrayNth);
        self.push_state(State::ObjectFirst)?;
        Ok(self)
    }

    /// Open an array under the given key.
    ///
    /// For example:
    ///     Before: {
    ///     After:  {"key": [
    pub fn open_array(&mut self, key: &str) -> Result<&mut Self, JsonError> {
        match self.current_state() {
            State::ObjectFirst => {}
            State::ObjectNth => {
                self.push(',')?;
            }
            _ => {
                debug_validate_fail!("invalid state");
                return Err(JsonError::InvalidState);
            }
        }
        self.push('"')?;
        self.push_str(key)?;
        self.push_str("\":[")?;
        self.set_state(State::ObjectNth);
        self.push_state(State::ArrayFirst)?;
        Ok(self)
    }

    /// Add a string to an array.
    pub fn append_string(&mut self, val: &str) -> Result<&mut Self, JsonError> {
        match self.current_state() {
            State::ArrayFirst => {
                self.encode_string(val)?;
                self.set_state(State::ArrayNth);
                Ok(self)
            }
            State::ArrayNth => {
                self.push(',')?;
                self.encode_string(val)?;
                Ok(self)
            }
            _ => {
                debug_validate_fail!("invalid state");
                Err(JsonError::InvalidState)
            }
        }
    }

    pub fn append_string_from_bytes(&mut self, val: &[u8]) -> Result<&mut Self, JsonError> {
        match std::str::from_utf8(val) {
            Ok(s) => self.append_string(s),
            Err(_) => self.append_string(&try_string_from_bytes(val)?),
        }
    }

    /// Add a string to an array.
    pub fn append_base64(&mut self, val: &[u8]) -> Result<&mut Self, JsonError> {
        match self.current_state() {
            State::ArrayFirst => {
                self.push('"')?;
                self.encode_base64(val)?;
                self.push('"')?;
                self.set_state(State::ArrayNth);
                Ok(self)
            }
            State::ArrayNth => {
                self.push(',')?;
                self.push('"')?;
                self.encode_base64(val)?;
                self.push('"')?;
                Ok(self)
            }
            _ => {
                debug_validate_fail!("invalid state");
                Err(JsonError::InvalidState)
            }
        }
    }

    /// Add a byte array to a JSON array encoded as hex.
    pub fn append_hex(&mut self, val: &[u8]) -> Result<&mut Self, JsonError> {
        match self.current_state() {
            State::ArrayFirst => {
                self.push('"')?;
                for i in 0..val.len() {
                    self.push(HEX[(val[i] >> 4) as usize] as char)?;
                    self.push(HEX[(val[i] & 0xf) as usize] as char)?;
                }
                self.push('"')?;
                self.set_state(State::ArrayNth);
                Ok(self)
            }
            State::ArrayNth => {
                self.push(',')?;
                self.push('"')?;
                for i in 0..val.len() {
                    self.push(HEX[(val[i] >> 4) as usize] as char)?;
                    self.push(HEX[(val[i] & 0xf) as usize] as char)?;
                }
                self.push('"')?;
                Ok(self)
            }
            _ => {
                debug_validate_fail!("invalid state");
                Err(JsonError::InvalidState)
            }
        }
    }

    /// Add an unsigned integer to an array.
    pub fn append_uint(&mut self, val: u64) -> Result<&mut Self, JsonError> {
        match self.current_state() {
            State::ArrayFirst => {
                self.set_state(State::ArrayNth);
            }
            State::ArrayNth => {
                self.push(',')?;
            }
            _ => {
                debug_validate_fail!("invalid state");
                return Err(JsonError::InvalidState);
            }
        }
        self.push_str(&val.to_string())
    }

    pub fn append_float(&mut self, val: f64) -> Result<&mut Self, JsonError> {
        match self.current_state() {
            State::ArrayFirst => {
                self.set_state(State::ArrayNth);
            }
            State::ArrayNth => {
                self.push(',')?;
            }
            _ => {
                debug_validate_fail!("invalid state");
                return Err(JsonError::InvalidState);
            }
        }
        self.push_float(val)?;
        Ok(self)
    }

    pub fn set_object(&mut self, key: &str, js: &JsonBuilder) -> Result<&mut Self, JsonError> {
        match self.current_state() {
            State::ObjectNth => {
                self.push(',')?;
            }
            State::ObjectFirst => {
                self.set_state(State::ObjectNth);
            }
            _ => {
                debug_validate_fail!("invalid state");
                return Err(JsonError::InvalidState);
            }
        }
        self.push('"')?;
        self.push_str(key)?;
        self.push_str("\":")?;
        self.push_str(&js.buf)?;
        Ok(self)
    }

    /// Append an object onto this array.
    ///
    /// '[' -> '[{...}'
    /// '[{...}' -> '[{...},{...}'
    pub fn append_object(&mut self, js: &JsonBuilder) -> Result<&mut Self, JsonError> {
        match self.current_state() {
            State::ArrayFirst => {
                self.set_state(State::ArrayNth);
            }
            State::ArrayNth => {
                self.push(',')?;
            }
            _ => {
                debug_validate_fail!("invalid state");
                return Err(JsonError::InvalidState);
            }
        }
        self.push_str(&js.buf)?;
        Ok(self)
    }

    /// Set a key and string value type on an object.
    #[inline(always)]
    pub fn set_string(&mut self, key: &str, val: &str) -> Result<&mut Self, JsonError> {
        match self.current_state() {
            State::ObjectNth => {
                self.push(',')?;
            }
            State::ObjectFirst => {
                self.set_state(State::ObjectNth);
            }
            _ => {
                debug_validate_fail!("invalid state");
                return Err(JsonError::InvalidState);
            }
        }
        self.push('"')?;
        self.push_str(key)?;
        self.push_str("\":")?;
        self.encode_string(val)?;
        Ok(self)
    }

    /// Set a key and a string value on an object, with a limited size
    pub fn set_string_limited(
        &mut self, key: &str, val: &str, limit: usize,
    ) -> Result<&mut Self, JsonError> {
        if val.len() > limit {
            // Gracefully handle splitting UTF-8 strings at arbitrary locations.
            // Strings in Rust are UTF-8; and a UTF-8 code point is max 4 bytes.
            // Hence we will find a suitable boundary within 4 bytes of any byte
            // position, in any direction with sufficiently long strings left.
            // This is an approach similar to Rust's (currently nightly unstable
            // only) "floor_char_boundary" str method:
            // https://doc.rust-lang.org/std/primitive.str.html#method.floor_char_boundary
            for i in 0..=std::cmp::min(limit, 4) {
                // We first try the requested boundary. In the expected
                // ("happy") case the limit is at a code point boundary so the
                // slice will succeed immediately. If not, we successively try
                // earlier positions in the string until we find a suitable
                // position.
                if let Some(valtrunc) = val.get(0..limit - i) {
                    let additional_bytes = val.len() - limit;
                    let outstr = format!(
                        "{valtrunc}[truncated {additional_bytes} additional byte{}]",
                        if additional_bytes != 1 { "s" } else { "" }
                    );
                    self.set_string(key, &outstr)?;
                    break;
                }
            }
        } else {
            self.set_string(key, val)?;
        }
        Ok(self)
    }

    pub fn set_formatted(&mut self, formatted: &str) -> Result<&mut Self, JsonError> {
        match self.current_state() {
            State::ObjectNth => {
                self.push(',')?;
            }
            State::ObjectFirst => {
                self.set_state(State::ObjectNth);
            }
            _ => {
                debug_validate_fail!("invalid state");
                return Err(JsonError::InvalidState);
            }
        }
        self.push_str(formatted)?;
        Ok(self)
    }

    /// Set a key and a string value (from bytes) on an object.
    pub fn set_string_from_bytes(&mut self, key: &str, val: &[u8]) -> Result<&mut Self, JsonError> {
        match std::str::from_utf8(val) {
            Ok(s) => self.set_string(key, s),
            Err(_) => self.set_string(key, &try_string_from_bytes(val)?),
        }
    }

    /// Set a key with a string value taking only ascii-printable bytes.
    /// Non-printable characters are replaced by a dot `.`, except
    /// CR and LF which are escaped the regular json way \r and \n
    pub fn set_print_ascii(&mut self, key: &str, val: &[u8]) -> Result<&mut Self, JsonError> {
        match self.current_state() {
            State::ObjectNth => {
                self.push(',')?;
            }
            State::ObjectFirst => {
                self.set_state(State::ObjectNth);
            }
            _ => {
                debug_validate_fail!("invalid state");
                return Err(JsonError::InvalidState);
            }
        }
        self.push('"')?;
        self.push_str(key)?;
        self.push_str("\":\"")?;
        for &x in val.iter() {
            match x {
                b'\r' => {
                    self.push_str("\\r")?;
                }
                b'\n'=> {
                    self.push_str("\\n")?;
                }
                b'"'=> {
                    self.push_str("\\\"")?;
                }
                b'\\'=> {
                    self.push_str("\\\\")?;
                }
                _ => {
                    if !x.is_ascii() || x.is_ascii_control()  {
                        self.push('.')?;
                    } else {
                        self.push(x as char)?;
                    }
                }
            }
        }
        self.push('"')?;
        Ok(self)
    }

    /// Set a key and a string value (from bytes) on an object, with a limited size
    pub fn set_string_from_bytes_limited(
        &mut self, key: &str, val: &[u8], limit: usize,
    ) -> Result<&mut Self, JsonError> {
        let mut valtrunc = Vec::new();
        let val = if val.len() > limit {
            let additional_bytes = val.len() - limit;
            valtrunc.extend_from_slice(&val[..limit]);
            valtrunc.extend_from_slice(
                format!(
                    "[truncated {additional_bytes} additional byte{}]",
                    if additional_bytes != 1 { "s" } else { "" }
                )
                .as_bytes(),
            );
            &valtrunc
        } else {
            val
        };
        match std::str::from_utf8(val) {
            Ok(s) => self.set_string(key, s),
            Err(_) => self.set_string(key, &try_string_from_bytes(val)?),
        }
    }

    /// Set a key and a string field as the base64 encoded string of the value.
    pub fn set_base64(&mut self, key: &str, val: &[u8]) -> Result<&mut Self, JsonError> {
        match self.current_state() {
            State::ObjectNth => {
                self.push(',')?;
            }
            State::ObjectFirst => {
                self.set_state(State::ObjectNth);
            }
            _ => {
                debug_validate_fail!("invalid state");
                return Err(JsonError::InvalidState);
            }
        }
        self.push('"')?;
        self.push_str(key)?;
        self.push_str("\":\"")?;
        self.encode_base64(val)?;
        self.push('"')?;

        Ok(self)
    }

    /// Set a key and a string field as the hex encoded string of the value.
    pub fn set_hex(&mut self, key: &str, val: &[u8]) -> Result<&mut Self, JsonError> {
        match self.current_state() {
            State::ObjectNth => {
                self.push(',')?;
            }
            State::ObjectFirst => {
                self.set_state(State::ObjectNth);
            }
            _ => {
                debug_validate_fail!("invalid state");
                return Err(JsonError::InvalidState);
            }
        }
        self.push('"')?;
        self.push_str(key)?;
        self.push_str("\":\"")?;
        for i in 0..val.len() {
            self.push(HEX[(val[i] >> 4) as usize] as char)?;
            self.push(HEX[(val[i] & 0xf) as usize] as char)?;
        }
        self.push('"')?;

        Ok(self)
    }

    /// Set a key and an unsigned integer type on an object.
    pub fn set_uint<T>(&mut self, key: &str, val: T) -> Result<&mut Self, JsonError>
    where
        T: Unsigned + Into<u64>,
    {
        let val: u64 = val.into();
        match self.current_state() {
            State::ObjectNth => {
                self.push(',')?;
            }
            State::ObjectFirst => {
                self.set_state(State::ObjectNth);
            }
            _ => {
                debug_validate_fail!("invalid state");
                return Err(JsonError::InvalidState);
            }
        }
        self.push('"')?;
        self.push_str(key)?;
        self.push_str("\":")?;
        self.push_str(&val.to_string())?;
        Ok(self)
    }

    /// Set a key and a signed integer type on an object.
    pub fn set_int(&mut self, key: &str, val: i64) -> Result<&mut Self, JsonError> {
        match self.current_state() {
            State::ObjectNth => {
                self.push(',')?;
            }
            State::ObjectFirst => {
                self.set_state(State::ObjectNth);
            }
            _ => {
                debug_validate_fail!("invalid state");
                return Err(JsonError::InvalidState);
            }
        }
        self.push('"')?;
        self.push_str(key)?;
        self.push_str("\":")?;
        self.push_str(&val.to_string())?;
        Ok(self)
    }

    pub fn set_float(&mut self, key: &str, val: f64) -> Result<&mut Self, JsonError> {
        match self.current_state() {
            State::ObjectNth => {
                self.push(',')?;
            }
            State::ObjectFirst => {
                self.set_state(State::ObjectNth);
            }
            _ => {
                debug_validate_fail!("invalid state");
                return Err(JsonError::InvalidState);
            }
        }
        self.push('"')?;
        self.push_str(key)?;
        self.push_str("\":")?;
        self.push_float(val)?;
        Ok(self)
    }

    pub fn set_bool(&mut self, key: &str, val: bool) -> Result<&mut Self, JsonError> {
        match self.current_state() {
            State::ObjectNth => {
                self.push(',')?;
            }
            State::ObjectFirst => {
                self.set_state(State::ObjectNth);
            }
            _ => {
                debug_validate_fail!("invalid state");
                return Err(JsonError::InvalidState);
            }
        }
        self.push('"')?;
        self.push_str(key)?;
        if val {
            self.push_str("\":true")?;
        } else {
            self.push_str("\":false")?;
        }
        Ok(self)
    }

    pub fn capacity(&self) -> usize {
        self.buf.capacity()
    }

    fn push_float(&mut self, val: f64) -> Result<(), JsonError> {
        if val.is_nan() || val.is_infinite() {
            self.push_str("null")?;
        } else {
            self.push_str(&val.to_string())?;
        }
        Ok(())
    }

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

        // Start by allocating a reasonable size buffer, it will be
        // grown if needed.
        buf.try_reserve(val.len() * 2 + 2)?;
        buf.resize(val.len() * 2 + 2, 0);

        let mut offset = 0;
        let bytes = val.as_bytes();
        buf[offset] = b'"';
        offset += 1;
        for &x in bytes.iter() {
            if offset + 7 >= buf.capacity() {
                // We could be smarter, but just double the buffer size.
                buf.try_reserve(buf.capacity())?;
                buf.resize(buf.capacity(), 0);
            }
            let escape = ESCAPED[x as usize];
            if escape == 0 {
                buf[offset] = x;
                offset += 1;
            } else if escape == b'u' {
                buf[offset] = b'\\';
                offset += 1;
                buf[offset] = b'u';
                offset += 1;
                buf[offset] = b'0';
                offset += 1;
                buf[offset] = b'0';
                offset += 1;
                buf[offset] = HEX[((x >> 4) & 0xf) as usize];
                offset += 1;
                buf[offset] = HEX[(x & 0xf) as usize];
                offset += 1;
            } else {
                buf[offset] = b'\\';
                offset += 1;
                buf[offset] = escape;
                offset += 1;
            }
        }
        buf[offset] = b'"';
        offset += 1;
        match std::str::from_utf8(&buf[0..offset]) {
            Ok(s) => {
                self.push_str(s)?;
            }
            Err(err) => {
                let error = format!(
                    "\"UTF8-ERROR: what=[escaped string] error={} output={:02x?} input={:02x?}\"",
                    err,
                    &buf[0..offset],
                    val.as_bytes(),
                );
                self.push_str(&error)?;
            }
        }
        Ok(())
    }

    fn encode_base64(&mut self, val: &[u8]) -> Result<&mut Self, JsonError> {
        let encoded_len = 4 * val.len().div_ceil(3);
        if self.buf.capacity() < self.buf.len() + encoded_len {
            self.buf.try_reserve(encoded_len)?;
        }
        STANDARD.encode_string(val, &mut self.buf);
        Ok(self)
    }
}

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

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

    for b in input.iter() {
        if *b < 128 {
            out.push(*b as char);
        } else {
            out.push_str(&format!("\\x{:02x}", *b));
        }
    }
    return Ok(out);
}

#[no_mangle]
pub extern "C" fn SCJbNewObject() -> *mut JsonBuilder {
    match JsonBuilder::try_new_object() {
        Ok(js) => {
            let boxed = Box::new(js);
            Box::into_raw(boxed)
        }
        Err(_) => std::ptr::null_mut(),
    }
}

#[no_mangle]
pub extern "C" fn SCJbNewArray() -> *mut JsonBuilder {
    match JsonBuilder::try_new_array() {
        Ok(js) => {
            let boxed = Box::new(js);
            Box::into_raw(boxed)
        }
        Err(_) => std::ptr::null_mut(),
    }
}

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

#[no_mangle]
pub unsafe extern "C" fn SCJbFree(js: &mut JsonBuilder) {
    let _ = Box::from_raw(js);
}

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

#[no_mangle]
pub extern "C" fn SCJbReset(jb: &mut JsonBuilder) {
    jb.reset();
}

#[no_mangle]
pub unsafe extern "C" fn SCJbOpenObject(js: &mut JsonBuilder, key: *const c_char) -> bool {
    if let Ok(s) = CStr::from_ptr(key).to_str() {
        js.open_object(s).is_ok()
    } else {
        false
    }
}

#[no_mangle]
pub unsafe extern "C" fn SCJbStartObject(js: &mut JsonBuilder) -> bool {
    js.start_object().is_ok()
}

#[no_mangle]
pub unsafe extern "C" fn SCJbOpenArray(js: &mut JsonBuilder, key: *const c_char) -> bool {
    if let Ok(s) = CStr::from_ptr(key).to_str() {
        js.open_array(s).is_ok()
    } else {
        false
    }
}

#[no_mangle]
pub unsafe extern "C" fn SCJbSetString(
    js: &mut JsonBuilder, key: *const c_char, val: *const c_char,
) -> bool {
    if val.is_null() {
        return false;
    }
    if let Ok(key) = CStr::from_ptr(key).to_str() {
        if let Ok(val) = CStr::from_ptr(val).to_str() {
            return js.set_string(key, val).is_ok();
        }
    }
    return false;
}

#[no_mangle]
pub unsafe extern "C" fn SCJbSetStringFromBytes(
    js: &mut JsonBuilder, key: *const c_char, bytes: *const u8, len: u32,
) -> bool {
    if bytes.is_null() || len == 0 {
        return false;
    }
    if let Ok(key) = CStr::from_ptr(key).to_str() {
        let val = std::slice::from_raw_parts(bytes, len as usize);
        return js.set_string_from_bytes(key, val).is_ok();
    }
    return false;
}

#[no_mangle]
pub unsafe extern "C" fn SCJbSetPrintAsciiString(
    js: &mut JsonBuilder, key: *const c_char, bytes: *const u8, len: u32,
) -> bool {
    if bytes.is_null() || len == 0 {
        return false;
    }
    if let Ok(key) = CStr::from_ptr(key).to_str() {
        let val = std::slice::from_raw_parts(bytes, len as usize);
        return js.set_print_ascii(key, val).is_ok();
    }
    return false;
}

#[no_mangle]
pub unsafe extern "C" fn SCJbSetBase64(
    js: &mut JsonBuilder, key: *const c_char, bytes: *const u8, len: u32,
) -> bool {
    if bytes.is_null() || len == 0 {
        return false;
    }
    if let Ok(key) = CStr::from_ptr(key).to_str() {
        let val = std::slice::from_raw_parts(bytes, len as usize);
        return js.set_base64(key, val).is_ok();
    }
    return false;
}

#[no_mangle]
pub unsafe extern "C" fn SCJbSetHex(
    js: &mut JsonBuilder, key: *const c_char, bytes: *const u8, len: u32,
) -> bool {
    if bytes.is_null() || len == 0 {
        return false;
    }
    if let Ok(key) = CStr::from_ptr(key).to_str() {
        let val = std::slice::from_raw_parts(bytes, len as usize);
        return js.set_hex(key, val).is_ok();
    }
    return false;
}

#[no_mangle]
pub unsafe extern "C" fn SCJbSetFormatted(js: &mut JsonBuilder, formatted: *const c_char) -> bool {
    if let Ok(formatted) = CStr::from_ptr(formatted).to_str() {
        return js.set_formatted(formatted).is_ok();
    }
    return false;
}

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

#[no_mangle]
pub unsafe extern "C" fn SCJbSetObject(
    js: &mut JsonBuilder, key: *const c_char, val: &mut JsonBuilder,
) -> bool {
    if let Ok(key) = CStr::from_ptr(key).to_str() {
        return js.set_object(key, val).is_ok();
    }
    return false;
}

#[no_mangle]
pub unsafe extern "C" fn SCJbAppendString(js: &mut JsonBuilder, val: *const c_char) -> bool {
    if val.is_null() {
        return false;
    }
    if let Ok(val) = CStr::from_ptr(val).to_str() {
        return js.append_string(val).is_ok();
    }
    return false;
}

#[no_mangle]
pub unsafe extern "C" fn SCJbAppendStringFromBytes(
    js: &mut JsonBuilder, bytes: *const u8, len: u32,
) -> bool {
    if bytes.is_null() || len == 0 {
        return false;
    }
    let val = std::slice::from_raw_parts(bytes, len as usize);
    return js.append_string_from_bytes(val).is_ok();
}

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

#[no_mangle]
pub unsafe extern "C" fn SCJbAppendUint(js: &mut JsonBuilder, val: u64) -> bool {
    return js.append_uint(val).is_ok();
}

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

#[no_mangle]
pub unsafe extern "C" fn SCJbSetUint(js: &mut JsonBuilder, key: *const c_char, val: u64) -> bool {
    if let Ok(key) = CStr::from_ptr(key).to_str() {
        return js.set_uint(key, val).is_ok();
    }
    return false;
}

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

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

#[no_mangle]
pub unsafe extern "C" fn SCJbSetBool(js: &mut JsonBuilder, key: *const c_char, val: bool) -> bool {
    if let Ok(key) = CStr::from_ptr(key).to_str() {
        return js.set_bool(key, val).is_ok();
    }
    return false;
}

#[no_mangle]
pub unsafe extern "C" fn SCJbClose(js: &mut JsonBuilder) -> bool {
    js.close().is_ok()
}

#[no_mangle]
pub unsafe extern "C" fn SCJbLen(js: &JsonBuilder) -> usize {
    js.buf.len()
}

#[no_mangle]
pub unsafe extern "C" fn SCJbPtr(js: &mut JsonBuilder) -> *const u8 {
    js.buf.as_ptr()
}

#[no_mangle]
pub unsafe extern "C" fn SCJbGetMark(js: &mut JsonBuilder, mark: &mut JsonBuilderMark) {
    let m = js.get_mark();
    mark.position = m.position;
    mark.state_index = m.state_index;
    mark.state = m.state;
}

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

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_try_reserve() {
        // Just a sanity check that try_reserve works as I expect.
        let mut buf = String::new();
        assert_eq!(buf.len(), 0);
        assert_eq!(buf.capacity(), 0);

        buf.try_reserve(1).unwrap();
        assert_eq!(buf.len(), 0);
        assert!(buf.capacity() >= 1);
    }

    #[test]
    fn test_set_bool() {
        let mut jb = JsonBuilder::try_new_object().unwrap();
        jb.set_bool("first", true).unwrap();
        assert_eq!(jb.buf, r#"{"first":true"#);
        jb.set_bool("second", false).unwrap();
        assert_eq!(jb.buf, r#"{"first":true,"second":false"#);

        let mut jb = JsonBuilder::try_new_object().unwrap();
        jb.set_bool("first", false).unwrap();
        assert_eq!(jb.buf, r#"{"first":false"#);
        jb.set_bool("second", true).unwrap();
        assert_eq!(jb.buf, r#"{"first":false,"second":true"#);
    }

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

        js.open_object("object")?;
        assert_eq!(js.current_state(), State::ObjectFirst);
        assert_eq!(js.buf, r#"{"object":{"#);

        js.set_string("one", "one")?;
        assert_eq!(js.current_state(), State::ObjectNth);
        assert_eq!(js.buf, r#"{"object":{"one":"one""#);

        js.close()?;
        assert_eq!(js.current_state(), State::ObjectNth);
        assert_eq!(js.buf, r#"{"object":{"one":"one"}"#);

        js.close()?;
        assert_eq!(js.current_state(), State::None);
        assert_eq!(js.buf, r#"{"object":{"one":"one"}}"#);

        Ok(())
    }

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

        js.open_array("array")?;
        assert_eq!(js.current_state(), State::ArrayFirst);

        js.close()?;
        assert_eq!(js.current_state(), State::ObjectNth);
        assert_eq!(js.buf, r#"{"array":[]"#);

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

        Ok(())
    }

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

        // Attempt to add an item, should fail.
        assert_eq!(
            js.append_string("will fail").err().unwrap(),
            JsonError::InvalidState
        );

        js.open_array("array")?;
        assert_eq!(js.current_state(), State::ArrayFirst);

        js.append_string("one")?;
        assert_eq!(js.current_state(), State::ArrayNth);
        assert_eq!(js.buf, r#"{"array":["one""#);

        js.append_string("two")?;
        assert_eq!(js.current_state(), State::ArrayNth);
        assert_eq!(js.buf, r#"{"array":["one","two""#);

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

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

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

        Ok(())
    }

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

        js.set_string("one", "one")?;
        assert_eq!(js.current_state(), State::ObjectNth);
        assert_eq!(js.buf, r#"{"one":"one""#);

        js.set_string("two", "two")?;
        assert_eq!(js.current_state(), State::ObjectNth);
        assert_eq!(js.buf, r#"{"one":"one","two":"two""#);

        js.set_uint("three", 3u8)?;

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

        Ok(())
    }

    #[test]
    fn test_combine() -> Result<(), JsonError> {
        let mut main = JsonBuilder::try_new_object().unwrap();
        let mut obj = JsonBuilder::try_new_object().unwrap();
        obj.close()?;

        let mut array = JsonBuilder::try_new_array().unwrap();
        array.append_string("one")?;
        array.append_uint(2)?;
        array.close()?;
        main.set_object("object", &obj)?;
        main.set_object("array", &array)?;
        main.close()?;

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

        Ok(())
    }

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

        js.start_object()?;
        assert_eq!(js.buf, r#"[{"#);

        js.set_string("uid", "0")?;
        assert_eq!(js.buf, r#"[{"uid":"0""#);

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

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

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

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

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

        Ok(())
    }

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

        // For efficiency reasons, more capacity may be allocated than
        // requested.
        assert!(jb.capacity() > 0);
        let capacity = jb.capacity();

        let mut value = String::new();
        for i in 0..capacity {
            value.push_str((i % 10).to_string().as_str());
        }
        jb.set_string("foo", &value)?;
        assert!(jb.capacity() > capacity);
        Ok(())
    }

    #[test]
    fn test_reset() -> Result<(), JsonError> {
        let mut jb = JsonBuilder::try_new_object().unwrap();
        assert_eq!(jb.buf, "{");
        jb.set_string("foo", "bar")?;
        let cap = jb.capacity();
        jb.reset();
        assert_eq!(jb.buf, "{");
        assert_eq!(jb.capacity(), cap);
        Ok(())
    }

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

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

        Ok(())
    }

    #[test]
    fn test_set_string_from_bytes() {
        let mut jb = JsonBuilder::try_new_object().unwrap();
        jb.set_string_from_bytes("first", &[]).unwrap();
        assert_eq!(jb.buf, r#"{"first":"""#);
        jb.set_string_from_bytes("second", &[]).unwrap();
        assert_eq!(jb.buf, r#"{"first":"","second":"""#);
    }

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

        for i in 1..1000 {
            let mut s = Vec::new();
            s.resize(i, 0x41);
            let mut jb = JsonBuilder::try_new_array().unwrap();
            jb.append_string_from_bytes(&s)?;
        }

        Ok(())
    }

    #[test]
    fn test_set_string_limited() {
        let mut jb = JsonBuilder::try_new_object().unwrap();
        jb.set_string_limited("val", "foobar", 10).unwrap();
        assert_eq!(jb.buf, r#"{"val":"foobar""#);
        jb.reset();
        jb.set_string_limited("val", "foobar", 2).unwrap();
        assert_eq!(jb.buf, r#"{"val":"fo[truncated 4 additional bytes]""#);
        jb.reset();
        jb.set_string_limited("val", "foobar", 0).unwrap();
        assert_eq!(jb.buf, r#"{"val":"[truncated 6 additional bytes]""#);
        jb.reset();
        let unicode_str = "Hello, 世界! 👋😊";
        // invalid unicode boundary, naive access should panic
        let result = std::panic::catch_unwind(|| _ = unicode_str[..9]);
        assert!(result.is_err());
        // our code should just skip the incomplete character
        jb.set_string_limited("val", unicode_str, 9).unwrap();
        assert_eq!(jb.buf, r#"{"val":"Hello, [truncated 14 additional bytes]""#);
        jb.reset();
        // valid unicode boundary, naive access should not panic
        let result = std::panic::catch_unwind(|| _ = unicode_str[..10]);
        assert!(result.is_ok());
        jb.set_string_limited("val", unicode_str, 10).unwrap();
        assert_eq!(
            jb.buf,
            r#"{"val":"Hello, 世[truncated 13 additional bytes]""#
        );
        jb.reset();
        let unicode_str2 = "世";
        // this character has three UTF-8 bytes
        assert_eq!(
            unicode_str2,
            std::str::from_utf8(&[0xE4, 0xB8, 0x96]).unwrap()
        );
        let result = std::panic::catch_unwind(|| _ = unicode_str2[..1]);
        assert!(result.is_err());
        jb.set_string_limited("val", unicode_str2, 1).unwrap();
        assert_eq!(jb.buf, r#"{"val":"[truncated 2 additional bytes]""#);
        jb.reset();
        jb.set_string_limited("val", unicode_str2, 2).unwrap();
        assert_eq!(jb.buf, r#"{"val":"[truncated 1 additional byte]""#);
        jb.reset();
        // with limit 3 or more we should include it in the log
        jb.set_string_limited("val", unicode_str2, 3).unwrap();
        assert_eq!(jb.buf, r#"{"val":"世""#);
        jb.reset();
        jb.set_string_limited("val", unicode_str2, 4).unwrap();
        assert_eq!(jb.buf, r#"{"val":"世""#);
        jb.reset();
        jb.set_string_limited("val", unicode_str2, 0).unwrap();
        assert_eq!(jb.buf, r#"{"val":"[truncated 3 additional bytes]""#);
        let unicode_str3 = "🏴󠁧󠁢󠁷󠁬󠁳󠁿";
        // this character consists of multiple code points
        jb.reset();
        jb.set_string_limited("val", unicode_str3, 7).unwrap();
        assert_eq!(jb.buf, r#"{"val":"🏴[truncated 21 additional bytes]""#);
        jb.reset();
        jb.set_string_limited("val", unicode_str3, 2).unwrap();
        assert_eq!(jb.buf, r#"{"val":"[truncated 26 additional bytes]""#);
    }

    #[test]
    fn test_set_string_from_bytes_limited() {
        let mut jb = JsonBuilder::try_new_object().unwrap();
        jb.set_string_from_bytes_limited("first", b"foobar", 10)
            .unwrap();
        assert_eq!(jb.buf, r#"{"first":"foobar""#);
        jb.set_string_from_bytes_limited("second", b"foobar", 2)
            .unwrap();
        assert_eq!(
            jb.buf,
            r#"{"first":"foobar","second":"fo[truncated 4 additional bytes]""#
        );
        jb.set_string_from_bytes_limited("third", b"foobar", 0)
            .unwrap();
        assert_eq!(
            jb.buf,
            r#"{"first":"foobar","second":"fo[truncated 4 additional bytes]","third":"[truncated 6 additional bytes]""#
        );
    }

    #[test]
    fn test_invalid_utf8() {
        let mut jb = JsonBuilder::try_new_object().unwrap();
        jb.set_string_from_bytes("invalid", &[0xf0, 0xf1, 0xf2])
            .unwrap();
        assert_eq!(jb.buf, r#"{"invalid":"\\xf0\\xf1\\xf2""#);

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

    #[test]
    fn test_marks() {
        let mut jb = JsonBuilder::try_new_object().unwrap();
        jb.set_string("foo", "bar").unwrap();
        assert_eq!(jb.buf, r#"{"foo":"bar""#);
        assert_eq!(jb.current_state(), State::ObjectNth);
        assert_eq!(jb.state.len(), 2);
        let mark = jb.get_mark();

        // Mutate such that states are transitioned.
        jb.open_array("bar").unwrap();
        jb.start_object().unwrap();
        assert_eq!(jb.buf, r#"{"foo":"bar","bar":[{"#);
        assert_eq!(jb.current_state(), State::ObjectFirst);
        assert_eq!(jb.state.len(), 4);

        // Restore to mark.
        jb.restore_mark(&mark).unwrap();
        assert_eq!(jb.buf, r#"{"foo":"bar""#);
        assert_eq!(jb.current_state(), State::ObjectNth);
        assert_eq!(jb.state.len(), 2);
    }

    #[test]
    fn test_set_formatted() {
        let mut jb = JsonBuilder::try_new_object().unwrap();
        jb.set_formatted("\"foo\":\"bar\"").unwrap();
        assert_eq!(jb.buf, r#"{"foo":"bar""#);
        jb.set_formatted("\"bar\":\"foo\"").unwrap();
        assert_eq!(jb.buf, r#"{"foo":"bar","bar":"foo""#);
        jb.close().unwrap();
        assert_eq!(jb.buf, r#"{"foo":"bar","bar":"foo"}"#);
    }

    #[test]
    fn test_set_float() {
        let mut jb = JsonBuilder::try_new_object().unwrap();
        jb.set_float("one", 1.1).unwrap();
        jb.set_float("two", 2.2).unwrap();
        jb.close().unwrap();
        assert_eq!(jb.buf, r#"{"one":1.1,"two":2.2}"#);
    }

    #[test]
    fn test_append_float() {
        let mut jb = JsonBuilder::try_new_array().unwrap();
        jb.append_float(1.1).unwrap();
        jb.append_float(2.2).unwrap();
        jb.close().unwrap();
        assert_eq!(jb.buf, r#"[1.1,2.2]"#);
    }

    #[test]
    fn test_set_nan() {
        let mut jb = JsonBuilder::try_new_object().unwrap();
        jb.set_float("nan", f64::NAN).unwrap();
        jb.close().unwrap();
        assert_eq!(jb.buf, r#"{"nan":null}"#);
    }

    #[test]
    fn test_append_nan() {
        let mut jb = JsonBuilder::try_new_array().unwrap();
        jb.append_float(f64::NAN).unwrap();
        jb.close().unwrap();
        assert_eq!(jb.buf, r#"[null]"#);
    }

    #[test]
    fn test_set_inf() {
        let mut jb = JsonBuilder::try_new_object().unwrap();
        jb.set_float("inf", f64::INFINITY).unwrap();
        jb.close().unwrap();
        assert_eq!(jb.buf, r#"{"inf":null}"#);

        let mut jb = JsonBuilder::try_new_object().unwrap();
        jb.set_float("inf", f64::NEG_INFINITY).unwrap();
        jb.close().unwrap();
        assert_eq!(jb.buf, r#"{"inf":null}"#);
    }

    #[test]
    fn test_append_inf() {
        let mut jb = JsonBuilder::try_new_array().unwrap();
        jb.append_float(f64::INFINITY).unwrap();
        jb.close().unwrap();
        assert_eq!(jb.buf, r#"[null]"#);

        let mut jb = JsonBuilder::try_new_array().unwrap();
        jb.append_float(f64::NEG_INFINITY).unwrap();
        jb.close().unwrap();
        assert_eq!(jb.buf, r#"[null]"#);
    }
}

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

const QU: u8 = b'"';
const BS: u8 = b'\\';
const BB: u8 = b'b';
const TT: u8 = b't';
const NN: u8 = b'n';
const FF: u8 = b'f';
const RR: u8 = b'r';
const UU: u8 = b'u';
const __: u8 = 0;

// Look up table for characters that need escaping in a product string
static ESCAPED: [u8; 256] = [
    // 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
    UU, UU, UU, UU, UU, UU, UU, UU, BB, TT, NN, UU, FF, RR, UU, UU, // 0
    UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, // 1
    __, __, QU, __, __, __, __, __, __, __, __, __, __, __, __, __, // 2
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 3
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 4
    __, __, __, __, __, __, __, __, __, __, __, __, BS, __, __, __, // 5
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 6
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 7
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 8
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 9
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // A
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // B
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // C
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // D
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // E
    __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // F
];

pub static HEX: [u8; 16] = [
    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',
];
