<commit_before>// Copyright 2021 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
//
//     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.
//
///////////////////////////////////////////////////////////////////////////////

#include "jwt_impl.h"

#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "tink/binary_keyset_writer.h"
#include "tink/cleartext_keyset_handle.h"
#include "tink/jwt/jwt_mac_config.h"
#include "tink/jwt/jwt_signature_config.h"
#include "tink/jwt/jwt_key_templates.h"
#include "proto/testing/testing_api.grpc.pb.h"
#include "tink/util/test_matchers.h"

namespace crypto {
namespace tink {
namespace {

using ::crypto::tink::BinaryKeysetWriter;
using ::crypto::tink::CleartextKeysetHandle;
using ::crypto::tink::KeysetHandle;
using ::crypto::tink::test::IsOk;
using ::google::crypto::tink::KeyTemplate;
using ::proto2::Map;
using ::testing::Eq;
using ::testing::IsEmpty;
using ::testing::ElementsAre;
using ::tink_testing_api::JwtClaimValue;
using ::tink_testing_api::JwtSignRequest;
using ::tink_testing_api::JwtSignResponse;
using ::tink_testing_api::JwtToken;
using ::tink_testing_api::JwtValidator;
using ::tink_testing_api::JwtVerifyRequest;
using ::tink_testing_api::JwtVerifyResponse;

std::string ValidKeyset() {
  const KeyTemplate& key_template = ::crypto::tink::JwtHs256Template();
  util::StatusOr<std::unique_ptr<KeysetHandle>> handle =
      KeysetHandle::GenerateNew(key_template);
  EXPECT_THAT(handle.status(), IsOk());

  std::stringbuf keyset;
  util::StatusOr<std::unique_ptr<BinaryKeysetWriter>> writer =
      BinaryKeysetWriter::New(absl::make_unique<std::ostream>(&keyset));
  EXPECT_THAT(writer.status(), IsOk());

  util::Status status = CleartextKeysetHandle::Write((*writer).get(), **handle);
  EXPECT_THAT(status, IsOk());
  return keyset.str();
}

class JwtImplMacTest : public ::testing::Test {
 protected:
  static void SetUpTestSuite() { ASSERT_THAT(JwtMacRegister(), IsOk()); }
};

TEST_F(JwtImplMacTest, MacComputeVerifySuccess) {
  tink_testing_api::JwtImpl jwt;
  std::string keyset = ValidKeyset();
  JwtSignRequest comp_request;
  comp_request.set_keyset(keyset);
  JwtToken* raw_jwt = comp_request.mutable_raw_jwt();
  raw_jwt->mutable_type_header()->set_value("type_header");
  raw_jwt->mutable_issuer()->set_value("issuer");
  raw_jwt->mutable_subject()->set_value("subject");
  raw_jwt->mutable_audiences()->Add("audience1");
  raw_jwt->mutable_audiences()->Add("audience2");
  raw_jwt->mutable_jwt_id()->set_value("jwt_id");
  raw_jwt->mutable_not_before()->set_seconds(12345);
  raw_jwt->mutable_not_before()->set_nanos(123000000);
  raw_jwt->mutable_issued_at()->set_seconds(23456);
  raw_jwt->mutable_expiration()->set_seconds(34567);
  Map<std::string, JwtClaimValue>* custom_claims =
      raw_jwt->mutable_custom_claims();
  (*custom_claims)["null_claim"].set_null_value(
      tink_testing_api::NullValue::NULL_VALUE);
  (*custom_claims)["bool_claim"].set_bool_value(true);
  (*custom_claims)["number_claim"].set_number_value(123.456);
  (*custom_claims)["string_claim"].set_string_value("string_value");
  JwtSignResponse comp_response;
  EXPECT_TRUE(
      jwt.ComputeMacAndEncode(nullptr, &comp_request, &comp_response).ok());
  EXPECT_THAT(comp_response.err(), IsEmpty());

  JwtVerifyRequest verify_request;
  verify_request.set_keyset(keyset);
  verify_request.set_signed_compact_jwt(comp_response.signed_compact_jwt());
  JwtValidator* validator = verify_request.mutable_validator();
  validator->mutable_expected_type_header()->set_value("type_header");
  validator->mutable_expected_issuer()->set_value("issuer");
  validator->mutable_expected_audience()->set_value("audience2");
  validator->mutable_now()->set_seconds(23456);
  JwtVerifyResponse verify_response;

  ASSERT_TRUE(
      jwt.VerifyMacAndDecode(nullptr, &verify_request, &verify_response).ok());
  ASSERT_THAT(verify_response.err(), IsEmpty());
  const JwtToken& verified_jwt = verify_response.verified_jwt();
  EXPECT_EQ(verified_jwt.type_header().value(), "type_header");
  EXPECT_THAT(verified_jwt.issuer().value(), Eq("issuer"));
  EXPECT_THAT(verified_jwt.subject().value(), Eq("subject"));
  EXPECT_THAT(verified_jwt.audiences_size(), Eq(2));
  EXPECT_THAT(verified_jwt.audiences(0), Eq("audience1"));
  EXPECT_THAT(verified_jwt.audiences(1), Eq("audience2"));
  EXPECT_THAT(verified_jwt.jwt_id().value(), Eq("jwt_id"));
  EXPECT_THAT(verified_jwt.not_before().seconds(), Eq(12345));
  EXPECT_THAT(verified_jwt.not_before().nanos(), Eq(0));
  EXPECT_THAT(verified_jwt.issued_at().seconds(), Eq(23456));
  EXPECT_THAT(verified_jwt.issued_at().nanos(), Eq(0));
  EXPECT_THAT(verified_jwt.expiration().seconds(), Eq(34567));
  EXPECT_THAT(verified_jwt.expiration().nanos(), Eq(0));
  auto verified_custom_claims = verified_jwt.custom_claims();
  EXPECT_THAT(verified_custom_claims["null_claim"].null_value(),
              Eq(tink_testing_api::NullValue::NULL_VALUE));
  EXPECT_THAT(verified_custom_claims["bool_claim"].bool_value(), Eq(true));
  EXPECT_THAT(verified_custom_claims["number_claim"].number_value(),
              Eq(123.456));
  EXPECT_THAT(verified_custom_claims["string_claim"].string_value(),
              Eq("string_value"));
}

TEST_F(JwtImplMacTest, ComputeBadKeysetFail) {
  tink_testing_api::JwtImpl jwt;
  JwtSignRequest comp_request;
  comp_request.set_keyset("bad keyset");
  comp_request.mutable_raw_jwt()->mutable_issuer()->set_value("issuer");
  JwtSignResponse comp_response;

  EXPECT_TRUE(
      jwt.ComputeMacAndEncode(nullptr, &comp_request, &comp_response).ok());
  EXPECT_THAT(comp_response.err(), Not(IsEmpty()));
}

TEST_F(JwtImplMacTest, VerifyWithWrongIssuerFails) {
  tink_testing_api::JwtImpl jwt;
  std::string keyset = ValidKeyset();
  JwtSignRequest comp_request;
  comp_request.set_keyset(keyset);
  comp_request.mutable_raw_jwt()->mutable_issuer()->set_value("unknown");
  JwtSignResponse comp_response;
  EXPECT_TRUE(
      jwt.ComputeMacAndEncode(nullptr, &comp_request, &comp_response).ok());
  EXPECT_THAT(comp_response.err(), IsEmpty());

  JwtVerifyRequest verify_request;
  verify_request.set_keyset(keyset);
  verify_request.set_signed_compact_jwt(comp_response.signed_compact_jwt());
  verify_request.mutable_validator()->mutable_expected_issuer()->set_value(
      "issuer");
  JwtVerifyResponse verify_response;

  EXPECT_TRUE(
      jwt.VerifyMacAndDecode(nullptr, &verify_request, &verify_response).ok());
  EXPECT_THAT(verify_response.err(), Not(IsEmpty()));
}

class JwtImplSignatureTest : public ::testing::Test {
 protected:
  static void SetUpTestSuite() { ASSERT_THAT(JwtSignatureRegister(), IsOk()); }
  void SetUp() override {
    const KeyTemplate& key_template = ::crypto::tink::JwtEs256Template();
    util::StatusOr<std::unique_ptr<KeysetHandle>> handle =
        KeysetHandle::GenerateNew(key_template);
    EXPECT_THAT(handle.status(), IsOk());

    std::stringbuf keyset;
    util::StatusOr<std::unique_ptr<BinaryKeysetWriter>> writer =
        BinaryKeysetWriter::New(absl::make_unique<std::ostream>(&keyset));
    EXPECT_THAT(writer.status(), IsOk());

    util::Status status =
        CleartextKeysetHandle::Write((*writer).get(), **handle);
    EXPECT_THAT(status, IsOk());
    private_keyset_ = keyset.str();

    util::StatusOr<std::unique_ptr<crypto::tink::KeysetHandle>> pub_handle =
        (*handle)->GetPublicKeysetHandle();
    EXPECT_THAT(pub_handle.status(), IsOk());

    std::stringbuf pub_keyset;
    util::StatusOr<std::unique_ptr<BinaryKeysetWriter>> pub_writer =
        BinaryKeysetWriter::New(absl::make_unique<std::ostream>(&pub_keyset));
    EXPECT_THAT(writer.status(), IsOk());

    util::Status pub_status =
        CleartextKeysetHandle::Write(pub_writer->get(), **pub_handle);
    EXPECT_THAT(pub_status, IsOk());
    public_keyset_ = pub_keyset.str();
  }
  std::string private_keyset_;
  std::string public_keyset_;
};

TEST_F(JwtImplSignatureTest, SignVerifySuccess) {
  tink_testing_api::JwtImpl jwt;
  JwtSignRequest comp_request;
  comp_request.set_keyset(private_keyset_);
  JwtToken* raw_jwt = comp_request.mutable_raw_jwt();
  raw_jwt->mutable_type_header()->set_value("type_header");
  raw_jwt->mutable_issuer()->set_value("issuer");
  raw_jwt->mutable_subject()->set_value("subject");
  raw_jwt->mutable_audiences()->Add("audience1");
  raw_jwt->mutable_audiences()->Add("audience2");
  raw_jwt->mutable_jwt_id()->set_value("jwt_id");
  raw_jwt->mutable_not_before()->set_seconds(12345);
  raw_jwt->mutable_issued_at()->set_seconds(23456);
  raw_jwt->mutable_expiration()->set_seconds(34567);
  Map<std::string, JwtClaimValue>* custom_claims =
      raw_jwt->mutable_custom_claims();
  (*custom_claims)["null_claim"].set_null_value(
      tink_testing_api::NullValue::NULL_VALUE);
  (*custom_claims)["bool_claim"].set_bool_value(true);
  (*custom_claims)["number_claim"].set_number_value(123.456);
  (*custom_claims)["string_claim"].set_string_value("string_value");
  JwtSignResponse comp_response;
  EXPECT_TRUE(
      jwt.PublicKeySignAndEncode(nullptr, &comp_request, &comp_response).ok());
  EXPECT_THAT(comp_response.err(), IsEmpty());

  JwtVerifyRequest verify_request;
  verify_request.set_keyset(public_keyset_);
  verify_request.set_signed_compact_jwt(comp_response.signed_compact_jwt());
  JwtValidator* validator = verify_request.mutable_validator();
  validator->mutable_expected_type_header()->set_value("type_header");
  validator->mutable_expected_issuer()->set_value("issuer");
  validator->mutable_expected_audience()->set_value("audience2");
  validator->mutable_now()->set_seconds(23456);
  JwtVerifyResponse verify_response;

  ASSERT_TRUE(
      jwt.PublicKeyVerifyAndDecode(nullptr, &verify_request, &verify_response)
          .ok());
  ASSERT_THAT(verify_response.err(), IsEmpty());
  const JwtToken& verified_jwt = verify_response.verified_jwt();
  EXPECT_EQ(verified_jwt.type_header().value(), "type_header");
  EXPECT_THAT(verified_jwt.issuer().value(), Eq("issuer"));
  EXPECT_THAT(verified_jwt.subject().value(), Eq("subject"));
  ASSERT_THAT(verified_jwt.audiences(),
              ElementsAre("audience1", "audience2"));
  EXPECT_THAT(verified_jwt.jwt_id().value(), Eq("jwt_id"));
  EXPECT_THAT(verified_jwt.not_before().seconds(), Eq(12345));
  EXPECT_THAT(verified_jwt.issued_at().seconds(), Eq(23456));
  EXPECT_THAT(verified_jwt.expiration().seconds(), Eq(34567));
  auto verified_custom_claims = verified_jwt.custom_claims();
  EXPECT_THAT(verified_custom_claims["null_claim"].null_value(),
              Eq(tink_testing_api::NullValue::NULL_VALUE));
  EXPECT_THAT(verified_custom_claims["bool_claim"].bool_value(), Eq(true));
  EXPECT_THAT(verified_custom_claims["number_claim"].number_value(),
              Eq(123.456));
  EXPECT_THAT(verified_custom_claims["string_claim"].string_value(),
              Eq("string_value"));
}

TEST_F(JwtImplSignatureTest, SignWithBadKeysetFails) {
  tink_testing_api::JwtImpl jwt;
  JwtSignRequest comp_request;
  comp_request.set_keyset("bad keyset");
  comp_request.mutable_raw_jwt()->mutable_issuer()->set_value("issuer");
  JwtSignResponse comp_response;

  EXPECT_TRUE(
      jwt.PublicKeySignAndEncode(nullptr, &comp_request, &comp_response).ok());
  EXPECT_THAT(comp_response.err(), Not(IsEmpty()));
}

TEST_F(JwtImplSignatureTest, VerifyWithWrongIssuerFails) {
  tink_testing_api::JwtImpl jwt;
  JwtSignRequest comp_request;
  comp_request.set_keyset(private_keyset_);
  comp_request.mutable_raw_jwt()->mutable_issuer()->set_value("unknown");
  JwtSignResponse comp_response;
  EXPECT_TRUE(
      jwt.PublicKeySignAndEncode(nullptr, &comp_request, &comp_response).ok());
  EXPECT_THAT(comp_response.err(), IsEmpty());

  JwtVerifyRequest verify_request;
  verify_request.set_keyset(public_keyset_);
  verify_request.set_signed_compact_jwt(comp_response.signed_compact_jwt());
  verify_request.mutable_validator()->mutable_expected_issuer()->set_value(
      "issuer");
  JwtVerifyResponse verify_response;

  EXPECT_TRUE(
      jwt.PublicKeyVerifyAndDecode(nullptr, &verify_request, &verify_response)
          .ok());
  EXPECT_THAT(verify_response.err(), Not(IsEmpty()));
}

}  // namespace
}  // namespace tink
}  // namespace crypto

