<commit_before>#if HAVE_CXX11
#include <regex>
#endif

#include "gtest/gtest.h"
#include "regex2dfa.h"

#include "fte/fte.h"

#if HAVE_CXX11
TEST(SanityCheckMatch, Test1) {
  std::string str = "aaaaaaaaaaaaaaaa";
  std::regex rx("^(a|b){16}$");
  bool match = regex_match(str.begin(), str.end(), rx);
  EXPECT_EQ(true, match);
}

TEST(SanityCheckMatch, Test2) {
  std::string str = "bbbbbbbbbbbbbbbb";
  std::regex rx("^(a|b){16}$");
  bool match = regex_match(str.begin(), str.end(), rx);
  EXPECT_EQ(true, match);
}

TEST(SanityCheckNoMatch, Test1) {
  std::string str = "aaaaaaaaaaaaaaaaa";
  std::regex rx("^(a|b){16}$");
  bool match = regex_match(str.begin(), str.end(), rx);
  EXPECT_EQ(false, match);
}

TEST(SanityCheckNoMatch, Test2) {
  std::string str = "aaaaaaaaaaaaaaa";
  std::regex rx("^(a|b){16}$");
  bool match = regex_match(str.begin(), str.end(), rx);
  EXPECT_EQ(false, match);
}

TEST(SanityCheckNoMatch, Test3) {
  std::string str = "c";
  std::regex rx("^(a|b){16}$");
  bool match = regex_match(str.begin(), str.end(), rx);
  EXPECT_EQ(false, match);
}

TEST(SanityCheckNoMatch, Test4) {
  std::string str = "cccccccccccccccc";
  std::regex rx("^(a|b){16}$");
  bool match = regex_match(str.begin(), str.end(), rx);
  EXPECT_EQ(false, match);
}
#endif

TEST(FteNormalUsage, FpeTest1) {
  std::string K = "00000000000000000000000000000000";
  fte::Fte fteObj;
  fteObj.set_key(K);
  std::string dfa;
  regex2dfa::Regex2Dfa("^(a|b)+$", &dfa);
  fteObj.SetLanguages(dfa, 64,
                      dfa, 64);

  std::string input_plaintext =
    "aaaabbbbaaaabbbbaaaabbbbaaaabbbbaaaabbbbaaaabbbbaaaabbbbaaaabbbb";
  std::string ciphertext, output_plaintext;
  fteObj.Encrypt(input_plaintext, &ciphertext);
  fteObj.Decrypt(ciphertext, &output_plaintext);

  EXPECT_EQ(input_plaintext, output_plaintext);

#if HAVE_CXX11
  std::regex rx("^(a|b){1,64}$");
  bool match = regex_match(ciphertext.begin(), ciphertext.end(), rx);
  EXPECT_EQ(true, match);
#endif
}

TEST(FteNormalUsage, FpeTest2) {
  std::string K = "00000000000000000000000000000000";
  fte::Fte fteObj;
  fteObj.set_key(K);
  std::string dfa = "";
  regex2dfa::Regex2Dfa("^.+$", &dfa);
  fteObj.SetLanguages(dfa, 128,
                      dfa, 128);

  std::string input_plaintext = "a";
  std::string ciphertext, output_plaintext;
  fteObj.Encrypt(input_plaintext, &ciphertext);
  fteObj.Decrypt(ciphertext, &output_plaintext);

  EXPECT_EQ(input_plaintext, output_plaintext);

#if HAVE_CXX11
  std::regex rx("^[\\x00-\\xFF]{1,128}$");
  bool match = regex_match(ciphertext.begin(), ciphertext.end(), rx);
  EXPECT_EQ(true, match);
#endif
}

TEST(FteNormalUsage, FpeTest3) {
  std::string K = "00000000000000000000000000000000";
  fte::Fte fteObj;
  fteObj.set_key(K);
  std::string dfa;
  regex2dfa::Regex2Dfa("^.+$", &dfa);
  fteObj.SetLanguages(dfa, 256,
                      dfa, 256);

  std::string input_plaintext = "a";
  std::string ciphertext, output_plaintext;
  fteObj.Encrypt(input_plaintext, &ciphertext);
  fteObj.Decrypt(ciphertext, &output_plaintext);

  EXPECT_EQ(input_plaintext, output_plaintext);

#if HAVE_CXX11
  std::regex rx("^[\\x00-\\xFF]{1,256}$");
  bool match = regex_match(ciphertext.begin(), ciphertext.end(), rx);
  EXPECT_EQ(true, match);
#endif
}

TEST(FteNormalUsage, FpeTest4) {
  std::string K = "00000000000000000000000000000000";
  fte::Fte fteObj;
  fteObj.set_key(K);
  std::string dfa;
  regex2dfa::Regex2Dfa("^.+$", &dfa);
  fteObj.SetLanguages(dfa, 512,
                      dfa, 512);

  std::string input_plaintext = "a";
  std::string ciphertext, output_plaintext;
  fteObj.Encrypt(input_plaintext, &ciphertext);
  fteObj.Decrypt(ciphertext, &output_plaintext);

  EXPECT_EQ(input_plaintext, output_plaintext);

#if HAVE_CXX11
  std::regex rx("^[\\x00-\\xFF]{1,512}$");
  bool match = regex_match(ciphertext.begin(), ciphertext.end(), rx);
  EXPECT_EQ(true, match);
#endif
}

TEST(FteNormalUsage, FpeTest5) {
  std::string K = "00000000000000000000000000000000";
  fte::Fte fteObj;
  fteObj.set_key(K);
  std::string dfa;
  regex2dfa::Regex2Dfa("^.+$", &dfa);
  fteObj.SetLanguages(dfa, 1024,
                      dfa, 1024);

  std::string input_plaintext = "a";
  std::string ciphertext, output_plaintext;
  fteObj.Encrypt(input_plaintext, &ciphertext);
  fteObj.Decrypt(ciphertext, &output_plaintext);

  EXPECT_EQ(input_plaintext, output_plaintext);

#if HAVE_CXX11
  std::regex rx("^[\\x00-\\xFF]{1,1024}$");
  bool match = regex_match(ciphertext.begin(), ciphertext.end(), rx);
  EXPECT_EQ(true, match);
#endif
}

TEST(FteNormalUsage, FpeTest6) {
  std::string K = "00000000000000000000000000000000";
  fte::Fte fteObj;
  fteObj.set_key(K);
  std::string dfa;
  regex2dfa::Regex2Dfa("^.+$", &dfa);
  fteObj.SetLanguages(dfa, 2048,
                      dfa, 2048);

  std::string input_plaintext = "a";
  std::string ciphertext, output_plaintext;
  fteObj.Encrypt(input_plaintext, &ciphertext);
  fteObj.Decrypt(ciphertext, &output_plaintext);

  EXPECT_EQ(input_plaintext, output_plaintext);

#if HAVE_CXX11
  std::regex rx("^[\\x00-\\xFF]{1,2048}$");
  bool match = regex_match(ciphertext.begin(), ciphertext.end(), rx);
  EXPECT_EQ(true, match);
#endif
}

TEST(FteNormalUsage, FteTest1) {
  std::string K = "00000000000000000000000000000000";
  fte::Fte fteObj;
  fteObj.set_key(K);
  std::string dfa;
  regex2dfa::Regex2Dfa("^(a|b)+$", &dfa);
  fteObj.SetLanguages(dfa, 32,
                      dfa, 64);

  std::string input_plaintext = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
  std::string ciphertext, output_plaintext;
  fteObj.Encrypt(input_plaintext, &ciphertext);
  fteObj.Decrypt(ciphertext, &output_plaintext);

  EXPECT_EQ(input_plaintext, output_plaintext);

#if HAVE_CXX11
  std::regex rx("^(a|b){1,64}$");
  bool match = regex_match(ciphertext.begin(), ciphertext.end(), rx);
  EXPECT_EQ(true, match);
#endif
}

TEST(FteNormalUsage, FteTest2) {
  std::string K = "00000000000000000000000000000000";
  fte::Fte fteObj;
  fteObj.set_key(K);
  std::string plaintext_dfa;
  std::string ciphertext_dfa;
  regex2dfa::Regex2Dfa("^(a|b)+$", &plaintext_dfa);
  regex2dfa::Regex2Dfa("^.+$", &ciphertext_dfa);
  fteObj.SetLanguages(plaintext_dfa, 32,
                      ciphertext_dfa, 32);

  std::string input_plaintext = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
  std::string ciphertext, output_plaintext;
  fteObj.Encrypt(input_plaintext, &ciphertext);
  fteObj.Decrypt(ciphertext, &output_plaintext);

  EXPECT_EQ(input_plaintext, output_plaintext);

#if HAVE_CXX11
  std::regex rx("^[\\x00-\\xFF]{1,32}$");
  bool match = regex_match(ciphertext.begin(), ciphertext.end(), rx);
  EXPECT_EQ(true, match);
#endif
}

// This test is in support of Issue#10:
//   https://github.com/uProxy/libfte/issues/10
TEST(FteMultipleEncrypts, FteTest1) {
  std::string K = "00000000000000000000000000000000";
  fte::Fte fteObj;
  fteObj.set_key(K);
  fteObj.SetLanguages(VALID_DFA_1, 32,
                      VALID_DFA_5, 32);

  std::string input_plaintext = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
  std::string ciphertext, output_plaintext;
  fteObj.Encrypt(input_plaintext, &ciphertext);
  fteObj.Encrypt(input_plaintext, &ciphertext);
  fteObj.Decrypt(ciphertext, &output_plaintext);

  EXPECT_EQ(input_plaintext, output_plaintext);
}

// This test is in support of Issue#10:
//   https://github.com/uProxy/libfte/issues/10
TEST(FteMultipleDecrypts, FteTest1) {
  std::string K = "00000000000000000000000000000000";
  fte::Fte fteObj;
  fteObj.set_key(K);
  fteObj.SetLanguages(VALID_DFA_1, 32,
                      VALID_DFA_5, 32);

  std::string input_plaintext = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
  std::string ciphertext, output_plaintext;
  fteObj.Encrypt(input_plaintext, &ciphertext);
  fteObj.Decrypt(ciphertext, &output_plaintext);
  fteObj.Decrypt(ciphertext, &output_plaintext);

  EXPECT_EQ(input_plaintext, output_plaintext);
}
<commit_msg>Issue #10: resolving conflict from merge<commit_after>#if HAVE_CXX11
#include <regex>
#endif

#include "gtest/gtest.h"
#include "regex2dfa.h"

#include "fte/fte.h"

#if HAVE_CXX11
TEST(SanityCheckMatch, Test1) {
  std::string str = "aaaaaaaaaaaaaaaa";
  std::regex rx("^(a|b){16}$");
  bool match = regex_match(str.begin(), str.end(), rx);
  EXPECT_EQ(true, match);
}

TEST(SanityCheckMatch, Test2) {
  std::string str = "bbbbbbbbbbbbbbbb";
  std::regex rx("^(a|b){16}$");
  bool match = regex_match(str.begin(), str.end(), rx);
  EXPECT_EQ(true, match);
}

TEST(SanityCheckNoMatch, Test1) {
  std::string str = "aaaaaaaaaaaaaaaaa";
  std::regex rx("^(a|b){16}$");
  bool match = regex_match(str.begin(), str.end(), rx);
  EXPECT_EQ(false, match);
}

TEST(SanityCheckNoMatch, Test2) {
  std::string str = "aaaaaaaaaaaaaaa";
  std::regex rx("^(a|b){16}$");
  bool match = regex_match(str.begin(), str.end(), rx);
  EXPECT_EQ(false, match);
}

TEST(SanityCheckNoMatch, Test3) {
  std::string str = "c";
  std::regex rx("^(a|b){16}$");
  bool match = regex_match(str.begin(), str.end(), rx);
  EXPECT_EQ(false, match);
}

TEST(SanityCheckNoMatch, Test4) {
  std::string str = "cccccccccccccccc";
  std::regex rx("^(a|b){16}$");
  bool match = regex_match(str.begin(), str.end(), rx);
  EXPECT_EQ(false, match);
}
#endif

TEST(FteNormalUsage, FpeTest1) {
  std::string K = "00000000000000000000000000000000";
  fte::Fte fteObj;
  fteObj.set_key(K);
  std::string dfa;
  regex2dfa::Regex2Dfa("^(a|b)+$", &dfa);
  fteObj.SetLanguages(dfa, 64,
                      dfa, 64);

  std::string input_plaintext =
    "aaaabbbbaaaabbbbaaaabbbbaaaabbbbaaaabbbbaaaabbbbaaaabbbbaaaabbbb";
  std::string ciphertext, output_plaintext;
  fteObj.Encrypt(input_plaintext, &ciphertext);
  fteObj.Decrypt(ciphertext, &output_plaintext);

  EXPECT_EQ(input_plaintext, output_plaintext);

#if HAVE_CXX11
  std::regex rx("^(a|b){1,64}$");
  bool match = regex_match(ciphertext.begin(), ciphertext.end(), rx);
  EXPECT_EQ(true, match);
#endif
}

