sassc 0.0.10 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/ext/libsass/.gitignore +6 -0
- data/ext/libsass/.travis.yml +5 -1
- data/ext/libsass/Makefile +12 -3
- data/ext/libsass/Makefile.am +16 -28
- data/ext/libsass/Readme.md +1 -0
- data/ext/libsass/appveyor.yml +1 -2
- data/ext/libsass/ast.cpp +9 -0
- data/ext/libsass/ast.hpp +152 -55
- data/ext/libsass/ast_factory.hpp +2 -0
- data/ext/libsass/ast_fwd_decl.hpp +1 -0
- data/ext/libsass/backtrace.hpp +2 -2
- data/ext/libsass/bind.cpp +15 -13
- data/ext/libsass/configure.ac +17 -5
- data/ext/libsass/constants.cpp +22 -2
- data/ext/libsass/constants.hpp +21 -2
- data/ext/libsass/context.cpp +79 -57
- data/ext/libsass/context.hpp +23 -9
- data/ext/libsass/contextualize.cpp +2 -28
- data/ext/libsass/contextualize.hpp +6 -10
- data/ext/libsass/contextualize_eval.cpp +93 -0
- data/ext/libsass/contextualize_eval.hpp +44 -0
- data/ext/libsass/contrib/plugin.cpp +57 -0
- data/ext/libsass/cssize.cpp +3 -1
- data/ext/libsass/debugger.hpp +242 -83
- data/ext/libsass/emitter.cpp +1 -1
- data/ext/libsass/emitter.hpp +1 -1
- data/ext/libsass/environment.hpp +109 -25
- data/ext/libsass/error_handling.cpp +3 -3
- data/ext/libsass/error_handling.hpp +0 -1
- data/ext/libsass/eval.cpp +145 -61
- data/ext/libsass/eval.hpp +9 -1
- data/ext/libsass/expand.cpp +134 -60
- data/ext/libsass/expand.hpp +5 -2
- data/ext/libsass/extend.cpp +7 -5
- data/ext/libsass/file.cpp +176 -123
- data/ext/libsass/file.hpp +44 -7
- data/ext/libsass/functions.cpp +36 -17
- data/ext/libsass/functions.hpp +2 -2
- data/ext/libsass/inspect.cpp +23 -14
- data/ext/libsass/inspect.hpp +1 -0
- data/ext/libsass/json.cpp +132 -135
- data/ext/libsass/lexer.cpp +133 -0
- data/ext/libsass/lexer.hpp +239 -0
- data/ext/libsass/listize.cpp +83 -0
- data/ext/libsass/listize.hpp +41 -0
- data/ext/libsass/operation.hpp +2 -0
- data/ext/libsass/output.cpp +5 -6
- data/ext/libsass/parser.cpp +426 -388
- data/ext/libsass/parser.hpp +97 -109
- data/ext/libsass/plugins.cpp +15 -2
- data/ext/libsass/plugins.hpp +6 -4
- data/ext/libsass/position.cpp +52 -17
- data/ext/libsass/position.hpp +19 -17
- data/ext/libsass/prelexer.cpp +202 -235
- data/ext/libsass/prelexer.hpp +73 -333
- data/ext/libsass/sass.cpp +21 -11
- data/ext/libsass/sass.h +6 -6
- data/ext/libsass/sass_context.cpp +167 -81
- data/ext/libsass/sass_context.h +26 -6
- data/ext/libsass/sass_functions.cpp +49 -40
- data/ext/libsass/sass_functions.h +55 -43
- data/ext/libsass/sass_interface.cpp +9 -8
- data/ext/libsass/sass_interface.h +3 -3
- data/ext/libsass/sass_version.h +8 -0
- data/ext/libsass/sass_version.h.in +8 -0
- data/ext/libsass/script/ci-build-libsass +3 -3
- data/ext/libsass/script/ci-report-coverage +2 -1
- data/ext/libsass/source_map.cpp +2 -2
- data/ext/libsass/util.cpp +60 -11
- data/ext/libsass/util.hpp +6 -1
- data/ext/libsass/win/libsass.filters +12 -0
- data/ext/libsass/win/libsass.vcxproj +10 -0
- data/lib/sassc.rb +3 -1
- data/lib/sassc/cache_stores/base.rb +2 -0
- data/lib/sassc/dependency.rb +3 -1
- data/lib/sassc/engine.rb +31 -16
- data/lib/sassc/error.rb +3 -2
- data/lib/sassc/functions_handler.rb +54 -0
- data/lib/sassc/import_handler.rb +41 -0
- data/lib/sassc/importer.rb +4 -31
- data/lib/sassc/native.rb +1 -1
- data/lib/sassc/native/native_context_api.rb +3 -2
- data/lib/sassc/script.rb +0 -51
- data/lib/sassc/version.rb +1 -1
- data/sassc.gemspec +1 -0
- data/test/custom_importer_test.rb +72 -69
- data/test/engine_test.rb +53 -54
- data/test/functions_test.rb +40 -39
- data/test/native_test.rb +145 -149
- data/test/output_style_test.rb +98 -0
- data/test/test_helper.rb +21 -7
- metadata +28 -2
data/ext/libsass/file.hpp
CHANGED
@@ -2,21 +2,58 @@
|
|
2
2
|
#define SASS_FILE_H
|
3
3
|
|
4
4
|
#include <string>
|
5
|
+
#include <vector>
|
5
6
|
|
6
7
|
namespace Sass {
|
7
8
|
using namespace std;
|
8
9
|
class Context;
|
9
10
|
namespace File {
|
11
|
+
|
12
|
+
// return the current directory
|
13
|
+
// always with forward slashes
|
10
14
|
string get_cwd();
|
11
|
-
|
12
|
-
|
13
|
-
|
15
|
+
|
16
|
+
// test if path exists and is a file
|
17
|
+
bool file_exists(const string& file);
|
18
|
+
|
19
|
+
// return if given path is absolute
|
20
|
+
// works with *nix and windows paths
|
14
21
|
bool is_absolute_path(const string& path);
|
22
|
+
|
23
|
+
// return only the directory part of path
|
24
|
+
string dir_name(const string& path);
|
25
|
+
|
26
|
+
// return only the filename part of path
|
27
|
+
string base_name(const string&);
|
28
|
+
|
29
|
+
// do a locigal clean up of the path
|
30
|
+
// no physical check on the filesystem
|
15
31
|
string make_canonical_path (string path);
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
32
|
+
|
33
|
+
// join two path segments cleanly together
|
34
|
+
// but only if right side is not absolute yet
|
35
|
+
string join_paths(string root, string name);
|
36
|
+
|
37
|
+
// create an absolute path by resolving relative paths with cwd
|
38
|
+
string make_absolute_path(const string& path, const string& cwd = ".");
|
39
|
+
|
40
|
+
// create a path that is relative to the given base directory
|
41
|
+
// path and base will first be resolved against cwd to make them absolute
|
42
|
+
string resolve_relative_path(const string& path, const string& base, const string& cwd = ".");
|
43
|
+
|
44
|
+
// try to find/resolve the filename
|
45
|
+
string resolve_file(const string& file);
|
46
|
+
|
47
|
+
// helper function to resolve a filename
|
48
|
+
string find_file(const string& file, const vector<string> paths);
|
49
|
+
// inc paths can be directly passed from C code
|
50
|
+
string find_file(const string& file, const char** paths);
|
51
|
+
|
52
|
+
// try to load the given filename
|
53
|
+
// returned memory must be freed
|
54
|
+
// will auto convert .sass files
|
55
|
+
char* read_file(const string& file);
|
56
|
+
|
20
57
|
}
|
21
58
|
}
|
22
59
|
|
data/ext/libsass/functions.cpp
CHANGED
@@ -35,7 +35,7 @@ namespace Sass {
|
|
35
35
|
using std::stringstream;
|
36
36
|
using std::endl;
|
37
37
|
|
38
|
-
Definition* make_native_function(Signature sig, Native_Function
|
38
|
+
Definition* make_native_function(Signature sig, Native_Function func, Context& ctx)
|
39
39
|
{
|
40
40
|
Parser sig_parser = Parser::from_c_str(sig, ctx, ParserState("[built-in function]"));
|
41
41
|
sig_parser.lex<Prelexer::identifier>();
|
@@ -45,12 +45,14 @@ namespace Sass {
|
|
45
45
|
sig,
|
46
46
|
name,
|
47
47
|
params,
|
48
|
-
|
48
|
+
func,
|
49
|
+
&ctx,
|
49
50
|
false);
|
50
51
|
}
|
51
52
|
|
52
|
-
Definition* make_c_function(
|
53
|
+
Definition* make_c_function(Sass_Function_Entry c_func, Context& ctx)
|
53
54
|
{
|
55
|
+
const char* sig = sass_function_get_signature(c_func);
|
54
56
|
Parser sig_parser = Parser::from_c_str(sig, ctx, ParserState("[c function]"));
|
55
57
|
// allow to overload generic callback plus @warn, @error and @debug with custom functions
|
56
58
|
sig_parser.lex < alternatives < identifier, exactly <'*'>,
|
@@ -64,8 +66,8 @@ namespace Sass {
|
|
64
66
|
sig,
|
65
67
|
name,
|
66
68
|
params,
|
67
|
-
|
68
|
-
|
69
|
+
c_func,
|
70
|
+
&ctx,
|
69
71
|
false, true);
|
70
72
|
}
|
71
73
|
|
@@ -145,7 +147,11 @@ namespace Sass {
|
|
145
147
|
static mt19937 rand(static_cast<unsigned int>(GetSeed()));
|
146
148
|
|
147
149
|
// features
|
148
|
-
static set<string> features
|
150
|
+
static set<string> features {
|
151
|
+
"global-variable-shadowing",
|
152
|
+
"at-error",
|
153
|
+
"units-level-3"
|
154
|
+
};
|
149
155
|
|
150
156
|
////////////////
|
151
157
|
// RGB FUNCTIONS
|
@@ -940,7 +946,7 @@ namespace Sass {
|
|
940
946
|
error(msg, pstate, backtrace);
|
941
947
|
}
|
942
948
|
catch (...) { throw; }
|
943
|
-
return new (ctx.mem)
|
949
|
+
return new (ctx.mem) String_Quoted(pstate, newstr);
|
944
950
|
}
|
945
951
|
|
946
952
|
Signature to_upper_case_sig = "to-upper-case($string)";
|
@@ -1065,16 +1071,16 @@ namespace Sass {
|
|
1065
1071
|
BUILT_IN(random)
|
1066
1072
|
{
|
1067
1073
|
Number* l = dynamic_cast<Number*>(env["$limit"]);
|
1068
|
-
if (l && trunc(l->value()) != l->value()) error("argument $limit of `" + string(sig) + "` must be an integer", pstate);
|
1069
1074
|
if (l) {
|
1075
|
+
if (trunc(l->value()) != l->value() || l->value() == 0) error("argument $limit of `" + string(sig) + "` must be a positive integer", pstate);
|
1070
1076
|
uniform_real_distribution<> distributor(1, l->value() + 1);
|
1071
1077
|
uint_fast32_t distributed = static_cast<uint_fast32_t>(distributor(rand));
|
1072
1078
|
return new (ctx.mem) Number(pstate, (double)distributed);
|
1073
1079
|
}
|
1074
1080
|
else {
|
1075
1081
|
uniform_real_distribution<> distributor(0, 1);
|
1076
|
-
|
1077
|
-
return new (ctx.mem) Number(pstate,
|
1082
|
+
double distributed = static_cast<double>(distributor(rand));
|
1083
|
+
return new (ctx.mem) Number(pstate, distributed);
|
1078
1084
|
}
|
1079
1085
|
}
|
1080
1086
|
|
@@ -1408,7 +1414,7 @@ namespace Sass {
|
|
1408
1414
|
{
|
1409
1415
|
string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));
|
1410
1416
|
|
1411
|
-
if(d_env.
|
1417
|
+
if(d_env.has_global("$"+s)) {
|
1412
1418
|
return new (ctx.mem) Boolean(pstate, true);
|
1413
1419
|
}
|
1414
1420
|
else {
|
@@ -1421,7 +1427,7 @@ namespace Sass {
|
|
1421
1427
|
{
|
1422
1428
|
string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));
|
1423
1429
|
|
1424
|
-
if(d_env.
|
1430
|
+
if(d_env.has_global(s+"[f]")) {
|
1425
1431
|
return new (ctx.mem) Boolean(pstate, true);
|
1426
1432
|
}
|
1427
1433
|
else {
|
@@ -1434,7 +1440,7 @@ namespace Sass {
|
|
1434
1440
|
{
|
1435
1441
|
string s = Util::normalize_underscores(unquote(ARG("$name", String_Constant)->value()));
|
1436
1442
|
|
1437
|
-
if(d_env.
|
1443
|
+
if(d_env.has_global(s+"[m]")) {
|
1438
1444
|
return new (ctx.mem) Boolean(pstate, true);
|
1439
1445
|
}
|
1440
1446
|
else {
|
@@ -1463,11 +1469,22 @@ namespace Sass {
|
|
1463
1469
|
|
1464
1470
|
Arguments* args = new (ctx.mem) Arguments(pstate);
|
1465
1471
|
for (size_t i = 0, L = arglist->length(); i < L; ++i) {
|
1466
|
-
|
1467
|
-
|
1472
|
+
Expression* expr = arglist->value_at_index(i);
|
1473
|
+
if (arglist->is_arglist()) {
|
1474
|
+
Argument* arg = static_cast<Argument*>(expr);
|
1475
|
+
*args << new (ctx.mem) Argument(pstate,
|
1476
|
+
expr,
|
1477
|
+
"",
|
1478
|
+
arg->is_rest_argument(),
|
1479
|
+
arg->is_keyword_argument());
|
1480
|
+
} else {
|
1481
|
+
*args << new (ctx.mem) Argument(pstate, expr);
|
1482
|
+
}
|
1468
1483
|
}
|
1469
1484
|
Function_Call* func = new (ctx.mem) Function_Call(pstate, name, args);
|
1470
|
-
|
1485
|
+
Contextualize contextualize(ctx, &d_env, backtrace);
|
1486
|
+
Listize listize(ctx);
|
1487
|
+
Eval eval(ctx, &contextualize, &listize, &d_env, backtrace);
|
1471
1488
|
return func->perform(&eval);
|
1472
1489
|
|
1473
1490
|
}
|
@@ -1485,7 +1502,9 @@ namespace Sass {
|
|
1485
1502
|
// { return ARG("$condition", Expression)->is_false() ? ARG("$if-false", Expression) : ARG("$if-true", Expression); }
|
1486
1503
|
BUILT_IN(sass_if)
|
1487
1504
|
{
|
1488
|
-
|
1505
|
+
Contextualize contextualize(ctx, &d_env, backtrace);
|
1506
|
+
Listize listize(ctx);
|
1507
|
+
Eval eval(ctx, &contextualize, &listize, &d_env, backtrace);
|
1489
1508
|
bool is_true = !ARG("$condition", Expression)->perform(&eval)->is_false();
|
1490
1509
|
if (is_true) {
|
1491
1510
|
return ARG("$if-true", Expression)->perform(&eval);
|
data/ext/libsass/functions.hpp
CHANGED
@@ -20,8 +20,8 @@ namespace Sass {
|
|
20
20
|
typedef const char* Signature;
|
21
21
|
typedef Expression* (*Native_Function)(Env&, Env&, Context&, Signature, ParserState, Backtrace*);
|
22
22
|
|
23
|
-
Definition* make_native_function(Signature, Native_Function, Context&);
|
24
|
-
Definition* make_c_function(
|
23
|
+
Definition* make_native_function(Signature, Native_Function, Context& ctx);
|
24
|
+
Definition* make_c_function(Sass_Function_Entry c_func, Context& ctx);
|
25
25
|
|
26
26
|
namespace Functions {
|
27
27
|
|
data/ext/libsass/inspect.cpp
CHANGED
@@ -44,9 +44,8 @@ namespace Sass {
|
|
44
44
|
|
45
45
|
void Inspect::operator()(Keyframe_Rule* rule)
|
46
46
|
{
|
47
|
-
|
48
|
-
if (rule->
|
49
|
-
rule->block()->perform(this);
|
47
|
+
if (rule->selector()) rule->selector()->perform(this);
|
48
|
+
if (rule->block()) rule->block()->perform(this);
|
50
49
|
}
|
51
50
|
|
52
51
|
void Inspect::operator()(Propset* propset)
|
@@ -59,14 +58,10 @@ namespace Sass {
|
|
59
58
|
void Inspect::operator()(Bubble* bubble)
|
60
59
|
{
|
61
60
|
append_indentation();
|
62
|
-
append_token("
|
63
|
-
|
64
|
-
append_string("(");
|
65
|
-
append_optional_space();
|
61
|
+
append_token("::BUBBLE", bubble);
|
62
|
+
append_scope_opener();
|
66
63
|
bubble->node()->perform(this);
|
67
|
-
|
68
|
-
append_string(")");
|
69
|
-
append_optional_space();
|
64
|
+
append_scope_closer();
|
70
65
|
}
|
71
66
|
|
72
67
|
void Inspect::operator()(Media_Block* media_block)
|
@@ -104,9 +99,9 @@ namespace Sass {
|
|
104
99
|
append_token(at_rule->keyword(), at_rule);
|
105
100
|
if (at_rule->selector()) {
|
106
101
|
append_mandatory_space();
|
107
|
-
|
102
|
+
in_wrapped = true;
|
108
103
|
at_rule->selector()->perform(this);
|
109
|
-
|
104
|
+
in_wrapped = false;
|
110
105
|
}
|
111
106
|
if (at_rule->block()) {
|
112
107
|
at_rule->block()->perform(this);
|
@@ -141,7 +136,7 @@ namespace Sass {
|
|
141
136
|
append_token(assn->variable(), assn);
|
142
137
|
append_colon_separator();
|
143
138
|
assn->value()->perform(this);
|
144
|
-
if (assn->
|
139
|
+
if (assn->is_default()) {
|
145
140
|
append_optional_space();
|
146
141
|
append_string("!default");
|
147
142
|
}
|
@@ -665,6 +660,17 @@ namespace Sass {
|
|
665
660
|
append_token("null", n);
|
666
661
|
}
|
667
662
|
|
663
|
+
void Inspect::operator()(Parent_Selector* p)
|
664
|
+
{
|
665
|
+
if (p->selector()) {
|
666
|
+
p->selector()->perform(this);
|
667
|
+
append_delimiter();
|
668
|
+
}
|
669
|
+
else {
|
670
|
+
append_string("&");
|
671
|
+
}
|
672
|
+
}
|
673
|
+
|
668
674
|
// parameters and arguments
|
669
675
|
void Inspect::operator()(Parameter* p)
|
670
676
|
{
|
@@ -781,9 +787,12 @@ namespace Sass {
|
|
781
787
|
|
782
788
|
void Inspect::operator()(Wrapped_Selector* s)
|
783
789
|
{
|
790
|
+
bool was = in_wrapped;
|
791
|
+
in_wrapped = true;
|
784
792
|
append_token(s->name(), s);
|
785
793
|
s->selector()->perform(this);
|
786
794
|
append_string(")");
|
795
|
+
in_wrapped = was;
|
787
796
|
}
|
788
797
|
|
789
798
|
void Inspect::operator()(Compound_Selector* s)
|
@@ -838,7 +847,7 @@ namespace Sass {
|
|
838
847
|
{
|
839
848
|
if (g->empty()) return;
|
840
849
|
for (size_t i = 0, L = g->length(); i < L; ++i) {
|
841
|
-
if (!
|
850
|
+
if (!in_wrapped && i == 0) append_indentation();
|
842
851
|
(*g)[i]->perform(this);
|
843
852
|
if (i < L - 1) {
|
844
853
|
append_comma_separator();
|
data/ext/libsass/inspect.hpp
CHANGED
@@ -71,6 +71,7 @@ namespace Sass {
|
|
71
71
|
virtual void operator()(Media_Query_Expression*);
|
72
72
|
virtual void operator()(At_Root_Expression*);
|
73
73
|
virtual void operator()(Null*);
|
74
|
+
virtual void operator()(Parent_Selector* p);
|
74
75
|
// parameters and arguments
|
75
76
|
virtual void operator()(Parameter*);
|
76
77
|
virtual void operator()(Parameters*);
|
data/ext/libsass/json.cpp
CHANGED
@@ -102,11 +102,11 @@ static void sb_grow(SB *sb, int need)
|
|
102
102
|
{
|
103
103
|
size_t length = sb->cur - sb->start;
|
104
104
|
size_t alloc = sb->end - sb->start;
|
105
|
-
|
105
|
+
|
106
106
|
do {
|
107
107
|
alloc *= 2;
|
108
108
|
} while (alloc < length + need);
|
109
|
-
|
109
|
+
|
110
110
|
sb->start = (char*) realloc(sb->start, alloc + 1);
|
111
111
|
if (sb->start == NULL)
|
112
112
|
out_of_memory();
|
@@ -150,13 +150,10 @@ static void sb_free(SB *sb)
|
|
150
150
|
* These are taken from the ccan/charset module and customized a bit.
|
151
151
|
* Putting them here means the compiler can (choose to) inline them,
|
152
152
|
* and it keeps ccan/json from having a dependency.
|
153
|
-
|
154
|
-
|
155
|
-
/*
|
156
|
-
* Type for Unicode codepoints.
|
153
|
+
*
|
154
|
+
* We use uint32_t Type for Unicode codepoints.
|
157
155
|
* We need our own because wchar_t might be 16 bits.
|
158
156
|
*/
|
159
|
-
typedef uint32_t uchar_t;
|
160
157
|
|
161
158
|
/*
|
162
159
|
* Validate a single UTF-8 character starting at @s.
|
@@ -181,7 +178,7 @@ typedef uint32_t uchar_t;
|
|
181
178
|
static int utf8_validate_cz(const char *s)
|
182
179
|
{
|
183
180
|
unsigned char c = *s++;
|
184
|
-
|
181
|
+
|
185
182
|
if (c <= 0x7F) { /* 00..7F */
|
186
183
|
return 1;
|
187
184
|
} else if (c <= 0xC1) { /* 80..C1 */
|
@@ -191,33 +188,33 @@ static int utf8_validate_cz(const char *s)
|
|
191
188
|
/* Make sure subsequent byte is in the range 0x80..0xBF. */
|
192
189
|
if (((unsigned char)*s++ & 0xC0) != 0x80)
|
193
190
|
return 0;
|
194
|
-
|
191
|
+
|
195
192
|
return 2;
|
196
193
|
} else if (c <= 0xEF) { /* E0..EF */
|
197
194
|
/* Disallow overlong 3-byte sequence. */
|
198
195
|
if (c == 0xE0 && (unsigned char)*s < 0xA0)
|
199
196
|
return 0;
|
200
|
-
|
197
|
+
|
201
198
|
/* Disallow U+D800..U+DFFF. */
|
202
199
|
if (c == 0xED && (unsigned char)*s > 0x9F)
|
203
200
|
return 0;
|
204
|
-
|
201
|
+
|
205
202
|
/* Make sure subsequent bytes are in the range 0x80..0xBF. */
|
206
203
|
if (((unsigned char)*s++ & 0xC0) != 0x80)
|
207
204
|
return 0;
|
208
205
|
if (((unsigned char)*s++ & 0xC0) != 0x80)
|
209
206
|
return 0;
|
210
|
-
|
207
|
+
|
211
208
|
return 3;
|
212
209
|
} else if (c <= 0xF4) { /* F0..F4 */
|
213
210
|
/* Disallow overlong 4-byte sequence. */
|
214
211
|
if (c == 0xF0 && (unsigned char)*s < 0x90)
|
215
212
|
return 0;
|
216
|
-
|
213
|
+
|
217
214
|
/* Disallow codepoints beyond U+10FFFF. */
|
218
215
|
if (c == 0xF4 && (unsigned char)*s > 0x8F)
|
219
216
|
return 0;
|
220
|
-
|
217
|
+
|
221
218
|
/* Make sure subsequent bytes are in the range 0x80..0xBF. */
|
222
219
|
if (((unsigned char)*s++ & 0xC0) != 0x80)
|
223
220
|
return 0;
|
@@ -225,7 +222,7 @@ static int utf8_validate_cz(const char *s)
|
|
225
222
|
return 0;
|
226
223
|
if (((unsigned char)*s++ & 0xC0) != 0x80)
|
227
224
|
return 0;
|
228
|
-
|
225
|
+
|
229
226
|
return 4;
|
230
227
|
} else { /* F5..FF */
|
231
228
|
return 0;
|
@@ -236,13 +233,13 @@ static int utf8_validate_cz(const char *s)
|
|
236
233
|
static bool utf8_validate(const char *s)
|
237
234
|
{
|
238
235
|
int len;
|
239
|
-
|
236
|
+
|
240
237
|
for (; *s != 0; s += len) {
|
241
238
|
len = utf8_validate_cz(s);
|
242
239
|
if (len == 0)
|
243
240
|
return false;
|
244
241
|
}
|
245
|
-
|
242
|
+
|
246
243
|
return true;
|
247
244
|
}
|
248
245
|
|
@@ -253,10 +250,10 @@ static bool utf8_validate(const char *s)
|
|
253
250
|
* This function assumes input is valid UTF-8,
|
254
251
|
* and that there are enough characters in front of @s.
|
255
252
|
*/
|
256
|
-
static int utf8_read_char(const char *s,
|
253
|
+
static int utf8_read_char(const char *s, uint32_t *out)
|
257
254
|
{
|
258
255
|
const unsigned char *c = (const unsigned char*) s;
|
259
|
-
|
256
|
+
|
260
257
|
assert(utf8_validate_cz(s));
|
261
258
|
|
262
259
|
if (c[0] <= 0x7F) {
|
@@ -265,21 +262,21 @@ static int utf8_read_char(const char *s, uchar_t *out)
|
|
265
262
|
return 1;
|
266
263
|
} else if (c[0] <= 0xDF) {
|
267
264
|
/* C2..DF (unless input is invalid) */
|
268
|
-
*out = ((
|
269
|
-
((
|
265
|
+
*out = ((uint32_t)c[0] & 0x1F) << 6 |
|
266
|
+
((uint32_t)c[1] & 0x3F);
|
270
267
|
return 2;
|
271
268
|
} else if (c[0] <= 0xEF) {
|
272
269
|
/* E0..EF */
|
273
|
-
*out = ((
|
274
|
-
((
|
275
|
-
((
|
270
|
+
*out = ((uint32_t)c[0] & 0xF) << 12 |
|
271
|
+
((uint32_t)c[1] & 0x3F) << 6 |
|
272
|
+
((uint32_t)c[2] & 0x3F);
|
276
273
|
return 3;
|
277
274
|
} else {
|
278
275
|
/* F0..F4 (unless input is invalid) */
|
279
|
-
*out = ((
|
280
|
-
((
|
281
|
-
((
|
282
|
-
((
|
276
|
+
*out = ((uint32_t)c[0] & 0x7) << 18 |
|
277
|
+
((uint32_t)c[1] & 0x3F) << 12 |
|
278
|
+
((uint32_t)c[2] & 0x3F) << 6 |
|
279
|
+
((uint32_t)c[3] & 0x3F);
|
283
280
|
return 4;
|
284
281
|
}
|
285
282
|
}
|
@@ -292,10 +289,10 @@ static int utf8_read_char(const char *s, uchar_t *out)
|
|
292
289
|
*
|
293
290
|
* This function will write up to 4 bytes to @out.
|
294
291
|
*/
|
295
|
-
static int utf8_write_char(
|
292
|
+
static int utf8_write_char(uint32_t unicode, char *out)
|
296
293
|
{
|
297
294
|
unsigned char *o = (unsigned char*) out;
|
298
|
-
|
295
|
+
|
299
296
|
assert(unicode <= 0x10FFFF && !(unicode >= 0xD800 && unicode <= 0xDFFF));
|
300
297
|
|
301
298
|
if (unicode <= 0x7F) {
|
@@ -329,10 +326,10 @@ static int utf8_write_char(uchar_t unicode, char *out)
|
|
329
326
|
* @uc should be 0xD800..0xDBFF, and @lc should be 0xDC00..0xDFFF.
|
330
327
|
* If they aren't, this function returns false.
|
331
328
|
*/
|
332
|
-
static bool from_surrogate_pair(uint16_t uc, uint16_t lc,
|
329
|
+
static bool from_surrogate_pair(uint16_t uc, uint16_t lc, uint32_t *unicode)
|
333
330
|
{
|
334
331
|
if (uc >= 0xD800 && uc <= 0xDBFF && lc >= 0xDC00 && lc <= 0xDFFF) {
|
335
|
-
*unicode = 0x10000 + ((((
|
332
|
+
*unicode = 0x10000 + ((((uint32_t)uc & 0x3FF) << 10) | (lc & 0x3FF));
|
336
333
|
return true;
|
337
334
|
} else {
|
338
335
|
return false;
|
@@ -344,12 +341,12 @@ static bool from_surrogate_pair(uint16_t uc, uint16_t lc, uchar_t *unicode)
|
|
344
341
|
*
|
345
342
|
* @unicode must be U+10000..U+10FFFF.
|
346
343
|
*/
|
347
|
-
static void to_surrogate_pair(
|
344
|
+
static void to_surrogate_pair(uint32_t unicode, uint16_t *uc, uint16_t *lc)
|
348
345
|
{
|
349
|
-
|
350
|
-
|
346
|
+
uint32_t n;
|
347
|
+
|
351
348
|
assert(unicode >= 0x10000 && unicode <= 0x10FFFF);
|
352
|
-
|
349
|
+
|
353
350
|
n = unicode - 0x10000;
|
354
351
|
*uc = ((n >> 10) & 0x3FF) | 0xD800;
|
355
352
|
*lc = (n & 0x3FF) | 0xDC00;
|
@@ -392,17 +389,17 @@ JsonNode *json_decode(const char *json)
|
|
392
389
|
{
|
393
390
|
const char *s = json;
|
394
391
|
JsonNode *ret;
|
395
|
-
|
392
|
+
|
396
393
|
skip_space(&s);
|
397
394
|
if (!parse_value(&s, &ret))
|
398
395
|
return NULL;
|
399
|
-
|
396
|
+
|
400
397
|
skip_space(&s);
|
401
398
|
if (*s != 0) {
|
402
399
|
json_delete(ret);
|
403
400
|
return NULL;
|
404
401
|
}
|
405
|
-
|
402
|
+
|
406
403
|
return ret;
|
407
404
|
}
|
408
405
|
|
@@ -415,9 +412,9 @@ char *json_encode_string(const char *str)
|
|
415
412
|
{
|
416
413
|
SB sb;
|
417
414
|
sb_init(&sb);
|
418
|
-
|
415
|
+
|
419
416
|
emit_string(&sb, str);
|
420
|
-
|
417
|
+
|
421
418
|
return sb_finish(&sb);
|
422
419
|
}
|
423
420
|
|
@@ -425,12 +422,12 @@ char *json_stringify(const JsonNode *node, const char *space)
|
|
425
422
|
{
|
426
423
|
SB sb;
|
427
424
|
sb_init(&sb);
|
428
|
-
|
425
|
+
|
429
426
|
if (space != NULL)
|
430
427
|
emit_value_indented(&sb, node, space, 0);
|
431
428
|
else
|
432
429
|
emit_value(&sb, node);
|
433
|
-
|
430
|
+
|
434
431
|
return sb_finish(&sb);
|
435
432
|
}
|
436
433
|
|
@@ -438,7 +435,7 @@ void json_delete(JsonNode *node)
|
|
438
435
|
{
|
439
436
|
if (node != NULL) {
|
440
437
|
json_remove_from_parent(node);
|
441
|
-
|
438
|
+
|
442
439
|
switch (node->tag) {
|
443
440
|
case JSON_STRING:
|
444
441
|
free(node->string_);
|
@@ -455,7 +452,7 @@ void json_delete(JsonNode *node)
|
|
455
452
|
}
|
456
453
|
default:;
|
457
454
|
}
|
458
|
-
|
455
|
+
|
459
456
|
free(node);
|
460
457
|
}
|
461
458
|
}
|
@@ -463,15 +460,15 @@ void json_delete(JsonNode *node)
|
|
463
460
|
bool json_validate(const char *json)
|
464
461
|
{
|
465
462
|
const char *s = json;
|
466
|
-
|
463
|
+
|
467
464
|
skip_space(&s);
|
468
465
|
if (!parse_value(&s, NULL))
|
469
466
|
return false;
|
470
|
-
|
467
|
+
|
471
468
|
skip_space(&s);
|
472
469
|
if (*s != 0)
|
473
470
|
return false;
|
474
|
-
|
471
|
+
|
475
472
|
return true;
|
476
473
|
}
|
477
474
|
|
@@ -479,30 +476,30 @@ JsonNode *json_find_element(JsonNode *array, int index)
|
|
479
476
|
{
|
480
477
|
JsonNode *element;
|
481
478
|
int i = 0;
|
482
|
-
|
479
|
+
|
483
480
|
if (array == NULL || array->tag != JSON_ARRAY)
|
484
481
|
return NULL;
|
485
|
-
|
482
|
+
|
486
483
|
json_foreach(element, array) {
|
487
484
|
if (i == index)
|
488
485
|
return element;
|
489
486
|
i++;
|
490
487
|
}
|
491
|
-
|
488
|
+
|
492
489
|
return NULL;
|
493
490
|
}
|
494
491
|
|
495
492
|
JsonNode *json_find_member(JsonNode *object, const char *name)
|
496
493
|
{
|
497
494
|
JsonNode *member;
|
498
|
-
|
495
|
+
|
499
496
|
if (object == NULL || object->tag != JSON_OBJECT)
|
500
497
|
return NULL;
|
501
|
-
|
498
|
+
|
502
499
|
json_foreach(member, object)
|
503
500
|
if (strcmp(member->key, name) == 0)
|
504
501
|
return member;
|
505
|
-
|
502
|
+
|
506
503
|
return NULL;
|
507
504
|
}
|
508
505
|
|
@@ -568,7 +565,7 @@ static void append_node(JsonNode *parent, JsonNode *child)
|
|
568
565
|
child->parent = parent;
|
569
566
|
child->prev = parent->children.tail;
|
570
567
|
child->next = NULL;
|
571
|
-
|
568
|
+
|
572
569
|
if (parent->children.tail != NULL)
|
573
570
|
parent->children.tail->next = child;
|
574
571
|
else
|
@@ -581,7 +578,7 @@ static void prepend_node(JsonNode *parent, JsonNode *child)
|
|
581
578
|
child->parent = parent;
|
582
579
|
child->prev = NULL;
|
583
580
|
child->next = parent->children.head;
|
584
|
-
|
581
|
+
|
585
582
|
if (parent->children.head != NULL)
|
586
583
|
parent->children.head->prev = child;
|
587
584
|
else
|
@@ -599,7 +596,7 @@ void json_append_element(JsonNode *array, JsonNode *element)
|
|
599
596
|
{
|
600
597
|
assert(array->tag == JSON_ARRAY);
|
601
598
|
assert(element->parent == NULL);
|
602
|
-
|
599
|
+
|
603
600
|
append_node(array, element);
|
604
601
|
}
|
605
602
|
|
@@ -607,7 +604,7 @@ void json_prepend_element(JsonNode *array, JsonNode *element)
|
|
607
604
|
{
|
608
605
|
assert(array->tag == JSON_ARRAY);
|
609
606
|
assert(element->parent == NULL);
|
610
|
-
|
607
|
+
|
611
608
|
prepend_node(array, element);
|
612
609
|
}
|
613
610
|
|
@@ -615,7 +612,7 @@ void json_append_member(JsonNode *object, const char *key, JsonNode *value)
|
|
615
612
|
{
|
616
613
|
assert(object->tag == JSON_OBJECT);
|
617
614
|
assert(value->parent == NULL);
|
618
|
-
|
615
|
+
|
619
616
|
append_member(object, json_strdup(key), value);
|
620
617
|
}
|
621
618
|
|
@@ -623,7 +620,7 @@ void json_prepend_member(JsonNode *object, const char *key, JsonNode *value)
|
|
623
620
|
{
|
624
621
|
assert(object->tag == JSON_OBJECT);
|
625
622
|
assert(value->parent == NULL);
|
626
|
-
|
623
|
+
|
627
624
|
value->key = json_strdup(key);
|
628
625
|
prepend_node(object, value);
|
629
626
|
}
|
@@ -631,7 +628,7 @@ void json_prepend_member(JsonNode *object, const char *key, JsonNode *value)
|
|
631
628
|
void json_remove_from_parent(JsonNode *node)
|
632
629
|
{
|
633
630
|
JsonNode *parent = node->parent;
|
634
|
-
|
631
|
+
|
635
632
|
if (parent != NULL) {
|
636
633
|
if (node->prev != NULL)
|
637
634
|
node->prev->next = node->next;
|
@@ -641,9 +638,9 @@ void json_remove_from_parent(JsonNode *node)
|
|
641
638
|
node->next->prev = node->prev;
|
642
639
|
else
|
643
640
|
parent->children.tail = node->prev;
|
644
|
-
|
641
|
+
|
645
642
|
free(node->key);
|
646
|
-
|
643
|
+
|
647
644
|
node->parent = NULL;
|
648
645
|
node->prev = node->next = NULL;
|
649
646
|
node->key = NULL;
|
@@ -653,7 +650,7 @@ void json_remove_from_parent(JsonNode *node)
|
|
653
650
|
static bool parse_value(const char **sp, JsonNode **out)
|
654
651
|
{
|
655
652
|
const char *s = *sp;
|
656
|
-
|
653
|
+
|
657
654
|
switch (*s) {
|
658
655
|
case 'n':
|
659
656
|
if (expect_literal(&s, "null")) {
|
@@ -663,7 +660,7 @@ static bool parse_value(const char **sp, JsonNode **out)
|
|
663
660
|
return true;
|
664
661
|
}
|
665
662
|
return false;
|
666
|
-
|
663
|
+
|
667
664
|
case 'f':
|
668
665
|
if (expect_literal(&s, "false")) {
|
669
666
|
if (out)
|
@@ -672,7 +669,7 @@ static bool parse_value(const char **sp, JsonNode **out)
|
|
672
669
|
return true;
|
673
670
|
}
|
674
671
|
return false;
|
675
|
-
|
672
|
+
|
676
673
|
case 't':
|
677
674
|
if (expect_literal(&s, "true")) {
|
678
675
|
if (out)
|
@@ -681,7 +678,7 @@ static bool parse_value(const char **sp, JsonNode **out)
|
|
681
678
|
return true;
|
682
679
|
}
|
683
680
|
return false;
|
684
|
-
|
681
|
+
|
685
682
|
case '"': {
|
686
683
|
char *str;
|
687
684
|
if (parse_string(&s, out ? &str : NULL)) {
|
@@ -692,21 +689,21 @@ static bool parse_value(const char **sp, JsonNode **out)
|
|
692
689
|
}
|
693
690
|
return false;
|
694
691
|
}
|
695
|
-
|
692
|
+
|
696
693
|
case '[':
|
697
694
|
if (parse_array(&s, out)) {
|
698
695
|
*sp = s;
|
699
696
|
return true;
|
700
697
|
}
|
701
698
|
return false;
|
702
|
-
|
699
|
+
|
703
700
|
case '{':
|
704
701
|
if (parse_object(&s, out)) {
|
705
702
|
*sp = s;
|
706
703
|
return true;
|
707
704
|
}
|
708
705
|
return false;
|
709
|
-
|
706
|
+
|
710
707
|
default: {
|
711
708
|
double num;
|
712
709
|
if (parse_number(&s, out ? &num : NULL)) {
|
@@ -725,34 +722,34 @@ static bool parse_array(const char **sp, JsonNode **out)
|
|
725
722
|
const char *s = *sp;
|
726
723
|
JsonNode *ret = out ? json_mkarray() : NULL;
|
727
724
|
JsonNode *element;
|
728
|
-
|
725
|
+
|
729
726
|
if (*s++ != '[')
|
730
727
|
goto failure;
|
731
728
|
skip_space(&s);
|
732
|
-
|
729
|
+
|
733
730
|
if (*s == ']') {
|
734
731
|
s++;
|
735
732
|
goto success;
|
736
733
|
}
|
737
|
-
|
734
|
+
|
738
735
|
for (;;) {
|
739
736
|
if (!parse_value(&s, out ? &element : NULL))
|
740
737
|
goto failure;
|
741
738
|
skip_space(&s);
|
742
|
-
|
739
|
+
|
743
740
|
if (out)
|
744
741
|
json_append_element(ret, element);
|
745
|
-
|
742
|
+
|
746
743
|
if (*s == ']') {
|
747
744
|
s++;
|
748
745
|
goto success;
|
749
746
|
}
|
750
|
-
|
747
|
+
|
751
748
|
if (*s++ != ',')
|
752
749
|
goto failure;
|
753
750
|
skip_space(&s);
|
754
751
|
}
|
755
|
-
|
752
|
+
|
756
753
|
success:
|
757
754
|
*sp = s;
|
758
755
|
if (out)
|
@@ -770,42 +767,42 @@ static bool parse_object(const char **sp, JsonNode **out)
|
|
770
767
|
JsonNode *ret = out ? json_mkobject() : NULL;
|
771
768
|
char *key;
|
772
769
|
JsonNode *value;
|
773
|
-
|
770
|
+
|
774
771
|
if (*s++ != '{')
|
775
772
|
goto failure;
|
776
773
|
skip_space(&s);
|
777
|
-
|
774
|
+
|
778
775
|
if (*s == '}') {
|
779
776
|
s++;
|
780
777
|
goto success;
|
781
778
|
}
|
782
|
-
|
779
|
+
|
783
780
|
for (;;) {
|
784
781
|
if (!parse_string(&s, out ? &key : NULL))
|
785
782
|
goto failure;
|
786
783
|
skip_space(&s);
|
787
|
-
|
784
|
+
|
788
785
|
if (*s++ != ':')
|
789
786
|
goto failure_free_key;
|
790
787
|
skip_space(&s);
|
791
|
-
|
788
|
+
|
792
789
|
if (!parse_value(&s, out ? &value : NULL))
|
793
790
|
goto failure_free_key;
|
794
791
|
skip_space(&s);
|
795
|
-
|
792
|
+
|
796
793
|
if (out)
|
797
794
|
append_member(ret, key, value);
|
798
|
-
|
795
|
+
|
799
796
|
if (*s == '}') {
|
800
797
|
s++;
|
801
798
|
goto success;
|
802
799
|
}
|
803
|
-
|
800
|
+
|
804
801
|
if (*s++ != ',')
|
805
802
|
goto failure;
|
806
803
|
skip_space(&s);
|
807
804
|
}
|
808
|
-
|
805
|
+
|
809
806
|
success:
|
810
807
|
*sp = s;
|
811
808
|
if (out)
|
@@ -827,10 +824,10 @@ bool parse_string(const char **sp, char **out)
|
|
827
824
|
char throwaway_buffer[4];
|
828
825
|
/* enough space for a UTF-8 character */
|
829
826
|
char *b;
|
830
|
-
|
827
|
+
|
831
828
|
if (*s++ != '"')
|
832
829
|
return false;
|
833
|
-
|
830
|
+
|
834
831
|
if (out) {
|
835
832
|
sb_init(&sb);
|
836
833
|
sb_need(&sb, 4);
|
@@ -838,10 +835,10 @@ bool parse_string(const char **sp, char **out)
|
|
838
835
|
} else {
|
839
836
|
b = throwaway_buffer;
|
840
837
|
}
|
841
|
-
|
838
|
+
|
842
839
|
while (*s != '"') {
|
843
840
|
unsigned char c = *s++;
|
844
|
-
|
841
|
+
|
845
842
|
/* Parse next character, and write it to b. */
|
846
843
|
if (c == '\\') {
|
847
844
|
c = *s++;
|
@@ -869,11 +866,11 @@ bool parse_string(const char **sp, char **out)
|
|
869
866
|
case 'u':
|
870
867
|
{
|
871
868
|
uint16_t uc, lc;
|
872
|
-
|
873
|
-
|
869
|
+
uint32_t unicode;
|
870
|
+
|
874
871
|
if (!parse_hex16(&s, &uc))
|
875
872
|
goto failed;
|
876
|
-
|
873
|
+
|
877
874
|
if (uc >= 0xD800 && uc <= 0xDFFF) {
|
878
875
|
/* Handle UTF-16 surrogate pair. */
|
879
876
|
if (*s++ != '\\' || *s++ != 'u' || !parse_hex16(&s, &lc))
|
@@ -886,7 +883,7 @@ bool parse_string(const char **sp, char **out)
|
|
886
883
|
} else {
|
887
884
|
unicode = uc;
|
888
885
|
}
|
889
|
-
|
886
|
+
|
890
887
|
b += utf8_write_char(unicode, b);
|
891
888
|
break;
|
892
889
|
}
|
@@ -900,16 +897,16 @@ bool parse_string(const char **sp, char **out)
|
|
900
897
|
} else {
|
901
898
|
/* Validate and echo a UTF-8 character. */
|
902
899
|
int len;
|
903
|
-
|
900
|
+
|
904
901
|
s--;
|
905
902
|
len = utf8_validate_cz(s);
|
906
903
|
if (len == 0)
|
907
904
|
goto failed; /* Invalid UTF-8 character. */
|
908
|
-
|
905
|
+
|
909
906
|
while (len--)
|
910
907
|
*b++ = *s++;
|
911
908
|
}
|
912
|
-
|
909
|
+
|
913
910
|
/*
|
914
911
|
* Update sb to know about the new bytes,
|
915
912
|
* and set up b to write another character.
|
@@ -923,7 +920,7 @@ bool parse_string(const char **sp, char **out)
|
|
923
920
|
}
|
924
921
|
}
|
925
922
|
s++;
|
926
|
-
|
923
|
+
|
927
924
|
if (out)
|
928
925
|
*out = sb_finish(&sb);
|
929
926
|
*sp = s;
|
@@ -1058,7 +1055,7 @@ void emit_value_indented(SB *out, const JsonNode *node, const char *space, int i
|
|
1058
1055
|
static void emit_array(SB *out, const JsonNode *array)
|
1059
1056
|
{
|
1060
1057
|
const JsonNode *element;
|
1061
|
-
|
1058
|
+
|
1062
1059
|
sb_putc(out, '[');
|
1063
1060
|
json_foreach(element, array) {
|
1064
1061
|
emit_value(out, element);
|
@@ -1072,18 +1069,18 @@ static void emit_array_indented(SB *out, const JsonNode *array, const char *spac
|
|
1072
1069
|
{
|
1073
1070
|
const JsonNode *element = array->children.head;
|
1074
1071
|
int i;
|
1075
|
-
|
1072
|
+
|
1076
1073
|
if (element == NULL) {
|
1077
1074
|
sb_puts(out, "[]");
|
1078
1075
|
return;
|
1079
1076
|
}
|
1080
|
-
|
1077
|
+
|
1081
1078
|
sb_puts(out, "[\n");
|
1082
1079
|
while (element != NULL) {
|
1083
1080
|
for (i = 0; i < indent_level + 1; i++)
|
1084
1081
|
sb_puts(out, space);
|
1085
1082
|
emit_value_indented(out, element, space, indent_level + 1);
|
1086
|
-
|
1083
|
+
|
1087
1084
|
element = element->next;
|
1088
1085
|
sb_puts(out, element != NULL ? ",\n" : "\n");
|
1089
1086
|
}
|
@@ -1095,7 +1092,7 @@ static void emit_array_indented(SB *out, const JsonNode *array, const char *spac
|
|
1095
1092
|
static void emit_object(SB *out, const JsonNode *object)
|
1096
1093
|
{
|
1097
1094
|
const JsonNode *member;
|
1098
|
-
|
1095
|
+
|
1099
1096
|
sb_putc(out, '{');
|
1100
1097
|
json_foreach(member, object) {
|
1101
1098
|
emit_string(out, member->key);
|
@@ -1111,12 +1108,12 @@ static void emit_object_indented(SB *out, const JsonNode *object, const char *sp
|
|
1111
1108
|
{
|
1112
1109
|
const JsonNode *member = object->children.head;
|
1113
1110
|
int i;
|
1114
|
-
|
1111
|
+
|
1115
1112
|
if (member == NULL) {
|
1116
1113
|
sb_puts(out, "{}");
|
1117
1114
|
return;
|
1118
1115
|
}
|
1119
|
-
|
1116
|
+
|
1120
1117
|
sb_puts(out, "{\n");
|
1121
1118
|
while (member != NULL) {
|
1122
1119
|
for (i = 0; i < indent_level + 1; i++)
|
@@ -1124,7 +1121,7 @@ static void emit_object_indented(SB *out, const JsonNode *object, const char *sp
|
|
1124
1121
|
emit_string(out, member->key);
|
1125
1122
|
sb_puts(out, ": ");
|
1126
1123
|
emit_value_indented(out, member, space, indent_level + 1);
|
1127
|
-
|
1124
|
+
|
1128
1125
|
member = member->next;
|
1129
1126
|
sb_puts(out, member != NULL ? ",\n" : "\n");
|
1130
1127
|
}
|
@@ -1138,20 +1135,20 @@ void emit_string(SB *out, const char *str)
|
|
1138
1135
|
bool escape_unicode = false;
|
1139
1136
|
const char *s = str;
|
1140
1137
|
char *b;
|
1141
|
-
|
1138
|
+
|
1142
1139
|
assert(utf8_validate(str));
|
1143
|
-
|
1140
|
+
|
1144
1141
|
/*
|
1145
1142
|
* 14 bytes is enough space to write up to two
|
1146
1143
|
* \uXXXX escapes and two quotation marks.
|
1147
1144
|
*/
|
1148
1145
|
sb_need(out, 14);
|
1149
1146
|
b = out->cur;
|
1150
|
-
|
1147
|
+
|
1151
1148
|
*b++ = '"';
|
1152
1149
|
while (*s != 0) {
|
1153
1150
|
unsigned char c = *s++;
|
1154
|
-
|
1151
|
+
|
1155
1152
|
/* Encode the next character, and write it to b. */
|
1156
1153
|
switch (c) {
|
1157
1154
|
case '"':
|
@@ -1184,10 +1181,10 @@ void emit_string(SB *out, const char *str)
|
|
1184
1181
|
break;
|
1185
1182
|
default: {
|
1186
1183
|
int len;
|
1187
|
-
|
1184
|
+
|
1188
1185
|
s--;
|
1189
1186
|
len = utf8_validate_cz(s);
|
1190
|
-
|
1187
|
+
|
1191
1188
|
if (len == 0) {
|
1192
1189
|
/*
|
1193
1190
|
* Handle invalid UTF-8 character gracefully in production
|
@@ -1210,9 +1207,9 @@ void emit_string(SB *out, const char *str)
|
|
1210
1207
|
} else if (c < 0x1F || (c >= 0x80 && escape_unicode)) {
|
1211
1208
|
/* Encode using \u.... */
|
1212
1209
|
uint32_t unicode;
|
1213
|
-
|
1210
|
+
|
1214
1211
|
s += utf8_read_char(s, &unicode);
|
1215
|
-
|
1212
|
+
|
1216
1213
|
if (unicode <= 0xFFFF) {
|
1217
1214
|
*b++ = '\\';
|
1218
1215
|
*b++ = 'u';
|
@@ -1234,11 +1231,11 @@ void emit_string(SB *out, const char *str)
|
|
1234
1231
|
while (len--)
|
1235
1232
|
*b++ = *s++;
|
1236
1233
|
}
|
1237
|
-
|
1234
|
+
|
1238
1235
|
break;
|
1239
1236
|
}
|
1240
1237
|
}
|
1241
|
-
|
1238
|
+
|
1242
1239
|
/*
|
1243
1240
|
* Update *out to know about the new bytes,
|
1244
1241
|
* and set up b to write another encoded character.
|
@@ -1248,7 +1245,7 @@ void emit_string(SB *out, const char *str)
|
|
1248
1245
|
b = out->cur;
|
1249
1246
|
}
|
1250
1247
|
*b++ = '"';
|
1251
|
-
|
1248
|
+
|
1252
1249
|
out->cur = b;
|
1253
1250
|
}
|
1254
1251
|
|
@@ -1262,7 +1259,7 @@ static void emit_number(SB *out, double num)
|
|
1262
1259
|
*/
|
1263
1260
|
char buf[64];
|
1264
1261
|
sprintf(buf, "%.16g", num);
|
1265
|
-
|
1262
|
+
|
1266
1263
|
if (number_is_valid(buf))
|
1267
1264
|
sb_puts(out, buf);
|
1268
1265
|
else
|
@@ -1282,11 +1279,11 @@ static bool number_is_valid(const char *num)
|
|
1282
1279
|
static bool expect_literal(const char **sp, const char *str)
|
1283
1280
|
{
|
1284
1281
|
const char *s = *sp;
|
1285
|
-
|
1282
|
+
|
1286
1283
|
while (*str != '\0')
|
1287
1284
|
if (*s++ != *str++)
|
1288
1285
|
return false;
|
1289
|
-
|
1286
|
+
|
1290
1287
|
*sp = s;
|
1291
1288
|
return true;
|
1292
1289
|
}
|
@@ -1317,7 +1314,7 @@ static bool parse_hex16(const char **sp, uint16_t *out)
|
|
1317
1314
|
ret <<= 4;
|
1318
1315
|
ret += tmp;
|
1319
1316
|
}
|
1320
|
-
|
1317
|
+
|
1321
1318
|
if (out)
|
1322
1319
|
*out = ret;
|
1323
1320
|
*sp = s;
|
@@ -1331,12 +1328,12 @@ static bool parse_hex16(const char **sp, uint16_t *out)
|
|
1331
1328
|
static int write_hex16(char *out, uint16_t val)
|
1332
1329
|
{
|
1333
1330
|
const char *hex = "0123456789ABCDEF";
|
1334
|
-
|
1331
|
+
|
1335
1332
|
*out++ = hex[(val >> 12) & 0xF];
|
1336
1333
|
*out++ = hex[(val >> 8) & 0xF];
|
1337
1334
|
*out++ = hex[(val >> 4) & 0xF];
|
1338
1335
|
*out++ = hex[ val & 0xF];
|
1339
|
-
|
1336
|
+
|
1340
1337
|
return 4;
|
1341
1338
|
}
|
1342
1339
|
|
@@ -1347,13 +1344,13 @@ bool json_check(const JsonNode *node, char errmsg[256])
|
|
1347
1344
|
snprintf(errmsg, 256, __VA_ARGS__); \
|
1348
1345
|
return false; \
|
1349
1346
|
} while (0)
|
1350
|
-
|
1347
|
+
|
1351
1348
|
if (node->key != NULL && !utf8_validate(node->key))
|
1352
1349
|
problem("key contains invalid UTF-8");
|
1353
|
-
|
1350
|
+
|
1354
1351
|
if (!tag_is_valid(node->tag))
|
1355
1352
|
problem("tag is invalid (%u)", node->tag);
|
1356
|
-
|
1353
|
+
|
1357
1354
|
if (node->tag == JSON_BOOL) {
|
1358
1355
|
if (node->bool_ != false && node->bool_ != true)
|
1359
1356
|
problem("bool_ is neither false (%d) nor true (%d)", (int)false, (int)true);
|
@@ -1365,7 +1362,7 @@ bool json_check(const JsonNode *node, char errmsg[256])
|
|
1365
1362
|
} else if (node->tag == JSON_ARRAY || node->tag == JSON_OBJECT) {
|
1366
1363
|
JsonNode *head = node->children.head;
|
1367
1364
|
JsonNode *tail = node->children.tail;
|
1368
|
-
|
1365
|
+
|
1369
1366
|
if (head == NULL || tail == NULL) {
|
1370
1367
|
if (head != NULL)
|
1371
1368
|
problem("tail is NULL, but head is not");
|
@@ -1374,10 +1371,10 @@ bool json_check(const JsonNode *node, char errmsg[256])
|
|
1374
1371
|
} else {
|
1375
1372
|
JsonNode *child;
|
1376
1373
|
JsonNode *last = NULL;
|
1377
|
-
|
1374
|
+
|
1378
1375
|
if (head->prev != NULL)
|
1379
1376
|
problem("First child's prev pointer is not NULL");
|
1380
|
-
|
1377
|
+
|
1381
1378
|
for (child = head; child != NULL; last = child, child = child->next) {
|
1382
1379
|
if (child == node)
|
1383
1380
|
problem("node is its own child");
|
@@ -1385,27 +1382,27 @@ bool json_check(const JsonNode *node, char errmsg[256])
|
|
1385
1382
|
problem("child->next == child (cycle)");
|
1386
1383
|
if (child->next == head)
|
1387
1384
|
problem("child->next == head (cycle)");
|
1388
|
-
|
1385
|
+
|
1389
1386
|
if (child->parent != node)
|
1390
1387
|
problem("child does not point back to parent");
|
1391
1388
|
if (child->next != NULL && child->next->prev != child)
|
1392
1389
|
problem("child->next does not point back to child");
|
1393
|
-
|
1390
|
+
|
1394
1391
|
if (node->tag == JSON_ARRAY && child->key != NULL)
|
1395
1392
|
problem("Array element's key is not NULL");
|
1396
1393
|
if (node->tag == JSON_OBJECT && child->key == NULL)
|
1397
1394
|
problem("Object member's key is NULL");
|
1398
|
-
|
1395
|
+
|
1399
1396
|
if (!json_check(child, errmsg))
|
1400
1397
|
return false;
|
1401
1398
|
}
|
1402
|
-
|
1399
|
+
|
1403
1400
|
if (last != tail)
|
1404
1401
|
problem("tail does not match pointer found by starting at head and following next links");
|
1405
1402
|
}
|
1406
1403
|
}
|
1407
|
-
|
1404
|
+
|
1408
1405
|
return true;
|
1409
|
-
|
1406
|
+
|
1410
1407
|
#undef problem
|
1411
1408
|
}
|