<commit_msg>Use "auto" for type returned by Struct fields.<commit_after>// Copyright 2021 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
//
//     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.
//
///////////////////////////////////////////////////////////////////////////////

#include "jwt_impl.h"

#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "tink/binary_keyset_writer.h"
#include "tink/cleartext_keyset_handle.h"
#include "tink/jwt/jwt_mac_config.h"
#include "tink/jwt/jwt_signature_config.h"
#include "tink/jwt/jwt_key_templates.h"
#include "proto/testing/testing_api.grpc.pb.h"
#include "tink/util/test_matchers.h"

namespace crypto {
namespace tink {
namespace {

using ::crypto::tink::BinaryKeysetWriter;
using ::crypto::tink::CleartextKeysetHandle;
using ::crypto::tink::KeysetHandle;
using ::crypto::tink::test::IsOk;
using ::google::crypto::tink::KeyTemplate;
using ::testing::Eq;
using ::testing::IsEmpty;
using ::testing::ElementsAre;
using ::tink_testing_api::JwtClaimValue;
using ::tink_testing_api::JwtSignRequest;
using ::tink_testing_api::JwtSignResponse;
using ::tink_testing_api::JwtToken;
using ::tink_testing_api::JwtValidator;
using ::tink_testing_api::JwtVerifyRequest;
using ::tink_testing_api::JwtVerifyResponse;

std::string ValidKeyset() {
  const KeyTemplate& key_template = ::crypto::tink::JwtHs256Template();
  util::StatusOr<std::unique_ptr<KeysetHandle>> handle =
      KeysetHandle::GenerateNew(key_template);
  EXPECT_THAT(handle.status(), IsOk());

  std::stringbuf keyset;
  util::StatusOr<std::unique_ptr<BinaryKeysetWriter>> writer =
      BinaryKeysetWriter::New(absl::make_unique<std::ostream>(&keyset));
  EXPECT_THAT(writer.status(), IsOk());

  util::Status status = CleartextKeysetHandle::Write((*writer).get(), **handle);
  EXPECT_THAT(status, IsOk());
  return keyset.str();
}

class JwtImplMacTest : public ::testing::Test {
 protected:
  static void SetUpTestSuite() { ASSERT_THAT(JwtMacRegister(), IsOk()); }
};

TEST_F(JwtImplMacTest, MacComputeVerifySuccess) {
  tink_testing_api::JwtImpl jwt;
  std::string keyset = ValidKeyset();
  JwtSignRequest comp_request;
  comp_request.set_keyset(keyset);
  JwtToken* raw_jwt = comp_request.mutable_raw_jwt();
  raw_jwt->mutable_type_header()->set_value("type_header");
  raw_jwt->mutable_issuer()->set_value("issuer");
  raw_jwt->mutable_subject()->set_value("subject");
  raw_jwt->mutable_audiences()->Add("audience1");
  raw_jwt->mutable_audiences()->Add("audience2");
  raw_jwt->mutable_jwt_id()->set_value("jwt_id");
  raw_jwt->mutable_not_before()->set_seconds(12345);
  raw_jwt->mutable_not_before()->set_nanos(123000000);
  raw_jwt->mutable_issued_at()->set_seconds(23456);
  raw_jwt->mutable_expiration()->set_seconds(34567);
  auto custom_claims = raw_jwt->mutable_custom_claims();
  (*custom_claims)["null_claim"].set_null_value(
      tink_testing_api::NullValue::NULL_VALUE);
  (*custom_claims)["bool_claim"].set_bool_value(true);
  (*custom_claims)["number_claim"].set_number_value(123.456);
  (*custom_claims)["string_claim"].set_string_value("string_value");
  JwtSignResponse comp_response;
  EXPECT_TRUE(
      jwt.ComputeMacAndEncode(nullptr, &comp_request, &comp_response).ok());
  EXPECT_THAT(comp_response.err(), IsEmpty());

  JwtVerifyRequest verify_request;
  verify_request.set_keyset(keyset);
  verify_request.set_signed_compact_jwt(comp_response.signed_compact_jwt());
  JwtValidator* validator = verify_request.mutable_validator();
  validator->mutable_expected_type_header()->set_value("type_header");
  validator->mutable_expected_issuer()->set_value("issuer");
  validator->mutable_expected_audience()->set_value("audience2");
  validator->mutable_now()->set_seconds(23456);
  JwtVerifyResponse verify_response;

  ASSERT_TRUE(
      jwt.VerifyMacAndDecode(nullptr, &verify_request, &verify_response).ok());
  ASSERT_THAT(verify_response.err(), IsEmpty());
  const JwtToken& verified_jwt = verify_response.verified_jwt();
  EXPECT_EQ(verified_jwt.type_header().value(), "type_header");
  EXPECT_THAT(verified_jwt.issuer().value(), Eq("issuer"));
  EXPECT_THAT(verified_jwt.subject().value(), Eq("subject"));
  EXPECT_THAT(verified_jwt.audiences_size(), Eq(2));
  EXPECT_THAT(verified_jwt.audiences(0), Eq("audience1"));
  EXPECT_THAT(verified_jwt.audiences(1), Eq("audience2"));
  EXPECT_THAT(verified_jwt.jwt_id().value(), Eq("jwt_id"));
  EXPECT_THAT(verified_jwt.not_before().seconds(), Eq(12345));
  EXPECT_THAT(verified_jwt.not_before().nanos(), Eq(0));
  EXPECT_THAT(verified_jwt.issued_at().seconds(), Eq(23456));
  EXPECT_THAT(verified_jwt.issued_at().nanos(), Eq(0));
  EXPECT_THAT(verified_jwt.expiration().seconds(), Eq(34567));
  EXPECT_THAT(verified_jwt.expiration().nanos(), Eq(0));
  auto verified_custom_claims = verified_jwt.custom_claims();
  EXPECT_THAT(verified_custom_claims["null_claim"].null_value(),
              Eq(tink_testing_api::NullValue::NULL_VALUE));
  EXPECT_THAT(verified_custom_claims["bool_claim"].bool_value(), Eq(true));
  EXPECT_THAT(verified_custom_claims["number_claim"].number_value(),
              Eq(123.456));
  EXPECT_THAT(verified_custom_claims["string_claim"].string_value(),
              Eq("string_value"));
}

TEST_F(JwtImplMacTest, ComputeBadKeysetFail) {
  tink_testing_api::JwtImpl jwt;
  JwtSignRequest comp_request;
  comp_request.set_keyset("bad keyset");
  comp_request.mutable_raw_jwt()->mutable_issuer()->set_value("issuer");
  JwtSignResponse comp_response;

  EXPECT_TRUE(
      jwt.ComputeMacAndEncode(nullptr, &comp_request, &comp_response).ok());
  EXPECT_THAT(comp_response.err(), Not(IsEmpty()));
}

TEST_F(JwtImplMacTest, VerifyWithWrongIssuerFails) {
  tink_testing_api::JwtImpl jwt;
  std::string keyset = ValidKeyset();
  JwtSignRequest comp_request;
  comp_request.set_keyset(keyset);
  comp_request.mutable_raw_jwt()->mutable_issuer()->set_value("unknown");
  JwtSignResponse comp_response;
  EXPECT_TRUE(
      jwt.ComputeMacAndEncode(nullptr, &comp_request, &comp_response).ok());
  EXPECT_THAT(comp_response.err(), IsEmpty());

  JwtVerifyRequest verify_request;
  verify_request.set_keyset(keyset);
  verify_request.set_signed_compact_jwt(comp_response.signed_compact_jwt());
  verify_request.mutable_validator()->mutable_expected_issuer()->set_value(
      "issuer");
  JwtVerifyResponse verify_response;

  EXPECT_TRUE(
      jwt.VerifyMacAndDecode(nullptr, &verify_request, &verify_response).ok());
  EXPECT_THAT(verify_response.err(), Not(IsEmpty()));
}

class JwtImplSignatureTest : public ::testing::Test {
 protected:
  static void SetUpTestSuite() { ASSERT_THAT(JwtSignatureRegister(), IsOk()); }
  void SetUp() override {
    const KeyTemplate& key_template = ::crypto::tink::JwtEs256Template();
    util::StatusOr<std::unique_ptr<KeysetHandle>> handle =
        KeysetHandle::GenerateNew(key_template);
    EXPECT_THAT(handle.status(), IsOk());

    std::stringbuf keyset;
    util::StatusOr<std::unique_ptr<BinaryKeysetWriter>> writer =
        BinaryKeysetWriter::New(absl::make_unique<std::ostream>(&keyset));
    EXPECT_THAT(writer.status(), IsOk());

    util::Status status =
        CleartextKeysetHandle::Write((*writer).get(), **handle);
    EXPECT_THAT(status, IsOk());
    private_keyset_ = keyset.str();

    util::StatusOr<std::unique_ptr<crypto::tink::KeysetHandle>> pub_handle =
        (*handle)->GetPublicKeysetHandle();
    EXPECT_THAT(pub_handle.status(), IsOk());

    std::stringbuf pub_keyset;
    util::StatusOr<std::unique_ptr<BinaryKeysetWriter>> pub_writer =
        BinaryKeysetWriter::New(absl::make_unique<std::ostream>(&pub_keyset));
    EXPECT_THAT(writer.status(), IsOk());

    util::Status pub_status =
        CleartextKeysetHandle::Write(pub_writer->get(), **pub_handle);
    EXPECT_THAT(pub_status, IsOk());
    public_keyset_ = pub_keyset.str();
  }
  std::string private_keyset_;
  std::string public_keyset_;
};

TEST_F(JwtImplSignatureTest, SignVerifySuccess) {
  tink_testing_api::JwtImpl jwt;
  JwtSignRequest comp_request;
  comp_request.set_keyset(private_keyset_);
  JwtToken* raw_jwt = comp_request.mutable_raw_jwt();
  raw_jwt->mutable_type_header()->set_value("type_header");
  raw_jwt->mutable_issuer()->set_value("issuer");
  raw_jwt->mutable_subject()->set_value("subject");
  raw_jwt->mutable_audiences()->Add("audience1");
  raw_jwt->mutable_audiences()->Add("audience2");
  raw_jwt->mutable_jwt_id()->set_value("jwt_id");
  raw_jwt->mutable_not_before()->set_seconds(12345);
  raw_jwt->mutable_issued_at()->set_seconds(23456);
  raw_jwt->mutable_expiration()->set_seconds(34567);
  auto custom_claims = raw_jwt->mutable_custom_claims();
  (*custom_claims)["null_claim"].set_null_value(
      tink_testing_api::NullValue::NULL_VALUE);
  (*custom_claims)["bool_claim"].set_bool_value(true);
  (*custom_claims)["number_claim"].set_number_value(123.456);
  (*custom_claims)["string_claim"].set_string_value("string_value");
  JwtSignResponse comp_response;
  EXPECT_TRUE(
      jwt.PublicKeySignAndEncode(nullptr, &comp_request, &comp_response).ok());
  EXPECT_THAT(comp_response.err(), IsEmpty());

  JwtVerifyRequest verify_request;
  verify_request.set_keyset(public_keyset_);
  verify_request.set_signed_compact_jwt(comp_response.signed_compact_jwt());
  JwtValidator* validator = verify_request.mutable_validator();
  validator->mutable_expected_type_header()->set_value("type_header");
  validator->mutable_expected_issuer()->set_value("issuer");
  validator->mutable_expected_audience()->set_value("audience2");
  validator->mutable_now()->set_seconds(23456);
  JwtVerifyResponse verify_response;

  ASSERT_TRUE(
      jwt.PublicKeyVerifyAndDecode(nullptr, &verify_request, &verify_response)
          .ok());
  ASSERT_THAT(verify_response.err(), IsEmpty());
  const JwtToken& verified_jwt = verify_response.verified_jwt();
  EXPECT_EQ(verified_jwt.type_header().value(), "type_header");
  EXPECT_THAT(verified_jwt.issuer().value(), Eq("issuer"));
  EXPECT_THAT(verified_jwt.subject().value(), Eq("subject"));
  ASSERT_THAT(verified_jwt.audiences(),
              ElementsAre("audience1", "audience2"));
  EXPECT_THAT(verified_jwt.jwt_id().value(), Eq("jwt_id"));
  EXPECT_THAT(verified_jwt.not_before().seconds(), Eq(12345));
  EXPECT_THAT(verified_jwt.issued_at().seconds(), Eq(23456));
  EXPECT_THAT(verified_jwt.expiration().seconds(), Eq(34567));
  auto verified_custom_claims = verified_jwt.custom_claims();
  EXPECT_THAT(verified_custom_claims["null_claim"].null_value(),
              Eq(tink_testing_api::NullValue::NULL_VALUE));
  EXPECT_THAT(verified_custom_claims["bool_claim"].bool_value(), Eq(true));
  EXPECT_THAT(verified_custom_claims["number_claim"].number_value(),
              Eq(123.456));
  EXPECT_THAT(verified_custom_claims["string_claim"].string_value(),
              Eq("string_value"));
}

TEST_F(JwtImplSignatureTest, SignWithBadKeysetFails) {
  tink_testing_api::JwtImpl jwt;
  JwtSignRequest comp_request;
  comp_request.set_keyset("bad keyset");
  comp_request.mutable_raw_jwt()->mutable_issuer()->set_value("issuer");
  JwtSignResponse comp_response;

  EXPECT_TRUE(
      jwt.PublicKeySignAndEncode(nullptr, &comp_request, &comp_response).ok());
  EXPECT_THAT(comp_response.err(), Not(IsEmpty()));
}

TEST_F(JwtImplSignatureTest, VerifyWithWrongIssuerFails) {
  tink_testing_api::JwtImpl jwt;
  JwtSignRequest comp_request;
  comp_request.set_keyset(private_keyset_);
  comp_request.mutable_raw_jwt()->mutable_issuer()->set_value("unknown");
  JwtSignResponse comp_response;
  EXPECT_TRUE(
      jwt.PublicKeySignAndEncode(nullptr, &comp_request, &comp_response).ok());
  EXPECT_THAT(comp_response.err(), IsEmpty());

  JwtVerifyRequest verify_request;
  verify_request.set_keyset(public_keyset_);
  verify_request.set_signed_compact_jwt(comp_response.signed_compact_jwt());
  verify_request.mutable_validator()->mutable_expected_issuer()->set_value(
      "issuer");
  JwtVerifyResponse verify_response;

  EXPECT_TRUE(
      jwt.PublicKeyVerifyAndDecode(nullptr, &verify_request, &verify_response)
          .ok());
  EXPECT_THAT(verify_response.err(), Not(IsEmpty()));
}

}  // namespace
}  // namespace tink
}  // namespace crypto