TEST(FteNormalUsage, FpeTest2) {
  std::string K = "00000000000000000000000000000000";
  fte::Fte fteObj;
  fteObj.set_key(K);
  std::string dfa = "";
  regex2dfa::Regex2Dfa("^.+$", &dfa);
  fteObj.SetLanguages(dfa, 128,
                      dfa, 128);

  std::string input_plaintext = "a";
  std::string ciphertext, output_plaintext;
  fteObj.Encrypt(input_plaintext, &ciphertext);
  fteObj.Decrypt(ciphertext, &output_plaintext);

  EXPECT_EQ(input_plaintext, output_plaintext);

#if HAVE_CXX11
  std::regex rx("^[\\x00-\\xFF]{1,128}$");
  bool match = regex_match(ciphertext.begin(), ciphertext.end(), rx);
  EXPECT_EQ(true, match);
#endif
}

TEST(FteNormalUsage, FpeTest3) {
  std::string K = "00000000000000000000000000000000";
  fte::Fte fteObj;
  fteObj.set_key(K);
  std::string dfa;
  regex2dfa::Regex2Dfa("^.+$", &dfa);
  fteObj.SetLanguages(dfa, 256,
                      dfa, 256);

  std::string input_plaintext = "a";
  std::string ciphertext, output_plaintext;
  fteObj.Encrypt(input_plaintext, &ciphertext);
  fteObj.Decrypt(ciphertext, &output_plaintext);

  EXPECT_EQ(input_plaintext, output_plaintext);

#if HAVE_CXX11
  std::regex rx("^[\\x00-\\xFF]{1,256}$");
  bool match = regex_match(ciphertext.begin(), ciphertext.end(), rx);
  EXPECT_EQ(true, match);
#endif
}

TEST(FteNormalUsage, FpeTest4) {
  std::string K = "00000000000000000000000000000000";
  fte::Fte fteObj;
  fteObj.set_key(K);
  std::string dfa;
  regex2dfa::Regex2Dfa("^.+$", &dfa);
  fteObj.SetLanguages(dfa, 512,
                      dfa, 512);

  std::string input_plaintext = "a";
  std::string ciphertext, output_plaintext;
  fteObj.Encrypt(input_plaintext, &ciphertext);
  fteObj.Decrypt(ciphertext, &output_plaintext);

  EXPECT_EQ(input_plaintext, output_plaintext);

#if HAVE_CXX11
  std::regex rx("^[\\x00-\\xFF]{1,512}$");
  bool match = regex_match(ciphertext.begin(), ciphertext.end(), rx);
  EXPECT_EQ(true, match);
#endif
}

TEST(FteNormalUsage, FpeTest5) {
  std::string K = "00000000000000000000000000000000";
  fte::Fte fteObj;
  fteObj.set_key(K);
  std::string dfa;
  regex2dfa::Regex2Dfa("^.+$", &dfa);
  fteObj.SetLanguages(dfa, 1024,
                      dfa, 1024);

  std::string input_plaintext = "a";
  std::string ciphertext, output_plaintext;
  fteObj.Encrypt(input_plaintext, &ciphertext);
  fteObj.Decrypt(ciphertext, &output_plaintext);

  EXPECT_EQ(input_plaintext, output_plaintext);

#if HAVE_CXX11
  std::regex rx("^[\\x00-\\xFF]{1,1024}$");
  bool match = regex_match(ciphertext.begin(), ciphertext.end(), rx);
  EXPECT_EQ(true, match);
#endif
}

TEST(FteNormalUsage, FpeTest6) {
  std::string K = "00000000000000000000000000000000";
  fte::Fte fteObj;
  fteObj.set_key(K);
  std::string dfa;
  regex2dfa::Regex2Dfa("^.+$", &dfa);
  fteObj.SetLanguages(dfa, 2048,
                      dfa, 2048);

  std::string input_plaintext = "a";
  std::string ciphertext, output_plaintext;
  fteObj.Encrypt(input_plaintext, &ciphertext);
  fteObj.Decrypt(ciphertext, &output_plaintext);

  EXPECT_EQ(input_plaintext, output_plaintext);

#if HAVE_CXX11
  std::regex rx("^[\\x00-\\xFF]{1,2048}$");
  bool match = regex_match(ciphertext.begin(), ciphertext.end(), rx);
  EXPECT_EQ(true, match);
#endif
}

TEST(FteNormalUsage, FteTest1) {
  std::string K = "00000000000000000000000000000000";
  fte::Fte fteObj;
  fteObj.set_key(K);
  std::string dfa;
  regex2dfa::Regex2Dfa("^(a|b)+$", &dfa);
  fteObj.SetLanguages(dfa, 32,
                      dfa, 64);

  std::string input_plaintext = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
  std::string ciphertext, output_plaintext;
  fteObj.Encrypt(input_plaintext, &ciphertext);
  fteObj.Decrypt(ciphertext, &output_plaintext);

  EXPECT_EQ(input_plaintext, output_plaintext);

#if HAVE_CXX11
  std::regex rx("^(a|b){1,64}$");
  bool match = regex_match(ciphertext.begin(), ciphertext.end(), rx);
  EXPECT_EQ(true, match);
#endif
}

TEST(FteNormalUsage, FteTest2) {
  std::string K = "00000000000000000000000000000000";
  fte::Fte fteObj;
  fteObj.set_key(K);
  std::string plaintext_dfa;
  std::string ciphertext_dfa;
  regex2dfa::Regex2Dfa("^(a|b)+$", &plaintext_dfa);
  regex2dfa::Regex2Dfa("^.+$", &ciphertext_dfa);
  fteObj.SetLanguages(plaintext_dfa, 32,
                      ciphertext_dfa, 32);

  std::string input_plaintext = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
  std::string ciphertext, output_plaintext;
  fteObj.Encrypt(input_plaintext, &ciphertext);
  fteObj.Decrypt(ciphertext, &output_plaintext);

  EXPECT_EQ(input_plaintext, output_plaintext);

#if HAVE_CXX11
  std::regex rx("^[\\x00-\\xFF]{1,32}$");
  bool match = regex_match(ciphertext.begin(), ciphertext.end(), rx);
  EXPECT_EQ(true, match);
#endif
}

// This test is in support of Issue#10:
//   https://github.com/uProxy/libfte/issues/10
TEST(FteMultipleEncrypts, FteTest1) {
  std::string K = "00000000000000000000000000000000";
  fte::Fte fteObj;
  fteObj.set_key(K);
  std::string plaintext_dfa;
  std::string ciphertext_dfa;
  regex2dfa::Regex2Dfa("^(a|b)+$", &plaintext_dfa);
  regex2dfa::Regex2Dfa("^.+$", &ciphertext_dfa);
  fteObj.SetLanguages(plaintext_dfa, 32,
                      ciphertext_dfa, 32);

  std::string input_plaintext = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
  std::string ciphertext, output_plaintext;
  fteObj.Encrypt(input_plaintext, &ciphertext);
  fteObj.Encrypt(input_plaintext, &ciphertext);
  fteObj.Decrypt(ciphertext, &output_plaintext);

  EXPECT_EQ(input_plaintext, output_plaintext);
}

// This test is in support of Issue#10:
//   https://github.com/uProxy/libfte/issues/10
TEST(FteMultipleDecrypts, FteTest1) {
  std::string K = "00000000000000000000000000000000";
  fte::Fte fteObj;
  fteObj.set_key(K);
  std::string plaintext_dfa;
  std::string ciphertext_dfa;
  regex2dfa::Regex2Dfa("^(a|b)+$", &plaintext_dfa);
  regex2dfa::Regex2Dfa("^.+$", &ciphertext_dfa);
  fteObj.SetLanguages(plaintext_dfa, 32,
                      ciphertext_dfa, 32);

  std::string input_plaintext = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
  std::string ciphertext, output_plaintext;
  fteObj.Encrypt(input_plaintext, &ciphertext);
  fteObj.Decrypt(ciphertext, &output_plaintext);
  fteObj.Decrypt(ciphertext, &output_plaintext);

  EXPECT_EQ(input_plaintext, output_plaintext);
}
<|endoftext|>


<commit_before>/* **** **** **** **** **** **** **** **** *
 *
 *         _/        _/        _/
 *        _/_/_/    _/_/_/    _/_/_/
 *       _/    _/  _/    _/  _/    _/
 *      _/    _/  _/    _/  _/    _/
 *     _/_/_/    _/_/_/    _/_/_/
 *
 * bit by bit
 * simple_test.hpp
 *
 * author: ISHII 2bit
 * mail:   2bit@backspace.tokyo
 *
 * **** **** **** **** **** **** **** **** */

#pragma once
#include <cassert>
#include <iostream>

#define bbb_test_declaretion(name)\
namespace name##_test { void test(); };

#define bbb_test_begin_definition(name)\
namespace name##_test {\
    std::size_t total{0};\
    std::size_t success{0};\
    std::size_t failure{0};\
    void test() {\
        std::cout << "**** begin test \"" #name "\"" << std::endl;

#define bbb_test_end_definition(name)\
        std::cout\
            << "**** finish test \"" #name "\"" << std::endl\
            << "result: " << std::endl\
            << "  success: " << success << " / " << total << std::endl\
            << "  failure: " << failure << " / " << total << std::endl;\
        if(success == total) std::cout << "    * * * * ALL PASSED!!!! * * * *" << std::endl;\
        std::cout << std::endl;\
   }\
};

#define bbb_test_begin_prepare(name)\
namespace name##_test {

#define bbb_test_end_prepare(name) };

#define bbb_test(name) name##_test::test();

#define bbb_assert(...) {\
    total++;\
    if(__VA_ARGS__) {\
        success++;\
        std::cout << "passed   [" << success << "/" << total << "] " << #__VA_ARGS__ << std::endl;\
    } else {\
        failure++;\
        std::cout << "failure. [" << failure << "/" << total << "] " #__VA_ARGS__ << std::endl;\
    }\
}<commit_msg>update macro<commit_after>/* **** **** **** **** **** **** **** **** *
 *
 *         _/        _/        _/
 *        _/_/_/    _/_/_/    _/_/_/
 *       _/    _/  _/    _/  _/    _/
 *      _/    _/  _/    _/  _/    _/
 *     _/_/_/    _/_/_/    _/_/_/
 *
 * bit by bit
 * simple_test.hpp
 *
 * author: ISHII 2bit
 * mail:   2bit@backspace.tokyo
 *
 * **** **** **** **** **** **** **** **** */

#pragma once
#include <cassert>
#include <iostream>

#define bbb_test_declaretion(name)\
namespace name##_test { void test(); };

#define bbb_test_begin_definition(name)\
namespace name##_test {\
    std::size_t total{0};\
    std::size_t success{0};\
    std::size_t failure{0};\
    void test() {\
        std::cout << "**** begin test \"" #name "\"" << std::endl;

#define bbb_test_end_definition(name)\
        std::cout\
            << "**** finish test \"" #name "\"" << std::endl\
            << "result: " << std::endl\
            << "  success: " << success << " / " << total << std::endl\
            << "  failure: " << failure << " / " << total << std::endl;\
        if(success == total) std::cout << "**** ALL PASSED ****" << std::endl;\
        else                 std::cout << "!!!! DON'T PASSED " << failure << " TEST(S) !!!!" << std::endl;\
        std::cout << std::endl;\
   }\
};

#define bbb_test_begin_prepare(name)\
namespace name##_test {

#define bbb_test_end_prepare(name) };

#define bbb_test(name) name##_test::test();

#define bbb_assert(...) {\
    total++;\
    if(__VA_ARGS__) {\
        success++;\
        std::cout << "passed   [" << success << "/" << total << "] " << #__VA_ARGS__ << std::endl;\
    } else {\
        failure++;\
        std::cout << "failure. [" << failure << "/" << total << "] " #__VA_ARGS__ << std::endl;\
    }\
}<|endoftext|>


<commit_before>#ifndef sw_x0_io_filter_hpp
#define sw_x0_io_filter_hpp 1

#include <x0/buffer.hpp>
#include <x0/io/source.hpp>
#include <x0/io/sink.hpp>

namespace x0 {

/** unidirectional data processor.
 *
 * a filter is a processor, that reads from a source, and passes
 * the received data to the sink. this data may or may not be
 * transformed befor passing to the sink.
 *
 * \see source, sink
 */
class filter
{
public:
	virtual buffer process(const buffer::view& input) = 0;

	buffer operator()(const buffer::view& input)
	{
		return process(input);
	}

#if 0
	//! pumps source through this filter, storing the result into the sink
	void once(source& source, sink& sink)
	{
		buffer b;
		if (buffer::view chunk = source.pull(b))
			sink(process(chunk));
	}

