Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion parser/enrich.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@

- ``category`` — a coarse semantic class (constructor, predicate, io, ...).
- ``typeEncodings``— for each opaque C type, how it round-trips to the wire
(text / MF-JSON / WKB) and the function names that do it.
(text / MF-JSON / WKB) and the function names that do it,
and under ``bytes`` the reader and writer of its WKB bytes.
- ``network`` — whether the function can be projected onto a *stateless*
endpoint, and if not, why.
- ``wire`` — per-parameter and return value, the concrete request /
Expand Down Expand Up @@ -238,6 +239,28 @@ def choose(cands: dict, base: str, suffix: str) -> str:
generic = base.lower() + suffix
return generic if generic in cands else sorted(cands)[0]

# The byte codec of a type, beside its wire encodings: a reader
# ``T *f(const uint8_t *wkb, size_t size)`` and a writer
# ``uint8_t *f(const T *, uint8_t variant, size_t *size_out)``, recognised by
# shape. The bytes are no wire string, so they add no ``encodings`` entry; an
# in-process binding carries a value as them (the JVM engines).
bread: dict[str, list] = {}
bwrite: dict[str, list] = {}
for fn in functions:
params = fn.get("params", [])
ret = fn["returnType"]["canonical"]
cs = [p["canonical"] for p in params]
if (len(cs) == 2 and _base(cs[0]) == "uint8_t" and _ptr_depth(cs[0]) == 1
and _base(cs[1]) == "size_t" and _ptr_depth(cs[1]) == 0
and _ptr_depth(ret) == 1 and _base(ret) in structs):
bread.setdefault(_base(ret), []).append(fn["name"])
if (len(cs) == 3 and _base(ret) == "uint8_t" and _ptr_depth(ret) == 1
and _ptr_depth(cs[0]) == 1 and _base(cs[0]) in structs
and _base(cs[1]) in ("uint8_t", "unsigned char")
and _ptr_depth(cs[1]) == 0
and _base(cs[2]) == "size_t" and _ptr_depth(cs[2]) == 1):
bwrite.setdefault(_base(cs[0]), []).append(fn["name"])

out: dict[str, dict] = {}
for base, s in enc.items():
dec = {e: choose(c, base, dec_suffix[e])
Expand All @@ -255,6 +278,11 @@ def choose(cands: dict, base: str, suffix: str) -> str:
"in_aux": s["decoders"][in_e][dec[in_e]] if in_e else [],
"out_aux": s["encoders"][out_e][encd[out_e]] if out_e else [],
}
if base in bread and base in bwrite:
out[base]["bytes"] = {
"decoder": choose(dict.fromkeys(bread[base]), base, "_from_wkb"),
"encoder": choose(dict.fromkeys(bwrite[base]), base, "_as_wkb"),
}
return out


Expand Down
20 changes: 20 additions & 0 deletions tests/test_enrich.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ def fn(name, ret, *params):
("const struct Box *", "box"), ("int", "maxdd")),
fn("weird_in", "struct Weird *",
("const char *", "str"), ("int", "basetype")),
# The byte codec: a reader of (bytes, length) and a writer of (value,
# variant, *size_out). Box has a writer and no reader, so no byte codec.
fn("temporal_from_wkb", "struct Temporal *",
("const uint8_t *", "wkb"), ("size_t", "size")),
fn("temporal_as_wkb", "uint8_t *",
(T, "temp"), ("uint8_t", "variant"), ("size_t *", "size_out")),
fn("box_as_wkb", "uint8_t *",
("const struct Box *", "box"), ("uint8_t", "variant"),
("size_t *", "size_out")),
# An otherwise-exposable function carrying an internal doxygen group: it
# must be policy-excluded (api=internal), like the programmer Datum API.
dict(fn("internal_op", "struct Temporal *", (T, "temp")),
Expand Down Expand Up @@ -149,6 +158,17 @@ def test_defaultable_aux_accepted_type_tag_rejected(self):
# Weird gets no decoder at all.
self.assertNotIn("Weird", self.te)

def test_byte_codec_beside_the_wire_encodings(self):
# the byte-codec twin of #test_struct_prefix_stripped_and_round_trip
self.assertEqual(self.te["Temporal"]["bytes"],
{"decoder": "temporal_from_wkb",
"encoder": "temporal_as_wkb"})
# the bytes are no wire string: the encodings stay the string forms
self.assertEqual(self.te["Temporal"]["encodings"], ["mfjson", "text"])
# a writer without a reader states no codec
self.assertNotIn("bytes", self.te["Box"])
self.assertNotIn("bytes", self.te["Set"])

def test_no_primitive_or_intermediate_false_positives(self):
self.assertNotIn("int", self.te) # was a real false positive
self.assertNotIn("char", self.te)
Expand Down
Loading