Skip to main content

cddl-gen 已重命名为 zcbor!

项目描述

cddl-gen 已重命名为zcbor 此软件包已弃用。

基于模式的数据操作和代码生成

CDDL 是IETF RFC 8610中定义的一种人类可读的数据描述语言。通过直接调用cddl-gen(通过 Pip 或 setup.py 安装时)或 Python 脚本cddl_gen.py,您可以生成 C 代码来验证/编码/解码符合 CDDL 模式的 CBOR 数据。cddl-gen 还可以验证 CBOR 数据并将其与 JSON/YAML 进行转换,无论是从命令行还是作为模块导入。最后,该包包含一个轻量级的 C CBOR 编码/解码库。该库供生成的代码使用,也可以直接在您自己的代码中使用。

特征

以下是一些可能的使用 cddl-gen 的方法:

  • Python脚本:
    • 验证 YAML 文件并将其转换为 CBOR,例如用于传输。
    • 在使用其他工具处理之前验证 YAML/JSON/CBOR 文件
    • 将传入的 CBOR 数据解码并验证为人类可读的 YAML/JSON。
    • 作为处理 YAML/JSON/CBOR 文件的 python 脚本的一部分。cddl-gen 与 PyYAML 兼容,并且可以通过命名元组额外提供验证和/或更简单的检查。
  • C代码:
    • 生成用于验证和解码或编码 CBOR 的 C 代码,用于优化或受限环境,例如微控制器。
    • 提供类似于 TinyCBOR/QCBOR/NanoCBOR 的低占用空间 CBOR 解码/编码库。

Python 脚本

从命令行调用 cddl_gen.py

cddl_gen.py 脚本可以直接读取 CBOR、YAML 或 JSON 数据,并根据 CDDL 描述对其进行验证。它还可以在 CBOR/YAML/JSON 之间自由转换数据。它还可以将数据输出到格式化为字节数组的 C 文件。

以下是从命令行转换(和验证)数据的通用示例。该脚本从文件扩展名推断数据格式,但也可以明确指定格式。有关cddl-gen convert --help更多信息,请参阅。

python3 <cddl-gen base>/cddl_gen/cddl_gen.py -c <CDDL description file> convert -t <which CDDL type to expect> -i <input data file> -o <output data file>

或调用其命令行可执行文件(如果通过 安装pip):

cddl-gen -c <CDDL description file> convert -t <which CDDL type to expect> -i <input data file> -o <output data file>

请注意,由于 CBOR 支持比 YAML 和 JSON 更多的数据类型,因此 cddl-gen 在与 YAML/JSON 之间进行转换时使用惯用格式。这在处理使用不受支持的功能的数据的 YAML/JSON 转换时是相关的。CBOR 支持以下数据类型,但 YAML(以及作为 YAML 子集的 JSON)不支持:

  1. bytestrings:YAML 仅支持文本字符串。在 YAML 中,字节串 ('<bytestring>') 表示为 {"bstr": "<hex-formatted bytestring>"},如果 CBOR 字节串包含 CBOR 格式的数据,则表示为 {"bstr": <any type>} ,其中数据被解码为<任何类型>。
  2. 除文本字符串之外的映射键:在 YAML 中,此类键值对表示为 {"keyval<unique int>": {"key": <key, not text>, "val": <value>}}
  3. tags:在 cbor2 中,标签由一种特殊类型 cbor2.CBORTag 表示。在 YAML 中,这些表示为 {"tag": <tag number>, "val": <tagged data>}。

在 Python 脚本中导入 cddl_gen

导入 cddl_gen 可以访问用于实现命令行转换功能的 DataTranslator 类。DataTranslator 可用于以编程方式执行翻译或操作数据。访问数据时,您可以在两种内部格式之间进行选择:

  1. cbor2、yaml (pyyaml) 和 json 包提供的格式。这是一种将序列化类型(映射、列表、字符串、数字等)直接映射到相应 Python 类型的格式。这种格式在这些包之间是通用的,这使得翻译非常简单。返回此格式时,DataTranslator 隐藏上述字节串、标签和非文本键的惯用表示。
  2. 一种自定义格式,允许通过 CDDL 描述文件中的名称访问数据。这种格式是使用命名元组实现的,并且是不可变的,这意味着它可以用于检查数据,但不能用于更改或创建数据。

代码生成

生成的代码包括:

  • 一个头文件,其中包含 CDDL 中定义的类型的 typedef,以及某些类型(指定为条目类型)的解码函数的声明。编码和解码的 typedef 相同。
  • 包含所有编码/解码代码的 AC 文件。代码分为多个函数,每个函数包含一个if语句,“and”s 和“or”s 一起调用 cbor 库或其他生成的解码函数。