	void all(source& source, sink& sink)
	{
		buffer b;
		while (buffer::view c = source.pull(b))
			sink.push(process(c));
	}
#endif
};

class chained_filter :
	public filter
{
private:
	filter left_;
	filter right_;

public:
	chained_filter(const filter& left, const filter& right) :
		left_(left), right_(right)
	{
	}
#if 0
	virtual buffer process(const buffer& data)
	{
		return right_(left_(data));
	}
#endif
};

chained_filter chain(const source&, const filter&);
chained_filter chain(const filter&, const filter&);
chained_filter chain(const filter&, const sink&);

} // namespace x0

#endif
<commit_msg>filter: implemented filter.once(source, sink) and filter.all(source, sink), and added some more docs<commit_after>#ifndef sw_x0_io_filter_hpp
#define sw_x0_io_filter_hpp 1

#include <x0/buffer.hpp>
#include <x0/io/source.hpp>
#include <x0/io/sink.hpp>
#include <memory>

namespace x0 {

/** unidirectional data processor.
 *
 * a filter is a processor, that reads from a source, and passes
 * the received data to the sink. this data may or may not be
 * transformed befor passing to the sink.
 *
 * \see source, sink
 */
class filter
{
public:
	/** processes given input data through this filter. */
	virtual buffer process(const buffer::view& input) = 0;

public:
	buffer operator()(const buffer::view& input)
	{
		return process(input);
	}

	/** pumps a single source chunk through this filter, storing the result into the sink.
	 *
	 * \param src the source to pull from.
	 * \param snk the sink to push the filtered data into.
	 *
	 * Think of a data-flow directed graph like: source -> filter -> sink.
	 */
	virtual bool once(source& src, sink& snk)
	{
		buffer sb;
		buffer::view chunk = src.pull(sb);
		if (!chunk)
			return false;

		buffer pb = process(chunk);
		if (chunk = snk.push(pb))
			while (chunk = snk.push(chunk))
				;

		return true;
	}

	/** pumps whole source through this filter into given sink.
	 *
	 * \param src the source to pull from.
	 * \param snk the sink to push the filtered data into.
	 *
	 * Think of a data-flow directed graph like: source -> filter -> sink.
	 */
	virtual void all(source& src, sink& snk)
	{
		buffer result;
		while (buffer::view chunk = src.pull(result))
		{
			buffer p(process(chunk));

			if (buffer::view v = snk.push(p))
				while (v = snk.push(v))
					;

			p = process(buffer::view());
			if (!p.empty())
			//if (p = process(buffer::view()))
				if (buffer::view v = snk.push(p))
					while (v = snk.push(v))
						;

//			result.clear();
		}
	}
};

typedef std::shared_ptr<filter> filter_ptr;

} // namespace x0

#endif
<|endoftext|>


<commit_before>#include "common.th"

#define elem(Dest, Base, Index) \
    Dest <- E * Index         ; \
    Dest <- Dest + Base       ; \
    //

#define swap(i0, i1)         \
    pushall(c,d,e,f,g)       \
    f <- i0                  \
    g <- i1                  \
    call(do_swap)            \
    popall(c,d,e,f,g)        \
    //

do_swap:
    l <- c
    k <- e
    o <- o - e
    c <- o + 1
    elem(d,l,f)
    /* E is already width */
    call(memcpy)
    elem(c,l,f)
    elem(d,l,g)
    e <- k
    call(memcpy)
    elem(c,l,g)
    d <- o + 1
    e <- k
    call(memcpy)
    o <- o + k
    ret

// c <- base
// d <- number of elements
// e <- size of element
// f <- comparator
#define SI M
#define II J
#define PI H
#define LI I
#define BASE C

    .global qsort
qsort:
    pushall(h,i,j,k,l,m)
    h <- d < 2                  // test for base case
    jnzrel(h,L_qsort_done)
    PI <- d >> 1                // partition index
    LI <- d - 1                 // last index
    // partitioning
    swap(h,i)
    SI <- 0                     // store index
    II <- 0                     // i index

L_qsort_partition:
    k <- II < LI
    jzrel(k,L_qsort_partition_done)
    pushall(c)
    elem(k, BASE, II)
    elem(d, BASE, LI)
    c <- k
    callr(f)                    // call comparator
    popall(c)
    k <- b < 0
    jzrel(k,L_qsort_noswap)
    swap(II,SI)
    SI <- SI + 1
L_qsort_noswap:
    II <- II + 1
    goto(L_qsort_partition)

L_qsort_partition_done:
    swap(SI,LI)
    PI <- SI

    // recursive cases --------------------
    // save argument registers
    m <- c
    j <- e
    k <- f
    // C is already elem(c,c,0)
    d <- PI
    // E is already width
    // F is already callback
    call(qsort)
    // restore argument registers
    c <- m
    d <- LI - PI
    e <- j
    f <- k
    PI <- PI + 1
    elem(k,BASE,PI)
    c <- k
    // TODO tail call
    call(qsort)

L_qsort_done:
    popall(h,i,j,k,l,m)
    ret

<commit_msg>Increasing competitiveness of tenyr quicksort<commit_after>#include "common.th"

#define elem(Dest, Base, Index) \
    Dest <- E * Index         ; \
    Dest <- Dest + Base       ; \
    //

#define swap(i0, i1)         \
    pushall(c,d,e,f)         \
    f <- i0                  \
    g <- i1                  \
    call(do_swap)            \
    popall(c,d,e,f)          \
    //

do_swap:
    l <- c
    k <- e
    o <- o - e
    c <- o + 1
    elem(d,l,f)
    /* E is already width */
    call(memcpy)
    elem(c,l,f)
    elem(d,l,g)
    e <- k
    call(memcpy)
    elem(c,l,g)
    d <- o + 1
    e <- k
    call(memcpy)
    o <- o + k
    ret

// c <- base
// d <- number of elements
// e <- size of element
// f <- comparator
#define SI M
#define II J
#define PI H
#define LI I
#define BASE C

    .global qsort
qsort:
    pushall(g,h,i,j,k,l,m)
    h <- d < 2                  // test for base case
    jnzrel(h,L_qsort_done)
    PI <- d >> 1                // partition index
    LI <- d - 1                 // last index
    // partitioning
    swap(h,i)
    SI <- 0                     // store index
    II <- 0                     // i index

L_qsort_partition:
    k <- II < LI
    jzrel(k,L_qsort_partition_done)
    pushall(c)
    elem(k, BASE, II)
    elem(d, BASE, LI)
    c <- k
    callr(f)                    // call comparator
    popall(c)
    k <- b < 0
    jzrel(k,L_qsort_noswap)
    swap(II,SI)
    SI <- SI + 1
L_qsort_noswap:
    II <- II + 1
    goto(L_qsort_partition)

L_qsort_partition_done:
    swap(SI,LI)
    PI <- SI

    // recursive cases --------------------
    // save argument registers
    m <- c
    j <- e
    k <- f
    // C is already elem(c,c,0)
    d <- PI
    // E is already width
    // F is already callback
    call(qsort)
    // restore argument registers
    c <- m
    d <- LI - PI
    e <- j
    f <- k
    PI <- PI + 1
    elem(k,BASE,PI)
    c <- k
    // TODO tail call
    call(qsort)

L_qsort_done:
    popall(g,h,i,j,k,l,m)
    ret

<|endoftext|>


<commit_before>/**
 * This file is part of Slideshow.
 * Copyright (C) 2008-2010 David Sveningsson <ext@sidvind.com>
 *
 * Slideshow 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, either version 3 of the
 * License, or (at your option) any later version.
 *
 * Slideshow 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Slideshow.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifdef HAVE_CONFIG_H
#	include "config.h"
#endif

#include "state/video.hpp"
#include "state/switch.hpp"
#include "core/asprintf.h"
#include "core/log.hpp"
#include <cstdlib>
#include <cstring>
#include <cerrno>
#include <cassert>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/wait.h>

static char read_buffer[1024];
static int child_pid = -1;
static int child_stdin = -1;
static int child_stdout = -1;

static const char* read2(int fd){
	while ( read(fd, read_buffer, 1024) > 0 ){
		if ( strncmp(read_buffer, "ANS_", 4) == 0 ){
			return read_buffer;
		}

		Log::verbose("mplayer: %s", read_buffer);
	}

	switch ( errno ){
		case EWOULDBLOCK:
			break;
		default:
			Log::warning("mplayer: read failed with code %d\n", errno);
	}

	return NULL;
}

void VideoState::command(const char* fmt, ...){
	assert(child_pid > 0);

	va_list ap;
	va_start(ap, fmt);
	char* tmp = vasprintf2(fmt, ap);
	write(child_stdin, tmp, strlen(tmp));
	free(tmp);
	va_end(ap);
}

VideoState::VideoState(State* state, const char* filename)
	: State(state)
	, _filename(strdup(filename)){

	if ( child_pid <= 0 ){
		Log::warning("no mplayer: process, cannot play video\n");
		return;
	}

	command("loadfile %s/src/video\n", filename);
}

VideoState::~VideoState(){
	free(_filename);
	_filename = NULL;
}

State* VideoState::action(bool &flip){
	if ( child_pid <= 0 ){
		return new SwitchState(this);
	}

	command("get_property filename\n");

	while ( true ){
		const char* ans = read2(child_stdout);

		if ( !ans ){
			continue;
		}

		if ( strcmp(ans, "ANS_ERROR=PROPERTY_UNAVAILABLE\n") == 0){
			return new SwitchState(this);
		}

		return this;
	}
}

int VideoState::init(){
	/* Fork and get child std{in,out} */
	/* http://lists.mplayerhq.hu/pipermail/mplayer-dev-eng/2007-August/053602.html */

	int pipefds1[2];
	int pipefds2[2];

	pipe2(pipefds1, O_NONBLOCK);
	pipe2(pipefds2, O_NONBLOCK);

	child_pid = fork();

	close(pipefds1[!!child_pid]);
	close(pipefds2[!child_pid]);

	if (!child_pid) { /* in child process */
		dup2(pipefds1[1], STDOUT_FILENO);
		dup2(pipefds2[0], STDIN_FILENO);
		setenv("TERM", "xterm", 0);
		return execlp("mplayer", "slideshow-mplayer", "-slave", "-idle", "-quiet", "-msglevel", "all=-1:global=4", "-fs", NULL);
	} else { /* in parent process */
		child_stdin  = pipefds2[1];
		child_stdout = pipefds1[0];
		return 0;
	}
}

int VideoState::cleanup(){
	if ( child_pid > 0 ){
		command("quit\n");
	}
	return 0;
}

void VideoState::poll(){
	if ( child_pid < 0 ){
		return;
	}

	int sigstate = kill(child_pid, 0);
	if ( sigstate != 0 ){ /* child process terminated */
		Log::fatal("mplayer process has gone away (for reasons unknown)");
		child_pid = -1;
	}

	int status;
	if ( waitpid(child_pid, &status, WNOHANG) != 0 ){
		if ( WIFEXITED(status) ){
			Log::fatal("mplayer process has gone away (exited with code %d)\n", WEXITSTATUS(status));
		} else {
			Log::fatal("mplayer process has gone away (terminated with signal %d)\n", WTERMSIG(status));
		}
		child_pid = -1;
	}

	read2(child_stdout);
}
<commit_msg>FSM: fixing some warnings<commit_after>/**
 * This file is part of Slideshow.
 * Copyright (C) 2008-2010 David Sveningsson <ext@sidvind.com>
 *
 * Slideshow 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, either version 3 of the
 * License, or (at your option) any later version.
 *
 * Slideshow 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Slideshow.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifdef HAVE_CONFIG_H
#	include "config.h"
#endif

#include "state/video.hpp"
#include "state/switch.hpp"
#include "core/asprintf.h"
#include "core/log.hpp"
#include <cstdlib>
#include <cstring>
#include <cerrno>
#include <cassert>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/wait.h>

static char read_buffer[1024];
static int child_pid = -1;
static int child_stdin = -1;
static int child_stdout = -1;

static const char* read2(int fd){
	while ( read(fd, read_buffer, 1024) > 0 ){
		if ( strncmp(read_buffer, "ANS_", 4) == 0 ){
			return read_buffer;
		}

		Log::verbose("mplayer: %s", read_buffer);
	}

	switch ( errno ){
		case EWOULDBLOCK:
			break;
		default:
			Log::warning("mplayer: read failed with code %d\n", errno);
	}

	return NULL;
}

void VideoState::command(const char* fmt, ...){
	assert(child_pid > 0);

	va_list ap;
	va_start(ap, fmt);
	char* tmp = vasprintf2(fmt, ap);
	if ( write(child_stdin, tmp, strlen(tmp)) == -1 ){
		fprintf(stderr, "write failed: %s\n", strerror(errno));
	}
	free(tmp);
	va_end(ap);
}

VideoState::VideoState(State* state, const char* filename)
	: State(state)
	, _filename(strdup(filename)){

	if ( child_pid <= 0 ){
		Log::warning("no mplayer: process, cannot play video\n");
		return;
	}

	command("loadfile %s/src/video\n", filename);
}

VideoState::~VideoState(){
	free(_filename);
	_filename = NULL;
}

State* VideoState::action(bool &flip){
	if ( child_pid <= 0 ){
		return new SwitchState(this);
	}

	command("get_property filename\n");

	while ( true ){
		const char* ans = read2(child_stdout);

		if ( !ans ){
			continue;
		}

		if ( strcmp(ans, "ANS_ERROR=PROPERTY_UNAVAILABLE\n") == 0){
			return new SwitchState(this);
		}

		return this;
	}
}

int VideoState::init(){
	/* Fork and get child std{in,out} */
	/* http://lists.mplayerhq.hu/pipermail/mplayer-dev-eng/2007-August/053602.html */

	int pipefds1[2];
	int pipefds2[2];

	if ( pipe2(pipefds1, O_NONBLOCK) == -1 ) fprintf(stderr, "pipe2 failed: %s\n", strerror(errno));
	if ( pipe2(pipefds2, O_NONBLOCK) == -1 ) fprintf(stderr, "pipe2 failed: %s\n", strerror(errno));

	child_pid = fork();

	close(pipefds1[!!child_pid]);
	close(pipefds2[!child_pid]);

	if (!child_pid) { /* in child process */
		dup2(pipefds1[1], STDOUT_FILENO);
		dup2(pipefds2[0], STDIN_FILENO);
		setenv("TERM", "xterm", 0);
		return execlp("mplayer", "slideshow-mplayer", "-slave", "-idle", "-quiet", "-msglevel", "all=-1:global=4", "-fs", NULL);
	} else { /* in parent process */
		child_stdin  = pipefds2[1];
		child_stdout = pipefds1[0];
		return 0;
	}
}

