<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2021.  All rights reserved
 */
#include "../../StroikaPreComp.h"

#if qHasFeature_OpenSSL
#include <openssl/evp.h>
#if OPENSSL_VERSION_MAJOR >= 3
#include <openssl/provider.h>
#endif
#endif

#include "../../Debug/Assertions.h"
#include "../../Execution/Exceptions.h"

#include "LibraryContext.h"

using namespace Stroika::Foundation;
using namespace Stroika::Foundation::Cryptography;
using namespace Stroika::Foundation::Cryptography::OpenSSL;

// Comment this in to turn on aggressive noisy DbgTrace in this module
//#define   USE_NOISY_TRACE_IN_THIS_MODULE_       1

#if qHasFeature_OpenSSL && defined(_MSC_VER)
// Use #pragma comment lib instead of explicit entry in the lib entry of the project file
#if OPENSSL_VERSION_NUMBER < 0x1010000fL
#pragma comment(lib, "libeay32.lib")
#pragma comment(lib, "ssleay32.lib")
#else
#pragma comment(lib, "libcrypto.lib")
#pragma comment(lib, "libssl.lib")
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "crypt32.lib")
#endif
#endif

#if qHasFeature_OpenSSL

namespace {
    void AccumulateIntoSetOfCipherNames_ (const ::EVP_CIPHER* ciph, Set<String>* ciphers)
    {
        RequireNotNull (ciphers);
        if (ciph != nullptr) {
#if USE_NOISY_TRACE_IN_THIS_MODULE_
#if OPENSSL_VERSION_MAJOR >= 3
            DbgTrace (L"cipher: %p (name: %s), provider: %p (name %s)", ciph,
                      CipherAlgorithm{ciph}.pName ().c_str (),
                      ::EVP_CIPHER_get0_provider (ciph),
                      (::EVP_CIPHER_get0_provider (ciph) == nullptr ? L"null" : String::FromNarrowSDKString (::OSSL_PROVIDER_get0_name (::EVP_CIPHER_get0_provider (ciph))).c_str ()));
#else
            DbgTrace (L"cipher: %p (name: %s)", ciph, CipherAlgorithm{ciph}.pName ().c_str ());
#endif
#if 0
            int flags = ::EVP_CIPHER_flags (ciph);
            DbgTrace ("flags=%x", flags);
#endif
#endif
            ciphers->Add (CipherAlgorithm{ciph}.pName ());
        }
    };
    void AccumulateIntoSetOfDigestNames_ (const ::EVP_MD* digest, Set<String>* digestNames)
    {
        RequireNotNull (digestNames);
        if (digest != nullptr) {
#if USE_NOISY_TRACE_IN_THIS_MODULE_
#if OPENSSL_VERSION_MAJOR >= 3
            DbgTrace (L"digest: %p (name: %s), provider: %p (name %s)",
                      digest, DigestAlgorithm{digest}.pName ().c_str (),
                      ::EVP_MD_get0_provider (digest),
                      (::EVP_MD_get0_provider (digest) == nullptr ? L"null" : String::FromNarrowSDKString (::OSSL_PROVIDER_get0_name (::EVP_MD_get0_provider (digest))).c_str ()));
#else
            DbgTrace (L"digest: %p (name: %s)", digest, DigestAlgorithm{digest}.pName ().c_str ());
#endif
#endif
            digestNames->Add (DigestAlgorithm{digest}.pName ());
        }
    };
}

/*
 ********************************************************************************
 ******************* Cryptography::OpenSSL::LibraryContext **********************
 ********************************************************************************
 */
#if qCompiler_cpp17InlineStaticMemberOfClassDoubleDeleteAtExit_Buggy
LibraryContext LibraryContext::sDefault;
#endif

LibraryContext::LibraryContext ()
    : pAvailableCipherAlgorithms{
          [qStroika_Foundation_Common_Property_ExtraCaptureStuff] ([[maybe_unused]] const auto* property) -> Set<CipherAlgorithm> {
              const LibraryContext*                                thisObj = qStroika_Foundation_Common_Property_OuterObjPtr (property, &LibraryContext::pAvailableCipherAlgorithms);
              shared_lock<const AssertExternallySynchronizedMutex> critSec{*thisObj};
              Set<String>                                          cipherNames;
#if OPENSSL_VERSION_MAJOR >= 3
              ::EVP_CIPHER_do_all_provided (
                  nullptr,
                  [] (::EVP_CIPHER* ciph, void* arg) { AccumulateIntoSetOfCipherNames_ (ciph, reinterpret_cast<Set<String>*> (arg)); },
                  &cipherNames);
#else
              ::EVP_CIPHER_do_all_sorted (
#if qCompilerAndStdLib_maybe_unused_in_lambda_ignored_Buggy
                  [] (const ::EVP_CIPHER* ciph, const char*, const char*, void* arg) { AccumulateIntoSetOfCipherNames_ (ciph, reinterpret_cast<Set<String>*> (arg)); },
#else
                  [] (const ::EVP_CIPHER* ciph, [[maybe_unused]] const char* from, [[maybe_unused]] const char* to, void* arg) { AccumulateIntoSetOfCipherNames_ (ciph, reinterpret_cast<Set<String>*> (arg)); },
#endif
                  &cipherNames);
#endif
#if USE_NOISY_TRACE_IN_THIS_MODULE_
              DbgTrace (L"Found pAvailableCipherAlgorithms-FIRST-PASS (cnt=%d): %s", cipherNames.size (), Characters::ToString (cipherNames).c_str ());
#endif

              Set<CipherAlgorithm> results{cipherNames.Select<CipherAlgorithm> ([] (const String& n) -> optional<CipherAlgorithm> { return OpenSSL::CipherAlgorithm::GetByNameQuietly (n); })};
              DbgTrace (L"Found pAvailableCipherAlgorithms (cnt=%d): %s", results.size (), Characters::ToString (results).c_str ());
              return results;
          }}
    , pStandardCipherAlgorithms{[qStroika_Foundation_Common_Property_ExtraCaptureStuff] ([[maybe_unused]] const auto* property) -> Set<CipherAlgorithm> {
        const LibraryContext*                                thisObj = qStroika_Foundation_Common_Property_OuterObjPtr (property, &LibraryContext::pStandardCipherAlgorithms);
        shared_lock<const AssertExternallySynchronizedMutex> critSec{*thisObj};
        Set<CipherAlgorithm>                                 results;

        results += CipherAlgorithms::kAES_128_CBC;
        results += CipherAlgorithms::kAES_128_ECB;
        results += CipherAlgorithms::kAES_128_OFB;
        results += CipherAlgorithms::kAES_128_CFB1;
        results += CipherAlgorithms::kAES_128_CFB8;
        results += CipherAlgorithms::kAES_128_CFB128;
        results += CipherAlgorithms::kAES_192_CBC;
        results += CipherAlgorithms::kAES_192_ECB;
        results += CipherAlgorithms::kAES_192_OFB;
        results += CipherAlgorithms::kAES_192_CFB1;
        results += CipherAlgorithms::kAES_192_CFB8;
        results += CipherAlgorithms::kAES_192_CFB128;
        results += CipherAlgorithms::kAES_256_CBC;
        results += CipherAlgorithms::kAES_256_ECB;
        results += CipherAlgorithms::kAES_256_OFB;
        results += CipherAlgorithms::kAES_256_CFB1;
        results += CipherAlgorithms::kAES_256_CFB8;
        results += CipherAlgorithms::kAES_256_CFB128;

    /*
     * @todo mark these below as deprecated...??? in openssl3?
     */
#if OPENSSL_VERSION_MAJOR < 3
        results += CipherAlgorithms::kBlowfish_CBC;
        results += CipherAlgorithms::kBlowfish_ECB;
        results += CipherAlgorithms::kBlowfish_CFB;
        results += CipherAlgorithms::kBlowfish_OFB;
        results += CipherAlgorithms::kBlowfish;
        results += CipherAlgorithms::kRC2_CBC;
        results += CipherAlgorithms::kRC2_ECB;
        results += CipherAlgorithms::kRC2_CFB;
        results += CipherAlgorithms::kRC2_OFB;
        results += CipherAlgorithms::kRC4;
#endif

        return results;
    }}
    , pAvailableDigestAlgorithms{[qStroika_Foundation_Common_Property_ExtraCaptureStuff] ([[maybe_unused]] const auto* property) -> Set<DigestAlgorithm> {
        const LibraryContext*                                thisObj = qStroika_Foundation_Common_Property_OuterObjPtr (property, &LibraryContext::pAvailableDigestAlgorithms);
        shared_lock<const AssertExternallySynchronizedMutex> critSec{*thisObj};

        Set<String> digestNames;
#if OPENSSL_VERSION_MAJOR >= 3
        ::EVP_MD_do_all_provided (
            nullptr,
            [] (::EVP_MD* md, void* arg) { AccumulateIntoSetOfDigestNames_ (md, reinterpret_cast<Set<String>*> (arg)); },
            &digestNames);
#else
        ::EVP_MD_do_all_sorted (
#if qCompilerAndStdLib_maybe_unused_in_lambda_ignored_Buggy
            [] (const ::EVP_MD* md, const char*, const char*, void* arg) { AccumulateIntoSetOfDigestNames_ (md, reinterpret_cast<Set<String>*> (arg)); },
#else
            [] (const ::EVP_MD* md, [[maybe_unused]] const char* from, [[maybe_unused]] const char* to, void* arg) { AccumulateIntoSetOfDigestNames_ (md, reinterpret_cast<Set<String>*> (arg)); },
#endif
            &digestNames);
#endif
#if USE_NOISY_TRACE_IN_THIS_MODULE_
        DbgTrace (L"Found pAvailableDigestAlgorithms-FIRST-PASS (cnt=%d): %s", digestNames.size (), Characters::ToString (digestNames).c_str ());
#endif

        Set<DigestAlgorithm> results{digestNames.Select<DigestAlgorithm> ([] (const String& n) -> optional<DigestAlgorithm> { return OpenSSL::DigestAlgorithm::GetByNameQuietly (n); })};
        DbgTrace (L"Found pAvailableDigestAlgorithms (cnt=%d): %s", results.size (), Characters::ToString (results).c_str ());

        return results;
    }}
    , pStandardDigestAlgorithms{[qStroika_Foundation_Common_Property_ExtraCaptureStuff] ([[maybe_unused]] const auto* property) -> Set<DigestAlgorithm> {
        const LibraryContext*                                thisObj = qStroika_Foundation_Common_Property_OuterObjPtr (property, &LibraryContext::pStandardDigestAlgorithms);
        shared_lock<const AssertExternallySynchronizedMutex> critSec{*thisObj};
        Set<DigestAlgorithm>                                 results;
        results += DigestAlgorithms::kMD5;
        results += DigestAlgorithms::kSHA1;
        results += DigestAlgorithms::kSHA1_224;
        results += DigestAlgorithms::kSHA1_256;
        results += DigestAlgorithms::kSHA1_384;
        results += DigestAlgorithms::kSHA1_512;
        results += DigestAlgorithms::kSHA3_224;
        results += DigestAlgorithms::kSHA3_256;
        results += DigestAlgorithms::kSHA3_384;
        results += DigestAlgorithms::kSHA3_512;
        return results;
    }}
{
    LoadProvider (kDefaultProvider);
}

LibraryContext ::~LibraryContext ()
{
    lock_guard<const AssertExternallySynchronizedMutex> critSec{*this};
#if OPENSSL_VERSION_MAJOR >= 3
    // reference counts dont matter here, just unload all the providers we loaded
    for (auto i : fLoadedProviders_.MappedValues ()) {
        Verify (::OSSL_PROVIDER_unload (i) == 1);
    }
#endif
}

