<commit_before>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "generator/internal/metadata_decorator_generator.h"
#include "absl/memory/memory.h"
#include "absl/strings/str_split.h"
#include "generator/internal/codegen_utils.h"
#include "generator/internal/predicate_utils.h"
#include "generator/internal/printer.h"
#include <google/protobuf/descriptor.h>

namespace google {
namespace cloud {
namespace generator_internal {

MetadataDecoratorGenerator::MetadataDecoratorGenerator(
    google::protobuf::ServiceDescriptor const* service_descriptor,
    VarsDictionary service_vars,
    std::map<std::string, VarsDictionary> service_method_vars,
    google::protobuf::compiler::GeneratorContext* context)
    : ServiceCodeGenerator("metadata_header_path", "metadata_cc_path",
                           service_descriptor, std::move(service_vars),
                           std::move(service_method_vars), context) {}

Status MetadataDecoratorGenerator::GenerateHeader() {
  HeaderPrint(CopyrightLicenseFileHeader());
  HeaderPrint(  // clang-format off
    "\n"
    "// Generated by the Codegen C++ plugin.\n"
    "// If you make any local changes, they will be lost.\n"
    "// source: $proto_file_name$\n"
    "\n"
    "#ifndef $header_include_guard$\n"
    "#define $header_include_guard$\n");
  // clang-format on

  // includes
  HeaderPrint("\n");
  HeaderLocalIncludes({vars("stub_header_path"), "google/cloud/version.h"});
  HeaderSystemIncludes(
      {HasLongrunningMethod() ? "google/longrunning/operations.grpc.pb.h" : "",
       "memory", "string"});

  auto result = HeaderOpenNamespaces(NamespaceType::kInternal);
  if (!result.ok()) return result;

  // metadata decorator class
  HeaderPrint(  // clang-format off
    "\n"
    "class $metadata_class_name$ : public $stub_class_name$ {\n"
    " public:\n"
    "  ~$metadata_class_name$() override = default;\n"
    "  explicit $metadata_class_name$(std::shared_ptr<$stub_class_name$> child);\n");
  // clang-format on

  for (auto const& method : methods()) {
    if (IsStreamingWrite(method)) {
      HeaderPrintMethod(method, __FILE__, __LINE__,
                        R"""(
  std::unique_ptr<::google::cloud::internal::StreamingWriteRpc<
      $request_type$,
      $response_type$>>
  $method_name$(
      std::unique_ptr<grpc::ClientContext> context) override;
)""");
      continue;
    }
    if (IsBidirStreaming(method)) {
      HeaderPrintMethod(method, __FILE__, __LINE__,
                        R"""(
  std::unique_ptr<::google::cloud::AsyncStreamingReadWriteRpc<
      $request_type$,
      $response_type$>>
  Async$method_name$(
      google::cloud::CompletionQueue const& cq,
      std::unique_ptr<grpc::ClientContext> context) override;
)""");
      continue;
    }
    HeaderPrintMethod(
        method,
        {MethodPattern({{IsResponseTypeEmpty,
                         // clang-format off
    "\n  Status $method_name$(\n",
    "\n  StatusOr<$response_type$> $method_name$(\n"},
   {"    grpc::ClientContext& context,\n"
    "    $request_type$ const& request) override;\n"
                            // clang-format on
                        }},
                       And(IsNonStreaming, Not(IsLongrunningOperation))),
         MethodPattern({{R"""(
  future<StatusOr<google::longrunning::Operation>> Async$method_name$(
      google::cloud::CompletionQueue& cq,
      std::unique_ptr<grpc::ClientContext> context,
      $request_type$ const& request) override;
)"""}},
                       IsLongrunningOperation),
         MethodPattern(
             {    // clang-format off
   {"\n"
    "  std::unique_ptr<google::cloud::internal::StreamingReadRpc<$response_type$>>\n"
    "    $method_name$(\n"
    "    std::unique_ptr<grpc::ClientContext> context,\n"
    "    $request_type$ const& request) override;\n"
                  // clang-format on
              }},
             IsStreamingRead)},
        __FILE__, __LINE__);
  }

  for (auto const& method : async_methods()) {
    if (IsStreamingRead(method)) {
      auto constexpr kDeclaration = R"""(
  std::unique_ptr<::google::cloud::internal::AsyncStreamingReadRpc<
      $response_type$>>
  Async$method_name$(
      google::cloud::CompletionQueue const& cq,
      std::unique_ptr<grpc::ClientContext> context,
      $request_type$ const& request) override;
)""";
      HeaderPrintMethod(method, __FILE__, __LINE__, kDeclaration);
      continue;
    }
    HeaderPrintMethod(
        method,
        {MethodPattern(
            {{IsResponseTypeEmpty,
              // clang-format off
    "\n  future<Status> Async$method_name$(\n",
    "\n  future<StatusOr<$response_type$>> Async$method_name$(\n"},
   {"    google::cloud::CompletionQueue& cq,\n"
    "    std::unique_ptr<grpc::ClientContext> context,\n"
    "    $request_type$ const& request) override;\n"
                 // clang-format on
             }},
            And(IsNonStreaming, Not(IsLongrunningOperation)))},
        __FILE__, __LINE__);
  }

  if (HasLongrunningMethod()) {
    HeaderPrint(
        R"""(
  future<StatusOr<google::longrunning::Operation>> AsyncGetOperation(
      google::cloud::CompletionQueue& cq,
      std::unique_ptr<grpc::ClientContext> context,
      google::longrunning::GetOperationRequest const& request) override;

  future<Status> AsyncCancelOperation(
      google::cloud::CompletionQueue& cq,
      std::unique_ptr<grpc::ClientContext> context,
      google::longrunning::CancelOperationRequest const& request) override;
)""");
  }

  HeaderPrint(R"""(
 private:
  void SetMetadata(grpc::ClientContext& context,
                   std::string const& request_params);
  void SetMetadata(grpc::ClientContext& context);

  std::shared_ptr<$stub_class_name$> child_;
  std::string api_client_header_;
};
)""");

  HeaderCloseNamespaces();
  // close header guard
  HeaderPrint("\n#endif  // $header_include_guard$\n");
  return {};
}

Status MetadataDecoratorGenerator::GenerateCc() {
  CcPrint(CopyrightLicenseFileHeader());
  CcPrint(  // clang-format off
            "\n"
            "// Generated by the Codegen C++ plugin.\n"
            "// If you make any local changes, they will be lost.\n"
            "// source: $proto_file_name$\n");
  // clang-format on

  // includes
  CcPrint("\n");
  CcLocalIncludes({vars("metadata_header_path"),
                   "google/cloud/internal/api_client_header.h",
                   "google/cloud/common_options.h",
                   "google/cloud/status_or.h"});
  CcSystemIncludes({vars("proto_grpc_header_path"), "memory"});

  auto result = CcOpenNamespaces(NamespaceType::kInternal);
  if (!result.ok()) return result;

  // constructor
  CcPrint(  // clang-format off
    "\n"
    "$metadata_class_name$::$metadata_class_name$(\n"
    "    std::shared_ptr<$stub_class_name$> child)\n"
    "    : child_(std::move(child)),\n"
    "      api_client_header_(google::cloud::internal::ApiClientHeader(\"generator\")) {}\n");
  // clang-format on

  // metadata decorator class member methods
  for (auto const& method : methods()) {
    if (IsStreamingWrite(method)) {
      CcPrintMethod(method, __FILE__, __LINE__,
                    R"""(
std::unique_ptr<::google::cloud::internal::StreamingWriteRpc<
    $request_type$,
    $response_type$>>
$metadata_class_name$::$method_name$(
    std::unique_ptr<grpc::ClientContext> context) {
  SetMetadata(*context);
  return child_->$method_name$(std::move(context));
}
)""");
      continue;
    }
    if (IsBidirStreaming(method)) {
      CcPrintMethod(method, __FILE__, __LINE__,
                    R"""(
std::unique_ptr<::google::cloud::AsyncStreamingReadWriteRpc<
      $request_type$,
      $response_type$>>
$metadata_class_name$::Async$method_name$(
    google::cloud::CompletionQueue const& cq,
    std::unique_ptr<grpc::ClientContext> context) {
  SetMetadata(*context);
  return child_->Async$method_name$(cq, std::move(context));
}
)""");
      continue;
    }
    CcPrintMethod(
        method,
        {MethodPattern(
             {
                 {IsResponseTypeEmpty,
                  // clang-format off
    "\nStatus\n",
    "\nStatusOr<$response_type$>\n"},
   {"$metadata_class_name$::$method_name$(\n"
    "    grpc::ClientContext& context,\n"
    "    $request_type$ const& request) {\n"},
   {HasRoutingHeader,
    "  SetMetadata(context, \"$method_request_param_key$=\" + request.$method_request_param_value$);\n",
    "  SetMetadata(context, {});\n"},
   {"  return child_->$method_name$(context, request);\n"
    "}\n",}
                 // clang-format on
             },
             And(IsNonStreaming, Not(IsLongrunningOperation))),
         MethodPattern({{HasRoutingHeader,
                         R"""(
future<StatusOr<google::longrunning::Operation>>
$metadata_class_name$::Async$method_name$(
    google::cloud::CompletionQueue& cq,
    std::unique_ptr<grpc::ClientContext> context,
    $request_type$ const& request) {
  SetMetadata(*context, "$method_request_param_key$=" + request.$method_request_param_value$);
  return child_->Async$method_name$(cq, std::move(context), request);
}
)""",
                         R"""(
future<StatusOr<google::longrunning::Operation>>
$metadata_class_name$::Async$method_name$(
    google::cloud::CompletionQueue& cq,
    std::unique_ptr<grpc::ClientContext> context,
    $request_type$ const& request) {
  SetMetadata(*context, {});
  return child_->Async$method_name$(cq, std::move(context), request);
}
)"""}},
                       IsLongrunningOperation),
         MethodPattern(
             {
                 // clang-format off
   {"\n"
    "std::unique_ptr<google::cloud::internal::StreamingReadRpc<$response_type$>>\n"
    "$metadata_class_name$::$method_name$(\n"
    "    std::unique_ptr<grpc::ClientContext> context,\n"
    "    $request_type$ const& request) {\n"},
   {HasRoutingHeader,
    "  SetMetadata(*context, \"$method_request_param_key$=\" + request.$method_request_param_value$);\n",
    "  SetMetadata(*context, {});\n"},
   {"  return child_->$method_name$(std::move(context), request);\n"
    "}\n",}
                 // clang-format on
             },
             IsStreamingRead)},
        __FILE__, __LINE__);
  }

  for (auto const& method : async_methods()) {
    if (IsStreamingRead(method)) {
      auto constexpr kDefinition = R"""(
std::unique_ptr<::google::cloud::internal::AsyncStreamingReadRpc<
      $response_type$>>
$metadata_class_name$::Async$method_name$(
    google::cloud::CompletionQueue const& cq,
    std::unique_ptr<grpc::ClientContext> context,
    $request_type$ const& request) {
  SetMetadata(*context);
  return child_->Async$method_name$(cq, std::move(context), request);
}
)""";
      CcPrintMethod(method, __FILE__, __LINE__, kDefinition);
      continue;
    }
    CcPrintMethod(
        method,
        {MethodPattern(
            {{IsResponseTypeEmpty,
              // clang-format off
    "\nfuture<Status>\n",
    "\nfuture<StatusOr<$response_type$>>\n"},
    {R"""($metadata_class_name$::Async$method_name$(
    google::cloud::CompletionQueue& cq,
    std::unique_ptr<grpc::ClientContext> context,
    $request_type$ const& request) {)"""},
   {HasRoutingHeader, R"""(
  SetMetadata(*context, "$method_request_param_key$=" + request.$method_request_param_value$);)""",
   R"""(
  SetMetadata(*context, {});)"""}, {R"""(
  return child_->Async$method_name$(cq, std::move(context), request);
}
)"""}},
            // clang-format on
            And(IsNonStreaming, Not(IsLongrunningOperation)))},
        __FILE__, __LINE__);
  }

  // long running operation support methods
  if (HasLongrunningMethod()) {
    CcPrint(R"""(
future<StatusOr<google::longrunning::Operation>>
$metadata_class_name$::AsyncGetOperation(
    google::cloud::CompletionQueue& cq,
    std::unique_ptr<grpc::ClientContext> context,
    google::longrunning::GetOperationRequest const& request) {
  SetMetadata(*context, "name=" + request.name());
  return child_->AsyncGetOperation(cq, std::move(context), request);
}

future<Status> $metadata_class_name$::AsyncCancelOperation(
    google::cloud::CompletionQueue& cq,
    std::unique_ptr<grpc::ClientContext> context,
    google::longrunning::CancelOperationRequest const& request) {
  SetMetadata(*context, "name=" + request.name());
  return child_->AsyncCancelOperation(cq, std::move(context), request);
}
)""");
  }

  CcPrint(R"""(
void $metadata_class_name$::SetMetadata(grpc::ClientContext& context,
                                        std::string const& request_params) {
  context.AddMetadata("x-goog-request-params", request_params);
  SetMetadata(context);
}

void $metadata_class_name$::SetMetadata(grpc::ClientContext& context) {
  context.AddMetadata("x-goog-api-client", api_client_header_);
  auto const& options = internal::CurrentOptions();
  if (options.has<UserProjectOption>()) {
    context.AddMetadata(
        "x-goog-user-project", options.get<UserProjectOption>());
  }
  auto const& authority = options.get<AuthorityOption>();
  if (!authority.empty()) context.set_authority(authority);
}
)""");

  CcCloseNamespaces();
  return {};
}

}  // namespace generator_internal
}  // namespace cloud
}  // namespace google
<commit_msg>fix(generator): async streaming read MD respects routing header (#8962)<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "generator/internal/metadata_decorator_generator.h"
#include "google/cloud/internal/absl_str_cat_quiet.h"
#include "absl/memory/memory.h"
#include "absl/strings/str_split.h"
#include "generator/internal/codegen_utils.h"
#include "generator/internal/predicate_utils.h"
#include "generator/internal/printer.h"
#include <google/protobuf/descriptor.h>

namespace google {
namespace cloud {
namespace generator_internal {

MetadataDecoratorGenerator::MetadataDecoratorGenerator(
    google::protobuf::ServiceDescriptor const* service_descriptor,
    VarsDictionary service_vars,
    std::map<std::string, VarsDictionary> service_method_vars,
    google::protobuf::compiler::GeneratorContext* context)
    : ServiceCodeGenerator("metadata_header_path", "metadata_cc_path",
                           service_descriptor, std::move(service_vars),
                           std::move(service_method_vars), context) {}

Status MetadataDecoratorGenerator::GenerateHeader() {
  HeaderPrint(CopyrightLicenseFileHeader());
  HeaderPrint(  // clang-format off
    "\n"
    "// Generated by the Codegen C++ plugin.\n"
    "// If you make any local changes, they will be lost.\n"
    "// source: $proto_file_name$\n"
    "\n"
    "#ifndef $header_include_guard$\n"
    "#define $header_include_guard$\n");
  // clang-format on

  // includes
  HeaderPrint("\n");
  HeaderLocalIncludes({vars("stub_header_path"), "google/cloud/version.h"});
  HeaderSystemIncludes(
      {HasLongrunningMethod() ? "google/longrunning/operations.grpc.pb.h" : "",
       "memory", "string"});

  auto result = HeaderOpenNamespaces(NamespaceType::kInternal);
  if (!result.ok()) return result;

  // metadata decorator class
  HeaderPrint(  // clang-format off
    "\n"
    "class $metadata_class_name$ : public $stub_class_name$ {\n"
    " public:\n"
    "  ~$metadata_class_name$() override = default;\n"
    "  explicit $metadata_class_name$(std::shared_ptr<$stub_class_name$> child);\n");
  // clang-format on

  for (auto const& method : methods()) {
    if (IsStreamingWrite(method)) {
      HeaderPrintMethod(method, __FILE__, __LINE__,
                        R"""(
  std::unique_ptr<::google::cloud::internal::StreamingWriteRpc<
      $request_type$,
      $response_type$>>
  $method_name$(
      std::unique_ptr<grpc::ClientContext> context) override;
)""");
      continue;
    }
    if (IsBidirStreaming(method)) {
      HeaderPrintMethod(method, __FILE__, __LINE__,
                        R"""(
  std::unique_ptr<::google::cloud::AsyncStreamingReadWriteRpc<
      $request_type$,
      $response_type$>>
  Async$method_name$(
      google::cloud::CompletionQueue const& cq,
      std::unique_ptr<grpc::ClientContext> context) override;
)""");
      continue;
    }
    HeaderPrintMethod(
        method,
        {MethodPattern({{IsResponseTypeEmpty,
                         // clang-format off
    "\n  Status $method_name$(\n",
    "\n  StatusOr<$response_type$> $method_name$(\n"},
   {"    grpc::ClientContext& context,\n"
    "    $request_type$ const& request) override;\n"
                            // clang-format on
                        }},
                       And(IsNonStreaming, Not(IsLongrunningOperation))),
         MethodPattern({{R"""(
  future<StatusOr<google::longrunning::Operation>> Async$method_name$(
      google::cloud::CompletionQueue& cq,
      std::unique_ptr<grpc::ClientContext> context,
      $request_type$ const& request) override;
)"""}},
                       IsLongrunningOperation),
         MethodPattern(
             {    // clang-format off
   {"\n"
    "  std::unique_ptr<google::cloud::internal::StreamingReadRpc<$response_type$>>\n"
    "    $method_name$(\n"
    "    std::unique_ptr<grpc::ClientContext> context,\n"
    "    $request_type$ const& request) override;\n"
                  // clang-format on
              }},
             IsStreamingRead)},
        __FILE__, __LINE__);
  }

  for (auto const& method : async_methods()) {
    if (IsStreamingRead(method)) {
      auto constexpr kDeclaration = R"""(
  std::unique_ptr<::google::cloud::internal::AsyncStreamingReadRpc<
      $response_type$>>
  Async$method_name$(
      google::cloud::CompletionQueue const& cq,
      std::unique_ptr<grpc::ClientContext> context,
      $request_type$ const& request) override;
)""";
      HeaderPrintMethod(method, __FILE__, __LINE__, kDeclaration);
      continue;
    }
    HeaderPrintMethod(
        method,
        {MethodPattern(
            {{IsResponseTypeEmpty,
              // clang-format off
    "\n  future<Status> Async$method_name$(\n",
    "\n  future<StatusOr<$response_type$>> Async$method_name$(\n"},
   {"    google::cloud::CompletionQueue& cq,\n"
    "    std::unique_ptr<grpc::ClientContext> context,\n"
    "    $request_type$ const& request) override;\n"
                 // clang-format on
             }},
            And(IsNonStreaming, Not(IsLongrunningOperation)))},
        __FILE__, __LINE__);
  }

  if (HasLongrunningMethod()) {
    HeaderPrint(
        R"""(
  future<StatusOr<google::longrunning::Operation>> AsyncGetOperation(
      google::cloud::CompletionQueue& cq,
      std::unique_ptr<grpc::ClientContext> context,
      google::longrunning::GetOperationRequest const& request) override;

  future<Status> AsyncCancelOperation(
      google::cloud::CompletionQueue& cq,
      std::unique_ptr<grpc::ClientContext> context,
      google::longrunning::CancelOperationRequest const& request) override;
)""");
  }

  HeaderPrint(R"""(
 private:
  void SetMetadata(grpc::ClientContext& context,
                   std::string const& request_params);
  void SetMetadata(grpc::ClientContext& context);

  std::shared_ptr<$stub_class_name$> child_;
  std::string api_client_header_;
};
)""");

  HeaderCloseNamespaces();
  // close header guard
  HeaderPrint("\n#endif  // $header_include_guard$\n");
  return {};
}

Status MetadataDecoratorGenerator::GenerateCc() {
  CcPrint(CopyrightLicenseFileHeader());
  CcPrint(  // clang-format off
            "\n"
            "// Generated by the Codegen C++ plugin.\n"
            "// If you make any local changes, they will be lost.\n"
            "// source: $proto_file_name$\n");
  // clang-format on

  // includes
  CcPrint("\n");
  CcLocalIncludes({vars("metadata_header_path"),
                   "google/cloud/internal/api_client_header.h",
                   "google/cloud/common_options.h",
                   "google/cloud/status_or.h"});
  CcSystemIncludes({vars("proto_grpc_header_path"), "memory"});

  auto result = CcOpenNamespaces(NamespaceType::kInternal);
  if (!result.ok()) return result;

  // constructor
  CcPrint(  // clang-format off
    "\n"
    "$metadata_class_name$::$metadata_class_name$(\n"
    "    std::shared_ptr<$stub_class_name$> child)\n"
    "    : child_(std::move(child)),\n"
    "      api_client_header_(google::cloud::internal::ApiClientHeader(\"generator\")) {}\n");
  // clang-format on

  // metadata decorator class member methods
  for (auto const& method : methods()) {
    if (IsStreamingWrite(method)) {
      CcPrintMethod(method, __FILE__, __LINE__,
                    R"""(
std::unique_ptr<::google::cloud::internal::StreamingWriteRpc<
    $request_type$,
    $response_type$>>
$metadata_class_name$::$method_name$(
    std::unique_ptr<grpc::ClientContext> context) {
  SetMetadata(*context);
  return child_->$method_name$(std::move(context));
}
)""");
      continue;
    }
    if (IsBidirStreaming(method)) {
      CcPrintMethod(method, __FILE__, __LINE__,
                    R"""(
std::unique_ptr<::google::cloud::AsyncStreamingReadWriteRpc<
      $request_type$,
      $response_type$>>
$metadata_class_name$::Async$method_name$(
    google::cloud::CompletionQueue const& cq,
    std::unique_ptr<grpc::ClientContext> context) {
  SetMetadata(*context);
  return child_->Async$method_name$(cq, std::move(context));
}
)""");
      continue;
    }
    CcPrintMethod(
        method,
        {MethodPattern(
             {
                 {IsResponseTypeEmpty,
                  // clang-format off
    "\nStatus\n",
    "\nStatusOr<$response_type$>\n"},
   {"$metadata_class_name$::$method_name$(\n"
    "    grpc::ClientContext& context,\n"
    "    $request_type$ const& request) {\n"},
   {HasRoutingHeader,
    "  SetMetadata(context, \"$method_request_param_key$=\" + request.$method_request_param_value$);\n",
    "  SetMetadata(context, {});\n"},
   {"  return child_->$method_name$(context, request);\n"
    "}\n",}
                 // clang-format on
             },
             And(IsNonStreaming, Not(IsLongrunningOperation))),
         MethodPattern({{HasRoutingHeader,
                         R"""(
future<StatusOr<google::longrunning::Operation>>
$metadata_class_name$::Async$method_name$(
    google::cloud::CompletionQueue& cq,
    std::unique_ptr<grpc::ClientContext> context,
    $request_type$ const& request) {
  SetMetadata(*context, "$method_request_param_key$=" + request.$method_request_param_value$);
  return child_->Async$method_name$(cq, std::move(context), request);
}
)""",
                         R"""(
future<StatusOr<google::longrunning::Operation>>
$metadata_class_name$::Async$method_name$(
    google::cloud::CompletionQueue& cq,
    std::unique_ptr<grpc::ClientContext> context,
    $request_type$ const& request) {
  SetMetadata(*context, {});
  return child_->Async$method_name$(cq, std::move(context), request);
}
)"""}},
                       IsLongrunningOperation),
         MethodPattern(
             {
                 // clang-format off
   {"\n"
    "std::unique_ptr<google::cloud::internal::StreamingReadRpc<$response_type$>>\n"
    "$metadata_class_name$::$method_name$(\n"
    "    std::unique_ptr<grpc::ClientContext> context,\n"
    "    $request_type$ const& request) {\n"},
   {HasRoutingHeader,
    "  SetMetadata(*context, \"$method_request_param_key$=\" + request.$method_request_param_value$);\n",
    "  SetMetadata(*context, {});\n"},
   {"  return child_->$method_name$(std::move(context), request);\n"
    "}\n",}
                 // clang-format on
             },
             IsStreamingRead)},
        __FILE__, __LINE__);
  }

  for (auto const& method : async_methods()) {
    if (IsStreamingRead(method)) {
      auto const definition = absl::StrCat(
          R"""(
std::unique_ptr<::google::cloud::internal::AsyncStreamingReadRpc<
      $response_type$>>
$metadata_class_name$::Async$method_name$(
    google::cloud::CompletionQueue const& cq,
    std::unique_ptr<grpc::ClientContext> context,
    $request_type$ const& request) {
)""",  // clang-format off
      HasRoutingHeader(method) ?
"  SetMetadata(*context, \"$method_request_param_key$=\" + request.$method_request_param_value$);" :
"  SetMetadata(*context);", // clang-format-on
          R"""(
  return child_->Async$method_name$(cq, std::move(context), request);
}
)""");
      CcPrintMethod(method, __FILE__, __LINE__, definition);
      continue;
    }
    CcPrintMethod(
        method,
        {MethodPattern(
            {{IsResponseTypeEmpty,
              // clang-format off
    "\nfuture<Status>\n",
    "\nfuture<StatusOr<$response_type$>>\n"},
    {R"""($metadata_class_name$::Async$method_name$(
    google::cloud::CompletionQueue& cq,
    std::unique_ptr<grpc::ClientContext> context,
    $request_type$ const& request) {)"""},
   {HasRoutingHeader, R"""(
  SetMetadata(*context, "$method_request_param_key$=" + request.$method_request_param_value$);)""",
   R"""(
  SetMetadata(*context, {});)"""}, {R"""(
  return child_->Async$method_name$(cq, std::move(context), request);
}
)"""}},
            // clang-format on
            And(IsNonStreaming, Not(IsLongrunningOperation)))},
        __FILE__, __LINE__);
  }

  // long running operation support methods
  if (HasLongrunningMethod()) {
    CcPrint(R"""(
future<StatusOr<google::longrunning::Operation>>
$metadata_class_name$::AsyncGetOperation(
    google::cloud::CompletionQueue& cq,
    std::unique_ptr<grpc::ClientContext> context,
    google::longrunning::GetOperationRequest const& request) {
  SetMetadata(*context, "name=" + request.name());
  return child_->AsyncGetOperation(cq, std::move(context), request);
}

future<Status> $metadata_class_name$::AsyncCancelOperation(
    google::cloud::CompletionQueue& cq,
    std::unique_ptr<grpc::ClientContext> context,
    google::longrunning::CancelOperationRequest const& request) {
  SetMetadata(*context, "name=" + request.name());
  return child_->AsyncCancelOperation(cq, std::move(context), request);
}
)""");
  }

  CcPrint(R"""(
void $metadata_class_name$::SetMetadata(grpc::ClientContext& context,
                                        std::string const& request_params) {
  context.AddMetadata("x-goog-request-params", request_params);
  SetMetadata(context);
}

void $metadata_class_name$::SetMetadata(grpc::ClientContext& context) {
  context.AddMetadata("x-goog-api-client", api_client_header_);
  auto const& options = internal::CurrentOptions();
  if (options.has<UserProjectOption>()) {
    context.AddMetadata(
        "x-goog-user-project", options.get<UserProjectOption>());
  }
  auto const& authority = options.get<AuthorityOption>();
  if (!authority.empty()) context.set_authority(authority);
}
)""");

  CcCloseNamespaces();
  return {};
}

}  // namespace generator_internal
}  // namespace cloud
}  // namespace google
<|endoftext|>


<commit_before>// bdldfp_decimalconvertutil.cpp                                      -*-C++-*-
#include <bdldfp_decimalconvertutil.h>

#include <bsls_ident.h>
BSLS_IDENT_RCSID(bdldfp_decimalconvertutil_cpp,"$Id$ $CSID$")

#include <bdldfp_decimalplatform.h>

#include <bsls_assert.h>

#ifdef BDLDFP_DECIMALPLATFORM_C99_TR
#  ifndef  __STDC_WANT_DEC_FP__
#    error __STDC_WANT_DEC_FP__ must be defined on the command line!
     char die[-42];     // if '#error' unsupported
#  endif
#endif

#include <bsl_algorithm.h>
#include <bsl_cstdlib.h>
#include <bsl_cstring.h>

namespace BloombergLP {
namespace bdldfp {

namespace {
                        // Reverse Memory

static void memrev(void *buffer, size_t count)
    // Reverse the order of the first specified 'count' bytes, at the beginning
    // of the specified 'buffer'.  'count % 2' must be zero.
{
    unsigned char *b = static_cast<unsigned char *>(buffer);
    bsl::reverse(b, b + count);
}

                        // Mem copy with reversal functions

unsigned char *memReverseIfNeeded(void *buffer, size_t count)
    // Reverse the first specified 'count' bytes from the specified 'buffer`,
    // if the host endian is different from network endian, and return the
    // address computed from 'static_cast<unsigned char *>(buffer) + count'.
{
#ifdef BDLDFP_DECIMALPLATFORM_LITTLE_ENDIAN
    // little endian, needs to do some byte juggling
    memrev(buffer, count);
#endif
    return static_cast<unsigned char*>(buffer) + count;
}

                        // Decimal-network conversion functions

template <class DECIMAL_TYPE>
const unsigned char *decimalFromNetworkT(DECIMAL_TYPE        *decimal,
                                         const unsigned char *buffer)
    // Construct into the specified 'decimal', the base-10 value represented by
    // the network-ordered bytes in the specified 'buffer', and return a raw
    // memory pointer, providing modifiable access, to one byte past the last
    // byte read from 'buffer'.
{
    bsl::memcpy(decimal, buffer, sizeof(DECIMAL_TYPE));
    memReverseIfNeeded(decimal, sizeof(DECIMAL_TYPE));

    DecimalConvertUtil::decimalFromDPD(
                                   decimal,
                                   reinterpret_cast<unsigned char *>(decimal));

    return buffer + sizeof(DECIMAL_TYPE);
}

template <class DECIMAL_TYPE>
unsigned char *decimalToNetworkT(unsigned char *buffer, DECIMAL_TYPE decimal)
    // Construct into the specified 'buffer', the network-ordered byte
    // representation of the base-10 value of the specified 'decimal', and,
    // return a raw memory pointer, providing modifiable access, to one byte
    // past the last written byte of the 'buffer'.
{
    DecimalConvertUtil::decimalToDPD(buffer, decimal);
    return memReverseIfNeeded(buffer, sizeof(DECIMAL_TYPE));
}

                        // =================
                        // class StdioFormat
                        // =================

template <class FORMATTED_TYPE> struct StdioFormat;
    // This 'struct' template provides a method, 'format', that returns a
    // 'printf'-style format string to format values of the template parameter
    // type 'FORMATTED_TYPE' that can be used to restore a decimal value that
    // was previously converted to this type.

template <>
struct StdioFormat<float> {
    // This template specialization of 'StdioFormat' provides a function that
    // returns a 'printf'-style format string for 'float' values.

    static const char* format();
        // Return a 'printf'-style format string that can be used to restore a
        // decimal value that was previously converted to a 'float' value.
        // Refer to the documentation of 'decimalFromFloat' for the conversion
        // rules.
};

template <>
struct StdioFormat<double> {
    // This template specialization of 'StdioFormat' provides a function that
    // returns a 'printf'-style format string for 'double' values.

    static char const* format();
        // Return a 'printf'-style format string that can be used to restore a
        // decimal value that was previously converted to a 'double' value.
        // Refer to the documentation of 'decimalFromDouble' for the conversion
        // rules.
};

                        // -----------------
                        // class StdioFormat
                        // -----------------

const char* StdioFormat<float>::format()
{
    return "%.6g";
}

const char* StdioFormat<double>::format()
{
    return "%.15g";
}

                        // ===================
                        // class DecimalTraits
                        // ===================

template <class DECIMAL_TYPE> struct DecimalTraits;
    // This 'struct' template provides a way to create an object of the
    // template parameter type 'DECIMAL_TYPE' though a consistent interface.

template <>
struct DecimalTraits<Decimal32> {
    // This template specialization of 'DecimalTraits' provides functions to
    // create 'Decimal32' values.

    typedef int SignificandType;
        // This 'typedef' defines a type that is large enough to hold the
        // significant of 'Decimal32'.

    static Decimal32 make(int significand, int exponent);
        // Return a 'Decimal32' value having the specified 'significand' and
        // the specified 'exponent'.
};

template <>
struct DecimalTraits<Decimal64> {
    // This template specialization of 'DecimalTraits' provides utilities to
    // create 'Decimal64' values.

    typedef long long SignificandType;
        // This 'typedef' defines a type that is large enough to hold the
        // significant of 'Decimal64'.

    static Decimal64 make(long long significand, int exponent);
        // Return a 'Decimal64' value having the specified 'significand' and
        // the specified 'exponent'.

};

template <>
struct DecimalTraits<bdldfp::Decimal128> {
    // This template specialization of 'DecimalTraits' provides utilities to
    // create 'Decimal128' values.

    typedef long long SignificandType;
        // This 'typedef' defines a type that is large enough to hold the
        // significant of 'Decimal128' if it's small enough to be convertible
        // to a double.

    static bdldfp::Decimal128 make(long long significand, int exponent);
        // Return a 'Decimal128' value having the specified 'significand' and
        // the specified 'exponent'.
};

                        // ===================
                        // class DecimalTraits
                        // ===================

Decimal32 DecimalTraits<Decimal32>::make(int significand, int exponent)
{
    return bdldfp::DecimalUtil::makeDecimalRaw32(significand, exponent);
}

Decimal64 DecimalTraits<Decimal64>::make(long long significand, int exponent)
{
    return bdldfp::DecimalUtil::makeDecimalRaw64(significand, exponent);
}

Decimal128 DecimalTraits<Decimal128>::make(long long significand, int exponent)
{
    return bdldfp::DecimalUtil::makeDecimalRaw128(significand, exponent);
}

                  // Helpers for Restoring Decimal from Binary

template <class DECIMAL_TYPE, class BINARY_TYPE>
void restoreDecimalFromBinary(DECIMAL_TYPE *dfp, BINARY_TYPE bfp)
    // Construct, in the specified 'dfp', a decimal floating point
    // representation of the value of the binary floating point value specified
    // by 'bfp'.
{

    if (bfp != bfp) {
        *dfp = bsl::numeric_limits<DECIMAL_TYPE>::quiet_NaN();
        if (bfp < 0) {
            *dfp = -*dfp;
        }
        return;                                                       // RETURN
    }

    if (bfp == bsl::numeric_limits<BINARY_TYPE>::infinity()) {
        *dfp = bsl::numeric_limits<DECIMAL_TYPE>::infinity();
        return;                                                       // RETURN
    }

    if (bfp == -bsl::numeric_limits<BINARY_TYPE>::infinity()) {
        *dfp = -bsl::numeric_limits<DECIMAL_TYPE>::infinity();
        return;                                                       // RETURN
    }

    char buffer[48];
    snprintf(buffer, sizeof(buffer), StdioFormat<BINARY_TYPE>::format(), bfp);

    typename DecimalTraits<DECIMAL_TYPE>::SignificandType significand(0);
    int  exponent(0);
    bool negative(false);

    char const* it(buffer);
    if (*it == '-') {
        negative = true;
        ++it;
    }
    for (; bsl::isdigit(static_cast<unsigned char>(*it)); ++it) {
        significand = significand * 10 + (*it - '0');
    }
    if (*it == '.') {
        ++it;
        for (; bsl::isdigit(static_cast<unsigned char>(*it)); ++it) {
            significand = significand * 10 + (*it - '0');
            --exponent;
        }
    }
    if (*it == 'e' || *it == 'E') {
        ++it;
        exponent += bsl::atoi(it);
    }

    *dfp = DecimalTraits<DECIMAL_TYPE>::make(significand, exponent);

    // Because the significand is a signed integer, it can not represent the
    // value -0, which distinct from +0 in decimal floating point. So instead
    // of converting the significand to a signed value, we change the decimal
    // value based on the sign appropriately after the decimal value is
    // created.

    if (negative) {
        *dfp = -(*dfp);
    }
}

}  // close unnamed namespace

                        // Network format converters

// Note that we do not use platform or bslsl supported converters because they
// work in terms of integers, so they would probably bleed out on the
// strict-aliasing rules.  We may solve that later on using the "union trick"
// and delegating to 'bsls_byteorder', but for now let's take it slow.

                        // Conversion to Network functions

unsigned char *DecimalConvertUtil::decimal32ToNetwork(unsigned char *buffer,
                                                      Decimal32      decimal)
{
    BSLS_ASSERT(buffer != 0);
    return decimalToNetworkT(buffer, decimal);
}

unsigned char *DecimalConvertUtil::decimal64ToNetwork(unsigned char *buffer,
                                                      Decimal64      decimal)
{
    BSLS_ASSERT(buffer != 0);
    return decimalToNetworkT(buffer, decimal);
}

unsigned char *DecimalConvertUtil::decimal128ToNetwork(unsigned char *buffer,
                                                       Decimal128     decimal)
{
    BSLS_ASSERT(buffer != 0);
    return decimalToNetworkT(buffer, decimal);
}

unsigned char *DecimalConvertUtil::decimalToNetwork(unsigned char *buffer,
                                                    Decimal32      decimal)
{
    BSLS_ASSERT(buffer != 0);
    return decimalToNetworkT(buffer, decimal);
}

unsigned char *DecimalConvertUtil::decimalToNetwork(unsigned char *buffer,
                                                    Decimal64      decimal)
{
    BSLS_ASSERT(buffer != 0);
    return decimalToNetworkT(buffer, decimal);
}

unsigned char *DecimalConvertUtil::decimalToNetwork(unsigned char *buffer,
                                                    Decimal128     decimal)
{
    BSLS_ASSERT(buffer != 0);
    return decimalToNetworkT(buffer, decimal);
}

                        // Conversion to Network functions

const unsigned char *DecimalConvertUtil::decimal32FromNetwork(
                                                  Decimal32           *decimal,
                                                  const unsigned char *buffer)
{
    BSLS_ASSERT(decimal != 0);
    return decimalFromNetworkT(decimal, buffer);
}

const unsigned char *DecimalConvertUtil::decimal64FromNetwork(
                                                  Decimal64           *decimal,
                                                  const unsigned char *buffer)
{
    BSLS_ASSERT(decimal != 0);
    return decimalFromNetworkT(decimal, buffer);
}

const unsigned char *DecimalConvertUtil::decimal128FromNetwork(
                                                  Decimal128          *decimal,
                                                  const unsigned char *buffer)
{
    BSLS_ASSERT(decimal != 0);
    return decimalFromNetworkT(decimal, buffer);
}

const unsigned char *DecimalConvertUtil::decimalFromNetwork(
                                                  Decimal32           *decimal,
                                                  const unsigned char *buffer)
{
    BSLS_ASSERT(decimal != 0);
    return decimalFromNetworkT(decimal, buffer);
}

const unsigned char *DecimalConvertUtil::decimalFromNetwork(
                                                  Decimal64           *decimal,
                                                  const unsigned char *buffer)
{
    BSLS_ASSERT(decimal != 0);
    return decimalFromNetworkT(decimal, buffer);
}

const unsigned char *DecimalConvertUtil::decimalFromNetwork(
                                                  Decimal128          *decimal,
                                                  const unsigned char *buffer)
{
    BSLS_ASSERT(decimal != 0);
    return decimalFromNetworkT(decimal, buffer);
}

            // Restore a Decimal Floating-Point from a Binary

                        // DecimalFromDouble functions

Decimal32 DecimalConvertUtil::decimal32FromDouble(double binary)
{
    Decimal32 rv;
    restoreDecimalFromBinary(&rv, binary);
    return rv;
}
Decimal64 DecimalConvertUtil::decimal64FromDouble(double binary)
{
    Decimal64 rv;
    restoreDecimalFromBinary(&rv, binary);
    return rv;
}
Decimal128 DecimalConvertUtil::decimal128FromDouble(double binary)
{
    Decimal128 rv;
    restoreDecimalFromBinary(&rv, binary);
    return rv;
}

                        // DecimalFromFloat functions

Decimal32 DecimalConvertUtil::decimal32FromFloat(float binary)
{
    Decimal32 rv;
    restoreDecimalFromBinary(&rv, binary);
    return rv;
}
Decimal64 DecimalConvertUtil::decimal64FromFloat(float binary)
{
    Decimal64 rv;
    restoreDecimalFromBinary(&rv, binary);
    return rv;
}
Decimal128 DecimalConvertUtil::decimal128FromFloat(float binary)
{
    Decimal128 rv;
    restoreDecimalFromBinary(&rv, binary);
    return rv;
}

}  // close package namespace
}  // close enterprise namespace

// ----------------------------------------------------------------------------
// Copyright 2014 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
<commit_msg>[APPROVAL] INTERNAL pending/master/windows-build-failures-drqs-63540529 to master<commit_after>// bdldfp_decimalconvertutil.cpp                                      -*-C++-*-
#include <bdldfp_decimalconvertutil.h>

#include <bsls_ident.h>
BSLS_IDENT_RCSID(bdldfp_decimalconvertutil_cpp,"$Id$ $CSID$")

#include <bdldfp_decimalplatform.h>

#include <bsls_assert.h>

#ifdef BDLDFP_DECIMALPLATFORM_C99_TR
#  ifndef  __STDC_WANT_DEC_FP__
#    error __STDC_WANT_DEC_FP__ must be defined on the command line!
     char die[-42];     // if '#error' unsupported
#  endif
#endif

#include <bsl_algorithm.h>
#include <bsl_cstdlib.h>
#include <bsl_cstring.h>
#include <ctype.h>

namespace BloombergLP {
namespace bdldfp {

namespace {
                        // Reverse Memory

static void memrev(void *buffer, size_t count)
    // Reverse the order of the first specified 'count' bytes, at the beginning
    // of the specified 'buffer'.  'count % 2' must be zero.
{
    unsigned char *b = static_cast<unsigned char *>(buffer);
    bsl::reverse(b, b + count);
}

                        // Mem copy with reversal functions

unsigned char *memReverseIfNeeded(void *buffer, size_t count)
    // Reverse the first specified 'count' bytes from the specified 'buffer`,
    // if the host endian is different from network endian, and return the
    // address computed from 'static_cast<unsigned char *>(buffer) + count'.
{
#ifdef BDLDFP_DECIMALPLATFORM_LITTLE_ENDIAN
    // little endian, needs to do some byte juggling
    memrev(buffer, count);
#endif
    return static_cast<unsigned char*>(buffer) + count;
}

                        // Decimal-network conversion functions

template <class DECIMAL_TYPE>
const unsigned char *decimalFromNetworkT(DECIMAL_TYPE        *decimal,
                                         const unsigned char *buffer)
    // Construct into the specified 'decimal', the base-10 value represented by
    // the network-ordered bytes in the specified 'buffer', and return a raw
    // memory pointer, providing modifiable access, to one byte past the last
    // byte read from 'buffer'.
{
    bsl::memcpy(decimal, buffer, sizeof(DECIMAL_TYPE));
    memReverseIfNeeded(decimal, sizeof(DECIMAL_TYPE));

    DecimalConvertUtil::decimalFromDPD(
                                   decimal,
                                   reinterpret_cast<unsigned char *>(decimal));

    return buffer + sizeof(DECIMAL_TYPE);
}

template <class DECIMAL_TYPE>
unsigned char *decimalToNetworkT(unsigned char *buffer, DECIMAL_TYPE decimal)
    // Construct into the specified 'buffer', the network-ordered byte
    // representation of the base-10 value of the specified 'decimal', and,
    // return a raw memory pointer, providing modifiable access, to one byte
    // past the last written byte of the 'buffer'.
{
    DecimalConvertUtil::decimalToDPD(buffer, decimal);
    return memReverseIfNeeded(buffer, sizeof(DECIMAL_TYPE));
}

                        // =================
                        // class StdioFormat
                        // =================

template <class FORMATTED_TYPE> struct StdioFormat;
    // This 'struct' template provides a method, 'format', that returns a
    // 'printf'-style format string to format values of the template parameter
    // type 'FORMATTED_TYPE' that can be used to restore a decimal value that
    // was previously converted to this type.

template <>
struct StdioFormat<float> {
    // This template specialization of 'StdioFormat' provides a function that
    // returns a 'printf'-style format string for 'float' values.

    static const char* format();
        // Return a 'printf'-style format string that can be used to restore a
        // decimal value that was previously converted to a 'float' value.
        // Refer to the documentation of 'decimalFromFloat' for the conversion
        // rules.
};

template <>
struct StdioFormat<double> {
    // This template specialization of 'StdioFormat' provides a function that
    // returns a 'printf'-style format string for 'double' values.

    static char const* format();
        // Return a 'printf'-style format string that can be used to restore a
        // decimal value that was previously converted to a 'double' value.
        // Refer to the documentation of 'decimalFromDouble' for the conversion
        // rules.
};

                        // -----------------
                        // class StdioFormat
                        // -----------------

const char* StdioFormat<float>::format()
{
    return "%.6g";
}

const char* StdioFormat<double>::format()
{
    return "%.15g";
}

                        // ===================
                        // class DecimalTraits
                        // ===================

template <class DECIMAL_TYPE> struct DecimalTraits;
    // This 'struct' template provides a way to create an object of the
    // template parameter type 'DECIMAL_TYPE' though a consistent interface.

template <>
struct DecimalTraits<Decimal32> {
    // This template specialization of 'DecimalTraits' provides functions to
    // create 'Decimal32' values.

    typedef int SignificandType;
        // This 'typedef' defines a type that is large enough to hold the
        // significant of 'Decimal32'.

    static Decimal32 make(int significand, int exponent);
        // Return a 'Decimal32' value having the specified 'significand' and
        // the specified 'exponent'.
};

template <>
struct DecimalTraits<Decimal64> {
    // This template specialization of 'DecimalTraits' provides utilities to
    // create 'Decimal64' values.

    typedef long long SignificandType;
        // This 'typedef' defines a type that is large enough to hold the
        // significant of 'Decimal64'.

    static Decimal64 make(long long significand, int exponent);
        // Return a 'Decimal64' value having the specified 'significand' and
        // the specified 'exponent'.

};

template <>
struct DecimalTraits<bdldfp::Decimal128> {
    // This template specialization of 'DecimalTraits' provides utilities to
    // create 'Decimal128' values.

    typedef long long SignificandType;
        // This 'typedef' defines a type that is large enough to hold the
        // significant of 'Decimal128' if it's small enough to be convertible
        // to a double.

    static bdldfp::Decimal128 make(long long significand, int exponent);
        // Return a 'Decimal128' value having the specified 'significand' and
        // the specified 'exponent'.
};

                        // ===================
                        // class DecimalTraits
                        // ===================

Decimal32 DecimalTraits<Decimal32>::make(int significand, int exponent)
{
    return bdldfp::DecimalUtil::makeDecimalRaw32(significand, exponent);
}

Decimal64 DecimalTraits<Decimal64>::make(long long significand, int exponent)
{
    return bdldfp::DecimalUtil::makeDecimalRaw64(significand, exponent);
}

Decimal128 DecimalTraits<Decimal128>::make(long long significand, int exponent)
{
    return bdldfp::DecimalUtil::makeDecimalRaw128(significand, exponent);
}

                  // Helpers for Restoring Decimal from Binary

template <class DECIMAL_TYPE, class BINARY_TYPE>
void restoreDecimalFromBinary(DECIMAL_TYPE *dfp, BINARY_TYPE bfp)
    // Construct, in the specified 'dfp', a decimal floating point
    // representation of the value of the binary floating point value specified
    // by 'bfp'.
{

    if (bfp != bfp) {
        *dfp = bsl::numeric_limits<DECIMAL_TYPE>::quiet_NaN();
        if (bfp < 0) {
            *dfp = -*dfp;
        }
        return;                                                       // RETURN
    }

    if (bfp == bsl::numeric_limits<BINARY_TYPE>::infinity()) {
        *dfp = bsl::numeric_limits<DECIMAL_TYPE>::infinity();
        return;                                                       // RETURN
    }

    if (bfp == -bsl::numeric_limits<BINARY_TYPE>::infinity()) {
        *dfp = -bsl::numeric_limits<DECIMAL_TYPE>::infinity();
        return;                                                       // RETURN
    }

#ifdef BSLS_PLATFORM_OS_WINDOWS
#   define snprintf _snprintf
#endif
    char buffer[48];
    int rc = snprintf(buffer, sizeof(buffer), StdioFormat<BINARY_TYPE>::format(), bfp);
    (void)rc;
    BSLS_ASSERT(0 <= rc && rc < sizeof(buffer));

    typename DecimalTraits<DECIMAL_TYPE>::SignificandType significand(0);
    int  exponent(0);
    bool negative(false);

    char const* it(buffer);
    if (*it == '-') {
        negative = true;
        ++it;
    }
    for (; isdigit(static_cast<unsigned char>(*it)); ++it) {
        significand = significand * 10 + (*it - '0');
    }
    if (*it == '.') {
        ++it;
        for (; isdigit(static_cast<unsigned char>(*it)); ++it) {
            significand = significand * 10 + (*it - '0');
            --exponent;
        }
    }
    if (*it == 'e' || *it == 'E') {
        ++it;
        exponent += bsl::atoi(it);
    }

    *dfp = DecimalTraits<DECIMAL_TYPE>::make(significand, exponent);

    // Because the significand is a signed integer, it can not represent the
    // value -0, which distinct from +0 in decimal floating point. So instead
    // of converting the significand to a signed value, we change the decimal
    // value based on the sign appropriately after the decimal value is
    // created.

    if (negative) {
        *dfp = -(*dfp);
    }
}

}  // close unnamed namespace

                        // Network format converters

// Note that we do not use platform or bslsl supported converters because they
// work in terms of integers, so they would probably bleed out on the
// strict-aliasing rules.  We may solve that later on using the "union trick"
// and delegating to 'bsls_byteorder', but for now let's take it slow.

                        // Conversion to Network functions

unsigned char *DecimalConvertUtil::decimal32ToNetwork(unsigned char *buffer,
                                                      Decimal32      decimal)
{
    BSLS_ASSERT(buffer != 0);
    return decimalToNetworkT(buffer, decimal);
}

unsigned char *DecimalConvertUtil::decimal64ToNetwork(unsigned char *buffer,
                                                      Decimal64      decimal)
{
    BSLS_ASSERT(buffer != 0);
    return decimalToNetworkT(buffer, decimal);
}

unsigned char *DecimalConvertUtil::decimal128ToNetwork(unsigned char *buffer,
                                                       Decimal128     decimal)
{
    BSLS_ASSERT(buffer != 0);
    return decimalToNetworkT(buffer, decimal);
}

unsigned char *DecimalConvertUtil::decimalToNetwork(unsigned char *buffer,
                                                    Decimal32      decimal)
{
    BSLS_ASSERT(buffer != 0);
    return decimalToNetworkT(buffer, decimal);
}

unsigned char *DecimalConvertUtil::decimalToNetwork(unsigned char *buffer,
                                                    Decimal64      decimal)
{
    BSLS_ASSERT(buffer != 0);
    return decimalToNetworkT(buffer, decimal);
}

unsigned char *DecimalConvertUtil::decimalToNetwork(unsigned char *buffer,
                                                    Decimal128     decimal)
{
    BSLS_ASSERT(buffer != 0);
    return decimalToNetworkT(buffer, decimal);
}

                        // Conversion to Network functions

const unsigned char *DecimalConvertUtil::decimal32FromNetwork(
                                                  Decimal32           *decimal,
                                                  const unsigned char *buffer)
{
    BSLS_ASSERT(decimal != 0);
    return decimalFromNetworkT(decimal, buffer);
}

const unsigned char *DecimalConvertUtil::decimal64FromNetwork(
                                                  Decimal64           *decimal,
                                                  const unsigned char *buffer)
{
    BSLS_ASSERT(decimal != 0);
    return decimalFromNetworkT(decimal, buffer);
}

const unsigned char *DecimalConvertUtil::decimal128FromNetwork(
                                                  Decimal128          *decimal,
                                                  const unsigned char *buffer)
{
    BSLS_ASSERT(decimal != 0);
    return decimalFromNetworkT(decimal, buffer);
}

const unsigned char *DecimalConvertUtil::decimalFromNetwork(
                                                  Decimal32           *decimal,
                                                  const unsigned char *buffer)
{
    BSLS_ASSERT(decimal != 0);
    return decimalFromNetworkT(decimal, buffer);
}

const unsigned char *DecimalConvertUtil::decimalFromNetwork(
                                                  Decimal64           *decimal,
                                                  const unsigned char *buffer)
{
    BSLS_ASSERT(decimal != 0);
    return decimalFromNetworkT(decimal, buffer);
}

const unsigned char *DecimalConvertUtil::decimalFromNetwork(
                                                  Decimal128          *decimal,
                                                  const unsigned char *buffer)
{
    BSLS_ASSERT(decimal != 0);
    return decimalFromNetworkT(decimal, buffer);
}

            // Restore a Decimal Floating-Point from a Binary

                        // DecimalFromDouble functions

Decimal32 DecimalConvertUtil::decimal32FromDouble(double binary)
{
    Decimal32 rv;
    restoreDecimalFromBinary(&rv, binary);
    return rv;
}
Decimal64 DecimalConvertUtil::decimal64FromDouble(double binary)
{
    Decimal64 rv;
    restoreDecimalFromBinary(&rv, binary);
    return rv;
}
Decimal128 DecimalConvertUtil::decimal128FromDouble(double binary)
{
    Decimal128 rv;
    restoreDecimalFromBinary(&rv, binary);
    return rv;
}

                        // DecimalFromFloat functions

Decimal32 DecimalConvertUtil::decimal32FromFloat(float binary)
{
    Decimal32 rv;
    restoreDecimalFromBinary(&rv, binary);
    return rv;
}
Decimal64 DecimalConvertUtil::decimal64FromFloat(float binary)
{
    Decimal64 rv;
    restoreDecimalFromBinary(&rv, binary);
    return rv;
}
Decimal128 DecimalConvertUtil::decimal128FromFloat(float binary)
{
    Decimal128 rv;
    restoreDecimalFromBinary(&rv, binary);
    return rv;
}

}  // close package namespace
}  // close enterprise namespace

// ----------------------------------------------------------------------------
// Copyright 2014 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
<|endoftext|>


<commit_before>#include "PsfVm.h"
#include "PsfLoader.h"
#include "MemoryUtils.h"
#include "CoffObjectFile.h"
#include "StdStreamUtils.h"
#include "Jitter.h"
#include "Jitter_CodeGenFactory.h"
#include "Jitter_CodeGen_x86_32.h"
#include "MemStream.h"
#include "Iop_PsfSubSystem.h"
#include "ThreadPool.h"
#include <boost/filesystem.hpp>
#include "Playlist.h"

namespace filesystem = boost::filesystem;

struct FUNCTION_TABLE_ITEM
{
	AOT_BLOCK_KEY	key;
	uint32			symbolIndex;
};
typedef std::vector<FUNCTION_TABLE_ITEM> FunctionTable;

typedef std::map<AOT_BLOCK_KEY, std::vector<uint32>> AotBlockMap;

extern "C" uint32 LWL_Proxy(uint32, uint32, CMIPS*);
extern "C" uint32 LWR_Proxy(uint32, uint32, CMIPS*);
extern "C" void SWL_Proxy(uint32, uint32, CMIPS*);
extern "C" void SWR_Proxy(uint32, uint32, CMIPS*);

void Gather(const char* archivePathName, const char* outputPathName)
{
	Framework::CStdStream outputStream(outputPathName, "wb");
	CBasicBlock::SetAotBlockOutputStream(&outputStream);

	{
		Framework::CThreadPool threadPool(std::thread::hardware_concurrency());

		filesystem::path archivePath = filesystem::path(archivePathName);
		auto archive = std::unique_ptr<CPsfArchive>(CPsfArchive::CreateFromPath(archivePath));

		for(auto fileInfoIterator = archive->GetFilesBegin();
			fileInfoIterator != archive->GetFilesEnd(); fileInfoIterator++)
		{
			filesystem::path archiveItemPath = fileInfoIterator->name;
			filesystem::path archiveItemExtension = archiveItemPath.extension();
			if(CPlaylist::IsLoadableExtension(archiveItemExtension.string().c_str() + 1))
			{
				threadPool.Enqueue(
					[=] ()
					{
						printf("Processing %s...\r\n", archiveItemPath.string().c_str());

						CPsfVm virtualMachine;

						CPsfLoader::LoadPsf(virtualMachine, archiveItemPath, archivePath);
						int currentTime = 0;
						virtualMachine.OnNewFrame.connect(
							[&currentTime] ()
							{
								currentTime += 16;
							});

						virtualMachine.Resume();

#ifdef _DEBUG
						static const unsigned int executionTime = 1;
#else
						static const unsigned int executionTime = 10;
#endif
						while(currentTime <= (executionTime * 60 * 1000))
						{
							std::this_thread::sleep_for(std::chrono::milliseconds(10));
						}

						virtualMachine.Pause();
					}
				);
			}
		}
	}

	CBasicBlock::SetAotBlockOutputStream(nullptr);
}

unsigned int CompileFunction(CPsfVm& virtualMachine, const std::vector<uint32>& blockCode, Jitter::CObjectFile& objectFile, const std::string& functionName, uint32 begin, uint32 end)
{
	auto& context = virtualMachine.GetCpu();

	uint8* ram = virtualMachine.GetRam();
	for(uint32 address = begin; address <= end; address += 4)
	{
		*reinterpret_cast<uint32*>(&ram[address]) = blockCode[(address - begin) / 4];
	}

	{
		Framework::CMemStream outputStream;
		Jitter::CObjectFile::INTERNAL_SYMBOL func;

		static CMipsJitter* jitter = nullptr;
		if(jitter == NULL)
		{
			Jitter::CCodeGen* codeGen = new Jitter::CCodeGen_x86_32();
			codeGen->SetExternalSymbolReferencedHandler(
				[&] (void* symbol, uint32 offset)
				{
					Jitter::CObjectFile::SYMBOL_REFERENCE ref;
					ref.offset		= offset;
					ref.type		= Jitter::CObjectFile::SYMBOL_TYPE_EXTERNAL;
					ref.symbolIndex	= objectFile.GetExternalSymbolIndexByValue(symbol);
					func.symbolReferences.push_back(ref);
				}
			);

			jitter = new CMipsJitter(codeGen);

			for(unsigned int i = 0; i < 4; i++)
			{
				jitter->SetVariableAsConstant(
					offsetof(CMIPS, m_State.nGPR[CMIPS::R0].nV[i]),
					0
					);
			}
		}

		jitter->SetStream(&outputStream);
		jitter->Begin();

		for(uint32 address = begin; address <= end; address += 4)
		{
			context.m_pArch->CompileInstruction(address, jitter, &context);
			//Sanity check
			assert(jitter->IsStackEmpty());
		}

		jitter->End();

		func.name		= functionName;
		func.data		= std::vector<uint8>(outputStream.GetBuffer(), outputStream.GetBuffer() + outputStream.GetSize());
		func.location	= Jitter::CObjectFile::INTERNAL_SYMBOL_LOCATION_TEXT;
		return objectFile.AddInternalSymbol(func);
	}
}

AotBlockMap GetBlocksFromCache(const filesystem::path& blockCachePath)
{
	AotBlockMap result;

	auto path_end = filesystem::directory_iterator();
	for(auto pathIterator = filesystem::directory_iterator(blockCachePath);
		pathIterator != path_end; pathIterator++)
	{
		const auto& filePath = (*pathIterator);
		printf("Processing %s...\r\n", filePath.path().string().c_str());

		auto blockCacheStream = Framework::CreateInputStdStream(filePath.path().native());

		uint32 fileSize = blockCacheStream.GetLength();
		while(fileSize != 0)
		{
			AOT_BLOCK_KEY key = {};
			key.crc		= blockCacheStream.Read32();
			key.begin	= blockCacheStream.Read32();
			key.end		= blockCacheStream.Read32();

			if(key.begin > key.end)
			{
				assert(0);
				throw std::runtime_error("Consistency error in block ranges.");
			}

			uint32 blockSize = (key.end - key.begin) + 4;

			std::vector<uint32> blockCode(blockSize / 4);
			blockCacheStream.Read(blockCode.data(), blockSize);

			auto blockIterator = result.find(key);
			if(blockIterator == std::end(result))
			{
				result.insert(std::make_pair(key, std::move(blockCode)));
			}
			else
			{
				if(!std::equal(std::begin(blockCode), std::end(blockCode), std::begin(blockIterator->second)))
				{
					assert(0);
					throw std::runtime_error("Block with same key already exists but with different data.");
				}
			}

			fileSize -= blockSize + 0x0C;
		}
	}

	return result;
}

void Compile(const char* databasePathName, const char* outputPath)
{
	CPsfVm virtualMachine;
	auto subSystem = std::make_shared<Iop::CPsfSubSystem>(false);
	virtualMachine.SetSubSystem(subSystem);

	auto objectFile = std::unique_ptr<Jitter::CObjectFile>(new Jitter::CCoffObjectFile());
	objectFile->AddExternalSymbol("_MemoryUtils_GetByteProxy", &MemoryUtils_GetByteProxy);
	objectFile->AddExternalSymbol("_MemoryUtils_GetHalfProxy", &MemoryUtils_GetHalfProxy);
	objectFile->AddExternalSymbol("_MemoryUtils_GetWordProxy", &MemoryUtils_GetWordProxy);
	objectFile->AddExternalSymbol("_MemoryUtils_SetByteProxy", &MemoryUtils_SetByteProxy);
	objectFile->AddExternalSymbol("_MemoryUtils_SetHalfProxy", &MemoryUtils_SetHalfProxy);
	objectFile->AddExternalSymbol("_MemoryUtils_SetWordProxy", &MemoryUtils_SetWordProxy);
	objectFile->AddExternalSymbol("_LWL_Proxy", &LWL_Proxy);
	objectFile->AddExternalSymbol("_LWR_Proxy", &LWR_Proxy);
	objectFile->AddExternalSymbol("_SWL_Proxy", &SWL_Proxy);
	objectFile->AddExternalSymbol("_SWR_Proxy", &SWR_Proxy);

	filesystem::path databasePath(databasePathName);
	auto blocks = GetBlocksFromCache(databasePath);

	printf("Got %d blocks to compile.\r\n", blocks.size());

	FunctionTable functionTable;
	functionTable.reserve(blocks.size());

	for(const auto& blockCachePair : blocks)
	{
		const auto& blockKey = blockCachePair.first;

		auto functionName = "aotblock_" + std::to_string(blockKey.crc) + "_" + std::to_string(blockKey.begin) + "_" + std::to_string(blockKey.end);

		unsigned int functionSymbolIndex = CompileFunction(virtualMachine, blockCachePair.second, *objectFile, functionName, blockKey.begin, blockKey.end);

		FUNCTION_TABLE_ITEM tableItem = { blockKey, functionSymbolIndex };
		functionTable.push_back(tableItem);
	}

	std::sort(functionTable.begin(), functionTable.end(),
		[] (const FUNCTION_TABLE_ITEM& item1, const FUNCTION_TABLE_ITEM& item2)
		{
			return item1.key < item2.key;
		}
	);

	{
		Framework::CMemStream blockTableStream;
		Jitter::CObjectFile::INTERNAL_SYMBOL blockTableSymbol;
		blockTableSymbol.name		= "__aot_firstBlock";
		blockTableSymbol.location	= Jitter::CObjectFile::INTERNAL_SYMBOL_LOCATION_DATA;

		for(const auto& functionTableItem : functionTable)
		{
			blockTableStream.Write32(functionTableItem.key.crc);
			blockTableStream.Write32(functionTableItem.key.begin);
			blockTableStream.Write32(functionTableItem.key.end);

			{
				Jitter::CObjectFile::SYMBOL_REFERENCE ref;
				ref.offset		= static_cast<uint32>(blockTableStream.Tell());
				ref.type		= Jitter::CObjectFile::SYMBOL_TYPE_INTERNAL;
				ref.symbolIndex	= functionTableItem.symbolIndex;
				blockTableSymbol.symbolReferences.push_back(ref);
			}

			blockTableStream.Write32(0);
		}

		blockTableSymbol.data = std::vector<uint8>(blockTableStream.GetBuffer(), blockTableStream.GetBuffer() + blockTableStream.GetLength());
		objectFile->AddInternalSymbol(blockTableSymbol);
	}

	{
		Jitter::CObjectFile::INTERNAL_SYMBOL blockCountSymbol;
		blockCountSymbol.name		= "__aot_blockCount";
		blockCountSymbol.location	= Jitter::CObjectFile::INTERNAL_SYMBOL_LOCATION_DATA;
		blockCountSymbol.data		= std::vector<uint8>(4);
		*reinterpret_cast<uint32*>(blockCountSymbol.data.data()) = functionTable.size();
		objectFile->AddInternalSymbol(blockCountSymbol);
	}

	objectFile->Write(Framework::CStdStream(outputPath, "wb"));
}

void PrintUsage()
{
	printf("PsfAot usage:\r\n");
	printf("\tPsfAot gather [InputFile] [DatabasePath]\r\n");
	printf("\tPsfAot compile [DatabasePath] [x86|x64|arm] [coff|macho] [OutputFile]\r\n");
}

int main(int argc, char** argv)
{
	if(argc <= 2)
	{
		PrintUsage();
		return -1;
	}

	if(!strcmp(argv[1], "gather"))
	{
		if(argc < 4)
		{
			PrintUsage();
			return -1;
		}
		else
		{
			Gather(argv[2], argv[3]);
		}
	}
	else if(!strcmp(argv[1], "compile"))
	{
		if(argc < 6)
		{
			PrintUsage();
			return -1;
		}

		try
		{
			Compile(argv[2], argv[5]);
		}
		catch(const std::exception& exception)
		{
			printf("Failed to compile: %s\r\n", exception.what());
			return -1;
		}
	}

	return 0;
}
<commit_msg>PsfPlayer: Updated AOT builder to support ARM platforms and MachO object format.<commit_after>#include "PsfVm.h"
#include "PsfLoader.h"
#include "MemoryUtils.h"
#include "CoffObjectFile.h"
#include "MachoObjectFile.h"
#include "StdStreamUtils.h"
#include "Jitter.h"
#include "Jitter_CodeGen_x86_32.h"
#include "Jitter_CodeGen_Arm.h"
#include "MemStream.h"
#include "Iop_PsfSubSystem.h"
#include "ThreadPool.h"
#include <boost/filesystem.hpp>
#include "Playlist.h"
#include "make_unique.h"

namespace filesystem = boost::filesystem;

struct FUNCTION_TABLE_ITEM
{
	AOT_BLOCK_KEY	key;
	uint32			symbolIndex;
};
typedef std::vector<FUNCTION_TABLE_ITEM> FunctionTable;

typedef std::map<AOT_BLOCK_KEY, std::vector<uint32>> AotBlockMap;

extern "C" uint32 LWL_Proxy(uint32, uint32, CMIPS*);
extern "C" uint32 LWR_Proxy(uint32, uint32, CMIPS*);
extern "C" void SWL_Proxy(uint32, uint32, CMIPS*);
extern "C" void SWR_Proxy(uint32, uint32, CMIPS*);

void Gather(const char* archivePathName, const char* outputPathName)
{
	Framework::CStdStream outputStream(outputPathName, "wb");
	CBasicBlock::SetAotBlockOutputStream(&outputStream);

	{
		Framework::CThreadPool threadPool(std::thread::hardware_concurrency());

		filesystem::path archivePath = filesystem::path(archivePathName);
		auto archive = std::unique_ptr<CPsfArchive>(CPsfArchive::CreateFromPath(archivePath));

		for(const auto& fileInfo : archive->GetFiles())
		{
			filesystem::path archiveItemPath = fileInfo.name;
			filesystem::path archiveItemExtension = archiveItemPath.extension();
			if(CPlaylist::IsLoadableExtension(archiveItemExtension.string().c_str() + 1))
			{
				threadPool.Enqueue(
					[=] ()
					{
						printf("Processing %s...\r\n", archiveItemPath.string().c_str());

						CPsfVm virtualMachine;

						CPsfLoader::LoadPsf(virtualMachine, archiveItemPath, archivePath);
						int currentTime = 0;
						virtualMachine.OnNewFrame.connect(
							[&currentTime] ()
							{
								currentTime += 16;
							});

						virtualMachine.Resume();

#ifdef _DEBUG
						static const unsigned int executionTime = 1;
#else
						static const unsigned int executionTime = 10;
#endif
						while(currentTime <= (executionTime * 60 * 1000))
						{
							std::this_thread::sleep_for(std::chrono::milliseconds(10));
						}

						virtualMachine.Pause();
					}
				);
			}
		}
	}

	CBasicBlock::SetAotBlockOutputStream(nullptr);
}

unsigned int CompileFunction(CPsfVm& virtualMachine, CMipsJitter* jitter, const std::vector<uint32>& blockCode, Jitter::CObjectFile& objectFile, const std::string& functionName, uint32 begin, uint32 end)
{
	auto& context = virtualMachine.GetCpu();

	uint8* ram = virtualMachine.GetRam();
	for(uint32 address = begin; address <= end; address += 4)
	{
		*reinterpret_cast<uint32*>(&ram[address]) = blockCode[(address - begin) / 4];
	}

	{
		Framework::CMemStream outputStream;
		Jitter::CObjectFile::INTERNAL_SYMBOL func;

		jitter->GetCodeGen()->SetExternalSymbolReferencedHandler(
			[&] (void* symbol, uint32 offset)
			{
				Jitter::CObjectFile::SYMBOL_REFERENCE ref;
				ref.offset		= offset;
				ref.type		= Jitter::CObjectFile::SYMBOL_TYPE_EXTERNAL;
				ref.symbolIndex	= objectFile.GetExternalSymbolIndexByValue(symbol);
				func.symbolReferences.push_back(ref);
			}
		);

		jitter->SetStream(&outputStream);
		jitter->Begin();

		for(uint32 address = begin; address <= end; address += 4)
		{
			context.m_pArch->CompileInstruction(address, jitter, &context);
			//Sanity check
			assert(jitter->IsStackEmpty());
		}

		jitter->End();

		func.name		= functionName;
		func.data		= std::vector<uint8>(outputStream.GetBuffer(), outputStream.GetBuffer() + outputStream.GetSize());
		func.location	= Jitter::CObjectFile::INTERNAL_SYMBOL_LOCATION_TEXT;
		return objectFile.AddInternalSymbol(func);
	}
}

AotBlockMap GetBlocksFromCache(const filesystem::path& blockCachePath)
{
	AotBlockMap result;

	auto path_end = filesystem::directory_iterator();
	for(auto pathIterator = filesystem::directory_iterator(blockCachePath);
		pathIterator != path_end; pathIterator++)
	{
		const auto& filePath = (*pathIterator);
		printf("Processing %s...\r\n", filePath.path().string().c_str());

		auto blockCacheStream = Framework::CreateInputStdStream(filePath.path().native());

		uint32 fileSize = blockCacheStream.GetLength();
		while(fileSize != 0)
		{
			AOT_BLOCK_KEY key = {};
			key.crc		= blockCacheStream.Read32();
			key.begin	= blockCacheStream.Read32();
			key.end		= blockCacheStream.Read32();

			if(key.begin > key.end)
			{
				assert(0);
				throw std::runtime_error("Consistency error in block ranges.");
			}

			uint32 blockSize = (key.end - key.begin) + 4;

			std::vector<uint32> blockCode(blockSize / 4);
			blockCacheStream.Read(blockCode.data(), blockSize);

			auto blockIterator = result.find(key);
			if(blockIterator == std::end(result))
			{
				result.insert(std::make_pair(key, std::move(blockCode)));
			}
			else
			{
				if(!std::equal(std::begin(blockCode), std::end(blockCode), std::begin(blockIterator->second)))
				{
					assert(0);
					throw std::runtime_error("Block with same key already exists but with different data.");
				}
			}

			fileSize -= blockSize + 0x0C;
		}
	}

	return result;
}

void Compile(const char* databasePathName, const char* cpuArchName, const char* imageFormatName, const char* outputPath)
{
	CPsfVm virtualMachine;
	auto subSystem = std::make_shared<Iop::CPsfSubSystem>(false);
	virtualMachine.SetSubSystem(subSystem);

	Jitter::CCodeGen* codeGen = nullptr;
	Jitter::CObjectFile::CPU_ARCH cpuArch = Jitter::CObjectFile::CPU_ARCH_X86;
	if(!strcmp(cpuArchName, "x86"))
	{
		codeGen = new Jitter::CCodeGen_x86_32();
		cpuArch = Jitter::CObjectFile::CPU_ARCH_X86;
	}
	else if(!strcmp(cpuArchName, "arm"))
	{
		codeGen = new Jitter::CCodeGen_Arm();
		cpuArch = Jitter::CObjectFile::CPU_ARCH_ARM;
	}
	else
	{
		throw std::runtime_error("Invalid cpu target.");
	}

	std::unique_ptr<Jitter::CObjectFile> objectFile;
	if(!strcmp(imageFormatName, "coff"))
	{
		objectFile = std::make_unique<Jitter::CCoffObjectFile>(cpuArch);
	}
	else if(!strcmp(imageFormatName, "macho"))
	{
		objectFile = std::make_unique<Jitter::CMachoObjectFile>(cpuArch);
	}
	else
	{
		throw std::runtime_error("Invalid executable image type (must be coff or macho).");
	}

	codeGen->RegisterExternalSymbols(objectFile.get());
	objectFile->AddExternalSymbol("_MemoryUtils_GetByteProxy", &MemoryUtils_GetByteProxy);
	objectFile->AddExternalSymbol("_MemoryUtils_GetHalfProxy", &MemoryUtils_GetHalfProxy);
	objectFile->AddExternalSymbol("_MemoryUtils_GetWordProxy", &MemoryUtils_GetWordProxy);
	objectFile->AddExternalSymbol("_MemoryUtils_SetByteProxy", &MemoryUtils_SetByteProxy);
	objectFile->AddExternalSymbol("_MemoryUtils_SetHalfProxy", &MemoryUtils_SetHalfProxy);
	objectFile->AddExternalSymbol("_MemoryUtils_SetWordProxy", &MemoryUtils_SetWordProxy);
	objectFile->AddExternalSymbol("_LWL_Proxy", &LWL_Proxy);
	objectFile->AddExternalSymbol("_LWR_Proxy", &LWR_Proxy);
	objectFile->AddExternalSymbol("_SWL_Proxy", &SWL_Proxy);
	objectFile->AddExternalSymbol("_SWR_Proxy", &SWR_Proxy);

	filesystem::path databasePath(databasePathName);
	auto blocks = GetBlocksFromCache(databasePath);

	//Initialize Jitter Service
	auto jitter = new CMipsJitter(codeGen);
	for(unsigned int i = 0; i < 4; i++)
	{
		jitter->SetVariableAsConstant(
			offsetof(CMIPS, m_State.nGPR[CMIPS::R0].nV[i]),
			0
			);
	}

	printf("Got %d blocks to compile.\r\n", blocks.size());

	FunctionTable functionTable;
	functionTable.reserve(blocks.size());

	for(const auto& blockCachePair : blocks)
	{
		const auto& blockKey = blockCachePair.first;

		auto functionName = "aotblock_" + std::to_string(blockKey.crc) + "_" + std::to_string(blockKey.begin) + "_" + std::to_string(blockKey.end);

		unsigned int functionSymbolIndex = CompileFunction(virtualMachine, jitter, blockCachePair.second, *objectFile, functionName, blockKey.begin, blockKey.end);

		FUNCTION_TABLE_ITEM tableItem = { blockKey, functionSymbolIndex };
		functionTable.push_back(tableItem);
	}

	std::sort(functionTable.begin(), functionTable.end(),
		[] (const FUNCTION_TABLE_ITEM& item1, const FUNCTION_TABLE_ITEM& item2)
		{
			return item1.key < item2.key;
		}
	);

	{
		Framework::CMemStream blockTableStream;
		Jitter::CObjectFile::INTERNAL_SYMBOL blockTableSymbol;
		blockTableSymbol.name		= "__aot_firstBlock";
		blockTableSymbol.location	= Jitter::CObjectFile::INTERNAL_SYMBOL_LOCATION_DATA;

		for(const auto& functionTableItem : functionTable)
		{
			blockTableStream.Write32(functionTableItem.key.crc);
			blockTableStream.Write32(functionTableItem.key.begin);
			blockTableStream.Write32(functionTableItem.key.end);

			{
				Jitter::CObjectFile::SYMBOL_REFERENCE ref;
				ref.offset		= static_cast<uint32>(blockTableStream.Tell());
				ref.type		= Jitter::CObjectFile::SYMBOL_TYPE_INTERNAL;
				ref.symbolIndex	= functionTableItem.symbolIndex;
				blockTableSymbol.symbolReferences.push_back(ref);
			}

			blockTableStream.Write32(0);
		}

		blockTableSymbol.data = std::vector<uint8>(blockTableStream.GetBuffer(), blockTableStream.GetBuffer() + blockTableStream.GetLength());
		objectFile->AddInternalSymbol(blockTableSymbol);
	}

	{
		Jitter::CObjectFile::INTERNAL_SYMBOL blockCountSymbol;
		blockCountSymbol.name		= "__aot_blockCount";
		blockCountSymbol.location	= Jitter::CObjectFile::INTERNAL_SYMBOL_LOCATION_DATA;
		blockCountSymbol.data		= std::vector<uint8>(4);
		*reinterpret_cast<uint32*>(blockCountSymbol.data.data()) = functionTable.size();
		objectFile->AddInternalSymbol(blockCountSymbol);
	}

	objectFile->Write(Framework::CStdStream(outputPath, "wb"));
}

void PrintUsage()
{
	printf("PsfAot usage:\r\n");
	printf("\tPsfAot gather [InputFile] [DatabasePath]\r\n");
	printf("\tPsfAot compile [DatabasePath] [x86|x64|arm] [coff|macho] [OutputFile]\r\n");
}

int main(int argc, char** argv)
{
	if(argc <= 2)
	{
		PrintUsage();
		return -1;
	}

	if(!strcmp(argv[1], "gather"))
	{
		if(argc < 4)
		{
			PrintUsage();
			return -1;
		}
		else
		{
			Gather(argv[2], argv[3]);
		}
	}
	else if(!strcmp(argv[1], "compile"))
	{
		if(argc < 6)
		{
			PrintUsage();
			return -1;
		}

		try
		{
			const char* databasePath = argv[2];
			const char* cpuArchName = argv[3];
			const char* imageFormatName = argv[4];
			const char* outputPath = argv[5];
			Compile(databasePath, cpuArchName, imageFormatName, outputPath);
		}
		catch(const std::exception& exception)
		{
			printf("Failed to compile: %s\r\n", exception.what());
			return -1;
		}
	}

	return 0;
}
<|endoftext|>


<commit_before>// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#include "test_precomp.hpp"
#include "test_intrin_utils.hpp"

namespace opencv_test { namespace hal {
CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN

void test_hal_intrin_float16()
{
    TheTest<v_float16x8>()
        .test_loadstore_fp16()
        .test_float_cvt_fp16()
        ;
}

CV_CPU_OPTIMIZATION_NAMESPACE_END
}} // namespace
<commit_msg>core:test Fix fp16 build if AVX2 sets as baseline<commit_after>// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#include "test_precomp.hpp"
#include "test_intrin_utils.hpp"

namespace opencv_test { namespace hal {
CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN

void test_hal_intrin_float16()
{
    TheTest<v_float16>()
        .test_loadstore_fp16()
        .test_float_cvt_fp16()
        ;
}

CV_CPU_OPTIMIZATION_NAMESPACE_END
}} // namespace
<|endoftext|>


<commit_before>//
// C++ Implementation: sdlplayer
//
// Description:
//
//
// Author: FThauer FHammer <webmaster@pokerth.net>, (C) 2007
//
// Copyright: See COPYING file that comes with this distribution
//
//
#include "sdlplayer.h"

#include <iostream>

using namespace std;

SDLPlayer::SDLPlayer(ConfigFile *c)
: soundData(NULL), currentChannel(0) , audioEnabled(0), myConfig(c)
{
	SDL_Init(SDL_INIT_AUDIO);
	initAudio();
}

SDLPlayer::~SDLPlayer()
{
	closeAudio();
	SDL_Quit();
}

void SDLPlayer::initAudio() {

	if (!audioEnabled && myConfig->readConfigInt("PlaySoundEffects"))
	{
		audio_rate = 44100;
		audio_format = AUDIO_S16; /* 16-bit stereo */
		audio_channels = 2;
		audio_buffers = 4096;
		sound = NULL;

		if(Mix_OpenAudio(audio_rate, audio_format, audio_channels, audio_buffers) == 0) {
			Mix_QuerySpec(&audio_rate, &audio_format, &audio_channels);
			audioEnabled = 1;
		}
	}
}

void SDLPlayer::playSound(string audioString, int playerID) {

	if(audioEnabled && myConfig->readConfigInt("PlaySoundEffects")) {

		QFile myFile(":sounds/resources/sounds/"+QString::fromStdString(audioString)+".wav");

		if(myFile.open(QIODevice::ReadOnly)) {

			//set 3d position for player
			int position = 0;
			int distance = 0;

			switch (playerID) {

			case 0: { position = 180; distance = 10; }
			break;
			case 1: { position = 281; distance = 30; }
			break;
			case 2: { position = 315; distance = 90; }
			break;
			case 3: { position = 338; distance = 130; }
			break;
			case 4: { position = 23; distance = 130; }
			break;
			case 5: { position = 45; distance = 90; }
			break;
			case 6: { position = 79; distance = 30; }
			break;
			default: { position = 0; distance = 0; }
			break;
			}

			audioDone();

			QDataStream in(&myFile);
			soundData = new Uint8[(int)myFile.size()];
			in.readRawData( (char*)soundData, (int)myFile.size() );

			sound = Mix_QuickLoad_WAV(soundData);

  			// set channel 0 to settings volume
			Mix_Volume(0,myConfig->readConfigInt("SoundVolume")*10);

			// set 3d effect
			if(!Mix_SetPosition(0, position, distance)) {
    				printf("Mix_SetPosition: %s\n", Mix_GetError());
    				// no position effect, is it ok?
			}
			currentChannel = Mix_PlayChannel(-1, sound,0);
		}
	// 	else cout << "could not load " << audioString << ".wav" << endl;

		//test
	//	audioDone();
	//	sound = Mix_LoadWAV( QString(QString::fromStdString(audioString)+QString(".wav")).toStdString().c_str() );
	//	currentChannel = Mix_PlayChannel(-1, sound,0);

	}
}

void SDLPlayer::audioDone() {

	if(audioEnabled) {
		Mix_HaltChannel(currentChannel);
		Mix_FreeChunk(sound);
		sound = NULL;
		delete[] soundData;
		soundData = NULL;
	}
}

void SDLPlayer::closeAudio() {

	if(audioEnabled) {
		audioDone();
		Mix_CloseAudio();
		audioEnabled = false;
	}
}
<commit_msg>wider 3d sound<commit_after>//
// C++ Implementation: sdlplayer
//
// Description:
//
//
// Author: FThauer FHammer <webmaster@pokerth.net>, (C) 2007
//
// Copyright: See COPYING file that comes with this distribution
//
//
#include "sdlplayer.h"

#include <iostream>

using namespace std;

SDLPlayer::SDLPlayer(ConfigFile *c)
: soundData(NULL), currentChannel(0) , audioEnabled(0), myConfig(c)
{
	SDL_Init(SDL_INIT_AUDIO);
	initAudio();
}

SDLPlayer::~SDLPlayer()
{
	closeAudio();
	SDL_Quit();
}

void SDLPlayer::initAudio() {

	if (!audioEnabled && myConfig->readConfigInt("PlaySoundEffects"))
	{
		audio_rate = 44100;
		audio_format = AUDIO_S16; /* 16-bit stereo */
		audio_channels = 2;
		audio_buffers = 4096;
		sound = NULL;

		if(Mix_OpenAudio(audio_rate, audio_format, audio_channels, audio_buffers) == 0) {
			Mix_QuerySpec(&audio_rate, &audio_format, &audio_channels);
			audioEnabled = 1;
		}
	}
}

void SDLPlayer::playSound(string audioString, int playerID) {

	if(audioEnabled && myConfig->readConfigInt("PlaySoundEffects")) {

		QFile myFile(":sounds/resources/sounds/"+QString::fromStdString(audioString)+".wav");

		if(myFile.open(QIODevice::ReadOnly)) {

			//set 3d position for player
			int position = 0;
			int distance = 0;

			switch (playerID) {

			case 0: { position = 180; distance = 10; }
			break;
			case 1: { position = 281; distance = 50; }
			break;
			case 2: { position = 315; distance = 120; }
			break;
			case 3: { position = 338; distance = 160; }
			break;
			case 4: { position = 23; distance = 160; }
			break;
			case 5: { position = 45; distance = 120; }
			break;
			case 6: { position = 79; distance = 50; }
			break;
			default: { position = 0; distance = 0; }
			break;
			}

			audioDone();

			QDataStream in(&myFile);
			soundData = new Uint8[(int)myFile.size()];
			in.readRawData( (char*)soundData, (int)myFile.size() );

			sound = Mix_QuickLoad_WAV(soundData);

  			// set channel 0 to settings volume
			Mix_Volume(0,myConfig->readConfigInt("SoundVolume")*10);

			// set 3d effect
			if(!Mix_SetPosition(0, position, distance)) {
    				printf("Mix_SetPosition: %s\n", Mix_GetError());
    				// no position effect, is it ok?
			}
			currentChannel = Mix_PlayChannel(-1, sound,0);
		}
	// 	else cout << "could not load " << audioString << ".wav" << endl;

		//test
	//	audioDone();
	//	sound = Mix_LoadWAV( QString(QString::fromStdString(audioString)+QString(".wav")).toStdString().c_str() );
	//	currentChannel = Mix_PlayChannel(-1, sound,0);

	}
}

void SDLPlayer::audioDone() {

	if(audioEnabled) {
		Mix_HaltChannel(currentChannel);
		Mix_FreeChunk(sound);
		sound = NULL;
		delete[] soundData;
		soundData = NULL;
	}
}

void SDLPlayer::closeAudio() {

	if(audioEnabled) {
		audioDone();
		Mix_CloseAudio();
		audioEnabled = false;
	}
}
<|endoftext|>


<commit_before>/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
#include "gm.h"
#include "SkSurface.h"
#include "SkTypeface.h"

namespace skiagm {

class DFTextGM : public GM {
public:
    DFTextGM() {
        this->setBGColor(0xFFFFFFFF);
    }

    virtual ~DFTextGM() {
    }

protected:
    virtual uint32_t onGetFlags() const SK_OVERRIDE {
        return kGPUOnly_Flag;
    }

    virtual SkString onShortName() {
        return SkString("dftext");
    }

    virtual SkISize onISize() {
        return SkISize::Make(1024, 768);
    }

    static void rotate_about(SkCanvas* canvas,
        SkScalar degrees,
        SkScalar px, SkScalar py) {
        canvas->translate(px, py);
        canvas->rotate(degrees);
        canvas->translate(-px, -py);
    }

    virtual void onDraw(SkCanvas* inputCanvas) {
        SkScalar textSizes[] = { 11.0f, 11.0f*2.0f, 11.0f*5.0f, 11.0f*2.0f*5.0f };
        SkScalar scales[] = { 2.0f*5.0f, 5.0f, 2.0f, 1.0f };

        // set up offscreen rendering with distance field text
#if SK_SUPPORT_GPU
        GrContext* ctx = inputCanvas->getGrContext();
        SkImageInfo info = SkImageInfo::MakeN32Premul(onISize());
        SkSurfaceProps props(SkSurfaceProps::kUseDistanceFieldFonts_Flag,
                             SkSurfaceProps::kLegacyFontHost_InitType);
        SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTarget(ctx, info, 0, &props));
        SkCanvas* canvas = surface.get() ? surface->getCanvas() : inputCanvas;
#else
        SkCanvas* canvas = inputCanvas;
#endif

        // apply global scale to test glyph positioning
        canvas->scale(1.05f, 1.05f);
        canvas->clear(0xffffffff);

        SkPaint paint;
        paint.setAntiAlias(true);
        paint.setSubpixelText(true);
#if !SK_SUPPORT_GPU
        paint.setDistanceFieldTextTEMP(true);
#endif
        sk_tool_utils::set_portable_typeface(&paint, "Times New Roman", SkTypeface::kNormal);

        const char* text = "Hamburgefons";
        const size_t textLen = strlen(text);

        // check scaling up
        SkScalar x = SkIntToScalar(0);
        SkScalar y = SkIntToScalar(78);
        for (size_t i = 0; i < SK_ARRAY_COUNT(textSizes); ++i) {
            SkAutoCanvasRestore acr(canvas, true);
            canvas->translate(x, y);
            canvas->scale(scales[i], scales[i]);
            paint.setTextSize(textSizes[i]);
            canvas->drawText(text, textLen, 0, 0, paint);
            y += paint.getFontMetrics(NULL)*scales[i];
        }

        // check rotation
        for (size_t i = 0; i < 5; ++i) {
            SkScalar rotX = SkIntToScalar(10);
            SkScalar rotY = y;

            SkAutoCanvasRestore acr(canvas, true);
            canvas->translate(SkIntToScalar(10 + i * 200), -80);
            rotate_about(canvas, SkIntToScalar(i * 5), rotX, rotY);
            for (int ps = 6; ps <= 32; ps += 3) {
                paint.setTextSize(SkIntToScalar(ps));
                canvas->drawText(text, textLen, rotX, rotY, paint);
                rotY += paint.getFontMetrics(NULL);
            }
        }

        // check scaling down
        paint.setLCDRenderText(true);
        x = SkIntToScalar(680);
        y = SkIntToScalar(20);
        size_t arraySize = SK_ARRAY_COUNT(textSizes);
        for (size_t i = 0; i < arraySize; ++i) {
            SkAutoCanvasRestore acr(canvas, true);
            canvas->translate(x, y);
            SkScalar scaleFactor = SkScalarInvert(scales[arraySize - i - 1]);
            canvas->scale(scaleFactor, scaleFactor);
            paint.setTextSize(textSizes[i]);
            canvas->drawText(text, textLen, 0, 0, paint);
            y += paint.getFontMetrics(NULL)*scaleFactor;
        }

        // check pos text
        {
            SkAutoCanvasRestore acr(canvas, true);

            canvas->scale(2.0f, 2.0f);

            SkAutoTArray<SkPoint>  pos(textLen);
            SkAutoTArray<SkScalar> widths(textLen);
            paint.setTextSize(textSizes[0]);

            paint.getTextWidths(text, textLen, &widths[0]);

            SkScalar x = SkIntToScalar(340);
            SkScalar y = SkIntToScalar(75);
            for (unsigned int i = 0; i < textLen; ++i) {
                pos[i].set(x, y);
                x += widths[i];
            }

            canvas->drawPosText(text, textLen, &pos[0], paint);
        }

        // check gamma-corrected blending
        const SkColor fg[] = {
            0xFFFFFFFF,
            0xFFFFFF00, 0xFFFF00FF, 0xFF00FFFF,
            0xFFFF0000, 0xFF00FF00, 0xFF0000FF,
            0xFF000000,
        };

        paint.setColor(0xFFF1F1F1);
        SkRect r = SkRect::MakeLTRB(670, 250, 820, 460);
        canvas->drawRect(r, paint);

        x = SkIntToScalar(680);
        y = SkIntToScalar(270);
        paint.setTextSize(SkIntToScalar(22));
        for (size_t i = 0; i < SK_ARRAY_COUNT(fg); ++i) {
            paint.setColor(fg[i]);

            canvas->drawText(text, textLen, x, y, paint);
            y += paint.getFontMetrics(NULL);
        }

        paint.setColor(0xFF1F1F1F);
        r = SkRect::MakeLTRB(820, 250, 970, 460);
        canvas->drawRect(r, paint);

        x = SkIntToScalar(830);
        y = SkIntToScalar(270);
        paint.setTextSize(SkIntToScalar(22));
        for (size_t i = 0; i < SK_ARRAY_COUNT(fg); ++i) {
            paint.setColor(fg[i]);

            canvas->drawText(text, textLen, x, y, paint);
            y += paint.getFontMetrics(NULL);
        }

#if SK_SUPPORT_GPU
        // render offscreen buffer
        if (surface) {
            SkImage* image = surface->newImageSnapshot();
            inputCanvas->drawImage(image, 0, 0, NULL);
            image->unref();
        }
#endif
    }

private:
    typedef GM INHERITED;
};

//////////////////////////////////////////////////////////////////////////////

static GM* MyFactory(void*) { return new DFTextGM; }
static GMRegistry reg(MyFactory);

}
<commit_msg>Reduce sizes in dftext GM on Android to match desktop better.<commit_after>/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
#include "gm.h"
#include "SkSurface.h"
#include "SkTypeface.h"

