Subject: [Cbor] CDDL style questions

From: Jim Hague <[email-masked]>
Date: Thu, 22 Nov 2018 14:23:41 +0000

A couple of CDDL style questions arising from the progress of our C-DNS
draft, which includes a CDDL spec. I'm not asking for any 'right'
answer, more curious to know whether there is such a thing as an
emerging consensus on CDDL style. Or just whether CDDL users out there
Have Opinions.

o Specifying fields with constant values. Should field types be explicit
or implicit?

FileHeader = {
    "identifier1" => "A string",
    "identifier2" => tstr .eq "Another string",
    "value1" => 1,
    "value2" => uint .eq 2,
}

I'm torn on this at the moment. Assuming the implementer of the format
is not a CDDL/CBOR sophisticate, including explicit types may be
valuable. But implicit is pleasingly succinct.

o Use of .size and ranges to restrict contents of fields.

The C-DNS file format obviously includes a lot of IP and DNS specific
quantities. In the main, I have not attempted to specify value
ranges/storage sizes for these - apart from anything else, I started on
this before the necessary facilities were added to CDDL:

    ? server-port           => uint,
    ? query-opcode          => uint,
    ? query-rcode           => uint,

The exception is for IP addresses where we take our lead from the CDDL
draft:

    IPv4Address = bstr .size 4
    IPv6Address = bstr .size 16
    IPAddress = IPv4Address / IPv6Address

I think my motivation here is to try minimise 'magic numbers' and define
a type rather than have 'bstr .size 16' all over the place.

It's been suggested, though, that we could:

    Port = uint .size 2
    OpCode = 0..15
    RCode = 0..15
    ....
    ? server-port           => Port,
    ? query-opcode          => OpCode,
    ? query-rcode           => RCode,

My initial reaction was that this isn't a clear-cut win; it feels like
I'm duplicating IP/DNS protocol definitions which are specified
elsewhere. However, the more I look at it, the more I wonder if the
extra info is in fact valuable to an implementer.
-- 
Jim Hague - [email-masked]          Never trust a computer you can't lift.

---

From: Carsten Bormann <[email-masked]>
Date: Thu, 22 Nov 2018 16:03:32 +0100

Hi Jim,

as CDDL was developed, the various groups that have picked it up have evolved their own style.
I’m not sure we are ready to say there is a received style for writing CDDL.
So:

> On Nov 22, 2018, at 15:23, Jim Hague <[email-masked]> wrote:
> 
> emerging consensus on CDDL style.

Inquiries like yours are great for exposing the questions that such a style would answer.

> Or just whether CDDL users out there
> Have Opinions.

This one.  Here is my opinion…

>    “value2" => uint .eq 2,

In CDDL, “A string” is a type.  With just one member in the set, that specific string.
Saying that this member of the type is also a member of a larger type doesn’t really buy you a lot.
(It is also rarely clear, which type.  Is 2 an uint or an int?  Or a number?  An unsigned?)
If I were writing the above, it might look like:

> FileHeader = {
>    identifier1: “A string",
>    identifier2: “Another string",
>    value1: 1,
>    value2: 2,
> }

(Not a very realistic example, but I think it should illustrate my answer to the question.)

Of course, it gets interesting once you need extensibility:

> FileHeader = {
>    identifier1: “A string",
>    identifier2: “Another string",
>    value1: 1,
>    value2: 2,
    * label => value
  label = text
  value = text / uint

Here you are indeed making a statement about what types you expect future additions to lie in.

> valuable.

Well, when they see “foo” in C, do they know it is a char[], or do they always write

char mystring[] = “foo”;

before using a string?

> But implicit is pleasingly succinct.

Indeed.

> a type rather than have ‘bstr .size 16' all over the place.

Looks good to me.

> extra info is in fact valuable to an implementer.

I don’t think there is a right answer to that.

Ultimately, the application will need to check that the port number is in range.
Whether you simply leave it to the application to do that or include information such as .size 2 in the CDDL is a tradeoff.
On one hand, the CDDL becomes longer.
On the other hand, it is more expressive of what you actually want to exchange; you may need less English text to add.
On the third hand, there are tons of application constraints that CDDL cannot express.

In the extreme,

FileHeader = any

is a valid CDDL specification for FileHeader, too :-)

Without knowing all the details of C-DNS, I would expect that 

>    Port = uint .size 2

is a good type to have at hand and use where the semantics ask for a TCP/UDP port.

(It also immediately exposes the question whether it shouldn’t really be

    Port = 1..0xFFFF

i.e., whether 0 should be excluded here. Of course, I don’t know what the answer to that question would be for C-DNS.)

By the way, you are exposing another style issue in your examples:
You seem to use CamelCase for your type names, and kebab-case for the map labels.
Looks good to me, but is not the only style in use.

Maybe we can evolve something like a PEP-8 [1] or the golang guidelines [2] for CDDL…
(We can already copy the text about “Know when to be inconsistent” from PEP-8 :-)

Oh, and we need a style guide for diagnostic notation, too…
Is it:

[
  / protected / << {
      / alg / 1: 1 / AES-GCM 128 /
    } >> ,
  / unprotected / {
    / iv / 5: h’02d1f7e6f26c43d4868d87ce'
  },
…

Or is it:

[
  << {                  / protected /
      1: 1              / alg: AES-GCM 128 /
    } >>,
  {                     / unprotected /
    5: h’02d1f7e6f26c43d4868d87ce'    / iv /
  },
…

Grüße, Carsten

[1]: https://www.python.org/dev/peps/pep-0008/
[2]: https://github.com/golang/go/wiki/CodeReviewComments

---

From: Brian E Carpenter <[email-masked]>
Date: Fri, 23 Nov 2018 08:37:46 +1300

Mainly I agree with Carsten, perhaps because I developed the CDDL for GRASP under his tuition. So I have snipped everything except one point.
On 2018-11-23 04:03, Carsten Bormann wrote:
...
> i.e., whether 0 should be excluded here. Of course, I don’t know what the answer to that question would be for C-DNS.)

For GRASP we ended up with:

port-number = 0..65535

one reason being that you can indicate an undefined port number by using 0. So it rather depends whether you are defining the type to use in a signaling message (as for GRASP) or to use in an actual transport header.

If anyone cares, the full GRASP CDDL is at https://tools.ietf.org/html/draft-ietf-anima-grasp-15#section-5 .

I did have one regret - using hyphens in names. When writing Python code using the same names, I had to change them all to underscore. Not serious, but a bit annoying.

I felt bad about including:

IPPROTO_TCP = 6
IPPROTO_UDP = 17

I wished I could have imported those from somewhere. As an extension to the style question, is there a role for a library of include files?

    Brian