void LibraryContext::LoadProvider ([[maybe_unused]] const String& providerName)
{
    Debug::TraceContextBumper                           ctx{Stroika_Foundation_Debug_OptionalizeTraceArgs (L"OpenSSL::LibraryContext::LoadProvider", L"%s", providerName.c_str ())};
    lock_guard<const AssertExternallySynchronizedMutex> critSec{*this};
#if OPENSSL_VERSION_MAJOR >= 3
    auto p = fLoadedProviders_.LookupOneValue (providerName);
    if (p == nullptr) {
        // really load cuz not already loaded
        DbgTrace (L"calling OSSL_PROVIDER_load");
        p = ::OSSL_PROVIDER_load (nullptr, providerName.AsNarrowSDKString ().c_str ());
        static const Execution::RuntimeErrorException kErr_{L"No such SSL provider"sv};
        Execution::ThrowIfNull (p, kErr_);
    }
    fLoadedProviders_.Add (providerName, p); // add association (perhaps redundantly)
#else
    Require (providerName == kDefaultProvider or providerName == kLegacyProvider);
#endif
}

void LibraryContext ::UnLoadProvider ([[maybe_unused]] const String& providerName)
{
    Debug::TraceContextBumper                           ctx{Stroika_Foundation_Debug_OptionalizeTraceArgs (L"OpenSSL::LibraryContext::UnLoadProvider", L"%s", providerName.c_str ())};
    lock_guard<const AssertExternallySynchronizedMutex> critSec{*this};
#if OPENSSL_VERSION_MAJOR >= 3
    Require (fLoadedProviders_.ContainsKey (providerName));
    fLoadedProviders_.Remove (providerName);
    if (auto p = fLoadedProviders_.LookupOneValue (providerName)) {
        DbgTrace (L"calling OSSL_PROVIDER_unload");
        Verify (::OSSL_PROVIDER_unload (p) == 1);
    }
#endif
}
#endif
<commit_msg>fixed small regression with recent OpenSSL/LibraryContext change to using Association<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2021.  All rights reserved
 */
#include "../../StroikaPreComp.h"

#if qHasFeature_OpenSSL
#include <openssl/evp.h>
#if OPENSSL_VERSION_MAJOR >= 3
#include <openssl/provider.h>
#endif
#endif

#include "../../Debug/Assertions.h"
#include "../../Execution/Exceptions.h"

#include "LibraryContext.h"

using namespace Stroika::Foundation;
using namespace Stroika::Foundation::Cryptography;
using namespace Stroika::Foundation::Cryptography::OpenSSL;

// Comment this in to turn on aggressive noisy DbgTrace in this module
//#define   USE_NOISY_TRACE_IN_THIS_MODULE_       1

#if qHasFeature_OpenSSL && defined(_MSC_VER)
// Use #pragma comment lib instead of explicit entry in the lib entry of the project file
#if OPENSSL_VERSION_NUMBER < 0x1010000fL
#pragma comment(lib, "libeay32.lib")
#pragma comment(lib, "ssleay32.lib")
#else
#pragma comment(lib, "libcrypto.lib")
#pragma comment(lib, "libssl.lib")
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "crypt32.lib")
#endif
#endif

#if qHasFeature_OpenSSL

namespace {
    void AccumulateIntoSetOfCipherNames_ (const ::EVP_CIPHER* ciph, Set<String>* ciphers)
    {
        RequireNotNull (ciphers);
        if (ciph != nullptr) {
#if USE_NOISY_TRACE_IN_THIS_MODULE_
#if OPENSSL_VERSION_MAJOR >= 3
            DbgTrace (L"cipher: %p (name: %s), provider: %p (name %s)", ciph,
                      CipherAlgorithm{ciph}.pName ().c_str (),
                      ::EVP_CIPHER_get0_provider (ciph),
                      (::EVP_CIPHER_get0_provider (ciph) == nullptr ? L"null" : String::FromNarrowSDKString (::OSSL_PROVIDER_get0_name (::EVP_CIPHER_get0_provider (ciph))).c_str ()));
#else
            DbgTrace (L"cipher: %p (name: %s)", ciph, CipherAlgorithm{ciph}.pName ().c_str ());
#endif
#if 0
            int flags = ::EVP_CIPHER_flags (ciph);
            DbgTrace ("flags=%x", flags);
#endif
#endif
            ciphers->Add (CipherAlgorithm{ciph}.pName ());
        }
    };
    void AccumulateIntoSetOfDigestNames_ (const ::EVP_MD* digest, Set<String>* digestNames)
    {
        RequireNotNull (digestNames);
        if (digest != nullptr) {
#if USE_NOISY_TRACE_IN_THIS_MODULE_
#if OPENSSL_VERSION_MAJOR >= 3
            DbgTrace (L"digest: %p (name: %s), provider: %p (name %s)",
                      digest, DigestAlgorithm{digest}.pName ().c_str (),
                      ::EVP_MD_get0_provider (digest),
                      (::EVP_MD_get0_provider (digest) == nullptr ? L"null" : String::FromNarrowSDKString (::OSSL_PROVIDER_get0_name (::EVP_MD_get0_provider (digest))).c_str ()));
#else
            DbgTrace (L"digest: %p (name: %s)", digest, DigestAlgorithm{digest}.pName ().c_str ());
#endif
#endif
            digestNames->Add (DigestAlgorithm{digest}.pName ());
        }
    };
}

/*
 ********************************************************************************
 ******************* Cryptography::OpenSSL::LibraryContext **********************
 ********************************************************************************
 */
#if qCompiler_cpp17InlineStaticMemberOfClassDoubleDeleteAtExit_Buggy
LibraryContext LibraryContext::sDefault;
#endif

LibraryContext::LibraryContext ()
    : pAvailableCipherAlgorithms{
          [qStroika_Foundation_Common_Property_ExtraCaptureStuff] ([[maybe_unused]] const auto* property) -> Set<CipherAlgorithm> {
              const LibraryContext*                                thisObj = qStroika_Foundation_Common_Property_OuterObjPtr (property, &LibraryContext::pAvailableCipherAlgorithms);
              shared_lock<const AssertExternallySynchronizedMutex> critSec{*thisObj};
              Set<String>                                          cipherNames;
#if OPENSSL_VERSION_MAJOR >= 3
              ::EVP_CIPHER_do_all_provided (
                  nullptr,
                  [] (::EVP_CIPHER* ciph, void* arg) { AccumulateIntoSetOfCipherNames_ (ciph, reinterpret_cast<Set<String>*> (arg)); },
                  &cipherNames);
#else
              ::EVP_CIPHER_do_all_sorted (
#if qCompilerAndStdLib_maybe_unused_in_lambda_ignored_Buggy
                  [] (const ::EVP_CIPHER* ciph, const char*, const char*, void* arg) { AccumulateIntoSetOfCipherNames_ (ciph, reinterpret_cast<Set<String>*> (arg)); },
#else
                  [] (const ::EVP_CIPHER* ciph, [[maybe_unused]] const char* from, [[maybe_unused]] const char* to, void* arg) { AccumulateIntoSetOfCipherNames_ (ciph, reinterpret_cast<Set<String>*> (arg)); },
#endif
                  &cipherNames);
#endif
#if USE_NOISY_TRACE_IN_THIS_MODULE_
              DbgTrace (L"Found pAvailableCipherAlgorithms-FIRST-PASS (cnt=%d): %s", cipherNames.size (), Characters::ToString (cipherNames).c_str ());
#endif

              Set<CipherAlgorithm> results{cipherNames.Select<CipherAlgorithm> ([] (const String& n) -> optional<CipherAlgorithm> { return OpenSSL::CipherAlgorithm::GetByNameQuietly (n); })};
              DbgTrace (L"Found pAvailableCipherAlgorithms (cnt=%d): %s", results.size (), Characters::ToString (results).c_str ());
              return results;
          }}
    , pStandardCipherAlgorithms{[qStroika_Foundation_Common_Property_ExtraCaptureStuff] ([[maybe_unused]] const auto* property) -> Set<CipherAlgorithm> {
        const LibraryContext*                                thisObj = qStroika_Foundation_Common_Property_OuterObjPtr (property, &LibraryContext::pStandardCipherAlgorithms);
        shared_lock<const AssertExternallySynchronizedMutex> critSec{*thisObj};
        Set<CipherAlgorithm>                                 results;

        results += CipherAlgorithms::kAES_128_CBC;
        results += CipherAlgorithms::kAES_128_ECB;
        results += CipherAlgorithms::kAES_128_OFB;
        results += CipherAlgorithms::kAES_128_CFB1;
        results += CipherAlgorithms::kAES_128_CFB8;
        results += CipherAlgorithms::kAES_128_CFB128;
        results += CipherAlgorithms::kAES_192_CBC;
        results += CipherAlgorithms::kAES_192_ECB;
        results += CipherAlgorithms::kAES_192_OFB;
        results += CipherAlgorithms::kAES_192_CFB1;
        results += CipherAlgorithms::kAES_192_CFB8;
        results += CipherAlgorithms::kAES_192_CFB128;
        results += CipherAlgorithms::kAES_256_CBC;
        results += CipherAlgorithms::kAES_256_ECB;
        results += CipherAlgorithms::kAES_256_OFB;
        results += CipherAlgorithms::kAES_256_CFB1;
        results += CipherAlgorithms::kAES_256_CFB8;
        results += CipherAlgorithms::kAES_256_CFB128;

    /*
     * @todo mark these below as deprecated...??? in openssl3?
     */
#if OPENSSL_VERSION_MAJOR < 3
        results += CipherAlgorithms::kBlowfish_CBC;
        results += CipherAlgorithms::kBlowfish_ECB;
        results += CipherAlgorithms::kBlowfish_CFB;
        results += CipherAlgorithms::kBlowfish_OFB;
        results += CipherAlgorithms::kBlowfish;
        results += CipherAlgorithms::kRC2_CBC;
        results += CipherAlgorithms::kRC2_ECB;
        results += CipherAlgorithms::kRC2_CFB;
        results += CipherAlgorithms::kRC2_OFB;
        results += CipherAlgorithms::kRC4;
#endif

        return results;
    }}
    , pAvailableDigestAlgorithms{[qStroika_Foundation_Common_Property_ExtraCaptureStuff] ([[maybe_unused]] const auto* property) -> Set<DigestAlgorithm> {
        const LibraryContext*                                thisObj = qStroika_Foundation_Common_Property_OuterObjPtr (property, &LibraryContext::pAvailableDigestAlgorithms);
        shared_lock<const AssertExternallySynchronizedMutex> critSec{*thisObj};

        Set<String> digestNames;
#if OPENSSL_VERSION_MAJOR >= 3
        ::EVP_MD_do_all_provided (
            nullptr,
            [] (::EVP_MD* md, void* arg) { AccumulateIntoSetOfDigestNames_ (md, reinterpret_cast<Set<String>*> (arg)); },
            &digestNames);
#else
        ::EVP_MD_do_all_sorted (
#if qCompilerAndStdLib_maybe_unused_in_lambda_ignored_Buggy
            [] (const ::EVP_MD* md, const char*, const char*, void* arg) { AccumulateIntoSetOfDigestNames_ (md, reinterpret_cast<Set<String>*> (arg)); },
#else
            [] (const ::EVP_MD* md, [[maybe_unused]] const char* from, [[maybe_unused]] const char* to, void* arg) { AccumulateIntoSetOfDigestNames_ (md, reinterpret_cast<Set<String>*> (arg)); },
#endif
            &digestNames);
#endif
#if USE_NOISY_TRACE_IN_THIS_MODULE_
        DbgTrace (L"Found pAvailableDigestAlgorithms-FIRST-PASS (cnt=%d): %s", digestNames.size (), Characters::ToString (digestNames).c_str ());
#endif

        Set<DigestAlgorithm> results{digestNames.Select<DigestAlgorithm> ([] (const String& n) -> optional<DigestAlgorithm> { return OpenSSL::DigestAlgorithm::GetByNameQuietly (n); })};
        DbgTrace (L"Found pAvailableDigestAlgorithms (cnt=%d): %s", results.size (), Characters::ToString (results).c_str ());

        return results;
    }}
    , pStandardDigestAlgorithms{[qStroika_Foundation_Common_Property_ExtraCaptureStuff] ([[maybe_unused]] const auto* property) -> Set<DigestAlgorithm> {
        const LibraryContext*                                thisObj = qStroika_Foundation_Common_Property_OuterObjPtr (property, &LibraryContext::pStandardDigestAlgorithms);
        shared_lock<const AssertExternallySynchronizedMutex> critSec{*thisObj};
        Set<DigestAlgorithm>                                 results;
        results += DigestAlgorithms::kMD5;
        results += DigestAlgorithms::kSHA1;
        results += DigestAlgorithms::kSHA1_224;
        results += DigestAlgorithms::kSHA1_256;
        results += DigestAlgorithms::kSHA1_384;
        results += DigestAlgorithms::kSHA1_512;
        results += DigestAlgorithms::kSHA3_224;
        results += DigestAlgorithms::kSHA3_256;
        results += DigestAlgorithms::kSHA3_384;
        results += DigestAlgorithms::kSHA3_512;
        return results;
    }}
{
    LoadProvider (kDefaultProvider);
}