int VideoState::cleanup(){
	if ( child_pid > 0 ){
		command("quit\n");
	}
	return 0;
}

void VideoState::poll(){
	if ( child_pid < 0 ){
		return;
	}

	int sigstate = kill(child_pid, 0);
	if ( sigstate != 0 ){ /* child process terminated */
		Log::fatal("mplayer process has gone away (for reasons unknown)");
		child_pid = -1;
	}

	int status;
	if ( waitpid(child_pid, &status, WNOHANG) != 0 ){
		if ( WIFEXITED(status) ){
			Log::fatal("mplayer process has gone away (exited with code %d)\n", WEXITSTATUS(status));
		} else {
			Log::fatal("mplayer process has gone away (terminated with signal %d)\n", WTERMSIG(status));
		}
		child_pid = -1;
	}

	read2(child_stdout);
}
<|endoftext|>


<commit_before>#include <iostream>
#include <string>
#include <assert.h>

#include <cstdlib>
#include <ctime>

#include <log4cxx/logger.h>
#include <log4cxx/xml/domconfigurator.h>
using namespace log4cxx;
using namespace log4cxx::xml;

#include "hdf5.h"
#include "swmr-testdata.h"
#include "swmr-writer.h"

using namespace std;

SWMRWriter::SWMRWriter(const string& fname)
{

    this->log = Logger::getLogger("SWMRWriter");
    LOG4CXX_TRACE(log, "SWMRWriter constructor. Filename: " << fname);
    this->filename = fname;
    this->fid = -1;
}

void SWMRWriter::create_file()
{
    hid_t fapl; /* File access property list */
    hid_t fcpl;

    /* Create file access property list */
    fapl = H5Pcreate(H5P_FILE_ACCESS);
    assert(fapl >= 0);

    /* Set to use the latest library format */
    assert(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) >= 0);

    /* Create file creation property list */
    if ((fcpl = H5Pcreate(H5P_FILE_CREATE)) < 0)
    assert(fcpl >= 0);

    /* Creating the file with SWMR write access*/
    LOG4CXX_INFO(log, "Creating file: " << filename);
    unsigned int flags = H5F_ACC_TRUNC;
    this->fid = H5Fcreate(this->filename.c_str(), flags, fcpl, fapl);
    assert(this->fid >= 0);

    /* Close file access property list */
    assert(H5Pclose(fapl) >= 0);
}

void SWMRWriter::get_test_data()
{
    LOG4CXX_DEBUG(log, "Getting test data from swmr_testdata");
    vector<hsize_t> dims(2);
    dims[0] = swmr_testdata_cols;
    dims[1] = swmr_testdata_rows;
    this->img = Frame(dims, (const uint32_t*)(swmr_testdata[0]));
}

void SWMRWriter::get_test_data(const string& fname, const string& dsetname)
{
    LOG4CXX_DEBUG(log, "Getting test data from file/dset: "
                  << fname << "/" << dsetname);
    this->img = Frame(fname, dsetname);
}

void SWMRWriter::write_test_data(unsigned int niter,
                                 unsigned int nframes_cache,
                                 double period)
{
    hid_t dataspace, dataset;
    hid_t filespace, memspace;
    hid_t prop;
    herr_t status;
    hsize_t chunk_dims[3];
    hsize_t max_dims[3];
    hsize_t img_dims[3];
    hsize_t offset[3] = { 0, 0, 0 };
    hsize_t size[3];

    assert(this->img.dimensions().size() == 2);
    chunk_dims[0] = nframes_cache;
    chunk_dims[1] = this->img.dimensions()[0];
    chunk_dims[2] = this->img.dimensions()[1];

    max_dims[0] = H5S_UNLIMITED;
    max_dims[1] = this->img.dimensions()[0];
    max_dims[2] = this->img.dimensions()[1];

    img_dims[0] = 1;
    img_dims[1] = this->img.dimensions()[0];
    img_dims[2] = this->img.dimensions()[1];
    size[0] = 1;
    size[1] = this->img.dimensions()[0];
    size[2] = this->img.dimensions()[1];

    /* Create the dataspace with the given dimensions - and max dimensions */
    dataspace = H5Screate_simple(3, img_dims, max_dims);
    assert(dataspace >= 0);

    /* Enable chunking  */
    prop = H5Pcreate(H5P_DATASET_CREATE);
    status = H5Pset_chunk(prop, 3, chunk_dims);
    assert(status >= 0);

    /* Create dataset  */
    LOG4CXX_DEBUG(log, "Creating dataset");
    dataset = H5Dcreate2(this->fid, "data", H5T_NATIVE_INT, dataspace,
    H5P_DEFAULT, prop, H5P_DEFAULT);

    /* Enable SWMR writing mode */
    assert(H5Fstart_swmr_write(this->fid) >= 0);
    LOG4CXX_INFO(log, "##### SWMR mode ######");
    LOG4CXX_INFO(log, "Clients can start reading");

    LOG4CXX_DEBUG(log, "Starting write loop. Iterations: " << niter);
    for (int i = 0; i < niter; i++) {
        /* Extend the dataset  */
        LOG4CXX_TRACE(log, "Extending. Size: " << size[2]
                      << ", " << size[1] << ", " << size[0]);
        status = H5Dset_extent(dataset, size);
        assert(status >= 0);

        /* Select a hyperslab */
        filespace = H5Dget_space(dataset);
        status = H5Sselect_hyperslab(filespace, H5S_SELECT_SET, offset, NULL,
                                     img_dims, NULL);
        assert(status >= 0);

        /* Write the data to the hyperslab */
        LOG4CXX_DEBUG(log, "Writing. Offset: " << offset[0] << ", "
                      << offset[1] << ", " << offset[2]);
        status = H5Dwrite(dataset, H5T_NATIVE_UINT32, dataspace, filespace,
        H5P_DEFAULT, this->img.pdata());
        assert(status >= 0);

        /* Increment offsets and dimensions as appropriate */
        offset[0]++;
        size[0]++;

        LOG4CXX_TRACE(log, "Flushing");
        assert(H5Dflush(dataset) >= 0);

        if (period > 0.0) {
            LOG4CXX_TRACE(log, "Sleeping " << period << "s");
            usleep((unsigned int) (period * 1000000));
        }
    }

    LOG4CXX_DEBUG(log, "Closing intermediate open HDF objects");
    H5Dclose(dataset);
    H5Pclose(prop);
    H5Sclose(dataspace);
    H5Sclose(filespace);
}

SWMRWriter::~SWMRWriter()
{
    LOG4CXX_TRACE(log, "SWMRWriter destructor");
    if (this->fid >= 0) {
        assert(H5Fclose(this->fid) >= 0);
        this->fid = -1;
    }
}

<commit_msg>Proper timing on writer loop<commit_after>#include <iostream>
#include <string>
#include <assert.h>

#include <cstdlib>

#include <log4cxx/logger.h>
#include <log4cxx/xml/domconfigurator.h>
using namespace log4cxx;
using namespace log4cxx::xml;

#include "hdf5.h"
#include "swmr-testdata.h"
#include "timestamp.h"
#include "swmr-writer.h"

using namespace std;

SWMRWriter::SWMRWriter(const string& fname)
{

    this->log = Logger::getLogger("SWMRWriter");
    LOG4CXX_TRACE(log, "SWMRWriter constructor. Filename: " << fname);
    this->filename = fname;
    this->fid = -1;
}

void SWMRWriter::create_file()
{
    hid_t fapl; /* File access property list */
    hid_t fcpl;

    /* Create file access property list */
    fapl = H5Pcreate(H5P_FILE_ACCESS);
    assert(fapl >= 0);

    /* Set to use the latest library format */
    assert(H5Pset_libver_bounds(fapl, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) >= 0);

    /* Create file creation property list */
    if ((fcpl = H5Pcreate(H5P_FILE_CREATE)) < 0)
    assert(fcpl >= 0);

    /* Creating the file with SWMR write access*/
    LOG4CXX_INFO(log, "Creating file: " << filename);
    unsigned int flags = H5F_ACC_TRUNC;
    this->fid = H5Fcreate(this->filename.c_str(), flags, fcpl, fapl);
    assert(this->fid >= 0);

    /* Close file access property list */
    assert(H5Pclose(fapl) >= 0);
}

void SWMRWriter::get_test_data()
{
    LOG4CXX_DEBUG(log, "Getting test data from swmr_testdata");
    vector<hsize_t> dims(2);
    dims[0] = swmr_testdata_cols;
    dims[1] = swmr_testdata_rows;
    this->img = Frame(dims, (const uint32_t*)(swmr_testdata[0]));
}

void SWMRWriter::get_test_data(const string& fname, const string& dsetname)
{
    LOG4CXX_DEBUG(log, "Getting test data from file/dset: "
                  << fname << "/" << dsetname);
    this->img = Frame(fname, dsetname);
}

void SWMRWriter::write_test_data(unsigned int niter,
                                 unsigned int nframes_cache,
                                 double period)
{
    hid_t dataspace, dataset;
    hid_t filespace, memspace;
    hid_t prop;
    herr_t status;
    hsize_t chunk_dims[3];
    hsize_t max_dims[3];
    hsize_t img_dims[3];
    hsize_t offset[3] = { 0, 0, 0 };
    hsize_t size[3];

    assert(this->img.dimensions().size() == 2);
    chunk_dims[0] = nframes_cache;
    chunk_dims[1] = this->img.dimensions()[0];
    chunk_dims[2] = this->img.dimensions()[1];

    max_dims[0] = H5S_UNLIMITED;
    max_dims[1] = this->img.dimensions()[0];
    max_dims[2] = this->img.dimensions()[1];

    img_dims[0] = 1;
    img_dims[1] = this->img.dimensions()[0];
    img_dims[2] = this->img.dimensions()[1];
    size[0] = 1;
    size[1] = this->img.dimensions()[0];
    size[2] = this->img.dimensions()[1];

    /* Create the dataspace with the given dimensions - and max dimensions */
    dataspace = H5Screate_simple(3, img_dims, max_dims);
    assert(dataspace >= 0);

    /* Enable chunking  */
    prop = H5Pcreate(H5P_DATASET_CREATE);
    status = H5Pset_chunk(prop, 3, chunk_dims);
    assert(status >= 0);

    /* Create dataset  */
    LOG4CXX_DEBUG(log, "Creating dataset");
    dataset = H5Dcreate2(this->fid, "data", H5T_NATIVE_INT, dataspace,
    H5P_DEFAULT, prop, H5P_DEFAULT);

    /* Enable SWMR writing mode */
    assert(H5Fstart_swmr_write(this->fid) >= 0);
    LOG4CXX_INFO(log, "##### SWMR mode ######");
    LOG4CXX_INFO(log, "Clients can start reading");

    TimeStamp ts;
    ts.reset();
    LOG4CXX_DEBUG(log, "Starting write loop. Iterations: " << niter);
    for (int i = 0; i < niter; i++) {
        /* Extend the dataset  */
        LOG4CXX_TRACE(log, "Extending. Size: " << size[2]
                      << ", " << size[1] << ", " << size[0]);
        status = H5Dset_extent(dataset, size);
        assert(status >= 0);

        /* Select a hyperslab */
        filespace = H5Dget_space(dataset);
        status = H5Sselect_hyperslab(filespace, H5S_SELECT_SET, offset, NULL,
                                     img_dims, NULL);
        assert(status >= 0);

        /* Write the data to the hyperslab */
        LOG4CXX_DEBUG(log, "Writing. Offset: " << offset[0] << ", "
                      << offset[1] << ", " << offset[2]);
        status = H5Dwrite(dataset, H5T_NATIVE_UINT32, dataspace, filespace,
        H5P_DEFAULT, this->img.pdata());
        assert(status >= 0);

        /* Increment offsets and dimensions as appropriate */
        offset[0]++;
        size[0]++;

        LOG4CXX_TRACE(log, "Flushing");
        assert(H5Dflush(dataset) >= 0);

        if (period > 0.0) {
            double sleeptime = period - ts.seconds_until_now();
            if (sleeptime > 0.0) {
                LOG4CXX_TRACE(log, "Sleeping " << sleeptime << "sec (period: "
                                   << period << "sec" );
                usleep((unsigned int) (sleeptime * 1000000));
            }
            ts.reset();
        }
    }

    LOG4CXX_DEBUG(log, "Closing intermediate open HDF objects");
    H5Dclose(dataset);
    H5Pclose(prop);
    H5Sclose(dataspace);
    H5Sclose(filespace);
}