<|endoftext|>


<commit_before>/**
 * Algebraic effects
 * =================
 *
 * Compile with -std=c++17.
 */

#include <functional>
#include <iostream>
#include <tuple>
#include <utility>
#include <vector>

template <typename T>
struct TypeIdentityHelper {
    using type = T;
};

template <typename T>
using TypeIdentity = typename TypeIdentityHelper<T>::type;

template <typename T>
struct FnTraits : public FnTraits<decltype(&T::operator())> {
};

template <typename T, typename R, typename... Args>
struct FnTraits<R (T::*)(Args...) const> {
    static constexpr auto arity = sizeof...(Args);

    using result_type = R;

    template <size_t i>
    struct arg {
        using type = typename std::tuple_element_t<i, std::tuple<Args...>>;
    };

    template <size_t i>
    using arg_t = typename arg<i>::type;
};

template <typename T, typename U>
class Context final {
  public:
    static std::vector<std::function<T(U)>> &
    Provide(std::function<T(U)> handler)
    {
        g_stk.emplace_back(handler);
        return g_stk;
    }

    static std::optional<T>
    Require(U arg)
    {
        if (g_stk.empty()) {
            return {};
        }
        const auto &fn = g_stk.back();
        T ret = fn(arg);
        return ret;
    }

  private:
    static std::vector<std::function<T(U)>> g_stk;
};

template <typename T, typename U>
std::vector<std::function<T(U)>> Context<T, U>::g_stk{};

template <typename T>
class StackGuard final {
  public:
    StackGuard(std::vector<T> &src)
        : stk_{src}
        , depth_{src.size()}
    {
    }

    StackGuard(std::vector<T> &&src) = delete;

    ~StackGuard()
    {
        if (depth_ == stk_.size()) {
            stk_.pop_back();
        }
    }

    StackGuard &operator=(const StackGuard &) = delete;
    StackGuard(const StackGuard &) = delete;

    StackGuard &operator=(StackGuard &&) = delete;
    StackGuard(StackGuard &&) = delete;

  private:
    std::vector<T> &stk_;
    size_t depth_;
};

