1#include <base/dbg.h>
2#include <base/str.h>
3
4#include <engine/shared/json.h>
5
6static bool JsonValidateUtf8Recursive(const json_value *pValue)
7{
8 dbg_assert(pValue != nullptr, "JsonValidateUtf8Recursive: pValue must not be null");
9 switch(pValue->type)
10 {
11 case json_string:
12 return str_utf8_check(str: *pValue);
13 case json_array:
14 for(unsigned i = 0; i < pValue->u.array.length; i++)
15 {
16 if(!JsonValidateUtf8Recursive(pValue: &(*pValue)[i]))
17 return false;
18 }
19 return true;
20 case json_object:
21 for(unsigned i = 0; i < pValue->u.object.length; i++)
22 {
23 const char *pName = pValue->u.object.values[i].name;
24 if(!str_utf8_check(str: pName))
25 return false;
26 if(!JsonValidateUtf8Recursive(pValue: &(*pValue)[i]))
27 return false;
28 }
29 return true;
30 default:
31 return true;
32 }
33}
34
35json_value *JsonParse(const json_char *pJson, size_t Length)
36{
37 json_value *pValue = json_parse(json: pJson, length: Length);
38 if(pValue == nullptr)
39 {
40 return nullptr;
41 }
42 if(!JsonValidateUtf8Recursive(pValue))
43 {
44 json_value_free(pValue);
45 return nullptr;
46 }
47 return pValue;
48}
49
50json_value *JsonParseEx(json_settings *pSettings, const json_char *pJson, size_t Length, char *pError)
51{
52 json_value *pValue = json_parse_ex(settings: pSettings, json: pJson, length: Length, error: pError);
53 if(pValue == nullptr)
54 {
55 return nullptr;
56 }
57 if(!JsonValidateUtf8Recursive(pValue))
58 {
59 if(pError)
60 {
61 str_copy(dst: pError, src: "invalid utf-8 in string literal", json_error_max);
62 }
63 json_value_free(pValue);
64 return nullptr;
65 }
66 return pValue;
67}
68
69const struct _json_value *json_object_get(const json_value *pObject, const char *pIndex)
70{
71 unsigned int i;
72
73 if(pObject->type != json_object)
74 return &json_value_none;
75
76 for(i = 0; i < pObject->u.object.length; ++i)
77 if(!str_comp(a: pObject->u.object.values[i].name, b: pIndex))
78 return pObject->u.object.values[i].value;
79
80 return &json_value_none;
81}
82
83const struct _json_value *json_array_get(const json_value *pArray, int Index)
84{
85 if(pArray->type != json_array || Index >= (int)pArray->u.array.length)
86 return &json_value_none;
87
88 return pArray->u.array.values[Index];
89}
90
91int json_array_length(const json_value *pArray)
92{
93 return pArray->u.array.length;
94}
95
96const char *json_string_get(const json_value *pString)
97{
98 return pString->u.string.ptr;
99}
100
101int json_int_get(const json_value *pInteger)
102{
103 return pInteger->u.integer;
104}
105
106int json_boolean_get(const json_value *pBoolean)
107{
108 return pBoolean->u.boolean != 0;
109}
110
111static char EscapeJsonChar(char c)
112{
113 switch(c)
114 {
115 case '\"': return '\"';
116 case '\\': return '\\';
117 case '\b': return 'b';
118 case '\n': return 'n';
119 case '\r': return 'r';
120 case '\t': return 't';
121 // Don't escape '\f', who uses that. :)
122 default: return 0;
123 }
124}
125
126char *EscapeJson(char *pBuffer, int BufferSize, const char *pString)
127{
128 dbg_assert(BufferSize > 0, "can't null-terminate the string");
129 dbg_assert(str_utf8_check(pString), "invalid UTF-8 in string");
130 // Subtract the space for null termination early.
131 BufferSize--;
132
133 char *pResult = pBuffer;
134 while(BufferSize && *pString)
135 {
136 char c = *pString;
137 pString++;
138 char Escaped = EscapeJsonChar(c);
139 if(Escaped)
140 {
141 if(BufferSize < 2)
142 {
143 break;
144 }
145 *pBuffer++ = '\\';
146 *pBuffer++ = Escaped;
147 BufferSize -= 2;
148 }
149 // Assuming ASCII/UTF-8, "if control character".
150 else if((unsigned char)c < 0x20)
151 {
152 // \uXXXX
153 if(BufferSize < 6)
154 {
155 break;
156 }
157 str_format(buffer: pBuffer, buffer_size: BufferSize, format: "\\u%04x", c);
158 pBuffer += 6;
159 BufferSize -= 6;
160 }
161 else
162 {
163 *pBuffer++ = c;
164 BufferSize--;
165 }
166 }
167 *pBuffer = 0;
168 return pResult;
169}
170
171const char *JsonBool(bool Bool)
172{
173 if(Bool)
174 {
175 return "true";
176 }
177 else
178 {
179 return "false";
180 }
181}
182