CDDL 允许对数据结构的成员施加限制。限制可以是类型、内容(例如整数或字符串的值/大小)和重复(例如列表中的成员数)。生成的代码将验证输入(即编码时的结构,或解码的有效负载),这意味着它将检查 CDDL 描述中设置的所有限制,如果限制被破坏则失败。

cbor 库完成了大部分字节的实际转换和移动,以及值的验证。

tests/中有代码生成测试。测试需要Zephyr(如果您的 shell 设置为构建 Zephyr 样本,那么测试也应该构建)。

构建系统

当使用参数调用 cddl-gen 时--output-cmake <file path>,将在该位置创建一个 cmake 文件。cmake 文件创建一个 cmake 目标并将生成的和未生成的源文件以及包含目录添加到头文件中。然后可以将这个 cmake 文件包含在您的项目CMakeLists.txt文件中,并且可以将目标链接到您的项目中。这在测试中得到了证明,例如在tests/cbor_decode/test3_simple/CMakeLists.txt。可以指示 cddl-gen 将未生成的源复制到与生成的源相同的位置,使用--copy-sources.

CBOR 解码/编码库

生成的代码使用头文件源代码中的 CBOR 库,但也可以直接使用。如果是这样,您必须实例化一个cbor_state_t对象以及一个cbor_state_backups_t对象(在简单的用例中备份可以为 NULL)。

elem_count 成员是指当前列表或映射中编码对象的数量。elem_count 在进入嵌套列表或映射时再次启动,并在退出时恢复。

elem_count 是需要“备份”状态的一个原因(另一个是允许回滚有效负载)。您需要与数据中嵌套级别的最大数量相对应的备份数量。

如果您使用规范编码 (CDDL_CBOR_CANONICAL) 或使用 bstrx_cbor_* 函数,则需要对编码进行备份。如果数据中有任何列表、映射或 CBOR 编码的字符串,则需要备份进行解码。

请注意,直接使用该库对编码的好处大于对解码的好处。对于解码,代码生成将提供许多手动编写乏味且容易忘记的检查。

/** The number of states must be at least equal to one more than the maximum
 *  nested depth of the data.
 */
cbor_state_t states[n];

/** Initialize the states. After calling this, states[0] is ready to be used
 *  with the encoding/decoding APIs.
 *  elem_count must be the maximum expected number of top-level elements when
 *  decoding (1 if the data is wrapped in a list).
 *  When encoding, elem_count must be 0.
 */
new_state(states, n, payload, payload_len, elem_count);

CDDL 简介

在 CDDL 中,您从其他类型定义类型。可以从基本类型或您定义的其他类型定义类型。类型用 ' =' 声明,例如Foo = int,它将类型声明为Foo整数,类似于typedef int Foo;C 中的。CDDL 定义了以下基本类型(这不是一个详尽的列表):

  • int: 正整数或负整数
  • uint: 正整数
  • bstr: 字节串
  • tstr: 文本字符串
  • bool: 布尔值
  • nil:无/空值
  • float: 浮点值
  • any: 任何单个元素

CDDL 允许创建聚合类型:

  • []: 列表。元素不需要具有相同的类型。
  • {}: 地图。声明为<key> => <value>or的键/值对<key>: <value>。请注意,:它也用于标签。
  • (): 组。没有封闭类型的分组,这意味着 egFoo = [(int, bstr)]等价于Foo = [int, bstr].
  • /: 工会。类似于 CEg中的联合,Foo = int/bstr/Bar其中 Foo 是 int、bstr 或 Bar(某些自定义类型)。

可以使用文字代替基本类型名称:

  • Number: Foo = 3,其中 Foo 是一个 uint,附加要求它的值必须为 3。
  • 数字范围:Foo = -100..100,其中 Foo 是一个 int 值,介于 -100 和 100 之间。
  • 文本字符串:Foo = "hello",其中 Foo 是一个 tstr,要求它必须是“hello”。
  • True/False: Foo = false,其中 Foo 是一个总是为假的布尔值。

基类型也可以通过其他方式进行限制:

  • .size:适用于整数和字符串。例如Foo = uint .size 4,Foo 是一个 uint,正好 4 个字节长。
  • .cbor/ .cborseq:例如Foo = bstr .cbor Bar,其中 Foo 是一个 bstr,其内容必须是可解码为 Bar 类型的 CBOR 数据。

一个元素可以重复:

  • ?: 0 或 1 次。例如Foo = [int, ?bstr],其中 Foo 是一个列表,其中一个 int 可能后跟一个 bstr。
  • *: 0 次以上。例如Foo = [*tstr],其中 Foo 是一个包含 0 个或多个 tstr 的列表。
  • +: 1 次以上。例如Foo = [+Bar]
  • x*y:在 x 和 y 次之间,包括在内。例如Foo = {4*8(int => bstr)},Foo 是一个包含 4 到 8 个键/值对的映射,其中每个键是一个 int,每个值是一个 bstr。