#define PROVIDE(handler) \
    auto __h##__LINE__ = (handler); \
    StackGuard __stackGuard##__LINE__( \
        Context< \
            FnTraits<decltype(__h##__LINE__)>::result_type, \
            FnTraits<decltype(__h##__LINE__)>::arg_t<0> \
        >::Provide(std::move(__h##__LINE__)))

#define REQUIRE(T, name) Context<T, TypeIdentity<T>>::Require(name)

struct User {
    std::string name;
    std::vector<std::string> friendNames;
};

std::string GetName(User user)
{
    if (user.name == "") {
        if (auto v = REQUIRE(std::string, "ask_name")) {
            return *v;
        }
        return "";
    }
    return user.name;
}

void MakeFriends(User &user1, User &user2)
{
    user1.friendNames.emplace_back(GetName(user2));
    user2.friendNames.emplace_back(GetName(user1));
}

int main()
{
    User foo{};
    User bar{"Bar", {}};

    PROVIDE([&](std::string effect) -> std::string {
        if (effect == "ask_name") {
            return "Foo";
        }
        return "";
    });

    MakeFriends(foo, bar);

    std::cout << foo.friendNames[0] << std::endl;
    std::cout << bar.friendNames[0] << std::endl;

    return 0;
}
<commit_msg>[ misc ] Updated algeff: eliminate local var<commit_after>/**
 * Algebraic effects
 * =================
 *
 * Compile with -std=c++17.
 */

#include <functional>
#include <iostream>
#include <tuple>
#include <utility>
#include <vector>

template <typename T>
struct TypeIdentityHelper {
    using type = T;
};

template <typename T>
using TypeIdentity = typename TypeIdentityHelper<T>::type;

template <typename T>
struct FnTraits : public FnTraits<decltype(&T::operator())> {
};

template <typename T, typename R, typename... Args>
struct FnTraits<R (T::*)(Args...) const> {
    static constexpr auto arity = sizeof...(Args);

    using result_type = R;

    template <size_t i>
    struct arg {
        using type = typename std::tuple_element_t<i, std::tuple<Args...>>;
    };

    template <size_t i>
    using arg_t = typename arg<i>::type;
};

template <typename T, typename U>
class Context final {
  public:
    static std::vector<std::function<T(U)>> &
    Provide(std::function<T(U)> handler)
    {
        g_stk.emplace_back(handler);
        return g_stk;
    }

    static std::optional<T>
    Require(U arg)
    {
        if (g_stk.empty()) {
            return {};
        }
        const auto &fn = g_stk.back();
        return fn(arg);
    }

  private:
    static std::vector<std::function<T(U)>> g_stk;
};

template <typename T, typename U>
std::vector<std::function<T(U)>> Context<T, U>::g_stk{};

template <typename T>
class StackGuard final {
  public:
    StackGuard(std::vector<T> &src)
        : stk_{src}
        , depth_{src.size()}
    {
    }

    StackGuard(std::vector<T> &&src) = delete;

    ~StackGuard()
    {
        if (depth_ == stk_.size()) {
            stk_.pop_back();
        }
    }

    StackGuard &operator=(const StackGuard &) = delete;
    StackGuard(const StackGuard &) = delete;

    StackGuard &operator=(StackGuard &&) = delete;
    StackGuard(StackGuard &&) = delete;

  private:
    std::vector<T> &stk_;
    size_t depth_;
};

#define PROVIDE(handler) \
    auto __h##__LINE__ = (handler); \
    StackGuard __stackGuard##__LINE__( \
        Context< \
            FnTraits<decltype(__h##__LINE__)>::result_type, \
            FnTraits<decltype(__h##__LINE__)>::arg_t<0> \
        >::Provide(std::move(__h##__LINE__)))

#define REQUIRE(T, name) Context<T, TypeIdentity<T>>::Require(name)

struct User {
    std::string name;
    std::vector<std::string> friendNames;
};

std::string GetName(User user)
{
    if (user.name == "") {
        if (auto v = REQUIRE(std::string, "ask_name")) {
            return *v;
        }
        return "";
    }
    return user.name;
}

void MakeFriends(User &user1, User &user2)
{
    user1.friendNames.emplace_back(GetName(user2));
    user2.friendNames.emplace_back(GetName(user1));
}

int main()
{
    User foo{};
    User bar{"Bar", {}};

    PROVIDE([&](std::string effect) -> std::string {
        if (effect == "ask_name") {
            return "Foo";
        }
        return "";
    });

    MakeFriends(foo, bar);

    std::cout << foo.friendNames[0] << std::endl;
    std::cout << bar.friendNames[0] << std::endl;

    return 0;
}
<|endoftext|>


<commit_before>/*
* Copyright (c) 2013-2016, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/

#include "Log.h"

//for std::transform
#include <algorithm>

namespace i2p {
namespace log {
	static Log logger;
	/**
	 * @brief Maps our loglevel to their symbolic name
	 */
	static const char * g_LogLevelStr[eNumLogLevels] =
	{
		"none",  // eLogNone
		"error", // eLogError
		"warn",  // eLogWarn
		"info",  // eLogInfo
		"debug"  // eLogDebug
	};

	/**
	 * @brief Colorize log output -- array of terminal control sequences
	 * @note Using ISO 6429 (ANSI) color sequences
	 */
#ifdef _WIN32
	static const char *LogMsgColors[] = { "", "", "", "", "", "" };
#else /* UNIX */
	static const char *LogMsgColors[] = {
		[eLogNone]      = "\033[0m",    /* reset */
		[eLogError]     = "\033[1;31m", /* red */
		[eLogWarning]   = "\033[1;33m", /* yellow */
		[eLogInfo]      = "\033[1;36m", /* cyan */
		[eLogDebug]     = "\033[1;34m", /* blue */
		[eNumLogLevels] = "\033[0m",    /* reset */
	};
#endif

#ifndef _WIN32
	/**
	 * @brief  Maps our log levels to syslog one
	 * @return syslog priority LOG_*, as defined in syslog.h
	 */
	static inline int GetSyslogPrio (enum LogLevel l) {
		int priority = LOG_DEBUG;
		switch (l) {
			case eLogNone    : priority = LOG_CRIT;    break;
			case eLogError   : priority = LOG_ERR;     break;
			case eLogWarning : priority = LOG_WARNING; break;
			case eLogInfo    : priority = LOG_INFO;    break;
			case eLogDebug   : priority = LOG_DEBUG;   break;
			default          : priority = LOG_DEBUG;   break;
		}
		return priority;
	}
#endif

	Log::Log():
	m_Destination(eLogStdout), m_MinLevel(eLogInfo),
	m_LogStream (nullptr), m_Logfile(""), m_HasColors(true), m_TimeFormat("%H:%M:%S"),
	m_IsRunning (false), m_Thread (nullptr)
	{
	}

	Log::~Log ()
	{
		delete m_Thread;
	}

	void Log::Start ()
	{
		if (!m_IsRunning)
		{
			m_IsRunning = true;
			m_Thread = new std::thread (std::bind (&Log::Run, this));
		}
	}

	void Log::Stop ()
	{
		switch (m_Destination)
		{
#ifndef _WIN32
			case eLogSyslog :
				closelog();
				break;
#endif
			case eLogFile:
			case eLogStream:
					if (m_LogStream) m_LogStream->flush();
				break;
			default:
				/* do nothing */
				break;
		}
		m_IsRunning = false;
		m_Queue.WakeUp ();
		if (m_Thread)
		{
			m_Thread->join ();
			delete m_Thread;
			m_Thread = nullptr;
		}
	}

    std::string str_tolower(std::string s) {
        std::transform(s.begin(), s.end(), s.begin(),
                    // static_cast<int(*)(int)>(std::tolower)         // wrong
                    // [](int c){ return std::tolower(c); }           // wrong
                    // [](char c){ return std::tolower(c); }          // wrong
                       [](unsigned char c){ return std::tolower(c); } // correct
                      );
        return s;
    }

    void Log::SetLogLevel (const std::string& level_) {
        std::string level=str_tolower(level_);
		if      (level == "none")  { m_MinLevel = eLogNone; }
		else if (level == "error") { m_MinLevel = eLogError; }
		else if (level == "warn")  { m_MinLevel = eLogWarning; }
		else if (level == "info")  { m_MinLevel = eLogInfo;  }
		else if (level == "debug") { m_MinLevel = eLogDebug; }
		else {
			LogPrint(eLogError, "Log: unknown loglevel: ", level);
			return;
		}
		LogPrint(eLogInfo, "Log: min messages level set to ", level);
	}

	const char * Log::TimeAsString(std::time_t t) {
		if (t != m_LastTimestamp) {
			strftime(m_LastDateTime, sizeof(m_LastDateTime), m_TimeFormat.c_str(), localtime(&t));
			m_LastTimestamp = t;
		}
		return m_LastDateTime;
	}

	/**
	 * @note This function better to be run in separate thread due to disk i/o.
	 * Unfortunately, with current startup process with late fork() this
	 * will give us nothing but pain. Maybe later. See in NetDb as example.
	 */
	void Log::Process(std::shared_ptr<LogMsg> msg)
	{
		if (!msg) return;
		std::hash<std::thread::id> hasher;
		unsigned short short_tid;
		short_tid = (short) (hasher(msg->tid) % 1000);
		switch (m_Destination) {
#ifndef _WIN32
			case eLogSyslog:
				syslog(GetSyslogPrio(msg->level), "[%03u] %s", short_tid, msg->text.c_str());
				break;
#endif
			case eLogFile:
			case eLogStream:
				if (m_LogStream)
					*m_LogStream << TimeAsString(msg->timestamp)
						<< "@" << short_tid
						<< "/" << g_LogLevelStr[msg->level]
						<< " - " << msg->text << std::endl;
				break;
			case eLogStdout:
			default:
				std::cout    << TimeAsString(msg->timestamp)
					<< "@" << short_tid
					<< "/" << LogMsgColors[msg->level] << g_LogLevelStr[msg->level] << LogMsgColors[eNumLogLevels]
					<< " - " << msg->text << std::endl;
				break;
		} // switch
	}

	void Log::Run ()
	{
		Reopen ();
		while (m_IsRunning)
		{
			std::shared_ptr<LogMsg> msg;
			while ((msg = m_Queue.Get ()))
				Process (msg);
			if (m_LogStream) m_LogStream->flush();
			if (m_IsRunning)
				m_Queue.Wait ();
		}
	}

	void Log::Append(std::shared_ptr<i2p::log::LogMsg> & msg)
	{
		m_Queue.Put(msg);
	}

	void Log::SendTo (const std::string& path)
	{
		if (m_LogStream) m_LogStream = nullptr; // close previous
		if (m_MinLevel == eLogNone) return;
		auto flags = std::ofstream::out | std::ofstream::app;
		auto os = std::make_shared<std::ofstream> (path, flags);
		if (os->is_open ())
		{
			m_HasColors = false;
			m_Logfile = path;
			m_Destination = eLogFile;
			m_LogStream = os;
			return;
		}
		LogPrint(eLogError, "Log: can't open file ", path);
	}

	void Log::SendTo (std::shared_ptr<std::ostream> os) {
		m_HasColors = false;
		m_Destination = eLogStream;
		m_LogStream = os;
	}

#ifndef _WIN32
	void Log::SendTo(const char *name, int facility) {
		if (m_MinLevel == eLogNone) return;
		m_HasColors = false;
		m_Destination = eLogSyslog;
		m_LogStream = nullptr;
		openlog(name, LOG_CONS | LOG_PID, facility);
	}
#endif

	void Log::Reopen() {
		if (m_Destination == eLogFile)
			SendTo(m_Logfile);
	}

	Log & Logger() {
		return logger;
	}
} // log
} // i2p
<commit_msg>[Log] create logfile even if loglevel is "none"<commit_after>/*
* Copyright (c) 2013-2016, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
* See full license text in LICENSE file at top of project tree
*/

#include "Log.h"

//for std::transform
#include <algorithm>

namespace i2p {
namespace log {
	static Log logger;
	/**
	 * @brief Maps our loglevel to their symbolic name
	 */
	static const char * g_LogLevelStr[eNumLogLevels] =
	{
		"none",  // eLogNone
		"error", // eLogError
		"warn",  // eLogWarn
		"info",  // eLogInfo
		"debug"  // eLogDebug
	};

	/**
	 * @brief Colorize log output -- array of terminal control sequences
	 * @note Using ISO 6429 (ANSI) color sequences
	 */
#ifdef _WIN32
	static const char *LogMsgColors[] = { "", "", "", "", "", "" };
#else /* UNIX */
	static const char *LogMsgColors[] = {
		[eLogNone]      = "\033[0m",    /* reset */
		[eLogError]     = "\033[1;31m", /* red */
		[eLogWarning]   = "\033[1;33m", /* yellow */
		[eLogInfo]      = "\033[1;36m", /* cyan */
		[eLogDebug]     = "\033[1;34m", /* blue */
		[eNumLogLevels] = "\033[0m",    /* reset */
	};
#endif

#ifndef _WIN32
	/**
	 * @brief  Maps our log levels to syslog one
	 * @return syslog priority LOG_*, as defined in syslog.h
	 */
	static inline int GetSyslogPrio (enum LogLevel l) {
		int priority = LOG_DEBUG;
		switch (l) {
			case eLogNone    : priority = LOG_CRIT;    break;
			case eLogError   : priority = LOG_ERR;     break;
			case eLogWarning : priority = LOG_WARNING; break;
			case eLogInfo    : priority = LOG_INFO;    break;
			case eLogDebug   : priority = LOG_DEBUG;   break;
			default          : priority = LOG_DEBUG;   break;
		}
		return priority;
	}
#endif

	Log::Log():
	m_Destination(eLogStdout), m_MinLevel(eLogInfo),
	m_LogStream (nullptr), m_Logfile(""), m_HasColors(true), m_TimeFormat("%H:%M:%S"),
	m_IsRunning (false), m_Thread (nullptr)
	{
	}

	Log::~Log ()
	{
		delete m_Thread;
	}

	void Log::Start ()
	{
		if (!m_IsRunning)
		{
			m_IsRunning = true;
			m_Thread = new std::thread (std::bind (&Log::Run, this));
		}
	}

	void Log::Stop ()
	{
		switch (m_Destination)
		{
#ifndef _WIN32
			case eLogSyslog :
				closelog();
				break;
#endif
			case eLogFile:
			case eLogStream:
					if (m_LogStream) m_LogStream->flush();
				break;
			default:
				/* do nothing */
				break;
		}
		m_IsRunning = false;
		m_Queue.WakeUp ();
		if (m_Thread)
		{
			m_Thread->join ();
			delete m_Thread;
			m_Thread = nullptr;
		}
	}

    std::string str_tolower(std::string s) {
        std::transform(s.begin(), s.end(), s.begin(),
                    // static_cast<int(*)(int)>(std::tolower)         // wrong
                    // [](int c){ return std::tolower(c); }           // wrong
                    // [](char c){ return std::tolower(c); }          // wrong
                       [](unsigned char c){ return std::tolower(c); } // correct
                      );
        return s;
    }

    void Log::SetLogLevel (const std::string& level_) {
        std::string level=str_tolower(level_);
		if      (level == "none")  { m_MinLevel = eLogNone; }
		else if (level == "error") { m_MinLevel = eLogError; }
		else if (level == "warn")  { m_MinLevel = eLogWarning; }
		else if (level == "info")  { m_MinLevel = eLogInfo;  }
		else if (level == "debug") { m_MinLevel = eLogDebug; }
		else {
			LogPrint(eLogError, "Log: unknown loglevel: ", level);
			return;
		}
		LogPrint(eLogInfo, "Log: min messages level set to ", level);
	}

	const char * Log::TimeAsString(std::time_t t) {
		if (t != m_LastTimestamp) {
			strftime(m_LastDateTime, sizeof(m_LastDateTime), m_TimeFormat.c_str(), localtime(&t));
			m_LastTimestamp = t;
		}
		return m_LastDateTime;
	}

	/**
	 * @note This function better to be run in separate thread due to disk i/o.
	 * Unfortunately, with current startup process with late fork() this
	 * will give us nothing but pain. Maybe later. See in NetDb as example.
	 */
	void Log::Process(std::shared_ptr<LogMsg> msg)
	{
		if (!msg) return;
		std::hash<std::thread::id> hasher;
		unsigned short short_tid;
		short_tid = (short) (hasher(msg->tid) % 1000);
		switch (m_Destination) {
#ifndef _WIN32
			case eLogSyslog:
				syslog(GetSyslogPrio(msg->level), "[%03u] %s", short_tid, msg->text.c_str());
				break;
#endif
			case eLogFile:
			case eLogStream:
				if (m_LogStream)
					*m_LogStream << TimeAsString(msg->timestamp)
						<< "@" << short_tid
						<< "/" << g_LogLevelStr[msg->level]
						<< " - " << msg->text << std::endl;
				break;
			case eLogStdout:
			default:
				std::cout    << TimeAsString(msg->timestamp)
					<< "@" << short_tid
					<< "/" << LogMsgColors[msg->level] << g_LogLevelStr[msg->level] << LogMsgColors[eNumLogLevels]
					<< " - " << msg->text << std::endl;
				break;
		} // switch
	}

	void Log::Run ()
	{
		Reopen ();
		while (m_IsRunning)
		{
			std::shared_ptr<LogMsg> msg;
			while ((msg = m_Queue.Get ()))
				Process (msg);
			if (m_LogStream) m_LogStream->flush();
			if (m_IsRunning)
				m_Queue.Wait ();
		}
	}

	void Log::Append(std::shared_ptr<i2p::log::LogMsg> & msg)
	{
		m_Queue.Put(msg);
	}

	void Log::SendTo (const std::string& path)
	{
		if (m_LogStream) m_LogStream = nullptr; // close previous
		auto flags = std::ofstream::out | std::ofstream::app;
		auto os = std::make_shared<std::ofstream> (path, flags);
		if (os->is_open ())
		{
			m_HasColors = false;
			m_Logfile = path;
			m_Destination = eLogFile;
			m_LogStream = os;
			return;
		}
		LogPrint(eLogError, "Log: can't open file ", path);
	}

	void Log::SendTo (std::shared_ptr<std::ostream> os) {
		m_HasColors = false;
		m_Destination = eLogStream;
		m_LogStream = os;
	}

#ifndef _WIN32
	void Log::SendTo(const char *name, int facility) {
		if (m_MinLevel == eLogNone) return;
		m_HasColors = false;
		m_Destination = eLogSyslog;
		m_LogStream = nullptr;
		openlog(name, LOG_CONS | LOG_PID, facility);
	}
#endif

	void Log::Reopen() {
		if (m_Destination == eLogFile)
			SendTo(m_Logfile);
	}

	Log & Logger() {
		return logger;
	}
} // log
} // i2p
<|endoftext|>


<commit_before>#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QTimer>
#include <QMimeData>
#include <QFileDialog>
#include <QBitmap>
#include <QIcon>
#include <QThreadPool>
#include <QDebug>

#include <stdexcept>
#include <algorithm>

#include "ezgraver_factory.h"

using namespace Ez;

MainWindow::MainWindow(QWidget* parent)
        :  QMainWindow{parent}, _ui{new Ui::MainWindow},
          _portTimer{}, _image{}, _ezGraver{}, _bytesWrittenProcessor{[](qint64){}}, _connected{false} {
    _ui->setupUi(this);
    setAcceptDrops(true);

    connect(&_portTimer, &QTimer::timeout, this, &MainWindow::updatePorts);
    _portTimer.start(PortUpdateDelay);

    _initBindings();
    _initConversionFlags();
    _initProtocols();
    _setConnected(false);

    _ui->image->setImageDimensions(QSize{EzGraver::ImageWidth, EzGraver::ImageHeight});
}

MainWindow::~MainWindow() {
    delete _ui;
}

void MainWindow::_initBindings() {
    connect(_ui->burnTime, &QSlider::valueChanged, [this](int const& v) { _ui->burnTimeLabel->setText(QString::number(v)); });

    connect(this, &MainWindow::connectedChanged, _ui->ports, &QComboBox::setDisabled);
    connect(this, &MainWindow::connectedChanged, _ui->connect, &QPushButton::setDisabled);
    connect(this, &MainWindow::connectedChanged, _ui->disconnect, &QPushButton::setEnabled);

    connect(this, &MainWindow::connectedChanged, _ui->home, &QPushButton::setEnabled);
    connect(this, &MainWindow::connectedChanged, _ui->up, &QPushButton::setEnabled);
    connect(this, &MainWindow::connectedChanged, _ui->left, &QPushButton::setEnabled);
    connect(this, &MainWindow::connectedChanged, _ui->center, &QPushButton::setEnabled);
    connect(this, &MainWindow::connectedChanged, _ui->right, &QPushButton::setEnabled);
    connect(this, &MainWindow::connectedChanged, _ui->down, &QPushButton::setEnabled);
    connect(this, &MainWindow::connectedChanged, _ui->preview, &QPushButton::setEnabled);
    connect(this, &MainWindow::connectedChanged, _ui->start, &QPushButton::setEnabled);
    connect(this, &MainWindow::connectedChanged, _ui->pause, &QPushButton::setEnabled);
    connect(this, &MainWindow::connectedChanged, _ui->reset, &QPushButton::setEnabled);
    connect(_ui->conversionFlags, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), [this](int index) {
        _ui->image->setConversionFlags(static_cast<Qt::ImageConversionFlags>(_ui->conversionFlags->itemData(index).toInt()));
    });

    connect(_ui->layered, &QCheckBox::toggled, _ui->selectedLayer, &QSpinBox::setEnabled);
    connect(_ui->layered, &QCheckBox::toggled, _ui->layerCount, &QSpinBox::setEnabled);
    connect(_ui->layered, &QCheckBox::toggled, _ui->image, &ImageLabel::setGrayscale);
    connect(_ui->layerCount, static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged), _ui->image, &ImageLabel::setLayerCount);
    connect(_ui->layerCount, static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged), _ui->selectedLayer, &QSpinBox::setMaximum);
    connect(_ui->selectedLayer, static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged), _ui->image, &ImageLabel::setLayer);

    auto uploadEnabled = [this] {
        _ui->upload->setEnabled(_ui->image->imageLoaded() && _connected && (!_ui->layered->isChecked() || _ui->selectedLayer->value() > 0));
    };
    connect(this, &MainWindow::connectedChanged, uploadEnabled);
    connect(_ui->image, &ImageLabel::imageLoadedChanged, uploadEnabled);
    connect(_ui->selectedLayer, static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged), uploadEnabled);
    connect(_ui->layered, &QCheckBox::toggled, uploadEnabled);

    connect(_ui->keepAspectRatio, &QCheckBox::toggled, _ui->image, &ImageLabel::setKeepAspectRatio);

    connect(_ui->scaled, &QCheckBox::toggled, _ui->imageScale, &QSlider::setEnabled);
    connect(_ui->scaled, &QCheckBox::toggled, _ui->resetImageScale, &QSlider::setEnabled);
    connect(_ui->scaled, &QCheckBox::toggled, _ui->image, &ImageLabel::setScaled);
    connect(_ui->imageScale, &QSlider::valueChanged, [this](int const& v) { _ui->imageScaleLabel->setText(QString::number(v)); });
    connect(_ui->imageScale, &QSlider::valueChanged, [this](int const& v) { _ui->image->setImageScale(v / 100.0); });
    connect(_ui->resetImageScale, &QPushButton::clicked, [this] { _ui->imageScale->setValue(100); });

    connect(this, &MainWindow::connectedChanged, _ui->protocolVersion, &QComboBox::setDisabled);
}

void MainWindow::_initConversionFlags() {
    _ui->conversionFlags->addItem("DiffuseDither", Qt::DiffuseDither);
    _ui->conversionFlags->addItem("OrderedDither", Qt::OrderedDither);
    _ui->conversionFlags->addItem("ThresholdDither", Qt::ThresholdDither);
    _ui->conversionFlags->setCurrentIndex(0);
}

void MainWindow::_initProtocols() {
    for(auto protocol : Ez::protocols()) {
        _ui->protocolVersion->addItem(QString{"v%1"}.arg(protocol), protocol);
    }
}

void MainWindow::_printVerbose(QString const& verbose) {
    _ui->verbose->appendPlainText(verbose);
}

void MainWindow::updatePorts() {
    QStringList ports{EzGraver::availablePorts()};
    ports.insert(0, "");

    QString original{_ui->ports->currentText()};
    _ui->ports->clear();
    _ui->ports->addItems(ports);

    if(ports.contains(original)) {
        _ui->ports->setCurrentText(original);
    }
}

void MainWindow::_loadImage(QString const& fileName) {
    _printVerbose(QString{"loading image: %1"}.arg(fileName));

    QImage image{};
    if(!image.load(fileName)) {
        _printVerbose("failed to load image");
        return;
    }

    _ui->image->setImage(image);
}

bool MainWindow::connected() const {
    return _connected;
}

void MainWindow::_setConnected(bool connected) {
    _connected = connected;
    emit connectedChanged(connected);
}

void MainWindow::bytesWritten(qint64 bytes) {
    _bytesWrittenProcessor(bytes);
}

void MainWindow::updateProgress(qint64 bytes) {
    qDebug() << "Bytes written:" << bytes;
    auto progress = _ui->progress->value() + bytes;
    _ui->progress->setValue(progress);
    if(progress >= _ui->progress->maximum()) {
        _printVerbose("upload completed");
        _bytesWrittenProcessor = [](qint64){};
    }
}

void MainWindow::on_connect_clicked() {
    try {
        _printVerbose(QString{"connecting to port %1"}.arg(_ui->ports->currentText()));
        _ezGraver = Ez::create(_ui->ports->currentText(), _ui->protocolVersion->currentData().toInt());
        _printVerbose("connection established successfully");
        _setConnected(true);

        connect(_ezGraver->serialPort().get(), &QSerialPort::bytesWritten, this, &MainWindow::bytesWritten);
    } catch(std::runtime_error const& e) {
        _printVerbose(QString{"Error: %1"}.arg(e.what()));
    }
}

void MainWindow::on_home_clicked() {
    _printVerbose("moving to home");
    _ezGraver->home();
}

void MainWindow::on_up_clicked() {
    _ezGraver->up();
}

void MainWindow::on_left_clicked() {
    _ezGraver->left();
}

void MainWindow::on_center_clicked() {
    _printVerbose("moving to center");
    _ezGraver->center();
}

void MainWindow::on_right_clicked() {
    _ezGraver->right();
}

void MainWindow::on_down_clicked() {
    _ezGraver->down();
}

void MainWindow::on_upload_clicked() {
    _printVerbose("erasing EEPROM");
    _ezGraver->erase();

    QImage image{_ui->image->pixmap()->toImage()};
    QTimer* eraseProgressTimer{new QTimer{this}};
    _ui->progress->setValue(0);
    _ui->progress->setMaximum(EzGraver::EraseTimeMs);

    auto eraseProgress = std::bind(&MainWindow::_eraseProgressed, this, eraseProgressTimer, image);
    connect(eraseProgressTimer, &QTimer::timeout, eraseProgress);
    eraseProgressTimer->start(EraseProgressDelay);
}

void MainWindow::_eraseProgressed(QTimer* eraseProgressTimer, QImage const& image) {
    auto value = _ui->progress->value() + EraseProgressDelay;
    _ui->progress->setValue(value);
    if(value < EzGraver::EraseTimeMs) {
        return;
    }
    eraseProgressTimer->stop();

    _uploadImage(image);
}

void MainWindow::_uploadImage(QImage const& image) {
    _bytesWrittenProcessor = std::bind(&MainWindow::updateProgress, this, std::placeholders::_1);
    _printVerbose("uploading image to EEPROM");
    auto bytes = _ezGraver->uploadImage(image);
    _ui->progress->setValue(0);
    _ui->progress->setMaximum(bytes);
}

void MainWindow::on_preview_clicked() {
    _printVerbose("drawing preview");
    _ezGraver->preview();
}

void MainWindow::on_start_clicked() {
    _printVerbose(QString{"starting engrave process with burn time %1"}.arg(_ui->burnTime->value()));
    _ezGraver->start(_ui->burnTime->value());
}

void MainWindow::on_pause_clicked() {
    _printVerbose("pausing engrave process");
    _ezGraver->pause();
}

void MainWindow::on_reset_clicked() {
    _printVerbose("resetting engraver");
    _ezGraver->reset();
}

void MainWindow::on_disconnect_clicked() {
    _printVerbose("disconnecting");
    _setConnected(false);
    _ezGraver.reset();
    _printVerbose("disconnected");
}

void MainWindow::on_image_clicked() {
    auto fileName = QFileDialog::getOpenFileName(this, "Open Image", "", "Images (*.png *.jpeg *.jpg *.bmp)");
    if(!fileName.isNull()) {
        _loadImage(fileName);
    }
}

void MainWindow::dragEnterEvent(QDragEnterEvent* event) {
    if(event->mimeData()->hasUrls() && event->mimeData()->urls().count() == 1) {
        event->acceptProposedAction();
    }
}

void MainWindow::dropEvent(QDropEvent* event) {
    QString fileName{event->mimeData()->urls()[0].toLocalFile()};
    _loadImage(fileName);
}

<commit_msg>Printing selected protocol version to verbose console.<commit_after>#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QTimer>
#include <QMimeData>
#include <QFileDialog>
#include <QBitmap>
#include <QIcon>
#include <QThreadPool>
#include <QDebug>

#include <stdexcept>
#include <algorithm>

#include "ezgraver_factory.h"

using namespace Ez;

MainWindow::MainWindow(QWidget* parent)
        :  QMainWindow{parent}, _ui{new Ui::MainWindow},
          _portTimer{}, _image{}, _ezGraver{}, _bytesWrittenProcessor{[](qint64){}}, _connected{false} {
    _ui->setupUi(this);
    setAcceptDrops(true);

    connect(&_portTimer, &QTimer::timeout, this, &MainWindow::updatePorts);
    _portTimer.start(PortUpdateDelay);

    _initBindings();
    _initConversionFlags();
    _initProtocols();
    _setConnected(false);

    _ui->image->setImageDimensions(QSize{EzGraver::ImageWidth, EzGraver::ImageHeight});
}

MainWindow::~MainWindow() {
    delete _ui;
}

void MainWindow::_initBindings() {
    connect(_ui->burnTime, &QSlider::valueChanged, [this](int const& v) { _ui->burnTimeLabel->setText(QString::number(v)); });

    connect(this, &MainWindow::connectedChanged, _ui->ports, &QComboBox::setDisabled);
    connect(this, &MainWindow::connectedChanged, _ui->connect, &QPushButton::setDisabled);
    connect(this, &MainWindow::connectedChanged, _ui->disconnect, &QPushButton::setEnabled);

    connect(this, &MainWindow::connectedChanged, _ui->home, &QPushButton::setEnabled);
    connect(this, &MainWindow::connectedChanged, _ui->up, &QPushButton::setEnabled);
    connect(this, &MainWindow::connectedChanged, _ui->left, &QPushButton::setEnabled);
    connect(this, &MainWindow::connectedChanged, _ui->center, &QPushButton::setEnabled);
    connect(this, &MainWindow::connectedChanged, _ui->right, &QPushButton::setEnabled);
    connect(this, &MainWindow::connectedChanged, _ui->down, &QPushButton::setEnabled);
    connect(this, &MainWindow::connectedChanged, _ui->preview, &QPushButton::setEnabled);
    connect(this, &MainWindow::connectedChanged, _ui->start, &QPushButton::setEnabled);
    connect(this, &MainWindow::connectedChanged, _ui->pause, &QPushButton::setEnabled);
    connect(this, &MainWindow::connectedChanged, _ui->reset, &QPushButton::setEnabled);
    connect(_ui->conversionFlags, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), [this](int index) {
        _ui->image->setConversionFlags(static_cast<Qt::ImageConversionFlags>(_ui->conversionFlags->itemData(index).toInt()));
    });

    connect(_ui->layered, &QCheckBox::toggled, _ui->selectedLayer, &QSpinBox::setEnabled);
    connect(_ui->layered, &QCheckBox::toggled, _ui->layerCount, &QSpinBox::setEnabled);
    connect(_ui->layered, &QCheckBox::toggled, _ui->image, &ImageLabel::setGrayscale);
    connect(_ui->layerCount, static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged), _ui->image, &ImageLabel::setLayerCount);
    connect(_ui->layerCount, static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged), _ui->selectedLayer, &QSpinBox::setMaximum);
    connect(_ui->selectedLayer, static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged), _ui->image, &ImageLabel::setLayer);

    auto uploadEnabled = [this] {
        _ui->upload->setEnabled(_ui->image->imageLoaded() && _connected && (!_ui->layered->isChecked() || _ui->selectedLayer->value() > 0));
    };
    connect(this, &MainWindow::connectedChanged, uploadEnabled);
    connect(_ui->image, &ImageLabel::imageLoadedChanged, uploadEnabled);
    connect(_ui->selectedLayer, static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged), uploadEnabled);
    connect(_ui->layered, &QCheckBox::toggled, uploadEnabled);

    connect(_ui->keepAspectRatio, &QCheckBox::toggled, _ui->image, &ImageLabel::setKeepAspectRatio);

    connect(_ui->scaled, &QCheckBox::toggled, _ui->imageScale, &QSlider::setEnabled);
    connect(_ui->scaled, &QCheckBox::toggled, _ui->resetImageScale, &QSlider::setEnabled);
    connect(_ui->scaled, &QCheckBox::toggled, _ui->image, &ImageLabel::setScaled);
    connect(_ui->imageScale, &QSlider::valueChanged, [this](int const& v) { _ui->imageScaleLabel->setText(QString::number(v)); });
    connect(_ui->imageScale, &QSlider::valueChanged, [this](int const& v) { _ui->image->setImageScale(v / 100.0); });
    connect(_ui->resetImageScale, &QPushButton::clicked, [this] { _ui->imageScale->setValue(100); });

    connect(this, &MainWindow::connectedChanged, _ui->protocolVersion, &QComboBox::setDisabled);
}