LibraryContext ::~LibraryContext ()
{
    lock_guard<const AssertExternallySynchronizedMutex> critSec{*this};
#if OPENSSL_VERSION_MAJOR >= 3
    // reference counts dont matter here, just unload all the providers we loaded
    for (auto i : fLoadedProviders_.MappedValues ()) {
        Verify (::OSSL_PROVIDER_unload (i) == 1);
    }
#endif
}

void LibraryContext::LoadProvider ([[maybe_unused]] const String& providerName)
{
    Debug::TraceContextBumper                           ctx{Stroika_Foundation_Debug_OptionalizeTraceArgs (L"OpenSSL::LibraryContext::LoadProvider", L"%s", providerName.c_str ())};
    lock_guard<const AssertExternallySynchronizedMutex> critSec{*this};
#if OPENSSL_VERSION_MAJOR >= 3
    auto p = fLoadedProviders_.LookupOneValue (providerName);
    if (p == nullptr) {
        // really load cuz not already loaded
        DbgTrace (L"calling OSSL_PROVIDER_load");
        p = ::OSSL_PROVIDER_load (nullptr, providerName.AsNarrowSDKString ().c_str ());
        static const Execution::RuntimeErrorException kErr_{L"No such SSL provider"sv};
        Execution::ThrowIfNull (p, kErr_);
    }
    fLoadedProviders_.Add (providerName, p); // add association (perhaps redundantly)
#else
    Require (providerName == kDefaultProvider or providerName == kLegacyProvider);
#endif
}

void LibraryContext ::UnLoadProvider ([[maybe_unused]] const String& providerName)
{
    Debug::TraceContextBumper                           ctx{Stroika_Foundation_Debug_OptionalizeTraceArgs (L"OpenSSL::LibraryContext::UnLoadProvider", L"%s", providerName.c_str ())};
    lock_guard<const AssertExternallySynchronizedMutex> critSec{*this};
#if OPENSSL_VERSION_MAJOR >= 3
    Require (fLoadedProviders_.ContainsKey (providerName));
    auto providerToMaybeRemove = fLoadedProviders_.LookupOneValue (providerName);
    fLoadedProviders_.Remove (providerName);
    if (not fLoadedProviders_.ContainsKey (providerName)) {
        DbgTrace (L"calling OSSL_PROVIDER_unload");
        Verify (::OSSL_PROVIDER_unload (providerToMaybeRemove) == 1);
    }
#endif
}
#endif
<|endoftext|>


<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2021.  All rights reserved
 */
#include "../../../StroikaPreComp.h"

#include "../../../Characters/Format.h"

#include "Reader.h"

//// SEEE http://www.zlib.net/zlib_how.html
/// THIS SHOWS PRETTY SIMPLE EXAMPLE OF HOW TO DO COMPRESS/DECOMPRESS AND WE CAN USE THAT to amke new stream object
/// where inner loop is done each time through with a CHUNK

#if qHasFeature_ZLib
#include <zlib.h>

#if defined(_MSC_VER)
// Use #pragma comment lib instead of explicit entry in the lib entry of the project file
#pragma comment(lib, "zlib.lib")
#endif

using std::byte;

using namespace Stroika::Foundation;
using namespace Stroika::Foundation::DataExchange;
using namespace Stroika::Foundation::DataExchange::Compression;
using namespace Stroika::Foundation::Streams;

// Comment this in to turn on aggressive noisy DbgTrace in this module
//#define   USE_NOISY_TRACE_IN_THIS_MODULE_       1

namespace {

    void ThrowIfZLibErr_ (int err)
    {
        // VERY ROUGH DRAFT - probably need a more specific exception object type
        if (err != Z_OK) [[UNLIKELY_ATTR]] {
            switch (err) {
                case Z_VERSION_ERROR:
                    Execution::Throw (Execution::RuntimeErrorException{L"ZLIB Z_VERSION_ERROR"sv});
                case Z_DATA_ERROR:
                    Execution::Throw (Execution::RuntimeErrorException{L"ZLIB Z_DATA_ERROR"sv});
                case Z_ERRNO:
                    Execution::Throw (Execution::RuntimeErrorException{Characters::Format (L"ZLIB Z_ERRNO (errno=%d", errno)});
                default:
                    Execution::Throw (Execution::RuntimeErrorException{Characters::Format (L"ZLIB ERR %d", err)});
            }
        }
    }

    struct MyCompressionStream_ : InputStream<byte>::Ptr {
        struct BaseRep_ : public InputStream<byte>::_IRep {
        private:
            static constexpr size_t CHUNK_ = 16384;

        public:
            Streams::InputStream<Memory::byte>::Ptr fInStream_;
            z_stream                                fZStream_;
            byte                                    fInBuf_[CHUNK_];
            SeekOffsetType                          _fSeekOffset{};
            BaseRep_ (const Streams::InputStream<byte>::Ptr& in)
                : fInStream_ (in)
                , fZStream_{}
            {
            }
            virtual ~BaseRep_ () = default;
            virtual bool IsSeekable () const override
            {
                // for now - KISS
                return false; // SHOULD allow seekable IFF src is seekable
            }
            virtual void CloseRead () override
            {
                Require (IsOpenRead ());
                fInStream_.Close ();
                Assert (fInStream_ == nullptr);
                Ensure (not IsOpenRead ());
            }
            virtual bool IsOpenRead () const override
            {
                return fInStream_ != nullptr;
            }
            virtual SeekOffsetType GetReadOffset () const override
            {
                Require (IsOpenRead ());
                return _fSeekOffset;
            }
            virtual SeekOffsetType SeekRead (Whence /*whence*/, SignedSeekOffsetType /*offset*/) override
            {
                RequireNotReached ();
                Require (IsOpenRead ());
                return SeekOffsetType{};
            }
            nonvirtual bool _AssureInputAvailableReturnTrueIfAtEOF ()
            {
                Require (IsOpenRead ());
                if (fZStream_.avail_in == 0) {
                    Assert (Memory::NEltsOf (fInBuf_) < numeric_limits<uInt>::max ());
                    fZStream_.avail_in = static_cast<uInt> (fInStream_.Read (begin (fInBuf_), end (fInBuf_)));
                    fZStream_.next_in  = reinterpret_cast<Bytef*> (begin (fInBuf_));
                }
                return fZStream_.avail_in == 0;
            }
        };
        struct DeflateRep_ : BaseRep_ {
            DeflateRep_ (const Streams::InputStream<byte>::Ptr& in)
                : BaseRep_ (in)
            {
                int level = Z_DEFAULT_COMPRESSION;
                ThrowIfZLibErr_ (::deflateInit (&fZStream_, level));
            }
            virtual ~DeflateRep_ ()
            {
                Verify (::deflateEnd (&fZStream_) == Z_OK);
            }
            virtual size_t Read (ElementType* intoStart, ElementType* intoEnd) override
            {
                Require (intoStart < intoEnd); // API rule for streams
                Require (IsOpenRead ());
            Again:
                bool isAtSrcEOF = _AssureInputAvailableReturnTrueIfAtEOF ();

                Require (intoStart < intoEnd);
                ptrdiff_t outBufSize = intoEnd - intoStart;

                int flush = isAtSrcEOF ? Z_FINISH : Z_NO_FLUSH;

                fZStream_.avail_out = static_cast<uInt> (outBufSize);
                fZStream_.next_out  = reinterpret_cast<Bytef*> (intoStart);
                int ret;
                switch (ret = ::deflate (&fZStream_, flush)) {
                    case Z_OK:
                        break;
                    case Z_STREAM_END:
                        break;
                    default:
                        ThrowIfZLibErr_ (ret);
                }

                ptrdiff_t pulledOut = outBufSize - fZStream_.avail_out;
                Assert (pulledOut <= outBufSize);
                if (pulledOut == 0 and not isAtSrcEOF and flush == Z_NO_FLUSH) {
                    goto Again;
                }
                _fSeekOffset += pulledOut;
                return pulledOut;
            }
            virtual optional<size_t> ReadNonBlocking ([[maybe_unused]] ElementType* intoStart, [[maybe_unused]] ElementType* intoEnd) override
            {
                Require ((intoStart == nullptr and intoEnd == nullptr) or (intoEnd - intoStart) >= 1);
                Require (IsOpenRead ());
// https://stroika.atlassian.net/browse/STK-567 EXPERIMENTAL DRAFT API - INCOMPLETE IMPL
#if 0
                if (intoStart == nullptr) {
                    // Just check if any data available

                }
                else {
                    Assert (intoStart < intoEnd);
                Again:
                    if (fZStream_.avail_in == 0) {
                        Assert (Memory::NEltsOf (fInBuf_) < numeric_limits<uInt>::max ());
                        if (auto o = fInStream_.ReadNonBlocking (begin (fInBuf_), end (fInBuf_))) {
                            fZStream_.avail_in = static_cast<uInt> (*o);
                            fZStream_.next_in = begin (fInBuf_);
                        }
                        else {
                            return {};
                        }
                    }
                    ptrdiff_t outBufSize = intoEnd - intoStart;
                    fZStream_.avail_out = static_cast<uInt> (outBufSize);
                    fZStream_.next_out = intoStart;
                    bool isAtSrcEOF = fZStream_.avail_in == 0;
                    int flush = isAtSrcEOF ? Z_FINISH : Z_NO_FLUSH;
                    int ret;
                    switch (ret = ::deflate (&fZStream_, flush)) {
                    case Z_OK:
                        break;
                    case Z_STREAM_END:
                        break;
                    default:
                        ThrowIfZLibErr_ (ret);
                    }
                    ptrdiff_t pulledOut = outBufSize - fZStream_.avail_out;
                    Assert (pulledOut <= outBufSize);
                    if (pulledOut == 0 and not isAtSrcEOF) {
                        goto Again;
                    }
                    _fSeekOffset += pulledOut;
                    return pulledOut;
                }
#else
                // while nothing available to output, try getting more from input stream until we cannot (either cuz {} or EOF)
                // and then report accordingly
                //
                // INCOMPLETE - tricky... most common cases easy, but edges hard...
                //&&&& issues are:
                //>> wrong meaning for available_out - we have no output buffer
                //  >> need output buffer for this API, since could pass in no argument buffer (OK to be one byte in that case)
                while (fZStream_.avail_out == 0) {
                    if (fZStream_.avail_in == 0) {
                        if (optional<size_t> tmpAvail = fInStream_.ReadNonBlocking (begin (fInBuf_), end (fInBuf_))) {
                        }
                        else {
                            return {};
                        }
                    }
                }
#endif
                WeakAssert (false);
                // @todo - FIX TO REALLY CHECK
                return {};
            }
        };
        struct InflateRep_ : BaseRep_ {
            InflateRep_ (const Streams::InputStream<byte>::Ptr& in)
                : BaseRep_ (in)
            {
                // see http://zlib.net/manual.html  for meaning of params and http://www.lemoda.net/c/zlib-open-read/ for example
                constexpr int windowBits       = 15;
                constexpr int ENABLE_ZLIB_GZIP = 32;
                ThrowIfZLibErr_ (::inflateInit2 (&fZStream_, windowBits | ENABLE_ZLIB_GZIP));
            }
            virtual ~InflateRep_ ()
            {
                Verify (::inflateEnd (&fZStream_) == Z_OK);
            }
            virtual size_t Read (ElementType* intoStart, ElementType* intoEnd) override
            {
                Require (intoStart < intoEnd); // API rule for streams
                Require (IsOpenRead ());
            Again:
                bool      isAtSrcEOF = _AssureInputAvailableReturnTrueIfAtEOF ();
                ptrdiff_t outBufSize = intoEnd - intoStart;

                fZStream_.avail_out = static_cast<uInt> (outBufSize);
                fZStream_.next_out  = reinterpret_cast<Bytef*> (intoStart);
                int ret;
                switch (ret = ::inflate (&fZStream_, Z_NO_FLUSH)) {
                    case Z_OK:
                        break;
                    case Z_STREAM_END:
                        break;
                    default:
                        ThrowIfZLibErr_ (ret);
                }

                ptrdiff_t pulledOut = outBufSize - fZStream_.avail_out;
                Assert (pulledOut <= outBufSize);
                if (pulledOut == 0 and not isAtSrcEOF) {
                    goto Again;
                }
                _fSeekOffset += pulledOut;
                return pulledOut;
            }
            virtual optional<size_t> ReadNonBlocking ([[maybe_unused]] ElementType* intoStart, [[maybe_unused]] ElementType* intoEnd) override
            {
                // https://stroika.atlassian.net/browse/STK-567 EXPERIMENTAL DRAFT API - incomplete IMPL
                Require ((intoStart == nullptr and intoEnd == nullptr) or (intoEnd - intoStart) >= 1);
                Require (IsOpenRead ());
                WeakAssert (false);
                // @todo - FIX TO REALLY CHECK
                return {};
            }
        };
        enum Compression { eCompression };
        enum DeCompression { eDeCompression };
        MyCompressionStream_ (Compression, const Streams::InputStream<byte>::Ptr& in)
            : InputStream<byte>::Ptr (make_shared<DeflateRep_> (in))
        {
        }
        MyCompressionStream_ (DeCompression, const Streams::InputStream<byte>::Ptr& in)
            : InputStream<byte>::Ptr (make_shared<InflateRep_> (in))
        {
        }
    };
}
#endif

