blob: e30d7d11ebc3c2798c236b2ae189a48e13d72e4f [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <zircon/assert.h>
#include <algorithm>
#include <optional>
#include <sstream>
#include <string_view>
#include <type_traits>
#include <utility>
#include "tools/kazoo/alias_workaround.h"
#include "tools/kazoo/output_util.h"
#include "tools/kazoo/outputs.h"
#include "tools/kazoo/string_util.h"
using namespace std::literals;
namespace {
std::string ToSingular(const std::string& s) {
if (s.size() > 1 && s[s.size() - 1] == 's') {
return s.substr(0, s.size() - 1);
}
return s;
}
class Formatter {
public:
explicit Formatter(const SyscallLibrary* library) : library_(library) {}
struct Names {
std::string base_name; // signals
std::string type_name; // zxio_signals_t
};
Names Format(const Alias& alias) {
return Names{
.base_name = alias.base_name(),
.type_name = library_->name() + "_" + alias.base_name() + "_t",
};
}
Names Format(const Enum& e) {
return Names{
.base_name = e.base_name(),
.type_name = library_->name() + "_" + e.base_name() + "_t",
};
}
struct StructNames {
std::string base_name; // signals
std::string type_name; // zxio_dirent_t
std::string c_struct_name; // zxio_dirent
};
StructNames Format(const Table& table) {
return StructNames{
.base_name = table.base_name(),
.type_name = library_->name() + "_" + table.base_name() + "_t",
.c_struct_name = library_->name() + "_" + table.base_name(),
};
}
std::string FormatMember(const Enum& e, const std::string& member_name) {
return ToUpperAscii(library_->name()) + "_" + ToUpperAscii(ToSingular(e.base_name())) + "_" +
member_name;
}
std::string TypeName(const Type& type) {
return std::visit(
[this](auto&& type) -> std::string {
using T = std::decay_t<decltype(type)>;
if constexpr (std::is_same_v<T, TypeBool>) {
return "bool";
}
if constexpr (std::is_same_v<T, TypeChar>) {
return "char";
}
if constexpr (std::is_same_v<T, TypeInt32>) {
return "int32_t";
}
if constexpr (std::is_same_v<T, TypeInt64>) {
return "int64_t";
}
if constexpr (std::is_same_v<T, TypeSizeT>) {
return "size_t";
}
if constexpr (std::is_same_v<T, TypeUint16>) {
return "uint16_t";
}
if constexpr (std::is_same_v<T, TypeUint32>) {
return "uint32_t";
}
if constexpr (std::is_same_v<T, TypeUint64>) {
return "uint64_t";
}
if constexpr (std::is_same_v<T, TypeUint8>) {
return "uint8_t";
}
if constexpr (std::is_same_v<T, TypeUintptrT>) {
return "uintptr_t";
}
if constexpr (std::is_same_v<T, TypeVoid>) {
return "void";
}
if constexpr (std::is_same_v<T, TypeZxBasicAlias>) {
return type.name();
}
if constexpr (std::is_same_v<T, TypeAlias>) {
return Format(type.alias_data()).type_name;
}
if constexpr (std::is_same_v<T, TypeEnum>) {
return Format(type.enum_data()).type_name;
}
if constexpr (std::is_same_v<T, TypeHandle>) {
return "zx_handle_t";
}
if constexpr (std::is_same_v<T, TypePointer>) {
return TypeName(type.pointed_to_type()) + "*";
}
ZX_ASSERT(false && "Unhandled type in TypeName");
},
type.type_data());
}
std::string FormatConstant(const Enum& e, uint64_t raw) {
return std::visit(
[raw](auto&& type) -> std::string {
std::stringstream stream;
stream << std::hex << raw;
std::string num_str = "0x" + stream.str();
using T = std::decay_t<decltype(type)>;
if constexpr (std::is_same_v<T, TypeUint8>) {
return num_str;
}
if constexpr (std::is_same_v<T, TypeUint16>) {
return num_str;
}
if constexpr (std::is_same_v<T, TypeUint32>) {
return num_str + "u";
}
if constexpr (std::is_same_v<T, TypeUint64>) {
return num_str + "ul";
}
if constexpr (std::is_same_v<T, TypeChar>) {
return num_str;
}
if constexpr (std::is_same_v<T, TypeInt32>) {
return num_str;
}
if constexpr (std::is_same_v<T, TypeInt64>) {
return num_str + "l";
}
ZX_PANIC("Unhandled primitive type");
},
e.underlying_type().type_data());
}
private:
const SyscallLibrary* library_;
};
std::string MakeTitleLine(const std::string& base_name) {
std::vector<std::string> words = SplitString(base_name, '_', kTrimWhitespace);
std::for_each(words.begin(), words.end(), [](std::string &s){ s[0] = ToUpperASCII(s[0]); });
std::string title = JoinStrings(words, " ");
// Pad up to 80 columns. 4 is to account for space characters around.
int trailing_length = 80 - static_cast<int>(title.size()) - 4;
if (trailing_length > 0) {
return "// " + title + " " + std::string(trailing_length, '-');
}
return "// " + title;
}
void PrintDocComments(const std::vector<std::string>& lines, Writer* writer,
uint32_t indent_level = 0) {
if (!lines.empty()) {
writer->PrintSpacerLine();
}
std::string indent(2 * indent_level, ' ');
for (const auto& line : lines) {
if (!line.empty()) {
writer->Printf("%s// %s\n", indent.c_str(), line.c_str());
} else {
writer->Printf("%s//\n", indent.c_str());
}
}
}
} // namespace
bool CUlibHeaderOutput(const SyscallLibrary& library, Writer* writer) {
CopyrightHeaderWithCppComments(writer);
std::string prelude = R"(
#ifndef LIB_ZXIO_TYPES_H_
#define LIB_ZXIO_TYPES_H_
#include <stdbool.h>
#include <stdint.h>
#include <zircon/compiler.h>
// This header defines the public types used in the zxio and zxio_ops interface.
__BEGIN_CDECLS
)";
writer->Printf("%s\n", TrimString(prelude, "\n").c_str());
writer->Puts("\n");
Formatter formatter(&library);
for (const auto& bits : library.bits()) {
auto names = formatter.Format(*bits);
std::string title_line = MakeTitleLine(names.base_name);
writer->Printf("%s\n", title_line.c_str());
writer->Puts("\n");
PrintDocComments(bits->description(), writer);
writer->Printf("typedef %s %s;\n", formatter.TypeName(bits->underlying_type()).c_str(),
names.type_name.c_str());
writer->Puts("\n");
writer->Printf("#define %s ((%s)%s)\n", formatter.FormatMember(*bits, "NONE").c_str(),
names.type_name.c_str(), formatter.FormatConstant(*bits, 0).c_str());
writer->Puts("\n");
uint64_t all = 0;
for (const auto& k : bits->members()) {
auto v = bits->ValueForMember(k);
PrintDocComments(v.description, writer);
writer->Printf("#define %s ((%s)%s)\n", formatter.FormatMember(*bits, k).c_str(),
names.type_name.c_str(), formatter.FormatConstant(*bits, v.value).c_str());
all |= v.value;
}
writer->Puts("\n");
writer->Printf("#define %s ((%s)%s)\n", formatter.FormatMember(*bits, "ALL").c_str(),
names.type_name.c_str(), formatter.FormatConstant(*bits, all).c_str());
writer->Puts("\n");
}
for (const auto& e : library.enums()) {
auto names = formatter.Format(*e);
std::string title_line = MakeTitleLine(names.base_name);
writer->Printf("%s\n", title_line.c_str());
writer->Puts("\n");
PrintDocComments(e->description(), writer);
writer->Printf("typedef %s %s;\n", formatter.TypeName(e->underlying_type()).c_str(),
names.type_name.c_str());
writer->Printf("\n");
for (const auto& k : e->members()) {
auto v = e->ValueForMember(k);
PrintDocComments(v.description, writer);
writer->Printf("#define %s ((%s)%s)\n", formatter.FormatMember(*e, k).c_str(),
names.type_name.c_str(), formatter.FormatConstant(*e, v.value).c_str());
}
writer->Puts("\n");
}
for (const auto& alias : library.type_aliases()) {
Type workaround_type;
if (AliasWorkaround(alias->original_name(), library, &workaround_type)) {
// Hide workaround types
continue;
}
auto names = formatter.Format(*alias);
PrintDocComments(alias->description(), writer);
writer->Printf("typedef %s %s;\n",
formatter.TypeName(library.TypeFromName(alias->partial_type_ctor())).c_str(),
names.type_name.c_str());
writer->Puts("\n");
}
for (const auto& table : library.tables()) {
bool all_required = true;
for (const auto& member : table->members()) {
if (member.required() == Required::kNo) {
all_required = false;
break;
}
}
auto names = formatter.Format(*table);
std::string setter_macro_name = ToUpperAscii(names.c_struct_name) + "_SET";
PrintDocComments(table->description(), writer);
if (!all_required) {
writer->Printf(R"(//
// Optional fields have corresponding presence indicators. When creating
// a new object, it is desirable to use the %s helper macro
// to set the fields, to avoid forgetting to change the presence indicator.
)", setter_macro_name.c_str());
}
writer->Printf("typedef struct %s {", names.c_struct_name.c_str());
writer->Puts("\n");
// Pack optional fields together
for (const auto& member : table->members()) {
if (member.required() == Required::kYes) {
continue;
}
PrintDocComments(member.description(), writer, 1);
writer->Printf(" %s %s;\n", formatter.TypeName(member.type()).c_str(),
member.name().c_str());
}
if (!all_required) {
std::string presence_bits_name = names.c_struct_name + "_has_t";
writer->Printf(R"(
// Presence indicator for these fields.
//
// If a particular field is absent, it should be set to zero/none,
// and the corresponding presence indicator will be false.
// Therefore, a completely empty |%s| may be conveniently
// obtained via value-initialization e.g. `%s a = {};`.
)", names.type_name.c_str(), names.type_name.c_str());
writer->Printf(" struct %s {\n", presence_bits_name.c_str());
for (const auto& member : table->members()) {
if (member.required() == Required::kYes) {
continue;
}
writer->Printf(" bool %s;\n", member.name().c_str());
}
writer->Printf(" } has;\n");
}
// Followed by required fields
for (const auto& member : table->members()) {
if (member.required() == Required::kNo) {
continue;
}
PrintDocComments(member.description(), writer, 1);
writer->Printf(" %s %s;\n", formatter.TypeName(member.type()).c_str(),
member.name().c_str());
}
writer->Printf("} %s;\n", names.type_name.c_str());
writer->Printf(R"(
#define %s(%s, field_name, value) \
do { \
%s* _tmp_%s= &(%s); \
_tmp_%s->field_name = value; \
_tmp_%s->has.field_name = true; \
} while (0)
)",
setter_macro_name.c_str(), names.base_name.c_str(), names.type_name.c_str(),
names.base_name.c_str(), names.base_name.c_str(), names.base_name.c_str(),
names.base_name.c_str());
writer->Puts("\n");
}
std::string epilogue = R"(
__END_CDECLS
#endif // LIB_ZXIO_TYPES_H_
)";
writer->Printf("%s\n", TrimString(epilogue, "\n").c_str());
return true;
}