SWMRWriter::~SWMRWriter()
{
    LOG4CXX_TRACE(log, "SWMRWriter destructor");
    if (this->fid >= 0) {
        assert(H5Fclose(this->fid) >= 0);
        this->fid = -1;
    }
}

<|endoftext|>


<commit_before>
#include "Session.hpp"
#include "../Mirror.hpp"
#include <onions-common/Common.hpp>
#include <onions-common/containers/Cache.hpp>
#include <onions-common/Log.hpp>
#include <onions-common/Utils.hpp>
#include <onions-common/tcp/MemAllocator.hpp>
#include <botan/sha2_64.h>
#include <botan/base64.h>
#include <boost/bind.hpp>

template <typename Handler>
inline MemAllocator<Handler> makeHandler(HandleAlloc& a, Handler h)
{
  return MemAllocator<Handler>(a, h);
}

Session::Session(const SocketPtr& socket, int id) : socket_(socket), id_(id)
{
}

Session::~Session()
{
  Log::get().notice("Session " + std::to_string(id_) + " deallocating.");
}

int Session::getID() const
{
  return id_;
}

void Session::asyncRead()
{
  socket_->async_read_some(
      boost::asio::buffer(inChunk_),
      makeHandler(allocator_,
                  boost::bind(&Session::processRead, shared_from_this(),
                              boost::asio::placeholders::error,
                              boost::asio::placeholders::bytes_transferred)));
}

void Session::asyncWrite(const std::string& type, const std::string& val)
{
  Json::Value out;
  out["type"] = type;
  out["value"] = val;
  asyncWrite(out);
}

void Session::asyncWrite(const Json::Value& val)
{
  Json::FastWriter writer;
  asyncWrite(writer.write(val));
}

// ***************************** PRIVATE METHODS *****************************

Json::Value Session::respond(const std::string& inputStr)
{
  Json::Value in, out;
  Json::Reader reader;
  out["type"] = "error";
  out["value"] = "";

  if (!reader.parse(inputStr, in))
    out["value"] = "Failed to parse message!";
  else if (!in.isMember("type"))
    out["value"] = "Message is missing the \"type\" field!";
  else if (!in.isMember("value"))
    out["value"] = "Message is missing the \"value\" field!";
  else
  {
    std::string type(in["type"].asString());
    Log::get().notice(std::to_string(id_) + ": Received " + type);

    if (type == "putRecord")
      out = respondToPutRecord(in);
    else if (type == "domainQuery")
      out = respondToDomainQuery(in);
    else if (type == "getMerkleSubtree")
      out = respondToGetMerkleSubtree(in);
    else if (type == "getRootSignature")
      out = respondToGetRootSignature(in);
    else if (type == "waitForRecord")
    {
      Mirror::get().subscribeForRecords(boost::shared_ptr<Session>(this));
      return nullptr;
    }
    else if (type == "success")
    {  // no need to reply
      Log::get().notice(std::to_string(id_) + ": Response: \"" +
                        in["value"].asString() + "\"");
      return nullptr;
    }
    else
    {
      out["type"] = "error";
      out["value"] = "Unknown type \"" + type + "\"";
    }
  }

  if (out["type"].asString() == "error")
  {
    Log::get().warn(std::to_string(id_) + ":     \"" + inputStr + "\"");
    Log::get().warn(std::to_string(id_) + ": " + out["value"].asString());
  }

  out["signature"] = Mirror::get().signTransmission(out);
  return out;
}

Json::Value Session::respondToPutRecord(const Json::Value& in) const
{
  Json::Value response;
  auto r = Common::parseRecord(in["value"].asString());
  Log::get().notice(std::to_string(id_) + ": received a Record for \"" +
                    r->getName() + "\"");

  if (Mirror::get().processNewRecord(id_, r))
  {
    Log::get().notice(std::to_string(id_) + ": Record successfully processed.");
    response["type"] = "success";
    response["value"] = "";
  }
  else
  {
    response["type"] = "error";
    response["value"] = "Name already taken.";
  }

  return response;
}

Json::Value Session::respondToDomainQuery(const Json::Value& in) const
{
  Json::Value response;
  std::string domain = in["value"].asString();
  if (Utils::strEndsWith(domain, ".tor"))
  {  // resolve .tor -> .onion

    auto record = Cache::get(domain);
    if (record)
    {
      response["type"] = "success";
      response["value"] = record->asJSON();
      Log::get().notice(std::to_string(id_) + ": Found Record for \"" + domain +
                        "\"");
    }
    else
    {
      response["type"] = "error";
      response["value"] = "404";
      Log::get().notice(std::to_string(id_) + ": 404ed request for \"" +
                        domain + "\"");
    }
  }
  else
  {
    response["type"] = "error";
    response["value"] = "Invalid request.";
  }

  return response;
}

Json::Value Session::respondToGetMerkleSubtree(const Json::Value& in) const
{
  Json::Value response;

  std::string domain = in["value"].asString();
  if (Utils::strEndsWith(domain, ".tor"))
  {
    response["type"] = "success";
    response["value"] = Mirror::get().getMerkleTree()->getPathTo(domain);
    Log::get().notice(std::to_string(id_) + ": Assembled subtree for \"" +
                      domain + "\"");
  }
  else
  {
    response["type"] = "error";
    response["value"] = "Invalid request.";
    Log::get().notice(std::to_string(id_) + ": Failed subtree request for \"" +
                      domain + "\"");
  }

  return response;
}

Json::Value Session::respondToGetRootSignature(const Json::Value& in) const
{
  return Mirror::get().getRootSignature();

  /*
  Log::get().notice(std::to_string(id_) + " received Merkle tree signature.");

  Json::Value response;
  response["type"] = "success";
  response["value"] = "";

  int status = Mirror::get().verifyRootSignature(in["value"].asString());
  if (status == 0)
    Log::get().notice(std::to_string(id_) + " good Merkle root signature.");
  else if (status == 1)
  {
    response["type"] = "error";
    response["value"] = "Bad Ed25519 signature on Merkle root.";
  }
  else if (status == -1)
  {
    response["type"] = "error";
    response["value"] = "General Ed25519 failure on Merkle root.";
  }

  return response;*/
}

// called by Asio whenever the socket has been read into the buffer
void Session::processRead(const boost::system::error_code& error, size_t n)
{
  // check error
  if (n == 0)
  {
    Log::get().notice(std::to_string(id_) + ": Connection closed.");
    return;
  }
  else if (error)
  {
    Log::get().warn(std::to_string(id_) + ": " + error.message());
    return;
  }

  // add to buffer
  inBuffer_.append(std::string(inChunk_.begin(), inChunk_.begin() + n));

  // check if we have the whole message (#64)
  std::size_t delimiter = inBuffer_.find_first_of('\n');
  if (delimiter == std::string::npos)
  {  // the whole message isn't here yet, so read more asynchronously
    asyncRead();
  }
  else
  {
    // cut out word
    std::string input = inBuffer_.substr(0, delimiter);
    inBuffer_.erase(0, delimiter);

    // process message
    auto response = respond(input);
    if (response == nullptr)
      asyncRead();  // no reply need, so read again
    else
      asyncWrite(response);
  }
}

// called by Asio when the buffer has been written to the socket
// we pass the buffer argument so that this is the end of its lifetime
void Session::processWrite(const boost::system::error_code& error,
                           const std::shared_ptr<Buffer>& buffer)
{
  if (error)
  {
    Log::get().warn(std::to_string(id_) + ": " + error.message());
    return;
  }

  asyncRead();
}

void Session::asyncWrite(const std::string& str)
{
  auto writeBuffer = std::make_shared<Buffer>();
  std::copy(str.begin(), str.end(), writeBuffer->data());

  boost::asio::async_write(
      *socket_, boost::asio::buffer(*writeBuffer, str.size()),
      makeHandler(allocator_,
                  boost::bind(&Session::processWrite, shared_from_this(),
                              boost::asio::placeholders::error, writeBuffer)));
}
<commit_msg>Fix #78<commit_after>
#include "Session.hpp"
#include "../Mirror.hpp"
#include <onions-common/Common.hpp>
#include <onions-common/containers/Cache.hpp>
#include <onions-common/Log.hpp>
#include <onions-common/Utils.hpp>
#include <onions-common/tcp/MemAllocator.hpp>
#include <botan/sha2_64.h>
#include <botan/base64.h>
#include <boost/bind.hpp>

template <typename Handler>
inline MemAllocator<Handler> makeHandler(HandleAlloc& a, Handler h)
{
  return MemAllocator<Handler>(a, h);
}

Session::Session(const SocketPtr& socket, int id) : socket_(socket), id_(id)
{
}

Session::~Session()
{
  Log::get().notice("Session " + std::to_string(id_) + " deallocating.");
}

int Session::getID() const
{
  return id_;
}

void Session::asyncRead()
{
  socket_->async_read_some(
      boost::asio::buffer(inChunk_),
      makeHandler(allocator_,
                  boost::bind(&Session::processRead, shared_from_this(),
                              boost::asio::placeholders::error,
                              boost::asio::placeholders::bytes_transferred)));
}

void Session::asyncWrite(const std::string& type, const std::string& val)
{
  Json::Value out;
  out["type"] = type;
  out["value"] = val;
  asyncWrite(out);
}

void Session::asyncWrite(const Json::Value& val)
{
  Json::FastWriter writer;
  asyncWrite(writer.write(val));
}

// ***************************** PRIVATE METHODS *****************************

Json::Value Session::respond(const std::string& inputStr)
{
  Json::Value in, out;
  Json::Reader reader;
  out["type"] = "error";
  out["value"] = "";

  if (!reader.parse(inputStr, in))
    out["value"] = "Failed to parse message!";
  else if (!in.isMember("type"))
    out["value"] = "Message is missing the \"type\" field!";
  else if (!in.isMember("value"))
    out["value"] = "Message is missing the \"value\" field!";
  else
  {
    std::string type(in["type"].asString());
    Log::get().notice(std::to_string(id_) + ": Received " + type);

    if (type == "putRecord")
      out = respondToPutRecord(in);
    else if (type == "domainQuery")
      out = respondToDomainQuery(in);
    else if (type == "getMerkleSubtree")
      out = respondToGetMerkleSubtree(in);
    else if (type == "getRootSignature")
      out = respondToGetRootSignature(in);
    else if (type == "waitForRecord")
    {
      Mirror::get().subscribeForRecords(boost::shared_ptr<Session>(this));
      return nullptr;
    }
    else if (type == "SYN")
    {
      out["type"] = "success";
      out["value"] = "ACK";
    }
    else if (type == "success")
    {  // no need to reply
      Log::get().notice(std::to_string(id_) + ": Response: \"" +
                        in["value"].asString() + "\"");
      return nullptr;
    }
    else
    { // todo: #78 might still be here
      out["type"] = "error";
      out["value"] = "Unknown type \"" + type + "\"";
    }
  }

  if (out["type"].asString() == "error")
  {
    Log::get().warn(std::to_string(id_) + ":     \"" + inputStr + "\"");
    Log::get().warn(std::to_string(id_) + ": " + out["value"].asString());
  }

  out["signature"] = Mirror::get().signTransmission(out);
  return out;
}