#if qHasFeature_ZLib
class Zip::Reader::Rep_ : public Reader::_IRep {
public:
    virtual InputStream<byte>::Ptr Compress (const InputStream<byte>::Ptr& src) const override
    {
        return MyCompressionStream_ (MyCompressionStream_::eCompression, src);
    }
    virtual InputStream<byte>::Ptr Decompress (const InputStream<byte>::Ptr& src) const override
    {
        return MyCompressionStream_ (MyCompressionStream_::eDeCompression, src);
    }
};

Zip::Reader::Reader ()
    : DataExchange::Compression::Reader{make_shared<Rep_> ()}
{
}
#endif
<commit_msg>cosmetic cleanups to DataExchange/Compression/Zip/Reader<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2021.  All rights reserved
 */
#include "../../../StroikaPreComp.h"

#include "../../../Characters/Format.h"

#include "Reader.h"

//// SEEE http://www.zlib.net/zlib_how.html
/// THIS SHOWS PRETTY SIMPLE EXAMPLE OF HOW TO DO COMPRESS/DECOMPRESS AND WE CAN USE THAT to amke new stream object
/// where inner loop is done each time through with a CHUNK

#if qHasFeature_ZLib
#include <zlib.h>

#if defined(_MSC_VER)
// Use #pragma comment lib instead of explicit entry in the lib entry of the project file
#pragma comment(lib, "zlib.lib")
#endif

using std::byte;

using namespace Stroika::Foundation;
using namespace Stroika::Foundation::DataExchange;
using namespace Stroika::Foundation::DataExchange::Compression;
using namespace Stroika::Foundation::Streams;

// Comment this in to turn on aggressive noisy DbgTrace in this module
//#define   USE_NOISY_TRACE_IN_THIS_MODULE_       1

namespace {

    void ThrowIfZLibErr_ (int err)
    {
        // VERY ROUGH DRAFT - probably need a more specific exception object type
        if (err != Z_OK) [[UNLIKELY_ATTR]] {
            switch (err) {
                case Z_VERSION_ERROR:
                    Execution::Throw (Execution::RuntimeErrorException{L"ZLIB Z_VERSION_ERROR"sv});
                case Z_DATA_ERROR:
                    Execution::Throw (Execution::RuntimeErrorException{L"ZLIB Z_DATA_ERROR"sv});
                case Z_ERRNO:
                    Execution::Throw (Execution::RuntimeErrorException{Characters::Format (L"ZLIB Z_ERRNO (errno=%d", errno)});
                default:
                    Execution::Throw (Execution::RuntimeErrorException{Characters::Format (L"ZLIB ERR %d", err)});
            }
        }
    }

    struct MyCompressionStream_ : InputStream<byte>::Ptr {
        struct BaseRep_ : public InputStream<byte>::_IRep {
        private:
            static constexpr size_t CHUNK_ = 16384;

        public:
            Streams::InputStream<Memory::byte>::Ptr fInStream_;
            z_stream                                fZStream_{};
            byte                                    fInBuf_[CHUNK_];
            SeekOffsetType                          _fSeekOffset{};
            BaseRep_ (const Streams::InputStream<byte>::Ptr& in)
                : fInStream_{in}
            {
            }
            virtual ~BaseRep_ () = default;
            virtual bool IsSeekable () const override
            {
                // for now - KISS
                return false; // SHOULD allow seekable IFF src is seekable
            }
            virtual void CloseRead () override
            {
                Require (IsOpenRead ());
                fInStream_.Close ();
                Assert (fInStream_ == nullptr);
                Ensure (not IsOpenRead ());
            }
            virtual bool IsOpenRead () const override
            {
                return fInStream_ != nullptr;
            }
            virtual SeekOffsetType GetReadOffset () const override
            {
                Require (IsOpenRead ());
                return _fSeekOffset;
            }
            virtual SeekOffsetType SeekRead (Whence /*whence*/, SignedSeekOffsetType /*offset*/) override
            {
                RequireNotReached ();
                Require (IsOpenRead ());
                return SeekOffsetType{};
            }
            nonvirtual bool _AssureInputAvailableReturnTrueIfAtEOF ()
            {
                Require (IsOpenRead ());
                if (fZStream_.avail_in == 0) {
                    Assert (Memory::NEltsOf (fInBuf_) < numeric_limits<uInt>::max ());
                    fZStream_.avail_in = static_cast<uInt> (fInStream_.Read (begin (fInBuf_), end (fInBuf_)));
                    fZStream_.next_in  = reinterpret_cast<Bytef*> (begin (fInBuf_));
                }
                return fZStream_.avail_in == 0;
            }
        };
        struct DeflateRep_ : BaseRep_ {
            DeflateRep_ (const Streams::InputStream<byte>::Ptr& in)
                : BaseRep_{in}
            {
                int level = Z_DEFAULT_COMPRESSION;
                ThrowIfZLibErr_ (::deflateInit (&fZStream_, level));
            }
            virtual ~DeflateRep_ ()
            {
                Verify (::deflateEnd (&fZStream_) == Z_OK);
            }
            virtual size_t Read (ElementType* intoStart, ElementType* intoEnd) override
            {
                Require (intoStart < intoEnd); // API rule for streams
                Require (IsOpenRead ());
            Again:
                bool isAtSrcEOF = _AssureInputAvailableReturnTrueIfAtEOF ();

                Require (intoStart < intoEnd);
                ptrdiff_t outBufSize = intoEnd - intoStart;

                int flush = isAtSrcEOF ? Z_FINISH : Z_NO_FLUSH;

                fZStream_.avail_out = static_cast<uInt> (outBufSize);
                fZStream_.next_out  = reinterpret_cast<Bytef*> (intoStart);
                int ret;
                switch (ret = ::deflate (&fZStream_, flush)) {
                    case Z_OK:
                        break;
                    case Z_STREAM_END:
                        break;
                    default:
                        ThrowIfZLibErr_ (ret);
                }

                ptrdiff_t pulledOut = outBufSize - fZStream_.avail_out;
                Assert (pulledOut <= outBufSize);
                if (pulledOut == 0 and not isAtSrcEOF and flush == Z_NO_FLUSH) {
                    goto Again;
                }
                _fSeekOffset += pulledOut;
                return pulledOut;
            }
            virtual optional<size_t> ReadNonBlocking ([[maybe_unused]] ElementType* intoStart, [[maybe_unused]] ElementType* intoEnd) override
            {
                Require ((intoStart == nullptr and intoEnd == nullptr) or (intoEnd - intoStart) >= 1);
                Require (IsOpenRead ());
// https://stroika.atlassian.net/browse/STK-567 EXPERIMENTAL DRAFT API - INCOMPLETE IMPL
#if 0
                if (intoStart == nullptr) {
                    // Just check if any data available

                }
                else {
                    Assert (intoStart < intoEnd);
                Again:
                    if (fZStream_.avail_in == 0) {
                        Assert (Memory::NEltsOf (fInBuf_) < numeric_limits<uInt>::max ());
                        if (auto o = fInStream_.ReadNonBlocking (begin (fInBuf_), end (fInBuf_))) {
                            fZStream_.avail_in = static_cast<uInt> (*o);
                            fZStream_.next_in = begin (fInBuf_);
                        }
                        else {
                            return {};
                        }
                    }
                    ptrdiff_t outBufSize = intoEnd - intoStart;
                    fZStream_.avail_out = static_cast<uInt> (outBufSize);
                    fZStream_.next_out = intoStart;
                    bool isAtSrcEOF = fZStream_.avail_in == 0;
                    int flush = isAtSrcEOF ? Z_FINISH : Z_NO_FLUSH;
                    int ret;
                    switch (ret = ::deflate (&fZStream_, flush)) {
                    case Z_OK:
                        break;
                    case Z_STREAM_END:
                        break;
                    default:
                        ThrowIfZLibErr_ (ret);
                    }
                    ptrdiff_t pulledOut = outBufSize - fZStream_.avail_out;
                    Assert (pulledOut <= outBufSize);
                    if (pulledOut == 0 and not isAtSrcEOF) {
                        goto Again;
                    }
                    _fSeekOffset += pulledOut;
                    return pulledOut;
                }
#else
                // while nothing available to output, try getting more from input stream until we cannot (either cuz {} or EOF)
                // and then report accordingly
                //
                // INCOMPLETE - tricky... most common cases easy, but edges hard...
                //&&&& issues are:
                //>> wrong meaning for available_out - we have no output buffer
                //  >> need output buffer for this API, since could pass in no argument buffer (OK to be one byte in that case)
                while (fZStream_.avail_out == 0) {
                    if (fZStream_.avail_in == 0) {
                        if (optional<size_t> tmpAvail = fInStream_.ReadNonBlocking (begin (fInBuf_), end (fInBuf_))) {
                        }
                        else {
                            return {};
                        }
                    }
                }
#endif
                WeakAssert (false);
                // @todo - FIX TO REALLY CHECK
                return {};
            }
        };
        struct InflateRep_ : BaseRep_ {
            InflateRep_ (const Streams::InputStream<byte>::Ptr& in)
                : BaseRep_{in}
            {
                // see http://zlib.net/manual.html  for meaning of params and http://www.lemoda.net/c/zlib-open-read/ for example
                constexpr int windowBits       = 15;
                constexpr int ENABLE_ZLIB_GZIP = 32;
                ThrowIfZLibErr_ (::inflateInit2 (&fZStream_, windowBits | ENABLE_ZLIB_GZIP));
            }
            virtual ~InflateRep_ ()
            {
                Verify (::inflateEnd (&fZStream_) == Z_OK);
            }
            virtual size_t Read (ElementType* intoStart, ElementType* intoEnd) override
            {
                Require (intoStart < intoEnd); // API rule for streams
                Require (IsOpenRead ());
            Again:
                bool      isAtSrcEOF = _AssureInputAvailableReturnTrueIfAtEOF ();
                ptrdiff_t outBufSize = intoEnd - intoStart;

                fZStream_.avail_out = static_cast<uInt> (outBufSize);
                fZStream_.next_out  = reinterpret_cast<Bytef*> (intoStart);
                int ret;
                switch (ret = ::inflate (&fZStream_, Z_NO_FLUSH)) {
                    case Z_OK:
                        break;
                    case Z_STREAM_END:
                        break;
                    default:
                        ThrowIfZLibErr_ (ret);
                }

                ptrdiff_t pulledOut = outBufSize - fZStream_.avail_out;
                Assert (pulledOut <= outBufSize);
                if (pulledOut == 0 and not isAtSrcEOF) {
                    goto Again;
                }
                _fSeekOffset += pulledOut;
                return pulledOut;
            }
            virtual optional<size_t> ReadNonBlocking ([[maybe_unused]] ElementType* intoStart, [[maybe_unused]] ElementType* intoEnd) override
            {
                // https://stroika.atlassian.net/browse/STK-567 EXPERIMENTAL DRAFT API - incomplete IMPL
                Require ((intoStart == nullptr and intoEnd == nullptr) or (intoEnd - intoStart) >= 1);
                Require (IsOpenRead ());
                WeakAssert (false);
                // @todo - FIX TO REALLY CHECK
                return {};
            }
        };
        enum Compression { eCompression };
        enum DeCompression { eDeCompression };
        MyCompressionStream_ (Compression, const Streams::InputStream<byte>::Ptr& in)
            : InputStream<byte>::Ptr{make_shared<DeflateRep_> (in)}
        {
        }
        MyCompressionStream_ (DeCompression, const Streams::InputStream<byte>::Ptr& in)
            : InputStream<byte>::Ptr{make_shared<InflateRep_> (in)}
        {
        }
    };
}
#endif