namespace skiagm {

class DFTextGM : public GM {
public:
    DFTextGM() {
        this->setBGColor(0xFFFFFFFF);
    }

    virtual ~DFTextGM() {
    }

protected:
    virtual uint32_t onGetFlags() const SK_OVERRIDE {
        return kGPUOnly_Flag;
    }

    virtual SkString onShortName() {
        return SkString("dftext");
    }

    virtual SkISize onISize() {
        return SkISize::Make(1024, 768);
    }

    static void rotate_about(SkCanvas* canvas,
        SkScalar degrees,
        SkScalar px, SkScalar py) {
        canvas->translate(px, py);
        canvas->rotate(degrees);
        canvas->translate(-px, -py);
    }

    virtual void onDraw(SkCanvas* inputCanvas) {
#if SK_BUILD_FOR_ANDROID
        SkScalar textSizes[] = { 9.0f, 9.0f*2.0f, 9.0f*5.0f, 9.0f*2.0f*5.0f };
#else
        SkScalar textSizes[] = { 11.0f, 11.0f*2.0f, 11.0f*5.0f, 11.0f*2.0f*5.0f };
#endif
        SkScalar scales[] = { 2.0f*5.0f, 5.0f, 2.0f, 1.0f };

        // set up offscreen rendering with distance field text
#if SK_SUPPORT_GPU
        GrContext* ctx = inputCanvas->getGrContext();
        SkImageInfo info = SkImageInfo::MakeN32Premul(onISize());
        SkSurfaceProps props(SkSurfaceProps::kUseDistanceFieldFonts_Flag,
                             SkSurfaceProps::kLegacyFontHost_InitType);
        SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTarget(ctx, info, 0, &props));
        SkCanvas* canvas = surface.get() ? surface->getCanvas() : inputCanvas;
#else
        SkCanvas* canvas = inputCanvas;
#endif

