Programming Language

Nocter

A self-contained systems language built around simplicity, encapsulation, and foolproof design.

/development/std/json/escaping.nct

escaping.nct

//! One compact JSON string-escaping authority shared by every output sink.

see ./index.nct

use /internal/ptr.slice_from_raw_parts_value
use /json/output.ByteSink
use /ptr.from_ref

struct EscapeBuffer {
    storage: [u8; 16]
    len: usize
}

construct EscapeBuffer {
    func empty(): Self {
        return EscapeBuffer {
            storage: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            len: 0,
        }
    }
}

instance EscapeBuffer {
    method &self.bytes(): &[u8] {
        return slice_from_raw_parts_value(from_ref(&self.storage[0]), self.len)
    }

    method &+self.flush<S>(sink: &+S): void! where S impl ByteSink {
        if self.len == 0 { return }
        sink.emit(self.bytes())?
        self.len = 0
        return
    }

    method &+self.push<S>(sink: &+S, byte: u8): void! where S impl ByteSink {
        if self.len == 16 {
            self.flush(sink)?
        }
        self.storage[self.len] = byte
        self.len += 1
        return
    }

    method &+self.prepare<S>(sink: &+S, additional: usize): void! where S impl ByteSink {
        if self.len + additional > 16 {
            self.flush(sink)?
        }
        return
    }
}

func hexadecimal_digit(value: u8): u8 {
    if value < 10 { return 48 + value }
    return 65 + value - 10
}

func push_control_escape<S>(
    buffer: &+EscapeBuffer,
    sink: &+S,
    byte: u8,
): void! where S impl ByteSink {
    buffer.push(sink, 92)?
    if byte == 8 {
        buffer.push(sink, 98)?
    } else if byte == 12 {
        buffer.push(sink, 102)?
    } else if byte == 10 {
        buffer.push(sink, 110)?
    } else if byte == 13 {
        buffer.push(sink, 114)?
    } else if byte == 9 {
        buffer.push(sink, 116)?
    } else {
        buffer.push(sink, 117)?
        buffer.push(sink, 48)?
        buffer.push(sink, 48)?
        buffer.push(sink, hexadecimal_digit(byte / 16))?
        buffer.push(sink, hexadecimal_digit(byte % 16))?
    }
    return
}

func emit_json_string<S>(sink: &+S, text: &str): void! where S impl ByteSink {
    sink.emit("\"".bytes())?
    var buffer = EscapeBuffer.empty()
    let bytes = text.bytes()
    var index: usize = 0
    while index < bytes.len() {
        let byte = bytes[index]
        if byte == 34 || byte == 92 {
            buffer.push(sink, 92)?
            buffer.push(sink, byte)?
        } else if byte < 32 {
            push_control_escape(&+buffer, sink, byte)?
        } else if byte < 128 {
            buffer.push(sink, byte)?
        } else {
            let width: usize = if byte < 224 {
                2
            } else if byte < 240 {
                3
            } else {
                4
            }
            buffer.prepare(sink, width)?
            var scalar_index: usize = 0
            while scalar_index < width {
                buffer.push(sink, bytes[index + scalar_index])?
                scalar_index += 1
            }
            index += width - 1
        }
        index += 1
    }
    buffer.flush(sink)?
    sink.emit("\"".bytes())?
    return
}