mirror of
https://github.com/felt/tippecanoe.git
synced 2026-10-02 16:35:40 +02:00
Move parser-only expect state out of json_object
`expect` was only meaningful while the parser was building a container,
and only ever read or written from jsonpull.cpp itself; once parsing
finished it was dead weight on every JSON_ARRAY and JSON_HASH (and
present-but-unused on every primitive too). Move it into the parser's
container stack, alongside the shared_ptr to the container it pertains
to:
struct json_pull::parse_frame {
json_object_ptr container;
json_type expect;
};
std::vector<parse_frame> container_stack;
The base class now only carries data-model state (parent, parser, type).
No external caller depended on `expect`, so no sweep was needed outside
jsonpull.cpp.
This change does not, in itself, shrink any json_object: the 4-byte
`expect` field used to live at offset 20 inside the base, where it was
already being eaten by alignment padding for the 8-byte-aligned first
member of every subclass (std::string, std::vector, double). The win is
in the data model, not the byte count -- the 4-byte hole is still
there, but it is now available for a future subclass whose first member
is small enough to slot into it.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+35
-34
@@ -115,35 +115,36 @@ static json_object_ptr fabricate_object(json_pull *jp, json_object *parent, json
|
||||
return make_object(type, parent, jp);
|
||||
}
|
||||
|
||||
static inline json_object *current_container(json_pull *j) {
|
||||
return j->container_stack.empty() ? nullptr : j->container_stack.back().get();
|
||||
static inline json_pull::parse_frame *current_frame(json_pull *j) {
|
||||
return j->container_stack.empty() ? nullptr : &j->container_stack.back();
|
||||
}
|
||||
|
||||
static json_object_ptr add_object(json_pull *j, json_type type) {
|
||||
json_object *c = current_container(j);
|
||||
json_pull::parse_frame *f = current_frame(j);
|
||||
json_object *c = f ? f->container.get() : nullptr;
|
||||
json_object_ptr o = make_object(type, c, j);
|
||||
|
||||
if (c != nullptr) {
|
||||
if (f != nullptr) {
|
||||
if (c->type == JSON_ARRAY) {
|
||||
if (c->expect == JSON_ITEM) {
|
||||
if (f->expect == JSON_ITEM) {
|
||||
c->array().push_back(o);
|
||||
c->expect = JSON_COMMA;
|
||||
f->expect = JSON_COMMA;
|
||||
} else {
|
||||
j->error = "Expected a comma, not a list item";
|
||||
return nullptr;
|
||||
}
|
||||
} else if (c->type == JSON_HASH) {
|
||||
if (c->expect == JSON_VALUE) {
|
||||
if (f->expect == JSON_VALUE) {
|
||||
c->entries().back().value = o;
|
||||
c->expect = JSON_COMMA;
|
||||
} else if (c->expect == JSON_KEY) {
|
||||
f->expect = JSON_COMMA;
|
||||
} else if (f->expect == JSON_KEY) {
|
||||
if (type != JSON_STRING) {
|
||||
j->error = "Hash key is not a string";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
c->entries().push_back({o, nullptr});
|
||||
c->expect = JSON_COLON;
|
||||
f->expect = JSON_COLON;
|
||||
} else {
|
||||
j->error = "Expected a comma or colon";
|
||||
return nullptr;
|
||||
@@ -229,8 +230,7 @@ again:
|
||||
if (o == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
o->expect = JSON_ITEM;
|
||||
j->container_stack.push_back(o);
|
||||
j->container_stack.push_back({o, JSON_ITEM});
|
||||
|
||||
if (cb != nullptr) {
|
||||
cb(JSON_ARRAY, j, state);
|
||||
@@ -240,25 +240,26 @@ again:
|
||||
}
|
||||
|
||||
case ']': {
|
||||
json_object *cc = current_container(j);
|
||||
if (cc == nullptr) {
|
||||
json_pull::parse_frame *f = current_frame(j);
|
||||
if (f == nullptr) {
|
||||
j->error = "Found ] at top level";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
json_object *cc = f->container.get();
|
||||
if (cc->type != JSON_ARRAY) {
|
||||
j->error = "Found ] not in an array";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (cc->expect != JSON_COMMA) {
|
||||
if (!(cc->expect == JSON_ITEM && cc->array().size() == 0)) {
|
||||
if (f->expect != JSON_COMMA) {
|
||||
if (!(f->expect == JSON_ITEM && cc->array().size() == 0)) {
|
||||
j->error = "Found ] without final element";
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
json_object_ptr ret = j->container_stack.back();
|
||||
json_object_ptr ret = f->container;
|
||||
j->container_stack.pop_back();
|
||||
return ret;
|
||||
}
|
||||
@@ -270,8 +271,7 @@ again:
|
||||
if (o == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
o->expect = JSON_KEY;
|
||||
j->container_stack.push_back(o);
|
||||
j->container_stack.push_back({o, JSON_KEY});
|
||||
|
||||
if (cb != nullptr) {
|
||||
cb(JSON_HASH, j, state);
|
||||
@@ -281,25 +281,26 @@ again:
|
||||
}
|
||||
|
||||
case '}': {
|
||||
json_object *cc = current_container(j);
|
||||
if (cc == nullptr) {
|
||||
json_pull::parse_frame *f = current_frame(j);
|
||||
if (f == nullptr) {
|
||||
j->error = "Found } at top level";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
json_object *cc = f->container.get();
|
||||
if (cc->type != JSON_HASH) {
|
||||
j->error = "Found } not in a hash";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (cc->expect != JSON_COMMA) {
|
||||
if (!(cc->expect == JSON_KEY && cc->entries().size() == 0)) {
|
||||
if (f->expect != JSON_COMMA) {
|
||||
if (!(f->expect == JSON_KEY && cc->entries().size() == 0)) {
|
||||
j->error = "Found } without final element";
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
json_object_ptr ret = j->container_stack.back();
|
||||
json_object_ptr ret = f->container;
|
||||
j->container_stack.pop_back();
|
||||
return ret;
|
||||
}
|
||||
@@ -366,17 +367,17 @@ again:
|
||||
/////////////////////////// Comma
|
||||
|
||||
case ',': {
|
||||
json_object *cc = current_container(j);
|
||||
if (cc != nullptr) {
|
||||
if (cc->expect != JSON_COMMA) {
|
||||
json_pull::parse_frame *f = current_frame(j);
|
||||
if (f != nullptr) {
|
||||
if (f->expect != JSON_COMMA) {
|
||||
j->error = "Found unexpected comma";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (cc->type == JSON_HASH) {
|
||||
cc->expect = JSON_KEY;
|
||||
if (f->container->type == JSON_HASH) {
|
||||
f->expect = JSON_KEY;
|
||||
} else {
|
||||
cc->expect = JSON_ITEM;
|
||||
f->expect = JSON_ITEM;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -390,18 +391,18 @@ again:
|
||||
/////////////////////////// Colon
|
||||
|
||||
case ':': {
|
||||
json_object *cc = current_container(j);
|
||||
if (cc == nullptr) {
|
||||
json_pull::parse_frame *f = current_frame(j);
|
||||
if (f == nullptr) {
|
||||
j->error = "Found colon at top level";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (cc->expect != JSON_COLON) {
|
||||
if (f->expect != JSON_COLON) {
|
||||
j->error = "Found unexpected colon";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
cc->expect = JSON_VALUE;
|
||||
f->expect = JSON_VALUE;
|
||||
|
||||
if (cb != nullptr) {
|
||||
cb(JSON_COLON, j, state);
|
||||
|
||||
+11
-5
@@ -72,7 +72,6 @@ struct json_object {
|
||||
json_pull *parser = nullptr;
|
||||
|
||||
json_type type;
|
||||
int expect = 0; // used by the parser on JSON_ARRAY / JSON_HASH nodes
|
||||
|
||||
json_object(json_type t) : type(t) {}
|
||||
json_object(json_type t, json_object *p, json_pull *pl) : parent(p), parser(pl), type(t) {}
|
||||
@@ -190,10 +189,17 @@ struct json_pull {
|
||||
ssize_t buffer_head = 0;
|
||||
|
||||
// Stack of currently-open containers; the top is the innermost container
|
||||
// being parsed. Replaces the previous single `container` pointer / parent
|
||||
// walk, which previously required enable_shared_from_this<json_object>
|
||||
// on every json_object instance (16 extra bytes per node).
|
||||
std::vector<json_object_ptr> container_stack;
|
||||
// being parsed. Each frame also remembers what token is expected next
|
||||
// (an item, a comma, a key, a colon, or a value). This stack is the
|
||||
// only place the parser-only `expect` state lives, so it does not
|
||||
// pollute json_object once parsing finishes. Replaces the previous
|
||||
// single `container` pointer / parent walk, which previously required
|
||||
// enable_shared_from_this<json_object> on every json_object instance.
|
||||
struct parse_frame {
|
||||
json_object_ptr container;
|
||||
json_type expect;
|
||||
};
|
||||
std::vector<parse_frame> container_stack;
|
||||
json_object_ptr root;
|
||||
|
||||
std::string number_buffer;
|
||||
|
||||
Reference in New Issue
Block a user