        // apply global scale to test glyph positioning
        canvas->scale(1.05f, 1.05f);
        canvas->clear(0xffffffff);

        SkPaint paint;
        paint.setAntiAlias(true);
        paint.setSubpixelText(true);
#if !SK_SUPPORT_GPU
        paint.setDistanceFieldTextTEMP(true);
#endif
        sk_tool_utils::set_portable_typeface(&paint, "Times New Roman", SkTypeface::kNormal);

        const char* text = "Hamburgefons";
        const size_t textLen = strlen(text);

        // check scaling up
        SkScalar x = SkIntToScalar(0);
        SkScalar y = SkIntToScalar(78);
        for (size_t i = 0; i < SK_ARRAY_COUNT(textSizes); ++i) {
            SkAutoCanvasRestore acr(canvas, true);
            canvas->translate(x, y);
            canvas->scale(scales[i], scales[i]);
            paint.setTextSize(textSizes[i]);
            canvas->drawText(text, textLen, 0, 0, paint);
            y += paint.getFontMetrics(NULL)*scales[i];
        }

        // check rotation
        for (size_t i = 0; i < 5; ++i) {
            SkScalar rotX = SkIntToScalar(10);
            SkScalar rotY = y;

            SkAutoCanvasRestore acr(canvas, true);
            canvas->translate(SkIntToScalar(10 + i * 200), -80);
            rotate_about(canvas, SkIntToScalar(i * 5), rotX, rotY);
            for (int ps = 6; ps <= 32; ps += 3) {
                paint.setTextSize(SkIntToScalar(ps));
                canvas->drawText(text, textLen, rotX, rotY, paint);
                rotY += paint.getFontMetrics(NULL);
            }
        }

