<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;

#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 {
    optional<String> GetCiphrName_ (CipherAlgorithm a)
    {
        if (auto i = ::EVP_CIPHER_name (a)) {
            return String::FromASCII (i);
        }
        return nullopt;
    }
}

/*
 ********************************************************************************
 ******************* Cryptography::OpenSSL::LibraryContext **********************
 ********************************************************************************
 */
LibraryContext LibraryContext::sDefault;

LibraryContext::LibraryContext ()
    : pAvailableAlgorithms{
          [qStroika_Foundation_Common_Property_ExtraCaptureStuff] ([[maybe_unused]] const auto* property) -> Set<CipherAlgorithm> {
              const LibraryContext*                               thisObj = qStroika_Foundation_Common_Property_OuterObjPtr (property, &LibraryContext::pAvailableAlgorithms);
              shared_lock<const AssertExternallySynchronizedLock> critSec{*thisObj};
              Set<String>                                         ciphers;
              ::EVP_CIPHER_do_all_provided (
                  nullptr,
                  [] (EVP_CIPHER* ciph, void* arg) {
                      Set<String>* ciphers = reinterpret_cast<Set<String>*> (arg);
                      if (ciph != nullptr) {
                          DbgTrace (L"cipher: %p (name: %s), provider: %p", ciph, CipherAlgorithm{ciph}.pName ().c_str (), ::EVP_CIPHER_provider (ciph));
                          if (auto cipherName = GetCiphrName_ (ciph)) {

                              if (auto provider = ::EVP_CIPHER_provider (ciph)) {
                                  DbgTrace ("providername = %s", ::OSSL_PROVIDER_name (provider));
                              }
                              int flags = ::EVP_CIPHER_flags (ciph);
                              DbgTrace ("flags=%x", flags);

                              ciphers->Add (*cipherName);
                          }
                      }
                  },
                  &ciphers);
              DbgTrace (L"Found kAllLoadedCiphers=%s", Characters::ToString (ciphers).c_str ());

              auto fn = [] (const String& n) -> optional<CipherAlgorithm> { return OpenSSL::GetCipherByNameQuietly (n); };

              Traversal::Iterable<int> yyy{};
              Set<int>                 resultyyy{yyy};

              using namespace Configuration;
              static_assert (IsIterable_v<Traversal::Iterable<CipherAlgorithm>>);

              using ITERABLE_OF_T = Traversal::Iterable<CipherAlgorithm>;
    //Configuration::Private::  IsIterableOfT_Impl2_<set<int>, int> aa;

#if 1

              Set<CipherAlgorithm> result{ciphers.Select<CipherAlgorithm> ([] (const String& n) -> optional<CipherAlgorithm> { return OpenSSL::GetCipherByNameQuietly (n); })};
              WeakAssert (result.size () == ciphers.size ());
#endif
              return result;
          }}
{
    LoadProvider (kDefaultProvider);
}

LibraryContext ::~LibraryContext ()
{
    lock_guard<const AssertExternallySynchronizedLock> critSec{*this};
#if OPENSSL_VERSION_MAJOR >= 3
    for (auto i : fLoadedProviders_) {
        Verify (::OSSL_PROVIDER_unload (i.fValue.first) == 1);
    }
#endif
}

void LibraryContext::LoadProvider (const String& providerName)
{
    lock_guard<const AssertExternallySynchronizedLock> critSec{*this};
#if OPENSSL_VERSION_MAJOR >= 3
    OSSL_PROVIDER*                                p = ::OSSL_PROVIDER_load (NULL, providerName.AsNarrowSDKString ().c_str ());
    static const Execution::RuntimeErrorException kErr_{L"No such SSL provider"sv};
    Execution::ThrowIfNull (p, kErr_);
    if (auto l = fLoadedProviders_.Lookup (providerName)) {
        l->second++;
        fLoadedProviders_.Add (providerName, *l);
    }
    else {
        fLoadedProviders_.Add (providerName, {p, 1});
    }
#else
    Require (providerName == kDefaultProvider or providerName == kLegacyProvider);
#endif
}

void LibraryContext ::UnLoadProvider (const String& providerName)
{
    lock_guard<const AssertExternallySynchronizedLock> critSec{*this};
#if OPENSSL_VERSION_MAJOR >= 3
    Require (fLoadedProviders_.ContainsKey (providerName));
    auto l = fLoadedProviders_.Lookup (providerName);
    Assert (l);
    l->second--;
    if (l->second == 0) {
        fLoadedProviders_.Remove (providerName);
        Verify (::OSSL_PROVIDER_unload (l->first) == 1);
    }
    else {
        fLoadedProviders_.Add (providerName, *l);
    }
#endif
}
#endif
<commit_msg>Tweaks to openssl code for non-ssl3<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;

#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 {
    optional<String> GetCiphrName_ (CipherAlgorithm a)
    {
        if (auto i = ::EVP_CIPHER_name (a)) {
            return String::FromASCII (i);
        }
        return nullopt;
    }
}

/*
 ********************************************************************************
 ******************* Cryptography::OpenSSL::LibraryContext **********************
 ********************************************************************************
 */
LibraryContext LibraryContext::sDefault;

LibraryContext::LibraryContext ()
    : pAvailableAlgorithms{
          [qStroika_Foundation_Common_Property_ExtraCaptureStuff] ([[maybe_unused]] const auto* property) -> Set<CipherAlgorithm> {
              const LibraryContext*                               thisObj = qStroika_Foundation_Common_Property_OuterObjPtr (property, &LibraryContext::pAvailableAlgorithms);
              shared_lock<const AssertExternallySynchronizedLock> critSec{*thisObj};
              Set<String>                                         ciphers;
              ::EVP_CIPHER_do_all_provided (
                  nullptr,
                  [] (EVP_CIPHER* ciph, void* arg) {
                      Set<String>* ciphers = reinterpret_cast<Set<String>*> (arg);
                      if (ciph != nullptr) {
                          DbgTrace (L"cipher: %p (name: %s), provider: %p", ciph, CipherAlgorithm{ciph}.pName ().c_str (), ::EVP_CIPHER_provider (ciph));
                          Assert (GetCiphrName_ (ciph));
                          if (auto cipherName = GetCiphrName_ (ciph)) {

#if OPENSSL_VERSION_MAJOR >= 3
                              if (auto provider = ::EVP_CIPHER_provider (ciph)) {
                                  DbgTrace ("providername = %s", ::OSSL_PROVIDER_name (provider));
                              }
#endif
                              int flags = ::EVP_CIPHER_flags (ciph);
                              DbgTrace ("flags=%x", flags);

                              ciphers->Add (*cipherName);
                          }
                      }
                  },
                  &ciphers);
              DbgTrace (L"Found kAllLoadedCiphers=%s", Characters::ToString (ciphers).c_str ());

              auto fn = [] (const String& n) -> optional<CipherAlgorithm> { return OpenSSL::GetCipherByNameQuietly (n); };

              Traversal::Iterable<int> yyy{};
              Set<int>                 resultyyy{yyy};

              using namespace Configuration;
              static_assert (IsIterable_v<Traversal::Iterable<CipherAlgorithm>>);

              using ITERABLE_OF_T = Traversal::Iterable<CipherAlgorithm>;
    //Configuration::Private::  IsIterableOfT_Impl2_<set<int>, int> aa;

#if 1

              Set<CipherAlgorithm> result{ciphers.Select<CipherAlgorithm> ([] (const String& n) -> optional<CipherAlgorithm> { return OpenSSL::GetCipherByNameQuietly (n); })};
              WeakAssert (result.size () == ciphers.size ());
#endif
              return result;
          }}
{
    LoadProvider (kDefaultProvider);
}

LibraryContext ::~LibraryContext ()
{
    lock_guard<const AssertExternallySynchronizedLock> critSec{*this};
#if OPENSSL_VERSION_MAJOR >= 3
    for (auto i : fLoadedProviders_) {
        Verify (::OSSL_PROVIDER_unload (i.fValue.first) == 1);
    }
#endif
}

void LibraryContext::LoadProvider (const String& providerName)
{
    lock_guard<const AssertExternallySynchronizedLock> critSec{*this};
#if OPENSSL_VERSION_MAJOR >= 3
    OSSL_PROVIDER*                                p = ::OSSL_PROVIDER_load (NULL, providerName.AsNarrowSDKString ().c_str ());
    static const Execution::RuntimeErrorException kErr_{L"No such SSL provider"sv};
    Execution::ThrowIfNull (p, kErr_);
    if (auto l = fLoadedProviders_.Lookup (providerName)) {
        l->second++;
        fLoadedProviders_.Add (providerName, *l);
    }
    else {
        fLoadedProviders_.Add (providerName, {p, 1});
    }
#else
    Require (providerName == kDefaultProvider or providerName == kLegacyProvider);
#endif
}

void LibraryContext ::UnLoadProvider (const String& providerName)
{
    lock_guard<const AssertExternallySynchronizedLock> critSec{*this};
#if OPENSSL_VERSION_MAJOR >= 3
    Require (fLoadedProviders_.ContainsKey (providerName));
    auto l = fLoadedProviders_.Lookup (providerName);
    Assert (l);
    l->second--;
    if (l->second == 0) {
        fLoadedProviders_.Remove (providerName);
        Verify (::OSSL_PROVIDER_unload (l->first) == 1);
    }
    else {
        fLoadedProviders_.Add (providerName, *l);
    }
#endif
}
#endif
<|endoftext|>


<commit_before>// The MIT License (MIT)
//
// Copyright (c) 2013 Patrick Van Oosterwijck
//
// 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 <node.h>
#include <node_buffer.h>
#include <v8.h>
#include <openssl/evp.h>

using namespace v8;
using namespace node;

// Authentication tag length

#define AUTH_TAG_LEN  16

// Exception shortcut

Handle<Value> VException(const char *msg) {
    HandleScope scope;
    return ThrowException(Exception::Error(String::New(msg)));
}

// Free callback to release memory once a Buffer created by MakeBuffer
// is released

void FreeCallback(char *data, void *hint) {
  delete [] data;
}

// Helper function to create a Buffer
// Source: https://gist.github.com/drewish/2732711
// Adjusted to prevent save memcpy from source data into Buffer

static Local<Object> MakeBuffer(unsigned char *data, size_t size) {
  HandleScope scope;

  // It ends up being kind of a pain to convert a slow buffer into a fast
  // one since the fast part is implemented in JavaScript.
  Local<Buffer> slowBuffer = Buffer::New((char *)data, size,
                                          FreeCallback, NULL);
  // First get the Buffer from global scope...
  Local<Object> global = Context::GetCurrent()->Global();
  Local<Value> bv = global->Get(String::NewSymbol("Buffer"));
  assert(bv->IsFunction());
  Local<Function> b = Local<Function>::Cast(bv);
  // ...call Buffer() with the slow buffer and get a fast buffer back...
  Handle<Value> argv[3] = { slowBuffer->handle_, Integer::New(size),
                            Integer::New(0) };
  Local<Object> fastBuffer = b->NewInstance(3, argv);

  return scope.Close(fastBuffer);
}

// Perform GCM mode AES-128 encryption using the provided key, IV, plaintext
// and auth_data buffers, and return an object containing "ciphertext"
// and "auth_tag" buffers.