#if qHasFeature_ZLib
class Zip::Reader::Rep_ : public Reader::_IRep {
public:
    virtual InputStream<byte>::Ptr Compress (const InputStream<byte>::Ptr& src) const override
    {
        return MyCompressionStream_ (MyCompressionStream_::eCompression, src);
    }
    virtual InputStream<byte>::Ptr Decompress (const InputStream<byte>::Ptr& src) const override
    {
        return MyCompressionStream_ (MyCompressionStream_::eDeCompression, src);
    }
};

Zip::Reader::Reader ()
    : DataExchange::Compression::Reader{make_shared<Rep_> ()}
{
}
#endif
<|endoftext|>


<commit_before>//
// Copyright (C) 2014, 2015 Ableton AG, Berlin. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//

#include <atria/xform/concepts.hpp>
#include <atria/xform/into_vector.hpp>
#include <atria/xform/transducer/product.hpp>
#include <atria/xform/transducer/take.hpp>
#include <atria/prelude/comp.hpp>
#include <atria/testing/gtest.hpp>

namespace atria {
namespace xform {

TEST(product, product)
{
  using tup = std::tuple<int, int>;
  auto v1 = std::vector<int> { 1, 2 };
  auto v2 = std::vector<int> { 4, 5 };
  auto res = into_vector(product(v2), v1);
  EXPECT_EQ(res, (decltype(res) {
        tup(1, 4), tup(1, 5), tup(2, 4), tup(2, 5) }));
}

TEST(product, variadic)
{
  using tup = std::tuple<int, int, char>;
  auto v1 = std::vector<int> { 1, 2 };
  auto v2 = std::vector<int> { 4, 5 };
  auto v3 = std::vector<char> { 'a', 'b' };

  auto res = into_vector(product(v2, v3), v1);
  EXPECT_EQ(res, (decltype(res) {
        tup(1, 4, 'a'), tup(1, 4, 'b'), tup(1, 5, 'a'), tup(1, 5, 'b'),
        tup(2, 4, 'a'), tup(2, 4, 'b'), tup(2, 5, 'a'), tup(2, 5, 'b')
   }));
}

TEST(product, generator)
{
  using tup = std::tuple<int, int>;
  auto v1 = std::vector<int> { 1, 2 };
  auto v2 = std::vector<int> { 4, 5 };

  auto res = into_vector(comp(take(1), product(v1, v2)));
  EXPECT_EQ(res, (decltype(res) {
        tup(1, 4), tup(1, 5), tup(2, 4), tup(2, 5) }));
}

} // namespace xform
} // namespace atria
<commit_msg>xform: add example for issue #13<commit_after>//
// Copyright (C) 2014, 2015, 2016 Ableton AG, Berlin. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//

#include <atria/xform/concepts.hpp>
#include <atria/xform/into_vector.hpp>
#include <atria/xform/transducer/product.hpp>
#include <atria/xform/transducer/range.hpp>
#include <atria/xform/transducer/take.hpp>
#include <atria/xform/sequence.hpp>
#include <atria/prelude/comp.hpp>
#include <atria/testing/gtest.hpp>

namespace atria {
namespace xform {

TEST(product, product)
{
  using tup = std::tuple<int, int>;
  auto v1 = std::vector<int> { 1, 2 };
  auto v2 = std::vector<int> { 4, 5 };
  auto res = into_vector(product(v2), v1);
  EXPECT_EQ(res, (decltype(res) {
        tup(1, 4), tup(1, 5), tup(2, 4), tup(2, 5) }));
}

TEST(product, variadic)
{
  using tup = std::tuple<int, int, char>;
  auto v1 = std::vector<int> { 1, 2 };
  auto v2 = std::vector<int> { 4, 5 };
  auto v3 = std::vector<char> { 'a', 'b' };

  auto res = into_vector(product(v2, v3), v1);
  EXPECT_EQ(res, (decltype(res) {
        tup(1, 4, 'a'), tup(1, 4, 'b'), tup(1, 5, 'a'), tup(1, 5, 'b'),
        tup(2, 4, 'a'), tup(2, 4, 'b'), tup(2, 5, 'a'), tup(2, 5, 'b')
   }));
}

TEST(product, generator)
{
  using tup = std::tuple<int, int>;
  auto v1 = std::vector<int> { 1, 2 };
  auto v2 = std::vector<int> { 4, 5 };

  auto res = into_vector(comp(take(1), product(v1, v2)));
  EXPECT_EQ(res, (decltype(res) {
        tup(1, 4), tup(1, 5), tup(2, 4), tup(2, 5) }));
}

TEST(product, tranducer_product_example)
{
  using tup = std::tuple<int, int>;

  auto idx = sequence(range(2));
  auto res = into_vector(product(idx), idx);

  EXPECT_EQ(res, (decltype(res) {
        tup(0, 0), tup(0, 1), tup(1, 0), tup(1, 1) }));
}

} // namespace xform
} // namespace atria
<|endoftext|>


<commit_before>/*******************************************************
 * Copyright (c) 2020, ArrayFire
 * All rights reserved.
 *
 * This file is distributed under 3-clause BSD license.
 * The complete license agreement can be obtained at:
 * http://arrayfire.com/licenses/BSD-3-Clause
 ********************************************************/

#pragma once

#include <backend.hpp>
#include <memory.hpp>
#include <platform.hpp>
#include <thrust/system/cuda/execution_policy.h>

namespace cuda {
struct ThrustArrayFirePolicy
    : thrust::cuda::execution_policy<ThrustArrayFirePolicy> {};

template<typename T>
thrust::pair<thrust::pointer<T, ThrustArrayFirePolicy>, std::ptrdiff_t>
get_temporary_buffer(ThrustArrayFirePolicy, std::ptrdiff_t n) {
    thrust::pointer<T, ThrustArrayFirePolicy> result(
        cuda::memAlloc<T>(n / sizeof(T)).release());

    return thrust::make_pair(result, n);
}

template<typename Pointer>
inline void return_temporary_buffer(ThrustArrayFirePolicy, Pointer p) {
    memFree(thrust::raw_pointer_cast(p));
}

}  // namespace cuda

namespace thrust {
namespace cuda_cub {
template<>
__DH__ inline cudaStream_t get_stream<::cuda::ThrustArrayFirePolicy>(
    execution_policy<::cuda::ThrustArrayFirePolicy> &) {
#if defined(__CUDA_ARCH__)
    return 0;
#else
    return ::cuda::getActiveStream();
#endif
}

__DH__
inline cudaError_t synchronize_stream(const ::cuda::ThrustArrayFirePolicy &) {
#if defined(__CUDA_ARCH__)
    return cudaDeviceSynchronize();
#else
    return cudaStreamSynchronize(::cuda::getActiveStream());
#endif
}

}  // namespace cuda_cub
}  // namespace thrust
<commit_msg>Remove cudaDeviceSynchronize from the ThrustArrayFirePolicy<commit_after>/*******************************************************
 * Copyright (c) 2020, ArrayFire
 * All rights reserved.
 *
 * This file is distributed under 3-clause BSD license.
 * The complete license agreement can be obtained at:
 * http://arrayfire.com/licenses/BSD-3-Clause
 ********************************************************/

#pragma once

#include <backend.hpp>
#include <memory.hpp>
#include <platform.hpp>
#include <thrust/system/cuda/execution_policy.h>

namespace cuda {
struct ThrustArrayFirePolicy
    : thrust::cuda::execution_policy<ThrustArrayFirePolicy> {};

template<typename T>
thrust::pair<thrust::pointer<T, ThrustArrayFirePolicy>, std::ptrdiff_t>
get_temporary_buffer(ThrustArrayFirePolicy, std::ptrdiff_t n) {
    thrust::pointer<T, ThrustArrayFirePolicy> result(
        cuda::memAlloc<T>(n / sizeof(T)).release());

    return thrust::make_pair(result, n);
}

template<typename Pointer>
inline void return_temporary_buffer(ThrustArrayFirePolicy, Pointer p) {
    memFree(thrust::raw_pointer_cast(p));
}

}  // namespace cuda

namespace thrust {
namespace cuda_cub {
template<>
__DH__ inline cudaStream_t get_stream<::cuda::ThrustArrayFirePolicy>(
    execution_policy<::cuda::ThrustArrayFirePolicy> &) {
#if defined(__CUDA_ARCH__)
    return 0;
#else
    return ::cuda::getActiveStream();
#endif
}

__DH__
inline cudaError_t synchronize_stream(const ::cuda::ThrustArrayFirePolicy &) {
#if defined(__CUDA_ARCH__)
    return cudaSuccess;
#else
    return cudaStreamSynchronize(::cuda::getActiveStream());
#endif
}

}  // namespace cuda_cub
}  // namespace thrust
<|endoftext|>