        // check scaling down
        paint.setLCDRenderText(true);
        x = SkIntToScalar(680);
        y = SkIntToScalar(20);
        size_t arraySize = SK_ARRAY_COUNT(textSizes);
        for (size_t i = 0; i < arraySize; ++i) {
            SkAutoCanvasRestore acr(canvas, true);
            canvas->translate(x, y);
            SkScalar scaleFactor = SkScalarInvert(scales[arraySize - i - 1]);
            canvas->scale(scaleFactor, scaleFactor);
            paint.setTextSize(textSizes[i]);
            canvas->drawText(text, textLen, 0, 0, paint);
            y += paint.getFontMetrics(NULL)*scaleFactor;
        }

        // check pos text
        {
            SkAutoCanvasRestore acr(canvas, true);

            canvas->scale(2.0f, 2.0f);

            SkAutoTArray<SkPoint>  pos(textLen);
            SkAutoTArray<SkScalar> widths(textLen);
            paint.setTextSize(textSizes[0]);

            paint.getTextWidths(text, textLen, &widths[0]);

            SkScalar x = SkIntToScalar(340);
            SkScalar y = SkIntToScalar(75);
            for (unsigned int i = 0; i < textLen; ++i) {
                pos[i].set(x, y);
                x += widths[i];
            }

            canvas->drawPosText(text, textLen, &pos[0], paint);
        }