void MainWindow::_initConversionFlags() {
    _ui->conversionFlags->addItem("DiffuseDither", Qt::DiffuseDither);
    _ui->conversionFlags->addItem("OrderedDither", Qt::OrderedDither);
    _ui->conversionFlags->addItem("ThresholdDither", Qt::ThresholdDither);
    _ui->conversionFlags->setCurrentIndex(0);
}

void MainWindow::_initProtocols() {
    for(auto protocol : Ez::protocols()) {
        _ui->protocolVersion->addItem(QString{"v%1"}.arg(protocol), protocol);
    }
}

void MainWindow::_printVerbose(QString const& verbose) {
    _ui->verbose->appendPlainText(verbose);
}

void MainWindow::updatePorts() {
    QStringList ports{EzGraver::availablePorts()};
    ports.insert(0, "");

    QString original{_ui->ports->currentText()};
    _ui->ports->clear();
    _ui->ports->addItems(ports);

    if(ports.contains(original)) {
        _ui->ports->setCurrentText(original);
    }
}

void MainWindow::_loadImage(QString const& fileName) {
    _printVerbose(QString{"loading image: %1"}.arg(fileName));

    QImage image{};
    if(!image.load(fileName)) {
        _printVerbose("failed to load image");
        return;
    }

    _ui->image->setImage(image);
}

