blob: 81fc0d76a2e8386f46e4764b96a9790adfd09566 [file] [log] [blame]
/*
* 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 <map>
#include <set>
#include <vector>
#include "fields/all_fields.h"
#include "fields/packet_field.h"
using FieldListIterator = std::vector<PacketField*>::const_iterator;
using ReverseFieldListIterator = std::vector<PacketField*>::const_reverse_iterator;
class FieldList {
public:
FieldList() = default;
FieldList(std::vector<PacketField*> fields) {
for (PacketField* field : fields) {
AppendField(field);
}
}
template <class Iterator>
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() == PayloadField::kFieldType || field->GetFieldType() == BodyField::kFieldType) {
break;
}
ret.AppendField(*it);
}
return ret;
}
FieldList GetFieldsAfterPayloadOrBody() const {
FieldListIterator it;
for (it = begin(); it != end(); it++) {
const auto& field = *it;
if (field->GetFieldType() == PayloadField::kFieldType || field->GetFieldType() == BodyField::kFieldType) {
// Increment it once to get first field after payload/body.
it++;
break;
}
}
return FieldList(it, end());
}
FieldList GetFieldsWithTypes(std::set<std::string> 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<std::string> 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() == PayloadField::kFieldType) {
if (has_body_) {
ERROR(field) << "Packet already has a body.";
}
has_payload_ = true;
}
if (field->GetFieldType() == BodyField::kFieldType) {
if (has_payload_) {
ERROR(field) << "Packet already has a payload.";
}
has_body_ = true;
}
field_map_.insert(std::pair(field->GetName(), field));
}
std::vector<PacketField*> field_list_;
std::map<std::string, PacketField*> field_map_;
bool has_payload_ = false;
bool has_body_ = false;
};