Handle<Value> GcmEncrypt(const Arguments& args) {
  HandleScope scope;

  // We want 4 buffer arguments, key needs to be 16 bytes and IV needs to be
  // 12 bytes
  if (args.Length() < 4 || !Buffer::HasInstance(args[0]) ||
      !Buffer::HasInstance(args[1]) || !Buffer::HasInstance(args[2]) ||
      !Buffer::HasInstance(args[3]) || Buffer::Length(args[0]) != 16 ||
      Buffer::Length(args[1]) != 12) {
    return VException("encrypt requires a 16-byte key Buffer, a 12-byte " \
                      "IV Buffer, a plaintext Buffer and an auth_data " \
                      "Buffer parameter");
  }

  // Make a buffer for the ciphertext that is the same size as the
  // plaintext, but padded to 16 byte increments
  size_t plaintext_len = Buffer::Length(args[2]);
  size_t ciphertext_len = (((plaintext_len - 1) / 16) + 1) * 16;
  unsigned char *ciphertext = new unsigned char[ciphertext_len];
  // Make a authentication tag buffer
  unsigned char *auth_tag = new unsigned char[AUTH_TAG_LEN];

  // Init OpenSSL interace with 128-bit AES GCM cipher and give it the
  // key and IV
  int outl;
  EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
  EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), NULL,
                    (unsigned char *)Buffer::Data(args[0]),
                    (unsigned char *)Buffer::Data(args[1]));
  // Pass additional authenticated data
  // There is some extra complication here because Buffer::Data seems to
  // return NULL for empty buffers, and NULL makes update not work as we
  // expect it to.  So we force a valid non-NULL pointer for empty buffers.
  EVP_EncryptUpdate(ctx, NULL, &outl, Buffer::Length(args[3]) ?
                    (unsigned char *)Buffer::Data(args[3]) : auth_tag,
                    Buffer::Length(args[3]));
  // Encrypt plaintext
  EVP_EncryptUpdate(ctx, ciphertext, &outl,
                    (unsigned char *)Buffer::Data(args[2]),
                    Buffer::Length(args[2]));
  // Finalize
  EVP_EncryptFinal_ex(ctx, ciphertext + outl, &outl);
  // Get the authentication tag
  EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, AUTH_TAG_LEN, auth_tag);
  // Free the OpenSSL interface structure
  EVP_CIPHER_CTX_free(ctx);

  // Create the return buffers and object
  // We strip padding from the ciphertext
  Local<Object> ciphertext_buf = MakeBuffer(ciphertext, plaintext_len);
  Local<Object> auth_tag_buf = MakeBuffer(auth_tag, AUTH_TAG_LEN);
  Local<Object> return_obj = Object::New();
  return_obj->Set(String::NewSymbol("ciphertext"), ciphertext_buf);
  return_obj->Set(String::NewSymbol("auth_tag"), auth_tag_buf);

  // Return it
  return scope.Close(return_obj);
}

// Perform GCM mode AES-128 decryption using the provided key, IV, ciphertext,
// auth_data and auth_tag buffers, and return an object containing a "plaintext"
// buffer and an "auth_ok" boolean.

Handle<Value> GcmDecrypt(const Arguments& args) {
  HandleScope scope;

  // We want 5 buffer arguments, key needs to be 16 bytes, IV needs to be
  // 12 bytes, auth_tag needs to be 16 bytes
  if (args.Length() < 5 || !Buffer::HasInstance(args[0]) ||
      !Buffer::HasInstance(args[1]) || !Buffer::HasInstance(args[2]) ||
      !Buffer::HasInstance(args[3]) || !Buffer::HasInstance(args[4]) ||
      Buffer::Length(args[0]) != 16 || Buffer::Length(args[1]) != 12 ||
      Buffer::Length(args[4]) != 16) {
    return VException("decrypt requires a 16-byte key Buffer, a 12-byte " \
                      "IV Buffer, a ciphertext Buffer, an auth_data " \
                      "Buffer and a 16-byte auth_tag Buffer parameter");
  }

  // Make a buffer for the plaintext that is the same size as the
  // ciphertext, but padded to 16 byte increments
  size_t ciphertext_len = Buffer::Length(args[2]);
  size_t plaintext_len = (((ciphertext_len - 1) / 16) + 1) * 16;
  unsigned char *plaintext = new unsigned char[plaintext_len];

  // Init OpenSSL interace with 128-bit AES GCM cipher and give it the
  // key and IV
  int outl;
  EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
  EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), NULL,
                    (unsigned char *)Buffer::Data(args[0]),
                    (unsigned char *)Buffer::Data(args[1]));
  // Set the input reference authentication tag
  EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, AUTH_TAG_LEN,
                    Buffer::Data(args[4]));
  // Example showed we needed to do init again
  EVP_DecryptInit(ctx, NULL,
                    (unsigned char *)Buffer::Data(args[0]),
                    (unsigned char *)Buffer::Data(args[1]));
  // Pass additional authenticated data
  // There is some extra complication here because Buffer::Data seems to
  // return NULL for empty buffers, and NULL makes update not work as we
  // expect it to.  So we force a valid non-NULL pointer for empty buffers.
  EVP_DecryptUpdate(ctx, NULL, &outl, Buffer::Length(args[3]) ?
                    (unsigned char *)Buffer::Data(args[3]) : plaintext,
                    Buffer::Length(args[3]));
  // Decrypt ciphertext
  EVP_DecryptUpdate(ctx, plaintext, &outl,
                    (unsigned char *)Buffer::Data(args[2]),
                    Buffer::Length(args[2]));
  // Finalize
  bool auth_ok = EVP_DecryptFinal_ex(ctx, plaintext + outl, &outl);
  // Free the OpenSSL interface structure
  EVP_CIPHER_CTX_free(ctx);

  // Create the return buffer and object
  // We strip padding from the plaintext
  Local<Object> plaintext_buf = MakeBuffer(plaintext, ciphertext_len);
  Local<Object> return_obj = Object::New();
  return_obj->Set(String::NewSymbol("plaintext"), plaintext_buf);
  return_obj->Set(String::NewSymbol("auth_ok"), Boolean::New(auth_ok));

  // Return it
  return scope.Close(return_obj);
}

// Module init function

void Init(Handle<Object> exports, Handle<Value> module) {
  exports->Set(String::NewSymbol("encrypt"),
      FunctionTemplate::New(GcmEncrypt)->GetFunction());
  exports->Set(String::NewSymbol("decrypt"),
      FunctionTemplate::New(GcmDecrypt)->GetFunction());
}

NODE_MODULE(node_aes_gcm, Init)

<commit_msg>Switched from another deprecated OpenSSL call<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2013 Patrick Van Oosterwijck
//
// 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 <node.h>
#include <node_buffer.h>
#include <v8.h>
#include <openssl/evp.h>

using namespace v8;
using namespace node;

// Authentication tag length

#define AUTH_TAG_LEN  16

// Exception shortcut

Handle<Value> VException(const char *msg) {
    HandleScope scope;
    return ThrowException(Exception::Error(String::New(msg)));
}

// Free callback to release memory once a Buffer created by MakeBuffer
// is released

void FreeCallback(char *data, void *hint) {
  delete [] data;
}

// Helper function to create a Buffer
// Source: https://gist.github.com/drewish/2732711
// Adjusted to prevent save memcpy from source data into Buffer

static Local<Object> MakeBuffer(unsigned char *data, size_t size) {
  HandleScope scope;

  // It ends up being kind of a pain to convert a slow buffer into a fast
  // one since the fast part is implemented in JavaScript.
  Local<Buffer> slowBuffer = Buffer::New((char *)data, size,
                                          FreeCallback, NULL);
  // First get the Buffer from global scope...
  Local<Object> global = Context::GetCurrent()->Global();
  Local<Value> bv = global->Get(String::NewSymbol("Buffer"));
  assert(bv->IsFunction());
  Local<Function> b = Local<Function>::Cast(bv);
  // ...call Buffer() with the slow buffer and get a fast buffer back...
  Handle<Value> argv[3] = { slowBuffer->handle_, Integer::New(size),
                            Integer::New(0) };
  Local<Object> fastBuffer = b->NewInstance(3, argv);

  return scope.Close(fastBuffer);
}

// Perform GCM mode AES-128 encryption using the provided key, IV, plaintext
// and auth_data buffers, and return an object containing "ciphertext"
// and "auth_tag" buffers.

Handle<Value> GcmEncrypt(const Arguments& args) {
  HandleScope scope;

  // We want 4 buffer arguments, key needs to be 16 bytes and IV needs to be
  // 12 bytes
  if (args.Length() < 4 || !Buffer::HasInstance(args[0]) ||
      !Buffer::HasInstance(args[1]) || !Buffer::HasInstance(args[2]) ||
      !Buffer::HasInstance(args[3]) || Buffer::Length(args[0]) != 16 ||
      Buffer::Length(args[1]) != 12) {
    return VException("encrypt requires a 16-byte key Buffer, a 12-byte " \
                      "IV Buffer, a plaintext Buffer and an auth_data " \
                      "Buffer parameter");
  }

  // Make a buffer for the ciphertext that is the same size as the
  // plaintext, but padded to 16 byte increments
  size_t plaintext_len = Buffer::Length(args[2]);
  size_t ciphertext_len = (((plaintext_len - 1) / 16) + 1) * 16;
  unsigned char *ciphertext = new unsigned char[ciphertext_len];
  // Make a authentication tag buffer
  unsigned char *auth_tag = new unsigned char[AUTH_TAG_LEN];

  // Init OpenSSL interace with 128-bit AES GCM cipher and give it the
  // key and IV
  int outl;
  EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
  EVP_EncryptInit_ex(ctx, EVP_aes_128_gcm(), NULL,
                    (unsigned char *)Buffer::Data(args[0]),
                    (unsigned char *)Buffer::Data(args[1]));
  // Pass additional authenticated data
  // There is some extra complication here because Buffer::Data seems to
  // return NULL for empty buffers, and NULL makes update not work as we
  // expect it to.  So we force a valid non-NULL pointer for empty buffers.
  EVP_EncryptUpdate(ctx, NULL, &outl, Buffer::Length(args[3]) ?
                    (unsigned char *)Buffer::Data(args[3]) : auth_tag,
                    Buffer::Length(args[3]));
  // Encrypt plaintext
  EVP_EncryptUpdate(ctx, ciphertext, &outl,
                    (unsigned char *)Buffer::Data(args[2]),
                    Buffer::Length(args[2]));
  // Finalize
  EVP_EncryptFinal_ex(ctx, ciphertext + outl, &outl);
  // Get the authentication tag
  EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, AUTH_TAG_LEN, auth_tag);
  // Free the OpenSSL interface structure
  EVP_CIPHER_CTX_free(ctx);

  // Create the return buffers and object
  // We strip padding from the ciphertext
  Local<Object> ciphertext_buf = MakeBuffer(ciphertext, plaintext_len);
  Local<Object> auth_tag_buf = MakeBuffer(auth_tag, AUTH_TAG_LEN);
  Local<Object> return_obj = Object::New();
  return_obj->Set(String::NewSymbol("ciphertext"), ciphertext_buf);
  return_obj->Set(String::NewSymbol("auth_tag"), auth_tag_buf);

  // Return it
  return scope.Close(return_obj);
}

// Perform GCM mode AES-128 decryption using the provided key, IV, ciphertext,
// auth_data and auth_tag buffers, and return an object containing a "plaintext"
// buffer and an "auth_ok" boolean.

