Add handling of .size CDDL control operation for byte strings.
This change recognizes parsed CDDL .size operator in AST, e.g. "salt: bytes .size 32" and generates different C++ structs and serialization compared to byte strings with no specified size, e.g. "salt: bytes"
Byte strings that have their size specified are defined as std::array<uint8_t, SIZE> instead of a vector<uint8_t>. Deserialization code requires the byte string to be the exact specified size.
Change-Id: I5cd0846526d7e12f1766c039b0ce4d049d93d09a
Reviewed-on: https://chromium-review.googlesource.com/c/openscreen/+/1537822
Commit-Queue: Max Yakimakha <yakimakha@google.com>
Reviewed-by: Peter Thatcher <pthatcher@google.com>
Reviewed-by: Ryan Keane <rwkeane@google.com>
diff --git a/tools/cddl/codegen.cc b/tools/cddl/codegen.cc
index 09df508..2ed3bc5 100644
--- a/tools/cddl/codegen.cc
+++ b/tools/cddl/codegen.cc
@@ -52,8 +52,15 @@
return "uint64_t";
case CppType::Which::kString:
return "std::string";
- case CppType::Which::kBytes:
- return "std::vector<uint8_t>";
+ case CppType::Which::kBytes: {
+ if (cpp_type.bytes_type.fixed_size) {
+ std::string size_string =
+ std::to_string(cpp_type.bytes_type.fixed_size.value());
+ return "std::array<uint8_t, " + size_string + ">";
+ } else {
+ return "std::vector<uint8_t>";
+ }
+ }
case CppType::Which::kVector: {
std::string element_string =
CppTypeToString(*cpp_type.vector_type.element_type);
@@ -842,8 +849,18 @@
"&length%d));\n",
decoder_depth, temp_length);
dprintf(fd, " }\n");
- dprintf(fd, " %s%sresize(length%d);\n", name.c_str(),
- member_accessor.c_str(), temp_length);
+ if (!cpp_type.bytes_type.fixed_size) {
+ dprintf(fd, " %s%sresize(length%d);\n", name.c_str(),
+ member_accessor.c_str(), temp_length);
+ } else {
+ dprintf(fd, " if (length%d < %d) {\n", temp_length,
+ static_cast<int>(cpp_type.bytes_type.fixed_size.value()));
+ dprintf(fd, " return -CborErrorTooFewItems;\n");
+ dprintf(fd, " } else if (length%d > %d) {\n", temp_length,
+ static_cast<int>(cpp_type.bytes_type.fixed_size.value()));
+ dprintf(fd, " return -CborErrorTooManyItems;\n");
+ dprintf(fd, " }\n");
+ }
dprintf(fd,
" CBOR_RETURN_ON_ERROR(cbor_value_copy_byte_string(&it%d, "
"const_cast<uint8_t*>(%s%sdata()), &length%d, nullptr));\n",
@@ -1236,6 +1253,7 @@
R"(#ifndef %s
#define %s
+#include <array>
#include <cstdint>
#include <string>
#include <vector>
diff --git a/tools/cddl/sema.cc b/tools/cddl/sema.cc
index b7171e1..1b93848 100644
--- a/tools/cddl/sema.cc
+++ b/tools/cddl/sema.cc
@@ -14,10 +14,12 @@
#include <string>
#include <vector>
+#include "third_party/abseil/src/absl/strings/numbers.h"
#include "third_party/abseil/src/absl/strings/string_view.h"
#include "third_party/abseil/src/absl/types/optional.h"
-CddlType::CddlType() : map(nullptr) {}
+CddlType::CddlType()
+ : map(nullptr), op(CddlType::Op::kNone), constraint_type(nullptr) {}
CddlType::~CddlType() {
switch (which) {
case CddlType::Which::kDirectChoice:
@@ -105,6 +107,10 @@
new (&discriminated_union) DiscriminatedUnion();
}
+void CppType::InitBytes() {
+ which = Which::kBytes;
+}
+
void InitString(std::string* s, absl::string_view value) {
new (s) std::string(value);
}
@@ -180,8 +186,112 @@
return nullptr;
}
+CddlType::Op AnalyzeRangeop(const AstNode& rangeop) {
+ if (rangeop.text == "..") {
+ return CddlType::Op::kInclusiveRange;
+ } else if (rangeop.text == "...") {
+ return CddlType::Op::kExclusiveRange;
+ } else {
+ dprintf(STDERR_FILENO, "Unsupported '%s' range operator.\n",
+ rangeop.text.c_str());
+ return CddlType::Op::kNone;
+ }
+}
+
+CddlType::Op AnalyzeCtlop(const AstNode& ctlop) {
+ if (!ctlop.children) {
+ dprintf(STDERR_FILENO, "Missing id for control operator '%s'.\n",
+ ctlop.text.c_str());
+ return CddlType::Op::kNone;
+ }
+ const std::string& id = ctlop.children->text;
+ if (id == "size") {
+ return CddlType::Op::kSize;
+ } else if (id == "bits") {
+ return CddlType::Op::kBits;
+ } else if (id == "regexp") {
+ return CddlType::Op::kRegexp;
+ } else if (id == "cbor") {
+ return CddlType::Op::kCbor;
+ } else if (id == "cborseq") {
+ return CddlType::Op::kCborseq;
+ } else if (id == "within") {
+ return CddlType::Op::kWithin;
+ } else if (id == "and") {
+ return CddlType::Op::kAnd;
+ } else if (id == "lt") {
+ return CddlType::Op::kLess;
+ } else if (id == "le") {
+ return CddlType::Op::kLessOrEqual;
+ } else if (id == "gt") {
+ return CddlType::Op::kGreater;
+ } else if (id == "ge") {
+ return CddlType::Op::kGreaterOrEqual;
+ } else if (id == "eq") {
+ return CddlType::Op::kEqual;
+ } else if (id == "ne") {
+ return CddlType::Op::kNotEqual;
+ } else if (id == "default") {
+ return CddlType::Op::kDefault;
+ } else {
+ dprintf(STDERR_FILENO, "Unsupported '%s' control operator.\n",
+ ctlop.text.c_str());
+ return CddlType::Op::kNone;
+ }
+}
+
+// Produces CddlType by analyzing AST parsed from type1 rule
+// ABNF rule: type1 = type2 [S (rangeop / ctlop) S type2]
CddlType* AnalyzeType1(CddlSymbolTable* table, const AstNode& type1) {
- return AnalyzeType2(table, *type1.children);
+ if (!type1.children) {
+ dprintf(STDERR_FILENO, "Missing type2 in type1 '%s'.\n",
+ type1.text.c_str());
+ return nullptr;
+ }
+ const AstNode& target_type = *type1.children;
+ CddlType* analyzed_type = AnalyzeType2(table, target_type);
+ if (!analyzed_type) {
+ dprintf(STDERR_FILENO, "Invalid type2 '%s' in type1 '%s'.\n",
+ target_type.text.c_str(), type1.text.c_str());
+ return nullptr;
+ }
+ if (!target_type.sibling) {
+ // No optional range or control operator, return type as-is
+ return analyzed_type;
+ }
+ const AstNode& operator_type = *target_type.sibling;
+ CddlType::Op op;
+ if (operator_type.type == AstNode::Type::kRangeop) {
+ op = AnalyzeRangeop(operator_type);
+ } else if (operator_type.type == AstNode::Type::kCtlop) {
+ op = AnalyzeCtlop(operator_type);
+ } else {
+ op = CddlType::Op::kNone;
+ }
+ if (op == CddlType::Op::kNone) {
+ dprintf(STDERR_FILENO,
+ "Unsupported or missing operator '%s' in type1 '%s'.\n",
+ operator_type.text.c_str(), type1.text.c_str());
+ return nullptr;
+ }
+ if (!operator_type.sibling) {
+ dprintf(STDERR_FILENO,
+ "Missing controller type for operator '%s' in type1 '%s'.\n",
+ operator_type.text.c_str(), type1.text.c_str());
+ return nullptr;
+ }
+ const AstNode& controller_type = *operator_type.sibling;
+ CddlType* constraint_type = AnalyzeType2(table, controller_type);
+ if (!constraint_type) {
+ dprintf(STDERR_FILENO,
+ "Invalid controller type '%s' for operator '%s' in type1 '%s'.\n",
+ controller_type.text.c_str(), operator_type.text.c_str(),
+ type1.text.c_str());
+ return nullptr;
+ }
+ analyzed_type->op = op;
+ analyzed_type->constraint_type = constraint_type;
+ return analyzed_type;
}
CddlType* AnalyzeType(CddlSymbolTable* table, const AstNode& type) {
@@ -268,7 +378,6 @@
}
int upper_bound = CddlGroup::Entry::kOccurrenceMaxUnbounded;
-
std::string second_half =
index >= node->text.length() ? "" : node->text.substr(index + 1);
if ((second_half.length() != 1 || second_half.at(0) != '0') &&
@@ -579,7 +688,14 @@
cpp_type->which = CppType::Which::kString;
} else if (type.id == "bytes") {
cpp_type = GetCppType(table, name);
- cpp_type->which = CppType::Which::kBytes;
+ cpp_type->InitBytes();
+ if (type.op == CddlType::Op::kSize) {
+ size_t size = 0;
+ if (!absl::SimpleAtoi(type.constraint_type->value, &size)) {
+ return nullptr;
+ }
+ cpp_type->bytes_type.fixed_size = size;
+ }
} else {
cpp_type = GetCppType(table, type.id);
}
diff --git a/tools/cddl/sema.h b/tools/cddl/sema.h
index 8f5ef6d..38399f6 100644
--- a/tools/cddl/sema.h
+++ b/tools/cddl/sema.h
@@ -30,6 +30,25 @@
kGroupnameChoice,
kTaggedType,
};
+ enum class Op {
+ kNone, // not specified
+ kInclusiveRange, // ..
+ kExclusiveRange, // ...
+ kSize, // .size
+ kBits, // .bits
+ kRegexp, // .regexp
+ kCbor, // .cbor
+ kCborseq, // .cborseq
+ kWithin, // .within
+ kAnd, // .and
+ kLess, // .lt
+ kLessOrEqual, // .lt
+ kGreater, // .gt
+ kGreaterOrEqual, // .ge
+ kEqual, // .eq
+ kNotEqual, // .ne
+ kDefault, // .default
+ };
struct TaggedType {
uint64_t tag_value;
CddlType* type;
@@ -60,6 +79,9 @@
CddlGroup* group_choice;
TaggedType tagged_type;
};
+
+ Op op;
+ CddlType* constraint_type;
};
// Represets a group defined in CDDL.
@@ -206,6 +228,10 @@
std::vector<CppType*> members;
};
+ struct Bytes {
+ absl::optional<size_t> fixed_size;
+ };
+
struct TaggedType {
uint64_t tag;
CppType* real_type;
@@ -218,6 +244,7 @@
void InitEnum();
void InitStruct();
void InitDiscriminatedUnion();
+ void InitBytes();
Which which = Which::kUninitialized;
std::string name;
@@ -227,6 +254,7 @@
Struct struct_type;
CppType* optional_type;
DiscriminatedUnion discriminated_union;
+ Bytes bytes_type;
TaggedType tagged_type;
};
};