请注意,在 cddl_gen 脚本及其生成的代码中,通过*和支持的条目数+受 default_max_qty 值的影响。

任何元素都可以用 标记:。标签仅用于可读性,不会以任何方式影响数据结构。例如Foo = [name: tstr, age: uint]等价于Foo = [tstr, uint]

有关 CDDL 示例代码,请参见test3_simple

使用示例

代码生成

此示例取自test3_simple

如果您的 CDDL 文件包含以下代码:

Timestamp = bstr .size 8

; Comments are denoted with a semicolon
Pet = [
    name: [ +tstr ],
    birthday: Timestamp,
    species: (cat: 1) / (dog: 2) / (other: 3),
]

调用 Python 脚本:

python3 <cddl-gen base>/cddl_gen/cddl_gen.py -c pet.cddl code -d -t Pet --oc pet_decode.c --oh pet_decode.h
# or
cddl-gen -c pet.cddl code -d -t Pet --oc pet_decode.c --oh pet_decode.h

并使用生成的代码

#include <pet_decode.h> /* The name of the header file is taken from the name of
                           the cddl file, but can also be specifiec when calling
                           the script. */

/* ... */

/* The following type and function refer to the Pet type in the CDDL, which
 * has been specified as an --entry-types (-t) when invoking cddl-gen. */
Pet_t pet;
uint32_t decode_len;
bool success = cbor_decode_Pet(input, sizeof(input), &pet, &decode_len);

编码的过程是相同的,除了:

  • 调用 cddl-gen 时更改-d-e
  • 在代码中输入参数变成输出参数,反之亦然:
#include <pet_encode.h> /* The name of the header file is taken from the name of
                           the cddl file, but can also be specifiec when calling
                           the script. */

/* ... */

/* The following type and function refer to the Pet type in the CDDL, which
 * has been specified as an --entry-types (-t) when invoking cddl-gen. */
Pet_t pet = { /* Initialize with desired data. */ };
uint8_t output[100]; /* 100 is an example. Must be large enough for data to fit. */
uint32_t out_len;
bool success = cbor_encode_Pet(output, sizeof(output), &pet, &out_len);

CBOR 解码/编码库

对于编码:

#include <cbor_encode.h>

uint8_t payload[100];
cbor_state_t state;
new_state(&state, 1, payload, sizeof(payload), 0);

res = res && list_start_encode(&state, 0);
res = res && tstrx_put(&state, "first");
res = res && tstrx_put(&state, "second");
res = res && list_end_encode(&state, 0);
uint8_t timestamp[8] = {1, 2, 3, 4, 5, 6, 7, 8};
cbor_string_type_t timestamp_str = {
  .value = timestamp,
  .len = sizeof(timestamp),
};
res = res && bstrx_encode(&state, &timestamp_str);
res = res && uintx32_put(&state, 2 /* dog */);
res = res && list_end_encode(&state, 0);

转换

这是从 YAML 转换为 CBOR 的示例调用:

python3 <cddl-gen base>/cddl_gen/cddl_gen.py -c pet.cddl convert -t Pet -i mypet.yaml -o mypet.cbor
# or
cddl-gen -c pet.cddl convert -t Pet -i mypet.yaml -o mypet.cbor

它从 mypet.yaml 中获取一个 yaml 结构,根据 pet.cddl 中 CDDL 描述中的 Pet 类型对其进行验证,并将二进制 CBOR 数据写入 mypet.cbor。

有关使用 python 模块的示例,请参阅 <cddl-gen base>/tests/ 中的测试

运行测试

生成代码的测试基于 Zephyr ztests。脚本中转换函数的测试是用 unittest 模块实现的。

还有 test.sh 脚本可以快速运行所有测试。 tests/test.sh运行所有测试,包括tests/scripts. tests/cbor_decode/test.sh运行所有解码测试。 tests/cbor_encode/test.sh运行所有编码测试。

这些测试依赖于pycodestyle来自pip. 不带参数运行这些脚本。

要设置运行 ztest 测试的环境,请遵循Zephyr 的入门指南,或查看.github目录中的工作流程。

项目详情


下载文件

下载适用于您平台的文件。如果您不确定要选择哪个,请了解有关安装包的更多信息。

源分布

cddl-gen-0.3.1.tar.gz (53.4 kB 查看哈希)

已上传 source

内置分布

cddl_gen-0.3.1-py3-none-any.whl (52.8 kB 查看哈希

已上传 py3