Handle<Value> GcmDecrypt(const Arguments& args) {
  HandleScope scope;

  // We want 5 buffer arguments, key needs to be 16 bytes, IV needs to be
  // 12 bytes, auth_tag needs to be 16 bytes
  if (args.Length() < 5 || !Buffer::HasInstance(args[0]) ||
      !Buffer::HasInstance(args[1]) || !Buffer::HasInstance(args[2]) ||
      !Buffer::HasInstance(args[3]) || !Buffer::HasInstance(args[4]) ||
      Buffer::Length(args[0]) != 16 || Buffer::Length(args[1]) != 12 ||
      Buffer::Length(args[4]) != 16) {
    return VException("decrypt requires a 16-byte key Buffer, a 12-byte " \
                      "IV Buffer, a ciphertext Buffer, an auth_data " \
                      "Buffer and a 16-byte auth_tag Buffer parameter");
  }

  // Make a buffer for the plaintext that is the same size as the
  // ciphertext, but padded to 16 byte increments
  size_t ciphertext_len = Buffer::Length(args[2]);
  size_t plaintext_len = (((ciphertext_len - 1) / 16) + 1) * 16;
  unsigned char *plaintext = new unsigned char[plaintext_len];

  // Init OpenSSL interace with 128-bit AES GCM cipher and give it the
  // key and IV
  int outl;
  EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
  EVP_DecryptInit_ex(ctx, EVP_aes_128_gcm(), NULL,
                    (unsigned char *)Buffer::Data(args[0]),
                    (unsigned char *)Buffer::Data(args[1]));
  // Set the input reference authentication tag
  EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, AUTH_TAG_LEN,
                    Buffer::Data(args[4]));
  // Example showed we needed to do init again
  EVP_DecryptInit_ex(ctx, NULL, NULL,
                    (unsigned char *)Buffer::Data(args[0]),
                    (unsigned char *)Buffer::Data(args[1]));
  // Pass additional authenticated data
  // There is some extra complication here because Buffer::Data seems to
  // return NULL for empty buffers, and NULL makes update not work as we
  // expect it to.  So we force a valid non-NULL pointer for empty buffers.
  EVP_DecryptUpdate(ctx, NULL, &outl, Buffer::Length(args[3]) ?
                    (unsigned char *)Buffer::Data(args[3]) : plaintext,
                    Buffer::Length(args[3]));
  // Decrypt ciphertext
  EVP_DecryptUpdate(ctx, plaintext, &outl,
                    (unsigned char *)Buffer::Data(args[2]),
                    Buffer::Length(args[2]));
  // Finalize
  bool auth_ok = EVP_DecryptFinal_ex(ctx, plaintext + outl, &outl);
  // Free the OpenSSL interface structure
  EVP_CIPHER_CTX_free(ctx);

  // Create the return buffer and object
  // We strip padding from the plaintext
  Local<Object> plaintext_buf = MakeBuffer(plaintext, ciphertext_len);
  Local<Object> return_obj = Object::New();
  return_obj->Set(String::NewSymbol("plaintext"), plaintext_buf);
  return_obj->Set(String::NewSymbol("auth_ok"), Boolean::New(auth_ok));

  // Return it
  return scope.Close(return_obj);
}

// Module init function

void Init(Handle<Object> exports, Handle<Value> module) {
  exports->Set(String::NewSymbol("encrypt"),
      FunctionTemplate::New(GcmEncrypt)->GetFunction());
  exports->Set(String::NewSymbol("decrypt"),
      FunctionTemplate::New(GcmDecrypt)->GetFunction());
}

NODE_MODULE(node_aes_gcm, Init)

<|endoftext|>