bool MainWindow::connected() const {
    return _connected;
}

void MainWindow::_setConnected(bool connected) {
    _connected = connected;
    emit connectedChanged(connected);
}

void MainWindow::bytesWritten(qint64 bytes) {
    _bytesWrittenProcessor(bytes);
}

void MainWindow::updateProgress(qint64 bytes) {
    qDebug() << "Bytes written:" << bytes;
    auto progress = _ui->progress->value() + bytes;
    _ui->progress->setValue(progress);
    if(progress >= _ui->progress->maximum()) {
        _printVerbose("upload completed");
        _bytesWrittenProcessor = [](qint64){};
    }
}

void MainWindow::on_connect_clicked() {
    try {
        auto protocol = _ui->protocolVersion->currentData().toInt();
        _printVerbose(QString{"connecting to port %1 with protocol version %2"}.arg(_ui->ports->currentText()).arg(protocol));
        _ezGraver = Ez::create(_ui->ports->currentText(), protocol);
        _printVerbose("connection established successfully");
        _setConnected(true);

        connect(_ezGraver->serialPort().get(), &QSerialPort::bytesWritten, this, &MainWindow::bytesWritten);
    } catch(std::runtime_error const& e) {
        _printVerbose(QString{"Error: %1"}.arg(e.what()));
    }
}

void MainWindow::on_home_clicked() {
    _printVerbose("moving to home");
    _ezGraver->home();
}

void MainWindow::on_up_clicked() {
    _ezGraver->up();
}

void MainWindow::on_left_clicked() {
    _ezGraver->left();
}

void MainWindow::on_center_clicked() {
    _printVerbose("moving to center");
    _ezGraver->center();
}

void MainWindow::on_right_clicked() {
    _ezGraver->right();
}

void MainWindow::on_down_clicked() {
    _ezGraver->down();
}

void MainWindow::on_upload_clicked() {
    _printVerbose("erasing EEPROM");
    _ezGraver->erase();

    QImage image{_ui->image->pixmap()->toImage()};
    QTimer* eraseProgressTimer{new QTimer{this}};
    _ui->progress->setValue(0);
    _ui->progress->setMaximum(EzGraver::EraseTimeMs);

    auto eraseProgress = std::bind(&MainWindow::_eraseProgressed, this, eraseProgressTimer, image);
    connect(eraseProgressTimer, &QTimer::timeout, eraseProgress);
    eraseProgressTimer->start(EraseProgressDelay);
}

void MainWindow::_eraseProgressed(QTimer* eraseProgressTimer, QImage const& image) {
    auto value = _ui->progress->value() + EraseProgressDelay;
    _ui->progress->setValue(value);
    if(value < EzGraver::EraseTimeMs) {
        return;
    }
    eraseProgressTimer->stop();

    _uploadImage(image);
}

void MainWindow::_uploadImage(QImage const& image) {
    _bytesWrittenProcessor = std::bind(&MainWindow::updateProgress, this, std::placeholders::_1);
    _printVerbose("uploading image to EEPROM");
    auto bytes = _ezGraver->uploadImage(image);
    _ui->progress->setValue(0);
    _ui->progress->setMaximum(bytes);
}

void MainWindow::on_preview_clicked() {
    _printVerbose("drawing preview");
    _ezGraver->preview();
}

void MainWindow::on_start_clicked() {
    _printVerbose(QString{"starting engrave process with burn time %1"}.arg(_ui->burnTime->value()));
    _ezGraver->start(_ui->burnTime->value());
}

void MainWindow::on_pause_clicked() {
    _printVerbose("pausing engrave process");
    _ezGraver->pause();
}

void MainWindow::on_reset_clicked() {
    _printVerbose("resetting engraver");
    _ezGraver->reset();
}

void MainWindow::on_disconnect_clicked() {
    _printVerbose("disconnecting");
    _setConnected(false);
    _ezGraver.reset();
    _printVerbose("disconnected");
}

void MainWindow::on_image_clicked() {
    auto fileName = QFileDialog::getOpenFileName(this, "Open Image", "", "Images (*.png *.jpeg *.jpg *.bmp)");
    if(!fileName.isNull()) {
        _loadImage(fileName);
    }
}

void MainWindow::dragEnterEvent(QDragEnterEvent* event) {
    if(event->mimeData()->hasUrls() && event->mimeData()->urls().count() == 1) {
        event->acceptProposedAction();
    }
}

void MainWindow::dropEvent(QDropEvent* event) {
    QString fileName{event->mimeData()->urls()[0].toLocalFile()};
    _loadImage(fileName);
}

<|endoftext|>


<commit_before>#include "hornet/java.hh"

#include "hornet/vm.hh"

#include <cassert>
#include <stack>

#include <classfile_constants.h>
#include <jni.h>