<commit_before>/**
 * Copyright (C) 2015 Virgil Security Inc.
 *
 * Lead Maintainer: Virgil Security Inc. <support@virgilsecurity.com>
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     (1) Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *     (2) Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in
 *     the documentation and/or other materials provided with the
 *     distribution.
 *
 *     (3) Neither the name of the copyright holder nor the names of its
 *     contributors may be used to endorse or promote products derived from
 *     this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <iostream>
#include <string>
#include <stdexcept>

#include <tclap/CmdLine.h>

#include <virgil/sdk/ServicesHub.h>
#include <virgil/sdk/io/Marshaller.h>

#include <cli/version.h>
#include <cli/config.h>
#include <cli/pair.h>
#include <cli/util.h>

namespace vsdk = virgil::sdk;
namespace vcrypto = virgil::crypto;
namespace vcli = virgil::cli;

#ifdef SPLIT_CLI
#define MAIN main
#else
#define MAIN card_search_app_main
#endif

int MAIN(int argc, char** argv) {
    try {
        std::string description = "Search for an Application Virgil Card from the Virgil Keys service.\n";

        std::vector<std::string> examples;
        examples.push_back("Search for application cards:\n"
                           "Virgil card-search-app -c <app_name>\n");

        examples.push_back("Get all application cards:\n"
                           "Virgil card-search-app -c \"*\"\n");

        std::string descriptionMessage = virgil::cli::getDescriptionMessage(description, examples);

        // Parse arguments.
        TCLAP::CmdLine cmd(descriptionMessage, ' ', virgil::cli_version());

        TCLAP::ValueArg<std::string> outArg("o", "out", "Application cards. If omitted, stdout is used.", false, "",
                                            "file");

        TCLAP::ValueArg<std::string> applicationNameArg(
            "c", "application-name", "Application name, name = '*' - get all Cards\n", true, "", "arg");

        cmd.add(applicationNameArg);
        cmd.add(outArg);
        cmd.parse(argc, argv);

        vsdk::ServicesHub servicesHub(VIRGIL_ACCESS_TOKEN);
        std::string appName = "com.virgilsecurity." + applicationNameArg.getValue();

        std::vector<vsdk::models::CardModel> appCards = servicesHub.card().searchApp(appName);
        std::string appCardsStr = virgil::sdk::io::cardsToJson(appCards, 4);
        vcli::writeBytes(outArg.getValue(), appCardsStr);

        if (appCards.empty()) {
            std::cout << "Application card by name: " << applicationNameArg.getValue() << " hasn't been found"
                      << std::endl;
        } else {
            std::cout << "Application card by name: " << applicationNameArg.getValue() << " has been received"
                      << std::endl;
        }

    } catch (TCLAP::ArgException& exception) {
        std::cerr << "card-search-app. Error: " << exception.error() << " for arg " << exception.argId() << std::endl;
        return EXIT_FAILURE;
    } catch (std::exception& exception) {
        std::cerr << "card-search-app. Error: " << exception.what() << std::endl;
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}
<commit_msg>'card-search-app' - saves Application Cards found in separate files<commit_after>/**
 * Copyright (C) 2015 Virgil Security Inc.
 *
 * Lead Maintainer: Virgil Security Inc. <support@virgilsecurity.com>
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     (1) Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *     (2) Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in
 *     the documentation and/or other materials provided with the
 *     distribution.
 *
 *     (3) Neither the name of the copyright holder nor the names of its
 *     contributors may be used to endorse or promote products derived from
 *     this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <iostream>
#include <string>
#include <stdexcept>

#include <tclap/CmdLine.h>

#include <virgil/sdk/ServicesHub.h>
#include <virgil/sdk/io/Marshaller.h>

#include <cli/version.h>
#include <cli/config.h>
#include <cli/pair.h>
#include <cli/util.h>

namespace vsdk = virgil::sdk;
namespace vcrypto = virgil::crypto;
namespace vcli = virgil::cli;

#ifdef SPLIT_CLI
#define MAIN main
#else
#define MAIN card_search_app_main
#endif

int MAIN(int argc, char** argv) {
    try {
        std::string description = "Search for an Application Virgil Card from the Virgil Keys service.\n";

        std::vector<std::string> examples;
        examples.push_back("Search for application cards:\n"
                           "Virgil card-search-app -c <app_name>\n");

        examples.push_back("Get all application cards:\n"
                           "Virgil card-search-app -c \"*\"\n");

        std::string descriptionMessage = virgil::cli::getDescriptionMessage(description, examples);

        // Parse arguments.
        TCLAP::CmdLine cmd(descriptionMessage, ' ', virgil::cli_version());

        TCLAP::ValueArg<std::string> outArg("o", "out", "Folder in which will be saved a Virgil Cards", false, "",
                "arg");

        TCLAP::ValueArg<std::string> applicationNameArg(
            "c", "application-name", "Application name, name = '*' - get all Cards\n", true, "", "arg");

        cmd.add(applicationNameArg);
        cmd.add(outArg);
        cmd.parse(argc, argv);

        vsdk::ServicesHub servicesHub(VIRGIL_ACCESS_TOKEN);
        std::string appName = "com.virgilsecurity." + applicationNameArg.getValue();

        std::vector<vsdk::models::CardModel> appCards = servicesHub.card().searchApp(appName);
        if (appCards.empty()) {
            std::cout << "Cards by name: " << applicationNameArg.getValue() << " haven't been found." << std::endl;
            return EXIT_FAILURE;
        }

        std::string pathTofolder = outArg.getValue();
        if (pathTofolder.empty()) {
            for(auto&& appCard : appCards) {
                std::string appCardStr = vsdk::io::Marshaller<vsdk::models::CardModel>::toJson<4>(appCard);
                vcli::writeBytes(pathTofolder, appCardStr);
            }
        } else {
            for(auto&& appCard : appCards) {
                std::string identity = appCard.getCardIdentity().getValue();
                std::string cardId = appCard.getId();

                std::string fileName = identity + "-id-" + appCard.getId() + ".vcard";
                std::string appCardStr = vsdk::io::Marshaller<vsdk::models::CardModel>::toJson<4>(appCard);
                vcli::writeBytes(pathTofolder + "/" + fileName, appCardStr);
            }
        }

        std::cout << "По заданному application name:" << applicationNameArg.getValue() << " получено "
                  << appCards.size() << " Карт." << std::endl;

    } catch (TCLAP::ArgException& exception) {
        std::cerr << "card-search-app. Error: " << exception.error() << " for arg " << exception.argId() << std::endl;
        return EXIT_FAILURE;
    } catch (std::exception& exception) {
        std::cerr << "card-search-app. Error: " << exception.what() << std::endl;
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2006-2013  Music Technology Group - Universitat Pompeu Fabra
 *
 * This file is part of Essentia
 *
 * Essentia is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation (FSF), either version 3 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the Affero GNU General Public License
 * version 3 along with this program.  If not, see http://www.gnu.org/licenses/
 */

#include <iostream>
#include <fstream>
#include <essentia/algorithmfactory.h>
#include <essentia/pool.h>
#include <essentia/essentiamath.h>

//#define INCLUDE_DELTA_SC

using namespace std;
using namespace essentia;
using namespace standard;

int main(int argc, char* argv[]) {

  if (argc != 3) {
    cout << "ERROR: incorrect number of arguments." << endl;
    cout << "Usage: " << argv[0] << " audio_input yaml_output" << endl;
    exit(1);
  }

  string audioFilename = argv[1];
  string outputFilename = argv[2];

  essentia::init();

  /********** SETUP ALGORITHMS **********/

  int frameSize  = 2048;
  int hopSize    = 1024;
  int sampleRate = 44100;

  AlgorithmFactory& factory = AlgorithmFactory::instance();

  Algorithm* audio      = factory.create("EqloudLoader",
                                         "filename", audioFilename,
                                         "sampleRate", sampleRate,
                                         "replayGain", -6.0);

  Algorithm* fcutter    = factory.create("FrameCutter",
                                         "frameSize", frameSize,
                                         "hopSize", hopSize);

  Algorithm* window     = factory.create("Windowing",
                                         "type", "blackmanharris62");

  Algorithm* fft        = factory.create("Spectrum");

  Algorithm* sc         = factory.create("SpectralContrast",
                                         "frameSize", frameSize,
                                         "sampleRate", sampleRate,
                                         "numberBands", 6,
                                         "lowFrequencyBound", 20,
                                         "highFrequencyBound", 11000,
                                         "neighbourRatio", 0.4,
                                         "staticDistribution", 0.15);

  /********** SETUP CONNECTIONS **********/

  vector<Real> audioBuffer;
  audio->output("audio").set(audioBuffer);
  fcutter->input("signal").set(audioBuffer);

  vector<Real> frame, windowedFrame;

  fcutter->output("frame").set(frame);
  window->input("frame").set(frame);

  window->output("frame").set(windowedFrame);
  fft->input("frame").set(windowedFrame);

  vector<Real> spectrum;

  fft->output("spectrum").set(spectrum);
  sc->input("spectrum").set(spectrum);

  vector<Real> sccoeffs;
  vector<Real> scvalleys;

  sc->output("spectralContrast").set(sccoeffs);
  sc->output("spectralValley").set(scvalleys);

  /********** COMPUTATION **********/

  audio->compute();

  Pool poolSc, poolTransformed, poolOut;

#ifdef INCLUDE_DELTA_SC
  bool add = false;
  vector<Real> prevFrame;
#endif

  /**** frame by frame ****/
  while (true) {

    // get a single frame
    fcutter->compute();

    // if it was the last one (ie: it was empty), then we're done.
    if (!frame.size())
      break;

    // if the frame is silent, just drop it and go on processing
    if (isSilent(frame)) continue;

    // C.O.M.P.U.T.E.
    window->compute();
    fft->compute();
    sc->compute();

    // merge the valleys and the contrasts so they can be transformed in one go
    vector<Real> merged;
    for(uint i=0; i<sccoeffs.size(); i++) {
      merged.push_back(sccoeffs[i]);
      merged.push_back(scvalleys[i]);
    }
#ifndef INCLUDE_DELTA_SC
    poolSc.add("contrast", merged);
#endif

#ifdef INCLUDE_DELTA_SC
    uint size = merged.size();

    if(add) {
      vector<Real> diff;
      for(uint i=0; i<size; i++) {
  merged.push_back(merged[i]-prevFrame[i]);
      }
      poolSc.add("contrast", merged);
    }

    prevFrame.clear();
    for(uint i=0; i<size; i++)
  prevFrame.push_back(merged[i]);

    add = true;
#endif // INCLUDE_DELTA_SC
  }

  /**** song by song ****/

  // do the PCA
  Algorithm* pca    = AlgorithmFactory::create("PCA",
                                                         "namespaceIn",  "contrast",
                                                         "namespaceOut", "contrast");
  pca->input("poolIn").set(poolSc);
  pca->output("poolOut").set(poolTransformed);
  pca->compute();

  /* without PCA
  vector<vector<Real> > rawFeats = poolSC.value<vector<Real> >("contrast");
  poolOUT.add("contrast.means", meanFrames(rawFeats));
  poolOUT.add("contrast.vars" , varianceFrames(rawFeats));
  */

  poolOut.add("contrast.means", meanFrames(poolTransformed.value<vector<vector<Real> > >("contrast")));
  poolOut.add("contrast.variances", varianceFrames(poolTransformed.value<vector<vector<Real> > >("contrast")));

  // write yaml file
  Algorithm* output = AlgorithmFactory::create("YamlOutput", "filename", outputFilename);
  output->input("pool").set(poolOut);
  output->compute();

  // clean up
  delete audio;
  delete fcutter;
  delete window;
  delete fft;
  delete sc;
  delete pca;
  delete output;

  essentia::shutdown();

  return 0;
}
<commit_msg>fixed spectral contrast extractor (removed pca, output both coeffs and valleys separately)<commit_after>/*
 * Copyright (C) 2006-2013  Music Technology Group - Universitat Pompeu Fabra
 *
 * This file is part of Essentia
 *
 * Essentia is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation (FSF), either version 3 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the Affero GNU General Public License
 * version 3 along with this program.  If not, see http://www.gnu.org/licenses/
 */

#include <iostream>
#include <fstream>
#include <essentia/algorithmfactory.h>
#include <essentia/pool.h>
#include <essentia/essentiamath.h>

using namespace std;
using namespace essentia;
using namespace standard;