<commit_before>/* indent-tabs-mode: nil -*- */
/*
  odbc-module.cpp

  Qore ODBC module

  Copyright (C) 2016 Ondrej Musil

  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 <sql.h>
#include <sqlext.h>

#include "qore/Qore.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "ODBCConnection.h"
#include "ODBCPreparedStatement.h"

QoreStringNode *odbc_module_init();
void odbc_module_ns_init(QoreNamespace *rns, QoreNamespace *qns);
void odbc_module_delete();

// qore module symbols
DLLEXPORT char qore_module_name[] = "odbc";
DLLEXPORT char qore_module_version[] = PACKAGE_VERSION;
DLLEXPORT char qore_module_description[] = "ODBC database driver module";
DLLEXPORT char qore_module_author[] = "Ondrej Musil <ondrej.musil@qoretechnologies.com>";
DLLEXPORT char qore_module_url[] = "http://qore.org";
DLLEXPORT int qore_module_api_major = QORE_MODULE_API_MAJOR;
DLLEXPORT int qore_module_api_minor = QORE_MODULE_API_MINOR;
DLLEXPORT qore_module_init_t qore_module_init = odbc_module_init;
DLLEXPORT qore_module_ns_init_t qore_module_ns_init = odbc_module_ns_init;
DLLEXPORT qore_module_delete_t qore_module_delete = odbc_module_delete;

DLLEXPORT qore_license_t qore_module_license = QL_MIT;
DLLEXPORT char qore_module_license_str[] = "MIT";

static DBIDriver* DBID_ODBC;

// capabilities of this driver
int DBI_ODBC_CAPS =
    DBI_CAP_TRANSACTION_MANAGEMENT
    | DBI_CAP_CHARSET_SUPPORT
    | DBI_CAP_LOB_SUPPORT
    | DBI_CAP_STORED_PROCEDURES
    | DBI_CAP_BIND_BY_VALUE
    | DBI_CAP_BIND_BY_PLACEHOLDER
    | DBI_CAP_HAS_ARRAY_BIND
    | DBI_CAP_HAS_NUMBER_SUPPORT
    | DBI_CAP_AUTORECONNECT
#ifdef _QORE_HAS_DBI_EXECRAW
    | DBI_CAP_HAS_EXECRAW
#endif
#ifdef _QORE_HAS_TIME_ZONES
    | DBI_CAP_TIME_ZONE_SUPPORT
#endif
#ifdef _QORE_HAS_FIND_CREATE_TIMEZONE
    | DBI_CAP_SERVER_TIME_ZONE
#endif
    ;

static int odbc_open(Datasource* ds, ExceptionSink* xsink) {
    ODBCConnection* conn = new ODBCConnection(ds, xsink);
    if (*xsink) {
        delete conn;
        return -1;
    }

    ds->setPrivateData((void*)conn);
    return 0;
}

static int odbc_close(Datasource* ds) {
    ODBCConnection* conn = static_cast<ODBCConnection *>(ds->getPrivateData());
    ds->setPrivateData(0);
    delete conn;
    return 0;
}

static AbstractQoreNode* odbc_select(Datasource* ds, const QoreString* qstr, const QoreListNode* args, ExceptionSink* xsink) {
    ODBCConnection* conn = static_cast<ODBCConnection *>(ds->getPrivateData());
    return conn->exec(qstr, args, xsink);
}

#ifdef _QORE_HAS_DBI_SELECT_ROW
static QoreHashNode* odbc_select_row(Datasource* ds, const QoreString* qstr, const QoreListNode* args, ExceptionSink* xsink) {
    ODBCConnection* conn = static_cast<ODBCConnection *>(ds->getPrivateData());
    return conn->selectRow(qstr, args, xsink);
}
#endif

static AbstractQoreNode* odbc_select_rows(Datasource* ds, const QoreString* qstr, const QoreListNode* args, ExceptionSink* xsink) {
    ODBCConnection* conn = static_cast<ODBCConnection *>(ds->getPrivateData());
    return conn->selectRows(qstr, args, xsink);
}

static AbstractQoreNode* odbc_exec(Datasource* ds, const QoreString* qstr, const QoreListNode* args, ExceptionSink* xsink) {
    ODBCConnection* conn = static_cast<ODBCConnection *>(ds->getPrivateData());
    return conn->exec(qstr, args, xsink);
}

#ifdef _QORE_HAS_DBI_EXECRAW
static AbstractQoreNode* odbc_execRaw(Datasource* ds, const QoreString* qstr, ExceptionSink* xsink) {
    ODBCConnection* conn = static_cast<ODBCConnection *>(ds->getPrivateData());
    return conn->execRaw(qstr, xsink);
}
#endif

static int odbc_begin_transaction(Datasource* ds, ExceptionSink* xsink) {
    return 0;
}

static int odbc_commit(Datasource* ds, ExceptionSink* xsink) {
    ODBCConnection* conn = static_cast<ODBCConnection *>(ds->getPrivateData());
    return conn->commit(xsink);
}

static int odbc_rollback(Datasource* ds, ExceptionSink* xsink) {
    ODBCConnection* conn = static_cast<ODBCConnection *>(ds->getPrivateData());
    return conn->rollback(xsink);
}

static AbstractQoreNode* odbc_get_client_version(const Datasource* ds, ExceptionSink* xsink) {
    ODBCConnection* conn = static_cast<ODBCConnection *>(ds->getPrivateData());
    return new QoreBigIntNode(conn->getClientVersion());
}

static AbstractQoreNode* odbc_get_server_version(Datasource* ds, ExceptionSink* xsink) {
    ODBCConnection* conn = static_cast<ODBCConnection *>(ds->getPrivateData());
    return new QoreBigIntNode(conn->getServerVersion());
}

static int odbc_stmt_prepare(SQLStatement* stmt, const QoreString& str, const QoreListNode* args, ExceptionSink* xsink) {
    assert(!stmt->getPrivateData());

    ODBCPreparedStatement* ps = new ODBCPreparedStatement(stmt->getDatasource(), xsink);
    if (*xsink) {
        delete ps;
        return -1;
    }
    stmt->setPrivateData(ps);

    return ps->prepare(str, args, xsink);
}

static int odbc_stmt_prepare_raw(SQLStatement* stmt, const QoreString& str, ExceptionSink* xsink) {
    assert(!stmt->getPrivateData());

    ODBCPreparedStatement* ps = new ODBCPreparedStatement(stmt->getDatasource(), xsink);
    if (*xsink) {
        delete ps;
        return -1;
    }
    stmt->setPrivateData(ps);

    return ps->prepare(str, 0, xsink);
}

static int odbc_stmt_bind(SQLStatement* stmt, const QoreListNode& args, ExceptionSink* xsink) {
    ODBCPreparedStatement* ps = static_cast<ODBCPreparedStatement*>(stmt->getPrivateData());
    assert(ps);

    return ps->bind(args, xsink);
}

static int odbc_stmt_bind_placeholders(SQLStatement* stmt, const QoreListNode& args, ExceptionSink* xsink) {
    xsink->raiseException("DBI:ODBC:BIND-PLACEHHODERS-ERROR", "binding placeholders is not necessary or supported with the odbc driver");
    return -1;
}

static int odbc_stmt_bind_values(SQLStatement* stmt, const QoreListNode& args, ExceptionSink* xsink) {
    ODBCPreparedStatement* ps = static_cast<ODBCPreparedStatement*>(stmt->getPrivateData());
    assert(ps);

    return ps->bind(args, xsink);
}

static int odbc_stmt_exec(SQLStatement* stmt, ExceptionSink* xsink) {
    ODBCPreparedStatement* ps = static_cast<ODBCPreparedStatement*>(stmt->getPrivateData());
    assert(ps);

    return ps->exec(xsink);
}

static int odbc_stmt_define(SQLStatement* stmt, ExceptionSink* xsink) {
    // define is a noop in the odbc driver
    return 0;
}

static int odbc_stmt_affected_rows(SQLStatement* stmt, ExceptionSink* xsink) {
    ODBCPreparedStatement* ps = static_cast<ODBCPreparedStatement*>(stmt->getPrivateData());
    assert(ps);

    return ps->rowsAffected();
}

static QoreHashNode* odbc_stmt_get_output(SQLStatement* stmt, ExceptionSink* xsink) {
    ODBCPreparedStatement* ps = static_cast<ODBCPreparedStatement*>(stmt->getPrivateData());
    assert(ps);

    return ps->getOutputHash(xsink, false);
}

static QoreHashNode* odbc_stmt_get_output_rows(SQLStatement* stmt, ExceptionSink* xsink) {
    ODBCPreparedStatement* ps = static_cast<ODBCPreparedStatement*>(stmt->getPrivateData());
    assert(ps);

    return ps->getOutputHash(xsink, false);
}

static QoreHashNode* odbc_stmt_fetch_row(SQLStatement* stmt, ExceptionSink* xsink) {
    ODBCPreparedStatement* ps = static_cast<ODBCPreparedStatement*>(stmt->getPrivateData());
    assert(ps);

    return ps->fetchRow(xsink);
}

static QoreListNode* odbc_stmt_fetch_rows(SQLStatement* stmt, int maxRows, ExceptionSink* xsink) {
    ODBCPreparedStatement* ps = static_cast<ODBCPreparedStatement*>(stmt->getPrivateData());
    assert(ps);

    return ps->fetchRows(maxRows, xsink);
}

static QoreHashNode* odbc_stmt_fetch_columns(SQLStatement* stmt, int maxRows, ExceptionSink* xsink) {
    ODBCPreparedStatement* ps = static_cast<ODBCPreparedStatement*>(stmt->getPrivateData());
    assert(ps);

    return ps->fetchColumns(maxRows, xsink);
}

static QoreHashNode* odbc_stmt_describe(SQLStatement* stmt, ExceptionSink* xsink) {
    ODBCPreparedStatement* ps = static_cast<ODBCPreparedStatement*>(stmt->getPrivateData());
    assert(ps);

    return ps->describe(xsink);
}

static bool odbc_stmt_next(SQLStatement* stmt, ExceptionSink* xsink) {
    ODBCPreparedStatement* ps = static_cast<ODBCPreparedStatement*>(stmt->getPrivateData());
    assert(ps);

    return ps->next(xsink);
}

static int odbc_stmt_close(SQLStatement* stmt, ExceptionSink* xsink) {
    ODBCPreparedStatement* ps = static_cast<ODBCPreparedStatement*>(stmt->getPrivateData());
    assert(ps);

    delete ps;
    stmt->setPrivateData(0);
    return *xsink ? -1 : 0;
}

static int odbc_opt_set(Datasource* ds, const char* opt, const AbstractQoreNode* val, ExceptionSink* xsink) {
   ODBCConnection* conn = (ODBCConnection*)ds->getPrivateData();
   return conn->setOption(opt, val, xsink);
}

static AbstractQoreNode* odbc_opt_get(const Datasource* ds, const char* opt) {
   ODBCConnection* conn = (ODBCConnection*)ds->getPrivateData();
   return conn->getOption(opt);
}

QoreNamespace OdbcNS("odbc");

QoreStringNode *odbc_module_init() {
    qore_dbi_method_list methods;
    methods.add(QDBI_METHOD_OPEN, odbc_open);
    methods.add(QDBI_METHOD_CLOSE, odbc_close);
    methods.add(QDBI_METHOD_SELECT, odbc_exec);
#ifdef _QORE_HAS_DBI_SELECT_ROW
    methods.add(QDBI_METHOD_SELECT_ROW, odbc_select_row);
#endif
    methods.add(QDBI_METHOD_SELECT_ROWS, odbc_select_rows);
    methods.add(QDBI_METHOD_EXEC, odbc_exec);
#ifdef _QORE_HAS_DBI_EXECRAW
    methods.add(QDBI_METHOD_EXECRAW, odbc_execRaw);
#endif
    methods.add(QDBI_METHOD_COMMIT, odbc_commit);
    methods.add(QDBI_METHOD_ROLLBACK, odbc_rollback);
    methods.add(QDBI_METHOD_BEGIN_TRANSACTION, odbc_begin_transaction);
    methods.add(QDBI_METHOD_ABORT_TRANSACTION_START, odbc_rollback);
    methods.add(QDBI_METHOD_GET_CLIENT_VERSION, odbc_get_client_version);
    methods.add(QDBI_METHOD_GET_SERVER_VERSION, odbc_get_server_version);

    methods.add(QDBI_METHOD_STMT_PREPARE, odbc_stmt_prepare);
    methods.add(QDBI_METHOD_STMT_PREPARE_RAW, odbc_stmt_prepare_raw);
    methods.add(QDBI_METHOD_STMT_BIND, odbc_stmt_bind);
    methods.add(QDBI_METHOD_STMT_BIND_PLACEHOLDERS, odbc_stmt_bind_placeholders);
    methods.add(QDBI_METHOD_STMT_BIND_VALUES, odbc_stmt_bind_values);
    methods.add(QDBI_METHOD_STMT_EXEC, odbc_stmt_exec);
    methods.add(QDBI_METHOD_STMT_DEFINE, odbc_stmt_define);
    methods.add(QDBI_METHOD_STMT_FETCH_ROW, odbc_stmt_fetch_row);
    methods.add(QDBI_METHOD_STMT_FETCH_ROWS, odbc_stmt_fetch_rows);
    methods.add(QDBI_METHOD_STMT_FETCH_COLUMNS, odbc_stmt_fetch_columns);
    methods.add(QDBI_METHOD_STMT_DESCRIBE, odbc_stmt_describe);
    methods.add(QDBI_METHOD_STMT_NEXT, odbc_stmt_next);
    methods.add(QDBI_METHOD_STMT_CLOSE, odbc_stmt_close);
    methods.add(QDBI_METHOD_STMT_AFFECTED_ROWS, odbc_stmt_affected_rows);
    methods.add(QDBI_METHOD_STMT_GET_OUTPUT, odbc_stmt_get_output);
    methods.add(QDBI_METHOD_STMT_GET_OUTPUT_ROWS, odbc_stmt_get_output_rows);

    methods.add(QDBI_METHOD_OPT_SET, odbc_opt_set);
    methods.add(QDBI_METHOD_OPT_GET, odbc_opt_get);

    methods.registerOption(DBI_OPT_NUMBER_OPT, "when set, numeric/decimal values are returned as integers if possible, otherwise as arbitrary-precision number values; the argument is ignored; setting this option turns it on and turns off 'string-numbers' and 'numeric-numbers'");
   methods.registerOption(DBI_OPT_NUMBER_STRING, "when set, numeric/decimal values are returned as strings for backwards-compatibility; the argument is ignored; setting this option turns it on and turns off 'optimal-numbers' and 'numeric-numbers'");
   methods.registerOption(DBI_OPT_NUMBER_NUMERIC, "when set, numeric/decimal values are returned as arbitrary-precision number values; the argument is ignored; setting this option turns it on and turns off 'string-numbers' and 'optimal-numbers'");

    DBID_ODBC = DBI.registerDriver("odbc", methods, DBI_ODBC_CAPS);

    return 0;
}

void odbc_module_ns_init(QoreNamespace *rns, QoreNamespace *qns) {
    qns->addNamespace(OdbcNS.copy());
}

void odbc_module_delete() {
    // nothing to do here in this case
}

<commit_msg>Fixed whitespace.<commit_after>/* indent-tabs-mode: nil -*- */
/*
  odbc-module.cpp

  Qore ODBC module

  Copyright (C) 2016 Ondrej Musil

  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 <sql.h>
#include <sqlext.h>

#include "qore/Qore.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "ODBCConnection.h"
#include "ODBCPreparedStatement.h"

QoreStringNode *odbc_module_init();
void odbc_module_ns_init(QoreNamespace *rns, QoreNamespace *qns);
void odbc_module_delete();

// qore module symbols
DLLEXPORT char qore_module_name[] = "odbc";
DLLEXPORT char qore_module_version[] = PACKAGE_VERSION;
DLLEXPORT char qore_module_description[] = "ODBC database driver module";
DLLEXPORT char qore_module_author[] = "Ondrej Musil <ondrej.musil@qoretechnologies.com>";
DLLEXPORT char qore_module_url[] = "http://qore.org";
DLLEXPORT int qore_module_api_major = QORE_MODULE_API_MAJOR;
DLLEXPORT int qore_module_api_minor = QORE_MODULE_API_MINOR;
DLLEXPORT qore_module_init_t qore_module_init = odbc_module_init;
DLLEXPORT qore_module_ns_init_t qore_module_ns_init = odbc_module_ns_init;
DLLEXPORT qore_module_delete_t qore_module_delete = odbc_module_delete;

DLLEXPORT qore_license_t qore_module_license = QL_MIT;
DLLEXPORT char qore_module_license_str[] = "MIT";

static DBIDriver* DBID_ODBC;

// capabilities of this driver
int DBI_ODBC_CAPS =
    DBI_CAP_TRANSACTION_MANAGEMENT
    | DBI_CAP_CHARSET_SUPPORT
    | DBI_CAP_LOB_SUPPORT
    | DBI_CAP_STORED_PROCEDURES
    | DBI_CAP_BIND_BY_VALUE
    | DBI_CAP_BIND_BY_PLACEHOLDER
    | DBI_CAP_HAS_ARRAY_BIND
    | DBI_CAP_HAS_NUMBER_SUPPORT
    | DBI_CAP_AUTORECONNECT
#ifdef _QORE_HAS_DBI_EXECRAW
    | DBI_CAP_HAS_EXECRAW
#endif
#ifdef _QORE_HAS_TIME_ZONES
    | DBI_CAP_TIME_ZONE_SUPPORT
#endif
#ifdef _QORE_HAS_FIND_CREATE_TIMEZONE
    | DBI_CAP_SERVER_TIME_ZONE
#endif
    ;

static int odbc_open(Datasource* ds, ExceptionSink* xsink) {
    ODBCConnection* conn = new ODBCConnection(ds, xsink);
    if (*xsink) {
        delete conn;
        return -1;
    }

    ds->setPrivateData((void*)conn);
    return 0;
}

static int odbc_close(Datasource* ds) {
    ODBCConnection* conn = static_cast<ODBCConnection *>(ds->getPrivateData());
    ds->setPrivateData(0);
    delete conn;
    return 0;
}

static AbstractQoreNode* odbc_select(Datasource* ds, const QoreString* qstr, const QoreListNode* args, ExceptionSink* xsink) {
    ODBCConnection* conn = static_cast<ODBCConnection *>(ds->getPrivateData());
    return conn->exec(qstr, args, xsink);
}

#ifdef _QORE_HAS_DBI_SELECT_ROW
static QoreHashNode* odbc_select_row(Datasource* ds, const QoreString* qstr, const QoreListNode* args, ExceptionSink* xsink) {
    ODBCConnection* conn = static_cast<ODBCConnection *>(ds->getPrivateData());
    return conn->selectRow(qstr, args, xsink);
}
#endif

static AbstractQoreNode* odbc_select_rows(Datasource* ds, const QoreString* qstr, const QoreListNode* args, ExceptionSink* xsink) {
    ODBCConnection* conn = static_cast<ODBCConnection *>(ds->getPrivateData());
    return conn->selectRows(qstr, args, xsink);
}

static AbstractQoreNode* odbc_exec(Datasource* ds, const QoreString* qstr, const QoreListNode* args, ExceptionSink* xsink) {
    ODBCConnection* conn = static_cast<ODBCConnection *>(ds->getPrivateData());
    return conn->exec(qstr, args, xsink);
}

#ifdef _QORE_HAS_DBI_EXECRAW
static AbstractQoreNode* odbc_execRaw(Datasource* ds, const QoreString* qstr, ExceptionSink* xsink) {
    ODBCConnection* conn = static_cast<ODBCConnection *>(ds->getPrivateData());
    return conn->execRaw(qstr, xsink);
}
#endif

static int odbc_begin_transaction(Datasource* ds, ExceptionSink* xsink) {
    return 0;
}

static int odbc_commit(Datasource* ds, ExceptionSink* xsink) {
    ODBCConnection* conn = static_cast<ODBCConnection *>(ds->getPrivateData());
    return conn->commit(xsink);
}

static int odbc_rollback(Datasource* ds, ExceptionSink* xsink) {
    ODBCConnection* conn = static_cast<ODBCConnection *>(ds->getPrivateData());
    return conn->rollback(xsink);
}

static AbstractQoreNode* odbc_get_client_version(const Datasource* ds, ExceptionSink* xsink) {
    ODBCConnection* conn = static_cast<ODBCConnection *>(ds->getPrivateData());
    return new QoreBigIntNode(conn->getClientVersion());
}

static AbstractQoreNode* odbc_get_server_version(Datasource* ds, ExceptionSink* xsink) {
    ODBCConnection* conn = static_cast<ODBCConnection *>(ds->getPrivateData());
    return new QoreBigIntNode(conn->getServerVersion());
}

static int odbc_stmt_prepare(SQLStatement* stmt, const QoreString& str, const QoreListNode* args, ExceptionSink* xsink) {
    assert(!stmt->getPrivateData());

    ODBCPreparedStatement* ps = new ODBCPreparedStatement(stmt->getDatasource(), xsink);
    if (*xsink) {
        delete ps;
        return -1;
    }
    stmt->setPrivateData(ps);

    return ps->prepare(str, args, xsink);
}

static int odbc_stmt_prepare_raw(SQLStatement* stmt, const QoreString& str, ExceptionSink* xsink) {
    assert(!stmt->getPrivateData());

    ODBCPreparedStatement* ps = new ODBCPreparedStatement(stmt->getDatasource(), xsink);
    if (*xsink) {
        delete ps;
        return -1;
    }
    stmt->setPrivateData(ps);

    return ps->prepare(str, 0, xsink);
}

static int odbc_stmt_bind(SQLStatement* stmt, const QoreListNode& args, ExceptionSink* xsink) {
    ODBCPreparedStatement* ps = static_cast<ODBCPreparedStatement*>(stmt->getPrivateData());
    assert(ps);

    return ps->bind(args, xsink);
}

static int odbc_stmt_bind_placeholders(SQLStatement* stmt, const QoreListNode& args, ExceptionSink* xsink) {
    xsink->raiseException("DBI:ODBC:BIND-PLACEHHODERS-ERROR", "binding placeholders is not necessary or supported with the odbc driver");
    return -1;
}

static int odbc_stmt_bind_values(SQLStatement* stmt, const QoreListNode& args, ExceptionSink* xsink) {
    ODBCPreparedStatement* ps = static_cast<ODBCPreparedStatement*>(stmt->getPrivateData());
    assert(ps);

    return ps->bind(args, xsink);
}

static int odbc_stmt_exec(SQLStatement* stmt, ExceptionSink* xsink) {
    ODBCPreparedStatement* ps = static_cast<ODBCPreparedStatement*>(stmt->getPrivateData());
    assert(ps);

    return ps->exec(xsink);
}

static int odbc_stmt_define(SQLStatement* stmt, ExceptionSink* xsink) {
    // define is a noop in the odbc driver
    return 0;
}

static int odbc_stmt_affected_rows(SQLStatement* stmt, ExceptionSink* xsink) {
    ODBCPreparedStatement* ps = static_cast<ODBCPreparedStatement*>(stmt->getPrivateData());
    assert(ps);

    return ps->rowsAffected();
}

static QoreHashNode* odbc_stmt_get_output(SQLStatement* stmt, ExceptionSink* xsink) {
    ODBCPreparedStatement* ps = static_cast<ODBCPreparedStatement*>(stmt->getPrivateData());
    assert(ps);

    return ps->getOutputHash(xsink, false);
}

static QoreHashNode* odbc_stmt_get_output_rows(SQLStatement* stmt, ExceptionSink* xsink) {
    ODBCPreparedStatement* ps = static_cast<ODBCPreparedStatement*>(stmt->getPrivateData());
    assert(ps);

    return ps->getOutputHash(xsink, false);
}

static QoreHashNode* odbc_stmt_fetch_row(SQLStatement* stmt, ExceptionSink* xsink) {
    ODBCPreparedStatement* ps = static_cast<ODBCPreparedStatement*>(stmt->getPrivateData());
    assert(ps);

    return ps->fetchRow(xsink);
}

static QoreListNode* odbc_stmt_fetch_rows(SQLStatement* stmt, int maxRows, ExceptionSink* xsink) {
    ODBCPreparedStatement* ps = static_cast<ODBCPreparedStatement*>(stmt->getPrivateData());
    assert(ps);

    return ps->fetchRows(maxRows, xsink);
}

static QoreHashNode* odbc_stmt_fetch_columns(SQLStatement* stmt, int maxRows, ExceptionSink* xsink) {
    ODBCPreparedStatement* ps = static_cast<ODBCPreparedStatement*>(stmt->getPrivateData());
    assert(ps);

    return ps->fetchColumns(maxRows, xsink);
}

static QoreHashNode* odbc_stmt_describe(SQLStatement* stmt, ExceptionSink* xsink) {
    ODBCPreparedStatement* ps = static_cast<ODBCPreparedStatement*>(stmt->getPrivateData());
    assert(ps);

    return ps->describe(xsink);
}

static bool odbc_stmt_next(SQLStatement* stmt, ExceptionSink* xsink) {
    ODBCPreparedStatement* ps = static_cast<ODBCPreparedStatement*>(stmt->getPrivateData());
    assert(ps);

    return ps->next(xsink);
}

static int odbc_stmt_close(SQLStatement* stmt, ExceptionSink* xsink) {
    ODBCPreparedStatement* ps = static_cast<ODBCPreparedStatement*>(stmt->getPrivateData());
    assert(ps);

    delete ps;
    stmt->setPrivateData(0);
    return *xsink ? -1 : 0;
}

static int odbc_opt_set(Datasource* ds, const char* opt, const AbstractQoreNode* val, ExceptionSink* xsink) {
   ODBCConnection* conn = (ODBCConnection*)ds->getPrivateData();
   return conn->setOption(opt, val, xsink);
}

static AbstractQoreNode* odbc_opt_get(const Datasource* ds, const char* opt) {
   ODBCConnection* conn = (ODBCConnection*)ds->getPrivateData();
   return conn->getOption(opt);
}

QoreNamespace OdbcNS("odbc");

QoreStringNode *odbc_module_init() {
    qore_dbi_method_list methods;
    methods.add(QDBI_METHOD_OPEN, odbc_open);
    methods.add(QDBI_METHOD_CLOSE, odbc_close);
    methods.add(QDBI_METHOD_SELECT, odbc_exec);
#ifdef _QORE_HAS_DBI_SELECT_ROW
    methods.add(QDBI_METHOD_SELECT_ROW, odbc_select_row);
#endif
    methods.add(QDBI_METHOD_SELECT_ROWS, odbc_select_rows);
    methods.add(QDBI_METHOD_EXEC, odbc_exec);
#ifdef _QORE_HAS_DBI_EXECRAW
    methods.add(QDBI_METHOD_EXECRAW, odbc_execRaw);
#endif
    methods.add(QDBI_METHOD_COMMIT, odbc_commit);
    methods.add(QDBI_METHOD_ROLLBACK, odbc_rollback);
    methods.add(QDBI_METHOD_BEGIN_TRANSACTION, odbc_begin_transaction);
    methods.add(QDBI_METHOD_ABORT_TRANSACTION_START, odbc_rollback);
    methods.add(QDBI_METHOD_GET_CLIENT_VERSION, odbc_get_client_version);
    methods.add(QDBI_METHOD_GET_SERVER_VERSION, odbc_get_server_version);

    methods.add(QDBI_METHOD_STMT_PREPARE, odbc_stmt_prepare);
    methods.add(QDBI_METHOD_STMT_PREPARE_RAW, odbc_stmt_prepare_raw);
    methods.add(QDBI_METHOD_STMT_BIND, odbc_stmt_bind);
    methods.add(QDBI_METHOD_STMT_BIND_PLACEHOLDERS, odbc_stmt_bind_placeholders);
    methods.add(QDBI_METHOD_STMT_BIND_VALUES, odbc_stmt_bind_values);
    methods.add(QDBI_METHOD_STMT_EXEC, odbc_stmt_exec);
    methods.add(QDBI_METHOD_STMT_DEFINE, odbc_stmt_define);
    methods.add(QDBI_METHOD_STMT_FETCH_ROW, odbc_stmt_fetch_row);
    methods.add(QDBI_METHOD_STMT_FETCH_ROWS, odbc_stmt_fetch_rows);
    methods.add(QDBI_METHOD_STMT_FETCH_COLUMNS, odbc_stmt_fetch_columns);
    methods.add(QDBI_METHOD_STMT_DESCRIBE, odbc_stmt_describe);
    methods.add(QDBI_METHOD_STMT_NEXT, odbc_stmt_next);
    methods.add(QDBI_METHOD_STMT_CLOSE, odbc_stmt_close);
    methods.add(QDBI_METHOD_STMT_AFFECTED_ROWS, odbc_stmt_affected_rows);
    methods.add(QDBI_METHOD_STMT_GET_OUTPUT, odbc_stmt_get_output);
    methods.add(QDBI_METHOD_STMT_GET_OUTPUT_ROWS, odbc_stmt_get_output_rows);

    methods.add(QDBI_METHOD_OPT_SET, odbc_opt_set);
    methods.add(QDBI_METHOD_OPT_GET, odbc_opt_get);

    methods.registerOption(DBI_OPT_NUMBER_OPT, "when set, numeric/decimal values are returned as integers if possible, otherwise as arbitrary-precision number values; the argument is ignored; setting this option turns it on and turns off 'string-numbers' and 'numeric-numbers'");
    methods.registerOption(DBI_OPT_NUMBER_STRING, "when set, numeric/decimal values are returned as strings for backwards-compatibility; the argument is ignored; setting this option turns it on and turns off 'optimal-numbers' and 'numeric-numbers'");
    methods.registerOption(DBI_OPT_NUMBER_NUMERIC, "when set, numeric/decimal values are returned as arbitrary-precision number values; the argument is ignored; setting this option turns it on and turns off 'string-numbers' and 'optimal-numbers'");

    DBID_ODBC = DBI.registerDriver("odbc", methods, DBI_ODBC_CAPS);

    return 0;
}

void odbc_module_ns_init(QoreNamespace *rns, QoreNamespace *qns) {
    qns->addNamespace(OdbcNS.copy());
}

void odbc_module_delete() {
    // nothing to do here in this case
}

<|endoftext|>


<commit_before>/*! \file omproutines.cxx
 *  \brief this file contains routines used with OpenMP compilation.

    OpenMP routines pertaining to complex OpenMP calls.
 */

