v/vlib/x/json2/encoder.v

186 lines
3.7 KiB
V

// Copyright (c) 2019-2022 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module json2
import strings
fn write_value(v Any, i int, len int, mut wr strings.Builder) {
str := v.json_str()
if v is string {
wr.write_string('"$str"')
} else {
wr.write_string(str)
}
if i >= len - 1 {
return
}
wr.write_byte(`,`)
}
// str returns the string representation of the `map[string]Any`.
[manualfree]
pub fn (flds map[string]Any) str() string {
mut wr := strings.new_builder(200)
wr.write_byte(`{`)
mut i := 0
for k, v in flds {
wr.write_string('"$k":')
write_value(v, i, flds.len, mut wr)
i++
}
wr.write_byte(`}`)
defer {
unsafe { wr.free() }
}
res := wr.str()
return res
}
// str returns the string representation of the `[]Any`.
[manualfree]
pub fn (flds []Any) str() string {
mut wr := strings.new_builder(200)
wr.write_byte(`[`)
for i, v in flds {
write_value(v, i, flds.len, mut wr)
}
wr.write_byte(`]`)
defer {
unsafe { wr.free() }
}
res := wr.str()
return res
}
// str returns the string representation of the `Any` type. Use the `json_str` method
// if you want to use the escaped str() version of the `Any` type.
pub fn (f Any) str() string {
if f is string {
return f
} else {
return f.json_str()
}
}
// json_str returns the JSON string representation of the `Any` type.
pub fn (f Any) json_str() string {
match f {
string {
return json_string(f)
}
bool, int, u64, i64 {
return f.str()
}
f32 {
$if !nofloat ? {
str_f32 := f.str()
if str_f32.ends_with('.') {
return '${str_f32}0'
}
return str_f32
}
return '0'
}
f64 {
$if !nofloat ? {
str_f64 := f.str()
if str_f64.ends_with('.') {
return '${str_f64}0'
}
return str_f64
}
return '0'
}
map[string]Any {
return f.str()
}
[]Any {
return f.str()
}
Null {
return 'null'
}
}
}
// char_len_list is a modified version of builtin.utf8_str_len
// that returns an array of character lengths. (e.g "t✔" => [1,2])
fn char_len_list(s string) []int {
mut l := 1
mut ls := []int{}
for i := 0; i < s.len; i++ {
c := s[i]
if (c & (1 << 7)) != 0 {
for t := byte(1 << 6); (c & t) != 0; t >>= 1 {
l++
i++
}
}
ls << l
l = 1
}
return ls
}
const escaped_chars = [r'\b', r'\f', r'\n', r'\r', r'\t']
// json_string returns the JSON spec-compliant version of the string.
[manualfree]
fn json_string(s string) string {
// not the best implementation but will revisit it soon
char_lens := char_len_list(s)
mut sb := strings.new_builder(s.len)
mut i := 0
defer {
unsafe {
char_lens.free()
// freeing string builder on defer after
// returning .str() still isn't working :(
// sb.free()
}
}
for char_len in char_lens {
if char_len == 1 {
chr := s[i]
if chr in important_escapable_chars {
for j := 0; j < important_escapable_chars.len; j++ {
if chr == important_escapable_chars[j] {
sb.write_string(json2.escaped_chars[j])
break
}
}
} else if chr == `"` || chr == `/` || chr == `\\` {
sb.write_string('\\' + chr.ascii_str())
} else if int(chr) < 0x20 {
hex_code := chr.hex()
sb.write_string('\\u00$hex_code')
} else {
sb.write_byte(chr)
}
} else {
slice := s[i..i + char_len]
hex_code := slice.utf32_code().hex()
if hex_code.len < 4 {
// an utf8 codepoint
sb.write_string(slice)
} else if hex_code.len == 4 {
sb.write_string('\\u$hex_code')
} else {
// TODO: still figuring out what
// to do with more than 4 chars
sb.write_byte(` `)
}
unsafe {
slice.free()
hex_code.free()
}
}
i += char_len
}
str := sb.str()
unsafe { sb.free() }
return str
}