Subject: [core] CoRAL href (CRI): simplicity vs. compactness?

From: Carsten Bormann <[email-masked]>
Date: Wed, 23 Jun 2021 13:42:43 +0200

(Please read this in a monospaced font.)

href-04 has the optimized href syntax that we worked out with Jim Schaad and further optimized recently, which can be summarized as:

CRI-Reference = [
  ((?scheme, ?authority) 
   // discard),
  *path-segment,
  ? ((null, fragment)         ; no query section, but fragment
     //([+query-segment], ?fragment)) ; present query section has >0 items
]

This is so efficient because relative URI-references such as /a are extremely short, with the CRI reference.

[“a”]

On the other hand, quite some juggling is needed to translate a URI/URI reference into this format.

The weirdness that kept us thinking was that the path doesn’t have its own delimiters, so the presence or absence of a path needs to be inferred from other information, here the discard value.  If the discard is absent, there is always a path; only if discard is present, there can be an absent path!

This, together with the RFC 3986 rule to suppress a leading empty path segment,  leads to the following translations of URI references info CRI references (*):

/           []
/a          ["a"]
/a/b        ["a", "b"]
/a/b?foo    ["a", "b", ["foo"]]
a           [1, "a"]
.           [1]n..          [2]
../a        [2, "a"]
../a?foo    [2, "a", ["foo"]]
../..       [3]
a?foo       [1, "a", ["foo"]]
?foo        [0, ["foo"]]
#bar        [0, null, "bar"]
            [0]

(Yes, that is the empty URI reference at the end.)
Most of the weirdness is inherited from RFC 3986.

Internally, my code uses

[scheme, [host, port], discard, path, query, fragment]

where most items can be null.

So I have code that translates between the internal and the external form, and that is full of if statements.

We could, instead, decide that we want to use something like the internal form for transfer as well.
We would replace absent items by nulls (or leave them off the end).

So this would lead to something like:

/           []                   0 []
/a          ["a"]                4 [null, null, null, ["a"]]
/a/b        ["a", "b"]           4 [null, null, null, ["a", "b"]]
/a/b?foo    ["a", "b", ["foo"]]  4 [null, null, null, ["a", "b"], ["foo"]]
a           [1, "a"]             3 [null, null, 1, ["a"]]
.           [1]                  2 [null, null, 1]
..          [2]                  2 [null, null, 2]
../a        [2, "a"]             3 [null, null, 2, ["a"]]
../a?foo    [2, "a", ["foo"]]    3 [null, null, 2, ["a"], ["foo"]]
../..       [3]                  2 [null, null, 2]
a?foo       [1, "a", ["foo"]]    3 [null, null, 1, ["a"], ["foo"]]
?foo        [0, ["foo"]]         3 [null, null, 0, null, ["foo"]]
#bar        [0, null, "bar"]     3 [null, null, 0, null, null, "bar"]
            [0]                  2 [null, null, 0]

The third column is the number of wasted bytes, which (apart from the outlier “/“) ranges from 2 to 4 bytes.

Simplicity vs. saving these 2 to 4 bytes for relative CRI references?
Maybe one simple tweak (**) can reduce this to 0 to 2 bytes.
But how important are relative references?
Let’s discuss in 160 minutes.

Grüße, Carsten

(*) I typed these by hand and apologize for the many errors likely to be in there…

(**) Something about leaving out the first two nulls; probably needs a separate discard value (true?) for discard all.  Exercise left to the reader…