#ifdef USEOPENMP

//-- For MPI

#include "stf.h"

/// \name routines which check to see if some search region overlaps with local mpi domain
//@{
///construct omp domains
OMP_Domain *OpenMPBuildDomains(Options &opt, const Int_t numompregions, KDTree *&tree, const Double_t rdist)
{
    OMP_Domain *ompdomain = new OMP_Domain[numompregions];
    Int_t noffset=0;
    Node *np;
    for (auto i=0;i<numompregions;i++) {
        np = (tree->FindLeafNode(noffset));
        ompdomain[i].ncount = np->GetCount();
        ompdomain[i].noffset = noffset;
        for (int j=0;j<3;j++) {
            ompdomain[i].bnd[j][0]=np->GetBoundary(j,0)*0.99;
            ompdomain[i].bnd[j][1]=np->GetBoundary(j,1)*1.01;
        }
        noffset+=ompdomain[i].ncount;
    }
    //determine for each omp region, what are neighbour omp regions within rdist
    Double_t xsearch[3][2];
    for (auto i=0;i<numompregions;i++) {
        for (auto k=0;k<3;k++) {xsearch[k][0]=ompdomain[i].bnd[k][0]-rdist;xsearch[k][0]=ompdomain[i].bnd[k][1]+rdist;}
        for (auto j=0;j<numompregions;j++) if (j!=i){
            if (OpenMPSearchForOverlap(xsearch,ompdomain[j].bnd, opt.p)) ompdomain[i].neighbour.push_back(j);
        }
        noffset+=ompdomain[i].ncount;
    }
    np=NULL;
    return ompdomain;
}