int main(int argc, char* argv[]) {

  if (argc != 3) {
    cout << "ERROR: incorrect number of arguments." << endl;
    cout << "Usage: " << argv[0] << " audio_input yaml_output" << endl;
    exit(1);
  }

  string audioFilename = argv[1];
  string outputFilename = argv[2];

  essentia::init();

  /********** SETUP ALGORITHMS **********/

  int frameSize  = 2048;
  int hopSize    = 1024;
  int sampleRate = 44100;

  AlgorithmFactory& factory = AlgorithmFactory::instance();

  Algorithm* audio      = factory.create("EqloudLoader",
                                         "filename", audioFilename,
                                         "sampleRate", sampleRate,
                                         "replayGain", -6.0);

  Algorithm* fcutter    = factory.create("FrameCutter",
                                         "frameSize", frameSize,
                                         "hopSize", hopSize);

  Algorithm* window     = factory.create("Windowing",
                                         "type", "blackmanharris62");

  Algorithm* fft        = factory.create("Spectrum");

  Algorithm* sc         = factory.create("SpectralContrast",
                                         "frameSize", frameSize,
                                         "sampleRate", sampleRate,
                                         "numberBands", 6,
                                         "lowFrequencyBound", 20,
                                         "highFrequencyBound", 11000,
                                         "neighbourRatio", 0.4,
                                         "staticDistribution", 0.15);

  /********** SETUP CONNECTIONS **********/

  vector<Real> audioBuffer;
  audio->output("audio").set(audioBuffer);
  fcutter->input("signal").set(audioBuffer);

  vector<Real> frame, windowedFrame;

  fcutter->output("frame").set(frame);
  window->input("frame").set(frame);

  window->output("frame").set(windowedFrame);
  fft->input("frame").set(windowedFrame);

  vector<Real> spectrum;

  fft->output("spectrum").set(spectrum);
  sc->input("spectrum").set(spectrum);

  vector<Real> sccoeffs;
  vector<Real> scvalleys;

  sc->output("spectralContrast").set(sccoeffs);
  sc->output("spectralValley").set(scvalleys);

  /********** COMPUTATION **********/

  audio->compute();

  Pool poolSc, poolOut;

  /**** frame by frame ****/
  while (true) {

    // get a single frame
    fcutter->compute();

    // if it was the last one (ie: it was empty), then we're done.
    if (!frame.size())
      break;

    // if the frame is silent, just drop it and go on processing
    if (isSilent(frame)) continue;

    // compute
    window->compute();
    fft->compute();
    sc->compute();

    poolSc.add("contrast_coeffs", sccoeffs);
    poolSc.add("contrast_valleys", scvalleys);
  }

  poolOut.add("contrast_coeffs.means", meanFrames(poolSc.value<vector<vector<Real> > >("contrast_coeffs")));
  poolOut.add("contrast_coeffs.vars" , varianceFrames(poolSc.value<vector<vector<Real> > >("contrast_coeffs")));
  poolOut.add("contrast_valleys.means", meanFrames(poolSc.value<vector<vector<Real> > >("contrast_valleys")));
  poolOut.add("contrast_valleys.vars" , varianceFrames(poolSc.value<vector<vector<Real> > >("contrast_valleys")));

  // write yaml file
  Algorithm* output = AlgorithmFactory::create("YamlOutput", "filename", outputFilename);
  output->input("pool").set(poolOut);
  output->compute();

  // clean up
  delete audio;
  delete fcutter;
  delete window;
  delete fft;
  delete sc;
  delete output;

  essentia::shutdown();

  return 0;
}
<|endoftext|>


<commit_before>/** \copyright
 * Copyright (c) 2014, Balazs Racz
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are  permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file TivaNRZ.hxx
 *
 * Device driver for TivaWare to decode DCC track signal.
 *
 * @author Balazs Racz
 * @date 29 Nov 2014
 */

#include "TivaDCC.hxx" // for FixedQueue
#include "TivaGPIO.hxx" // for debug pins
#include "dcc/Receiver.hxx"

typedef DummyPin PIN_RailcomCutout;

#define SIGNAL_LEVEL_ONE 0x80000000UL

/*
struct DCCDecode
{
    static const auto TIMER_BASE = WTIMER4_BASE;
    static const auto TIMER_PERIPH = SYSCTL_PERIPH_WTIMER4;
    static const auto TIMER_INTERRUPT = INT_WTIMER4A;
    static const auto TIMER = TIMER_A;
    static const auto CFG_CAP_TIME_UP =
        TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_CAP_TIME_UP | TIMER_CFG_B_ONE_SHOT;
    // Interrupt bits.
    static const auto TIMER_CAP_EVENT = TIMER_CAPA_EVENT;
    static const auto TIMER_TIM_TIMEOUT = TIMER_TIMA_TIMEOUT;

    static const auto OS_INTERRUPT = INT_WTIMER4B;
    DECL_PIN(NRZPIN, D, 4);
    static const auto NRZPIN_CONFIG = GPIO_PD4_WT4CCP0;

    static const uint32_t TIMER_MAX_VALUE = 0x8000000UL;

    static const int Q_SIZE = 16;

};
*/

template <class HW> class TivaDccDecoder : public Node
{
public:
    TivaDccDecoder(const char *name);

    ~TivaDccDecoder()
    {
    }

    /** Handles a raw interrupt. */
    inline void interrupt_handler() __attribute__((always_inline));

    /** Handles a software interrupt to FreeRTOS. */
    inline void os_interrupt_handler() __attribute__((always_inline));

private:
    /** Read from a file or device.
     * @param file file reference for this device
     * @param buf location to place read data
     * @param count number of bytes to read
     * @return number of bytes read upon success, -1 upon failure with errno
     * containing the cause
     */
    ssize_t read(File *file, void *buf, size_t count) OVERRIDE;

    /** Write to a file or device.
     * @param file file reference for this device
     * @param buf location to find write data
     * @param count number of bytes to write
     * @return number of bytes written upon success, -1 upon failure with errno
     * containing the cause
     */
    ssize_t write(File *file, const void *buf, size_t count) OVERRIDE
    {
        return -EINVAL;
    }

    /** Request an ioctl transaction
     * @param file file reference for this device
     * @param node node reference for this device
     * @param key ioctl key
     * @param data key data
     */
    int ioctl(File *file, unsigned long int key, unsigned long data) OVERRIDE;

    void enable();  /**< function to enable device */
    void disable(); /**< function to disable device */

    /** Discards all pending buffers.  Called after disable().
     */
    void flush_buffers()
    {
        while (!inputData_.empty())
        {
            inputData_.increment_front();
        }
    };

    FixedQueue<uint32_t, HW::Q_SIZE> inputData_;
    uint32_t lastTimerValue_;
    uint32_t reloadCount_;
    unsigned lastLevel_;
    bool overflowed_ = false;

    Notifiable *readableNotifiable_ = nullptr;

    dcc::DccDecoder decoder_;

    DISALLOW_COPY_AND_ASSIGN(TivaDccDecoder);
};

template <class HW>
TivaDccDecoder<HW>::TivaDccDecoder(const char *name)
    : Node(name)
{
    MAP_SysCtlPeripheralEnable(HW::TIMER_PERIPH);
    MAP_SysCtlPeripheralEnable(HW::NRZPIN_PERIPH);
    MAP_GPIOPinConfigure(HW::NRZPIN_CONFIG);
    MAP_GPIOPinTypeTimer(HW::NRZPIN_BASE, HW::NRZPIN_PIN);

    disable();
}

template <class HW> void TivaDccDecoder<HW>::enable()
{
    disable();
    MAP_TimerClockSourceSet(HW::TIMER_BASE, TIMER_CLOCK_SYSTEM);
    MAP_TimerConfigure(HW::TIMER_BASE, HW::CFG_CAP_TIME_UP);
    MAP_TimerControlStall(HW::TIMER_BASE, HW::TIMER, true);
    MAP_TimerControlEvent(HW::TIMER_BASE, HW::TIMER, TIMER_EVENT_BOTH_EDGES);
    MAP_TimerLoadSet(HW::TIMER_BASE, HW::TIMER, HW::TIMER_MAX_VALUE);
    MAP_TimerPrescaleSet(HW::TIMER_BASE, HW::TIMER, HW::PS_MAX);

    reloadCount_ = 0;
    lastTimerValue_ = 0;

    MAP_TimerIntEnable(HW::TIMER_BASE, HW::TIMER_CAP_EVENT);
    MAP_TimerIntEnable(HW::TIMER_BASE, HW::TIMER_TIM_TIMEOUT);

    MAP_IntPrioritySet(HW::TIMER_INTERRUPT, 0);
    MAP_IntPrioritySet(HW::OS_INTERRUPT, configKERNEL_INTERRUPT_PRIORITY);
    MAP_IntEnable(HW::OS_INTERRUPT);
    MAP_IntEnable(HW::TIMER_INTERRUPT);

    MAP_TimerEnable(HW::TIMER_BASE, HW::TIMER);
}

template <class HW> void TivaDccDecoder<HW>::disable()
{
    MAP_IntDisable(HW::TIMER_INTERRUPT);
    MAP_IntDisable(HW::OS_INTERRUPT);
    MAP_TimerDisable(HW::TIMER_BASE, HW::TIMER);
}

template <class HW>
__attribute__((optimize("-O3"))) void TivaDccDecoder<HW>::interrupt_handler()
{
    // get masked interrupt status
    auto status = MAP_TimerIntStatus(HW::TIMER_BASE, true);
    if (status & HW::TIMER_TIM_TIMEOUT)
    {
        // The timer got reloaded.
        reloadCount_++;
        MAP_TimerIntClear(HW::TIMER_BASE, HW::TIMER_TIM_TIMEOUT);
    }
    // TODO(balazs.racz): Technically it is possible that the timer reload
    // happens between the event match and the interrupt entry. In this case we
    // will incorrectly add a full cycle to the event length.
    if (status & HW::TIMER_CAP_EVENT)
    {
        MAP_TimerIntClear(HW::TIMER_BASE, HW::TIMER_CAP_EVENT);
        static uint32_t raw_new_value;
        raw_new_value = MAP_TimerValueGet(HW::TIMER_BASE, HW::TIMER);
        static uint32_t new_value;
        new_value = raw_new_value;
        while (reloadCount_ > 0)
        {
            new_value += HW::TIMER_MAX_VALUE;
            reloadCount_--;
        }
        //HASSERT(new_value > lastTimerValue_);
        new_value -= lastTimerValue_;
        /*if (!inputData_.full())
        {
            inputData_.back() = overflowed_ ? 3 : new_value;
            overflowed_ = false;
            inputData_.increment_back();
        } else {
            overflowed_ = true;
            }*/
        decoder_.process_data(new_value);
        if (decoder_.state() == dcc::DccDecoder::DCC_MAYBE_CUTOUT) {
            PIN_RailcomCutout::set(true);
            for (unsigned i = 0; i < ARRAYSIZE(HW::RAILCOM_UART_BASE); ++i) {
                HWREG(HW::RAILCOM_UART_BASE[i] + UART_O_CTL) |= UART_CTL_RXE;
            }
        } else if (decoder_.state() == dcc::DccDecoder::DCC_PREAMBLE) {
            PIN_RailcomCutout::set(false);
        }
        lastTimerValue_ = raw_new_value;
        MAP_IntPendSet(HW::OS_INTERRUPT);
    }
}

template <class HW>
__attribute__((optimize("-O3"))) void TivaDccDecoder<HW>::os_interrupt_handler()
{
    if (!inputData_.empty())
    {
        Notifiable *n = readableNotifiable_;
        readableNotifiable_ = nullptr;
        if (n) {
            n->notify_from_isr();
            os_isr_exit_yield_test(true);
        }
    }
}

template <class HW>
int TivaDccDecoder<HW>::ioctl(File *file, unsigned long int key, unsigned long data)
{
    if (IOC_TYPE(key) == CAN_IOC_MAGIC && IOC_SIZE(key) == NOTIFIABLE_TYPE &&
        key == CAN_IOC_READ_ACTIVE)
    {
        Notifiable *n = reinterpret_cast<Notifiable *>(data);
        HASSERT(n);
        // If there is no data for reading, we put the incoming notification
        // into the holder. Otherwise we notify it immediately.
        if (inputData_.empty())
        {
            portENTER_CRITICAL();
            if (inputData_.empty())
            {
                // We are in a critical section now. If we got into this
                // branch, then the buffer was full at the beginning of the
                // critical section. If the hardware interrupt kicks in now,
                // and sets the os_interrupt to pending, the os interrupt will
                // not happen until we leave the critical section, and thus the
                // swap will be in effect by then.
                swap(n, readableNotifiable_);
            }
            portEXIT_CRITICAL();
        }
        if (n)
        {
            n->notify();
        }
        return 0;
    }
    errno = EINVAL;
    return -1;
}

