// Copyright 2017 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 <lib/fidl/coding.h>
#include <inttypes.h>
#include <stdarg.h>
#include <string.h>
#include <lib/fidl/internal.h>
#include <zircon/assert.h>
#include <zircon/compiler.h>
namespace {
class StringBuilder {
public:
StringBuilder(char* buffer, size_t capacity)
: buffer_(buffer), capacity_(capacity) {}
size_t length() const { return length_; }
void Append(const char* data, size_t length) {
size_t remaining = capacity_ - length_;
if (length > remaining) {
length = remaining;
}
memcpy(buffer_ + length_, data, length);
length_ += length;
}
void Append(const char* data) {
Append(data, strlen(data));
}
void AppendPrintf(const char* format, ...) __PRINTFLIKE(2, 3) {
va_list ap;
va_start(ap, format);
AppendVPrintf(format, ap);
va_end(ap);
}
void AppendVPrintf(const char* format, va_list ap) {
size_t remaining = capacity_ - length_;
if (remaining == 0u) {
return;
}
int count = vsnprintf(buffer_ + length_, remaining, format, ap);
if (count <= 0) {
return;
}
size_t length = static_cast<size_t>(count);
length_ += (length >= remaining ? remaining : length);
}
private:
char* buffer_;
size_t capacity_;
size_t length_ = 0u;
};
void FormatNullability(StringBuilder* str, fidl::FidlNullability nullable) {
if (nullable == fidl::kNullable) {
str->Append("?");
}
}
void FormatStructName(StringBuilder* str, const fidl::FidlCodedStruct* coded_struct) {
if (coded_struct->name) {
str->Append(coded_struct->name);
} else {
str->Append("struct");
}
}
void FormatUnionName(StringBuilder* str, const fidl::FidlCodedUnion* coded_union) {
if (coded_union->name) {
str->Append(coded_union->name);
} else {
str->Append("union");
}
}
void FormatTypeName(StringBuilder* str, const fidl_type_t* type);
void FormatElementName(StringBuilder* str, const fidl_type_t* type) {
if (type) {
FormatTypeName(str, type);
} else {
// TODO(jeffbrown): Print the actual primitive type name, assuming we
// start recording that information in the tables.
str->Append("primitive");
}
}
void FormatTypeName(StringBuilder* str, const fidl_type_t* type) {
switch (type->type_tag) {
case fidl::kFidlTypeStruct:
FormatStructName(str, &type->coded_struct);
break;
case fidl::kFidlTypeStructPointer:
FormatStructName(str, type->coded_struct_pointer.struct_type);
str->Append("?");
break;
case fidl::kFidlTypeUnion:
FormatUnionName(str, &type->coded_union);
break;
case fidl::kFidlTypeUnionPointer:
FormatUnionName(str, type->coded_union_pointer.union_type);
str->Append("?");
break;
case fidl::kFidlTypeArray:
str->Append("array<");
FormatElementName(str, type->coded_array.element);
str->Append(">");
str->AppendPrintf(":%" PRIu32, type->coded_array.array_size /
type->coded_array.element_size);
break;
case fidl::kFidlTypeString:
str->Append("string");
if (type->coded_string.max_size != FIDL_MAX_SIZE) {
str->AppendPrintf(":%" PRIu32, type->coded_string.max_size);
}
FormatNullability(str, type->coded_string.nullable);
break;
case fidl::kFidlTypeHandle:
str->Append("handle");
if (type->coded_handle.handle_subtype) {
str->Append("<");
switch (type->coded_handle.handle_subtype) {
case fidl::kFidlHandleSubtypeHandle:
str->Append("handle");
break;
case fidl::kFidlHandleSubtypeProcess:
str->Append("process");
break;
case fidl::kFidlHandleSubtypeThread:
str->Append("thread");
break;
case fidl::kFidlHandleSubtypeVmo:
str->Append("vmo");
break;
case fidl::kFidlHandleSubtypeChannel:
str->Append("channel");
break;
case fidl::kFidlHandleSubtypeEvent:
str->Append("event");
break;
case fidl::kFidlHandleSubtypePort:
str->Append("port");
break;
case fidl::kFidlHandleSubtypeInterrupt:
str->Append("interrupt");
break;
case fidl::kFidlHandleSubtypeLog:
str->Append("log");
break;
case fidl::kFidlHandleSubtypeSocket:
str->Append("socket");
break;
case fidl::kFidlHandleSubtypeResource:
str->Append("resource");
break;
case fidl::kFidlHandleSubtypeEventpair:
str->Append("eventpair");
break;
case fidl::kFidlHandleSubtypeJob:
str->Append("job");
break;
case fidl::kFidlHandleSubtypeVmar:
str->Append("vmar");
break;
case fidl::kFidlHandleSubtypeFifo:
str->Append("fifo");
break;
case fidl::kFidlHandleSubtypeGuest:
str->Append("guest");
break;
case fidl::kFidlHandleSubtypeTimer:
str->Append("timer");
break;
// TODO(pascallouis): Add support for iomap, pci, and hypervisor
// when they are supported in FIDL.
default:
str->AppendPrintf("%" PRIu32, type->coded_handle.handle_subtype);
break;
}
str->Append(">");
}
FormatNullability(str, type->coded_handle.nullable);
break;
case fidl::kFidlTypeVector:
str->Append("vector<");
FormatElementName(str, type->coded_vector.element);
str->Append(">");
if (type->coded_vector.max_count != FIDL_MAX_SIZE) {
str->AppendPrintf(":%" PRIu32, type->coded_vector.max_count);
}
FormatNullability(str, type->coded_vector.nullable);
break;
default:
ZX_PANIC("unrecognized tag");
break;
}
}
} // namespace
size_t fidl_format_type_name(const fidl_type_t* type,
char* buffer, size_t capacity) {
if (!type || !buffer || !capacity) {
return 0u;
}
StringBuilder str(buffer, capacity);
FormatTypeName(&str, type);
return str.length();
}