KDTree **OpenMPBuildLocalTrees(Options &opt, const Int_t numompregions, vector<Particle> &Part, OMP_Domain *ompdomain, Double_t *period)
{
    KDTree **tree3dfofomp = new KDTree*[numompregions];
    Int_t i;
    //get fof in each region
    #pragma omp parallel default(shared) \
    private(i)
    {
    #pragma omp for schedule(dynamic) nowait
    for (i=0;i<numompregions;i++) {
        tree3dfofomp[i] = new KDTree(&Part.data()[ompdomain[i].noffset],ompdomain[i].ncount,opt.Bsize,tree3dfofomp[i]->TPHYS,tree3dfofomp[i]->KEPAN,100,0,0,0,period);
    }
    }
    return tree3dfofomp;
}

///search if some region is in the local mpi domain
int OpenMPSearchForOverlap(Double_t xsearch[3][2], Double_t bnd[3][2], Double_t period){
    Double_t xsearchp[3][2];
    if (!((bnd[0][1] < xsearch[0][0]) || (bnd[0][0] > xsearch[0][1]) ||
        (bnd[1][1] < xsearch[1][0]) || (bnd[1][0] > xsearch[1][1]) ||
        (bnd[2][1] < xsearch[2][0]) || (bnd[2][0] > xsearch[2][1])))
            return 1;
    else {
        if (period==0) return 0;
        else {
            for (int j=0;j<3;j++) {xsearchp[j][0]=xsearch[j][0];xsearchp[j][1]=xsearch[j][1];}
            for (int j=0;j<3;j++) {
                if (!((bnd[j][1] < xsearch[j][0]+period) || (bnd[j][0] > xsearch[j][1]+period))) {xsearchp[j][0]+=period;xsearchp[j][1]+=period;}
                else if (!((bnd[j][1] < xsearch[j][0]-period) || (bnd[j][0] > xsearch[j][1]-period))) {xsearchp[j][0]-=period;xsearchp[j][1]-=period;}
            }
            if (!((bnd[0][1] < xsearchp[0][0]) || (bnd[0][0] > xsearchp[0][1]) ||
            (bnd[1][1] < xsearchp[1][0]) || (bnd[1][0] > xsearchp[1][1]) ||
            (bnd[2][1] < xsearchp[2][0]) || (bnd[2][0] > xsearchp[2][1])))
                return 1;
            else return 0;
        }
    }
}

/// Determine if a particle needs to be exported to another mpi domain based on a physical search radius
int OpenMPSearchForOverlap(Particle &Part, Double_t bnd[3][2], Double_t rdist, Double_t period){
    Double_t xsearch[3][2];
    for (auto k=0;k<3;k++) {xsearch[k][0]=Part.GetPosition(k)-rdist;xsearch[k][1]=Part.GetPosition(k)+rdist;}
    return OpenMPSearchForOverlap(xsearch,bnd,period);
}

///Saerch particles to see if they overlap other OpenMP domains
Particle *OpenMPImportParticles(Options &opt, const Int_t nbodies, vector<Particle> &Part, Int_t * &pfof, Int_t *&storetype,
    const Int_t numompregions, OMP_Domain *&ompdomain, const Double_t rdist,
    Int_t *&omp_nrecv_total, Int_t *&omp_nrecv_offset)
{
    Int_t i,orgIndex;
    int omptask;
    Int_t importtotal=0;
    double time1=MyGetTime();
    Particle *Partompimport;
    Int_t *Partompimportindex;
#ifndef USEMPI
    int ThisTask=0,NProcs=1;
#endif
    cout<<ThisTask<<": Starting import build "<<endl;
    for (i=0;i<numompregions;i++) omp_nrecv_total[i]=omp_nrecv_offset[i]=0;
    #pragma omp parallel default(shared) \
    private(i,orgIndex,omptask)
    {
    #pragma omp for
    for (i=0;i<numompregions;i++) {
        for (auto j=ompdomain[i].noffset;j<ompdomain[i].noffset+ompdomain[i].ncount;j++) {
            orgIndex = storetype[Part[j].GetID()+ompdomain[i].noffset];
            if (pfof[orgIndex] == 0) continue;
            //for (auto k=0;k<numompregions;k++) if (k!=i) {
            for (auto k: ompdomain[i].neighbour) {
                if (OpenMPSearchForOverlap(Part[i],ompdomain[k].bnd,rdist,opt.p)) omp_nrecv_total[k] += 1;
            }
        }
    }
    }

    for (i=1;i<numompregions;i++) omp_nrecv_offset[i] = omp_nrecv_offset[i-1]+omp_nrecv_total[i-1];
    for (i=0;i<numompregions;i++) {
        if (opt.iverbose > 1) cout<<ThisTask<<" omp region "<<i<<" is importing "<<omp_nrecv_total[i]<<endl;
        importtotal += omp_nrecv_total[i];
        omp_nrecv_total[i] = 0;
    }
    Partompimport = new Particle[importtotal];

    #pragma omp parallel default(shared) \
    private(i,orgIndex,omptask)
    {
    #pragma omp for
    for (i=0;i<numompregions;i++) {
        for (auto j=ompdomain[i].noffset;j<ompdomain[i].noffset+ompdomain[i].ncount;j++) {
            orgIndex = storetype[Part[j].GetID()+ompdomain[i].noffset];
            if (pfof[orgIndex] == 0) continue;
            //for (auto k=0;k<numompregions;k++) if (k!=i) {
            for (auto k: ompdomain[i].neighbour) {
                if (OpenMPSearchForOverlap(Part[i],ompdomain[k].bnd,rdist,opt.p)) {
                    Partompimport[omp_nrecv_total[k]+omp_nrecv_offset[k]] = Part[j];
                    Partompimport[omp_nrecv_total[k]+omp_nrecv_offset[k]].SetID(pfof[orgIndex]);
                    omp_nrecv_total[k] += 1;
                }
            }
        }
    }
    }
    cout<<ThisTask<<" finished import "<<MyGetTime()-time1<<endl;
    return Partompimport;
}

void OpenMPLinkAcross(Options &opt,
    Int_t nbodies, vector<Particle> &Part, Int_t * &pfof,
    Int_t *&storetype, Int_tree_t *&Head, Int_tree_t *&Next,
    Double_t *param, FOFcheckfunc &fofcheck,
    const Int_t numompregions, OMP_Domain *&ompdomain, KDTree **tree3dfofomp,
    Int_t *&omp_nrecv_total, Int_t *&omp_nrecv_offset, Particle* &Partompimport)
{
    //now begin linking across
    Int_t i;
    Int_t omp_links_across_total;
    Int_t nt, orgIndex, curIndex, *nn=new Int_t[nbodies];
    Coordinate x;
    double time1=MyGetTime();
#ifndef USEMPI
    int ThisTask=0,NProcs=1;
#endif

    cout<<ThisTask<<": Starting linking across OpenMP domains"<<endl;
    do {
        omp_links_across_total = 0;
        #pragma omp parallel default(shared) \
        private(i,orgIndex,curIndex, x, nt)
        {
        #pragma omp for schedule(dynamic,1) nowait reduction(+:omp_links_across_total)
        for (i=0;i<numompregions;i++) {
            for (auto j=0;j<omp_nrecv_total[i];j++) {
                //for each imported particle, find all particles within search window
                for (auto k=0;k<3;k++) x[k]=Partompimport[omp_nrecv_offset[i]+j].GetPosition(k);
                nt=tree3dfofomp[i]->SearchBallPosTagged(x, param[1], &nn[ompdomain[i].noffset]);
                for (auto k=0;k<nt;k++) {
                    curIndex=nn[k+ompdomain[i].noffset]+ompdomain[i].noffset;
                    //check that at least on of the particles meets the type criterion if necessary
                    if (opt.partsearchtype==PSTALL && opt.iBaryonSearch>1)
                        if (fofcheck(Part[curIndex],param)!=0 && fofcheck(Partompimport[omp_nrecv_offset[i]+j],param)!=0) continue;

                    orgIndex = storetype[Part[curIndex].GetID()+ompdomain[i].noffset];
                    //otherwise, change these particles to local group id if local group id smaller
                    //if local particle in a group
                    if (pfof[orgIndex]>0)  {
                        //only change if both particles are appropriate type and group ids indicate local needs to be exported
                        if (opt.partsearchtype==PSTALL && opt.iBaryonSearch>1)
                            if (!(fofcheck(Part[curIndex],param)==0 && fofcheck(Partompimport[omp_nrecv_offset[i]+j],param)==0)) continue;
                        //if local group id is larger, change locally
                        if(pfof[orgIndex] > Partompimport[omp_nrecv_offset[i]+j].GetPID()) {
                            Int_t ss = Head[nn[k+ompdomain[i].noffset]+ompdomain[i].noffset];
                            do{
                                orgIndex = storetype[Part[ss+ompdomain[i].noffset].GetID()+ompdomain[i].noffset];
                                pfof[orgIndex]=Partompimport[omp_nrecv_offset[i]+j].GetPID();
                            }while((ss = Next[ss+ompdomain[i].noffset]) >= 0);
                            omp_links_across_total++;
                        }
                    }
                    //if local particle not in a group and export is appropriate type, link
                    else {
                        if (opt.partsearchtype==PSTALL && opt.iBaryonSearch>1)
                            if (fofcheck(Partompimport[omp_nrecv_offset[i]+j],param)!=0) continue;
                        pfof[orgIndex]=Partompimport[omp_nrecv_offset[i]+j].GetPID();
                        omp_links_across_total++;
                    }
                }
            }
        }
        }
    }while(omp_links_across_total>0);
    delete[] nn;
    cout<<ThisTask<<" finished linking "<<MyGetTime()-time1<<endl;
}

Int_t OpenMPResortParticleandGroups(Int_t nbodies, vector<Particle> &Part, Int_t *&pfof, Int_t minsize)
{
    Int_t start, ngroups=0;
    Int_t *numingroup, **plist;
    //now get number of groups and reorder group ids
    for (auto i=0;i<nbodies;i++) Part[i].SetID(-pfof[i]);
    //used to use ID store store group id info
    qsort(Part.data(),nbodies,sizeof(Particle),IDCompare);

    //determine the # of groups, their size and the current group ID
    for (auto i=0,start=0;i<nbodies;i++) {
        if (Part[i].GetID()!=Part[start].GetID()) {
            //if group is too small set type to zero, which currently is used to store the group id
            if ((i-start)<minsize) for (Int_t j=start;j<i;j++) Part[j].SetID(0);
            else ngroups++;
            start=i;
        }
        if (Part[i].GetID()==0) break;
    }
    //again resort to move untagged particles to the end.
    qsort(Part.data(),nbodies,sizeof(Particle),IDCompare);

    //now adjust pfof and ids.
    for (auto i=0;i<nbodies;i++) {pfof[i]=-Part[i].GetID();Part[i].SetID(i);}
    numingroup=new Int_t[ngroups+1];
    plist=new Int_t*[ngroups+1];
    ngroups=1;//offset as group zero is untagged
    for (auto i=0,start=0;i<nbodies;i++) {
        if (pfof[i]!=pfof[start]) {
            numingroup[ngroups]=i-start;
            plist[ngroups]=new Int_t[numingroup[ngroups]];
            for (auto j=start,count=0;j<i;j++) plist[ngroups][count++]=j;
            ngroups++;
            start=i;
        }
        if (pfof[i]==0) break;
    }
    ngroups--;

    //reorder groups ids according to size
    ReorderGroupIDs(ngroups,ngroups,numingroup,pfof,plist);
    for (auto i=1;i<=ngroups;i++) delete[] plist[i];
    delete[] plist;
    delete[] numingroup;
    return ngroups;
}

//@}

#endif
<commit_msg>bug fix<commit_after>/*! \file omproutines.cxx
 *  \brief this file contains routines used with OpenMP compilation.

    OpenMP routines pertaining to complex OpenMP calls.
 */

#ifdef USEOPENMP

//-- For MPI

#include "stf.h"

