From 4d6b760fad979c8c9bcda34f4f68804cb21764ea Mon Sep 17 00:00:00 2001 From: Ajay Panicker Date: Wed, 6 Feb 2019 14:02:36 -0800 Subject: [PATCH] packet: Add the PDL Packet Generator Test: bluetooth_packet_parser_test Change-Id: Ia741e608aebff7b55d005a37e0f0eaeae63767a8 --- gd/Android.bp | 21 + gd/packet/parser/Android.bp | 30 ++ gd/packet/parser/README | 43 ++ gd/packet/parser/declarations.h | 76 +++ gd/packet/parser/enum_def.cc | 42 ++ gd/packet/parser/enum_def.h | 39 ++ gd/packet/parser/enum_gen.cc | 53 ++ gd/packet/parser/enum_gen.h | 33 ++ gd/packet/parser/field_list.h | 210 ++++++++ gd/packet/parser/fields/all_fields.h | 26 + gd/packet/parser/fields/body_field.cc | 54 +++ gd/packet/parser/fields/body_field.h | 44 ++ gd/packet/parser/fields/enum_field.cc | 116 +++++ gd/packet/parser/fields/enum_field.h | 50 ++ gd/packet/parser/fields/fixed_field.cc | 148 ++++++ gd/packet/parser/fields/fixed_field.h | 58 +++ gd/packet/parser/fields/group_field.cc | 73 +++ gd/packet/parser/fields/group_field.h | 54 +++ gd/packet/parser/fields/packet_field.cc | 68 +++ gd/packet/parser/fields/packet_field.h | 97 ++++ gd/packet/parser/fields/payload_field.cc | 128 +++++ gd/packet/parser/fields/payload_field.h | 51 ++ gd/packet/parser/fields/reserved_field.cc | 62 +++ gd/packet/parser/fields/reserved_field.h | 49 ++ gd/packet/parser/fields/scalar_field.cc | 129 +++++ gd/packet/parser/fields/scalar_field.h | 46 ++ gd/packet/parser/fields/size_field.cc | 121 +++++ gd/packet/parser/fields/size_field.h | 52 ++ gd/packet/parser/language_l.ll | 113 +++++ gd/packet/parser/language_y.yy | 520 ++++++++++++++++++++ gd/packet/parser/logging.h | 93 ++++ gd/packet/parser/main.cc | 79 +++ gd/packet/parser/packet_def.cc | 644 +++++++++++++++++++++++++ gd/packet/parser/packet_def.h | 86 ++++ gd/packet/parser/parse_location.h | 31 ++ gd/packet/parser/size.h | 133 +++++ gd/packet/parser/test/Android.bp | 20 + gd/packet/parser/test/generated_packet_test.cc | 115 +++++ gd/packet/parser/test/test_packet.pdl | 40 ++ gd/packet/parser/type_def.h | 33 ++ gd/packet/parser/util.h | 84 ++++ 41 files changed, 3964 insertions(+) create mode 100644 gd/packet/parser/Android.bp create mode 100644 gd/packet/parser/README create mode 100644 gd/packet/parser/declarations.h create mode 100644 gd/packet/parser/enum_def.cc create mode 100644 gd/packet/parser/enum_def.h create mode 100644 gd/packet/parser/enum_gen.cc create mode 100644 gd/packet/parser/enum_gen.h create mode 100644 gd/packet/parser/field_list.h create mode 100644 gd/packet/parser/fields/all_fields.h create mode 100644 gd/packet/parser/fields/body_field.cc create mode 100644 gd/packet/parser/fields/body_field.h create mode 100644 gd/packet/parser/fields/enum_field.cc create mode 100644 gd/packet/parser/fields/enum_field.h create mode 100644 gd/packet/parser/fields/fixed_field.cc create mode 100644 gd/packet/parser/fields/fixed_field.h create mode 100644 gd/packet/parser/fields/group_field.cc create mode 100644 gd/packet/parser/fields/group_field.h create mode 100644 gd/packet/parser/fields/packet_field.cc create mode 100644 gd/packet/parser/fields/packet_field.h create mode 100644 gd/packet/parser/fields/payload_field.cc create mode 100644 gd/packet/parser/fields/payload_field.h create mode 100644 gd/packet/parser/fields/reserved_field.cc create mode 100644 gd/packet/parser/fields/reserved_field.h create mode 100644 gd/packet/parser/fields/scalar_field.cc create mode 100644 gd/packet/parser/fields/scalar_field.h create mode 100644 gd/packet/parser/fields/size_field.cc create mode 100644 gd/packet/parser/fields/size_field.h create mode 100644 gd/packet/parser/language_l.ll create mode 100644 gd/packet/parser/language_y.yy create mode 100644 gd/packet/parser/logging.h create mode 100644 gd/packet/parser/main.cc create mode 100644 gd/packet/parser/packet_def.cc create mode 100644 gd/packet/parser/packet_def.h create mode 100644 gd/packet/parser/parse_location.h create mode 100644 gd/packet/parser/size.h create mode 100644 gd/packet/parser/test/Android.bp create mode 100644 gd/packet/parser/test/generated_packet_test.cc create mode 100644 gd/packet/parser/test/test_packet.pdl create mode 100644 gd/packet/parser/type_def.h create mode 100644 gd/packet/parser/util.h diff --git a/gd/Android.bp b/gd/Android.bp index 58add5a1e..4bbf5b796 100644 --- a/gd/Android.bp +++ b/gd/Android.bp @@ -113,6 +113,27 @@ cc_test { }, } +cc_test { + name: "bluetooth_packet_parser_test", + test_suites: ["device-tests"], + defaults: [ + "gd_defaults", + "gd_clang_coverage_bin", + ], + host_supported: true, + srcs: [ + ":BluetoothPacketSources", + ":BluetoothPacketParserTestPacketTestSources", + ], + generated_headers : [ + "BluetoothPacketParserTestPacketPdlGen_h", + ], + sanitize: { + address: true, + cfi: true, + }, +} + cc_benchmark { name: "bluetooth_benchmark_gd", defaults: ["gd_defaults"], diff --git a/gd/packet/parser/Android.bp b/gd/packet/parser/Android.bp new file mode 100644 index 000000000..5447347ac --- /dev/null +++ b/gd/packet/parser/Android.bp @@ -0,0 +1,30 @@ +cc_binary_host { + name: "bluetooth_packetgen", + srcs: [ + "fields/body_field.cc", + "fields/enum_field.cc", + "fields/fixed_field.cc", + "fields/group_field.cc", + "fields/packet_field.cc", + "fields/payload_field.cc", + "fields/reserved_field.cc", + "fields/scalar_field.cc", + "fields/size_field.cc", + "enum_def.cc", + "enum_gen.cc", + "packet_def.cc", + "main.cc", + "language_y.yy", + "language_l.ll", + ], + + cppflags: [ + "-Wno-implicit-fallthrough", + "-fno-exceptions", + "-O0", + ], + ldflags: [ + "-fuse-ld=ld", + "-O0", + ], +} diff --git a/gd/packet/parser/README b/gd/packet/parser/README new file mode 100644 index 000000000..09e78dd4b --- /dev/null +++ b/gd/packet/parser/README @@ -0,0 +1,43 @@ +This file just contains some notes about the design and usage of the PDL language. + +------- + TERMS +------- +.pdl + The file type that defines packet definitions. You may think of each pdl file + as its own translation unit. + +Packet Views and Builders + Generated from a packet definition. Views are used to validate packets and + extract the fields that are defined in the pdl file. Builders check the input + arguments and can be serialized. + +------------- + LIMITATIONS +------------- + - Size fields for a variable length field MUST come before the definition + of said field. + + - Payload fields must be byte-aligned unless they have an unknown size. + Body fields are allowed to not be byte aligned. + + - No conditionals + + - Can not have to fields with the same name anywhere in the in an inheritence chain + + - Can't handle size for Body type fields yet since they might not be byte aligned. + +------- + NOTES +------- +All Field names should be in CamelCase. Field names matching type names is supported. + +The payload keyword generates a getter but body doesn't. Therefore, a payload must be byte aligned. +Supports constraints on grandparents +Supports multiple constraints +Every field handles its own generation. +One pdl file will result in one header file with all the packets + +Things to cover - + Constraints + Inheritence vs Contains diff --git a/gd/packet/parser/declarations.h b/gd/packet/parser/declarations.h new file mode 100644 index 000000000..8471c25f8 --- /dev/null +++ b/gd/packet/parser/declarations.h @@ -0,0 +1,76 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#include "enum_def.h" +#include "enum_gen.h" +#include "packet_def.h" + +class Declarations { + public: + void AddEnumDef(std::string name, EnumDef def) { + enum_defs_.insert(std::pair(name, def)); + enum_defs_queue_.push_back(std::pair(name, def)); + } + + EnumDef* GetEnumDef(const std::string& name) { + auto it = enum_defs_.find(name); + if (it == enum_defs_.end()) { + return nullptr; + } + + return &(it->second); + } + + void AddPacketDef(std::string name, PacketDef def) { + packet_defs_.insert(std::pair(name, def)); + packet_defs_queue_.push_back(std::pair(name, def)); + } + + PacketDef* GetPacketDef(const std::string& name) { + auto it = packet_defs_.find(name); + if (it == packet_defs_.end()) { + return nullptr; + } + + return &(it->second); + } + + void AddGroupDef(std::string name, FieldList* group_def) { + group_defs_.insert(std::pair(name, group_def)); + } + + FieldList* GetGroupDef(std::string name) { + if (group_defs_.find(name) == group_defs_.end()) { + return nullptr; + } + + return group_defs_.at(name); + } + + std::map group_defs_; + + std::map enum_defs_; + std::deque> enum_defs_queue_; + std::map packet_defs_; + std::deque> packet_defs_queue_; + bool is_little_endian; +}; diff --git a/gd/packet/parser/enum_def.cc b/gd/packet/parser/enum_def.cc new file mode 100644 index 000000000..e66797e39 --- /dev/null +++ b/gd/packet/parser/enum_def.cc @@ -0,0 +1,42 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "enum_def.h" + +#include +#include + +#include "util.h" + +EnumDef::EnumDef(std::string name, int size) : name_(name), size_(size){}; + +void EnumDef::AddEntry(std::string name, uint32_t value) { + if (value > util::GetMaxValueForBits(size_)) { + std::cerr << __func__ << ": Value provided is greater than max possible value for enum. " << name_ << "\n"; + abort(); + } + + constants_.insert(std::pair(value, name)); + entries_.insert(name); +} + +bool EnumDef::HasEntry(std::string name) const { + return entries_.count(name) != 0; +} + +std::string EnumDef::GetTypeName() const { + return name_; +} diff --git a/gd/packet/parser/enum_def.h b/gd/packet/parser/enum_def.h new file mode 100644 index 000000000..ef711ff80 --- /dev/null +++ b/gd/packet/parser/enum_def.h @@ -0,0 +1,39 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +// Holds the definition of an enum. +class EnumDef { + public: + EnumDef(std::string name, int size); + + void AddEntry(std::string name, uint32_t value); + + bool HasEntry(std::string name) const; + + std::string GetTypeName() const; + + // data + const std::string name_; + int size_; + std::map constants_; + std::set entries_; +}; diff --git a/gd/packet/parser/enum_gen.cc b/gd/packet/parser/enum_gen.cc new file mode 100644 index 000000000..769a43a90 --- /dev/null +++ b/gd/packet/parser/enum_gen.cc @@ -0,0 +1,53 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "enum_gen.h" + +#include + +#include "util.h" + +EnumGen::EnumGen(EnumDef e) : e_(e){}; + +void EnumGen::GenDefinition(std::ostream& stream) { + stream << "enum class "; + stream << e_.name_; + stream << " : " << util::GetTypeForSize(e_.size_); + stream << " {"; + for (const auto& pair : e_.constants_) { + stream << pair.second << " = 0x" << std::hex << pair.first << std::dec << ","; + } + stream << "};\n"; +} + +void EnumGen::GenLogging(std::ostream& stream) { + // Print out the switch statement that converts all the constants to strings. + stream << "inline std::string " << e_.name_ << "Text(const " << e_.name_ << "& param) {"; + stream << "switch (param) {"; + for (const auto& pair : e_.constants_) { + stream << "case " << e_.name_ << "::" << pair.second << ":"; + stream << " return \"" << pair.second << "\";"; + } + stream << "default:"; + stream << " return std::string(\"Unknown " << e_.name_ << ": \") + std::to_string(static_cast(param));"; + stream << "}"; + stream << "}\n\n"; + + // Print out the stream operator so that the constant can be written to streams. + stream << "inline std::ostream& operator<<(std::ostream& os, const " << e_.name_ << "& param) {"; + stream << " return os << " << e_.name_ << "Text(param);"; + stream << "}\n"; +} diff --git a/gd/packet/parser/enum_gen.h b/gd/packet/parser/enum_gen.h new file mode 100644 index 000000000..f3483dda4 --- /dev/null +++ b/gd/packet/parser/enum_gen.h @@ -0,0 +1,33 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include "enum_def.h" + +// Generates the C++ code for an enum. +class EnumGen { + public: + EnumGen(EnumDef e); + + void GenDefinition(std::ostream& stream); + + void GenLogging(std::ostream& stream); + + EnumDef e_; +}; diff --git a/gd/packet/parser/field_list.h b/gd/packet/parser/field_list.h new file mode 100644 index 000000000..1cd7df4a9 --- /dev/null +++ b/gd/packet/parser/field_list.h @@ -0,0 +1,210 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#include "fields/packet_field.h" + +using FieldListIterator = std::vector::const_iterator; +using ReverseFieldListIterator = std::vector::const_reverse_iterator; + +class FieldList { + public: + FieldList() = default; + + FieldList(std::vector fields) { + for (PacketField* field : fields) { + AppendField(field); + } + } + + template + FieldList(Iterator begin, Iterator end) { + while (begin != end) { + AppendField(*begin); + begin++; + } + } + + PacketField* operator[](int index) const { + return field_list_[index]; + } + + PacketField* GetField(std::string field_name) const { + auto it = field_map_.find(field_name); + if (it == field_map_.end()) { + return nullptr; + } + + return it->second; + } + + void AppendField(PacketField* field) { + AddField(field); + field_list_.push_back(field); + } + + void PrependField(PacketField* field) { + AddField(field); + field_list_.insert(field_list_.begin(), field); + } + + FieldList GetFieldsBeforePayloadOrBody() const { + FieldList ret; + for (auto it = begin(); it != end(); it++) { + const auto& field = *it; + if (field->GetFieldType() == PacketField::Type::PAYLOAD || field->GetFieldType() == PacketField::Type::BODY) { + break; + } + ret.AppendField(*it); + } + + return ret; + } + + FieldList GetFieldsAfterPayloadOrBody() const { + FieldListIterator it; + for (it = begin(); it != end(); it++) { + const auto& field = *it; + if (field->GetFieldType() == PacketField::Type::PAYLOAD || field->GetFieldType() == PacketField::Type::BODY) { + // Increment it once to get first field after payload/body. + it++; + break; + } + } + + return FieldList(it, end()); + } + + FieldList GetFieldsWithTypes(std::set field_types) const { + FieldList ret; + + for (const auto& field : field_list_) { + if (field_types.find(field->GetFieldType()) != field_types.end()) { + ret.AppendField(field); + } + } + + return ret; + } + + FieldList GetFieldsWithoutTypes(std::set field_types) const { + FieldList ret; + + for (const auto& field : field_list_) { + if (field_types.find(field->GetFieldType()) == field_types.end()) { + ret.AppendField(field); + } + } + + return ret; + } + + // Appends header fields of param to header fields of the current and + // prepends footer fields of the param to footer fields of the current. + // Ex. Assuming field_list_X has the layout: + // field_list_X_header + // payload/body + // field_list_X_footer + // The call to field_list_1.Merge(field_list_2) would result in + // field_list_1_header + // field_list_2_header + // payload/body (uses whatever was in field_list_2) + // field_list_2_footer + // field_list_1_footer + FieldList Merge(FieldList nested) const { + FieldList ret; + + for (const auto& field : GetFieldsBeforePayloadOrBody()) { + ret.AppendField(field); + } + + for (const auto& field : nested) { + ret.AppendField(field); + } + + for (const auto& field : GetFieldsAfterPayloadOrBody()) { + ret.AppendField(field); + } + + return ret; + } + + bool HasPayloadOrBody() const { + return has_payload_ || has_body_; + } + + bool HasPayload() const { + return has_payload_; + } + + bool HasBody() const { + return has_body_; + } + + FieldListIterator begin() const { + return field_list_.begin(); + } + + FieldListIterator end() const { + return field_list_.end(); + } + + ReverseFieldListIterator rbegin() const { + return field_list_.rbegin(); + } + + ReverseFieldListIterator rend() const { + return field_list_.rend(); + } + + size_t size() const { + return field_list_.size(); + } + + private: + void AddField(PacketField* field) { + if (field_map_.find(field->GetName()) != field_map_.end()) { + ERROR(field) << "Field with name \"" << field->GetName() << "\" was " + << "previously defined.\n"; + } + + if (field->GetFieldType() == PacketField::Type::PAYLOAD) { + if (HasBody()) { + ERROR(field) << "Can not have payload field in packet that already has a body."; + } + has_payload_ = true; + } + + if (field->GetFieldType() == PacketField::Type::BODY) { + if (HasPayload()) { + ERROR(field) << "Can not have body field in packet that already has a payload."; + } + has_body_ = true; + } + + field_map_.insert(std::pair(field->GetName(), field)); + } + + std::vector field_list_; + std::map field_map_; + bool has_payload_ = false; + bool has_body_ = false; +}; diff --git a/gd/packet/parser/fields/all_fields.h b/gd/packet/parser/fields/all_fields.h new file mode 100644 index 000000000..0b1126ec1 --- /dev/null +++ b/gd/packet/parser/fields/all_fields.h @@ -0,0 +1,26 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "fields/body_field.h" +#include "fields/enum_field.h" +#include "fields/fixed_field.h" +#include "fields/group_field.h" +#include "fields/payload_field.h" +#include "fields/reserved_field.h" +#include "fields/scalar_field.h" +#include "fields/size_field.h" diff --git a/gd/packet/parser/fields/body_field.cc b/gd/packet/parser/fields/body_field.cc new file mode 100644 index 000000000..9a0fe2563 --- /dev/null +++ b/gd/packet/parser/fields/body_field.cc @@ -0,0 +1,54 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "fields/body_field.h" + +BodyField::BodyField(ParseLocation loc) : PacketField(loc, "Body") {} + +PacketField::Type BodyField::GetFieldType() const { + return PacketField::Type::BODY; +} + +Size BodyField::GetSize() const { + return Size(0); +} + +std::string BodyField::GetType() const { + ERROR(this) << "No need to know the type of a body field."; + return "BodyType"; +} + +void BodyField::GenGetter(std::ostream&, Size, Size) const {} + +bool BodyField::GenBuilderParameter(std::ostream&) const { + return false; +} + +bool BodyField::HasParameterValidator() const { + return false; +} + +void BodyField::GenParameterValidator(std::ostream&) const { + // There is no validation needed for a payload +} + +void BodyField::GenInserter(std::ostream&) const { + // Do nothing +} + +void BodyField::GenValidator(std::ostream&) const { + // Do nothing +} diff --git a/gd/packet/parser/fields/body_field.h b/gd/packet/parser/fields/body_field.h new file mode 100644 index 000000000..9cc2301ed --- /dev/null +++ b/gd/packet/parser/fields/body_field.h @@ -0,0 +1,44 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "fields/packet_field.h" +#include "fields/size_field.h" +#include "parse_location.h" + +class BodyField : public PacketField { + public: + BodyField(ParseLocation loc); + + virtual PacketField::Type GetFieldType() const override; + + virtual Size GetSize() const override; + + virtual std::string GetType() const override; + + virtual void GenGetter(std::ostream&, Size, Size) const override; + + virtual bool GenBuilderParameter(std::ostream&) const override; + + virtual bool HasParameterValidator() const override; + + virtual void GenParameterValidator(std::ostream&) const override; + + virtual void GenInserter(std::ostream&) const override; + + virtual void GenValidator(std::ostream&) const override; +}; diff --git a/gd/packet/parser/fields/enum_field.cc b/gd/packet/parser/fields/enum_field.cc new file mode 100644 index 000000000..9e95a561a --- /dev/null +++ b/gd/packet/parser/fields/enum_field.cc @@ -0,0 +1,116 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "fields/enum_field.h" + +#include "util.h" + +EnumField::EnumField(std::string name, EnumDef enum_def, std::string value, ParseLocation loc) + : PacketField(loc, name), enum_def_(enum_def), value_(value) {} + +EnumDef EnumField::GetEnumDef() { + return enum_def_; +} + +PacketField::Type EnumField::GetFieldType() const { + return PacketField::Type::ENUM; +} + +Size EnumField::GetSize() const { + return enum_def_.size_; +} + +std::string EnumField::GetType() const { + return enum_def_.name_; +} + +void EnumField::GenGetter(std::ostream& s, Size start_offset, Size end_offset) const { + // Write the Getter Function Definition + s << GetType(); + s << " Get" << GetName() << "() const {"; + + // Write the Getter Function Body + int num_leading_bits = 0; + int field_size = enum_def_.size_; + + // Start from the beginning, if possible. + if (!start_offset.empty()) { + num_leading_bits = start_offset.bits() % 8; + s << "auto it = begin()" + << " + " << start_offset.bytes() << " + (" << start_offset.dynamic_string() << ");"; + } else if (!end_offset.empty()) { + int offset_from_end = end_offset.bits() + field_size; + num_leading_bits = 8 - (offset_from_end % 8); + int byte_offset = (7 + offset_from_end) / 8; + s << "auto it = end() - " << byte_offset << " - (" << end_offset.dynamic_string() << ");"; + } else { + ERROR(this) << "Ambiguous offset for field."; + } + + // We don't need any masking, just return the extracted value. + if (num_leading_bits == 0 && util::RoundSizeUp(field_size) == field_size) { + s << "return it.extract<" << GetType() << ">();"; + s << "}\n"; + return; + } + + // Extract the correct number of bytes. The return type could be different + // from the extract type if an earlier field causes the beginning of the + // current field to start in the middle of a byte. + std::string extract_type = util::GetTypeForSize(field_size + num_leading_bits); + s << "auto value = it.extract<" << extract_type << ">();"; + + // Right shift the result if necessary. + int shift_amount = num_leading_bits; + if (shift_amount != 0) { + s << "value >>= " << shift_amount << ";"; + } + + // Mask the result if necessary. + if (util::RoundSizeUp(field_size) != field_size) { + uint64_t mask = 0; + for (int i = 0; i < field_size; i++) { + mask <<= 1; + mask |= 1; + } + s << "value &= 0x" << std::hex << mask << std::dec << ";"; + } + + s << "return static_cast<" << GetType() << ">(value);"; + s << "}\n"; +} + +bool EnumField::GenBuilderParameter(std::ostream& s) const { + s << GetType() << " " << util::CamelCaseToUnderScore(GetName()); + return true; +} + +bool EnumField::HasParameterValidator() const { + return false; +} + +void EnumField::GenParameterValidator(std::ostream&) const { + // Validated at compile time. +} + +void EnumField::GenInserter(std::ostream& s) const { + s << "insert(static_cast<" << util::GetTypeForSize(GetSize().bits()) << ">(" << util::CamelCaseToUnderScore(GetName()) + << "_), i, " << GetSize().bits() << ");"; +} + +void EnumField::GenValidator(std::ostream&) const { + // Do nothing +} diff --git a/gd/packet/parser/fields/enum_field.h b/gd/packet/parser/fields/enum_field.h new file mode 100644 index 000000000..2ab77f1a5 --- /dev/null +++ b/gd/packet/parser/fields/enum_field.h @@ -0,0 +1,50 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "enum_def.h" +#include "fields/packet_field.h" +#include "parse_location.h" + +class EnumField : public PacketField { + public: + EnumField(std::string name, EnumDef enum_def, std::string value, ParseLocation loc); + + EnumDef GetEnumDef(); + + virtual PacketField::Type GetFieldType() const override; + + virtual Size GetSize() const override; + + virtual std::string GetType() const override; + + virtual void GenGetter(std::ostream& s, Size start_offset, Size end_offset) const override; + + virtual bool GenBuilderParameter(std::ostream& s) const override; + + virtual bool HasParameterValidator() const override; + + virtual void GenParameterValidator(std::ostream&) const override; + + virtual void GenInserter(std::ostream& s) const override; + + virtual void GenValidator(std::ostream&) const override; + + private: + EnumDef enum_def_; + std::string value_; +}; diff --git a/gd/packet/parser/fields/fixed_field.cc b/gd/packet/parser/fields/fixed_field.cc new file mode 100644 index 000000000..801975320 --- /dev/null +++ b/gd/packet/parser/fields/fixed_field.cc @@ -0,0 +1,148 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "fields/fixed_field.h" +#include "util.h" + +int FixedField::unique_id_ = 0; + +FixedField::FixedField(int size, int64_t value, ParseLocation loc) + : PacketField(loc, "FixedScalar" + std::to_string(unique_id_++)), type_(Type::FIXED_SCALAR), size_(size), + value_(value) {} + +FixedField::FixedField(EnumDef* enum_def, std::string value, ParseLocation loc) + : PacketField(loc, "FixedScalar" + std::to_string(unique_id_++)), type_(Type::FIXED_ENUM), enum_(enum_def), + value_(value) {} + +PacketField::Type FixedField::GetFieldType() const { + return type_; +} + +Size FixedField::GetSize() const { + if (type_ == PacketField::Type::FIXED_SCALAR) { + return size_; + } + + return enum_->size_; +} + +std::string FixedField::GetType() const { + if (type_ == PacketField::Type::FIXED_SCALAR) { + return util::GetTypeForSize(size_); + } + + return enum_->name_; +} + +void FixedField::GenGetter(std::ostream& s, Size start_offset, Size end_offset) const { + // Write the Getter Function Definiton + s << GetType(); + s << " Get" << GetName() << "() const {"; + + // Write the Getter Function Body + int num_leading_bits = 0; + int field_size = GetSize().bits(); + + // Handle if to start the iterator at begin or end. + if (!start_offset.empty()) { + // Default to start if available. + num_leading_bits = start_offset.bits() % 8; + s << "auto it = begin() + " << start_offset.bytes() << " + (" << start_offset.dynamic_string() << ");"; + } else if (!end_offset.empty()) { + int offset_from_end = end_offset.bits() + field_size; + num_leading_bits = 8 - (offset_from_end % 8); + // Add 7 so it rounds up + int byte_offset = (7 + offset_from_end) / 8; + s << "auto it = end() - " << byte_offset << " - (" << end_offset.dynamic_string() << ");"; + } else { + ERROR(this) << "Ambiguous offset for field.\n"; + } + + // We don't need any masking, just return the extracted value. + if (num_leading_bits == 0 && util::RoundSizeUp(field_size) == field_size) { + s << "return it.extract<" << GetType() << ">();"; + s << "}\n"; + return; + } + + // Extract the correct number of bytes. The return type could be different + // from the extract type if an earlier field causes the beginning of the + // current field to start in the middle of a byte. + std::string extract_type = util::GetTypeForSize(field_size + num_leading_bits); + s << "auto value = it.extract<" << extract_type << ">();"; + + // Right shift to remove leading bits. + if (num_leading_bits != 0) { + s << "value >>= " << num_leading_bits << ";"; + } + + // Mask the result if necessary. + if (util::RoundSizeUp(field_size) != field_size) { + uint64_t mask = 0; + for (int i = 0; i < field_size; i++) { + mask <<= 1; + mask |= 1; + } + s << "value &= 0x" << std::hex << mask << std::dec << ";"; + } + + // Cast the result if necessary. + if (extract_type != GetType()) { + s << "return static_cast<" << GetType() << ">(value);"; + } else { + s << "return value;"; + } + s << "}\n"; +} + +bool FixedField::GenBuilderParameter(std::ostream&) const { + // No parameter needed for a fixed field. + return false; +} + +bool FixedField::HasParameterValidator() const { + return false; +} + +void FixedField::GenParameterValidator(std::ostream&) const { + // No parameter validator needed for a fixed field. +} + +void FixedField::GenInserter(std::ostream& s) const { + s << "insert("; + if (type_ == PacketField::Type::FIXED_SCALAR) { + GenValue(s); + } else { + s << "static_cast<" << util::GetTypeForSize(GetSize().bits()) << ">("; + GenValue(s); + s << ")"; + } + s << ", i , " << GetSize().bits() << ");"; +} + +void FixedField::GenValidator(std::ostream& s) const { + s << "if (Get" << GetName() << "() != "; + GenValue(s); + s << ") return false;"; +} + +void FixedField::GenValue(std::ostream& s) const { + if (type_ == PacketField::Type::FIXED_SCALAR) { + s << std::get(value_); + } else { + s << enum_->name_ << "::" << std::get(value_); + } +} diff --git a/gd/packet/parser/fields/fixed_field.h b/gd/packet/parser/fields/fixed_field.h new file mode 100644 index 000000000..f91e50c74 --- /dev/null +++ b/gd/packet/parser/fields/fixed_field.h @@ -0,0 +1,58 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include "enum_def.h" +#include "fields/packet_field.h" +#include "parse_location.h" + +class FixedField : public PacketField { + public: + FixedField(int size, int64_t value, ParseLocation loc); + + FixedField(EnumDef* enum_def, std::string value, ParseLocation loc); + + virtual PacketField::Type GetFieldType() const override; + + virtual Size GetSize() const override; + + virtual std::string GetType() const override; + + virtual void GenGetter(std::ostream& s, Size start_offset, Size end_offset) const override; + + virtual bool GenBuilderParameter(std::ostream&) const override; + + virtual bool HasParameterValidator() const override; + + virtual void GenParameterValidator(std::ostream&) const override; + + virtual void GenInserter(std::ostream& s) const override; + + virtual void GenValidator(std::ostream& s) const override; + + private: + void GenValue(std::ostream& s) const; + + PacketField::Type type_; + int size_; + EnumDef* enum_; + std::variant value_; + + static int unique_id_; +}; diff --git a/gd/packet/parser/fields/group_field.cc b/gd/packet/parser/fields/group_field.cc new file mode 100644 index 000000000..7e4cabac8 --- /dev/null +++ b/gd/packet/parser/fields/group_field.cc @@ -0,0 +1,73 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "fields/group_field.h" + +GroupField::GroupField(ParseLocation loc, std::list* fields) + : PacketField(loc, "Groups have no name"), fields_(fields) {} + +GroupField::~GroupField() { + delete fields_; +} + +PacketField::Type GroupField::GetFieldType() const { + return PacketField::Type::GROUP; +} + +std::string GroupField::GetName() const { + ERROR(this) << "GetName should never be called."; + return ""; +} + +Size GroupField::GetSize() const { + ERROR(this) << "GetSize should never be called."; + return Size(); +} + +std::string GroupField::GetType() const { + ERROR(this) << "GetType should never be called."; + return ""; +} + +void GroupField::GenGetter(std::ostream&, Size, Size) const { + ERROR(this) << "GenGetter should never be called."; +} + +bool GroupField::GenBuilderParameter(std::ostream&) const { + ERROR(this) << "GenBuilderParameter should never be called"; + return false; +} + +bool GroupField::HasParameterValidator() const { + ERROR(this) << "HasParameterValidator should never be called"; + return false; +} + +void GroupField::GenParameterValidator(std::ostream&) const { + ERROR(this) << "Not implemented"; +} + +void GroupField::GenInserter(std::ostream&) const { + ERROR(this) << "GenInserter should never be called."; +} + +void GroupField::GenValidator(std::ostream&) const { + ERROR(this) << "GenValidator should never be called."; +} + +const std::list* GroupField::GetFields() const { + return fields_; +} diff --git a/gd/packet/parser/fields/group_field.h b/gd/packet/parser/fields/group_field.h new file mode 100644 index 000000000..73e7e35f3 --- /dev/null +++ b/gd/packet/parser/fields/group_field.h @@ -0,0 +1,54 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include "fields/packet_field.h" +#include "parse_location.h" + +class GroupField : public PacketField { + public: + GroupField(ParseLocation loc, std::list* fields); + + ~GroupField(); + + virtual PacketField::Type GetFieldType() const override; + + virtual std::string GetName() const override; + + virtual Size GetSize() const override; + + virtual std::string GetType() const override; + + virtual void GenGetter(std::ostream&, Size, Size) const override; + + virtual bool GenBuilderParameter(std::ostream&) const override; + + virtual bool HasParameterValidator() const override; + + virtual void GenParameterValidator(std::ostream&) const override; + + virtual void GenInserter(std::ostream&) const override; + + virtual void GenValidator(std::ostream&) const override; + + const std::list* GetFields() const; + + private: + std::list* fields_; +}; diff --git a/gd/packet/parser/fields/packet_field.cc b/gd/packet/parser/fields/packet_field.cc new file mode 100644 index 000000000..d6e1af5c8 --- /dev/null +++ b/gd/packet/parser/fields/packet_field.cc @@ -0,0 +1,68 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "fields/packet_field.h" + +PacketField::PacketField(ParseLocation loc, std::string name) : loc_(loc), name_(name) {} + +std::string PacketField::GetDebugName() const { + std::string ret = ""; + switch (GetFieldType()) { + case Type::GROUP: + ret = "GROUP"; + break; + case Type::FIXED_SCALAR: + ret = "FIXED SCALAR"; + break; + case Type::FIXED_ENUM: + ret = "FIXED ENUM"; + break; + case Type::RESERVED_SCALAR: + ret = "RESERVED SCALAR"; + break; + case Type::SCALAR: + ret = "SCALAR"; + break; + case Type::ENUM: + ret = "ENUM"; + break; + case Type::SIZE: + ret = "SIZE"; + break; + case Type::COUNT: + ret = "COUNT"; + break; + case Type::BODY: + ret = "BODY"; + break; + case Type::PAYLOAD: + ret = "PAYLOAD"; + break; + default: + std::cerr << "UNKNOWN DEBUG NAME TYPE\n"; + abort(); + } + + return "Field{Type:" + ret + ", Name:" + GetName() + "}"; +} + +ParseLocation PacketField::GetLocation() const { + return loc_; +} + +std::string PacketField::GetName() const { + return name_; +} diff --git a/gd/packet/parser/fields/packet_field.h b/gd/packet/parser/fields/packet_field.h new file mode 100644 index 000000000..f8d175da8 --- /dev/null +++ b/gd/packet/parser/fields/packet_field.h @@ -0,0 +1,97 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include "logging.h" +#include "parse_location.h" +#include "size.h" + +// The base field that every packet needs to inherit from. +class PacketField : public Loggable { + public: + virtual ~PacketField() = default; + + PacketField(ParseLocation loc, std::string name); + + enum class Type { + GROUP, + FIXED_SCALAR, + FIXED_ENUM, + RESERVED_SCALAR, + SCALAR, + ENUM, + SIZE, + COUNT, + BODY, + PAYLOAD, + }; + + // Get the field type for the field. + virtual Type GetFieldType() const = 0; + + // Returns the size of the field in bits and a string that evaluates into + // bytes for dynamically sized arrays. + virtual Size GetSize() const = 0; + + // Get the type of the field to be used in the builders constructor and + // variables. + virtual std::string GetType() const = 0; + + // Get parser getter definition. Start_offset points to the first bit of the + // field. end_offset is the first bit after the field. If an offset is empty + // that means that there was a field with an unknown size when trying to + // calculate the offset. + virtual void GenGetter(std::ostream& s, Size start_offset, Size end_offset) const = 0; + + // Generate the parameter for Create(), return true if a parameter was added. + virtual bool GenBuilderParameter(std::ostream& s) const = 0; + + // Returns whether or not the field must be validated. + virtual bool HasParameterValidator() const = 0; + + // Fail if the value doesn't fit in the field. + virtual void GenParameterValidator(std::ostream& s) const = 0; + + // Generate the inserter for pushing the data in the builder. + virtual void GenInserter(std::ostream& s) const = 0; + + // Generate the validator for a field for the IsValid() function. + // + // The way this function works is by assuming that there is an iterator |it| + // that was defined earlier. The implementer of the function will then move + // it forward based on the dynamic size of the field and then check to see if + // its past the end of the packet. + // It should be unused for fixed size fields unless special consideration is + // needed. This is because all fixed size fields are tallied together with + // GetSize() and used as an initial offset. One special consideration is for + // enums where instead of checking if they can be read, they are checked to + // see if they contain the correct value. + virtual void GenValidator(std::ostream& s) const = 0; + + std::string GetDebugName() const override; + + ParseLocation GetLocation() const override; + + virtual std::string GetName() const; + + private: + ParseLocation loc_; + std::string name_; +}; diff --git a/gd/packet/parser/fields/payload_field.cc b/gd/packet/parser/fields/payload_field.cc new file mode 100644 index 000000000..fffc50610 --- /dev/null +++ b/gd/packet/parser/fields/payload_field.cc @@ -0,0 +1,128 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "fields/payload_field.h" +#include "util.h" + +PayloadField::PayloadField(std::string modifier, ParseLocation loc) + : PacketField(loc, "Payload"), size_field_(nullptr), size_modifier_(modifier) {} + +void PayloadField::SetSizeField(const SizeField* size_field) { + if (size_field_ != nullptr) { + ERROR(this, size_field_, size_field) << "The size field for the payload has already been assigned."; + } + + if (size_field->GetFieldType() == PacketField::Type::COUNT) { + ERROR(this, size_field) << "Can not use count field to describe a payload."; + } + + size_field_ = size_field; +} + +PacketField::Type PayloadField::GetFieldType() const { + return PacketField::Type::PAYLOAD; +} + +Size PayloadField::GetSize() const { + if (size_field_ == nullptr) { + // Require a size field if there is a modifier. + if (!size_modifier_.empty()) { + ERROR(this) << "Missing size field for payload with size modifier."; + } + + return Size(); + } + + std::string dynamic_size = "Get" + size_field_->GetName() + "()"; + if (!size_modifier_.empty()) { + dynamic_size += size_modifier_; + } + + return dynamic_size; +} + +std::string PayloadField::GetType() const { + return "PacketView"; +} + +void PayloadField::GenGetter(std::ostream& s, Size start_offset, Size end_offset) const { + // Write the Getter Function Body + if (start_offset.empty()) { + ERROR(this) << "Can not have a payload that has an ambiguous start offset. " + << "Is there a field with an unknown length before the " + << "payload?\n"; + } + + if (start_offset.bits() % 8 != 0 && !GetSize().empty()) { + ERROR(this) << "Can not have a sized payload field " + << "at a non byte-aligned offset.\n"; + } + + if (GetSize().empty() && end_offset.empty()) { + ERROR(this) << "Ambiguous end offset for payload with no defined size."; + } + + s << "PacketView GetPayload() {"; + + s << "size_t payload_begin = " << start_offset.bits() / 8 << " + (" << start_offset.dynamic_string() << ");"; + + // If the payload is sized, use the size + payload_begin for payload_end, otherwise use the end_offset. + if (!GetSize().empty()) { + // If the size isn't empty then it must have a dynamic string only. + s << "size_t payload_end = payload_begin + (" << GetSize().dynamic_string() << ");"; + } else { + s << "size_t payload_end = size() - " << end_offset.bits() / 8 << " - (" << end_offset.dynamic_string() << ");"; + } + + s << "return GetLittleEndianSubview(payload_begin, payload_end);"; + s << "}\n\n"; + + s << "PacketView GetPayloadBigEndian() {"; + + s << "size_t payload_begin = " << start_offset.bits() / 8 << " + (" << start_offset.dynamic_string() << ");"; + + // If the payload is sized, use the size + payload_begin for payload_end, otherwise use the end_offset. + if (!GetSize().empty()) { + // If the size isn't empty then it must have a dynamic string only. + s << "size_t payload_end = payload_begin + (" << GetSize().dynamic_string() << ");"; + } else { + s << "size_t payload_end = size() - " << end_offset.bits() / 8 << " - (" << end_offset.dynamic_string() << ");"; + } + + s << "return GetBigEndianSubview(payload_begin, payload_end);"; + s << "}\n"; +} + +bool PayloadField::GenBuilderParameter(std::ostream& s) const { + s << "std::unique_ptr " << util::CamelCaseToUnderScore(GetName()); + return true; +} + +bool PayloadField::HasParameterValidator() const { + return false; +} + +void PayloadField::GenParameterValidator(std::ostream&) const { + // There is no validation needed for a payload +} + +void PayloadField::GenInserter(std::ostream&) const { + ERROR() << __func__ << " Should never be called."; +} + +void PayloadField::GenValidator(std::ostream&) const { + // Do nothing +} diff --git a/gd/packet/parser/fields/payload_field.h b/gd/packet/parser/fields/payload_field.h new file mode 100644 index 000000000..8787b4149 --- /dev/null +++ b/gd/packet/parser/fields/payload_field.h @@ -0,0 +1,51 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "fields/packet_field.h" +#include "fields/size_field.h" +#include "parse_location.h" + +class PayloadField : public PacketField { + public: + PayloadField(std::string modifier, ParseLocation loc); + + void SetSizeField(const SizeField* size_field); + + virtual PacketField::Type GetFieldType() const override; + + virtual Size GetSize() const override; + + virtual std::string GetType() const override; + + virtual void GenGetter(std::ostream& s, Size start_offset, Size end_offset) const override; + + virtual bool GenBuilderParameter(std::ostream& s) const override; + + virtual bool HasParameterValidator() const override; + + virtual void GenParameterValidator(std::ostream&) const override; + + virtual void GenInserter(std::ostream&) const override; + + virtual void GenValidator(std::ostream&) const override; + + // Payload fields can only be dynamically sized. + const SizeField* size_field_; + // Only used if the size of the payload is based on another field. + std::string size_modifier_; +}; diff --git a/gd/packet/parser/fields/reserved_field.cc b/gd/packet/parser/fields/reserved_field.cc new file mode 100644 index 000000000..3db1527d0 --- /dev/null +++ b/gd/packet/parser/fields/reserved_field.cc @@ -0,0 +1,62 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "fields/reserved_field.h" +#include "util.h" + +int ReservedField::unique_id_ = 0; + +ReservedField::ReservedField(int size, ParseLocation loc) + : PacketField(loc, "ReservedScalar" + std::to_string(unique_id_++)), type_(PacketField::Type::RESERVED_SCALAR), + size_(size) {} + +PacketField::Type ReservedField::GetFieldType() const { + return type_; +} + +Size ReservedField::GetSize() const { + return size_; +} + +std::string ReservedField::GetType() const { + return util::GetTypeForSize(size_); +} + +void ReservedField::GenGetter(std::ostream&, Size, Size) const { + // There is no Getter for a reserved field +} + +bool ReservedField::GenBuilderParameter(std::ostream&) const { + // There is no builder parameter for a reserved field + return false; +} + +bool ReservedField::HasParameterValidator() const { + return false; +} + +void ReservedField::GenParameterValidator(std::ostream&) const { + // There is no builder parameter for a reserved field +} + +void ReservedField::GenInserter(std::ostream& s) const { + s << "insert(static_cast<" << util::GetTypeForSize(GetSize().bits()) << ">(0) /* Reserved */, i, " << GetSize().bits() + << " );\n"; +} + +void ReservedField::GenValidator(std::ostream&) const { + // There is no need to validate the value of a reserved field +} diff --git a/gd/packet/parser/fields/reserved_field.h b/gd/packet/parser/fields/reserved_field.h new file mode 100644 index 000000000..46f7f7b86 --- /dev/null +++ b/gd/packet/parser/fields/reserved_field.h @@ -0,0 +1,49 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "fields/packet_field.h" +#include "parse_location.h" + +class ReservedField : public PacketField { + public: + ReservedField(int size, ParseLocation loc); + + virtual PacketField::Type GetFieldType() const override; + + virtual Size GetSize() const override; + + virtual std::string GetType() const override; + + virtual void GenGetter(std::ostream&, Size, Size) const override; + + virtual bool GenBuilderParameter(std::ostream&) const override; + + virtual bool HasParameterValidator() const override; + + virtual void GenParameterValidator(std::ostream&) const override; + + virtual void GenInserter(std::ostream& s) const override; + + virtual void GenValidator(std::ostream&) const override; + + private: + PacketField::Type type_; + std::string name_; + int size_; + static int unique_id_; +}; diff --git a/gd/packet/parser/fields/scalar_field.cc b/gd/packet/parser/fields/scalar_field.cc new file mode 100644 index 000000000..6e6477a0d --- /dev/null +++ b/gd/packet/parser/fields/scalar_field.cc @@ -0,0 +1,129 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "fields/scalar_field.h" +#include "util.h" + +ScalarField::ScalarField(std::string name, int size, ParseLocation loc) : PacketField(loc, name), size_(size) {} + +PacketField::Type ScalarField::GetFieldType() const { + return PacketField::Type::SCALAR; +} + +Size ScalarField::GetSize() const { + return size_; +} + +std::string ScalarField::GetType() const { + return util::GetTypeForSize(size_); +} + +void ScalarField::GenGetter(std::ostream& s, Size start_offset, Size end_offset) const { + // Write the Getter Function Definiton + s << GetType(); + s << " Get" << GetName() << "() const {"; + + // Write the Getter Function Body + int num_leading_bits = 0; + int field_size = size_; + + // Handle if to start the iterator at begin or end. + s << "auto it = "; + if (!start_offset.empty()) { + // Default to start if available. + num_leading_bits = start_offset.bits() % 8; + s << "begin()"; + if (start_offset.bits() / 8 != 0) s << " + " << start_offset.bits() / 8; + if (start_offset.has_dynamic()) s << " + " << start_offset.dynamic_string(); + } else if (!end_offset.empty()) { + num_leading_bits = (8 - ((end_offset.bits() + field_size) % 8)) % 8; + // Add 7 so it rounds up + int byte_offset = (7 + end_offset.bits() + field_size) / 8; + s << "end() - " << byte_offset; + if (end_offset.has_dynamic()) s << " - (" << end_offset.dynamic_string() << ")"; + } else { + ERROR(this) << "Ambiguous offset for field."; + } + s << ";"; + + // We don't need any masking, just return the extracted value. + if (num_leading_bits == 0 && util::RoundSizeUp(field_size) == field_size) { + s << "return it.extract<" << util::GetTypeForSize(field_size) << ">();"; + s << "}\n"; + return; + } + + // Extract the correct number of bytes. The return type could be different + // from the extract type if an earlier field causes the beginning of the + // current field to start in the middle of a byte. + std::string extract_type = util::GetTypeForSize(field_size + num_leading_bits); + s << "auto value = it.extract<" << extract_type << ">();"; + + // Right shift the result if necessary. + int shift_amount = num_leading_bits; + if (shift_amount != 0) { + s << "value >>= " << shift_amount << ";"; + } + + // Mask the result if necessary. + if (util::RoundSizeUp(field_size) != field_size) { + uint64_t mask = 0; + for (int i = 0; i < field_size; i++) { + mask <<= 1; + mask |= 1; + } + s << "value &= 0x" << std::hex << mask << std::dec << ";"; + } + + // Cast the result if necessary. + if (extract_type != util::GetTypeForSize(field_size)) { + s << "return static_cast<" << GetType() << ">(value);"; + } else { + s << "return value;"; + } + s << "}\n"; +} + +bool ScalarField::GenBuilderParameter(std::ostream& s) const { + if (size_ > 64 || size_ < 0) { + ERROR(this) << "Not implemented"; + } + std::string param_type = util::GetTypeForSize(size_); + s << param_type << " " << util::CamelCaseToUnderScore(GetName()); + return true; +} + +bool ScalarField::HasParameterValidator() const { + const auto bits = GetSize().bits(); + return util::RoundSizeUp(bits) != bits; +} + +void ScalarField::GenParameterValidator(std::ostream& s) const { + const auto bits = GetSize().bits(); + if (util::RoundSizeUp(bits) == bits) { + return; + } + s << "ASSERT(" << util::CamelCaseToUnderScore(GetName()) << " < " + << "(static_cast(1) << " << bits << "));"; +} + +void ScalarField::GenInserter(std::ostream& s) const { + s << "insert(" << util::CamelCaseToUnderScore(GetName()) << "_, i," << GetSize().bits() << ");"; +} + +void ScalarField::GenValidator(std::ostream&) const { + // Do nothing since the fixed size fields will be handled seperatly. +} diff --git a/gd/packet/parser/fields/scalar_field.h b/gd/packet/parser/fields/scalar_field.h new file mode 100644 index 000000000..4e52fd170 --- /dev/null +++ b/gd/packet/parser/fields/scalar_field.h @@ -0,0 +1,46 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "fields/packet_field.h" +#include "parse_location.h" + +class ScalarField : public PacketField { + public: + ScalarField(std::string name, int size, ParseLocation loc); + + virtual PacketField::Type GetFieldType() const override; + + virtual Size GetSize() const override; + + virtual std::string GetType() const override; + + virtual void GenGetter(std::ostream& s, Size start_offset, Size end_offset) const override; + + virtual bool GenBuilderParameter(std::ostream& s) const override; + + virtual bool HasParameterValidator() const override; + + virtual void GenParameterValidator(std::ostream& s) const override; + + virtual void GenInserter(std::ostream& s) const override; + + virtual void GenValidator(std::ostream&) const override; + + private: + int size_; +}; diff --git a/gd/packet/parser/fields/size_field.cc b/gd/packet/parser/fields/size_field.cc new file mode 100644 index 000000000..9f4e20f2f --- /dev/null +++ b/gd/packet/parser/fields/size_field.cc @@ -0,0 +1,121 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "fields/size_field.h" +#include "util.h" + +SizeField::SizeField(std::string name, int size, bool is_count, ParseLocation loc) + : PacketField(loc, name + (is_count ? "Count" : "Size")), size_(size), is_count_(is_count), + sized_field_name_(name) {} + +PacketField::Type SizeField::GetFieldType() const { + return (is_count_ ? PacketField::Type::COUNT : PacketField::Type::SIZE); +} + +Size SizeField::GetSize() const { + return size_; +} + +std::string SizeField::GetType() const { + return util::GetTypeForSize(size_); +} + +void SizeField::GenGetter(std::ostream& s, Size start_offset, Size end_offset) const { + // Write the Getter Function Definiton + s << GetType(); + s << " Get" << GetName() << "() const {"; + + // Write the Getter Function Body + int num_leading_bits = 0; + + // Handle if to start the iterator at begin or end. + if (!start_offset.empty()) { + // Default to start if available. + num_leading_bits = start_offset.bits() % 8; + s << "auto it = begin() + " << start_offset.bits() / 8 << " + (" << start_offset.dynamic_string() << ");"; + } else if (!end_offset.empty()) { + int offset_from_end = end_offset.bits() + size_; + num_leading_bits = 8 - (offset_from_end % 8); + // Add 7 so it rounds up + int byte_offset = (7 + offset_from_end) / 8; + s << "auto it = end() - " << byte_offset << " - (" << end_offset.dynamic_string() << ");"; + + } else { + ERROR(this) << "Ambiguous offset for field."; + } + + // We don't need any masking, just return the extracted value. + if (num_leading_bits == 0 && util::RoundSizeUp(size_) == size_) { + s << "return it.extract<" << GetType() << ">();"; + s << "}\n"; + return; + } + + // Extract the correct number of bytes. The return type could be different + // from the extract type if an earlier field causes the beginning of the + // current field to start in the middle of a byte. + std::string extract_type = util::GetTypeForSize(size_ + num_leading_bits); + s << "auto value = it.extract<" << extract_type << ">();"; + + // Right shift the result to remove leading bits. + if (num_leading_bits != 0) { + s << "value >>= " << num_leading_bits << ";"; + } + + // Mask the result if necessary. + if (util::RoundSizeUp(size_) != size_) { + uint64_t mask = 0; + for (int i = 0; i < size_; i++) { + mask <<= 1; + mask |= 1; + } + s << "value &= 0x" << std::hex << mask << std::dec << ";"; + } + + // Cast the result if necessary. + if (extract_type != util::GetTypeForSize(size_)) { + s << "return static_cast<" << GetType() << ">(value);"; + } else { + s << "return value;"; + } + s << "}\n"; +} + +bool SizeField::GenBuilderParameter(std::ostream&) const { + // There is no builder parameter for a size field + return false; +} + +bool SizeField::HasParameterValidator() const { + return false; +} + +void SizeField::GenParameterValidator(std::ostream&) const { + // There is no builder parameter for a size field + // TODO: Check if the payload fits in the packet? +} + +void SizeField::GenInserter(std::ostream&) const { + ERROR(this) << __func__ << ": This should not be called for size fields"; +} + +void SizeField::GenValidator(std::ostream&) const { + // Do nothing since the fixed size fields will be handled specially. +} + +std::string SizeField::GetSizedFieldName() const { + return sized_field_name_; +} diff --git a/gd/packet/parser/fields/size_field.h b/gd/packet/parser/fields/size_field.h new file mode 100644 index 000000000..61e8666ee --- /dev/null +++ b/gd/packet/parser/fields/size_field.h @@ -0,0 +1,52 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "fields/packet_field.h" +#include "parse_location.h" + +class SizeField : public PacketField { + public: + SizeField(std::string name, int size, bool is_count, ParseLocation loc); + + std::string GetField() const; + + virtual PacketField::Type GetFieldType() const override; + + virtual Size GetSize() const override; + + virtual std::string GetType() const override; + + virtual void GenGetter(std::ostream& s, Size start_offset, Size end_offset) const override; + + virtual bool GenBuilderParameter(std::ostream&) const override; + + virtual bool HasParameterValidator() const override; + + virtual void GenParameterValidator(std::ostream&) const override; + + virtual void GenInserter(std::ostream&) const override; + + virtual void GenValidator(std::ostream&) const override; + + virtual std::string GetSizedFieldName() const; + + private: + int size_; + bool is_count_; + std::string sized_field_name_; +}; diff --git a/gd/packet/parser/language_l.ll b/gd/packet/parser/language_l.ll new file mode 100644 index 000000000..862af8c18 --- /dev/null +++ b/gd/packet/parser/language_l.ll @@ -0,0 +1,113 @@ +%{ + +#include +#include +#include + +#include "declarations.h" +#include "language_y.h" + +using token = yy::parser::token; + +#define YY_USER_ACTION yylloc->step(); yylloc->columns(yyleng); + +%} + +%option debug + +%option yylineno +%option noyywrap +%option nounput +%option noinput +%option reentrant +%option bison-bridge +%option bison-locations + +identifier [a-zA-Z][_a-zA-Z0-9]* +size_modifier [+*/-][ +*/\-0-9]* +intvalue (0|[1-9][0-9]*) +hexvalue 0[x|X][0-9a-fA-F]+ +string_literal \".*\" + +%x COMMENT_STATE + +%% + /* NOTE: + * Rule ordering is important in order to establist priority. Some + * rules are a superset of other rules and will cause the sub rules to + * never match. Ex. Keywords must always go before identifiers, otherwise + * all keywords will be treated as an identifier. + */ + + /* Block Comment */ +"/*" { BEGIN(COMMENT_STATE); } +"*/" { BEGIN(INITIAL); } +[\n]+ { yylloc->lines(yyleng); } +. { /* do nothing */ } + + /* Line Comment */ +"//"[^\r\n]* { /* do nothing */ } + + /* Begin reserved keyword definitions */ +"enum" { return(token::ENUM); } +"packet" { return(token::PACKET); } +"body" { return(token::BODY); } +"payload" { return(token::PAYLOAD); } +"size" { return(token::SIZE); } +"count" { return(token::COUNT); } +"fixed" { return(token::FIXED); } +"reserved" { return(token::RESERVED); } +"group" { return(token::GROUP); } +"little_endian_packets" { + yylval->integer = 1; + return token::IS_LITTLE_ENDIAN; + } +"big_endian_packets" { + yylval->integer = 0; + return token::IS_LITTLE_ENDIAN; + } + + /* Begin identifier definitions */ +{string_literal} { + yylval->string = new std::string(yytext); + return token::STRING; + } + +{size_modifier} { + yylval->string = new std::string(yytext); + return token::SIZE_MODIFIER; + } + +{identifier} { + yylval->string = new std::string(yytext); + return token::IDENTIFIER; + } + +{intvalue} { + yylval->integer = std::stoi(std::string(yytext), nullptr, 10); + return token::INTEGER; + } + +{hexvalue} { + yylval->integer = std::stoi(std::string(yytext), nullptr, 16); + return token::INTEGER; + } + + /* Begin token definitions */ +":" { return(':'); } +"{" { return('{'); } +"}" { return('}'); } +"[" { return('['); } +"]" { return(']'); } +"(" { return('('); } +")" { return(')'); } +"<" { return('<'); } +">" { return('>'); } +"=" { return('='); } +"," { return(','); } + +(\n|\r\n)+ { yylloc->lines(yyleng); } +[ \t\f\v]+ { /* Ignore all other whitespace */ } + +%% + diff --git a/gd/packet/parser/language_y.yy b/gd/packet/parser/language_y.yy new file mode 100644 index 000000000..af17cec5e --- /dev/null +++ b/gd/packet/parser/language_y.yy @@ -0,0 +1,520 @@ +%{ + #include + #include + #include + #include + + #include "declarations.h" + #include "logging.h" + #include "language_y.h" + #include "field_list.h" + #include "fields/all_fields.h" + + extern int yylex(yy::parser::semantic_type*, yy::parser::location_type*, void *); + + ParseLocation toParseLocation(yy::parser::location_type loc) { + return ParseLocation(loc.begin.line); + } + #define LOC toParseLocation(yylloc) +%} + +%parse-param { void* scanner } +%parse-param { Declarations* decls } +%lex-param { void* scanner } + +%pure-parser +%glr-parser +%skeleton "glr.cc" + +%expect-rr 0 + +%debug +%error-verbose +%verbose + +%union { + int integer; + std::string* string; + + EnumDef* enum_definition; + std::map* enumeration_values; + std::pair* enumeration_value; + + PacketDef* packet_definition_value; + FieldList* packet_field_definitions; + PacketField* packet_field_type; + + std::map>* constraint_list_t; + std::pair>* constraint_t; +} + +%token INTEGER +%token IS_LITTLE_ENDIAN +%token IDENTIFIER +%token SIZE_MODIFIER +%token STRING + +%token ENUM "enum" +%token PACKET "packet" +%token PAYLOAD "payload" +%token BODY "body" +%token SIZE "size" +%token COUNT "count" +%token FIXED "fixed" +%token RESERVED "reserved" +%token GROUP "group" + +%type enum_definition +%type enumeration_list +%type enumeration + +%type packet_definition; +%type field_definition_list; +%type field_definition; +%type group_field_definition; +%type special_field_definition; +%type scalar_field_definition; +%type size_field_definition; +%type payload_field_definition; +%type body_field_definition; +%type fixed_field_definition; +%type reserved_field_definition; + +%type constraint_list; +%type constraint; +%destructor { std::cout << "DESTROYING STRING " << *$$ << "\n"; delete $$; } IDENTIFIER STRING SIZE_MODIFIER + +%% + +file + : IS_LITTLE_ENDIAN declarations + { + decls->is_little_endian = ($1 == 1); + if (decls->is_little_endian) { + DEBUG() << "LITTLE ENDIAN "; + } else { + DEBUG() << "BIG ENDIAN "; + } + } + +declarations + : /* empty */ + | declarations declaration + +declaration + : enum_definition + { + std::cerr << "FOUND ENUM\n\n"; + decls->AddEnumDef($1->name_, std::move(*$1)); + delete $1; + } + | packet_definition + { + std::cerr << "FOUND PACKET\n\n"; + decls->AddPacketDef($1->name_, std::move(*$1)); + delete $1; + } + | group_definition + { + // All actions are handled in group_definition + } + +enum_definition + : ENUM IDENTIFIER ':' INTEGER '{' enumeration_list ',' '}' + { + std::cerr << "Enum Declared: name=" << *$2 + << " size=" << $4 << "\n"; + + $$ = new EnumDef(std::move(*$2), $4); + for (const auto& e : *$6) { + $$->AddEntry(e.second, e.first); + } + delete $2; + delete $6; + } + +enumeration_list + : enumeration + { + std::cerr << "Enumerator with comma\n"; + $$ = new std::map(); + $$->insert(std::move(*$1)); + delete $1; + } + | enumeration_list ',' enumeration + { + std::cerr << "Enumerator with list\n"; + $$ = $1; + $$->insert(std::move(*$3)); + delete $3; + } + +enumeration + : IDENTIFIER '=' INTEGER + { + std::cerr << "Enumerator: name=" << *$1 + << " value=" << $3 << "\n"; + $$ = new std::pair($3, std::move(*$1)); + delete $1; + } + +group_definition + : GROUP IDENTIFIER '{' field_definition_list '}' + { + decls->AddGroupDef(*$2, $4); + delete $2; + } + +packet_definition + : PACKET IDENTIFIER '{' field_definition_list '}' /* Packet with no parent */ + { + auto&& packet_name = *$2; + auto&& field_definition_list = *$4; + + DEBUG() << "Packet " << packet_name << " with no parent"; + DEBUG() << "PACKET FIELD LIST SIZE: " << field_definition_list.size(); + auto packet_definition = new PacketDef(std::move(packet_name), std::move(field_definition_list)); + packet_definition->AssignSizeFields(); + + $$ = packet_definition; + delete $2; + delete $4; + } + | PACKET IDENTIFIER ':' IDENTIFIER '{' field_definition_list '}' + { + auto&& packet_name = *$2; + auto&& parent_packet_name = *$4; + auto&& field_definition_list = *$6; + + DEBUG() << "Packet " << packet_name << " with parent " << parent_packet_name << "\n"; + DEBUG() << "PACKET FIELD LIST SIZE: " << field_definition_list.size() << "\n"; + + auto parent_packet = decls->GetPacketDef(parent_packet_name); + if (parent_packet == nullptr) { + ERRORLOC(LOC) << "Could not find packet " << parent_packet_name + << " used as parent for " << packet_name; + } + + auto packet_definition = new PacketDef(std::move(packet_name), std::move(field_definition_list), parent_packet); + packet_definition->AssignSizeFields(); + + $$ = packet_definition; + delete $2; + delete $4; + delete $6; + } + | PACKET IDENTIFIER ':' IDENTIFIER '(' constraint_list ')' '{' field_definition_list '}' + { + auto&& packet_name = *$2; + auto&& parent_packet_name = *$4; + auto&& constraints = *$6; + auto&& field_definition_list = *$9; + + DEBUG() << "Packet " << packet_name << " with parent " << parent_packet_name << "\n"; + DEBUG() << "PACKET FIELD LIST SIZE: " << field_definition_list.size() << "\n"; + DEBUG() << "CONSTRAINT LIST SIZE: " << constraints.size() << "\n"; + + auto parent_packet = decls->GetPacketDef(parent_packet_name); + if (parent_packet == nullptr) { + ERRORLOC(LOC) << "Could not find packet " << parent_packet_name + << " used as parent for " << packet_name; + } + + auto packet_definition = new PacketDef(std::move(packet_name), std::move(field_definition_list), parent_packet); + packet_definition->AssignSizeFields(); + + for (const auto& constraint : constraints) { + const auto& constraint_name = constraint.first; + const auto& constraint_value = constraint.second; + DEBUG() << "Parent constraint on " << constraint_name; + packet_definition->AddParentConstraint(constraint_name, constraint_value); + } + + $$ = packet_definition; + + delete $2; + delete $4; + delete $6; + delete $9; + } + +field_definition_list + : /* empty */ + { + std::cerr << "Empty Field definition\n"; + $$ = new FieldList(); + } + | field_definition + { + std::cerr << "Field definition\n"; + $$ = new FieldList(); + + if ($1->GetFieldType() == PacketField::Type::GROUP) { + auto group_fields = static_cast($1)->GetFields(); + FieldList reversed_fields(group_fields->rbegin(), group_fields->rend()); + for (auto& field : reversed_fields) { + $$->PrependField(field); + } + delete $1; + break; + } + + $$->PrependField($1); + } + | field_definition ',' field_definition_list + { + std::cerr << "Field definition with list\n"; + $$ = $3; + + if ($1->GetFieldType() == PacketField::Type::GROUP) { + auto group_fields = static_cast($1)->GetFields(); + FieldList reversed_fields(group_fields->rbegin(), group_fields->rend()); + for (auto& field : reversed_fields) { + $$->PrependField(field); + } + delete $1; + break; + } + + $$->PrependField($1); + } + +field_definition + : group_field_definition + { + DEBUG() << "Group Field"; + $$ = $1; + } + | special_field_definition + { + std::cerr << "Special field\n"; + $$ = $1; + } + | scalar_field_definition + { + std::cerr << "Scalar field\n"; + $$ = $1; + } + | size_field_definition + { + std::cerr << "Size field\n"; + $$ = $1; + } + | body_field_definition + { + std::cerr << "Body field\n"; + $$ = $1; + } + | payload_field_definition + { + std::cerr << "Payload field\n"; + $$ = $1; + } + | fixed_field_definition + { + std::cerr << "Fixed field\n"; + $$ = $1; + } + | reserved_field_definition + { + std::cerr << "Reserved field\n"; + $$ = $1; + } + +group_field_definition + : IDENTIFIER + { + auto group = decls->GetGroupDef(*$1); + if (group == nullptr) { + ERRORLOC(LOC) << "Could not find group with name " << *$1; + } + + std::list* expanded_fields; + expanded_fields = new std::list(group->begin(), group->end()); + $$ = new GroupField(LOC, expanded_fields); + delete $1; + } + | IDENTIFIER '{' constraint_list '}' + { + std::cerr << "Group with fixed field(s) " << *$1 << "\n"; + auto group = decls->GetGroupDef(*$1); + if (group == nullptr) { + ERRORLOC(LOC) << "Could not find group with name " << *$1; + } + + std::list* expanded_fields = new std::list(); + for (const auto field : *group) { + const auto constraint = $3->find(field->GetName()); + if (constraint != $3->end()) { + if (field->GetFieldType() == PacketField::Type::SCALAR) { + std::cerr << "Fixing group scalar value\n"; + expanded_fields->push_back(new FixedField(field->GetSize().bits(), std::get(constraint->second), LOC)); + } else if (field->GetFieldType() == PacketField::Type::ENUM) { + std::cerr << "Fixing group enum value\n"; + + auto enum_def = decls->GetEnumDef(field->GetType()); + if (enum_def == nullptr) { + ERRORLOC(LOC) << "No enum found of type " << field->GetType(); + } + if (!enum_def->HasEntry(std::get(constraint->second))) { + ERRORLOC(LOC) << "Enum " << field->GetType() << " has no enumeration " << std::get(constraint->second); + } + + expanded_fields->push_back(new FixedField(enum_def, std::get(constraint->second), LOC)); + } else { + ERRORLOC(LOC) << "Unimplemented constraint of type " << field->GetType(); + } + $3->erase(constraint); + } else { + expanded_fields->push_back(field); + } + } + if ($3->size() > 0) { + ERRORLOC(LOC) << "Could not find member " << $3->begin()->first << " in group " << *$1; + } + + $$ = new GroupField(LOC, expanded_fields); + delete $1; + delete $3; + } + +constraint_list + : constraint ',' constraint_list + { + std::cerr << "Group field value list\n"; + $3->insert(*$1); + $$ = $3; + delete($1); + } + | constraint + { + std::cerr << "Group field value\n"; + $$ = new std::map>(); + $$->insert(*$1); + delete($1); + } + +constraint + : IDENTIFIER '=' INTEGER + { + std::cerr << "Group with a fixed integer value=" << $1 << " value=" << $3 << "\n"; + + $$ = new std::pair(*$1, std::variant($3)); + delete $1; + } + | IDENTIFIER '=' IDENTIFIER + { + DEBUG() << "Group with a fixed enum field value=" << *$3 << " enum=" << *$1; + + $$ = new std::pair(*$1, std::variant(*$3)); + delete $1; + delete $3; + } + +special_field_definition + : IDENTIFIER ':' IDENTIFIER + { + std::cerr << "Special field " << *$1 << " : " << *$3 << "\n"; + if (auto enum_def = decls->GetEnumDef(*$3)) { + $$ = new EnumField(*$1, *enum_def, "", LOC); + } else { + ERRORLOC(LOC) << "No type with this name\n"; + } + delete $1; + delete $3; + } + +scalar_field_definition + : IDENTIFIER ':' INTEGER + { + std::cerr << "Scalar field " << *$1 << " : " << $3 << "\n"; + $$ = new ScalarField(*$1, $3, LOC); + delete $1; + } + +body_field_definition + : BODY + { + std::cerr << "Body field\n"; + $$ = new BodyField(LOC); + } + +payload_field_definition + : PAYLOAD ':' '[' SIZE_MODIFIER ']' + { + std::cerr << "Payload field with modifier " << *$4 << "\n"; + $$ = new PayloadField(*$4, LOC); + delete $4; + } + | PAYLOAD ':' '[' INTEGER ']' + { + ERRORLOC(LOC) << "Payload fields can only be dynamically sized."; + } + | PAYLOAD + { + std::cerr << "Payload field\n"; + $$ = new PayloadField("", LOC); + } + +size_field_definition + : SIZE '(' IDENTIFIER ')' ':' INTEGER + { + std::cerr << "Size field defined\n"; + $$ = new SizeField(*$3, $6, false, LOC); + delete $3; + } + | SIZE '(' PAYLOAD ')' ':' INTEGER + { + std::cerr << "Size for payload defined\n"; + $$ = new SizeField("Payload", $6, false, LOC); + } + | COUNT '(' IDENTIFIER ')' ':' INTEGER + { + std::cerr << "Count field defined\n"; + $$ = new SizeField(*$3, $6, true, LOC); + delete $3; + } + | COUNT '(' PAYLOAD ')' ':' INTEGER + { + std::cerr << "Count for payload defined\n"; + $$ = new SizeField("Payload", $6, true, LOC); + ERRORLOC(LOC) << "Can not use count to describe payload fields."; + } + +fixed_field_definition + : FIXED '=' INTEGER ':' INTEGER + { + std::cerr << "Fixed field defined value=" << $3 << " size=" << $5 << "\n"; + $$ = new FixedField($5, $3, LOC); + } + | FIXED '=' IDENTIFIER ':' IDENTIFIER + { + DEBUG() << "Fixed enum field defined value=" << *$3 << " enum=" << *$5; + if (auto enum_def = decls->GetEnumDef(*$5)) { + if (!enum_def->HasEntry(*$3)) { + ERRORLOC(LOC) << "Previously defined enum " << enum_def->GetTypeName() << " has no entry for " << *$3; + } + + $$ = new FixedField(enum_def, *$3, LOC); + } else { + ERRORLOC(LOC) << "No enum found with name " << *$5; + } + + delete $3; + delete $5; + } + +reserved_field_definition + : RESERVED ':' INTEGER + { + std::cerr << "Reserved field of size=" << $3 << "\n"; + $$ = new ReservedField($3, LOC); + } + +%% + + +void yy::parser::error(const yy::parser::location_type& loc, const std::string& error) { + std::cerr << error << " at location " << loc << "\n"; + abort(); +} diff --git a/gd/packet/parser/logging.h b/gd/packet/parser/logging.h new file mode 100644 index 000000000..8d9e4671e --- /dev/null +++ b/gd/packet/parser/logging.h @@ -0,0 +1,93 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include + +#include "parse_location.h" + +class Loggable { + public: + virtual ~Loggable() = default; + virtual std::string GetDebugName() const = 0; + virtual ParseLocation GetLocation() const = 0; +}; + +class LogMessage { + public: + LogMessage(ParseLocation loc, std::initializer_list tokens) + : debug_(false), loc_(loc), tokens_(tokens) { + Init(); + } + + LogMessage(bool debug, std::initializer_list tokens) : debug_(debug), tokens_(tokens) { + Init(); + } + + void Init() { + if (loc_.GetLine() != -1) { + stream_ << "\033[1mLine " << loc_.GetLine() << ": "; + } + + if (!debug_) { + stream_ << "\033[1;31m"; + stream_ << "ERROR: "; + stream_ << "\033[0m"; + } else { + stream_ << "\033[1;m"; + stream_ << "DEBUG: "; + stream_ << "\033[0m"; + } + } + + ~LogMessage() { + std::cerr << stream_.str() << "\n"; + for (const auto& token : tokens_) { + // Bold line number + std::cerr << "\033[1m"; + std::cerr << " Line " << token->GetLocation().GetLine() << ": "; + std::cerr << "\033[0m"; + std::cerr << token->GetDebugName() << "\n"; + } + + if (!debug_) { + abort(); + } + } + + std::ostream& stream() { + return stream_; + } + + private: + std::ostringstream stream_; + bool debug_; + ParseLocation loc_; + std::vector tokens_; +}; + +// Error Log stream. Aborts the program after the message is printed. +// The arguments to the macro is a list of Loggable objects that are printed when the error is printed. +#define ERROR(...) LogMessage(false, {__VA_ARGS__}).stream() + +// ParseLocation error log, the first argument is a location. +#define ERRORLOC(_1, ...) LogMessage(_1, {__VA_ARGS__}).stream() + +#define DEBUG(...) LogMessage(true, {__VA_ARGS__}).stream() diff --git a/gd/packet/parser/main.cc b/gd/packet/parser/main.cc new file mode 100644 index 000000000..49e25d1e8 --- /dev/null +++ b/gd/packet/parser/main.cc @@ -0,0 +1,79 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include "declarations.h" + +#include "language_y.h" + +void yylex_init(void**); +void yylex_destroy(void*); +void yyset_debug(int, void*); + +int main() { + void* scanner; + yylex_init(&scanner); + + Declarations decls; + int ret = yy::parser(scanner, &decls).parse(); + + yylex_destroy(scanner); + + if (ret != 0) return ret; + + std::cout << "\n\n"; + std::cout << "#include \n"; + std::cout << "#include \n"; + std::cout << "\n\n"; + std::cout << "#include \"os/log.h\"\n"; + std::cout << "#include \"packet/base_packet_builder.h\"\n"; + std::cout << "#include \"packet/bit_inserter.h\"\n"; + std::cout << "#include \"packet/packet_builder.h\"\n"; + std::cout << "#include \"packet/packet_view.h\"\n"; + std::cout << "\n\n"; + std::cout << "using bluetooth::packet::BasePacketBuilder;"; + std::cout << "using bluetooth::packet::BitInserter;"; + std::cout << "using bluetooth::packet::kLittleEndian;"; + std::cout << "using bluetooth::packet::PacketBuilder;"; + std::cout << "using bluetooth::packet::PacketView;"; + std::cout << "\n\n"; + + for (const auto& e : decls.enum_defs_queue_) { + EnumGen gen(e.second); + gen.GenDefinition(std::cout); + std::cout << "\n\n"; + } + for (const auto& e : decls.enum_defs_queue_) { + EnumGen gen(e.second); + gen.GenLogging(std::cout); + std::cout << "\n\n"; + } + + for (size_t i = 0; i < decls.packet_defs_queue_.size(); i++) { + decls.packet_defs_queue_[i].second.SetEndianness(decls.is_little_endian); + decls.packet_defs_queue_[i].second.GenParserDefinition(std::cout); + std::cout << "\n\n"; + } + + for (const auto p : decls.packet_defs_queue_) { + p.second.GenBuilderDefinition(std::cout); + std::cout << "\n\n"; + } +} diff --git a/gd/packet/parser/packet_def.cc b/gd/packet/parser/packet_def.cc new file mode 100644 index 000000000..ec6f6f215 --- /dev/null +++ b/gd/packet/parser/packet_def.cc @@ -0,0 +1,644 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "packet_def.h" + +#include +#include + +#include "fields/all_fields.h" +#include "util.h" + +PacketDef::PacketDef(std::string name, FieldList fields) : name_(name), fields_(fields), parent_(nullptr){}; +PacketDef::PacketDef(std::string name, FieldList fields, PacketDef* parent) + : name_(name), fields_(fields), parent_(parent){}; + +void PacketDef::AddParentConstraint(std::string field_name, std::variant value) { + // NOTE: This could end up being very slow if there are a lot of constraints. + const auto& parent_params = parent_->GetParamList(); + const auto& constrained_field = parent_params.GetField(field_name); + if (constrained_field == nullptr) { + ERROR() << "Attempting to constrain field " << field_name << " in parent packet " << parent_->name_ + << ", but no such field exists."; + } + + if (constrained_field->GetFieldType() == PacketField::Type::SCALAR) { + if (!std::holds_alternative(value)) { + ERROR(constrained_field) << "Attemting to constrain a scalar field using an enum value in packet " + << parent_->name_ << "."; + } + } else if (constrained_field->GetFieldType() == PacketField::Type::ENUM) { + if (!std::holds_alternative(value)) { + ERROR(constrained_field) << "Attemting to constrain an enum field using an scalar value in packet " + << parent_->name_ << "."; + } + const auto& enum_def = static_cast(constrained_field)->GetEnumDef(); + if (!enum_def.HasEntry(std::get(value))) { + ERROR(constrained_field) << "No matching enumeration \"" << std::get(value) + << "for constraint on enum in parent packet " << parent_->name_ << "."; + } + + // For enums, we have to qualify the value using the enum type name. + value = enum_def.GetTypeName() + "::" + std::get(value); + } else { + ERROR(constrained_field) << "Field in parent packet " << parent_->name_ << " is not viable for constraining."; + } + + parent_constraints_.insert(std::pair(field_name, value)); +} + +// Assign all size fields to their corresponding variable length fields. +// Will crash if +// - there aren't any fields that don't match up to a field. +// - the size field points to a fixed size field. +// - if the size field comes after the variable length field. +void PacketDef::AssignSizeFields() { + for (const auto& field : fields_) { + DEBUG() << "field name: " << field->GetName(); + + if (field->GetFieldType() != PacketField::Type::SIZE && field->GetFieldType() != PacketField::Type::COUNT) { + continue; + } + + const SizeField* size_field = nullptr; + size_field = static_cast(field); + // Check to see if a corresponding field can be found. + const auto& var_len_field = fields_.GetField(size_field->GetSizedFieldName()); + if (var_len_field == nullptr) { + ERROR(field) << "Could not find corresponding field for size/count field."; + } + + // Do the ordering check to ensure the size field comes before the + // variable length field. + for (auto it = fields_.begin(); *it != size_field; it++) { + DEBUG() << "field name: " << (*it)->GetName(); + if (*it == var_len_field) { + ERROR(var_len_field, size_field) << "Size/count field must come before the variable length field it describes."; + } + } + + if (var_len_field->GetFieldType() == PacketField::Type::PAYLOAD) { + const auto& payload_field = static_cast(var_len_field); + payload_field->SetSizeField(size_field); + continue; + } + + // If we've reached this point then the field wasn't a variable length field. + // Check to see if the field is a variable length field + std::cerr << "Can not use size/count in reference to a fixed size field.\n"; + abort(); + } +} + +void PacketDef::SetEndianness(bool is_little_endian) { + is_little_endian_ = is_little_endian; +} + +// Get the size for the packet. You scan specify without_payload in order +// to exclude payload fields as child packets will be overriding it. +Size PacketDef::GetSize(bool without_payload) const { + auto size = Size(); + + for (const auto& field : fields_) { + if (without_payload && field->GetFieldType() == PacketField::Type::PAYLOAD) { + continue; + } + + size += field->GetSize(); + } + + if (parent_ != nullptr) { + size += parent_->GetSize(true); + } + + return size; +} + +// Get the offset until the field is reached, if there is no field +// returns an empty Size. from_end requests the offset to the field +// starting from the end() iterator. If there is a field with an unknown +// size along the traversal, then an empty size is returned. +Size PacketDef::GetOffsetForField(std::string field_name, bool from_end) const { + // Check first if the field exists. + if (fields_.GetField(field_name) == nullptr) { + return Size(); + } + + // We have to use a generic lambda to conditionally change iteration direction + // due to iterator and reverse_iterator being different types. + auto size_lambda = [&](auto from, auto to) -> Size { + auto size = Size(0); + for (auto it = from; it != to; it++) { + // We've reached the field, end the loop. + if ((*it)->GetName() == field_name) break; + const auto& field = *it; + // If there was a field that wasn't the payload with an unknown size, + // return an empty Size. + if (field->GetSize().empty()) { + return Size(); + } + size += field->GetSize(); + } + return size; + }; + + // Change iteration direction based on from_end. + auto size = Size(); + if (from_end) + size = size_lambda(fields_.rbegin(), fields_.rend()); + else + size = size_lambda(fields_.begin(), fields_.end()); + if (size.empty()) return Size(); + + // For parent packets we need the offset until a payload or body field. + if (parent_ != nullptr) { + auto parent_payload_offset = parent_->GetOffsetForField("Payload", from_end); + if (!parent_payload_offset.empty()) { + size += parent_payload_offset; + } else { + parent_payload_offset = parent_->GetOffsetForField("Body", from_end); + if (!parent_payload_offset.empty()) { + size += parent_payload_offset; + } else { + return Size(); + } + } + } + + return size; +} + +void PacketDef::GenParserDefinition(std::ostream& s) const { + s << "class " << name_ << "View"; + if (parent_ != nullptr) { + s << " : public " << parent_->name_ << "View {"; + } else { + s << " : public PacketView<" << (is_little_endian_ ? "" : "!") << "kLittleEndian> {"; + } + s << " public:"; + + // Constructor from a View + s << name_ << "View(PacketView<" << (is_little_endian_ ? "" : "!") << "kLittleEndian> packet) "; + if (parent_ != nullptr) { + s << " : " << parent_->name_ << "View(packet) {}"; + } else { + s << " : PacketView<" << (is_little_endian_ ? "" : "!") << "kLittleEndian>(packet) {}"; + } + + // TODO: Specialize function? + + std::set fixed_types = { + PacketField::Type::FIXED_SCALAR, + PacketField::Type::FIXED_ENUM, + }; + + // Print all of the public fields which are all the fields minus the fixed fields. + const auto& public_fields = fields_.GetFieldsWithoutTypes(fixed_types); + bool has_fixed_fields = public_fields.size() != fields_.size(); + for (const auto& field : public_fields) { + GenParserFieldGetter(s, field); + s << "\n"; + } + GenValidator(s); + s << "\n"; + + // Print the private fields which are the fixed fields. + if (has_fixed_fields) { + const auto& private_fields = fields_.GetFieldsWithTypes(fixed_types); + s << " private:\n"; + for (const auto& field : private_fields) { + GenParserFieldGetter(s, field); + s << "\n"; + } + } + s << "};\n"; +} + +void PacketDef::GenParserFieldGetter(std::ostream& s, const PacketField* field) const { + // Start field offset + auto start_field_offset = GetOffsetForField(field->GetName(), false); + auto end_field_offset = GetOffsetForField(field->GetName(), true); + + if (start_field_offset.empty() && end_field_offset.empty()) { + std::cerr << "Field location for " << field->GetName() << " is ambiguous, " + << "no method exists to determine field location from begin() or end().\n"; + abort(); + } + + if (field->GetFieldType() == PacketField::Type::SIZE) { + s << "protected:"; + } + field->GenGetter(s, start_field_offset, end_field_offset); + if (field->GetFieldType() == PacketField::Type::SIZE) { + s << "public:"; + } +} + +void PacketDef::GenSerialize(std::ostream& s) const { + auto header_fields = fields_.GetFieldsBeforePayloadOrBody(); + auto footer_fields = fields_.GetFieldsAfterPayloadOrBody(); + + s << "protected:"; + s << "void SerializeHeader(BitInserter&"; + if (parent_ != nullptr || header_fields.size() != 0) { + s << " i "; + } + s << ") const {"; + + if (parent_ != nullptr) { + s << parent_->name_ << "Builder::SerializeHeader(i);"; + } + + for (const auto& field : header_fields) { + if (field->GetFieldType() == PacketField::Type::SIZE) { + const auto& field_name = ((SizeField*)field)->GetSizedFieldName(); + const auto& sized_field = GetParamList().GetField(field_name); + if (sized_field == nullptr) { + ERROR(field) << __func__ << "Can't find sized field named " << field_name; + } + if (sized_field->GetFieldType() == PacketField::Type::PAYLOAD) { + s << "size_t payload_bytes = size() - (BitsOfHeader() + BitsOfFooter()) / 8;"; + s << "ASSERT(payload_bytes < (1 << " << field->GetSize().bits() << "));"; + s << "insert(static_cast<" << field->GetType() << ">(payload_bytes), i," << field->GetSize().bits() << ");"; + } else { + ERROR(field) << __func__ << "Unhandled sized field type for " << field_name; + } + } else { + field->GenInserter(s); + } + } + s << "}\n\n"; + + s << "void SerializeFooter(BitInserter&"; + if (parent_ != nullptr || footer_fields.size() != 0) { + s << " i "; + } + s << ") const {"; + + for (const auto& field : footer_fields) { + field->GenInserter(s); + } + if (parent_ != nullptr) { + s << parent_->name_ << "Builder::SerializeFooter(i);"; + } + s << "}\n\n"; + + s << "public:"; + s << "virtual void Serialize(BitInserter& i) const override {"; + s << "SerializeHeader(i);"; + if (fields_.HasPayload()) { + s << "payload_->Serialize(i);"; + } + s << "SerializeFooter(i);"; + + s << "}\n"; +} + +void PacketDef::GenBuilderSize(std::ostream& s) const { + auto header_fields = fields_.GetFieldsBeforePayloadOrBody(); + auto footer_fields = fields_.GetFieldsAfterPayloadOrBody(); + + s << "protected:"; + s << "size_t BitsOfHeader() const {"; + s << "return "; + + if (parent_ != nullptr) { + s << parent_->name_ << "Builder::BitsOfHeader() + "; + } + + size_t header_bits = 0; + for (const auto& field : header_fields) { + header_bits += field->GetSize().bits(); + } + s << header_bits << ";"; + + s << "}\n\n"; + + s << "size_t BitsOfFooter() const {"; + s << "return "; + size_t footer_bits = 0; + for (const auto& field : footer_fields) { + footer_bits += field->GetSize().bits(); + } + + if (parent_ != nullptr) { + s << parent_->name_ << "Builder::BitsOfFooter() + "; + } + s << footer_bits << ";"; + s << "}\n\n"; + + s << "public:"; + s << "virtual size_t size() const override {"; + s << "return (BitsOfHeader() / 8)"; + if (fields_.HasPayload()) { + s << "+ payload_->size()"; + } + s << " + (BitsOfFooter() / 8);"; + s << "}\n"; +} + +void PacketDef::GenValidator(std::ostream& s) const { + // Get the static offset for all of our fields. + int bits_size = 0; + for (const auto& field : fields_) { + bits_size += field->GetSize().bits(); + } + + // Write the function declaration. + s << "virtual bool IsValid() const" << (parent_ != nullptr ? " override" : "") << " {"; + + if (parent_constraints_.size() > 0 && parent_ == nullptr) { + ERROR() << "Can't have a constraint on a NULL parent"; + } + + for (const auto& constraint : parent_constraints_) { + s << "if (Get" << constraint.first << "() != "; + const auto& field = parent_->GetParamList().GetField(constraint.first); + if (field->GetFieldType() == PacketField::Type::SCALAR) { + s << std::get(constraint.second); + } else { + s << std::get(constraint.second); + } + s << ") return false;"; + } + + // Offset by the parents known size. We know that any dynamic fields can + // already be called since the parent must have already been validated by + // this point. + auto parent_size = Size(); + if (parent_ != nullptr) { + parent_size = parent_->GetSize(true); + } + + s << "auto it = begin() + " << parent_size.bytes() << " + (" << parent_size.dynamic_string() << ");"; + + // Check if you can extract the static fields. + // At this point you know you can use the size getters without crashing + // as long as they follow the instruction that size fields cant come before + // their corrisponding variable length field. + s << "it += " << ((bits_size + 7) / 8) << " /* Total size of the fixed fields */;"; + s << "if (it > end()) return false;"; + + // For any variable length fields, use their size check. + for (const auto& field : fields_) { + auto field_size = field->GetSize(); + + // Fixed size fields have already been handled. + if (!field_size.has_dynamic()) { + continue; + } + + s << "it += " << field_size.dynamic_string() << ";\n"; + s << "if (it > end()) return false;\n"; + s << "\n"; + } + + for (const auto& field : fields_) { + field->GenValidator(s); + s << "\n"; + } + + s << "return true;"; + s << "}\n"; +} + +void PacketDef::GenBuilderDefinition(std::ostream& s) const { + s << "class " << name_ << "Builder"; + if (parent_ != nullptr) { + s << " : public " << parent_->name_ << "Builder"; + } else { + if (is_little_endian_) { + s << " : public PacketBuilder"; + } else { + s << " : public PacketBuilder"; + } + } + s << " {"; + s << " public:"; + s << " virtual ~" << name_ << "Builder()" << (parent_ != nullptr ? " override" : "") << " = default;"; + + if (!fields_.HasBody()) { + GenBuilderCreate(s); + s << "\n"; + } + + GenSerialize(s); + s << "\n"; + + GenBuilderSize(s); + s << "\n"; + + s << " protected:\n"; + GenBuilderConstructor(s); + s << "\n"; + + GenBuilderParameterChecker(s); + s << "\n"; + + GenBuilderMembers(s); + s << "};\n"; +} + +FieldList PacketDef::GetParamList() const { + FieldList params; + + std::set param_types = { + PacketField::Type::SCALAR, + PacketField::Type::ENUM, + PacketField::Type::BODY, + PacketField::Type::PAYLOAD, + }; + + if (parent_ != nullptr) { + auto parent_params = parent_->GetParamList().GetFieldsWithTypes(param_types); + + // Do not include constrained fields in the params + for (const auto& field : parent_params) { + if (parent_constraints_.find(field->GetName()) == parent_constraints_.end()) { + params.AppendField(field); + } + } + } + + // Add the parameters for this packet. + return params.Merge(fields_.GetFieldsWithTypes(param_types)); +} + +FieldList PacketDef::GetParametersToValidate() const { + FieldList params_to_validate; + for (const auto& field : GetParamList()) { + if (field->HasParameterValidator()) { + params_to_validate.AppendField(field); + } + } + return params_to_validate; +} + +void PacketDef::GenBuilderCreate(std::ostream& s) const { + s << "static std::unique_ptr<" << name_ << "Builder> Create("; + + auto params = GetParamList(); + for (int i = 0; i < params.size(); i++) { + params[i]->GenBuilderParameter(s); + if (i != params.size() - 1) { + s << ", "; + } + } + s << ") {"; + + // Call the constructor + s << "auto builder = std::unique_ptr<" << name_ << "Builder>(new " << name_ << "Builder("; + + params = params.GetFieldsWithoutTypes({ + PacketField::Type::PAYLOAD, + PacketField::Type::BODY, + }); + // Add the parameters. + for (int i = 0; i < params.size(); i++) { + s << util::CamelCaseToUnderScore(params[i]->GetName()); + if (i != params.size() - 1) { + s << ", "; + } + } + + s << "));"; + if (fields_.HasPayload()) { + s << "builder->payload_ = std::move(payload);"; + } + s << "return builder;"; + s << "}\n"; +} + +void PacketDef::GenBuilderParameterChecker(std::ostream& s) const { + FieldList params_to_validate = GetParametersToValidate(); + + // Skip writing this function if there is nothing to validate. + if (params_to_validate.size() == 0) { + return; + } + + // Generate function arguments. + s << "void CheckParameterValues("; + for (int i = 0; i < params_to_validate.size(); i++) { + params_to_validate[i]->GenBuilderParameter(s); + if (i != params_to_validate.size() - 1) { + s << ", "; + } + } + s << ") {"; + + // Check the parameters. + for (const auto& field : params_to_validate) { + field->GenParameterValidator(s); + } + s << "}\n"; +} + +void PacketDef::GenBuilderConstructor(std::ostream& s) const { + s << name_ << "Builder("; + + // Generate the constructor parameters. + auto params = GetParamList().GetFieldsWithoutTypes({ + PacketField::Type::PAYLOAD, + PacketField::Type::BODY, + }); + for (int i = 0; i < params.size(); i++) { + params[i]->GenBuilderParameter(s); + if (i != params.size() - 1) { + s << ", "; + } + } + s << ") :"; + + // Get the list of parent params to call the parent constructor with. + FieldList parent_params; + if (parent_ != nullptr) { + // Pass parameters to the parent constructor + s << parent_->name_ << "Builder("; + parent_params = parent_->GetParamList().GetFieldsWithoutTypes({ + PacketField::Type::PAYLOAD, + PacketField::Type::BODY, + }); + + // Go through all the fields and replace constrained fields with fixed values + // when calling the parent constructor. + for (int i = 0; i < parent_params.size(); i++) { + const auto& field = parent_params[i]; + const auto& constraint = parent_constraints_.find(field->GetName()); + if (constraint != parent_constraints_.end()) { + if (field->GetFieldType() == PacketField::Type::SCALAR) { + s << std::get(constraint->second); + } else if (field->GetFieldType() == PacketField::Type::ENUM) { + s << std::get(constraint->second); + } else { + ERROR(field) << "Constraints on non enum/scalar fields should be impossible."; + } + + s << "/* " << util::CamelCaseToUnderScore(field->GetName()) << "_ */"; + } else { + s << util::CamelCaseToUnderScore(field->GetName()); + } + + if (i != parent_params.size() - 1) { + s << ", "; + } + } + s << ") "; + } + + // Build a list of parameters that excludes all parent parameters. + FieldList saved_params; + for (const auto& field : params) { + if (parent_params.GetField(field->GetName()) == nullptr) { + saved_params.AppendField(field); + } + } + if (parent_ != nullptr && saved_params.size() > 0) { + s << ","; + } + for (int i = 0; i < saved_params.size(); i++) { + const auto& saved_param_name = util::CamelCaseToUnderScore(saved_params[i]->GetName()); + s << saved_param_name << "_(" << saved_param_name << ")"; + if (i != saved_params.size() - 1) { + s << ","; + } + } + s << " {"; + + FieldList params_to_validate = GetParametersToValidate(); + + if (params_to_validate.size() > 0) { + s << "CheckParameterValues("; + for (int i = 0; i < params_to_validate.size(); i++) { + s << util::CamelCaseToUnderScore(params_to_validate[i]->GetName()) << "_"; + if (i != params_to_validate.size() - 1) { + s << ", "; + } + } + s << ");"; + } + + s << "}\n"; +} + +void PacketDef::GenBuilderMembers(std::ostream& s) const { + // Add the parameter list. + for (int i = 0; i < fields_.size(); i++) { + if (fields_[i]->GenBuilderParameter(s)) { + s << "_;"; + } + } +} diff --git a/gd/packet/parser/packet_def.h b/gd/packet/parser/packet_def.h new file mode 100644 index 000000000..647fc4df7 --- /dev/null +++ b/gd/packet/parser/packet_def.h @@ -0,0 +1,86 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include "enum_def.h" +#include "field_list.h" +#include "fields/packet_field.h" + +class PacketDef { + public: + PacketDef(std::string name, FieldList fields); + PacketDef(std::string name, FieldList fields, PacketDef* parent); + + void AddParentConstraint(std::string field_name, std::variant value); + + // Assign all size fields to their corresponding variable length fields. + // Will crash if + // - there aren't any fields that don't match up to a field. + // - the size field points to a fixed size field. + // - if the size field comes after the variable length field. + void AssignSizeFields(); + + void SetEndianness(bool is_little_endian); + + // Get the size for the packet. You scan specify without_payload in order + // to exclude payload fields as child packets will be overriding it. + Size GetSize(bool without_payload = false) const; + + // Get the offset until the field is reached, if there is no field + // returns an empty Size. from_end requests the offset to the field + // starting from the end() iterator. If there is a field with an unknown + // size along the traversal, then an empty size is returned. + Size GetOffsetForField(std::string field_name, bool from_end = false) const; + + void GenParserDefinition(std::ostream& s) const; + + void GenParserFieldGetter(std::ostream& s, const PacketField* field) const; + + void GenSerialize(std::ostream& s) const; + + void GenBuilderSize(std::ostream& s) const; + + void GenValidator(std::ostream& s) const; + + void GenBuilderDefinition(std::ostream& s) const; + + FieldList GetParamList() const; + + FieldList GetParametersToValidate() const; + + void GenBuilderCreate(std::ostream& s) const; + + void GenBuilderParameterChecker(std::ostream& s) const; + + void GenBuilderConstructor(std::ostream& s) const; + + void GenBuilderMembers(std::ostream& s) const; + + std::string name_; + FieldList fields_; + + std::variant specialize_on_; + std::variant specialization_value_; + + PacketDef* parent_; // Parent packet type + + std::map> parent_constraints_; + bool is_little_endian_; +}; diff --git a/gd/packet/parser/parse_location.h b/gd/packet/parser/parse_location.h new file mode 100644 index 000000000..80bdc729a --- /dev/null +++ b/gd/packet/parser/parse_location.h @@ -0,0 +1,31 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +class ParseLocation { + public: + ParseLocation() : line_(-1) {} + + ParseLocation(int line) : line_(line) {} + + int GetLine() { + return line_; + } + + private: + int line_; +}; diff --git a/gd/packet/parser/size.h b/gd/packet/parser/size.h new file mode 100644 index 000000000..926d12c84 --- /dev/null +++ b/gd/packet/parser/size.h @@ -0,0 +1,133 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +class Size { + public: + Size() {} + + Size(int bits) { + is_valid_ = true; + bits_ = bits; + } + + Size(std::string dynamic) { + is_valid_ = true; + dynamic_.push_back(dynamic); + } + + Size(int bits, std::string dynamic) { + is_valid_ = true; + bits_ = bits; + dynamic_.push_back(dynamic); + } + + Size(const Size& size) { + is_valid_ = size.is_valid_; + bits_ = size.bits_; + dynamic_ = size.dynamic_; + } + + std::string dynamic_string() { + if (dynamic_.empty()) return " 0 /* dynamic */ "; + + std::stringstream result; + // Print everything but the last element then append it manually to avoid + // the trailing "+" operator. + std::copy(dynamic_.begin(), dynamic_.end() - 1, std::ostream_iterator(result, " + ")); + result << dynamic_.back(); + return result.str(); + } + + std::vector dynamic_string_list() { + return dynamic_; + } + + bool empty() { + return !is_valid_; + } + + bool has_bits() { + return bits_ != 0; + } + + bool has_dynamic() { + return !dynamic_.empty(); + } + + int bits() { + return bits_; + } + + int bytes() { + return bits_ / 8; + } + + Size operator+(int rhs) { + return Size(bits_ + rhs); + } + + Size operator+(std::string rhs) { + auto ret = Size(); + ret.is_valid_ = true; + ret.dynamic_.insert(ret.dynamic_.end(), dynamic_.begin(), dynamic_.end()); + ret.dynamic_.push_back(rhs); + return ret; + } + + Size operator+(const Size& rhs) { + auto ret = Size(bits_ += rhs.bits_); + ret.is_valid_ = true; + ret.dynamic_.insert(ret.dynamic_.end(), dynamic_.begin(), dynamic_.end()); + ret.dynamic_.insert(ret.dynamic_.end(), rhs.dynamic_.begin(), rhs.dynamic_.end()); + return ret; + } + + Size& operator+=(int rhs) { + is_valid_ = true; + bits_ += rhs; + return *this; + } + + Size& operator+=(std::string rhs) { + is_valid_ = true; + dynamic_.push_back(rhs); + return *this; + } + + Size& operator+=(const Size& rhs) { + is_valid_ = true; + bits_ += rhs.bits_; + dynamic_.insert(dynamic_.end(), rhs.dynamic_.begin(), rhs.dynamic_.end()); + return *this; + } + + std::string ToString() { + std::stringstream str; + str << "Bits: " << bits_ << " | " + << "Dynamic: " << dynamic_string(); + return str.str(); + } + + private: + bool is_valid_ = false; + int bits_ = 0; + std::vector dynamic_; +}; diff --git a/gd/packet/parser/test/Android.bp b/gd/packet/parser/test/Android.bp new file mode 100644 index 000000000..0aff068c6 --- /dev/null +++ b/gd/packet/parser/test/Android.bp @@ -0,0 +1,20 @@ +genrule { + name: "BluetoothPacketParserTestPacketPdlGen_h", + tools: [ + "bluetooth_packetgen", + ], + cmd: "mkdir -p $(genDir)/packet/parser/test/; $(location bluetooth_packetgen) < $(in) > $(genDir)/packet/parser/test/generated_packets.h 2>/dev/null", + srcs: [ + "test_packet.pdl", + ], + out: [ + "packet/parser/test/generated_packets.h", + ], +} + +filegroup { + name: "BluetoothPacketParserTestPacketTestSources", + srcs: [ + "generated_packet_test.cc", + ], +} diff --git a/gd/packet/parser/test/generated_packet_test.cc b/gd/packet/parser/test/generated_packet_test.cc new file mode 100644 index 000000000..3b6d126ef --- /dev/null +++ b/gd/packet/parser/test/generated_packet_test.cc @@ -0,0 +1,115 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "packet/parser/test/generated_packets.h" + +#include +#include +#include + +#include "os/log.h" +#include "packet/bit_inserter.h" +#include "packet/raw_builder.h" + +using bluetooth::packet::BitInserter; +using bluetooth::packet::kLittleEndian; +using bluetooth::packet::RawBuilder; +using std::vector; + +namespace { +vector child_two_two_three = { + 0x20 /* Reserved : 4, FourBits::TWO */, + 0x03 /* FourBits::THREE, Reserved : 4 */, +}; +vector child = { + 0x12 /* fixed */, + 0x02 /* Size of the payload */, + 0xa1 /* First byte of the payload */, + 0xa2, + 0xb1 /* footer */, + 0xc1 /* First byte of the FCS */, + 0xc2, +}; + +} // namespace + +namespace bluetooth { +namespace packet { +namespace parser { + +TEST(GeneratedPacketTest, testChildTwoTwoThree) { + auto packet = ChildTwoTwoThreeBuilder::Create(); + + ASSERT_EQ(child_two_two_three.size(), packet->size()); + + std::shared_ptr> packet_bytes = std::make_shared>(); + BitInserter it(*packet_bytes); + packet->Serialize(it); + + ASSERT_EQ(packet_bytes->size(), child_two_two_three.size()); + for (size_t i = 0; i < child_two_two_three.size(); i++) { + ASSERT_EQ(packet_bytes->at(i), child_two_two_three[i]); + } + + PacketView packet_bytes_view(packet_bytes); + ParentView wrong_view(packet_bytes_view); + ASSERT_FALSE(wrong_view.IsValid()); + + ParentTwoView parent_view(packet_bytes_view); + ASSERT_TRUE(parent_view.IsValid()); + ASSERT_EQ(FourBits::TWO, parent_view.GetFourBits()); + + ChildTwoTwoView child_view(packet_bytes_view); + ASSERT_TRUE(child_view.IsValid()); + ASSERT_EQ(FourBits::THREE, child_view.GetMoreBits()); + + ChildTwoTwoThreeView grandchild_view(packet_bytes_view); + ASSERT_TRUE(grandchild_view.IsValid()); +} + +TEST(GeneratedPacketTest, testChild) { + auto packet = ChildBuilder::Create(0xa2a1 /* field_name */, 0xb1 /* footer */, 0xc2c1 /* fcs */); + + ASSERT_EQ(child.size(), packet->size()); + + std::shared_ptr> packet_bytes = std::make_shared>(); + BitInserter it(*packet_bytes); + packet->Serialize(it); + + ASSERT_EQ(packet_bytes->size(), child.size()); + for (size_t i = 0; i < child.size(); i++) { + ASSERT_EQ(packet_bytes->at(i), child[i]); + } + + PacketView packet_bytes_view(packet_bytes); + ParentView parent_view(packet_bytes_view); + ASSERT_TRUE(parent_view.IsValid()); + auto payload = parent_view.GetPayload(); + + ASSERT_EQ(child[1 /* skip fixed field */], payload.size()); + for (size_t i = 0; i < payload.size(); i++) { + ASSERT_EQ(child[i + 2 /* fixed & size */], payload[i]); + } + + ChildView child_view(packet_bytes_view); + ASSERT_TRUE(child_view.IsValid()); + + ASSERT_EQ(0xa2a1, child_view.GetFieldName()); +} + +} // namespace parser +} // namespace packet +} // namespace bluetooth diff --git a/gd/packet/parser/test/test_packet.pdl b/gd/packet/parser/test/test_packet.pdl new file mode 100644 index 000000000..9f904c799 --- /dev/null +++ b/gd/packet/parser/test/test_packet.pdl @@ -0,0 +1,40 @@ +little_endian_packets + +packet Parent { + fixed = 0x12 : 8, + size(payload) : 8, + payload, + Footer : 8, + Fcs : 16, +} + +packet Child : Parent { + FieldName : 16, +} + +enum FourBits : 4 { + ONE = 1, + TWO = 2, + THREE = 3, + LAZY_ME = 15, +} + +packet ParentTwo { + reserved : 4, + FourBits : FourBits, + payload, +} + +packet ChildTwoThree : ParentTwo (FourBits = THREE) { + MoreBits : FourBits, + reserved : 4, + SixteenBits : 16 +} + +packet ChildTwoTwo : ParentTwo (FourBits = TWO) { + MoreBits : FourBits, + reserved : 4, +} + +packet ChildTwoTwoThree :ChildTwoTwo (MoreBits = THREE) { +} diff --git a/gd/packet/parser/type_def.h b/gd/packet/parser/type_def.h new file mode 100644 index 000000000..3b4c77f25 --- /dev/null +++ b/gd/packet/parser/type_def.h @@ -0,0 +1,33 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +class TypeDef { + public: + TypeDef(std::string name) : name_(name) {} + + TypeDef(std::string name, int size) : name_(name), size_(size) {} + + std::string GetTypeName() const { + return name_; + } + + const std::string name_; + const int size_{-1}; +}; diff --git a/gd/packet/parser/util.h b/gd/packet/parser/util.h new file mode 100644 index 000000000..dbd10bac9 --- /dev/null +++ b/gd/packet/parser/util.h @@ -0,0 +1,84 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include + +#include "logging.h" + +namespace util { + +inline std::string GetTypeForSize(int size) { + if (size > 64) { + ERROR() << __func__ << ": Cannot use a type larger than 64 bits. (" << size << ")\n"; + } + + if (size <= 8) return "uint8_t"; + + if (size <= 16) return "uint16_t"; + + if (size <= 32) return "uint32_t"; + + return "uint64_t"; +} + +inline int RoundSizeUp(int size) { + if (size > 64) { + ERROR() << __func__ << ": Cannot use a type larger than 64 bits. (" << size << ")\n"; + } + + if (size <= 8) return 8; + if (size <= 16) return 16; + if (size <= 32) return 32; + return 64; +} + +// Returns the max value that can be contained unsigned in a number of bits. +inline uint64_t GetMaxValueForBits(int bits) { + if (bits > 64) { + ERROR() << __func__ << ": Cannot use a type larger than 64 bits. (" << bits << ")\n"; + } + + uint64_t max = 0; + for (int i = 0; i < bits; i++) { + max <<= 1; + max |= 1; + } + + return max; +} + +inline std::string CamelCaseToUnderScore(std::string value) { + // Use static to avoid compiling the regex more than once. + static const std::regex camel_case_regex("[A-Z][a-z0-9]*"); + + // Add an underscore to the end of each pattern match. + value = std::regex_replace(value, camel_case_regex, "$&_"); + + // Remove the last underscore at the end of the string. + value.pop_back(); + + // Convert all characters to lowercase. + std::transform(value.begin(), value.end(), value.begin(), [](unsigned char c) { return std::tolower(c); }); + + return value; +} + +} // namespace util -- 2.11.0