text: // Copyright 2018 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 "fidl/c_generator.h"
  
  #include "fidl/attributes.h"
  #include "fidl/flat_ast.h"
  #include "fidl/names.h"
  #include "fidl/type_shape.h"
  
  namespace fidl {
  
  namespace {
  
  // RAII helper class to reset the iostream to its original flags.
  class IOFlagsGuard {
   public:
    explicit IOFlagsGuard(std::ostream* stream) : stream_(stream), flags_(stream_->flags()) {}
  
    ~IOFlagsGuard() { stream_->setf(flags_); }
  
   private:
    std::ostream* stream_;
    std::ios::fmtflags flags_;
  };
  
  // Various string values are looked up or computed in these
  // functions. Nothing else should be dealing in string literals, or
  // computing strings from these or AST values.
  
  constexpr const char* kIndent = "    ";
  
  // Mapping of library name to set of declaration names.
  // These declarations are treated as though they have the
  // [ForDeprecatedCBindings] attribute even though they violate the constraints
  // enforced on them.
  //
  // For protocols this means that some of the methods can't be supported and
  // will simply be left out (unless they're listed below in allowed_methods).
  //
  // For structs this means that a member can have an unsupported type such as a
  // vector of strings or a union.
  const std::map<std::string, std::set<std::string>> allowed_decls({
      {"fuchsia.tracing.provider", {"Provider", "ProviderConfig", "StartOptions"}},
      {"fuchsia.logger", {"Log", "LogSink", "LogMessage", "LogListenerSafe", "LogFilterOptions"}},
      {"fuchsia.device.manager",
       {
           "BindInstruction",
           "CompositeDeviceDescriptor",
           "Coordinator",
           "DevhostController",
           "DeviceController",
           "DeviceFragment",
           "DeviceFragmentPart",
           "DeviceMetadata",
           "DeviceProperty",
       }},
      {"fuchsia.hardware.power.statecontrol", {"Admin"}},
      {"fidl.test.llcpp.dirent", {"DirEntTestInterface"}},
  });
  
  bool DeclAlwaysAllowed(const flat::Name& name) {
    auto library_name = flat::LibraryName(name.library(), ".");
  
    auto iter = allowed_decls.find(library_name);
    if (iter != allowed_decls.end()) {
      const auto& decls = iter->second;
      if (decls.find(std::string(name.decl_name())) != decls.end()) {
        return true;
      }
    }
    return false;
  }
  
  // Mapping of library name to mapping of protocol name to set of methods.
  // Data structures should be generated for these methods even if they violate
  // the constraints of the simple C bindings.
  std::map<std::string, std::map<std::string, std::set<std::string>>> allowed_methods({
      {"fuchsia.device.manager", {{"DeviceController", {"CompleteRemoval", "Unbind"}}}},
      {"fuchsia.hardware.power.statecontrol",
       {{"Admin", {"Poweroff", "Reboot", "RebootToBootloader", "RebootToRecovery", "SuspendToRam"}}}},
  });
  
  bool MethodAlwaysAllowed(const flat::Protocol::Method& method) {
    auto library_name = flat::LibraryName(method.owning_protocol->name.library(), ".");
    auto iter = allowed_methods.find(library_name);
    if (iter != allowed_methods.end()) {
      const auto& protocols = iter->second;
      auto protocol = protocols.find(std::string(method.owning_protocol->name.decl_name()));
      if (protocol != protocols.end()) {
        const auto& methods = protocol->second;
        if (methods.find(std::string(method.name.data())) != methods.end()) {
          return true;
        }
      }
    }
    return false;
  }
  
  bool TypeAllowed(const flat::Library* library, const flat::Type* type);
  
  bool DeclAllowed(const flat::Decl* decl) {
    if (HasSimpleLayout(decl) || DeclAlwaysAllowed(decl->name)) {
      return true;
    }
  
    switch (decl->kind) {
      case flat::Decl::Kind::kBits:
      case flat::Decl::Kind::kConst:
      case flat::Decl::Kind::kEnum:
        // bits, const, enum are always allowed.
        return true;
      default:
        return false;
    }
  }
  
  bool IdentifierAllowed(const flat::Library* library, const flat::Name& name) {
    if (DeclAlwaysAllowed(name)) {
      return true;
    }
    const flat::Decl* decl = library->LookupDeclByName(name);
    assert(decl != nullptr);
    return DeclAllowed(decl);
  }
  
  bool TypeAllowed(const flat::Library* library, const flat::Type* type) {
    assert(type != nullptr);
    if (type->kind == flat::Type::Kind::kIdentifier) {
      auto identifier_type = static_cast<const flat::IdentifierType*>(type);
      if (!IdentifierAllowed(library, identifier_type->name)) {
        return false;
      }
    }
    return true;
  }
  
  bool MessageStructAllowed(const flat::Library* library, const flat::Struct* args) {
    if (!args) {
      return true;
    }
    for (const auto& member : args->members) {
      if (!TypeAllowed(library, flat::GetType(member.type_ctor))) {
        return false;
      }
    }
    return true;
  }
  
  bool MethodAllowed(const flat::Library* library, const flat::Protocol::Method& method) {
    return MethodAlwaysAllowed(method) ||
           (MessageStructAllowed(library, method.maybe_request_payload) &&
            MessageStructAllowed(library, method.maybe_response_payload));
  }
  
  CGenerator::Member MessageHeader() {
    return {
        flat::Type::Kind::kIdentifier,
        flat::Decl::Kind::kStruct,
        "fidl_message_header_t",
        "hdr",
        {},
        {},
        types::Nullability::kNonnullable,
        {},
    };
  }
  
  CGenerator::Member EmptyStructMember() {
    return {
        .kind = flat::Type::Kind::kPrimitive,
        .type = NamePrimitiveCType(types::PrimitiveSubtype::kUint8),
  
        // Prepend the reserved uint8_t field with a single underscore, which is
        // for reserved identifiers (see ISO C standard, section 7.1.3
        // <http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf>).
        .name = "_reserved",
    };
  }
  
  // Can encode and decode functions be generated for these members?
  bool CanGenerateCodecFunctions(const std::vector<CGenerator::Member>& members) {
    for (const auto& m : members) {
      switch (m.decl_kind) {
        case flat::Decl::Kind::kUnion:
          return false;
        default:
          break;
      }
    }
    return true;
  }
  
  // Functions named "Emit..." are called to actually emit to an std::ostream
  // is here. No other functions should directly emit to the streams.
  
  void EmitFileComment(std::ostream* file) {
    *file << "// WARNING: This file is machine generated by fidlc.\n\n";
  }
  
  void EmitHeaderGuard(std::ostream* file) {
    // TODO(fxbug.dev/704) Generate an appropriate header guard name.
    *file << "#pragma once\n";
  }
  
  void EmitIncludeHeader(std::ostream* file, std::string_view header) {
    *file << "#include " << header << "\n";
  }
  
  void EmitBeginExternC(std::ostream* file) {
    *file << "#if defined(__cplusplus)\nextern \"C\" {\n#endif\n";
  }
  
  void EmitEndExternC(std::ostream* file) { *file << "#if defined(__cplusplus)\n}\n#endif\n"; }
  
  void EmitBlank(std::ostream* file) { *file << "\n"; }
  
  void EmitMemberDecl(std::ostream* file, const CGenerator::Member& member) {
    *file << member.type << " " << member.name;
    for (uint32_t array_count : member.array_counts) {
      *file << "[" << array_count << "]";
    }
  }
  
  void EmitMethodInParamDecl(std::ostream* file, const CGenerator::Member& member) {
    switch (member.kind) {
      case flat::Type::Kind::kBox:
        assert(false && "no box types should appear at this point");
        __builtin_unreachable();
      case flat::Type::Kind::kArray:
        *file << "const " << member.type << " " << member.name;
        for (uint32_t array_count : member.array_counts) {
          *file << "[" << array_count << "]";
        }
        break;
      case flat::Type::Kind::kVector:
        *file << "const " << member.element_type << "* " << member.name << "_data, "
              << "size_t " << member.name << "_count";
        break;
      case flat::Type::Kind::kString:
        *file << "const char* " << member.name << "_data, "
              << "size_t " << member.name << "_size";
        break;
      case flat::Type::Kind::kHandle:
      case flat::Type::Kind::kRequestHandle:
      case flat::Type::Kind::kTransportSide:
      case flat::Type::Kind::kPrimitive:
        *file << member.type << " " << member.name;
        break;
      case flat::Type::Kind::kIdentifier:
        switch (member.decl_kind) {
          case flat::Decl::Kind::kConst:
          case flat::Decl::Kind::kResource:
          case flat::Decl::Kind::kService:
          case flat::Decl::Kind::kTypeAlias:
            assert(false && "bad decl kind for member");
            break;
          case flat::Decl::Kind::kBits:
          case flat::Decl::Kind::kEnum:
          case flat::Decl::Kind::kProtocol:
            *file << member.type << " " << member.name;
            break;
          case flat::Decl::Kind::kStruct:
          case flat::Decl::Kind::kTable:
          case flat::Decl::Kind::kUnion:
            switch (member.nullability) {
              case types::Nullability::kNullable:
                *file << "const " << member.type << " " << member.name;
                break;
              case types::Nullability::kNonnullable:
                *file << "const " << member.type << "* " << member.name;
                break;
            }
            break;
        }
        break;
    }
  }
  
  void EmitMethodOutParamDecl(std::ostream* file, const CGenerator::Member& member) {
    switch (member.kind) {
      case flat::Type::Kind::kBox:
        assert(false && "no box types should appear at this point");
        __builtin_unreachable();
      case flat::Type::Kind::kArray:
        *file << member.type << " out_" << member.name;
        for (uint32_t array_count : member.array_counts) {
          *file << "[" << array_count << "]";
        }
        break;
      case flat::Type::Kind::kVector:
        *file << member.element_type << "* " << member.name << "_buffer, "
              << "size_t " << member.name << "_capacity, "
              << "size_t* out_" << member.name << "_count";
        break;
      case flat::Type::Kind::kString:
        *file << "char* " << member.name << "_buffer, "
              << "size_t " << member.name << "_capacity, "
              << "size_t* out_" << member.name << "_size";
        break;
      case flat::Type::Kind::kHandle:
      case flat::Type::Kind::kRequestHandle:
      case flat::Type::Kind::kTransportSide:
      case flat::Type::Kind::kPrimitive:
        *file << member.type << "* out_" << member.name;
        break;
      case flat::Type::Kind::kIdentifier:
        switch (member.decl_kind) {
          case flat::Decl::Kind::kConst:
          case flat::Decl::Kind::kResource:
          case flat::Decl::Kind::kService:
          case flat::Decl::Kind::kTypeAlias:
            assert(false && "bad decl kind for member");
            break;
          case flat::Decl::Kind::kBits:
          case flat::Decl::Kind::kEnum:
          case flat::Decl::Kind::kProtocol:
            *file << member.type << "* out_" << member.name;
            break;
          case flat::Decl::Kind::kStruct:
          case flat::Decl::Kind::kTable:
          case flat::Decl::Kind::kUnion:
            switch (member.nullability) {
              case types::Nullability::kNullable:
                *file << member.type << " out_" << member.name;
                break;
              case types::Nullability::kNonnullable:
                *file << member.type << "* out_" << member.name;
                break;
            }
            break;
        }
        break;
    }
  }
  
  void EmitClientMethodDecl(std::ostream* file, std::string_view method_name,
                            const std::vector<CGenerator::Member>& request,
                            const std::vector<CGenerator::Member>& response) {
    *file << "zx_status_t " << method_name << "(zx_handle_t _channel";
    for (const auto& member : request) {
      *file << ", ";
      EmitMethodInParamDecl(file, member);
    }
    for (auto member : response) {
      *file << ", ";
      EmitMethodOutParamDecl(file, member);
    }
    *file << ")";
  }
  
  void EmitServerMethodDecl(std::ostream* file, std::string_view method_name,
                            const std::vector<CGenerator::Member>& request, bool has_response) {
    *file << "zx_status_t "
          << " (*" << method_name << ")(void* ctx";
  
    for (const auto& member : request) {
      *file << ", ";
      EmitMethodInParamDecl(file, member);
    }
    if (has_response) {
      *file << ", fidl_txn_t* txn";
    }
    *file << ")";
  }
  
  void EmitServerDispatchDecl(std::ostream* file, std::string_view protocol_name) {
    *file << "zx_status_t " << protocol_name
          << "_dispatch(void* ctx, fidl_txn_t* txn, fidl_incoming_msg_t* msg, const " << protocol_name
          << "_ops_t* ops)";
  }
  
  void EmitServerTryDispatchDecl(std::ostream* file, std::string_view protocol_name) {
    *file << "zx_status_t " << protocol_name
          << "_try_dispatch(void* ctx, fidl_txn_t* txn, fidl_incoming_msg_t* msg, const "
          << protocol_name << "_ops_t* ops)";
  }
  
  void EmitServerReplyDecl(std::ostream* file, std::string_view method_name,
                           const std::vector<CGenerator::Member>& response) {
    *file << "zx_status_t " << method_name << "_reply(fidl_txn_t* _txn";
    for (const auto& member : response) {
      *file << ", ";
      EmitMethodInParamDecl(file, member);
    }
    *file << ")";
  }
  
  bool IsStoredOutOfLine(const CGenerator::Member& member) {
    if (member.kind == flat::Type::Kind::kVector || member.kind == flat::Type::Kind::kString)
      return true;
    if (member.kind == flat::Type::Kind::kIdentifier) {
      if (member.decl_kind == flat::Decl::Kind::kTable)
        return true;
      if (member.nullability == types::Nullability::kNullable)
        return member.decl_kind == flat::Decl::Kind::kStruct ||
               member.decl_kind == flat::Decl::Kind::kUnion;
    }
    return false;
  }
  
  void EmitMeasureInParams(std::ostream* file, const std::vector<CGenerator::Member>& params) {
    for (const auto& member : params) {
      if (member.kind == flat::Type::Kind::kVector)
        *file << " + FIDL_ALIGN(sizeof(*" << member.name << "_data) * " << member.name << "_count)";
      else if (member.kind == flat::Type::Kind::kString)
        *file << " + FIDL_ALIGN(" << member.name << "_size)";
      else if (IsStoredOutOfLine(member))
        *file << " + (" << member.name << " ? FIDL_ALIGN(sizeof(*" << member.name << ")) : 0u)";
    }
  }
  
  void EmitParameterSizeValidation(std::ostream* file,
                                   const std::vector<CGenerator::Member>& params) {
    for (const auto& member : params) {
      if (member.max_num_elements == std::numeric_limits<uint32_t>::max())
        continue;
      std::string param_name;
      if (member.kind == flat::Type::Kind::kVector) {
        param_name = member.name + "_count";
      } else if (member.kind == flat::Type::Kind::kString) {
        param_name = member.name + "_size";
      } else {
        assert(false && "only vector/string has size limit");
      }
      *file << kIndent << "if (" << param_name << " > " << member.max_num_elements << ") {\n";
      *file << kIndent << kIndent << "return ZX_ERR_INVALID_ARGS;\n";
      *file << kIndent << "}\n";
    }
  }
  
  void EmitMeasureOutParams(std::ostream* file, const std::vector<CGenerator::Member>& params) {
    for (const auto& member : params) {
      if (member.kind == flat::Type::Kind::kVector)
        *file << " + FIDL_ALIGN(sizeof(*" << member.name << "_buffer) * " << member.name
              << "_capacity)";
      else if (member.kind == flat::Type::Kind::kString)
        *file << " + FIDL_ALIGN(" << member.name << "_capacity)";
      else if (IsStoredOutOfLine(member))
        *file << " + (out_" << member.name << " ? FIDL_ALIGN(sizeof(*out_" << member.name
              << ")) : 0u)";
    }
  }
  
  void EmitArraySizeOf(std::ostream* file, const CGenerator::Member& member) {
    for (const auto c : member.array_counts) {
      *file << c;
      *file << " * ";
    }
    *file << "sizeof(" << member.element_type << ")";
  }
  
  void EmitMagicNumberCheck(std::ostream* file) {
    *file << kIndent << "status = fidl_validate_txn_header(hdr);\n";
    *file << kIndent << "if (status != ZX_OK) {\n";
    *file << kIndent << kIndent << "FidlHandleInfoCloseMany(msg->handles, msg->num_handles);\n";
    *file << kIndent << kIndent << "ZX_DEBUG_ASSERT(status == ZX_ERR_PROTOCOL_NOT_SUPPORTED);";
    *file << kIndent << kIndent << "return status;\n";
    *file << kIndent << "}\n";
  }
  
  // This function assumes the |params| are part of a [ForDeprecatedCBindings] protocol.
  // In particular, simple protocols don't have nullable structs or nested
  // vectors. The only secondary objects they contain are top-level vectors and
  // strings.
  size_t CountSecondaryObjects(const std::vector<CGenerator::Member>& params) {
    size_t count = 0u;
    for (const auto& member : params) {
      if (IsStoredOutOfLine(member))
        ++count;
    }
    return count;
  }
  
  void EmitTxnHeader(std::ostream* file, const std::string& msg_name,
                     const std::string& ordinal_name) {
    *file << kIndent << "fidl_init_txn_header(&" << msg_name << "->hdr, 0, " << ordinal_name
          << ");\n";
  }
  
  void EmitLinearizeMessage(std::ostream* file, std::string_view receiver, std::string_view bytes,
                            const std::vector<CGenerator::Member>& request) {
    if (CountSecondaryObjects(request) > 0)
      *file << kIndent << "uint32_t _next = sizeof(*" << receiver << ");\n";
    for (const auto& member : request) {
      const auto& name = member.name;
      switch (member.kind) {
        case flat::Type::Kind::kBox:
          assert(false && "no box types should appear at this point");
          __builtin_unreachable();
        case flat::Type::Kind::kArray:
          *file << kIndent << "memcpy(" << receiver << "->" << name << ", " << name << ", ";
          EmitArraySizeOf(file, member);
          *file << ");\n";
          break;
        case flat::Type::Kind::kVector:
          *file << kIndent << receiver << "->" << name << ".data = &" << bytes << "[_next];\n";
          *file << kIndent << receiver << "->" << name << ".count = " << name << "_count;\n";
          *file << kIndent << "memcpy(" << receiver << "->" << name << ".data, " << name
                << "_data, sizeof(*" << name << "_data) * " << name << "_count);\n";
          *file << kIndent << "_next += FIDL_ALIGN(sizeof(*" << name << "_data) * " << name
                << "_count);\n";
          break;
        case flat::Type::Kind::kString:
          *file << kIndent << receiver << "->" << name << ".data = &" << bytes << "[_next];\n";
          *file << kIndent << receiver << "->" << name << ".size = " << name << "_size;\n";
          *file << kIndent << "_next += FIDL_ALIGN(" << name << "_size);\n";
          *file << kIndent << "if (" << name << "_data) {\n";
          *file << kIndent << kIndent << "memcpy(" << receiver << "->" << name << ".data, " << name
                << "_data, " << name << "_size);\n";
          *file << kIndent << "} else {\n";
          *file << kIndent << kIndent << "if (" << name << "_size != 0) {\n";
          *file << kIndent << kIndent << kIndent << "return ZX_ERR_INVALID_ARGS;\n";
          *file << kIndent << kIndent << "}\n";
          if (member.nullability == types::Nullability::kNullable) {
            *file << kIndent << kIndent << receiver << "->" << name << ".data = NULL;\n";
          }
          *file << kIndent << "}\n";
          break;
        case flat::Type::Kind::kHandle:
        case flat::Type::Kind::kRequestHandle:
        case flat::Type::Kind::kTransportSide:
        case flat::Type::Kind::kPrimitive:
          *file << kIndent << receiver << "->" << name << " = " << name << ";\n";
          break;
        case flat::Type::Kind::kIdentifier:
          switch (member.decl_kind) {
            case flat::Decl::Kind::kConst:
            case flat::Decl::Kind::kResource:
            case flat::Decl::Kind::kService:
            case flat::Decl::Kind::kTypeAlias:
              assert(false && "bad decl kind for member");
              break;
            case flat::Decl::Kind::kBits:
            case flat::Decl::Kind::kEnum:
            case flat::Decl::Kind::kProtocol:
              *file << kIndent << receiver << "->" << name << " = " << name << ";\n";
              break;
            case flat::Decl::Kind::kTable:
              assert(false && "c-codegen for tables not implemented");
              break;
            case flat::Decl::Kind::kUnion:
              assert(false && "c-codegen for unions not implemented");
              break;
            case flat::Decl::Kind::kStruct:
              switch (member.nullability) {
                case types::Nullability::kNullable:
                  *file << kIndent << "if (" << name << ") {\n";
                  *file << kIndent << kIndent << receiver << "->" << name << " = (void*)&" << bytes
                        << "[_next];\n";
                  *file << kIndent << kIndent << "memcpy(" << receiver << "->" << name << ", " << name
                        << ", sizeof(*" << name << "));\n";
                  *file << kIndent << kIndent << "_next += sizeof(*" << name << ");\n";
                  *file << kIndent << "} else {\n";
                  *file << kIndent << kIndent << receiver << "->" << name << " = NULL;\n";
                  *file << kIndent << "}\n";
                  break;
                case types::Nullability::kNonnullable:
                  *file << kIndent << receiver << "->" << name << " = *" << name << ";\n";
                  break;
              }
              break;
          }
      }
    }
  }
  
  // Various computational helper routines.
  
  void BitsValue(const flat::Constant* constant, std::string* out_value) {
    std::ostringstream member_value;
  
    const flat::ConstantValue& const_val = constant->Value();
    switch (const_val.kind) {
      case flat::ConstantValue::Kind::kUint8: {
        auto value = static_cast<const flat::NumericConstantValue<uint8_t>&>(const_val);
        member_value << value;
        break;
      }
      case flat::ConstantValue::Kind::kUint16: {
        auto value = static_cast<const flat::NumericConstantValue<uint16_t>&>(const_val);
        member_value << value;
        break;
      }
      case flat::ConstantValue::Kind::kUint32: {
        auto value = static_cast<const flat::NumericConstantValue<uint32_t>&>(const_val);
        member_value << value;
        break;
      }
      case flat::ConstantValue::Kind::kUint64: {
        auto value = static_cast<const flat::NumericConstantValue<uint64_t>&>(const_val);
        member_value << value;
        break;
      }
      case flat::ConstantValue::Kind::kInt8:
      case flat::ConstantValue::Kind::kInt16:
      case flat::ConstantValue::Kind::kInt32:
      case flat::ConstantValue::Kind::kInt64:
      case flat::ConstantValue::Kind::kBool:
      case flat::ConstantValue::Kind::kFloat32:
      case flat::ConstantValue::Kind::kFloat64:
      case flat::ConstantValue::Kind::kDocComment:
      case flat::ConstantValue::Kind::kString:
        assert(false && "bad primitive type for a bits declaration");
        break;
    }
  
    *out_value = member_value.str();
  }
  
  void EnumValue(const flat::Constant* constant, std::string* out_value) {
    std::ostringstream member_value;
  
    const flat::ConstantValue& const_val = constant->Value();
    switch (const_val.kind) {
      case flat::ConstantValue::Kind::kInt8: {
        auto value = static_cast<const flat::NumericConstantValue<int8_t>&>(const_val);
        member_value << value;
        break;
      }
      case flat::ConstantValue::Kind::kInt16: {
        auto value = static_cast<const flat::NumericConstantValue<int16_t>&>(const_val);
        member_value << value;
        break;
      }
      case flat::ConstantValue::Kind::kInt32: {
        auto value = static_cast<const flat::NumericConstantValue<int32_t>&>(const_val);
        member_value << value;
        break;
      }
      case flat::ConstantValue::Kind::kInt64: {
        auto value = static_cast<const flat::NumericConstantValue<int64_t>&>(const_val);
        member_value << value;
        break;
      }
      case flat::ConstantValue::Kind::kUint8: {
        auto value = static_cast<const flat::NumericConstantValue<uint8_t>&>(const_val);
        member_value << value;
        break;
      }
      case flat::ConstantValue::Kind::kUint16: {
        auto value = static_cast<const flat::NumericConstantValue<uint16_t>&>(const_val);
        member_value << value;
        break;
      }
      case flat::ConstantValue::Kind::kUint32: {
        auto value = static_cast<const flat::NumericConstantValue<uint32_t>&>(const_val);
        member_value << value;
        break;
      }
      case flat::ConstantValue::Kind::kUint64: {
        auto value = static_cast<const flat::NumericConstantValue<uint64_t>&>(const_val);
        member_value << value;
        break;
      }
      case flat::ConstantValue::Kind::kBool:
      case flat::ConstantValue::Kind::kFloat32:
      case flat::ConstantValue::Kind::kFloat64:
      case flat::ConstantValue::Kind::kDocComment:
      case flat::ConstantValue::Kind::kString:
        assert(false && "bad primitive type for an enum");
        break;
    }
  
    *out_value = member_value.str();
  }
  
  flat::Decl::Kind GetDeclKind(const flat::Library* library, const flat::Type* type) {
    if (type->kind != flat::Type::Kind::kIdentifier)
      return flat::Decl::Kind::kConst;
    auto identifier_type = static_cast<const flat::IdentifierType*>(type);
    auto named_decl = library->LookupDeclByName(identifier_type->name);
    assert(named_decl && "library must contain declaration");
    return named_decl->kind;
  }
  
  void ArrayCountsAndElementTypeName(const flat::Library* library, const flat::Type* type,
                                     std::vector<uint32_t>* out_array_counts,
                                     std::string* out_element_type_name) {
    std::vector<uint32_t> array_counts;
    for (;;) {
      switch (type->kind) {
        default: {
          *out_element_type_name = NameFlatCType(type, GetDeclKind(library, type));
          *out_array_counts = array_counts;
          return;
        }
        case flat::Type::Kind::kArray: {
          auto array_type = static_cast<const flat::ArrayType*>(type);
          array_counts.push_back(array_type->element_count->value);
          type = array_type->element_type;
          continue;
        }
      }
    }
  }
  
  template <typename T>
  CGenerator::Member CreateMember(const flat::Library* library, const T& decl,
                                  bool* out_allowed = nullptr) {
    std::string name = NameIdentifier(decl.name);
    const flat::Type* type = flat::GetType(decl.type_ctor);
    // treat box types like we do nullable structs
    if (type->kind == flat::Type::Kind::kBox)
      type = static_cast<const flat::BoxType*>(type)->boxed_type;
    auto decl_kind = GetDeclKind(library, type);
    auto type_name = NameFlatCType(type, decl_kind);
    std::string element_type_name;
    std::vector<uint32_t> array_counts;
    types::Nullability nullability = types::Nullability::kNonnullable;
    uint32_t max_num_elements = std::numeric_limits<uint32_t>::max();
    if (out_allowed) {
      *out_allowed = true;
    }
    switch (type->kind) {
      case flat::Type::Kind::kBox:
        assert(false && "no box types should appear at this point");
        __builtin_unreachable();
      case flat::Type::Kind::kArray: {
        ArrayCountsAndElementTypeName(library, type, &array_counts, &element_type_name);
        break;
      }
      case flat::Type::Kind::kVector: {
        auto vector_type = static_cast<const flat::VectorType*>(type);
        const auto element_type = vector_type->element_type;
        element_type_name = NameFlatCType(element_type, GetDeclKind(library, element_type));
        max_num_elements = vector_type->element_count->value;
        break;
      }
      case flat::Type::Kind::kIdentifier: {
        auto identifier_type = static_cast<const flat::IdentifierType*>(type);
        nullability = identifier_type->nullability;
        if (out_allowed) {
          *out_allowed = IdentifierAllowed(library, identifier_type->name);
        }
        break;
      }
      case flat::Type::Kind::kString: {
        auto string_type = static_cast<const flat::StringType*>(type);
        nullability = string_type->nullability;
        max_num_elements = string_type->max_size->value;
        break;
      }
      case flat::Type::Kind::kHandle:
        break;
      case flat::Type::Kind::kRequestHandle:
      case flat::Type::Kind::kTransportSide:
        break;
      case flat::Type::Kind::kPrimitive:
        break;
    }
    return CGenerator::Member{
        type->kind,
        decl_kind,
        std::move(type_name),
        std::move(name),
        std::move(element_type_name),
        std::move(array_counts),
        nullability,
        max_num_elements,
    };
  }
  
  bool GetMethodParameters(const flat::Library* library, const CGenerator::NamedMethod& method_info,
                           std::vector<CGenerator::Member>* request,
                           std::vector<CGenerator::Member>* response) {
    if (request && method_info.request->parameters) {
      request->reserve(method_info.request->parameters->size());
      for (const auto& parameter : *method_info.request->parameters) {
        bool allowed = true;
        request->push_back(CreateMember(library, parameter, &allowed));
        if (!allowed) {
          request->clear();
          if (response) {
            response->clear();
          }
          return false;
        }
      }
    }
  
    if (response && method_info.response && method_info.response->parameters) {
      response->reserve(method_info.response->parameters->size());
      for (const auto& parameter : *method_info.response->parameters) {
        bool allowed = true;
        response->push_back(CreateMember(library, parameter, &allowed));
        if (!allowed) {
          if (request) {
            request->clear();
          }
          response->clear();
          return false;
        }
      }
    }
    return true;
  }
  
  }  // namespace
  
  uint32_t CGenerator::GetMaxHandlesFor(Transport transport, const TypeShape& typeshape) {
    switch (transport) {
      case Transport::Channel:
        return std::min(kChannelMaxMessageHandles, typeshape.MaxHandles());
    }
    assert(false && "what transport?");
    return 0u;
  }
  
  void CGenerator::GeneratePrologues() {
    EmitFileComment(&file_);
    EmitHeaderGuard(&file_);
    EmitBlank(&file_);
    EmitIncludeHeader(&file_, "<stdalign.h>");
    EmitIncludeHeader(&file_, "<stdbool.h>");
    EmitIncludeHeader(&file_, "<stdint.h>");
    EmitIncludeHeader(&file_, "<zircon/fidl.h>");
    EmitIncludeHeader(&file_, "<zircon/syscalls/object.h>");
    EmitIncludeHeader(&file_, "<zircon/types.h>");
    // Dependencies are in pointer order... change to a deterministic
    // ordering prior to output.
    std::set<std::string> add_includes;
    for (const auto& dep_library : library_->dependencies()) {
      if (dep_library == library_)
        continue;
      if (dep_library->HasAttribute("Internal"))
        continue;
      add_includes.insert(NameLibraryCHeader(dep_library->name()));
    }
    for (const auto& include : add_includes) {
      EmitIncludeHeader(&file_, "<" + include + ">");
    }
    EmitBlank(&file_);
    EmitBeginExternC(&file_);
    EmitBlank(&file_);
  }
  
  void CGenerator::GenerateEpilogues() { EmitEndExternC(&file_); }
  
  void CGenerator::GenerateIntegerDefine(std::string_view name, types::PrimitiveSubtype subtype,
                                         std::string_view value) {
    std::string literal_macro = NamePrimitiveIntegerCConstantMacro(subtype);
    file_ << "#define " << name << " " << literal_macro << "(" << value << ")\n";
  }
  
  void CGenerator::GeneratePrimitiveDefine(std::string_view name, types::PrimitiveSubtype subtype,
                                           std::string_view value) {
    switch (subtype) {
      case types::PrimitiveSubtype::kInt8:
      case types::PrimitiveSubtype::kInt16:
      case types::PrimitiveSubtype::kInt32:
      case types::PrimitiveSubtype::kInt64:
      case types::PrimitiveSubtype::kUint8:
      case types::PrimitiveSubtype::kUint16:
      case types::PrimitiveSubtype::kUint32:
      case types::PrimitiveSubtype::kUint64: {
        std::string literal_macro = NamePrimitiveIntegerCConstantMacro(subtype);
        file_ << "#define " << name << " " << literal_macro << "(" << value << ")\n";
        break;
      }
      case types::PrimitiveSubtype::kBool:
      case types::PrimitiveSubtype::kFloat32:
      case types::PrimitiveSubtype::kFloat64: {
        file_ << "#define " << name << " "
              << "(" << value << ")\n";
        break;
      }
    }  // switch
  }
  
  void CGenerator::GenerateStringDefine(std::string_view name, std::string_view value) {
    file_ << "#define " << name << " " << value << "\n";
  }
  
  void CGenerator::GenerateIntegerTypedef(types::PrimitiveSubtype subtype, std::string_view name) {
    std::string underlying_type = NamePrimitiveCType(subtype);
    file_ << "typedef " << underlying_type << " " << name << ";\n";
  }
  
  void CGenerator::GenerateStructTypedef(std::string_view name) {
    file_ << "typedef struct " << name << " " << name << ";\n";
  }
  
  void CGenerator::GenerateStructDeclaration(std::string_view name,
                                             const std::vector<Member>& members, StructKind kind) {
    file_ << "struct " << name << " {\n";
  
    if (kind == StructKind::kMessage) {
      file_ << kIndent << "FIDL_ALIGNDECL\n";
    }
  
    auto emit_member = [this](const Member& member) {
      file_ << kIndent;
      EmitMemberDecl(&file_, member);
      file_ << ";\n";
    };
  
    for (const auto& member : members) {
      emit_member(member);
    }
  
    if (members.empty()) {
      emit_member(EmptyStructMember());
    }
  
    file_ << "};\n";
  }
  
  void CGenerator::GenerateTableDeclaration(std::string_view name) {
    file_ << "struct " << name << " {\n";
    file_ << kIndent << "fidl_table_t table_header;\n";
    file_ << "};\n";
  }
  
  void CGenerator::GenerateTaggedUnionDeclaration(std::string_view name,
                                                  const std::vector<Member>& members) {
  #ifdef FIDLC_DEPRECATE_C_UNIONS
    file_ << "struct __attribute__ ((deprecated)) " << name << " {\n";
  #else
    file_ << "struct " << name << " {\n";
  #endif
    file_ << kIndent << "fidl_union_tag_t tag;\n";
    file_ << kIndent << "union {\n";
    for (const auto& member : members) {
      file_ << kIndent << kIndent;
      EmitMemberDecl(&file_, member);
      file_ << ";\n";
    }
    file_ << kIndent << "};\n";
    file_ << "};\n";
  }
  
  std::map<const flat::Decl*, CGenerator::NamedBits> CGenerator::NameBits(
      const std::vector<std::unique_ptr<flat::Bits>>& bits_infos) {
    std::map<const flat::Decl*, NamedBits> named_bits;
    for (const auto& bits_info : bits_infos) {
      std::string bits_name = NameCodedName(bits_info->name);
      named_bits.emplace(bits_info.get(), NamedBits{std::move(bits_name), *bits_info});
    }
    return named_bits;
  }
  
  // TODO(fxbug.dev/27764) These should maybe check for global name
  // collisions? Otherwise, is there some other way they should fail?
  std::map<const flat::Decl*, CGenerator::NamedConst> CGenerator::NameConsts(
      const std::vector<std::unique_ptr<flat::Const>>& const_infos) {
    std::map<const flat::Decl*, NamedConst> named_consts;
    for (const auto& const_info : const_infos) {
      named_consts.emplace(const_info.get(),
                           NamedConst{NameCodedName(const_info->name), *const_info});
    }
    return named_consts;
  }
  
  std::map<const flat::Decl*, CGenerator::NamedEnum> CGenerator::NameEnums(
      const std::vector<std::unique_ptr<flat::Enum>>& enum_infos) {
    std::map<const flat::Decl*, NamedEnum> named_enums;
    for (const auto& enum_info : enum_infos) {
      std::string enum_name = NameCodedName(enum_info->name);
      named_enums.emplace(enum_info.get(), NamedEnum{std::move(enum_name), *enum_info});
    }
    return named_enums;
  }
  
  std::map<const flat::Decl*, CGenerator::NamedProtocol> CGenerator::NameProtocols(
      const std::vector<std::unique_ptr<flat::Protocol>>& protocol_infos) {
    std::map<const flat::Decl*, NamedProtocol> named_protocols;
    for (const auto& protocol_info : protocol_infos) {
      NamedProtocol named_protocol;
      named_protocol.c_name = NameCodedName(protocol_info->name);
      if (protocol_info->HasAttribute("discoverable")) {
        named_protocol.discoverable_name = NameDiscoverable(*protocol_info);
      }
      named_protocol.transport = CGenerator::Transport::Channel;
      for (const auto& method_with_info : protocol_info->all_methods) {
        assert(method_with_info.method != nullptr);
        const auto& method = *method_with_info.method;
        if (!MethodAllowed(library_, method)) {
          continue;
        }
        NamedMethod named_method;
        std::string method_name = NameMethod(named_protocol.c_name, method);
        named_method.ordinal = static_cast<uint64_t>(method.generated_ordinal64->value);
        named_method.ordinal_name = NameOrdinal(method_name);
        named_method.identifier = NameIdentifier(method.name);
        named_method.c_name = method_name;
        if (method.has_request) {
          std::string c_name = NameMessage(method_name, types::MessageKind::kRequest);
          std::string coded_name = NameTable(c_name);
          const auto typeshape = method.maybe_request_payload
                                     ? method.maybe_request_payload->typeshape(WireFormat::kV1NoEe)
                                     : TypeShape::ForEmptyPayload();
          const auto* members =
              method.maybe_request_payload ? &method.maybe_request_payload->members : nullptr;
          // TODO(fxbug.dev/76316): remove this assert once this generator is able
          //  to properly handle empty structs as payloads.  Since no new uses of
          //  the C bindings are allowed, it may be prudent to leave this assert
          //  in.
          assert(!members || (members && !members->empty()));
          named_method.request = std::make_unique<NamedMessage>(
              NamedMessage{std::move(c_name), std::move(coded_name), members, typeshape});
        }
        if (method.has_response) {
          auto message_kind =
              method.has_request ? types::MessageKind::kResponse : types::MessageKind::kEvent;
          std::string c_name = NameMessage(method_name, message_kind);
          std::string coded_name = NameTable(c_name);
          const auto typeshape = method.maybe_response_payload != nullptr
                                     ? method.maybe_response_payload->typeshape(WireFormat::kV1NoEe)
                                     : TypeShape::ForEmptyPayload();
          const auto* members =
              method.maybe_response_payload ? &method.maybe_response_payload->members : nullptr;
          // TODO(fxbug.dev/76316): remove this assert once this generator is able
          //  to properly handle empty structs as payloads.  Since no new uses of
          //  the C bindings are allowed, it may be prudent to leave this assert
          //  in.
          assert(!members || (members && !members->empty()));
          named_method.response = std::make_unique<NamedMessage>(
              NamedMessage{std::move(c_name), std::move(coded_name), members, typeshape});
        }
        named_protocol.methods.push_back(std::move(named_method));
      }
      if (named_protocol.methods.size() > 0) {
        named_protocols.emplace(protocol_info.get(), std::move(named_protocol));
      }
    }
    return named_protocols;
  }
  
  std::map<const flat::Decl*, CGenerator::NamedStruct> CGenerator::NameStructs(
      const std::vector<std::unique_ptr<flat::Struct>>& struct_infos) {
    std::map<const flat::Decl*, NamedStruct> named_structs;
    for (const auto& struct_info : struct_infos) {
      if (struct_info->is_request_or_response)
        continue;
      std::string c_name = NameCodedName(struct_info->name);
      std::string coded_name = c_name + "Coded";
      named_structs.emplace(struct_info.get(),
                            NamedStruct{std::move(c_name), std::move(coded_name), *struct_info});
    }
    return named_structs;
  }
  
  void CGenerator::ProduceBitsForwardDeclaration(const NamedBits& named_bits) {
    auto subtype =
        static_cast<const flat::PrimitiveType*>(flat::GetType(named_bits.bits_info.subtype_ctor))
            ->subtype;
    GenerateIntegerTypedef(subtype, named_bits.name);
    for (const auto& member : named_bits.bits_info.members) {
      std::string member_name = named_bits.name + "_" + NameIdentifier(member.name);
      std::string member_value;
      BitsValue(member.value.get(), &member_value);
      GenerateIntegerDefine(member_name, subtype, std::move(member_value));
    }
  
    EmitBlank(&file_);
  }
  
  void CGenerator::ProduceConstForwardDeclaration(const NamedConst& named_const) {
    // TODO(fxbug.dev/27764)
  }
  
  void CGenerator::ProduceEnumForwardDeclaration(const NamedEnum& named_enum) {
    types::PrimitiveSubtype subtype = named_enum.enum_info.type->subtype;
    GenerateIntegerTypedef(subtype, named_enum.name);
    for (const auto& member : named_enum.enum_info.members) {
      std::string member_name = named_enum.name + "_" + NameIdentifier(member.name);
      std::string member_value;
      EnumValue(member.value.get(), &member_value);
      GenerateIntegerDefine(member_name, subtype, std::move(member_value));
    }
  
    EmitBlank(&file_);
  }
  
  void CGenerator::ProduceProtocolForwardDeclaration(const NamedProtocol& named_protocol) {
    if (!named_protocol.discoverable_name.empty()) {
      file_ << "#define " << named_protocol.c_name << "_Name \"" << named_protocol.discoverable_name
            << "\"\n";
    }
    for (const auto& method_info : named_protocol.methods) {
      {
        IOFlagsGuard reset_flags(&file_);
        file_ << "#define " << method_info.ordinal_name << " ((uint64_t)0x" << std::uppercase
              << std::hex << method_info.ordinal << std::dec << ")\n";
      }
      if (method_info.request)
        GenerateStructTypedef(method_info.request->c_name);
      if (method_info.response)
        GenerateStructTypedef(method_info.response->c_name);
    }
  }
  
  void CGenerator::ProduceStructForwardDeclaration(const NamedStruct& named_struct) {
    GenerateStructTypedef(named_struct.c_name);
  }
  
  void CGenerator::ProduceProtocolExternDeclaration(const NamedProtocol& named_protocol) {
    for (const auto& method_info : named_protocol.methods) {
      if (method_info.request) {
        file_ << "__LOCAL extern const fidl_type_t " << method_info.request->coded_name << ";\n";
      }
      if (method_info.response) {
        file_ << "__LOCAL extern const fidl_type_t " << method_info.response->coded_name << ";\n";
      }
    }
  }
  
  void CGenerator::ProduceConstDeclaration(const NamedConst& named_const) {
    const flat::Const& ci = named_const.const_info;
  
    // Some constants are not literals.  Odd.
    if (ci.value->kind != flat::Constant::Kind::kLiteral) {
      return;
    }
  
    switch (flat::GetType(ci.type_ctor)->kind) {
      case flat::Type::Kind::kPrimitive:
        GeneratePrimitiveDefine(
            named_const.name,
            static_cast<const flat::PrimitiveType*>(flat::GetType(ci.type_ctor))->subtype,
            static_cast<flat::LiteralConstant*>(ci.value.get())->literal->span().data());
        break;
      case flat::Type::Kind::kString:
        GenerateStringDefine(
            named_const.name,
            static_cast<flat::LiteralConstant*>(ci.value.get())->literal->span().data());
        break;
      default:
        abort();
    }
  
    EmitBlank(&file_);
  }
  
  void CGenerator::ProduceMessageDeclaration(const NamedMessage& named_message) {
    // When we generate a request or response struct (i.e. messages), we must
    // both include the message header, and ensure the message is FIDL aligned.
  
    std::vector<CGenerator::Member> members;
    if (named_message.parameters) {
      members.reserve(1 + named_message.parameters->size());
      members.push_back(MessageHeader());
      for (const auto& parameter : *named_message.parameters) {
        members.push_back(CreateMember(library_, parameter));
      }
    } else {
      members.reserve(1);
      members.push_back(MessageHeader());
    }
  
    GenerateStructDeclaration(named_message.c_name, members, StructKind::kMessage);
  
    EmitBlank(&file_);
  }
  
  void CGenerator::ProduceProtocolDeclaration(const NamedProtocol& named_protocol) {
    for (const auto& method_info : named_protocol.methods) {
      if (method_info.request)
        ProduceMessageDeclaration(*method_info.request);
      if (method_info.response)
        ProduceMessageDeclaration(*method_info.response);
    }
  }
  
  void CGenerator::ProduceStructDeclaration(const NamedStruct& named_struct) {
    std::vector<CGenerator::Member> members;
    members.reserve(named_struct.struct_info.members.size());
    for (const auto& struct_member : named_struct.struct_info.members) {
      members.push_back(CreateMember(library_, struct_member));
    }
  
    GenerateStructDeclaration(named_struct.c_name, members, StructKind::kNonmessage);
  
    EmitBlank(&file_);
  }
  
  void CGenerator::ProduceProtocolClientDeclaration(const NamedProtocol& named_protocol) {
    for (const auto& method_info : named_protocol.methods) {
      if (!method_info.request)
        continue;
      std::vector<Member> request;
      std::vector<Member> response;
      if (GetMethodParameters(library_, method_info, &request, &response)) {
        if (CanGenerateCodecFunctions(request) && CanGenerateCodecFunctions(response)) {
          EmitClientMethodDecl(&file_, method_info.c_name, request, response);
          file_ << ";\n";
        }
      }
    }
  
    EmitBlank(&file_);
  }
  
  void CGenerator::ProduceProtocolClientImplementation(const NamedProtocol& named_protocol) {
    for (const auto& method_info : named_protocol.methods) {
      if (!method_info.request)
        continue;
      std::vector<Member> request;
      std::vector<Member> response;
      if (!GetMethodParameters(library_, method_info, &request, &response) ||
          !CanGenerateCodecFunctions(request) || !CanGenerateCodecFunctions(response)) {
        continue;
      }
  
      size_t count = CountSecondaryObjects(request);
      size_t request_hcount =
          GetMaxHandlesFor(named_protocol.transport, method_info.request->typeshape);
      size_t response_hcount = 0;
      if (method_info.response) {
        response_hcount = GetMaxHandlesFor(named_protocol.transport, method_info.response->typeshape);
      }
  
      bool has_padding = method_info.request->typeshape.HasPadding();
      bool encode_request = (count > 0) || (request_hcount > 0) || has_padding;
  
      EmitClientMethodDecl(&file_, method_info.c_name, request, response);
      file_ << " {\n";
      EmitParameterSizeValidation(&file_, request);
      file_ << kIndent << "uint32_t _wr_num_bytes = sizeof(" << method_info.request->c_name << ")";
      EmitMeasureInParams(&file_, request);
      file_ << ";\n";
      file_ << kIndent << "FIDL_ALIGNDECL char _wr_bytes[_wr_num_bytes];\n";
      file_ << kIndent << method_info.request->c_name << "* _request = ("
            << method_info.request->c_name << "*)_wr_bytes;\n";
      file_ << kIndent << "memset(_wr_bytes, 0, sizeof(_wr_bytes));\n";
      EmitTxnHeader(&file_, "_request", method_info.ordinal_name);
      EmitLinearizeMessage(&file_, "_request", "_wr_bytes", request);
      const char* handle_infos_value = "NULL";
      const char* handle_dispositions_value = "NULL";
      if (request_hcount > 0) {
        file_ << kIndent << "zx_handle_disposition_t _handle_dispositions[" << request_hcount
              << "];\n";
        handle_dispositions_value = "_handle_dispositions";
      }
      if (response_hcount > 0) {
        file_ << kIndent << "zx_handle_info_t _handle_infos[" << response_hcount << "];\n";
        handle_infos_value = "_handle_infos";
      }
      if (encode_request) {
        file_ << kIndent << "uint32_t _wr_num_handles = 0u;\n";
        file_ << kIndent << "zx_status_t _status = fidl_encode_etc(&"
              << method_info.request->coded_name << ", _wr_bytes, _wr_num_bytes, "
              << handle_dispositions_value << ", " << request_hcount
              << ", &_wr_num_handles, NULL);\n";
        file_ << kIndent << "if (_status != ZX_OK)\n";
        file_ << kIndent << kIndent << "return _status;\n";
      } else {
        file_ << kIndent << "// OPTIMIZED AWAY fidl_encode() of POD-only request\n";
      }
      if (!method_info.response) {
        switch (named_protocol.transport) {
          case Transport::Channel:
            if (encode_request) {
              file_ << kIndent
                    << "return zx_channel_write_etc(_channel, 0u, _wr_bytes, _wr_num_bytes, "
                    << handle_dispositions_value << ", _wr_num_handles);\n";
            } else {
              file_ << kIndent
                    << "return zx_channel_write_etc(_channel, 0u, _wr_bytes, _wr_num_bytes, NULL, "
                       "0);\n";
            }
            break;
        }
      } else {
        file_ << kIndent << "uint32_t _rd_num_bytes = sizeof(" << method_info.response->c_name << ")";
        EmitMeasureOutParams(&file_, response);
        file_ << ";\n";
  
        file_ << kIndent << "uint32_t _rd_num_bytes_max = _rd_num_bytes;\n";
  
        file_ << kIndent << "FIDL_ALIGNDECL uint8_t _rd_bytes_storage[_rd_num_bytes_max];\n";
        file_ << kIndent << "uint8_t* _rd_bytes = _rd_bytes_storage;\n";
        if (!response.empty())
          file_ << kIndent << method_info.response->c_name << "* _response = ("
                << method_info.response->c_name << "*)_rd_bytes;\n";
        switch (named_protocol.transport) {
          case Transport::Channel:
            file_ << kIndent << "zx_channel_call_etc_args_t _args = {\n";
            file_ << kIndent << kIndent << ".wr_bytes = _wr_bytes,\n";
            file_ << kIndent << kIndent << ".wr_handles = " << handle_dispositions_value << ",\n";
            file_ << kIndent << kIndent << ".rd_bytes = _rd_bytes,\n";
            file_ << kIndent << kIndent << ".rd_handles = " << handle_infos_value << ",\n";
            file_ << kIndent << kIndent << ".wr_num_bytes = _wr_num_bytes,\n";
            if (encode_request) {
              file_ << kIndent << kIndent << ".wr_num_handles = _wr_num_handles,\n";
            } else {
              file_ << kIndent << kIndent << ".wr_num_handles = 0,\n";
            }
            file_ << kIndent << kIndent << ".rd_num_bytes = _rd_num_bytes_max,\n";
            file_ << kIndent << kIndent << ".rd_num_handles = " << response_hcount << ",\n";
            file_ << kIndent << "};\n";
  
            file_ << kIndent << "uint32_t _actual_num_bytes = 0u;\n";
            file_ << kIndent << "uint32_t _actual_num_handles = 0u;\n";
            if (encode_request) {
              file_ << kIndent;
            } else {
              file_ << kIndent << "zx_status_t ";
            }
            file_ << "_status = zx_channel_call_etc(_channel, 0u, ZX_TIME_INFINITE, &_args, "
                     "&_actual_num_bytes, &_actual_num_handles);\n";
            break;
        }
        file_ << kIndent << "if (_status != ZX_OK)\n";
        file_ << kIndent << kIndent << "return _status;\n";
  
        // We check that we have enough capacity to copy out the parameters
        // before decoding the message so that we can close the handles
        // using |_handles| rather than trying to find them in the decoded
        // message.
        count = CountSecondaryObjects(response);
        has_padding = method_info.response->typeshape.HasPadding();
        bool decode_response = (count > 0) || (response_hcount > 0) || has_padding;
        if (count > 0u) {
          file_ << kIndent << "if ";
          if (count > 1u)
            file_ << "(";
          size_t i = 0;
          for (const auto& member : response) {
            if (member.kind == flat::Type::Kind::kVector) {
              if (i++ > 0u)
                file_ << " || ";
              file_ << "(_response->" << member.name << ".count > " << member.name << "_capacity)";
            } else if (member.kind == flat::Type::Kind::kString) {
              if (i++ > 0u)
                file_ << " || ";
              file_ << "(_response->" << member.name << ".size > " << member.name << "_capacity)";
            } else if (IsStoredOutOfLine(member)) {
              if (i++ > 0u)
                file_ << " || ";
              file_ << "((uintptr_t)_response->" << member.name << " == FIDL_ALLOC_PRESENT && out_"
                    << member.name << " == NULL)";
            }
          }
          if (count > 1u)
            file_ << ")";
          file_ << " {\n";
          if (response_hcount > 0) {
            file_ << kIndent << kIndent
                  << "FidlHandleInfoCloseMany(_handle_infos, _actual_num_handles);\n";
          }
          file_ << kIndent << kIndent << "return ZX_ERR_BUFFER_TOO_SMALL;\n";
          file_ << kIndent << "}\n";
        }
  
        if (decode_response) {
          // TODO(fxbug.dev/7499): Validate the response ordinal. C++ bindings also need to do that.
          switch (named_protocol.transport) {
            case Transport::Channel:
              file_ << kIndent << "_status = fidl_decode_etc(&" << method_info.response->coded_name
                    << ", _rd_bytes, _actual_num_bytes, " << handle_infos_value
                    << ", _actual_num_handles, NULL);\n";
              break;
          }
          file_ << kIndent << "if (_status != ZX_OK)\n";
          file_ << kIndent << kIndent << "return _status;\n";
        } else {
          file_ << kIndent << "// OPTIMIZED AWAY fidl_decode() of POD-only response\n";
        }
  
        for (const auto& member : response) {
          const auto& name = member.name;
          switch (member.kind) {
            case flat::Type::Kind::kBox:
              assert(false && "no box types should appear at this point");
              __builtin_unreachable();
            case flat::Type::Kind::kArray:
              file_ << kIndent << "memcpy(out_" << name << ", _response->" << name << ", ";
              EmitArraySizeOf(&file_, member);
              file_ << ");\n";
              break;
            case flat::Type::Kind::kVector:
              file_ << kIndent << "memcpy(" << name << "_buffer, _response->" << name
                    << ".data, sizeof(*" << name << "_buffer) * _response->" << name << ".count);\n";
              file_ << kIndent << "*out_" << name << "_count = _response->" << name << ".count;\n";
              break;
            case flat::Type::Kind::kString:
              file_ << kIndent << "memcpy(" << name << "_buffer, _response->" << name
                    << ".data, _response->" << name << ".size);\n";
              file_ << kIndent << "*out_" << name << "_size = _response->" << name << ".size;\n";
              break;
            case flat::Type::Kind::kHandle:
            case flat::Type::Kind::kRequestHandle:
            case flat::Type::Kind::kTransportSide:
            case flat::Type::Kind::kPrimitive:
              file_ << kIndent << "*out_" << name << " = _response->" << name << ";\n";
              break;
            case flat::Type::Kind::kIdentifier:
              switch (member.decl_kind) {
                case flat::Decl::Kind::kConst:
                case flat::Decl::Kind::kResource:
                case flat::Decl::Kind::kService:
                case flat::Decl::Kind::kTypeAlias:
                  assert(false && "bad decl kind for member");
                  break;
                case flat::Decl::Kind::kBits:
                case flat::Decl::Kind::kEnum:
                case flat::Decl::Kind::kProtocol:
                  file_ << kIndent << "*out_" << name << " = _response->" << name << ";\n";
                  break;
                case flat::Decl::Kind::kTable:
                  assert(false && "c-codegen for tables not implemented");
                  break;
                case flat::Decl::Kind::kUnion:
                  assert(false && "c-codegen for unions not implemented");
                  break;
                case flat::Decl::Kind::kStruct:
                  switch (member.nullability) {
                    case types::Nullability::kNullable:
                      file_ << kIndent << "if (_response->" << name << ") {\n";
                      file_ << kIndent << kIndent << "*out_" << name << " = *(_response->" << name
                            << ");\n";
                      file_ << kIndent << "} else {\n";
                      // We don't have a great way of signaling that the optional response member
                      // was not in the message. That means these bindings aren't particularly
                      // useful when the client needs to extract that bit. The best we can do is
                      // zero out the value to make sure the client has defined behavior.
                      //
                      // In many cases, the response contains other information (e.g., a status code)
                      // that lets the client do something reasonable.
                      file_ << kIndent << kIndent << "memset(out_" << name << ", 0, sizeof(*out_"
                            << name << "));\n";
                      file_ << kIndent << "}\n";
                      break;
                    case types::Nullability::kNonnullable:
                      file_ << kIndent << "*out_" << name << " = _response->" << name << ";\n";
                      break;
                  }
                  break;
              }
              break;
          }
        }
  
        file_ << kIndent << "return ZX_OK;\n";
      }
      file_ << "}\n\n";
    }
  }
  
  void CGenerator::ProduceProtocolServerDeclaration(const NamedProtocol& named_protocol) {
    file_ << "typedef struct " << named_protocol.c_name << "_ops {\n";
    for (const auto& method_info : named_protocol.methods) {
      if (!method_info.request)
        continue;
      std::vector<Member> request;
      if (GetMethodParameters(library_, method_info, &request, nullptr) &&
          CanGenerateCodecFunctions(request)) {
        bool has_response = method_info.response != nullptr;
        file_ << kIndent;
        EmitServerMethodDecl(&file_, method_info.identifier, request, has_response);
        file_ << ";\n";
      }
    }
    file_ << "} " << named_protocol.c_name << "_ops_t;\n\n";
  
    EmitServerDispatchDecl(&file_, named_protocol.c_name);
    file_ << ";\n";
    EmitServerTryDispatchDecl(&file_, named_protocol.c_name);
    file_ << ";\n\n";
  
    for (const auto& method_info : named_protocol.methods) {
      if (!method_info.request || !method_info.response)
        continue;
      std::vector<Member> response;
      if (GetMethodParameters(library_, method_info, nullptr, &response) &&
          CanGenerateCodecFunctions(response)) {
        EmitServerReplyDecl(&file_, method_info.c_name, response);
        file_ << ";\n";
      }
    }
  
    EmitBlank(&file_);
  }
  
  void CGenerator::ProduceProtocolServerImplementation(const NamedProtocol& named_protocol) {
    EmitServerTryDispatchDecl(&file_, named_protocol.c_name);
    file_ << " {\n";
    file_ << kIndent << "if (msg->num_bytes < sizeof(fidl_message_header_t)) {\n";
    file_ << kIndent << kIndent << "FidlHandleInfoCloseMany(msg->handles, msg->num_handles);\n";
    file_ << kIndent << kIndent << "return ZX_ERR_INVALID_ARGS;\n";
    file_ << kIndent << "}\n";
    file_ << kIndent << "zx_status_t status = ZX_OK;\n";
    file_ << kIndent << "fidl_message_header_t* hdr = (fidl_message_header_t*)msg->bytes;\n";
    EmitMagicNumberCheck(&file_);
    file_ << kIndent << "switch (hdr->ordinal) {\n";
  
    for (const auto& method_info : named_protocol.methods) {
      if (!method_info.request)
        continue;
      std::vector<Member> request;
      if (!GetMethodParameters(library_, method_info, &request, nullptr)) {
        continue;
      }
      file_ << kIndent << "case " << method_info.ordinal_name << ": {\n";
      file_ << kIndent << kIndent << "status = fidl_decode_msg(&" << method_info.request->coded_name
            << ", msg, NULL);\n";
      file_ << kIndent << kIndent << "if (status != ZX_OK)\n";
      file_ << kIndent << kIndent << kIndent << "break;\n";
      if (!request.empty())
        file_ << kIndent << kIndent << method_info.request->c_name << "* request = ("
              << method_info.request->c_name << "*)msg->bytes;\n";
      file_ << kIndent << kIndent << "status = (*ops->" << method_info.identifier << ")(ctx";
      for (const auto& member : request) {
        switch (member.kind) {
          case flat::Type::Kind::kBox:
            assert(false && "no box types should appear at this point");
            __builtin_unreachable();
          case flat::Type::Kind::kArray:
          case flat::Type::Kind::kHandle:
          case flat::Type::Kind::kRequestHandle:
          case flat::Type::Kind::kTransportSide:
          case flat::Type::Kind::kPrimitive:
            file_ << ", request->" << member.name;
            break;
          case flat::Type::Kind::kVector:
            file_ << ", (" << member.element_type << "*)request->" << member.name << ".data"
                  << ", request->" << member.name << ".count";
            break;
          case flat::Type::Kind::kString:
            file_ << ", request->" << member.name << ".data"
                  << ", request->" << member.name << ".size";
            break;
          case flat::Type::Kind::kIdentifier:
            switch (member.decl_kind) {
              case flat::Decl::Kind::kConst:
              case flat::Decl::Kind::kResource:
              case flat::Decl::Kind::kService:
              case flat::Decl::Kind::kTypeAlias:
                assert(false && "bad decl kind for member");
                break;
              case flat::Decl::Kind::kBits:
              case flat::Decl::Kind::kEnum:
              case flat::Decl::Kind::kProtocol:
                file_ << ", request->" << member.name;
                break;
              case flat::Decl::Kind::kTable:
                assert(false && "c-codegen for tables not yet implemented");
                break;
              case flat::Decl::Kind::kStruct:
              case flat::Decl::Kind::kUnion:
                switch (member.nullability) {
                  case types::Nullability::kNullable:
                    file_ << ", request->" << member.name;
                    break;
                  case types::Nullability::kNonnullable:
                    file_ << ", &(request->" << member.name << ")";
                    break;
                }
                break;
            }
        }
      }
      if (method_info.response != nullptr)
        file_ << ", txn";
      file_ << ");\n";
      file_ << kIndent << kIndent << "break;\n";
      file_ << kIndent << "}\n";
    }
    file_ << kIndent << "default: {\n";
    file_ << kIndent << kIndent << "return ZX_ERR_NOT_SUPPORTED;\n";
    file_ << kIndent << "}\n";
    file_ << kIndent << "}\n";
    file_ << kIndent << "if ("
          << "status != ZX_OK && "
          << "status != ZX_ERR_STOP && "
          << "status != ZX_ERR_NEXT && "
          << "status != ZX_ERR_ASYNC) {\n";
    file_ << kIndent << kIndent << "return ZX_ERR_INTERNAL;\n";
    file_ << kIndent << "} else {\n";
    file_ << kIndent << kIndent << "return status;\n";
    file_ << kIndent << "}\n";
    file_ << "}\n\n";
  
    EmitServerDispatchDecl(&file_, named_protocol.c_name);
    file_ << " {\n";
    file_ << kIndent << "zx_status_t status = " << named_protocol.c_name
          << "_try_dispatch(ctx, txn, msg, ops);\n";
    file_ << kIndent << "if (status == ZX_ERR_NOT_SUPPORTED)\n";
    file_ << kIndent << kIndent << "FidlHandleInfoCloseMany(msg->handles, msg->num_handles);\n";
    file_ << kIndent << "return status;\n";
    file_ << "}\n\n";
  
    for (const auto& method_info : named_protocol.methods) {
      if (!method_info.request || !method_info.response)
        continue;
  
      std::vector<Member> response;
      if (!GetMethodParameters(library_, method_info, nullptr, &response) ||
          !CanGenerateCodecFunctions(response)) {
        continue;
      }
  
      size_t hcount = GetMaxHandlesFor(named_protocol.transport, method_info.response->typeshape);
  
      EmitServerReplyDecl(&file_, method_info.c_name, response);
      file_ << " {\n";
      file_ << kIndent << "uint32_t _wr_num_bytes = sizeof(" << method_info.response->c_name << ")";
      EmitMeasureInParams(&file_, response);
      file_ << ";\n";
      file_ << kIndent << "char _wr_bytes[_wr_num_bytes];\n";
      file_ << kIndent << method_info.response->c_name << "* _response = ("
            << method_info.response->c_name << "*)_wr_bytes;\n";
      file_ << kIndent << "memset(_wr_bytes, 0, sizeof(_wr_bytes));\n";
      EmitTxnHeader(&file_, "_response", method_info.ordinal_name);
      EmitLinearizeMessage(&file_, "_response", "_wr_bytes", response);
      const char* handle_value = "NULL";
      if (hcount > 0) {
        file_ << kIndent << "zx_handle_disposition_t _handles[" << hcount << "];\n";
        handle_value = "_handles";
      }
      file_ << kIndent << "fidl_outgoing_msg_t _msg = {\n";
      file_ << kIndent << kIndent << ".type = FIDL_OUTGOING_MSG_TYPE_BYTE,\n";
      file_ << kIndent << kIndent << ".byte = {\n";
      file_ << kIndent << kIndent << kIndent << ".bytes = _wr_bytes,\n";
      file_ << kIndent << kIndent << kIndent << ".handles = " << handle_value << ",\n";
      file_ << kIndent << kIndent << kIndent << ".num_bytes = _wr_num_bytes,\n";
      file_ << kIndent << kIndent << kIndent << ".num_handles = " << hcount << ",\n";
      file_ << kIndent << kIndent << "},\n";
      file_ << kIndent << "};\n";
      bool has_padding = method_info.response->typeshape.HasPadding();
      bool encode_response = (hcount > 0) || CountSecondaryObjects(response) > 0 || has_padding;
      if (encode_response) {
        file_ << kIndent << "zx_status_t _status = fidl_encode_msg(&"
              << method_info.response->coded_name << ", &_msg.byte, &_msg.byte.num_handles, NULL);\n";
        file_ << kIndent << "if (_status != ZX_OK)\n";
        file_ << kIndent << kIndent << "return _status;\n";
      } else {
        file_ << kIndent << "// OPTIMIZED AWAY fidl_encode() of POD-only reply\n";
      }
      file_ << kIndent << "return _txn->reply(_txn, &_msg);\n";
      file_ << "}\n\n";
    }
  }
  
  std::ostringstream CGenerator::ProduceHeader() {
    GeneratePrologues();
  
    std::map<const flat::Decl*, NamedBits> named_bits = NameBits(library_->bits_declarations_);
    std::map<const flat::Decl*, NamedConst> named_consts = NameConsts(library_->const_declarations_);
    std::map<const flat::Decl*, NamedEnum> named_enums = NameEnums(library_->enum_declarations_);
    std::map<const flat::Decl*, NamedProtocol> named_protocols =
        NameProtocols(library_->protocol_declarations_);
    std::map<const flat::Decl*, NamedStruct> named_structs =
        NameStructs(library_->struct_declarations_);
  
    file_ << "\n// Forward declarations\n\n";
  
    for (const auto* decl : library_->declaration_order_) {
      if (!DeclAllowed(decl)) {
        continue;
      }
      switch (decl->kind) {
        case flat::Decl::Kind::kBits: {
          auto iter = named_bits.find(decl);
          if (iter != named_bits.end()) {
            ProduceBitsForwardDeclaration(iter->second);
          }
          break;
        }
        case flat::Decl::Kind::kConst: {
          auto iter = named_consts.find(decl);
          if (iter != named_consts.end()) {
            ProduceConstForwardDeclaration(iter->second);
          }
          break;
        }
        case flat::Decl::Kind::kEnum: {
          auto iter = named_enums.find(decl);
          if (iter != named_enums.end()) {
            ProduceEnumForwardDeclaration(iter->second);
          }
          break;
        }
        case flat::Decl::Kind::kProtocol: {
          auto iter = named_protocols.find(decl);
          if (iter != named_protocols.end()) {
            ProduceProtocolForwardDeclaration(iter->second);
          }
          break;
        }
        case flat::Decl::Kind::kResource:
          // Do nothing.
          break;
        case flat::Decl::Kind::kService:
          // Do nothing.
          break;
        case flat::Decl::Kind::kStruct: {
          auto iter = named_structs.find(decl);
          if (iter != named_structs.end()) {
            ProduceStructForwardDeclaration(iter->second);
          }
          break;
        }
        case flat::Decl::Kind::kTable:
          // Do nothing.
          break;
        case flat::Decl::Kind::kTypeAlias:
          // TODO(fxbug.dev/7807): Do more than nothing.
          break;
        case flat::Decl::Kind::kUnion:
          // Do nothing.
          break;
      }  // switch
    }
  
    file_ << "\n// Extern declarations\n\n";
  
    for (const auto* decl : library_->declaration_order_) {
      if (!DeclAllowed(decl)) {
        continue;
      }
  
      switch (decl->kind) {
        case flat::Decl::Kind::kBits:
        case flat::Decl::Kind::kConst:
        case flat::Decl::Kind::kEnum:
        case flat::Decl::Kind::kResource:
        case flat::Decl::Kind::kService:
        case flat::Decl::Kind::kStruct:
        case flat::Decl::Kind::kTable:
        case flat::Decl::Kind::kTypeAlias:
        case flat::Decl::Kind::kUnion:
          // Only messages have extern fidl_type_t declarations.
          break;
        case flat::Decl::Kind::kProtocol: {
          auto iter = named_protocols.find(decl);
          if (iter != named_protocols.end()) {
            ProduceProtocolExternDeclaration(iter->second);
          }
          break;
        }
      }  // switch
    }
  
    file_ << "\n// Declarations\n\n";
  
    for (const auto* decl : library_->declaration_order_) {
      if (!DeclAllowed(decl)) {
        continue;
      }
  
      switch (decl->kind) {
        case flat::Decl::Kind::kBits:
          // Bits can be entirely forward declared, as they have no
          // dependencies other than standard headers.
          break;
        case flat::Decl::Kind::kConst: {
          auto iter = named_consts.find(decl);
          if (iter != named_consts.end()) {
            ProduceConstDeclaration(iter->second);
          }
          break;
        }
        case flat::Decl::Kind::kEnum:
          // Enums can be entirely forward declared, as they have no
          // dependencies other than standard headers.
          break;
        case flat::Decl::Kind::kProtocol: {
          auto iter = named_protocols.find(decl);
          if (iter != named_protocols.end()) {
            ProduceProtocolDeclaration(iter->second);
          }
          break;
        }
        case flat::Decl::Kind::kResource:
          // Do nothing.
          break;
        case flat::Decl::Kind::kService:
          // Do nothing.
          break;
        case flat::Decl::Kind::kStruct: {
          auto iter = named_structs.find(decl);
          if (iter != named_structs.end()) {
            ProduceStructDeclaration(iter->second);
          }
          break;
        }
        case flat::Decl::Kind::kTable:
          // Do nothing.
          break;
        case flat::Decl::Kind::kTypeAlias:
          // TODO(fxbug.dev/7807): Do more than nothing.
          break;
        case flat::Decl::Kind::kUnion:
          // Do nothing.
          break;
      }  // switch
    }
  
    file_ << "\n// Simple bindings \n\n";
  
    for (const auto* decl : library_->declaration_order_) {
      switch (decl->kind) {
        case flat::Decl::Kind::kBits:
        case flat::Decl::Kind::kConst:
        case flat::Decl::Kind::kEnum:
        case flat::Decl::Kind::kResource:
        case flat::Decl::Kind::kService:
        case flat::Decl::Kind::kStruct:
        case flat::Decl::Kind::kTable:
        case flat::Decl::Kind::kTypeAlias:
        case flat::Decl::Kind::kUnion:
          // Only protocols have client declarations.
          break;
        case flat::Decl::Kind::kProtocol: {
          if (!HasSimpleLayout(decl))
            break;
          auto iter = named_protocols.find(decl);
          if (iter != named_protocols.end()) {
            ProduceProtocolClientDeclaration(iter->second);
            ProduceProtocolServerDeclaration(iter->second);
          }
          break;
        }
      }  // switch
    }
  
    GenerateEpilogues();
  
    return std::move(file_);
  }
  
  std::ostringstream CGenerator::ProduceClient() {
    EmitFileComment(&file_);
    EmitIncludeHeader(&file_, "<lib/fidl/coding.h>");
    EmitIncludeHeader(&file_, "<lib/fidl/internal.h>");
    EmitIncludeHeader(&file_, "<lib/fidl/txn_header.h>");
    EmitIncludeHeader(&file_, "<alloca.h>");
    EmitIncludeHeader(&file_, "<string.h>");
    EmitIncludeHeader(&file_, "<zircon/assert.h>");
    EmitIncludeHeader(&file_, "<zircon/syscalls.h>");
    EmitIncludeHeader(&file_, "<" + NameLibraryCHeader(library_->name()) + ">");
    EmitBlank(&file_);
  
    std::map<const flat::Decl*, NamedProtocol> named_protocols =
        NameProtocols(library_->protocol_declarations_);
  
    for (const auto* decl : library_->declaration_order_) {
      switch (decl->kind) {
        case flat::Decl::Kind::kBits:
        case flat::Decl::Kind::kConst:
        case flat::Decl::Kind::kEnum:
        case flat::Decl::Kind::kResource:
        case flat::Decl::Kind::kService:
        case flat::Decl::Kind::kStruct:
        case flat::Decl::Kind::kTable:
        case flat::Decl::Kind::kTypeAlias:
        case flat::Decl::Kind::kUnion:
          // Only protocols have client implementations.
          break;
        case flat::Decl::Kind::kProtocol: {
          if (!HasSimpleLayout(decl))
            break;
          auto iter = named_protocols.find(decl);
          if (iter != named_protocols.end()) {
            ProduceProtocolClientImplementation(iter->second);
          }
          break;
        }
      }  // switch
    }
  
    return std::move(file_);
  }
  
  std::ostringstream CGenerator::ProduceServer() {
    EmitFileComment(&file_);
    EmitIncludeHeader(&file_, "<lib/fidl/coding.h>");
    EmitIncludeHeader(&file_, "<lib/fidl/internal.h>");
    EmitIncludeHeader(&file_, "<lib/fidl/txn_header.h>");
    EmitIncludeHeader(&file_, "<alloca.h>");
    EmitIncludeHeader(&file_, "<string.h>");
    EmitIncludeHeader(&file_, "<zircon/assert.h>");
    EmitIncludeHeader(&file_, "<zircon/syscalls.h>");
    EmitIncludeHeader(&file_, "<" + NameLibraryCHeader(library_->name()) + ">");
    EmitBlank(&file_);
  
    std::map<const flat::Decl*, NamedProtocol> named_protocols =
        NameProtocols(library_->protocol_declarations_);
  
    for (const auto* decl : library_->declaration_order_) {
      switch (decl->kind) {
        case flat::Decl::Kind::kBits:
        case flat::Decl::Kind::kConst:
        case flat::Decl::Kind::kEnum:
        case flat::Decl::Kind::kResource:
        case flat::Decl::Kind::kService:
        case flat::Decl::Kind::kStruct:
        case flat::Decl::Kind::kTable:
        case flat::Decl::Kind::kTypeAlias:
        case flat::Decl::Kind::kUnion:
          // Only protocols have client implementations.
          break;
        case flat::Decl::Kind::kProtocol: {
          if (!HasSimpleLayout(decl))
            break;
          auto iter = named_protocols.find(decl);
          if (iter != named_protocols.end()) {
            ProduceProtocolServerImplementation(iter->second);
          }
          break;
        }
      }  // switch
    }
  
    return std::move(file_);
  }
  
  }  // namespace fidl
  