/// \name routines which check to see if some search region overlaps with local mpi domain
//@{
///construct omp domains
OMP_Domain *OpenMPBuildDomains(Options &opt, const Int_t numompregions, KDTree *&tree, const Double_t rdist)
{
    OMP_Domain *ompdomain = new OMP_Domain[numompregions];
    Int_t noffset=0;
    Node *np;
    for (auto i=0;i<numompregions;i++) {
        np = (tree->FindLeafNode(noffset));
        ompdomain[i].ncount = np->GetCount();
        ompdomain[i].noffset = noffset;
        for (int j=0;j<3;j++) {
            ompdomain[i].bnd[j][0]=np->GetBoundary(j,0)*0.99;
            ompdomain[i].bnd[j][1]=np->GetBoundary(j,1)*1.01;
        }
        noffset+=ompdomain[i].ncount;
    }
    //determine for each omp region, what are neighbour omp regions within rdist
    Double_t xsearch[3][2];
    for (auto i=0;i<numompregions;i++) {
        for (auto k=0;k<3;k++) {xsearch[k][0]=ompdomain[i].bnd[k][0]-rdist;xsearch[k][1]=ompdomain[i].bnd[k][1]+rdist;}
        for (auto j=0;j<numompregions;j++) if (j!=i){
            if (OpenMPSearchForOverlap(xsearch,ompdomain[j].bnd, opt.p)) ompdomain[i].neighbour.push_back(j);
        }
        noffset+=ompdomain[i].ncount;
    }
    np=NULL;
    return ompdomain;
}

KDTree **OpenMPBuildLocalTrees(Options &opt, const Int_t numompregions, vector<Particle> &Part, OMP_Domain *ompdomain, Double_t *period)
{
    KDTree **tree3dfofomp = new KDTree*[numompregions];
    Int_t i;
    //get fof in each region
    #pragma omp parallel default(shared) \
    private(i)
    {
    #pragma omp for schedule(dynamic) nowait
    for (i=0;i<numompregions;i++) {
        tree3dfofomp[i] = new KDTree(&Part.data()[ompdomain[i].noffset],ompdomain[i].ncount,opt.Bsize,tree3dfofomp[i]->TPHYS,tree3dfofomp[i]->KEPAN,100,0,0,0,period);
    }
    }
    return tree3dfofomp;
}

///search if some region is in the local mpi domain
int OpenMPSearchForOverlap(Double_t xsearch[3][2], Double_t bnd[3][2], Double_t period){
    Double_t xsearchp[3][2];
    if (!((bnd[0][1] < xsearch[0][0]) || (bnd[0][0] > xsearch[0][1]) ||
        (bnd[1][1] < xsearch[1][0]) || (bnd[1][0] > xsearch[1][1]) ||
        (bnd[2][1] < xsearch[2][0]) || (bnd[2][0] > xsearch[2][1])))
            return 1;
    else {
        if (period==0) return 0;
        else {
            for (int j=0;j<3;j++) {xsearchp[j][0]=xsearch[j][0];xsearchp[j][1]=xsearch[j][1];}
            for (int j=0;j<3;j++) {
                if (!((bnd[j][1] < xsearch[j][0]+period) || (bnd[j][0] > xsearch[j][1]+period))) {xsearchp[j][0]+=period;xsearchp[j][1]+=period;}
                else if (!((bnd[j][1] < xsearch[j][0]-period) || (bnd[j][0] > xsearch[j][1]-period))) {xsearchp[j][0]-=period;xsearchp[j][1]-=period;}
            }
            if (!((bnd[0][1] < xsearchp[0][0]) || (bnd[0][0] > xsearchp[0][1]) ||
            (bnd[1][1] < xsearchp[1][0]) || (bnd[1][0] > xsearchp[1][1]) ||
            (bnd[2][1] < xsearchp[2][0]) || (bnd[2][0] > xsearchp[2][1])))
                return 1;
            else return 0;
        }
    }
}

/// Determine if a particle needs to be exported to another mpi domain based on a physical search radius
int OpenMPSearchForOverlap(Particle &Part, Double_t bnd[3][2], Double_t rdist, Double_t period){
    Double_t xsearch[3][2];
    for (auto k=0;k<3;k++) {xsearch[k][0]=Part.GetPosition(k)-rdist;xsearch[k][1]=Part.GetPosition(k)+rdist;}
    return OpenMPSearchForOverlap(xsearch,bnd,period);
}

///Saerch particles to see if they overlap other OpenMP domains
Particle *OpenMPImportParticles(Options &opt, const Int_t nbodies, vector<Particle> &Part, Int_t * &pfof, Int_t *&storetype,
    const Int_t numompregions, OMP_Domain *&ompdomain, const Double_t rdist,
    Int_t *&omp_nrecv_total, Int_t *&omp_nrecv_offset)
{
    Int_t i,orgIndex;
    int omptask;
    Int_t importtotal=0;
    double time1=MyGetTime();
    Particle *Partompimport;
    Int_t *Partompimportindex;
#ifndef USEMPI
    int ThisTask=0,NProcs=1;
#endif
    cout<<ThisTask<<": Starting import build "<<endl;
    for (i=0;i<numompregions;i++) omp_nrecv_total[i]=omp_nrecv_offset[i]=0;
    #pragma omp parallel default(shared) \
    private(i,orgIndex,omptask)
    {
    #pragma omp for
    for (i=0;i<numompregions;i++) {
        for (auto j=ompdomain[i].noffset;j<ompdomain[i].noffset+ompdomain[i].ncount;j++) {
            orgIndex = storetype[Part[j].GetID()+ompdomain[i].noffset];
            if (pfof[orgIndex] == 0) continue;
            //for (auto k=0;k<numompregions;k++) if (k!=i) {
            for (auto k: ompdomain[i].neighbour) {
                if (OpenMPSearchForOverlap(Part[i],ompdomain[k].bnd,rdist,opt.p)) omp_nrecv_total[k] += 1;
            }
        }
    }
    }

    for (i=1;i<numompregions;i++) omp_nrecv_offset[i] = omp_nrecv_offset[i-1]+omp_nrecv_total[i-1];
    for (i=0;i<numompregions;i++) {
        if (opt.iverbose > 1) cout<<ThisTask<<" omp region "<<i<<" is importing "<<omp_nrecv_total[i]<<endl;
        importtotal += omp_nrecv_total[i];
        omp_nrecv_total[i] = 0;
    }
    Partompimport = new Particle[importtotal];

    #pragma omp parallel default(shared) \
    private(i,orgIndex,omptask)
    {
    #pragma omp for
    for (i=0;i<numompregions;i++) {
        for (auto j=ompdomain[i].noffset;j<ompdomain[i].noffset+ompdomain[i].ncount;j++) {
            orgIndex = storetype[Part[j].GetID()+ompdomain[i].noffset];
            if (pfof[orgIndex] == 0) continue;
            //for (auto k=0;k<numompregions;k++) if (k!=i) {
            for (auto k: ompdomain[i].neighbour) {
                if (OpenMPSearchForOverlap(Part[i],ompdomain[k].bnd,rdist,opt.p)) {
                    Partompimport[omp_nrecv_total[k]+omp_nrecv_offset[k]] = Part[j];
                    Partompimport[omp_nrecv_total[k]+omp_nrecv_offset[k]].SetID(pfof[orgIndex]);
                    omp_nrecv_total[k] += 1;
                }
            }
        }
    }
    }
    cout<<ThisTask<<" finished import "<<MyGetTime()-time1<<endl;
    return Partompimport;
}

void OpenMPLinkAcross(Options &opt,
    Int_t nbodies, vector<Particle> &Part, Int_t * &pfof,
    Int_t *&storetype, Int_tree_t *&Head, Int_tree_t *&Next,
    Double_t *param, FOFcheckfunc &fofcheck,
    const Int_t numompregions, OMP_Domain *&ompdomain, KDTree **tree3dfofomp,
    Int_t *&omp_nrecv_total, Int_t *&omp_nrecv_offset, Particle* &Partompimport)
{
    //now begin linking across
    Int_t i;
    Int_t omp_links_across_total;
    Int_t nt, orgIndex, curIndex, *nn=new Int_t[nbodies];
    Coordinate x;
    double time1=MyGetTime();
#ifndef USEMPI
    int ThisTask=0,NProcs=1;
#endif

    cout<<ThisTask<<": Starting linking across OpenMP domains"<<endl;
    do {
        omp_links_across_total = 0;
        #pragma omp parallel default(shared) \
        private(i,orgIndex,curIndex, x, nt)
        {
        #pragma omp for schedule(dynamic,1) nowait reduction(+:omp_links_across_total)
        for (i=0;i<numompregions;i++) {
            for (auto j=0;j<omp_nrecv_total[i];j++) {
                //for each imported particle, find all particles within search window
                for (auto k=0;k<3;k++) x[k]=Partompimport[omp_nrecv_offset[i]+j].GetPosition(k);
                nt=tree3dfofomp[i]->SearchBallPosTagged(x, param[1], &nn[ompdomain[i].noffset]);
                for (auto k=0;k<nt;k++) {
                    curIndex=nn[k+ompdomain[i].noffset]+ompdomain[i].noffset;
                    //check that at least on of the particles meets the type criterion if necessary
                    if (opt.partsearchtype==PSTALL && opt.iBaryonSearch>1)
                        if (fofcheck(Part[curIndex],param)!=0 && fofcheck(Partompimport[omp_nrecv_offset[i]+j],param)!=0) continue;

                    orgIndex = storetype[Part[curIndex].GetID()+ompdomain[i].noffset];
                    //otherwise, change these particles to local group id if local group id smaller
                    //if local particle in a group
                    if (pfof[orgIndex]>0)  {
                        //only change if both particles are appropriate type and group ids indicate local needs to be exported
                        if (opt.partsearchtype==PSTALL && opt.iBaryonSearch>1)
                            if (!(fofcheck(Part[curIndex],param)==0 && fofcheck(Partompimport[omp_nrecv_offset[i]+j],param)==0)) continue;
                        //if local group id is larger, change locally
                        if(pfof[orgIndex] > Partompimport[omp_nrecv_offset[i]+j].GetPID()) {
                            Int_t ss = Head[nn[k+ompdomain[i].noffset]+ompdomain[i].noffset];
                            do{
                                orgIndex = storetype[Part[ss+ompdomain[i].noffset].GetID()+ompdomain[i].noffset];
                                pfof[orgIndex]=Partompimport[omp_nrecv_offset[i]+j].GetPID();
                            }while((ss = Next[ss+ompdomain[i].noffset]) >= 0);
                            omp_links_across_total++;
                        }
                    }
                    //if local particle not in a group and export is appropriate type, link
                    else {
                        if (opt.partsearchtype==PSTALL && opt.iBaryonSearch>1)
                            if (fofcheck(Partompimport[omp_nrecv_offset[i]+j],param)!=0) continue;
                        pfof[orgIndex]=Partompimport[omp_nrecv_offset[i]+j].GetPID();
                        omp_links_across_total++;
                    }
                }
            }
        }
        }
    }while(omp_links_across_total>0);
    delete[] nn;
    cout<<ThisTask<<" finished linking "<<MyGetTime()-time1<<endl;
}