Json::Value Session::respondToPutRecord(const Json::Value& in) const
{
  Json::Value response;
  auto r = Common::parseRecord(in["value"].asString());
  Log::get().notice(std::to_string(id_) + ": received a Record for \"" +
                    r->getName() + "\"");

  if (Mirror::get().processNewRecord(id_, r))
  {
    Log::get().notice(std::to_string(id_) + ": Record successfully processed.");
    response["type"] = "success";
    response["value"] = "";
  }
  else
  {
    response["type"] = "error";
    response["value"] = "Name already taken.";
  }

  return response;
}

Json::Value Session::respondToDomainQuery(const Json::Value& in) const
{
  Json::Value response;
  std::string domain = in["value"].asString();
  if (Utils::strEndsWith(domain, ".tor"))
  {  // resolve .tor -> .onion

    auto record = Cache::get(domain);
    if (record)
    {
      response["type"] = "success";
      response["value"] = record->asJSON();
      Log::get().notice(std::to_string(id_) + ": Found Record for \"" + domain +
                        "\"");
    }
    else
    {
      response["type"] = "error";
      response["value"] = "404";
      Log::get().notice(std::to_string(id_) + ": 404ed request for \"" +
                        domain + "\"");
    }
  }
  else
  {
    response["type"] = "error";
    response["value"] = "Invalid request.";
  }

  return response;
}

Json::Value Session::respondToGetMerkleSubtree(const Json::Value& in) const
{
  Json::Value response;

  std::string domain = in["value"].asString();
  if (Utils::strEndsWith(domain, ".tor"))
  {
    response["type"] = "success";
    response["value"] = Mirror::get().getMerkleTree()->getPathTo(domain);
    Log::get().notice(std::to_string(id_) + ": Assembled subtree for \"" +
                      domain + "\"");
  }
  else
  {
    response["type"] = "error";
    response["value"] = "Invalid request.";
    Log::get().notice(std::to_string(id_) + ": Failed subtree request for \"" +
                      domain + "\"");
  }

  return response;
}

Json::Value Session::respondToGetRootSignature(const Json::Value& in) const
{
  return Mirror::get().getRootSignature();

  /*
  Log::get().notice(std::to_string(id_) + " received Merkle tree signature.");

  Json::Value response;
  response["type"] = "success";
  response["value"] = "";

  int status = Mirror::get().verifyRootSignature(in["value"].asString());
  if (status == 0)
    Log::get().notice(std::to_string(id_) + " good Merkle root signature.");
  else if (status == 1)
  {
    response["type"] = "error";
    response["value"] = "Bad Ed25519 signature on Merkle root.";
  }
  else if (status == -1)
  {
    response["type"] = "error";
    response["value"] = "General Ed25519 failure on Merkle root.";
  }

  return response;*/
}

// called by Asio whenever the socket has been read into the buffer
void Session::processRead(const boost::system::error_code& error, size_t n)
{
  // check error
  if (n == 0)
  {
    Log::get().notice(std::to_string(id_) + ": Connection closed.");
    return;
  }
  else if (error)
  {
    Log::get().warn(std::to_string(id_) + ": " + error.message());
    return;
  }

  // add to buffer
  inBuffer_.append(std::string(inChunk_.begin(), inChunk_.begin() + n));

  // check if we have the whole message (#64)
  std::size_t delimiter = inBuffer_.find_first_of('\n');
  if (delimiter == std::string::npos)
  {  // the whole message isn't here yet, so read more asynchronously
    asyncRead();
  }
  else
  {
    // cut out word
    std::string input = inBuffer_.substr(0, delimiter);
    inBuffer_.erase(0, delimiter);

    // process message
    auto response = respond(input);
    if (response == nullptr)
      asyncRead();  // no reply need, so read again
    else
      asyncWrite(response);
  }
}

// called by Asio when the buffer has been written to the socket
// we pass the buffer argument so that this is the end of its lifetime
void Session::processWrite(const boost::system::error_code& error,
                           const std::shared_ptr<Buffer>& buffer)
{
  if (error)
  {
    Log::get().warn(std::to_string(id_) + ": " + error.message());
    return;
  }

  asyncRead();
}

void Session::asyncWrite(const std::string& str)
{
  auto writeBuffer = std::make_shared<Buffer>();
  std::copy(str.begin(), str.end(), writeBuffer->data());

  boost::asio::async_write(
      *socket_, boost::asio::buffer(*writeBuffer, str.size()),
      makeHandler(allocator_,
                  boost::bind(&Session::processWrite, shared_from_this(),
                              boost::asio::placeholders::error, writeBuffer)));
}
<|endoftext|>


<commit_before>#ifndef TOML_VALUES
#define TOML_VALUES
#include <vector>
#include <string>
#include <memory>
#include <map>

namespace toml
{

template<typename T>
struct typed_value : public value_base
{
    T value;
    typed_value() = default;
    typed_value(const T& t): value(t){}
    ~typed_value() noexcept override = default;
};

struct array_type : public value_base
{
    std::vector<std::shared_ptr<value_base>> value;
    ~array_type() noexcept override = default;
};

struct table_type : public value_base
{
    std::map<std::string, std::shared_ptr<value_base>> value;
    ~table_type() noexcept override = default;
};

}//toml

#endif /* TOML_VALUES */
<commit_msg>remove definition of needless ctor<commit_after>#ifndef TOML_VALUES
#define TOML_VALUES
#include <vector>
#include <string>
#include <memory>
#include <map>

namespace toml
{

template<typename T>
struct typed_value : public value_base
{
    T value;
    typed_value() = default;
    ~typed_value() noexcept override = default;
};

struct array_type : public value_base
{
    std::vector<std::shared_ptr<value_base>> value;
    ~array_type() noexcept override = default;
};

struct table_type : public value_base
{
    std::map<std::string, std::shared_ptr<value_base>> value;
    ~table_type() noexcept override = default;
};

}//toml

#endif /* TOML_VALUES */
<|endoftext|>


<commit_before>#include <dlfcn.h>

#include "loader.h"

using BVS::Loader;
using BVS::ModuleDataMap;
using BVS::ModuleVector;
using BVS::LibHandle;

ModuleDataMap Loader::modules;

ModuleVector* Loader::hotSwapGraveYard = nullptr;

Loader::Loader(const Info& info, std::function<void()> errorHandler)
	: logger{"Loader"},
	info(info),
	errorHandler(errorHandler)
{ }

void Loader::registerModule(const std::string& id, Module* module, bool hotSwap)
{
	if (hotSwap)
	{
#ifdef BVS_MODULE_HOTSWAP
		// NOTE: hotSwapGraveYard will only be initialized when the HotSwap
		// functionality is used. It is intended as a store for unneeded
		// shared_ptr until the process execution ends, but since it is a
		// static pointer it will never be explicitly deleted.
		if (hotSwapGraveYard==nullptr) hotSwapGraveYard = new ModuleVector();
		hotSwapGraveYard->push_back(std::shared_ptr<Module>(module));
		modules[id]->module.swap(hotSwapGraveYard->back());
#endif //BVS_MODULE_HOTSWAP
	}
	else
	{
		modules[id] = std::shared_ptr<ModuleData>{new ModuleData{id, {}, {}, {},
			module, nullptr, {}, ControlFlag::WAIT, Status::OK, {}}};
	}
}

Loader& Loader::load(const std::string& id, const std::string& library, const std::string& configuration, const std::string& options, const std::string& poolName)
{
	if (modules.find(id)!=modules.end())
	{
		LOG(0, "Duplicate id for module: " << id << std::endl << "If you try to load a module more than once, use unique ids and the id(library).options syntax!");
		errorHandler();
	}

	LibHandle dlib = loadLibrary(id, library);

	// execute bvsRegisterModule in loaded lib
	typedef void (*bvsRegisterModule_t)(ModuleInfo moduleInfo, const Info& info);
	bvsRegisterModule_t bvsRegisterModule;
	*reinterpret_cast<void**>(&bvsRegisterModule)=dlsym(dlib, "bvsRegisterModule");

	char* dlerr = dlerror();
	if (dlerr)
	{
		LOG(0, "Loading function bvsRegisterModule() in '" << library << "' resulted in: " << dlerr);
		errorHandler();
	}

	ModuleInfo moduleInfo{id, configuration};
	bvsRegisterModule(moduleInfo, info);

	modules[id]->configuration = configuration;
	modules[id]->dlib = dlib;
	modules[id]->library = library;
	modules[id]->options = options;

	// get connectors
	modules[id]->connectors = std::move(ConnectorDataCollector::connectors);
	modules[id]->poolName = poolName;

	LOG(2, "Loading '" << id << "' successfull!");

	return *this;
}

Loader& Loader::unload(const std::string& id)
{
	disconnectModule(id);
	modules[id]->connectors.clear();
	modules[id]->module.reset();
	unloadLibrary(id);
	modules.erase(id);

	return *this;
}

Loader& Loader::connectAllModules(const bool connectorTypeMatching)
{
	for (auto& it: modules) connectModule(it.second->id, connectorTypeMatching);

	return *this;
}

Loader& Loader::connectModule(const std::string& id, const bool connectorTypeMatching)
{
	ModuleData* module = modules[id].get();
	std::string options = module->options;
	std::string selection;
	std::string input;
	std::string targetModule;
	std::string targetOutput;
	size_t separator;
	size_t separator2;

	while (!options.empty())
	{
		separator = options.find_first_of('(');
		separator2 = options.find_first_of(')');
		selection = options.substr(0, separator2+1);
		if (separator!=std::string::npos)
		{
			input = options.substr(0, separator);
			targetModule = options.substr(separator+1, separator2-separator-1);
		}
		else
		{
			LOG(0, "No input selection found: " << selection);
			errorHandler();
		}

		options.erase(0, separator2+1);
		if (options[0] == '.') options.erase(options.begin());

		separator = targetModule.find_first_of('.');
		if (separator!=std::string::npos)
		{
			targetOutput = targetModule.substr(separator+1, std::string::npos);
			targetModule = targetModule.substr(0, separator);
		}
		else
		{
			LOG(0, "No module output selected: " << module->id << "." << selection);
			errorHandler();
		}

		checkModuleInput(module, input);
		checkModuleOutput(module, targetModule, targetOutput);

		if (connectorTypeMatching && module->connectors[input]->typeIDHash != modules[targetModule]->connectors[targetOutput]->typeIDHash)
		{
			LOG(0, "Selected input and output connector template instantiations are of different type: " << module->id << "." << selection << " -> " << module->connectors[input]->typeIDName << " != " << modules[targetModule]->connectors[targetOutput]->typeIDName);
			errorHandler();
		}

		module->connectors[input]->pointer = modules[targetModule]->connectors[targetOutput]->pointer;
		module->connectors[input]->lock = std::unique_lock<std::mutex>{modules[targetModule]->connectors[targetOutput]->mutex, std::defer_lock};
		LOG(3, "Connected: " << module->id << "." << module->connectors[input]->id << " <- " << modules[targetModule]->id << "." << modules[targetModule]->connectors[targetOutput]->id);
	}

	return *this;
}

Loader& Loader::disconnectModule(const std::string& id)
{
	for (auto& connector: modules[id]->connectors)
	{
		if (connector.second->type==ConnectorType::INPUT) continue;

		for (auto& module: modules)
		{
			for (auto& targetConnector: module.second->connectors)
			{
				if (connector.second->pointer==targetConnector.second->pointer)
				{
					targetConnector.second->active = false;
					targetConnector.second->pointer = nullptr;
				}
			}
		}
	}

	return *this;
}

#ifdef BVS_MODULE_HOTSWAP
Loader& Loader::hotSwapModule(const std::string& id)
{
	unloadLibrary(id);
	LibHandle dlib = loadLibrary(id, modules[id]->library);

	typedef void (*bvsHotSwapModule_t)(const std::string& id, Module* module);
	bvsHotSwapModule_t bvsHotSwapModule;
	*reinterpret_cast<void**>(&bvsHotSwapModule)=dlsym(dlib, "bvsHotSwapModule");

	char* dlerr = dlerror();
	if (dlerr)
	{
		LOG(0, "Loading function bvsHotSwapModule() in '" << modules[id]->library << "' resulted in: " << dlerr);
		errorHandler();
	}

	bvsHotSwapModule(id, modules[id]->module.get());
	modules[id]->dlib = dlib;

	LOG(2, "Hotswapping '" << id << "' successfull!");

	return *this;
}
#endif //BVS_MODULE_HOTSWAP

