| // 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 <inttypes.h> |
| #include <lib/fidl/coding.h> |
| #include <lib/fidl/internal.h> |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <string.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 FormatEnumName(StringBuilder* str, const fidl::FidlCodedEnum* coded_enum) { |
| if (coded_enum->name) { |
| str->Append(coded_enum->name); |
| } else { |
| str->Append("enum"); |
| } |
| } |
| |
| void FormatBitsName(StringBuilder* str, const fidl::FidlCodedBits* coded_bits) { |
| if (coded_bits->name) { |
| str->Append(coded_bits->name); |
| } else { |
| str->Append("bits"); |
| } |
| } |
| |
| 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 FormatTableName(StringBuilder* str, const fidl::FidlCodedTable* coded_table) { |
| if (coded_table->name) { |
| str->Append(coded_table->name); |
| } else { |
| str->Append("table"); |
| } |
| } |
| |
| void FormatXUnionName(StringBuilder* str, const fidl::FidlCodedXUnion* coded_xunion) { |
| if (coded_xunion->name) { |
| str->Append(coded_xunion->name); |
| } else { |
| str->Append("xunion"); |
| } |
| } |
| |
| 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::kFidlTypeEnum: |
| FormatEnumName(str, &type->coded_enum); |
| break; |
| case fidl::kFidlTypeBits: |
| FormatBitsName(str, &type->coded_bits); |
| break; |
| 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::kFidlHandleSubtypeBti: |
| str->Append("bti"); |
| break; |
| case fidl::kFidlHandleSubtypeChannel: |
| str->Append("channel"); |
| break; |
| case fidl::kFidlHandleSubtypeEvent: |
| str->Append("event"); |
| break; |
| case fidl::kFidlHandleSubtypeEventpair: |
| str->Append("eventpair"); |
| break; |
| case fidl::kFidlHandleSubtypeException: |
| str->Append("exception"); |
| break; |
| case fidl::kFidlHandleSubtypeFifo: |
| str->Append("fifo"); |
| break; |
| case fidl::kFidlHandleSubtypeGuest: |
| str->Append("guest"); |
| break; |
| case fidl::kFidlHandleSubtypeInterrupt: |
| str->Append("interrupt"); |
| break; |
| case fidl::kFidlHandleSubtypeIommu: |
| str->Append("iommu"); |
| break; |
| case fidl::kFidlHandleSubtypeJob: |
| str->Append("job"); |
| break; |
| case fidl::kFidlHandleSubtypeLog: |
| str->Append("log"); |
| break; |
| case fidl::kFidlHandleSubtypePager: |
| str->Append("pager"); |
| break; |
| case fidl::kFidlHandleSubtypePciDevice: |
| str->Append("pcidevice"); |
| break; |
| case fidl::kFidlHandleSubtypePmt: |
| str->Append("pmt"); |
| break; |
| case fidl::kFidlHandleSubtypePort: |
| str->Append("port"); |
| break; |
| case fidl::kFidlHandleSubtypeProcess: |
| str->Append("process"); |
| break; |
| case fidl::kFidlHandleSubtypeProfile: |
| str->Append("profile"); |
| break; |
| case fidl::kFidlHandleSubtypeResource: |
| str->Append("resource"); |
| break; |
| case fidl::kFidlHandleSubtypeSocket: |
| str->Append("socket"); |
| break; |
| case fidl::kFidlHandleSubtypeSuspendToken: |
| str->Append("suspendtoken"); |
| break; |
| case fidl::kFidlHandleSubtypeThread: |
| str->Append("thread"); |
| break; |
| case fidl::kFidlHandleSubtypeTimer: |
| str->Append("timer"); |
| break; |
| case fidl::kFidlHandleSubtypeVcpu: |
| str->Append("vcpu"); |
| break; |
| case fidl::kFidlHandleSubtypeVmar: |
| str->Append("vmar"); |
| break; |
| case fidl::kFidlHandleSubtypeVmo: |
| str->Append("vmo"); |
| break; |
| 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; |
| case fidl::kFidlTypeTable: |
| FormatTableName(str, &type->coded_table); |
| break; |
| case fidl::kFidlTypeXUnion: |
| FormatXUnionName(str, &type->coded_xunion); |
| break; |
| case fidl::kFidlTypePrimitive: |
| 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(); |
| } |