        // check gamma-corrected blending
        const SkColor fg[] = {
            0xFFFFFFFF,
            0xFFFFFF00, 0xFFFF00FF, 0xFF00FFFF,
            0xFFFF0000, 0xFF00FF00, 0xFF0000FF,
            0xFF000000,
        };

        paint.setColor(0xFFF1F1F1);
        SkRect r = SkRect::MakeLTRB(670, 250, 820, 460);
        canvas->drawRect(r, paint);

        x = SkIntToScalar(680);
        y = SkIntToScalar(270);
#if SK_BUILD_FOR_ANDROID
        paint.setTextSize(SkIntToScalar(19));
#else
        paint.setTextSize(SkIntToScalar(22));
#endif
        for (size_t i = 0; i < SK_ARRAY_COUNT(fg); ++i) {
            paint.setColor(fg[i]);

            canvas->drawText(text, textLen, x, y, paint);
            y += paint.getFontMetrics(NULL);
        }

        paint.setColor(0xFF1F1F1F);
        r = SkRect::MakeLTRB(820, 250, 970, 460);
        canvas->drawRect(r, paint);

        x = SkIntToScalar(830);
        y = SkIntToScalar(270);
#if SK_BUILD_FOR_ANDROID
        paint.setTextSize(SkIntToScalar(19));
#else
        paint.setTextSize(SkIntToScalar(22));
#endif
        for (size_t i = 0; i < SK_ARRAY_COUNT(fg); ++i) {
            paint.setColor(fg[i]);

            canvas->drawText(text, textLen, x, y, paint);
            y += paint.getFontMetrics(NULL);
        }

#if SK_SUPPORT_GPU
        // render offscreen buffer
        if (surface) {
            SkImage* image = surface->newImageSnapshot();
            inputCanvas->drawImage(image, 0, 0, NULL);
            image->unref();
        }
#endif
    }

private:
    typedef GM INHERITED;
};

//////////////////////////////////////////////////////////////////////////////

static GM* MyFactory(void*) { return new DFTextGM; }
static GMRegistry reg(MyFactory);

}
<|endoftext|>