LibHandle Loader::loadLibrary(const std::string& id, const std::string& library)
{
	std::string modulePath = "lib" + library + ".so";
	LibHandle dlib = dlopen(modulePath.c_str(), RTLD_NOW);
	if (dlib!=NULL)
	{
		LOG(3, "Loading '" << id << "' from '" << modulePath << "'!");
	}
	else
	{
#ifdef BVS_OSX_ANOMALIES
		// additional check for 'dylib' (when compiled under OSX as SHARED instead of MODULE)
		modulePath.resize(modulePath.size()-2);
		modulePath += "dylib";
		dlib = dlopen(modulePath.c_str(), RTLD_NOW);
		if (dlib!=NULL) LOG(3, "Loading " << id << " from " << modulePath << "!");
#endif //BVS_OSX_ANOMALIES
	}

	if (dlib==NULL)
	{
		LOG(0, "Loading '" << modulePath << "' resulted in: " << dlerror());
		errorHandler();
	}

	return dlib;
}

Loader& Loader::unloadLibrary(const std::string& id)
{
#ifndef BVS_OSX_ANOMALIES
	std::string modulePath = "./lib" + modules[id]->library + ".so";
#else
	std::string modulePath = "./lib" + modules[id]->library + ".(so|dylib)";
#endif //BVS_OSX_ANOMALIES
	LOG(3, "Module '" << id << "' unloading from '" << modulePath << "'!");

	void* dlib = modules[id]->dlib;
	if (dlib==nullptr) LOG(0, "Requested module '" << id << "' not found!");

	dlclose(dlib);

	char* dlerr = dlerror();
	if (dlerr)
	{
		LOG(0, "While closing '" << modulePath << "' following error occured: " << dlerror());
		errorHandler();
	}
	LOG(2, "Library '" << modulePath << "' unloaded!");

	return *this;
}

Loader& Loader::checkModuleInput(const ModuleData* module, const std::string& inputName)
{
	auto input = module->connectors.find(inputName);

	if (input == module->connectors.end() || input->second->type != ConnectorType::INPUT)
	{
		LOG(0, "Input not found: " << module->id << "." << inputName);
		printModuleConnectors(module);
		errorHandler();
	}

	if (input->second->active)
	{
		LOG(0, "Input already connected: " << module->id << "." << inputName);
		errorHandler();
	}

	return *this;
}

Loader& Loader::checkModuleOutput(const ModuleData* module, const std::string& targetModule, const std::string& targetOutput)
{
	auto target = modules.find(targetModule);
	if (target == modules.end())
	{
		LOG(0, "Module not found: " << targetModule << " in " << module->id << "." << module->options);
		errorHandler();
	}

	auto output = target->second->connectors.find(targetOutput);
	if (output == target->second->connectors.end() || output->second->type != ConnectorType::OUTPUT)
	{
		LOG(0, "Output not found: " << targetOutput << " in " << module->id << "." << module->options);
		printModuleConnectors(target->second.get());
		errorHandler();
	}

	return *this;
}

Loader& Loader::printModuleConnectors(const ModuleData* module)
{
	if (module->connectors.size()==0)
	{
		LOG(0, "Module " << module->id << " does not define any connector!");
	}
	else
	{
		LOG(0, "Module " << module->id << " defines the following connectors: ");
		for (auto& it: module->connectors) LOG(0, it.second->type << ": " << it.second->id);
	}

	return *this;
}

<commit_msg>loader: proper log message for module unloading<commit_after>#include <dlfcn.h>

#include "loader.h"

using BVS::Loader;
using BVS::ModuleDataMap;
using BVS::ModuleVector;
using BVS::LibHandle;

ModuleDataMap Loader::modules;

ModuleVector* Loader::hotSwapGraveYard = nullptr;

Loader::Loader(const Info& info, std::function<void()> errorHandler)
	: logger{"Loader"},
	info(info),
	errorHandler(errorHandler)
{ }

void Loader::registerModule(const std::string& id, Module* module, bool hotSwap)
{
	if (hotSwap)
	{
#ifdef BVS_MODULE_HOTSWAP
		// NOTE: hotSwapGraveYard will only be initialized when the HotSwap
		// functionality is used. It is intended as a store for unneeded
		// shared_ptr until the process execution ends, but since it is a
		// static pointer it will never be explicitly deleted.
		if (hotSwapGraveYard==nullptr) hotSwapGraveYard = new ModuleVector();
		hotSwapGraveYard->push_back(std::shared_ptr<Module>(module));
		modules[id]->module.swap(hotSwapGraveYard->back());
#endif //BVS_MODULE_HOTSWAP
	}
	else
	{
		modules[id] = std::shared_ptr<ModuleData>{new ModuleData{id, {}, {}, {},
			module, nullptr, {}, ControlFlag::WAIT, Status::OK, {}}};
	}
}

Loader& Loader::load(const std::string& id, const std::string& library, const std::string& configuration, const std::string& options, const std::string& poolName)
{
	if (modules.find(id)!=modules.end())
	{
		LOG(0, "Duplicate id for module: " << id << std::endl << "If you try to load a module more than once, use unique ids and the id(library).options syntax!");
		errorHandler();
	}

	LibHandle dlib = loadLibrary(id, library);

	// execute bvsRegisterModule in loaded lib
	typedef void (*bvsRegisterModule_t)(ModuleInfo moduleInfo, const Info& info);
	bvsRegisterModule_t bvsRegisterModule;
	*reinterpret_cast<void**>(&bvsRegisterModule)=dlsym(dlib, "bvsRegisterModule");

	char* dlerr = dlerror();
	if (dlerr)
	{
		LOG(0, "Loading function bvsRegisterModule() in '" << library << "' resulted in: " << dlerr);
		errorHandler();
	}

	ModuleInfo moduleInfo{id, configuration};
	bvsRegisterModule(moduleInfo, info);

	modules[id]->configuration = configuration;
	modules[id]->dlib = dlib;
	modules[id]->library = library;
	modules[id]->options = options;

	// get connectors
	modules[id]->connectors = std::move(ConnectorDataCollector::connectors);
	modules[id]->poolName = poolName;

	LOG(2, "Loading '" << id << "' successfull!");

	return *this;
}

Loader& Loader::unload(const std::string& id)
{
	disconnectModule(id);
	modules[id]->connectors.clear();
	modules[id]->module.reset();
	unloadLibrary(id);
	modules.erase(id);

	LOG(2, "Unloading '" << id << "' successfull!");

	return *this;
}

Loader& Loader::connectAllModules(const bool connectorTypeMatching)
{
	for (auto& it: modules) connectModule(it.second->id, connectorTypeMatching);

	return *this;
}

Loader& Loader::connectModule(const std::string& id, const bool connectorTypeMatching)
{
	ModuleData* module = modules[id].get();
	std::string options = module->options;
	std::string selection;
	std::string input;
	std::string targetModule;
	std::string targetOutput;
	size_t separator;
	size_t separator2;

	while (!options.empty())
	{
		separator = options.find_first_of('(');
		separator2 = options.find_first_of(')');
		selection = options.substr(0, separator2+1);
		if (separator!=std::string::npos)
		{
			input = options.substr(0, separator);
			targetModule = options.substr(separator+1, separator2-separator-1);
		}
		else
		{
			LOG(0, "No input selection found: " << selection);
			errorHandler();
		}

		options.erase(0, separator2+1);
		if (options[0] == '.') options.erase(options.begin());

		separator = targetModule.find_first_of('.');
		if (separator!=std::string::npos)
		{
			targetOutput = targetModule.substr(separator+1, std::string::npos);
			targetModule = targetModule.substr(0, separator);
		}
		else
		{
			LOG(0, "No module output selected: " << module->id << "." << selection);
			errorHandler();
		}

		checkModuleInput(module, input);
		checkModuleOutput(module, targetModule, targetOutput);

		if (connectorTypeMatching && module->connectors[input]->typeIDHash != modules[targetModule]->connectors[targetOutput]->typeIDHash)
		{
			LOG(0, "Selected input and output connector template instantiations are of different type: " << module->id << "." << selection << " -> " << module->connectors[input]->typeIDName << " != " << modules[targetModule]->connectors[targetOutput]->typeIDName);
			errorHandler();
		}

		module->connectors[input]->pointer = modules[targetModule]->connectors[targetOutput]->pointer;
		module->connectors[input]->lock = std::unique_lock<std::mutex>{modules[targetModule]->connectors[targetOutput]->mutex, std::defer_lock};
		LOG(3, "Connected: " << module->id << "." << module->connectors[input]->id << " <- " << modules[targetModule]->id << "." << modules[targetModule]->connectors[targetOutput]->id);
	}

	return *this;
}

Loader& Loader::disconnectModule(const std::string& id)
{
	for (auto& connector: modules[id]->connectors)
	{
		if (connector.second->type==ConnectorType::INPUT) continue;

		for (auto& module: modules)
		{
			for (auto& targetConnector: module.second->connectors)
			{
				if (connector.second->pointer==targetConnector.second->pointer)
				{
					targetConnector.second->active = false;
					targetConnector.second->pointer = nullptr;
				}
			}
		}
	}

	return *this;
}

#ifdef BVS_MODULE_HOTSWAP
Loader& Loader::hotSwapModule(const std::string& id)
{
	unloadLibrary(id);
	LibHandle dlib = loadLibrary(id, modules[id]->library);

	typedef void (*bvsHotSwapModule_t)(const std::string& id, Module* module);
	bvsHotSwapModule_t bvsHotSwapModule;
	*reinterpret_cast<void**>(&bvsHotSwapModule)=dlsym(dlib, "bvsHotSwapModule");

	char* dlerr = dlerror();
	if (dlerr)
	{
		LOG(0, "Loading function bvsHotSwapModule() in '" << modules[id]->library << "' resulted in: " << dlerr);
		errorHandler();
	}

	bvsHotSwapModule(id, modules[id]->module.get());
	modules[id]->dlib = dlib;

	LOG(2, "Hotswapping '" << id << "' successfull!");

	return *this;
}
#endif //BVS_MODULE_HOTSWAP

LibHandle Loader::loadLibrary(const std::string& id, const std::string& library)
{
	std::string modulePath = "lib" + library + ".so";
	LibHandle dlib = dlopen(modulePath.c_str(), RTLD_NOW);
	if (dlib!=NULL)
	{
		LOG(3, "Loading '" << id << "' from '" << modulePath << "'!");
	}
	else
	{
#ifdef BVS_OSX_ANOMALIES
		// additional check for 'dylib' (when compiled under OSX as SHARED instead of MODULE)
		modulePath.resize(modulePath.size()-2);
		modulePath += "dylib";
		dlib = dlopen(modulePath.c_str(), RTLD_NOW);
		if (dlib!=NULL) LOG(3, "Loading " << id << " from " << modulePath << "!");
#endif //BVS_OSX_ANOMALIES
	}

	if (dlib==NULL)
	{
		LOG(0, "Loading '" << modulePath << "' resulted in: " << dlerror());
		errorHandler();
	}

	return dlib;
}

Loader& Loader::unloadLibrary(const std::string& id)
{
#ifndef BVS_OSX_ANOMALIES
	std::string modulePath = "./lib" + modules[id]->library + ".so";
#else
	std::string modulePath = "./lib" + modules[id]->library + ".(so|dylib)";
#endif //BVS_OSX_ANOMALIES
	LOG(3, "Module '" << id << "' unloading from '" << modulePath << "'!");

	void* dlib = modules[id]->dlib;
	if (dlib==nullptr) LOG(0, "Requested module '" << id << "' not found!");

	dlclose(dlib);

	char* dlerr = dlerror();
	if (dlerr)
	{
		LOG(0, "While closing '" << modulePath << "' following error occured: " << dlerror());
		errorHandler();
	}
	LOG(3, "Library '" << modulePath << "' unloaded!");

	return *this;
}

Loader& Loader::checkModuleInput(const ModuleData* module, const std::string& inputName)
{
	auto input = module->connectors.find(inputName);

	if (input == module->connectors.end() || input->second->type != ConnectorType::INPUT)
	{
		LOG(0, "Input not found: " << module->id << "." << inputName);
		printModuleConnectors(module);
		errorHandler();
	}

	if (input->second->active)
	{
		LOG(0, "Input already connected: " << module->id << "." << inputName);
		errorHandler();
	}

	return *this;
}

Loader& Loader::checkModuleOutput(const ModuleData* module, const std::string& targetModule, const std::string& targetOutput)
{
	auto target = modules.find(targetModule);
	if (target == modules.end())
	{
		LOG(0, "Module not found: " << targetModule << " in " << module->id << "." << module->options);
		errorHandler();
	}

	auto output = target->second->connectors.find(targetOutput);
	if (output == target->second->connectors.end() || output->second->type != ConnectorType::OUTPUT)
	{
		LOG(0, "Output not found: " << targetOutput << " in " << module->id << "." << module->options);
		printModuleConnectors(target->second.get());
		errorHandler();
	}

	return *this;
}