/** Read from a file or device.
 * @param file file reference for this device
 * @param buf location to place read data
 * @param count number of bytes to read
 * @return number of bytes read upon success, -1 upon failure with errno containing the cause
 */
template<class HW>
ssize_t TivaDccDecoder<HW>::read(File *file, void *buf, size_t count)
{
    if (count != 4)
    {
        return -EINVAL;
    }
    // We only need this critical section to prevent concurrent threads from
    // reading at the same time.
    portENTER_CRITICAL();
    if (inputData_.empty()) {
        portEXIT_CRITICAL();
        return -EAGAIN;
    }
    uint32_t v = reinterpret_cast<uint32_t>(buf);
    HASSERT((v & 3) == 0); // alignment check.
    uint32_t* pv = static_cast<uint32_t*>(buf);
    *pv = inputData_.front();
    inputData_.increment_front();
    portEXIT_CRITICAL();
    return count;
}
<commit_msg>removes direct access to the railcom UART registers and adds railcom driver pointer for notifying about the cutout.<commit_after>/** \copyright
 * Copyright (c) 2014, Balazs Racz
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are  permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file TivaNRZ.hxx
 *
 * Device driver for TivaWare to decode DCC track signal.
 *
 * @author Balazs Racz
 * @date 29 Nov 2014
 */

#include "TivaDCC.hxx"  // for FixedQueue
#include "TivaGPIO.hxx" // for pin definitions
#include "RailcomDriver.hxx" // for debug pins
#include "dcc/Receiver.hxx"

typedef DummyPin PIN_RailcomCutout;

#define SIGNAL_LEVEL_ONE 0x80000000UL

/*
struct DCCDecode
{
    static const auto TIMER_BASE = WTIMER4_BASE;
    static const auto TIMER_PERIPH = SYSCTL_PERIPH_WTIMER4;
    static const auto TIMER_INTERRUPT = INT_WTIMER4A;
    static const auto TIMER = TIMER_A;
    static const auto CFG_CAP_TIME_UP =
        TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_CAP_TIME_UP | TIMER_CFG_B_ONE_SHOT;
    // Interrupt bits.
    static const auto TIMER_CAP_EVENT = TIMER_CAPA_EVENT;
    static const auto TIMER_TIM_TIMEOUT = TIMER_TIMA_TIMEOUT;

    static const auto OS_INTERRUPT = INT_WTIMER4B;
    DECL_PIN(NRZPIN, D, 4);
    static const auto NRZPIN_CONFIG = GPIO_PD4_WT4CCP0;

    static const uint32_t TIMER_MAX_VALUE = 0x8000000UL;

    static const int Q_SIZE = 16;

};
*/

template <class HW> class TivaDccDecoder : public Node
{
public:
    TivaDccDecoder(const char *name, RailcomDriver *railcom_driver);

    ~TivaDccDecoder()
    {
    }

    /** Handles a raw interrupt. */
    inline void interrupt_handler() __attribute__((always_inline));

    /** Handles a software interrupt to FreeRTOS. */
    inline void os_interrupt_handler() __attribute__((always_inline));

private:
    /** Read from a file or device.
     * @param file file reference for this device
     * @param buf location to place read data
     * @param count number of bytes to read
     * @return number of bytes read upon success, -1 upon failure with errno
     * containing the cause
     */
    ssize_t read(File *file, void *buf, size_t count) OVERRIDE;

    /** Write to a file or device.
     * @param file file reference for this device
     * @param buf location to find write data
     * @param count number of bytes to write
     * @return number of bytes written upon success, -1 upon failure with errno
     * containing the cause
     */
    ssize_t write(File *file, const void *buf, size_t count) OVERRIDE
    {
        return -EINVAL;
    }

    /** Request an ioctl transaction
     * @param file file reference for this device
     * @param node node reference for this device
     * @param key ioctl key
     * @param data key data
     */
    int ioctl(File *file, unsigned long int key, unsigned long data) OVERRIDE;

    void enable();  /**< function to enable device */
    void disable(); /**< function to disable device */

    /** Discards all pending buffers.  Called after disable().
     */
    void flush_buffers()
    {
        while (!inputData_.empty())
        {
            inputData_.increment_front();
        }
    };

    FixedQueue<uint32_t, HW::Q_SIZE> inputData_;
    uint32_t lastTimerValue_;
    uint32_t reloadCount_;
    unsigned lastLevel_;
    bool overflowed_ = false;

    Notifiable *readableNotifiable_ = nullptr;
    RailcomDriver *railcomDriver_; //< notified for cutout events.

    dcc::DccDecoder decoder_;

    DISALLOW_COPY_AND_ASSIGN(TivaDccDecoder);
};

template <class HW>
TivaDccDecoder<HW>::TivaDccDecoder(const char *name,
                                   RailcomDriver *railcom_driver)
    : Node(name)
    , railcomDriver_(railcom_driver)
{
    MAP_SysCtlPeripheralEnable(HW::TIMER_PERIPH);
    MAP_SysCtlPeripheralEnable(HW::NRZPIN_PERIPH);
    MAP_GPIOPinConfigure(HW::NRZPIN_CONFIG);
    MAP_GPIOPinTypeTimer(HW::NRZPIN_BASE, HW::NRZPIN_PIN);

    disable();
}

template <class HW> void TivaDccDecoder<HW>::enable()
{
    disable();
    MAP_TimerClockSourceSet(HW::TIMER_BASE, TIMER_CLOCK_SYSTEM);
    MAP_TimerConfigure(HW::TIMER_BASE, HW::CFG_CAP_TIME_UP);
    MAP_TimerControlStall(HW::TIMER_BASE, HW::TIMER, true);
    MAP_TimerControlEvent(HW::TIMER_BASE, HW::TIMER, TIMER_EVENT_BOTH_EDGES);
    MAP_TimerLoadSet(HW::TIMER_BASE, HW::TIMER, HW::TIMER_MAX_VALUE);
    MAP_TimerPrescaleSet(HW::TIMER_BASE, HW::TIMER, HW::PS_MAX);

    reloadCount_ = 0;
    lastTimerValue_ = 0;

    MAP_TimerIntEnable(HW::TIMER_BASE, HW::TIMER_CAP_EVENT);
    MAP_TimerIntEnable(HW::TIMER_BASE, HW::TIMER_TIM_TIMEOUT);

    MAP_IntPrioritySet(HW::TIMER_INTERRUPT, 0);
    MAP_IntPrioritySet(HW::OS_INTERRUPT, configKERNEL_INTERRUPT_PRIORITY);
    MAP_IntEnable(HW::OS_INTERRUPT);
    MAP_IntEnable(HW::TIMER_INTERRUPT);

    MAP_TimerEnable(HW::TIMER_BASE, HW::TIMER);
}

template <class HW> void TivaDccDecoder<HW>::disable()
{
    MAP_IntDisable(HW::TIMER_INTERRUPT);
    MAP_IntDisable(HW::OS_INTERRUPT);
    MAP_TimerDisable(HW::TIMER_BASE, HW::TIMER);
}

template <class HW>
__attribute__((optimize("-O3"))) void TivaDccDecoder<HW>::interrupt_handler()
{
    // get masked interrupt status
    auto status = MAP_TimerIntStatus(HW::TIMER_BASE, true);
    if (status & HW::TIMER_TIM_TIMEOUT)
    {
        // The timer got reloaded.
        reloadCount_++;
        MAP_TimerIntClear(HW::TIMER_BASE, HW::TIMER_TIM_TIMEOUT);
    }
    // TODO(balazs.racz): Technically it is possible that the timer reload
    // happens between the event match and the interrupt entry. In this case we
    // will incorrectly add a full cycle to the event length.
    if (status & HW::TIMER_CAP_EVENT)
    {
        MAP_TimerIntClear(HW::TIMER_BASE, HW::TIMER_CAP_EVENT);
        static uint32_t raw_new_value;
        raw_new_value = MAP_TimerValueGet(HW::TIMER_BASE, HW::TIMER);
        static uint32_t new_value;
        new_value = raw_new_value;
        while (reloadCount_ > 0)
        {
            new_value += HW::TIMER_MAX_VALUE;
            reloadCount_--;
        }
        // HASSERT(new_value > lastTimerValue_);
        new_value -= lastTimerValue_;
        /*if (!inputData_.full())
        {
            inputData_.back() = overflowed_ ? 3 : new_value;
            overflowed_ = false;
            inputData_.increment_back();
        } else {
            overflowed_ = true;
            }*/
        decoder_.process_data(new_value);
        if (decoder_.state() == dcc::DccDecoder::DCC_MAYBE_CUTOUT)
        {
            railcomDriver_->start_cutout();
        }
        /// TODO(balazs.racz) recognize middle cutout.
        else if (decoder_.state() == dcc::DccDecoder::DCC_PREAMBLE)
        {
            railcomDriver_->end_cutout();
        }
        lastTimerValue_ = raw_new_value;
        MAP_IntPendSet(HW::OS_INTERRUPT);
    }
}

template <class HW>
__attribute__((optimize("-O3"))) void TivaDccDecoder<HW>::os_interrupt_handler()
{
    if (!inputData_.empty())
    {
        Notifiable *n = readableNotifiable_;
        readableNotifiable_ = nullptr;
        if (n)
        {
            n->notify_from_isr();
            os_isr_exit_yield_test(true);
        }
    }
}

template <class HW>
int TivaDccDecoder<HW>::ioctl(File *file, unsigned long int key,
                              unsigned long data)
{
    if (IOC_TYPE(key) == CAN_IOC_MAGIC && IOC_SIZE(key) == NOTIFIABLE_TYPE &&
        key == CAN_IOC_READ_ACTIVE)
    {
        Notifiable *n = reinterpret_cast<Notifiable *>(data);
        HASSERT(n);
        // If there is no data for reading, we put the incoming notification
        // into the holder. Otherwise we notify it immediately.
        if (inputData_.empty())
        {
            portENTER_CRITICAL();
            if (inputData_.empty())
            {
                // We are in a critical section now. If we got into this
                // branch, then the buffer was full at the beginning of the
                // critical section. If the hardware interrupt kicks in now,
                // and sets the os_interrupt to pending, the os interrupt will
                // not happen until we leave the critical section, and thus the
                // swap will be in effect by then.
                swap(n, readableNotifiable_);
            }
            portEXIT_CRITICAL();
        }
        if (n)
        {
            n->notify();
        }
        return 0;
    }
    errno = EINVAL;
    return -1;
}

/** Read from a file or device.
 * @param file file reference for this device
 * @param buf location to place read data
 * @param count number of bytes to read
 * @return number of bytes read upon success, -1 upon failure with errno
 * containing the cause
 */
template <class HW>
ssize_t TivaDccDecoder<HW>::read(File *file, void *buf, size_t count)
{
    if (count != 4)
    {
        return -EINVAL;
    }
    // We only need this critical section to prevent concurrent threads from
    // reading at the same time.
    portENTER_CRITICAL();
    if (inputData_.empty())
    {
        portEXIT_CRITICAL();
        return -EAGAIN;
    }
    uint32_t v = reinterpret_cast<uint32_t>(buf);
    HASSERT((v & 3) == 0); // alignment check.
    uint32_t *pv = static_cast<uint32_t *>(buf);
    *pv = inputData_.front();
    inputData_.increment_front();
    portEXIT_CRITICAL();
    return count;
}
<|endoftext|>