Int_t OpenMPResortParticleandGroups(Int_t nbodies, vector<Particle> &Part, Int_t *&pfof, Int_t minsize)
{
    Int_t start, ngroups=0;
    Int_t *numingroup, **plist;
    //now get number of groups and reorder group ids
    for (auto i=0;i<nbodies;i++) Part[i].SetID(-pfof[i]);
    //used to use ID store store group id info
    qsort(Part.data(),nbodies,sizeof(Particle),IDCompare);

    //determine the # of groups, their size and the current group ID
    for (auto i=0,start=0;i<nbodies;i++) {
        if (Part[i].GetID()!=Part[start].GetID()) {
            //if group is too small set type to zero, which currently is used to store the group id
            if ((i-start)<minsize) for (Int_t j=start;j<i;j++) Part[j].SetID(0);
            else ngroups++;
            start=i;
        }
        if (Part[i].GetID()==0) break;
    }
    //again resort to move untagged particles to the end.
    qsort(Part.data(),nbodies,sizeof(Particle),IDCompare);

    //now adjust pfof and ids.
    for (auto i=0;i<nbodies;i++) {pfof[i]=-Part[i].GetID();Part[i].SetID(i);}
    numingroup=new Int_t[ngroups+1];
    plist=new Int_t*[ngroups+1];
    ngroups=1;//offset as group zero is untagged
    for (auto i=0,start=0;i<nbodies;i++) {
        if (pfof[i]!=pfof[start]) {
            numingroup[ngroups]=i-start;
            plist[ngroups]=new Int_t[numingroup[ngroups]];
            for (auto j=start,count=0;j<i;j++) plist[ngroups][count++]=j;
            ngroups++;
            start=i;
        }
        if (pfof[i]==0) break;
    }
    ngroups--;

    //reorder groups ids according to size
    ReorderGroupIDs(ngroups,ngroups,numingroup,pfof,plist);
    for (auto i=1;i<=ngroups;i++) delete[] plist[i];
    delete[] plist;
    delete[] numingroup;
    return ngroups;
}

//@}

#endif
<|endoftext|>


<commit_before>#ifndef SRC_ONLINE_AROW_HPP_
#define SRC_ONLINE_AROW_HPP_

#include <Eigen/Dense>
#include <cstdbool>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/split_member.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <fstream>
#include "utility.hpp"

class AROW {
private :
  const std::size_t kDim;
  const double kR;

private :
  Eigen::VectorXd _covariances;
  Eigen::VectorXd _means;

public :
  AROW(const std::size_t dim, const double r)
    : kDim(dim),
      kR(r),
      _covariances(Eigen::VectorXd::Ones(kDim)),
      _means(Eigen::VectorXd::Zero(kDim)) {

    static_assert(std::numeric_limits<decltype(dim)>::max() > 0, "Dimension Error. (Dimension > 0)");
    static_assert(std::numeric_limits<decltype(r)>::max() > 0, "Hyper Parameter Error. (r > 0)");
    assert(dim > 0);
    assert(r > 0);

  }

  virtual ~AROW() { }

  double suffer_loss(const double margin, const int label) const {
    return margin * label;
  }

  double calculate_margin(const Eigen::VectorXd& x) const {
    return _means.dot(x);
  }

  double calculate_confidence(const Eigen::VectorXd& feature) const {
    auto confidence = 0.0;
    utility::enumerate(feature.data(), feature.data() + feature.size(), 0,
                       [&](const int index, const double value) {
                         confidence += _covariances[index] * value * value;
                       });
    return confidence;
  }

  bool update(const Eigen::VectorXd& feature, const int label) {
    const auto margin = calculate_margin(feature);

    if (suffer_loss(margin, label) >= 1.0) { return false; }

    const auto confidence = calculate_confidence(feature);
    const auto beta = 1.0 / (confidence + kR);
    const auto alpha = std::max(0.0, 1.0 - label * margin) * beta;

    utility::enumerate(feature.data(), feature.data() + feature.size(), 0,
                       [&](const int index, const double value) {
                         const auto v = _covariances[index] * value;
                         _means[index] += alpha * label * v;
                         _covariances[index] -= beta * v * v;
                       });
    return true;
  }

  int predict(Eigen::VectorXd& x) const {
    return calculate_margin(x) > 0.0 ? 1 : -1;
  }

  void save(const std::string& filename) {
    std::ofstream ofs(filename);
    assert(ofs);
    boost::archive::text_oarchive oa(ofs);
    oa << *this;
    ofs.close();
  }

  void load(const std::string& filename) {
    std::ifstream ifs(filename);
    assert(ifs);
    boost::archive::text_iarchive ia(ifs);
    ia >> *this;
    ifs.close();
  }

private :
  friend class boost::serialization::access;
  BOOST_SERIALIZATION_SPLIT_MEMBER();
  template <class Archive>
  void save(Archive& ar, const unsigned int version) const {
    std::vector<double> covariances_vector(_covariances.data(), _covariances.data() + _covariances.size());
    std::vector<double> means_vector(_means.data(), _means.data() + _means.size());
    ar & boost::serialization::make_nvp("covariances", covariances_vector);
    ar & boost::serialization::make_nvp("means", means_vector);
    ar & boost::serialization::make_nvp("Dimension", const_cast<std::size_t&>(kDim));
    ar & boost::serialization::make_nvp("R", const_cast<double&>(kR));
  }

  template <class Archive>
  void load(Archive& ar, const unsigned int version) {
    std::vector<double> covariances_vector;
    std::vector<double> means_vector;
    ar & boost::serialization::make_nvp("covariances", covariances_vector);
    ar & boost::serialization::make_nvp("means", means_vector);
    ar & boost::serialization::make_nvp("Dimension", const_cast<std::size_t&>(kDim));
    ar & boost::serialization::make_nvp("R", const_cast<double&>(kR));
    _covariances = Eigen::Map<Eigen::VectorXd>(&covariances_vector[0], covariances_vector.size());
    _means = Eigen::Map<Eigen::VectorXd>(&means_vector[0], means_vector.size());
  }
};

#endif //SRC_ONLINE_AROW_HPP_
<commit_msg>コメントのミスを修正<commit_after>#ifndef SRC_ONLINE_AROW_HPP_
#define SRC_ONLINE_AROW_HPP_

#include <Eigen/Dense>
#include <cstdbool>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/split_member.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <fstream>
#include "utility.hpp"

class AROW {
private :
  const std::size_t kDim;
  const double kR;

private :
  Eigen::VectorXd _covariances;
  Eigen::VectorXd _means;

public :
  AROW(const std::size_t dim, const double r)
    : kDim(dim),
      kR(r),
      _covariances(Eigen::VectorXd::Ones(kDim)),
      _means(Eigen::VectorXd::Zero(kDim)) {

    static_assert(std::numeric_limits<decltype(dim)>::max() > 0, "Dimension Error. (Dimension > 0)");
    static_assert(std::numeric_limits<decltype(r)>::max() > 0, "Hyper Parameter Error. (r > 0)");
    assert(dim > 0);
    assert(r > 0);

  }

  virtual ~AROW() { }

  double suffer_loss(const double margin, const int label) const {
    return margin * label;
  }

  double calculate_margin(const Eigen::VectorXd& x) const {
    return _means.dot(x);
  }

  double calculate_confidence(const Eigen::VectorXd& feature) const {
    auto confidence = 0.0;
    utility::enumerate(feature.data(), feature.data() + feature.size(), 0,
                       [&](const int index, const double value) {
                         confidence += _covariances[index] * value * value;
                       });
    return confidence;
  }

  bool update(const Eigen::VectorXd& feature, const int label) {
    const auto margin = calculate_margin(feature);

    if (suffer_loss(margin, label) >= 1.0) { return false; }

    const auto confidence = calculate_confidence(feature);
    const auto beta = 1.0 / (confidence + kR);
    const auto alpha = std::max(0.0, 1.0 - label * margin) * beta;

    utility::enumerate(feature.data(), feature.data() + feature.size(), 0,
                       [&](const int index, const double value) {
                         const auto v = _covariances[index] * value;
                         _means[index] += alpha * label * v;
                         _covariances[index] -= beta * v * v;
                       });
    return true;
  }

  int predict(Eigen::VectorXd& x) const {
    return calculate_margin(x) > 0.0 ? 1 : -1;
  }

  void save(const std::string& filename) {
    std::ofstream ofs(filename);
    assert(ofs);
    boost::archive::text_oarchive oa(ofs);
    oa << *this;
    ofs.close();
  }

  void load(const std::string& filename) {
    std::ifstream ifs(filename);
    assert(ifs);
    boost::archive::text_iarchive ia(ifs);
    ia >> *this;
    ifs.close();
  }

private :
  friend class boost::serialization::access;
  BOOST_SERIALIZATION_SPLIT_MEMBER();
  template <class Archive>
  void save(Archive& ar, const unsigned int version) const {
    std::vector<double> covariances_vector(_covariances.data(), _covariances.data() + _covariances.size());
    std::vector<double> means_vector(_means.data(), _means.data() + _means.size());
    ar & boost::serialization::make_nvp("covariances", covariances_vector);
    ar & boost::serialization::make_nvp("means", means_vector);
    ar & boost::serialization::make_nvp("dimension", const_cast<std::size_t&>(kDim));
    ar & boost::serialization::make_nvp("r", const_cast<double&>(kR));
  }

  template <class Archive>
  void load(Archive& ar, const unsigned int version) {
    std::vector<double> covariances_vector;
    std::vector<double> means_vector;
    ar & boost::serialization::make_nvp("covariances", covariances_vector);
    ar & boost::serialization::make_nvp("means", means_vector);
    ar & boost::serialization::make_nvp("dimension", const_cast<std::size_t&>(kDim));
    ar & boost::serialization::make_nvp("r", const_cast<double&>(kR));
    _covariances = Eigen::Map<Eigen::VectorXd>(&covariances_vector[0], covariances_vector.size());
    _means = Eigen::Map<Eigen::VectorXd>(&means_vector[0], means_vector.size());
  }
};

#endif //SRC_ONLINE_AROW_HPP_
<|endoftext|>


<commit_before>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
 *
 * This library is open source and may be redistributed and/or modified under
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 *
 * This library 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
 * OpenSceneGraph Public License for more details.
*/
#include <osg/Version>
#include <string>

extern "C" {

const char* osgGetVersion()
{
    static char osg_version[256];
    static int osg_version_init = 1;
    if (osg_version_init)
    {
        if (OSG_VERSION_RELEASE==0)
        {
            if (OSG_VERSION_REVISION==0)
            {
                sprintf(osg_version,"%d.%d",OSG_VERSION_MAJOR,OSG_VERSION_MINOR);
            }
            else
            {
                sprintf(osg_version,"%d.%d-%d",OSG_VERSION_MAJOR,OSG_VERSION_MINOR,OSG_VERSION_REVISION);
            }
        }
        else
        {
            if (OSG_VERSION_REVISION==0)
            {
                sprintf(osg_version,"%d.%d.%d",OSG_VERSION_MAJOR,OSG_VERSION_MINOR,OSG_VERSION_RELEASE);
            }
            else
            {
                sprintf(osg_version,"%d.%d.%d-%d",OSG_VERSION_MAJOR,OSG_VERSION_MINOR,OSG_VERSION_RELEASE,OSG_VERSION_REVISION);
            }
        }
        osg_version_init = 0;
    }

    return osg_version;
}

const char* osgGetLibraryName()
{
    return "OpenSceneGraph Library";
}

}
<commit_msg>Changed the version format.<commit_after>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
 *
 * This library is open source and may be redistributed and/or modified under
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 *
 * This library 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
 * OpenSceneGraph Public License for more details.
*/
#include <osg/Version>
#include <string>

extern "C" {

const char* osgGetVersion()
{
    static char osg_version[256];
    static int osg_version_init = 1;
    if (osg_version_init)
    {
        if (OSG_VERSION_REVISION==0)
        {
            sprintf(osg_version,"%d.%d.%d",OSG_VERSION_MAJOR,OSG_VERSION_MINOR,OSG_VERSION_RELEASE);
        }
        else
        {
            sprintf(osg_version,"%d.%d.%d-%d",OSG_VERSION_MAJOR,OSG_VERSION_MINOR,OSG_VERSION_RELEASE,OSG_VERSION_REVISION);
        }

        osg_version_init = 0;
    }

    return osg_version;
}

const char* osgGetLibraryName()
{
    return "OpenSceneGraph Library";
}

}
<|endoftext|>