namespace hornet {

value_t object_to_value(object* obj)
{
    return reinterpret_cast<value_t>(obj);
}

jint value_to_jint(value_t value)
{
    return static_cast<jint>(value);
}

jlong value_to_jlong(value_t value)
{
   return static_cast<jlong>(value);
}

value_t jint_to_value(jint n)
{
    return static_cast<value_t>(n);
}

value_t jlong_to_value(jlong n)
{
    return static_cast<value_t>(n);
}

#define CONST_INTERP(type, value)                               \
    do {                                                        \
        frame.ostack.push(type##_to_value(value));              \
    } while (0)

#define LOAD_INTERP(type, idx)                                  \
    do {                                                        \
        frame.ostack.push(frame.locals[idx]);                   \
    } while (0)

#define STORE_INTERP(type, idx)                                 \
    do {                                                        \
        frame.locals[idx] = frame.ostack.top();                 \
        frame.ostack.pop();                                     \
    } while (0)

#define BINOP_INTERP(type, op)                                  \
    do {                                                        \
        auto value2 = value_to_##type(frame.ostack.top());      \
        frame.ostack.pop();                                     \
        auto value1 = value_to_##type(frame.ostack.top());      \
        frame.ostack.pop();                                     \
        type result = value1 op value2;                         \
        frame.ostack.push(type##_to_value(result));             \
   } while (0)

void interp(method* method, frame& frame)
{
next_insn:
    assert(frame.pc < method->code_length);

    uint8_t opc = method->code[frame.pc];

    switch (opc) {
    case JVM_OPC_iconst_m1:
    case JVM_OPC_iconst_0:
    case JVM_OPC_iconst_1:
    case JVM_OPC_iconst_2:
    case JVM_OPC_iconst_3:
    case JVM_OPC_iconst_4:
    case JVM_OPC_iconst_5: {
        jint value = opc - JVM_OPC_iconst_0;
        CONST_INTERP(jint, value);
        break;
    }
    case JVM_OPC_lconst_0:
    case JVM_OPC_lconst_1: {
        jlong value = opc - JVM_OPC_lconst_0;
        CONST_INTERP(jlong, value);
        break;
    }
    case JVM_OPC_iload_0:
    case JVM_OPC_iload_1:
    case JVM_OPC_iload_2:
    case JVM_OPC_iload_3: {
        uint16_t idx = opc - JVM_OPC_iload_0;
        LOAD_INTERP(jint, idx);
        break;
    }
    case JVM_OPC_lload_0:
    case JVM_OPC_lload_1:
    case JVM_OPC_lload_2:
    case JVM_OPC_lload_3: {
        uint16_t idx = opc - JVM_OPC_lload_0;
        LOAD_INTERP(jlong, idx);
        break;
    }
    case JVM_OPC_aload_0:
    case JVM_OPC_aload_1:
    case JVM_OPC_aload_2:
    case JVM_OPC_aload_3: {
        uint16_t idx = opc - JVM_OPC_aload_0;
        LOAD_INTERP(jobject, idx);
        break;
    }
    case JVM_OPC_istore: {
        auto idx = read_opc_u1(method->code + frame.pc);
        STORE_INTERP(jint, idx);
        break;
    }
    case JVM_OPC_lstore: {
        auto idx = read_opc_u1(method->code + frame.pc);
        STORE_INTERP(jlong, idx);
        break;
    }
    case JVM_OPC_istore_0:
    case JVM_OPC_istore_1:
    case JVM_OPC_istore_2:
    case JVM_OPC_istore_3: {
        uint16_t idx = opc - JVM_OPC_istore_0;
        STORE_INTERP(jint, idx);
        break;
    }
    case JVM_OPC_lstore_0:
    case JVM_OPC_lstore_1:
    case JVM_OPC_lstore_2:
    case JVM_OPC_lstore_3: {
        uint16_t idx = opc - JVM_OPC_lstore_0;
        STORE_INTERP(jlong, idx);
        break;
    }
    case JVM_OPC_pop: {
        frame.ostack.pop();
        break;
    }
    case JVM_OPC_dup: {
        auto value = frame.ostack.top();
        frame.ostack.push(value);
        break;
    }
    case JVM_OPC_iadd: {
        BINOP_INTERP(jint, +);
        break;
    }
    case JVM_OPC_ladd: {
        BINOP_INTERP(jlong, +);
        break;
    }
    case JVM_OPC_isub: {
        BINOP_INTERP(jint, -);
        break;
    }
    case JVM_OPC_lsub: {
        BINOP_INTERP(jlong, -);
        break;
    }
    case JVM_OPC_imul: {
        BINOP_INTERP(jint, *);
        break;
    }
    case JVM_OPC_lmul: {
        BINOP_INTERP(jlong, *);
        break;
    }
    case JVM_OPC_idiv: {
        BINOP_INTERP(jint, /);
        break;
    }
    case JVM_OPC_ldiv: {
        BINOP_INTERP(jlong, /);
        break;
    }
    case JVM_OPC_irem: {
        BINOP_INTERP(jint, %);
        break;
    }
    case JVM_OPC_lrem: {
        BINOP_INTERP(jlong, %);
        break;
    }
    case JVM_OPC_ishl: {
        auto value2 = value_to_jint(ostack.top());
        ostack.pop();
        auto value1 = value_to_jint(ostack.top());
        ostack.pop();
        jint result = value1 << (value2 & 0x1f);
        ostack.push(jint_to_value(result));
        break;
    }
    case JVM_OPC_iand: {
        BINOP_INTERP(jint, &);
        break;
    }
    case JVM_OPC_land: {
        BINOP_INTERP(jlong, &);
        break;
    }
    case JVM_OPC_ior: {
        BINOP_INTERP(jint, |);
        break;
    }
    case JVM_OPC_lor: {
        BINOP_INTERP(jlong, |);
        break;
    }
    case JVM_OPC_ixor: {
        BINOP_INTERP(jint, ^);
        break;
    }
    case JVM_OPC_lxor: {
        BINOP_INTERP(jlong, ^);
        break;
    }
    case JVM_OPC_goto: {
        int16_t offset = read_opc_u2(method->code + frame.pc);
        frame.pc += offset;
        goto next_insn;
    }
    case JVM_OPC_return: {
        frame.ostack.empty();
        return;
    }
    case JVM_OPC_invokespecial: {
        break;
    }
    case JVM_OPC_new: {
        auto obj = gc_new_object(nullptr);
        frame.ostack.push(object_to_value(obj));
        break;
    }
    default:
        fprintf(stderr, "error: unsupported bytecode: %u\n", opc);
        abort();
    }

    frame.pc += opcode_length[opc];

    goto next_insn;
}

}
<commit_msg>interp: lshl<commit_after>#include "hornet/java.hh"

#include "hornet/vm.hh"

#include <cassert>
#include <stack>

#include <classfile_constants.h>
#include <jni.h>

namespace hornet {

value_t object_to_value(object* obj)
{
    return reinterpret_cast<value_t>(obj);
}

jint value_to_jint(value_t value)
{
    return static_cast<jint>(value);
}

jlong value_to_jlong(value_t value)
{
   return static_cast<jlong>(value);
}

value_t jint_to_value(jint n)
{
    return static_cast<value_t>(n);
}

value_t jlong_to_value(jlong n)
{
    return static_cast<value_t>(n);
}

#define CONST_INTERP(type, value)                               \
    do {                                                        \
        frame.ostack.push(type##_to_value(value));              \
    } while (0)

#define LOAD_INTERP(type, idx)                                  \
    do {                                                        \
        frame.ostack.push(frame.locals[idx]);                   \
    } while (0)

#define STORE_INTERP(type, idx)                                 \
    do {                                                        \
        frame.locals[idx] = frame.ostack.top();                 \
        frame.ostack.pop();                                     \
    } while (0)

#define BINOP_INTERP(type, op)                                  \
    do {                                                        \
        auto value2 = value_to_##type(frame.ostack.top());      \
        frame.ostack.pop();                                     \
        auto value1 = value_to_##type(frame.ostack.top());      \
        frame.ostack.pop();                                     \
        type result = value1 op value2;                         \
        frame.ostack.push(type##_to_value(result));             \
   } while (0)

#define SHL_INTERP(type, mask)                                  \
    do {                                                        \
        auto value2 = value_to_jint(frame.ostack.top());        \
        frame.ostack.pop();                                     \
        auto value1 = value_to_##type(frame.ostack.top());      \
        frame.ostack.pop();                                     \
        type result = value1 << (value2 & mask);                \
        frame.ostack.push(type##_to_value(result));             \
    } while (0)

void interp(method* method, frame& frame)
{
next_insn:
    assert(frame.pc < method->code_length);

    uint8_t opc = method->code[frame.pc];

    switch (opc) {
    case JVM_OPC_iconst_m1:
    case JVM_OPC_iconst_0:
    case JVM_OPC_iconst_1:
    case JVM_OPC_iconst_2:
    case JVM_OPC_iconst_3:
    case JVM_OPC_iconst_4:
    case JVM_OPC_iconst_5: {
        jint value = opc - JVM_OPC_iconst_0;
        CONST_INTERP(jint, value);
        break;
    }
    case JVM_OPC_lconst_0:
    case JVM_OPC_lconst_1: {
        jlong value = opc - JVM_OPC_lconst_0;
        CONST_INTERP(jlong, value);
        break;
    }
    case JVM_OPC_iload_0:
    case JVM_OPC_iload_1:
    case JVM_OPC_iload_2:
    case JVM_OPC_iload_3: {
        uint16_t idx = opc - JVM_OPC_iload_0;
        LOAD_INTERP(jint, idx);
        break;
    }
    case JVM_OPC_lload_0:
    case JVM_OPC_lload_1:
    case JVM_OPC_lload_2:
    case JVM_OPC_lload_3: {
        uint16_t idx = opc - JVM_OPC_lload_0;
        LOAD_INTERP(jlong, idx);
        break;
    }
    case JVM_OPC_aload_0:
    case JVM_OPC_aload_1:
    case JVM_OPC_aload_2:
    case JVM_OPC_aload_3: {
        uint16_t idx = opc - JVM_OPC_aload_0;
        LOAD_INTERP(jobject, idx);
        break;
    }
    case JVM_OPC_istore: {
        auto idx = read_opc_u1(method->code + frame.pc);
        STORE_INTERP(jint, idx);
        break;
    }
    case JVM_OPC_lstore: {
        auto idx = read_opc_u1(method->code + frame.pc);
        STORE_INTERP(jlong, idx);
        break;
    }
    case JVM_OPC_istore_0:
    case JVM_OPC_istore_1:
    case JVM_OPC_istore_2:
    case JVM_OPC_istore_3: {
        uint16_t idx = opc - JVM_OPC_istore_0;
        STORE_INTERP(jint, idx);
        break;
    }
    case JVM_OPC_lstore_0:
    case JVM_OPC_lstore_1:
    case JVM_OPC_lstore_2:
    case JVM_OPC_lstore_3: {
        uint16_t idx = opc - JVM_OPC_lstore_0;
        STORE_INTERP(jlong, idx);
        break;
    }
    case JVM_OPC_pop: {
        frame.ostack.pop();
        break;
    }
    case JVM_OPC_dup: {
        auto value = frame.ostack.top();
        frame.ostack.push(value);
        break;
    }
    case JVM_OPC_iadd: {
        BINOP_INTERP(jint, +);
        break;
    }
    case JVM_OPC_ladd: {
        BINOP_INTERP(jlong, +);
        break;
    }
    case JVM_OPC_isub: {
        BINOP_INTERP(jint, -);
        break;
    }
    case JVM_OPC_lsub: {
        BINOP_INTERP(jlong, -);
        break;
    }
    case JVM_OPC_imul: {
        BINOP_INTERP(jint, *);
        break;
    }
    case JVM_OPC_lmul: {
        BINOP_INTERP(jlong, *);
        break;
    }
    case JVM_OPC_idiv: {
        BINOP_INTERP(jint, /);
        break;
    }
    case JVM_OPC_ldiv: {
        BINOP_INTERP(jlong, /);
        break;
    }
    case JVM_OPC_irem: {
        BINOP_INTERP(jint, %);
        break;
    }
    case JVM_OPC_lrem: {
        BINOP_INTERP(jlong, %);
        break;
    }
    case JVM_OPC_ishl: {
        SHL_INTERP(jint, 0x1f);
        break;
    }
    case JVM_OPC_lshl: {
        SHL_INTERP(jlong, 0x3f);
        break;
    }
    case JVM_OPC_iand: {
        BINOP_INTERP(jint, &);
        break;
    }
    case JVM_OPC_land: {
        BINOP_INTERP(jlong, &);
        break;
    }
    case JVM_OPC_ior: {
        BINOP_INTERP(jint, |);
        break;
    }
    case JVM_OPC_lor: {
        BINOP_INTERP(jlong, |);
        break;
    }
    case JVM_OPC_ixor: {
        BINOP_INTERP(jint, ^);
        break;
    }
    case JVM_OPC_lxor: {
        BINOP_INTERP(jlong, ^);
        break;
    }
    case JVM_OPC_goto: {
        int16_t offset = read_opc_u2(method->code + frame.pc);
        frame.pc += offset;
        goto next_insn;
    }
    case JVM_OPC_return: {
        frame.ostack.empty();
        return;
    }
    case JVM_OPC_invokespecial: {
        break;
    }
    case JVM_OPC_new: {
        auto obj = gc_new_object(nullptr);
        frame.ostack.push(object_to_value(obj));
        break;
    }
    default:
        fprintf(stderr, "error: unsupported bytecode: %u\n", opc);
        abort();
    }

    frame.pc += opcode_length[opc];

    goto next_insn;
}

}
<|endoftext|>


<commit_before>//********************************************************************
//
// JESUS HILARIO HERNANDEZ
// Course: COSC 1436.001 Programming Fundamentals 1
// Lab # 6 Temperature Functions//  Celsius to Fahrenheit extended
// Due Date: October 5, 2016
// Instructor: Korinne Caruso
//
// PURPOSE: This program converts Celsius to Fahrenheit or
// Fahrenheit to Celsius. Functions will be used within the program to
// display a menu, convert C to F, convert F to C, display results,
// send results to an external text file, and to handle error checking
//
// INPUT: Input will come from the user, through the (cin) object.
// The information the user provides will either be stored
// in a double, char, or an int. Further, input validation will
// occur each time the user provides information at the keyboard.
// If invalid input should occur, the user will be redirected through
// a while loop, to input only valid information.
//
// OUTPUT: All output will be sent using the (cout) object.
// A brief description of what
// the program does will be displayed when the program is run.
// A menu with three options will be provided for the user
// to choose from. At one point in the program,
// the user will be given the option to continue the
// program, end the program, or quit the program.
//
//********************************************************************

#include <iostream>
#include <iomanip>
#include <fstream>
using namespace std;

ofstream outputFile;

// Global Constants
const int CHOICE_1 = 1,
CHOICE_2 = 2,
CHOICE_3 = 3;

// Function Prototypes
void displayMenu();                             // Display menu
double convertCtoF(double, double);             // C to F Conversion
double convertFtoC(double, double);             // F to C Conversion
void displayResults(double, double);                          // Display result
void sendResults(int, double, double);                    // Send results to .txt file
int errorChecking(int);   // Error checking Handler

int main()
{
    outputFile.open("results.txt");
    // Variables:
    double C, // Holds Celsius Temp
    F; // Holds Fahrenheit Temp
    int choice, switchChoice; //Holds Menu choice
    char choice2; // Hold (Y/N) Ans.

    cout << "\n\n\t\tThis program converts Celsius to fahrenheit "
    << "\n\t\tor Fahrenheit to Celsius." << endl;

    // Do-While menu
    do
    {
        // Display Menu
        displayMenu();

        // Error checking menu choice
        switchChoice = errorChecking(choice);

        // Switch menu
        switch (switchChoice)
        {
                // Choice 1
            case CHOICE_1:
                // Ask for Celsius temp.
                cout << "\n\t\tWhat is the Celsius temperature "
                << "\n\t\tthat you would wish to convert? ";

                // Error check choice 1
                while (!(cin >> C))
                {
                    // Explain Error
                    cout << "\n\t\tError: A number must be entered. "
                    << "\n\t\tWhat is the Celsius temperature? ";
                    // Clear input stream
                    cin.clear();
                    // Discard previous input
                    cin.ignore(1200, '\n');
                }

                // Calculate C to F
                F = convertCtoF(F = 0, C);

                // Display C to F conversion
                displayResults(C, F);

                // Send Results to results.txt
                sendResults(switchChoice, C, F);

                // Asking if user would like to make another conversion
                cout << "\n\t\tWould you like to make another conversion?" << endl;
                cout << "\t\tY: if yes" << endl;
                cout << "\t\tN: if no \n\t\t";
                cin >> choice2;

                // Validating user input, whether y or n is pressed.
                while(!(choice2 == 'y' || choice2 == 'Y' || choice2 == 'n' || choice2 == 'N'))
                {
                    cout << "Error: choose either Y or N." << endl;
                    // Clear input stream
                    cin.clear();
                    // Discard previous input
                    cin.ignore(1200, '\n');

                    cin >> choice2;
                }
                // If pressed Y, acknowledge that they did
                if (choice2 == 'y' || choice2 == 'Y')
                {
                    cout << "\n\n\t\tOk. " ;

                }
                // If pressed N, acknowledge that they did
                else if (choice2 == 'n' || choice2 == 'N')
                {
                    cout << "\n\nThank you! Have a nice day.\n\n" << endl;
                    cout << "\n\n\t\tThis program converts Celsius to fahrenheit "
                    << "\n\t\tor Fahrenheit to Celsius." << endl;

                }
                break; // End of case 1

                // Choice 2
            case CHOICE_2:
                cout << "\n\t\tWhat is the Fahrenheit temperature " << endl;
                cout << "\t\tthat you would wish to convert? ";

                // Error check choice 2
                while(!(cin >> F))
                {
                    // Explain Error
                    cout << "\n\t\tError: A number must be entered. "
                    << "\n\t\tWhat is the Fahrenheit temperature? ";
                    // Clear input stream
                    cin.clear();
                    // Discard previous input
                    cin.ignore(1200, '\n');
                }

                // Calculate F to C
                C = convertFtoC(C = 0, F);

                // Display F to C conversion
                displayResults(F, C);

                // Send Results to results.txt
                sendResults(switchChoice, F, C);

                // Asking if user would like to make another conversion
                cout << "\n\t\tWould you like to make another conversion?" << endl;
                cout << "\t\tY: if yes" << endl;
                cout << "\t\tN: if no \n\t\t";
                cin >> choice2;

                // Validating user input, whether y or n is pressed.
                while(!(choice2 == 'y' || choice2 == 'Y' || choice2 == 'n' || choice2 == 'N'))
                {
                    cout << "Error: choose either Y or N." << endl;
                    // Clear input stream
                    cin.clear();
                    // Discard previous input
                    cin.ignore(1200, '\n');

                    cin >> choice2;
                }

                // If Y pressed, acknowledge that they did
                if (choice2 == 'y')
                {
                    cout << "\n\n\t\tOk. " ;

                }
                // If N pressed, acknowledge that they did
                else if (choice2 == 'n')
                {
                    cout << "\n\nThank you! Have a nice day.\n\n" << endl;
                    cout << "\n\n\t\tThis program converts Celsius to fahrenheit "
                    << "\n\t\tor Fahrenheit to Celsius." << endl;

                }
                break; // End of case 2

            case CHOICE_3 :
                // Let user know that they have chosen to quit the program.
                cout << "\n\t\tQuitting program..." << endl;
                cout << "\n\t\tProgram Ended." << endl;
                break; // End of case 3

        }   // End Switch menu

    } while (switchChoice == 1 || switchChoice == 2);// End Do-While menu.

    // For padding before the 0 exit code.
    cout << endl << endl;

    outputFile.close();

    return 0;
}

// Display menu
void displayMenu()
{
    cout << "\n\t\tHere are your options: \n"
    << "\t\t\t1. Convert Celsius to Fahrenheit" << endl
    << "\t\t\t2. Convert Fahrenheit to Celsius" << endl
    << "\t\t\t3. Quit Program\t\t\t\t";
}

// C to F Conversion
double convertCtoF(double num1, double num2)
{
    num1 = (9.0/5.0) * num2 + 32;
    return num1;
}

// F to C Conversion
double convertFtoC(double num1, double num2)
{
    num1 = (num2 - 32) * (5.0/9.0);
    return num1;
}

// Display result
void displayResults(double num1, double num2)
{
    cout << setprecision(1) << fixed;
    cout << "\n\n\t\t\t"<< num1 << " = "
         << num2 << endl;
    cout << endl;
}

// Send to results.txt file
void sendResults(int num, double num1, double num2)
{
    if (CHOICE_1 == num)
    {
        outputFile << setprecision(1) << setw(10) << num1 << setw(10) << " Celsius    = "
                   << num2 << " Fahrenheit" << endl;
    }
    else if (CHOICE_2 == num)
    {
        outputFile << setprecision(1) << setw(10) << num1 << setw(10) << " Fahrenheit = "
                   << num2 << " Celsius" << endl;
    }
}

// Error checking Handler
int errorChecking(int num)
{
    while(!(cin >> num) || num < CHOICE_1 || num > CHOICE_3)
    {
        // Explain error
        cout << "\n\t\tError: You must choose either 1, 2, or 3. ";
        // Clear input stream
        cin.clear();
        // Discard previous input
        cin.ignore(1200, '\n');
    }
    return num;
}
<commit_msg>working through lab 6, I love it!.<commit_after>//********************************************************************
//
// JESUS HILARIO HERNANDEZ
// Course: COSC 1436.001 Programming Fundamentals 1
// Lab # 6 Temperature Functions//  Celsius to Fahrenheit extended
// Due Date: October 5, 2016
// Instructor: Korinne Caruso
//
// PURPOSE: This program converts Celsius to Fahrenheit or
// Fahrenheit to Celsius. Functions will be used within the program to
// display a menu, convert C to F, convert F to C, display results,
// send results to an external text file, and to handle error checking
//
// INPUT: Input will come from the user, through the (cin) object.
// The information the user provides will either be stored
// in a double, char, or an int. Further, input validation will
// occur each time the user provides information at the keyboard.
// If invalid input should occur, the user will be redirected through
// a while loop, to input only valid information.
//
// OUTPUT: All output will be sent using the (cout) object.
// A brief description of what
// the program does will be displayed when the program is run.
// A menu with three options will be provided for the user
// to choose from. At one point in the program,
// the user will be given the option to continue the
// program, end the program, or quit the program.
//
//********************************************************************

#include <iostream>
#include <iomanip>
#include <fstream>
using namespace std;

// File Stream Object
ofstream outputFile;

// Global Constants
const int CHOICE_1 = 1,
CHOICE_2 = 2,
CHOICE_3 = 3;

// Function Prototypes
void displayMenu();                      // Display menu
double convertCtoF(double, double);      // C to F Conversion
double convertFtoC(double, double);      // F to C Conversion
void displayResults(int, double, double);     // Display result
void sendResults(int, double, double);   // Send results to .txt file
int errorChecking1(int);                 // Error Checking Menu Choice
double errorChecking2(double);           // Error Checking For Numbers
char errorChecking3(char);               // Error Checking (Y/N)

int main()
{
    // Open results.txt file
    outputFile.open("results.txt");

    // Variables:
    double C,         // Holds Celsius Temp
    F;         // Holds Fahrenheit Temp
    int choice; //Holds Menu choice
    char choice2;     // Hold (Y/N) Ans.

    cout << "\n\n\t\tThis program converts Celsius to fahrenheit "
    << "\n\t\tor Fahrenheit to Celsius." << endl;

    // Do-While menu
    do
    {
        // Display Menu
        displayMenu();

        // Error Check Menu Choice
        choice = errorChecking1(choice);

        // Switch menu
        switch (choice)
        {
                // Choice 1
            case CHOICE_1:
                // Ask for Celsius temp.
                cout << "\n\t\tWhat is the Celsius temperature "
                << "\n\t\tthat you would wish to convert? ";

                // Error Check For Numbers
                C = errorChecking2(C = 0);

                // Calculate C to F
                F = convertCtoF(F = 0, C);

                // Display C to F conversion
                displayResults(choice, C, F);

                // Send Results to results.txt
                sendResults(choice, C, F);

                // Asking if user would like to make another conversion
                cout << "\n\t\tWould you like to make another conversion?" << endl;
                cout << "\t\tY: if yes" << endl;
                cout << "\t\tN: if no \n\t\t";
                cin >> choice2;

                // Validating user input, whether y or n is pressed.
                choice2 = errorChecking3(choice2);

                // If pressed Y, acknowledge that they did
                if (choice2 == 'y' || choice2 == 'Y')
                {
                    cout << "\n\n\t\tOk. " ;

                }
                // If pressed N, acknowledge that they did
                else if (choice2 == 'n' || choice2 == 'N')
                {
                    cout << "\n\nThank you! Have a nice day.\n\n" << endl;
                    cout << "\n\n\t\tThis program converts Celsius to fahrenheit "
                    << "\n\t\tor Fahrenheit to Celsius." << endl;

                }
                break; // End of case 1

                // Choice 2
            case CHOICE_2:
                cout << "\n\t\tWhat is the Fahrenheit temperature " << endl;
                cout << "\t\tthat you would wish to convert? ";

                // Error check choice 2
                F = errorChecking2(F = 0);

                // Calculate F to C
                C = convertFtoC(C = 0, F);

                // Display F to C conversion
                displayResults(choice, F, C);

                // Send Results to results.txt
                sendResults(choice, F, C);

                // Asking if user would like to make another conversion
                cout << "\n\t\tWould you like to make another conversion?" << endl;
                cout << "\t\tY: if yes" << endl;
                cout << "\t\tN: if no \n\t\t";
                cin >> choice2;

                // Validating user input, whether y or n is pressed.
                choice2 = errorChecking3(choice2);

                // If Y pressed, acknowledge that they did
                if (choice2 == 'y')
                {
                    cout << "\n\n\t\tOk. " ;

                }
                // If N pressed, acknowledge that they did
                else if (choice2 == 'n')
                {
                    cout << "\n\nThank you! Have a nice day.\n\n" << endl;
                    cout << "\n\n\t\tThis program converts Celsius to fahrenheit "
                    << "\n\t\tor Fahrenheit to Celsius." << endl;

                }
                break; // End of case 2

            case CHOICE_3 :
                // Let user know that they have chosen to quit the program.
                cout << "\n\t\tQuitting program..." << endl;
                cout << "\n\t\tProgram Ended." << endl;
                break; // End of case 3

        }   // End Switch menu

    } while (choice == 1 || choice == 2);// End Do-While menu.

    // For padding before the 0 exit code.
    cout << endl << endl;

    outputFile.close();

    return 0;
}

// Display menu
void displayMenu()
{
    cout << "\n\t\tHere are your options: \n"
    << "\t\t\t1. Convert Celsius to Fahrenheit" << endl
    << "\t\t\t2. Convert Fahrenheit to Celsius" << endl
    << "\t\t\t3. Quit Program\t\t\t\t";
}

// C to F Conversion
double convertCtoF(double num1, double num2)
{
    num1 = (9.0/5.0) * num2 + 32;
    return num1;
}

// F to C Conversion
double convertFtoC(double num1, double num2)
{
    num1 = (num2 - 32) * (5.0/9.0);
    return num1;
}

// Display result
void displayResults(int num, double num1, double num2)
{
    if (CHOICE_1 == num)
    {
        cout << "\n\n\t\t" << setprecision(1) << fixed << num1 << " Celsius = "
        << num2 << " Fahrenheit" << endl;
    }
    else if (CHOICE_2 == num)
    {
        cout << "\n\n\t\t" << setprecision(1) << fixed << num1 << " Fahrenheit = "
        << num2 << " Celsius" << endl;
    }
}

// Send to results.txt file
void sendResults(int num, double num1, double num2)
{
    if (CHOICE_1 == num)
    {
        outputFile << setprecision(1) << fixed << num1 << " Celsius = "
        << num2 << " Fahrenheit" << endl;
    }
    else if (CHOICE_2 == num)
    {
        outputFile << setprecision(1) << fixed << num1 << " Fahrenheit = "
        << num2 << " Celsius" << endl;
    }

}

// Error Checking Choices
int errorChecking1(int num)
{
    while(!(cin >> num) || num < CHOICE_1 || num > CHOICE_3)
    {
        // Explain error
        cout << "\n\t\tError: You must choose either 1, 2, or 3. ";
        // Clear input stream
        cin.clear();
        // Discard previous input
        cin.ignore(1200, '\n');
    }
    return num;
}

// Error Check For Numbers
double errorChecking2(double num)
{
    while (!(cin >> num))
    {
        // Explain Error
        cout << "\n\t\tError: A number must be entered. "
        << "\n\t\tWhat is the Celsius temperature? ";
        // Clear input stream
        cin.clear();
        // Discard previous input
        cin.ignore(1200, '\n');
    }
    return num;
}
// Error Check (Y/N)
char errorChecking3(char letter)
{
    while(!(letter == 'y' || letter == 'Y' || letter == 'n' || letter == 'N'))
    {
        cout << "\n\t\tError: choose either Y or N.";
        // Clear input stream
        cin.clear();
        // Discard previous input
        cin.ignore(1200, '\n');

        cin >> letter;
    }
    return letter;
}
<|endoftext|>