Loader& Loader::printModuleConnectors(const ModuleData* module)
{
	if (module->connectors.size()==0)
	{
		LOG(0, "Module " << module->id << " does not define any connector!");
	}
	else
	{
		LOG(0, "Module " << module->id << " defines the following connectors: ");
		for (auto& it: module->connectors) LOG(0, it.second->type << ": " << it.second->id);
	}

	return *this;
}

<|endoftext|>


<commit_before>/* Copyright (C) 2016 INRA
 *
 * 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.
 */

#ifndef ORG_VLEPROJECT_LP_UTILS_HPP
#define ORG_VLEPROJECT_LP_UTILS_HPP

#include <lpcore>

namespace lp {

inline void
Expects(bool condition)
{
    if (not condition)
        throw precondition_error("precondition failure");
}

inline void
Ensures(bool condition)
{
    if (not condition)
        throw postcondition_error("postcondition failure");
}

inline void
Expects(bool condition, const char* s)
{
    if (not condition)
        throw precondition_error(s);
}

inline void
Ensures(bool condition, const char* s)
{
    if (not condition)
        throw postcondition_error(s);
}
}

#endif
<commit_msg>utils: add a function to test real equality<commit_after>/* Copyright (C) 2016 INRA
 *
 * 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.
 */

#ifndef ORG_VLEPROJECT_LP_UTILS_HPP
#define ORG_VLEPROJECT_LP_UTILS_HPP

#include <cmath>
#include <lpcore>

namespace lp {

inline void
Expects(bool condition)
{
    if (not condition)
        throw precondition_error("precondition failure");
}

inline void
Ensures(bool condition)
{
    if (not condition)
        throw postcondition_error("postcondition failure");
}

inline void
Expects(bool condition, const char* s)
{
    if (not condition)
        throw precondition_error(s);
}

inline void
Ensures(bool condition, const char* s)
{
    if (not condition)
        throw postcondition_error(s);
}

template <typename T>
inline bool
is_essentially_equal(const T v1, const T v2, const T epsilon)
{
    static_assert(std::is_floating_point<T>::value);

    return fabs((v1) - (v2)) <=
           ((fabs(v1) > fabs(v2) ? fabs(v2) : fabs(v1)) * (epsilon));
}
}
#endif
<|endoftext|>


<commit_before>#pragma once

#include "byte.hpp"
#include "chunk_headers.hpp"
#include "magic_number.hpp"
#include "string_helpers.hpp"
#include "type_pun.hpp"

#include <gsl/gsl>

#include <cstddef>
#include <string>
#include <type_traits>

template<Magic_number type_mn>
class Ucfb_reader_strict;

class Ucfb_reader {
public:
   Ucfb_reader() = delete;
   Ucfb_reader(const Byte* bytes, const std::uint32_t size);

   template<Magic_number type_mn>
   Ucfb_reader(const Ucfb_reader_strict<type_mn>&) = delete;
   template<Magic_number type_mn>
   Ucfb_reader& operator=(const Ucfb_reader_strict<type_mn>&) = delete;

   template<Magic_number type_mn>
   Ucfb_reader(Ucfb_reader_strict<type_mn>&&) = delete;
   template<Magic_number type_mn>
   Ucfb_reader& operator=(Ucfb_reader_strict<type_mn>&&) = delete;

   template<typename Type>
   const Type& read_trivial(const bool unaligned = false)
   {
      static_assert(std::is_trivially_copyable_v<Type>,
                    "Type must be trivially copyable.");
      static_assert(!std::is_reference_v<Type>, "Type can not be a reference.");
      static_assert(!std::is_pointer_v<Type>, "Type can not be a pointer.");

      const auto cur_pos = _head;
      _head += sizeof(Type);

      check_head();

      if (!unaligned) align_head();

      return view_type_as<Type>(_data[cur_pos]);
   }

   template<typename Type>
   const Type& read_trivial_unaligned()
   {
      return read_trivial<Type>(true);
   }

   template<typename Type>
   auto read_array(typename const gsl::span<const Type>::index_type size,
                   const bool unaligned = false) -> gsl::span<const Type>
   {
      static_assert(std::is_trivially_copyable_v<Type>,
                    "Type must be trivially copyable.");
      static_assert(!std::is_reference_v<Type>, "Type can not be a reference.");
      static_assert(!std::is_pointer_v<Type>, "Type can not be a pointer.");

      const auto cur_pos = _head;
      _head += sizeof(Type) * size;

      check_head();

      if (!unaligned) align_head();

      return {&view_type_as<Type>(_data[cur_pos]), size};
   }

   template<typename Type>
   auto read_array_unaligned(typename const gsl::span<const Type>::index_type size)
      -> gsl::span<const Type>
   {
      return read_array<Type>(size, true);
   }

   template<typename Char_type = char>
   auto read_string(const bool unaligned = false) -> std::basic_string_view<Char_type>
   {
      const Char_type* const string = reinterpret_cast<const Char_type*>(_data + _head);
      const auto string_length = cstring_length(string, _size - _head);

      _head += (string_length + 1) * sizeof(Char_type);

      check_head();

      if (!unaligned) align_head();

      return {string, string_length};
   }

   template<typename Char_type = char>
   auto read_string_unaligned() -> std::basic_string_view<Char_type>
   {
      return read_string<Char_type>(true);
   }

   Ucfb_reader read_child(const bool unaligned = false);

   Ucfb_reader read_child_unaligned()
   {
      return read_child(true);
   }

   template<Magic_number type_mn>
   auto read_child_strict(const bool unaligned = false) -> Ucfb_reader_strict<type_mn>
   {
      return {read_child_strict_impl(type_mn, unaligned),
              Ucfb_reader_strict<type_mn>::Unchecked_tag{}};
   }

   template<Magic_number type_mn>
   auto read_child_strict_unaligned() -> Ucfb_reader_strict<type_mn>
   {
      return read_child_strict<type_mn>(true);
   }

   void consume(const std::size_t amount, const bool unaligned = false);

   void consume_unaligned(const std::size_t amount)
   {
      consume(amount, true);
   }

   explicit operator bool() const noexcept;

   void reset_head() noexcept;

   Magic_number magic_number() const noexcept;

   std::size_t size() const noexcept;

   // Temporary function for use as a compatibility shim. This *will* be removed
   // as soon as all code has been migrated away from using raw structs.
   template<typename Type>
   [[deprecated("Replace with ucfb_reader abstraction.")]] const Type& view_as_chunk()
      const noexcept
   {
      return view_type_as<Type>(_data[-8]);
   }

private:
   // Special constructor for use by read_child, performs no error checking.
   Ucfb_reader(const Magic_number mn, const std::uint32_t size, const Byte* const data);

   Ucfb_reader read_child_strict_impl(const Magic_number child_mn, const bool unaligned);

   void check_head();

   void align_head() noexcept
   {
      const auto remainder = _head % 4;

      if (remainder != 0) _head += (4 - remainder);
   }

   const Magic_number _mn;
   const std::size_t _size;
   const Byte* const _data;

   std::size_t _head = 0;
};

template<Magic_number type_mn>
class Ucfb_reader_strict : public Ucfb_reader {
public:
   Ucfb_reader_strict() = delete;

   explicit Ucfb_reader_strict(Ucfb_reader ucfb_reader) : Ucfb_reader{ucfb_reader}
   {
      Expects(type_mn == ucfb_reader.magic_number());
   }

private:
   friend class Ucfb_reader;

   struct Unchecked_tag {
   };

   Ucfb_reader_strict(Ucfb_reader ucfb_reader, Unchecked_tag) : Ucfb_reader{ucfb_reader}
   {
   }
};
<commit_msg>Marked Ucfb_reader_strict's constructor as noexcept<commit_after>#pragma once

#include "byte.hpp"
#include "chunk_headers.hpp"
#include "magic_number.hpp"
#include "string_helpers.hpp"
#include "type_pun.hpp"

#include <gsl/gsl>

#include <cstddef>
#include <string>
#include <type_traits>

template<Magic_number type_mn>
class Ucfb_reader_strict;

class Ucfb_reader {
public:
   Ucfb_reader() = delete;
   Ucfb_reader(const Byte* bytes, const std::uint32_t size);

   template<Magic_number type_mn>
   Ucfb_reader(const Ucfb_reader_strict<type_mn>&) = delete;
   template<Magic_number type_mn>
   Ucfb_reader& operator=(const Ucfb_reader_strict<type_mn>&) = delete;

   template<Magic_number type_mn>
   Ucfb_reader(Ucfb_reader_strict<type_mn>&&) = delete;
   template<Magic_number type_mn>
   Ucfb_reader& operator=(Ucfb_reader_strict<type_mn>&&) = delete;

   template<typename Type>
   const Type& read_trivial(const bool unaligned = false)
   {
      static_assert(std::is_trivially_copyable_v<Type>,
                    "Type must be trivially copyable.");
      static_assert(!std::is_reference_v<Type>, "Type can not be a reference.");
      static_assert(!std::is_pointer_v<Type>, "Type can not be a pointer.");

      const auto cur_pos = _head;
      _head += sizeof(Type);

      check_head();

      if (!unaligned) align_head();

      return view_type_as<Type>(_data[cur_pos]);
   }

   template<typename Type>
   const Type& read_trivial_unaligned()
   {
      return read_trivial<Type>(true);
   }

   template<typename Type>
   auto read_array(typename const gsl::span<const Type>::index_type size,
                   const bool unaligned = false) -> gsl::span<const Type>
   {
      static_assert(std::is_trivially_copyable_v<Type>,
                    "Type must be trivially copyable.");
      static_assert(!std::is_reference_v<Type>, "Type can not be a reference.");
      static_assert(!std::is_pointer_v<Type>, "Type can not be a pointer.");

      const auto cur_pos = _head;
      _head += sizeof(Type) * size;

      check_head();

      if (!unaligned) align_head();

      return {&view_type_as<Type>(_data[cur_pos]), size};
   }

   template<typename Type>
   auto read_array_unaligned(typename const gsl::span<const Type>::index_type size)
      -> gsl::span<const Type>
   {
      return read_array<Type>(size, true);
   }

   template<typename Char_type = char>
   auto read_string(const bool unaligned = false) -> std::basic_string_view<Char_type>
   {
      const Char_type* const string = reinterpret_cast<const Char_type*>(_data + _head);
      const auto string_length = cstring_length(string, _size - _head);

      _head += (string_length + 1) * sizeof(Char_type);

      check_head();

      if (!unaligned) align_head();

      return {string, string_length};
   }

   template<typename Char_type = char>
   auto read_string_unaligned() -> std::basic_string_view<Char_type>
   {
      return read_string<Char_type>(true);
   }

   Ucfb_reader read_child(const bool unaligned = false);

   Ucfb_reader read_child_unaligned()
   {
      return read_child(true);
   }

   template<Magic_number type_mn>
   auto read_child_strict(const bool unaligned = false) -> Ucfb_reader_strict<type_mn>
   {
      return {read_child_strict_impl(type_mn, unaligned),
              Ucfb_reader_strict<type_mn>::Unchecked_tag{}};
   }

   template<Magic_number type_mn>
   auto read_child_strict_unaligned() -> Ucfb_reader_strict<type_mn>
   {
      return read_child_strict<type_mn>(true);
   }

   void consume(const std::size_t amount, const bool unaligned = false);

   void consume_unaligned(const std::size_t amount)
   {
      consume(amount, true);
   }

   explicit operator bool() const noexcept;

   void reset_head() noexcept;

   Magic_number magic_number() const noexcept;

   std::size_t size() const noexcept;

   // Temporary function for use as a compatibility shim. This *will* be removed
   // as soon as all code has been migrated away from using raw structs.
   template<typename Type>
   [[deprecated("Replace with ucfb_reader abstraction.")]] const Type& view_as_chunk()
      const noexcept
   {
      return view_type_as<Type>(_data[-8]);
   }

private:
   // Special constructor for use by read_child, performs no error checking.
   Ucfb_reader(const Magic_number mn, const std::uint32_t size, const Byte* const data);

   Ucfb_reader read_child_strict_impl(const Magic_number child_mn, const bool unaligned);

   void check_head();

   void align_head() noexcept
   {
      const auto remainder = _head % 4;

      if (remainder != 0) _head += (4 - remainder);
   }

   const Magic_number _mn;
   const std::size_t _size;
   const Byte* const _data;

   std::size_t _head = 0;
};

template<Magic_number type_mn>
class Ucfb_reader_strict : public Ucfb_reader {
public:
   Ucfb_reader_strict() = delete;

   explicit Ucfb_reader_strict(Ucfb_reader ucfb_reader) noexcept
      : Ucfb_reader{ucfb_reader}
   {
      Expects(type_mn == ucfb_reader.magic_number());
   }

private:
   friend class Ucfb_reader;

   struct Unchecked_tag {
   };

   Ucfb_reader_strict(Ucfb_reader ucfb_reader, Unchecked_tag) : Ucfb_reader{ucfb_reader}
   {
   }
};<|endoftext|>