<commit_before>#include <botan/dsa.h>
#include <botan/rsa.h>
#include <botan/dh.h>
#include <botan/nr.h>
#include <botan/rw.h>
#include <botan/elgamal.h>
#include <botan/parsing.h>

#include <botan/pkcs8.h>
#include <botan/mem_ops.h>
#include <botan/look_pk.h>
#include <botan/libstate.h>

using namespace Botan;

#include "common.h"
#include "timer.h"
#include "bench.h"

#include <iostream>
#include <fstream>
#include <string>
#include <memory>

namespace {

void benchmark_enc_dec(PK_Encryptor& enc, PK_Decryptor& dec,
                       Timer& enc_timer, Timer& dec_timer,
                       RandomNumberGenerator& rng,
                       u32bit runs, double seconds)
   {
   SecureVector<byte> plaintext, ciphertext;

   for(u32bit i = 0; i != runs; ++i)
      {
      if(enc_timer.seconds() < seconds || ciphertext.size() == 0)
         {
         plaintext.create(enc.maximum_input_size());

         // Ensure for Raw, etc, it stays large
         if((i % 100) == 0)
            {
            rng.randomize(plaintext.begin(), plaintext.size());
            plaintext[0] |= 0x80;
            }

         enc_timer.start();
         ciphertext = enc.encrypt(plaintext, rng);
         enc_timer.stop();
         }

      if(dec_timer.seconds() < seconds)
         {
         dec_timer.start();
         SecureVector<byte> plaintext_out = dec.decrypt(ciphertext);
         dec_timer.stop();

         if(plaintext_out != plaintext)
            { // has never happened...
            std::cerr << "Contents mismatched on decryption during benchmark!\n";
            }
         }
      }
   }

void benchmark_sig_ver(PK_Verifier& ver, PK_Signer& sig,
                       Timer& verify_timer, Timer& sig_timer,
                       RandomNumberGenerator& rng,
                       u32bit runs, double seconds)
   {
   SecureVector<byte> message, signature, sig_random;

   for(u32bit i = 0; i != runs; ++i)
      {
      if(sig_timer.seconds() < seconds || signature.size() == 0)
         {
         if((i % 100) == 0)
            {
            message.create(48);
            rng.randomize(message.begin(), message.size());
            }

         sig_timer.start();
         signature = sig.sign_message(message, rng);
         sig_timer.stop();
         }

      if(verify_timer.seconds() < seconds)
         {
         verify_timer.start();
         bool verified = ver.verify_message(message, signature);
         verify_timer.stop();

         if(!verified)
            std::cerr << "Signature verification failure\n";

         if((i % 100) == 0)
            {
            sig_random.create(signature.size());
            rng.randomize(sig_random, sig_random.size());

            verify_timer.start();
            bool verified2 = ver.verify_message(message, sig_random);
            verify_timer.stop();

            if(verified2)
               std::cerr << "Signature verification failure (bad sig OK)\n";
            }
         }
      }
   }

/*
  Between benchmark_rsa_rw + benchmark_dsa_nr:
     Type of the key
     Arguments to the constructor (A list of some arbitrary type?)
     Type of padding
*/

void benchmark_rsa(RandomNumberGenerator& rng,
                   double seconds,
                   Benchmark_Report& report)
   {
   for(size_t keylen = 1024; keylen <= 4096; keylen += 1024)
      {
      Timer verify_timer("verify");
      Timer sig_timer("signature");
      Timer enc_timer("encrypt");
      Timer dec_timer("decrypt");

      const std::string sig_padding = "EMSA4(SHA-1)";
      const std::string enc_padding = "EME1(SHA-1)";

      try
         {

#if 0
         PKCS8_PrivateKey* pkcs8_key = PKCS8::load_key("rsa/" + to_string(keylen) + ".pem", rng);
         RSA_PrivateKey* key_ptr = dynamic_cast<RSA_PrivateKey*>(pkcs8_key);

         RSA_PrivateKey key = *key_ptr;
#else

         RSA_PrivateKey key(rng, keylen);
#endif

         while(verify_timer.seconds() < seconds ||
               sig_timer.seconds() < seconds)
            {
            std::auto_ptr<PK_Encryptor> enc(get_pk_encryptor(key, enc_padding));
            std::auto_ptr<PK_Decryptor> dec(get_pk_decryptor(key, enc_padding));
            benchmark_enc_dec(*enc, *dec, enc_timer, dec_timer, rng, 10000, seconds);

            std::auto_ptr<PK_Signer> sig(get_pk_signer(key, sig_padding));
            std::auto_ptr<PK_Verifier> ver(get_pk_verifier(key, sig_padding));
            benchmark_sig_ver(*ver, *sig, verify_timer, sig_timer, rng, 10000, seconds);
            }

         const std::string rsa_keylen = "RSA " + to_string(keylen);

         report.report(rsa_keylen + " " + sig_padding, verify_timer);
         report.report(rsa_keylen + " " + sig_padding, sig_timer);
         report.report(rsa_keylen + " " + enc_padding, enc_timer);
         report.report(rsa_keylen + " " + enc_padding, dec_timer);
         }
      catch(Stream_IO_Error)
         {
         }
      catch(Exception& e)
         {
         std::cout << e.what() << "\n";
         }
      }
   }

void benchmark_rw(RandomNumberGenerator& rng,
                  double seconds,
                  Benchmark_Report& report)
   {
   const u32bit keylens[] = { 512, 1024, 2048, 3072, 4096, 6144, 8192, 0 };

   for(size_t j = 0; keylens[j]; j++)
      {
      u32bit keylen = keylens[j];

      Timer keygen_timer("keygen");
      Timer verify_timer("verify");
      Timer sig_timer("signature");

      std::string padding = "EMSA2(SHA-1)";

      while(verify_timer.seconds() < seconds ||
            sig_timer.seconds() < seconds)
         {
         keygen_timer.start();
         RW_PrivateKey key(rng, keylen);
         keygen_timer.stop();

         std::auto_ptr<PK_Signer> sig(get_pk_signer(key, padding));
         std::auto_ptr<PK_Verifier> ver(get_pk_verifier(key, padding));

         benchmark_sig_ver(*ver, *sig, verify_timer, sig_timer, rng, 10000, seconds);
         }

      const std::string nm = "RW-" + to_string(keylen);
      report.report(nm, keygen_timer);
      report.report(nm, verify_timer);
      report.report(nm, sig_timer);
      }
   }

template<typename PRIV_KEY_TYPE>
void benchmark_dsa_nr(RandomNumberGenerator& rng,
                      double seconds,
                      Benchmark_Report& report)
   {
   const char* domains[] = { "dsa/jce/512",
                             "dsa/jce/768",
                             "dsa/jce/1024",
                             "dsa/botan/2048",
                             "dsa/botan/3072",
                             NULL };

   const std::string algo_name = PRIV_KEY_TYPE().algo_name();

   for(size_t j = 0; domains[j]; j++)
      {
      u32bit pbits = to_u32bit(split_on(domains[j], '/')[2]);
      u32bit qbits = (pbits <= 1024) ? 160 : 256;

      Timer keygen_timer("keygen");
      Timer verify_timer("verify");
      Timer sig_timer("signature");

      while(verify_timer.seconds() < seconds ||
            sig_timer.seconds() < seconds)
         {
         DL_Group group(domains[j]);

         keygen_timer.start();
         PRIV_KEY_TYPE key(rng, group);
         keygen_timer.stop();

         const std::string padding = "EMSA1(SHA-" + to_string(qbits) + ")";

         std::auto_ptr<PK_Signer> sig(get_pk_signer(key, padding));
         std::auto_ptr<PK_Verifier> ver(get_pk_verifier(key, padding));

         benchmark_sig_ver(*ver, *sig, verify_timer, sig_timer, rng, 1000, seconds);
         }

      const std::string nm = algo_name + "-" + to_string(pbits);
      report.report(nm, keygen_timer);
      report.report(nm, verify_timer);
      report.report(nm, sig_timer);
      }
   }

void benchmark_dh(RandomNumberGenerator& rng,
                  double seconds,
                  Benchmark_Report& report)
   {
   const char* domains[] = { "modp/ietf/768",
                             "modp/ietf/1024",
                             "modp/ietf/2048",
                             "modp/ietf/3072",
                             "modp/ietf/4096",
                             "modp/ietf/6144",
                             "modp/ietf/8192",
                             NULL };

   for(size_t j = 0; domains[j]; j++)
      {
      Timer keygen_timer("keygen");
      Timer kex_timer("kex");

      while(kex_timer.seconds() < seconds)
         {
         DL_Group group(domains[j]);

         keygen_timer.start();
         DH_PrivateKey dh1(rng, group);
         keygen_timer.stop();

         keygen_timer.start();
         DH_PrivateKey dh2(rng, group);
         keygen_timer.stop();

         DH_PublicKey pub1(dh1);
         DH_PublicKey pub2(dh2);

         SecureVector<byte> secret1, secret2;

         for(u32bit i = 0; i != 1000; ++i)
            {
            if(kex_timer.seconds() > seconds)
               break;

            kex_timer.start();
            secret1 = dh1.derive_key(pub2);
            kex_timer.stop();

            kex_timer.start();
            secret2 = dh2.derive_key(pub1);
            kex_timer.stop();

            if(secret1 != secret2)
               {
               std::cerr << "DH secrets did not match, bug in the library!?!\n";
               }

            }

         }

      const std::string nm = "DH-" + split_on(domains[j], '/')[2];
      report.report(nm, keygen_timer);
      report.report(nm, kex_timer);
      }
   }

void benchmark_elg(RandomNumberGenerator& rng,
                   double seconds,
                   Benchmark_Report& report)
   {
   const char* domains[] = { "modp/ietf/768",
                             "modp/ietf/1024",
                             "modp/ietf/2048",
                             "modp/ietf/3072",
                             "modp/ietf/4096",
                             "modp/ietf/6144",
                             "modp/ietf/8192",
                             NULL };

   const std::string algo_name = "ElGamal";

   for(size_t j = 0; domains[j]; j++)
      {
      u32bit pbits = to_u32bit(split_on(domains[j], '/')[2]);

      Timer keygen_timer("keygen");
      Timer enc_timer("encrypt");
      Timer dec_timer("decrypt");

      while(enc_timer.seconds() < seconds ||
            dec_timer.seconds() < seconds)
         {
         DL_Group group(domains[j]);

         keygen_timer.start();
         ElGamal_PrivateKey key(rng, group);
         keygen_timer.stop();

         const std::string padding = "EME1(SHA-1)";

         std::auto_ptr<PK_Decryptor> dec(get_pk_decryptor(key, padding));
         std::auto_ptr<PK_Encryptor> enc(get_pk_encryptor(key, padding));

         benchmark_enc_dec(*enc, *dec, enc_timer, dec_timer, rng, 1000, seconds);
         }

      const std::string nm = algo_name + "-" + to_string(pbits);
      report.report(nm, keygen_timer);
      report.report(nm, enc_timer);
      report.report(nm, dec_timer);
      }
   }

}

void bench_pk(RandomNumberGenerator& rng,
              const std::string& algo, bool, double seconds)
   {
   /*
     There is some strangeness going on here. It looks like algorithms
     at the end take some kind of penalty. For example, running the RW tests
     first got a result of:
         RW-1024: 148.14 ms / private operation
     but running them last output:
         RW-1024: 363.54 ms / private operation

     I think it's from memory fragmentation in the allocators, but I'm
     not really sure. Need to investigate.

     Until then, I've basically ordered the tests in order of most important
     algorithms (RSA, DSA) to least important (NR, RW).

     This strange behaviour does not seem to occur with DH (?)

     To get more accurate runs, use --bench-algo (RSA|DSA|DH|ELG|NR); in this
     case the distortion is less than 5%, which is good enough.

     We do random keys with the DL schemes, since it's so easy and fast to
     generate keys for them. For RSA and RW, we load the keys from a file.  The
     RSA keys are stored in a PKCS #8 structure, while RW is stored in a more
     ad-hoc format (the RW algorithm has no assigned OID that I know of, so
     there is no way to encode a RW key into a PKCS #8 structure).
   */

   global_state().set_option("pk/test/private_gen", "basic");

   Benchmark_Report report;

   if(algo == "All" || algo == "RSA")
      benchmark_rsa(rng, seconds, report);

   if(algo == "All" || algo == "DSA")
      benchmark_dsa_nr<DSA_PrivateKey>(rng, seconds, report);

   if(algo == "All" || algo == "DH")
      benchmark_dh(rng, seconds, report);

   if(algo == "All" || algo == "ELG" || algo == "ElGamal")
      benchmark_elg(rng, seconds, report);

   if(algo == "All" || algo == "NR")
      benchmark_dsa_nr<NR_PrivateKey>(rng, seconds, report);

   if(algo == "All" || algo == "RW")
      benchmark_rw(rng, seconds, report);
   }
<commit_msg>Time RSA key generation again<commit_after>#include <botan/dsa.h>
#include <botan/rsa.h>
#include <botan/dh.h>
#include <botan/nr.h>
#include <botan/rw.h>
#include <botan/elgamal.h>
#include <botan/parsing.h>

#include <botan/pkcs8.h>
#include <botan/mem_ops.h>
#include <botan/look_pk.h>
#include <botan/libstate.h>

using namespace Botan;

#include "common.h"
#include "timer.h"
#include "bench.h"

#include <iostream>
#include <fstream>
#include <string>
#include <memory>

namespace {

void benchmark_enc_dec(PK_Encryptor& enc, PK_Decryptor& dec,
                       Timer& enc_timer, Timer& dec_timer,
                       RandomNumberGenerator& rng,
                       u32bit runs, double seconds)
   {
   SecureVector<byte> plaintext, ciphertext;

   for(u32bit i = 0; i != runs; ++i)
      {
      if(enc_timer.seconds() < seconds || ciphertext.size() == 0)
         {
         plaintext.create(enc.maximum_input_size());

         // Ensure for Raw, etc, it stays large
         if((i % 100) == 0)
            {
            rng.randomize(plaintext.begin(), plaintext.size());
            plaintext[0] |= 0x80;
            }

         enc_timer.start();
         ciphertext = enc.encrypt(plaintext, rng);
         enc_timer.stop();
         }

      if(dec_timer.seconds() < seconds)
         {
         dec_timer.start();
         SecureVector<byte> plaintext_out = dec.decrypt(ciphertext);
         dec_timer.stop();

         if(plaintext_out != plaintext)
            { // has never happened...
            std::cerr << "Contents mismatched on decryption during benchmark!\n";
            }
         }
      }
   }

void benchmark_sig_ver(PK_Verifier& ver, PK_Signer& sig,
                       Timer& verify_timer, Timer& sig_timer,
                       RandomNumberGenerator& rng,
                       u32bit runs, double seconds)
   {
   SecureVector<byte> message, signature, sig_random;

   for(u32bit i = 0; i != runs; ++i)
      {
      if(sig_timer.seconds() < seconds || signature.size() == 0)
         {
         if((i % 100) == 0)
            {
            message.create(48);
            rng.randomize(message.begin(), message.size());
            }

         sig_timer.start();
         signature = sig.sign_message(message, rng);
         sig_timer.stop();
         }

      if(verify_timer.seconds() < seconds)
         {
         verify_timer.start();
         bool verified = ver.verify_message(message, signature);
         verify_timer.stop();

         if(!verified)
            std::cerr << "Signature verification failure\n";

         if((i % 100) == 0)
            {
            sig_random.create(signature.size());
            rng.randomize(sig_random, sig_random.size());

            verify_timer.start();
            bool verified2 = ver.verify_message(message, sig_random);
            verify_timer.stop();

            if(verified2)
               std::cerr << "Signature verification failure (bad sig OK)\n";
            }
         }
      }
   }

/*
  Between benchmark_rsa_rw + benchmark_dsa_nr:
     Type of the key
     Arguments to the constructor (A list of some arbitrary type?)
     Type of padding
*/

void benchmark_rsa(RandomNumberGenerator& rng,
                   double seconds,
                   Benchmark_Report& report)
   {
   for(size_t keylen = 1024; keylen <= 4096; keylen += 1024)
      {
      Timer keygen_timer("keygen");
      Timer verify_timer("verify");
      Timer sig_timer("signature");
      Timer enc_timer("encrypt");
      Timer dec_timer("decrypt");

      const std::string sig_padding = "EMSA4(SHA-1)";
      const std::string enc_padding = "EME1(SHA-1)";

      try
         {

#if 0
         // for profiling
         PKCS8_PrivateKey* pkcs8_key = PKCS8::load_key("rsa/" + to_string(keylen) + ".pem", rng);
         RSA_PrivateKey* key_ptr = dynamic_cast<RSA_PrivateKey*>(pkcs8_key);

         RSA_PrivateKey key = *key_ptr;
#else
         keygen_timer.start();
         RSA_PrivateKey key(rng, keylen);
         keygen_timer.stop();
#endif

         while(verify_timer.seconds() < seconds ||
               sig_timer.seconds() < seconds)
            {
            std::auto_ptr<PK_Encryptor> enc(get_pk_encryptor(key, enc_padding));
            std::auto_ptr<PK_Decryptor> dec(get_pk_decryptor(key, enc_padding));
            benchmark_enc_dec(*enc, *dec, enc_timer, dec_timer, rng, 10000, seconds);

            std::auto_ptr<PK_Signer> sig(get_pk_signer(key, sig_padding));
            std::auto_ptr<PK_Verifier> ver(get_pk_verifier(key, sig_padding));
            benchmark_sig_ver(*ver, *sig, verify_timer, sig_timer, rng, 10000, seconds);
            }

         const std::string rsa_keylen = "RSA " + to_string(keylen);

         report.report(rsa_keylen, keygen_timer);
         report.report(rsa_keylen + " " + sig_padding, verify_timer);
         report.report(rsa_keylen + " " + sig_padding, sig_timer);
         report.report(rsa_keylen + " " + enc_padding, enc_timer);
         report.report(rsa_keylen + " " + enc_padding, dec_timer);
         }
      catch(Stream_IO_Error)
         {
         }
      catch(Exception& e)
         {
         std::cout << e.what() << "\n";
         }
      }
   }

void benchmark_rw(RandomNumberGenerator& rng,
                  double seconds,
                  Benchmark_Report& report)
   {
   const u32bit keylens[] = { 512, 1024, 2048, 3072, 4096, 6144, 8192, 0 };

   for(size_t j = 0; keylens[j]; j++)
      {
      u32bit keylen = keylens[j];

      Timer keygen_timer("keygen");
      Timer verify_timer("verify");
      Timer sig_timer("signature");

      std::string padding = "EMSA2(SHA-1)";

      while(verify_timer.seconds() < seconds ||
            sig_timer.seconds() < seconds)
         {
         keygen_timer.start();
         RW_PrivateKey key(rng, keylen);
         keygen_timer.stop();

         std::auto_ptr<PK_Signer> sig(get_pk_signer(key, padding));
         std::auto_ptr<PK_Verifier> ver(get_pk_verifier(key, padding));

         benchmark_sig_ver(*ver, *sig, verify_timer, sig_timer, rng, 10000, seconds);
         }

      const std::string nm = "RW-" + to_string(keylen);
      report.report(nm, keygen_timer);
      report.report(nm, verify_timer);
      report.report(nm, sig_timer);
      }
   }

template<typename PRIV_KEY_TYPE>
void benchmark_dsa_nr(RandomNumberGenerator& rng,
                      double seconds,
                      Benchmark_Report& report)
   {
   const char* domains[] = { "dsa/jce/512",
                             "dsa/jce/768",
                             "dsa/jce/1024",
                             "dsa/botan/2048",
                             "dsa/botan/3072",
                             NULL };

   const std::string algo_name = PRIV_KEY_TYPE().algo_name();

   for(size_t j = 0; domains[j]; j++)
      {
      u32bit pbits = to_u32bit(split_on(domains[j], '/')[2]);
      u32bit qbits = (pbits <= 1024) ? 160 : 256;

      Timer keygen_timer("keygen");
      Timer verify_timer("verify");
      Timer sig_timer("signature");

      while(verify_timer.seconds() < seconds ||
            sig_timer.seconds() < seconds)
         {
         DL_Group group(domains[j]);

         keygen_timer.start();
         PRIV_KEY_TYPE key(rng, group);
         keygen_timer.stop();

         const std::string padding = "EMSA1(SHA-" + to_string(qbits) + ")";

         std::auto_ptr<PK_Signer> sig(get_pk_signer(key, padding));
         std::auto_ptr<PK_Verifier> ver(get_pk_verifier(key, padding));

         benchmark_sig_ver(*ver, *sig, verify_timer, sig_timer, rng, 1000, seconds);
         }

      const std::string nm = algo_name + "-" + to_string(pbits);
      report.report(nm, keygen_timer);
      report.report(nm, verify_timer);
      report.report(nm, sig_timer);
      }
   }

void benchmark_dh(RandomNumberGenerator& rng,
                  double seconds,
                  Benchmark_Report& report)
   {
   const char* domains[] = { "modp/ietf/768",
                             "modp/ietf/1024",
                             "modp/ietf/2048",
                             "modp/ietf/3072",
                             "modp/ietf/4096",
                             "modp/ietf/6144",
                             "modp/ietf/8192",
                             NULL };

   for(size_t j = 0; domains[j]; j++)
      {
      Timer keygen_timer("keygen");
      Timer kex_timer("kex");

      while(kex_timer.seconds() < seconds)
         {
         DL_Group group(domains[j]);

         keygen_timer.start();
         DH_PrivateKey dh1(rng, group);
         keygen_timer.stop();

         keygen_timer.start();
         DH_PrivateKey dh2(rng, group);
         keygen_timer.stop();

         DH_PublicKey pub1(dh1);
         DH_PublicKey pub2(dh2);

         SecureVector<byte> secret1, secret2;

         for(u32bit i = 0; i != 1000; ++i)
            {
            if(kex_timer.seconds() > seconds)
               break;

            kex_timer.start();
            secret1 = dh1.derive_key(pub2);
            kex_timer.stop();

            kex_timer.start();
            secret2 = dh2.derive_key(pub1);
            kex_timer.stop();

            if(secret1 != secret2)
               {
               std::cerr << "DH secrets did not match, bug in the library!?!\n";
               }

            }

         }

      const std::string nm = "DH-" + split_on(domains[j], '/')[2];
      report.report(nm, keygen_timer);
      report.report(nm, kex_timer);
      }
   }

void benchmark_elg(RandomNumberGenerator& rng,
                   double seconds,
                   Benchmark_Report& report)
   {
   const char* domains[] = { "modp/ietf/768",
                             "modp/ietf/1024",
                             "modp/ietf/2048",
                             "modp/ietf/3072",
                             "modp/ietf/4096",
                             "modp/ietf/6144",
                             "modp/ietf/8192",
                             NULL };

   const std::string algo_name = "ElGamal";

   for(size_t j = 0; domains[j]; j++)
      {
      u32bit pbits = to_u32bit(split_on(domains[j], '/')[2]);

      Timer keygen_timer("keygen");
      Timer enc_timer("encrypt");
      Timer dec_timer("decrypt");

      while(enc_timer.seconds() < seconds ||
            dec_timer.seconds() < seconds)
         {
         DL_Group group(domains[j]);

         keygen_timer.start();
         ElGamal_PrivateKey key(rng, group);
         keygen_timer.stop();

         const std::string padding = "EME1(SHA-1)";

         std::auto_ptr<PK_Decryptor> dec(get_pk_decryptor(key, padding));
         std::auto_ptr<PK_Encryptor> enc(get_pk_encryptor(key, padding));

         benchmark_enc_dec(*enc, *dec, enc_timer, dec_timer, rng, 1000, seconds);
         }

      const std::string nm = algo_name + "-" + to_string(pbits);
      report.report(nm, keygen_timer);
      report.report(nm, enc_timer);
      report.report(nm, dec_timer);
      }
   }

}

void bench_pk(RandomNumberGenerator& rng,
              const std::string& algo, bool, double seconds)
   {
   /*
     There is some strangeness going on here. It looks like algorithms
     at the end take some kind of penalty. For example, running the RW tests
     first got a result of:
         RW-1024: 148.14 ms / private operation
     but running them last output:
         RW-1024: 363.54 ms / private operation

     I think it's from memory fragmentation in the allocators, but I'm
     not really sure. Need to investigate.

     Until then, I've basically ordered the tests in order of most important
     algorithms (RSA, DSA) to least important (NR, RW).

     This strange behaviour does not seem to occur with DH (?)

     To get more accurate runs, use --bench-algo (RSA|DSA|DH|ELG|NR); in this
     case the distortion is less than 5%, which is good enough.

     We do random keys with the DL schemes, since it's so easy and fast to
     generate keys for them. For RSA and RW, we load the keys from a file.  The
     RSA keys are stored in a PKCS #8 structure, while RW is stored in a more
     ad-hoc format (the RW algorithm has no assigned OID that I know of, so
     there is no way to encode a RW key into a PKCS #8 structure).
   */

   global_state().set_option("pk/test/private_gen", "basic");

   Benchmark_Report report;

   if(algo == "All" || algo == "RSA")
      benchmark_rsa(rng, seconds, report);

   if(algo == "All" || algo == "DSA")
      benchmark_dsa_nr<DSA_PrivateKey>(rng, seconds, report);

   if(algo == "All" || algo == "DH")
      benchmark_dh(rng, seconds, report);

   if(algo == "All" || algo == "ELG" || algo == "ElGamal")
      benchmark_elg(rng, seconds, report);

   if(algo == "All" || algo == "NR")
      benchmark_dsa_nr<NR_PrivateKey>(rng, seconds, report);

   if(algo == "All" || algo == "RW")
      benchmark_rw(rng, seconds, report);
   }
<|endoftext|>


<commit_before>// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#include "common.h"
#include "CommonTypes.h"
#include "CommonMacros.h"
#include "daccess.h"
#include "PalRedhawkCommon.h"
#include "PalRedhawk.h"
#include "holder.h"
#include "rhassert.h"
#include "slist.h"
#include "gcrhinterface.h"
#include "shash.h"
#include "RWLock.h"
#include "module.h"
#include "varint.h"
#include "rhbinder.h"
#include "regdisplay.h"
#include "StackFrameIterator.h"
#include "thread.h"
#include "event.h"
#include "threadstore.h"
#include "TypeManager.h"

/* static */
TypeManager * TypeManager::Create(HANDLE osModule, void * pModuleHeader)
{
    ReadyToRunHeader * pReadyToRunHeader = (ReadyToRunHeader *)pModuleHeader;

    // Sanity check the signature magic
    ASSERT(pReadyToRunHeader->Signature == ReadyToRunHeaderConstants::Signature);
    if (pReadyToRunHeader->Signature != ReadyToRunHeaderConstants::Signature)
        return nullptr;

    // Only the current major version is supported currently
    ASSERT(pReadyToRunHeader->MajorVersion == ReadyToRunHeaderConstants::CurrentMajorVersion);
    if (pReadyToRunHeader->MajorVersion != ReadyToRunHeaderConstants::CurrentMajorVersion)
        return nullptr;

    return new (nothrow) TypeManager(osModule, pReadyToRunHeader);
}

TypeManager::TypeManager(HANDLE osModule, ReadyToRunHeader * pHeader)
    : m_osModule(osModule), m_pHeader(pHeader), m_pDispatchMapTable(nullptr)
{
    int length;
    m_pStaticsGCDataSection = (UInt8*)GetModuleSection(ReadyToRunSectionType::GCStaticRegion, &length);
    m_pStaticsGCInfo = (StaticGcDesc*)GetModuleSection(ReadyToRunSectionType::GCStaticDesc, &length);
    m_pThreadStaticsDataSection = (UInt8*)GetModuleSection(ReadyToRunSectionType::ThreadStaticRegion, &length);
    m_pThreadStaticsGCInfo = (StaticGcDesc*)GetModuleSection(ReadyToRunSectionType::ThreadStaticGCDescRegion, &length);
    m_pTlsIndex = (UInt32*)GetModuleSection(ReadyToRunSectionType::ThreadStaticIndex, &length);
    UInt32 *pManagedTlsStartOffset = (UInt32*)GetModuleSection(ReadyToRunSectionType::ThreadStaticStartOffset, &length);
    m_managedTlsStartOffset = pManagedTlsStartOffset ? *pManagedTlsStartOffset : 0;
}

void * TypeManager::GetModuleSection(ReadyToRunSectionType sectionId, int * length)
{
    ModuleInfoRow * pModuleInfoRows = (ModuleInfoRow *)(m_pHeader + 1);

    ASSERT(m_pHeader->EntrySize == sizeof(ModuleInfoRow));

    // TODO: Binary search
    for (int i = 0; i < m_pHeader->NumberOfSections; i++)
    {
        ModuleInfoRow * pCurrent = pModuleInfoRows + i;
        if ((int32_t)sectionId == pCurrent->SectionId)
        {
            *length = pCurrent->GetLength();
            return pCurrent->Start;
        }
    }

    *length = 0;
    return nullptr;
}

DispatchMap** TypeManager::GetDispatchMapLookupTable()
{
    if (m_pDispatchMapTable == nullptr)
    {
        int length = 0;
        DispatchMap ** pDispatchMapTable = (DispatchMap **)GetModuleSection(ReadyToRunSectionType::InterfaceDispatchTable, &length);
        m_pDispatchMapTable = pDispatchMapTable;
    }

    return m_pDispatchMapTable;
}

bool TypeManager::ModuleInfoRow::HasEndPointer()
{
    return Flags & (int32_t)ModuleInfoFlags::HasEndPointer;
}

int TypeManager::ModuleInfoRow::GetLength()
{
    if (HasEndPointer())
    {
        return (int)((UInt8*)End - (UInt8*)Start);
    }
    else
    {
        return sizeof(void*);
    }
}

void TypeManager::EnumStaticGCRefsBlock(void * pfnCallback, void * pvCallbackData, StaticGcDesc* pStaticGcInfo)
{
    if (pStaticGcInfo == NULL)
        return;

    for (UInt32 idxSeries = 0; idxSeries < pStaticGcInfo->m_numSeries; idxSeries++)
    {
        PTR_StaticGcDescGCSeries pSeries = dac_cast<PTR_StaticGcDescGCSeries>(dac_cast<TADDR>(pStaticGcInfo) +
            offsetof(StaticGcDesc, m_series) + (idxSeries * sizeof(StaticGcDesc::GCSeries)));

        // The m_startOffset field is really 32-bit relocation (IMAGE_REL_BASED_RELPTR32) to the GC static base of the type
        // the GCSeries is describing for. This makes it tolerable to the symbol sorting that the linker conducts.
        PTR_RtuObjectRef    pRefLocation = dac_cast<PTR_RtuObjectRef>(dac_cast<PTR_UInt8>(&pSeries->m_startOffset) + (Int32)pSeries->m_startOffset);
        UInt32              numObjects = pSeries->m_size;

        RedhawkGCInterface::BulkEnumGcObjRef(pRefLocation, numObjects, pfnCallback, pvCallbackData);
    }
}

void TypeManager::EnumThreadStaticGCRefsBlock(void * pfnCallback, void * pvCallbackData, StaticGcDesc* pStaticGcInfo, UInt8* pbThreadStaticData)
{
    if (pStaticGcInfo == NULL)
        return;

    for (UInt32 idxSeries = 0; idxSeries < pStaticGcInfo->m_numSeries; idxSeries++)
    {
        PTR_StaticGcDescGCSeries pSeries = dac_cast<PTR_StaticGcDescGCSeries>(dac_cast<TADDR>(pStaticGcInfo) +
            offsetof(StaticGcDesc, m_series) + (idxSeries * sizeof(StaticGcDesc::GCSeries)));

        // The m_startOffset field is really a 32-bit relocation (IMAGE_REL_SECREL) to the TLS section.
        UInt8* pTlsObject = pbThreadStaticData + pSeries->m_startOffset;
        PTR_RtuObjectRef    pRefLocation = dac_cast<PTR_RtuObjectRef>(pTlsObject);
        UInt32              numObjects = pSeries->m_size;

        RedhawkGCInterface::BulkEnumGcObjRef(pRefLocation, numObjects, pfnCallback, pvCallbackData);
    }
}

void TypeManager::EnumStaticGCRefs(void * pfnCallback, void * pvCallbackData)
{
    // Regular statics.
    EnumStaticGCRefsBlock(pfnCallback, pvCallbackData, m_pStaticsGCInfo);

    // Thread local statics.
    if (m_pThreadStaticsGCInfo != NULL)
    {
        FOREACH_THREAD(pThread)
        {
            // To calculate the address of the data for each thread's TLS fields we need two values:
            //  1) The TLS slot index allocated for this module by the OS loader. We keep a pointer to this
            //     value in the module header.
            //  2) The offset into the TLS block at which managed data begins.
            EnumThreadStaticGCRefsBlock(pfnCallback, pvCallbackData, m_pThreadStaticsGCInfo,
                dac_cast<UInt8*>(pThread->GetThreadLocalStorage(*m_pTlsIndex, m_managedTlsStartOffset)));
        }
        END_FOREACH_THREAD
    }
}

HANDLE TypeManager::GetOsModuleHandle()
{
    return m_osModule;
}

bool TypeManagerHandle::IsTypeManager()
{
#if !CORERT
    if (((int)_value & 1) == 0)
        return false;
#endif

    return true;
}

TypeManager* TypeManagerHandle::AsTypeManager()
{
    ASSERT(IsTypeManager());
    return (TypeManager*)(((uint8_t *)_value) - 1);
}

HANDLE TypeManagerHandle::AsOsModule()
{
    ASSERT(!IsTypeManager());
    return (HANDLE)_value;
}

<commit_msg>Fix build break on amd64 platforms - Pointer truncation warning removed - Use uintptr_t instead of int<commit_after>// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#include "common.h"
#include "CommonTypes.h"
#include "CommonMacros.h"
#include "daccess.h"
#include "PalRedhawkCommon.h"
#include "PalRedhawk.h"
#include "holder.h"
#include "rhassert.h"
#include "slist.h"
#include "gcrhinterface.h"
#include "shash.h"
#include "RWLock.h"
#include "module.h"
#include "varint.h"
#include "rhbinder.h"
#include "regdisplay.h"
#include "StackFrameIterator.h"
#include "thread.h"
#include "event.h"
#include "threadstore.h"
#include "TypeManager.h"

/* static */
TypeManager * TypeManager::Create(HANDLE osModule, void * pModuleHeader)
{
    ReadyToRunHeader * pReadyToRunHeader = (ReadyToRunHeader *)pModuleHeader;

    // Sanity check the signature magic
    ASSERT(pReadyToRunHeader->Signature == ReadyToRunHeaderConstants::Signature);
    if (pReadyToRunHeader->Signature != ReadyToRunHeaderConstants::Signature)
        return nullptr;

    // Only the current major version is supported currently
    ASSERT(pReadyToRunHeader->MajorVersion == ReadyToRunHeaderConstants::CurrentMajorVersion);
    if (pReadyToRunHeader->MajorVersion != ReadyToRunHeaderConstants::CurrentMajorVersion)
        return nullptr;

    return new (nothrow) TypeManager(osModule, pReadyToRunHeader);
}

TypeManager::TypeManager(HANDLE osModule, ReadyToRunHeader * pHeader)
    : m_osModule(osModule), m_pHeader(pHeader), m_pDispatchMapTable(nullptr)
{
    int length;
    m_pStaticsGCDataSection = (UInt8*)GetModuleSection(ReadyToRunSectionType::GCStaticRegion, &length);
    m_pStaticsGCInfo = (StaticGcDesc*)GetModuleSection(ReadyToRunSectionType::GCStaticDesc, &length);
    m_pThreadStaticsDataSection = (UInt8*)GetModuleSection(ReadyToRunSectionType::ThreadStaticRegion, &length);
    m_pThreadStaticsGCInfo = (StaticGcDesc*)GetModuleSection(ReadyToRunSectionType::ThreadStaticGCDescRegion, &length);
    m_pTlsIndex = (UInt32*)GetModuleSection(ReadyToRunSectionType::ThreadStaticIndex, &length);
    UInt32 *pManagedTlsStartOffset = (UInt32*)GetModuleSection(ReadyToRunSectionType::ThreadStaticStartOffset, &length);
    m_managedTlsStartOffset = pManagedTlsStartOffset ? *pManagedTlsStartOffset : 0;
}

void * TypeManager::GetModuleSection(ReadyToRunSectionType sectionId, int * length)
{
    ModuleInfoRow * pModuleInfoRows = (ModuleInfoRow *)(m_pHeader + 1);

    ASSERT(m_pHeader->EntrySize == sizeof(ModuleInfoRow));

    // TODO: Binary search
    for (int i = 0; i < m_pHeader->NumberOfSections; i++)
    {
        ModuleInfoRow * pCurrent = pModuleInfoRows + i;
        if ((int32_t)sectionId == pCurrent->SectionId)
        {
            *length = pCurrent->GetLength();
            return pCurrent->Start;
        }
    }

    *length = 0;
    return nullptr;
}

DispatchMap** TypeManager::GetDispatchMapLookupTable()
{
    if (m_pDispatchMapTable == nullptr)
    {
        int length = 0;
        DispatchMap ** pDispatchMapTable = (DispatchMap **)GetModuleSection(ReadyToRunSectionType::InterfaceDispatchTable, &length);
        m_pDispatchMapTable = pDispatchMapTable;
    }

    return m_pDispatchMapTable;
}

bool TypeManager::ModuleInfoRow::HasEndPointer()
{
    return Flags & (int32_t)ModuleInfoFlags::HasEndPointer;
}

int TypeManager::ModuleInfoRow::GetLength()
{
    if (HasEndPointer())
    {
        return (int)((UInt8*)End - (UInt8*)Start);
    }
    else
    {
        return sizeof(void*);
    }
}

void TypeManager::EnumStaticGCRefsBlock(void * pfnCallback, void * pvCallbackData, StaticGcDesc* pStaticGcInfo)
{
    if (pStaticGcInfo == NULL)
        return;

    for (UInt32 idxSeries = 0; idxSeries < pStaticGcInfo->m_numSeries; idxSeries++)
    {
        PTR_StaticGcDescGCSeries pSeries = dac_cast<PTR_StaticGcDescGCSeries>(dac_cast<TADDR>(pStaticGcInfo) +
            offsetof(StaticGcDesc, m_series) + (idxSeries * sizeof(StaticGcDesc::GCSeries)));

        // The m_startOffset field is really 32-bit relocation (IMAGE_REL_BASED_RELPTR32) to the GC static base of the type
        // the GCSeries is describing for. This makes it tolerable to the symbol sorting that the linker conducts.
        PTR_RtuObjectRef    pRefLocation = dac_cast<PTR_RtuObjectRef>(dac_cast<PTR_UInt8>(&pSeries->m_startOffset) + (Int32)pSeries->m_startOffset);
        UInt32              numObjects = pSeries->m_size;

        RedhawkGCInterface::BulkEnumGcObjRef(pRefLocation, numObjects, pfnCallback, pvCallbackData);
    }
}

void TypeManager::EnumThreadStaticGCRefsBlock(void * pfnCallback, void * pvCallbackData, StaticGcDesc* pStaticGcInfo, UInt8* pbThreadStaticData)
{
    if (pStaticGcInfo == NULL)
        return;

    for (UInt32 idxSeries = 0; idxSeries < pStaticGcInfo->m_numSeries; idxSeries++)
    {
        PTR_StaticGcDescGCSeries pSeries = dac_cast<PTR_StaticGcDescGCSeries>(dac_cast<TADDR>(pStaticGcInfo) +
            offsetof(StaticGcDesc, m_series) + (idxSeries * sizeof(StaticGcDesc::GCSeries)));

        // The m_startOffset field is really a 32-bit relocation (IMAGE_REL_SECREL) to the TLS section.
        UInt8* pTlsObject = pbThreadStaticData + pSeries->m_startOffset;
        PTR_RtuObjectRef    pRefLocation = dac_cast<PTR_RtuObjectRef>(pTlsObject);
        UInt32              numObjects = pSeries->m_size;

        RedhawkGCInterface::BulkEnumGcObjRef(pRefLocation, numObjects, pfnCallback, pvCallbackData);
    }
}

void TypeManager::EnumStaticGCRefs(void * pfnCallback, void * pvCallbackData)
{
    // Regular statics.
    EnumStaticGCRefsBlock(pfnCallback, pvCallbackData, m_pStaticsGCInfo);

    // Thread local statics.
    if (m_pThreadStaticsGCInfo != NULL)
    {
        FOREACH_THREAD(pThread)
        {
            // To calculate the address of the data for each thread's TLS fields we need two values:
            //  1) The TLS slot index allocated for this module by the OS loader. We keep a pointer to this
            //     value in the module header.
            //  2) The offset into the TLS block at which managed data begins.
            EnumThreadStaticGCRefsBlock(pfnCallback, pvCallbackData, m_pThreadStaticsGCInfo,
                dac_cast<UInt8*>(pThread->GetThreadLocalStorage(*m_pTlsIndex, m_managedTlsStartOffset)));
        }
        END_FOREACH_THREAD
    }
}

HANDLE TypeManager::GetOsModuleHandle()
{
    return m_osModule;
}

bool TypeManagerHandle::IsTypeManager()
{
#if !CORERT
    if (((uintptr_t)_value & 1) == 0)
        return false;
#endif

    return true;
}

TypeManager* TypeManagerHandle::AsTypeManager()
{
    ASSERT(IsTypeManager());
    return (TypeManager*)(((uint8_t *)_value) - 1);
}

HANDLE TypeManagerHandle::AsOsModule()
{
    ASSERT(!IsTypeManager());
    return (HANDLE)_value;
}

<|endoftext|>


<commit_before>// Copyright (C) 2012 Jérôme Leclercq
// This file is part of the "Nazara Engine - Core module"
// For conditions of distribution and use, see copyright notice in Config.hpp

#include <Nazara/Core/Posix/FileImpl.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Core/Win32/Time.hpp>
#include <Nazara/Core/Debug.hpp>
#include <stdio.h>
#include <unistd.h>
#include <time.h>

NzFileImpl::NzFileImpl(const NzFile* parent) :
m_endOfFile(false),
m_endOfFileUpdated(true)
{
	NazaraUnused(parent);
}

void NzFileImpl::Close()
{
	close(m_fileDescriptor);
}

bool NzFileImpl::EndOfFile() const
{
	if (!m_endOfFileUpdated)
	{
		stat64 fileSize;
		if (fstat64(m_handle, &fileSize) == -1)
			fileSize.st_size = 0;

		m_endOfFile = (GetCursorPos() >= static_cast<nzUInt64>(fileSize.st_size));
		m_endOfFileUpdated = true;
	}

	return m_endOfFile;
}

void NzFileImpl::Flush()
{
	if (fsync(m_fileDescriptor) == -1)
		NazaraError("Unable to flush file: " + NzGetLastSystemError());
}

nzUInt64 NzFileImpl::GetCursorPos() const
{
    off64_t position = lseek64(m_fileDescriptor, 0, SEEK_CUR);
	return static_cast<nzUInt64>(position);
}

bool NzFileImpl::Open(const NzString& filePath, unsigned int mode)
{
    int flags;
    mode_t permissions = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;

	if (mode & NzFile::ReadOnly)
		flags = O_RDONLY;

	else if (mode & NzFile::ReadWrite)
	{
	    flags = O_CREAT | O_RDWR;

		if (mode & NzFile::Append)
			flags |= O_APPEND;

		if (mode & NzFile::Truncate)
            flags |= O_TRUNC;

	}
	else if (mode & NzFile::WriteOnly)
	{
	    flags = O_CREAT | O_WRONLY;

		if (mode & NzFile::Append)
			flags |= O_APPEND;

		if (mode & NzFile::Truncate)
			flags |= O_TRUNC;
	}
	else
		return false;

// TODO: lock
//	if ((mode & NzFile::Lock) == 0)
//		shareMode |= FILE_SHARE_WRITE;

	const char* path = filePath.GetConstBuffer();

	m_fileDescriptor = open64(path, flags, permissions);
    return m_fileDescriptor != -1;
}

std::size_t NzFileImpl::Read(void* buffer, std::size_t size)
{
    ssize_t bytes;
	if ((bytes = read(m_fileDescriptor, buffer, size)) != -1)
	{
		m_endOfFile = (static_cast<std::size_t>(bytes) != size);
		m_endOfFileUpdated = true;

		return static_cast<std::size_t>(bytes);
	}
	else
		return 0;
}

bool NzFileImpl::SetCursorPos(NzFile::CursorPosition pos, nzInt64 offset)
{
	int moveMethod;
	switch (pos)
	{
		case NzFile::AtBegin:
			moveMethod = SEEK_SET;
			break;

		case NzFile::AtCurrent:
			moveMethod = SEEK_CUR;
			break;

		case NzFile::AtEnd:
			moveMethod = SEEK_END;
			break;

		default:
			NazaraInternalError("Cursor position not handled (0x" + NzString::Number(pos, 16) + ')');
			return false;
	}

	m_endOfFileUpdated = false;

    return lseek64(m_fileDescriptor, offset, moveMethod) != -1;
}

std::size_t NzFileImpl::Write(const void* buffer, std::size_t size)
{
    lockf64(m_fileDescriptor, F_LOCK, size);
    ssize_t written = write(m_fileDescriptor, buffer, size);
    lockf64(m_fileDescriptor, F_ULOCK, size);

	m_endOfFileUpdated = false;

	return written;
}

bool NzFileImpl::Copy(const NzString& sourcePath, const NzString& targetPath)
{
    int fd1 = open64(sourcePath.GetConstBuffer(), O_RDONLY);
    if (fd1 == -1)
    {
        NazaraError("Fail to open input file (" + sourcePath + "): " + NzGetLastSystemError());
        return false;
    }

    mode_t permissions; // TODO : get permission from first file
    int fd2 = open64(sourcePath.GetConstBuffer(), O_WRONLY | O_TRUNC, permissions);
    if (fd2 == -1)
    {
        NazaraError("Fail to open output file (" + targetPath + "): " + NzGetLastSystemError()); // TODO: more info ?
        return false;
    }

    char buffer[512];
    ssize_t bytes;
    do
    {
        bytes = read(fd1,buffer,512);
        if (bytes == -1)
        {
            close(fd1);
            close(fd2);
            NazaraError("An error occured from copy : " + NzGetLastSystemError());
            return false;
        }
        write(fd2,buffer,bytes);

    } while (bytes == 512);
}

bool NzFileImpl::Delete(const NzString& filePath)
{
	const char* path = filePath.GetConstBuffer();
	bool success = unlink(path) != -1;

	if (success)
		return true;
	else
	{
		NazaraError("Failed to delete file (" + filePath + "): " + NzGetLastSystemError());
		return false;
	}
}

bool NzFileImpl::Exists(const NzString& filePath)
{
	char* path = filePath.GetConstBuffer();
    if (access(path, F_OK) != -1)
        return true;
    return false;
}

time_t NzFileImpl::GetCreationTime(const NzString& filePath)
{
    struct stat64 stats;
    stat64(filePath.GetConstBuffer(), &stats);

	return NzFileTimeToTime(std::ctime(&stats.st_ctim)); // not sure ?
}

time_t NzFileImpl::GetLastAccessTime(const NzString& filePath)
{
	struct stat64 stats;
    stat64(filePath.GetConstBuffer(), &stats);

    return NzFileTimeToTime(std::ctime(&stats.st_atim));
}

time_t NzFileImpl::GetLastWriteTime(const NzString& filePath)
{
	struct stat64 stats;
    stat64(filePath.GetConstBuffer(), &stats);

    return NzFileTimeToTime(std::ctime(&stats.st_mtim));
}

nzUInt64 NzFileImpl::GetSize(const NzString& filePath)
{
	struct stat64 stats;
    stat64(filePath.GetConstBuffer(), &stats);

    return static_cast<nzUInt64>(stats.st_size);
}

bool NzFileImpl::Rename(const NzString& sourcePath, const NzString& targetPath)
{
	const char* path = sourcePath.GetConstBuffer();
	const char* newPath = targetPath.GetConstBuffer();

	bool success = std::rename(path, newPath) != -1;

	if (success)
		return true;
	else
	{
		NazaraError("Unable to rename file: " + NzGetLastSystemError());
		return false;
	}
}
<commit_msg>Forgot to close files after copy or error.<commit_after>// Copyright (C) 2012 Jérôme Leclercq
// This file is part of the "Nazara Engine - Core module"
// For conditions of distribution and use, see copyright notice in Config.hpp

#include <Nazara/Core/Posix/FileImpl.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Core/Win32/Time.hpp>
#include <Nazara/Core/Debug.hpp>
#include <stdio.h>
#include <unistd.h>
#include <time.h>

NzFileImpl::NzFileImpl(const NzFile* parent) :
m_endOfFile(false),
m_endOfFileUpdated(true)
{
	NazaraUnused(parent);
}

void NzFileImpl::Close()
{
	close(m_fileDescriptor);
}

bool NzFileImpl::EndOfFile() const
{
	if (!m_endOfFileUpdated)
	{
		stat64 fileSize;
		if (fstat64(m_handle, &fileSize) == -1)
			fileSize.st_size = 0;

		m_endOfFile = (GetCursorPos() >= static_cast<nzUInt64>(fileSize.st_size));
		m_endOfFileUpdated = true;
	}

	return m_endOfFile;
}

void NzFileImpl::Flush()
{
	if (fsync(m_fileDescriptor) == -1)
		NazaraError("Unable to flush file: " + NzGetLastSystemError());
}

nzUInt64 NzFileImpl::GetCursorPos() const
{
    off64_t position = lseek64(m_fileDescriptor, 0, SEEK_CUR);
	return static_cast<nzUInt64>(position);
}

bool NzFileImpl::Open(const NzString& filePath, unsigned int mode)
{
    int flags;
    mode_t permissions = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;

	if (mode & NzFile::ReadOnly)
		flags = O_RDONLY;

	else if (mode & NzFile::ReadWrite)
	{
	    flags = O_CREAT | O_RDWR;

		if (mode & NzFile::Append)
			flags |= O_APPEND;

		if (mode & NzFile::Truncate)
            flags |= O_TRUNC;

	}
	else if (mode & NzFile::WriteOnly)
	{
	    flags = O_CREAT | O_WRONLY;

		if (mode & NzFile::Append)
			flags |= O_APPEND;

		if (mode & NzFile::Truncate)
			flags |= O_TRUNC;
	}
	else
		return false;

// TODO: lock
//	if ((mode & NzFile::Lock) == 0)
//		shareMode |= FILE_SHARE_WRITE;

	const char* path = filePath.GetConstBuffer();

	m_fileDescriptor = open64(path, flags, permissions);
    return m_fileDescriptor != -1;
}

std::size_t NzFileImpl::Read(void* buffer, std::size_t size)
{
    ssize_t bytes;
	if ((bytes = read(m_fileDescriptor, buffer, size)) != -1)
	{
		m_endOfFile = (static_cast<std::size_t>(bytes) != size);
		m_endOfFileUpdated = true;

		return static_cast<std::size_t>(bytes);
	}
	else
		return 0;
}

bool NzFileImpl::SetCursorPos(NzFile::CursorPosition pos, nzInt64 offset)
{
	int moveMethod;
	switch (pos)
	{
		case NzFile::AtBegin:
			moveMethod = SEEK_SET;
			break;

		case NzFile::AtCurrent:
			moveMethod = SEEK_CUR;
			break;

		case NzFile::AtEnd:
			moveMethod = SEEK_END;
			break;

		default:
			NazaraInternalError("Cursor position not handled (0x" + NzString::Number(pos, 16) + ')');
			return false;
	}

	m_endOfFileUpdated = false;

    return lseek64(m_fileDescriptor, offset, moveMethod) != -1;
}

std::size_t NzFileImpl::Write(const void* buffer, std::size_t size)
{
    lockf64(m_fileDescriptor, F_LOCK, size);
    ssize_t written = write(m_fileDescriptor, buffer, size);
    lockf64(m_fileDescriptor, F_ULOCK, size);

	m_endOfFileUpdated = false;

	return written;
}

bool NzFileImpl::Copy(const NzString& sourcePath, const NzString& targetPath)
{
    int fd1 = open64(sourcePath.GetConstBuffer(), O_RDONLY);
    if (fd1 == -1)
    {
        NazaraError("Fail to open input file (" + sourcePath + "): " + NzGetLastSystemError());
        return false;
    }

    mode_t permissions; // TODO : get permission from first file
    int fd2 = open64(sourcePath.GetConstBuffer(), O_WRONLY | O_TRUNC, permissions);
    if (fd2 == -1)
    {
        NazaraError("Fail to open output file (" + targetPath + "): " + NzGetLastSystemError()); // TODO: more info ?
        close(fd1);
        return false;
    }

    char buffer[512];
    ssize_t bytes;
    do
    {
        bytes = read(fd1,buffer,512);
        if (bytes == -1)
        {
            close(fd1);
            close(fd2);
            NazaraError("An error occured from copy : " + NzGetLastSystemError());
            return false;
        }
        write(fd2,buffer,bytes);

    } while (bytes == 512);

    close(fd1);
    close(fd2);
}

bool NzFileImpl::Delete(const NzString& filePath)
{
	const char* path = filePath.GetConstBuffer();
	bool success = unlink(path) != -1;

	if (success)
		return true;
	else
	{
		NazaraError("Failed to delete file (" + filePath + "): " + NzGetLastSystemError());
		return false;
	}
}

bool NzFileImpl::Exists(const NzString& filePath)
{
	char* path = filePath.GetConstBuffer();
    if (access(path, F_OK) != -1)
        return true;
    return false;
}

time_t NzFileImpl::GetCreationTime(const NzString& filePath)
{
    struct stat64 stats;
    stat64(filePath.GetConstBuffer(), &stats);

	return NzFileTimeToTime(std::ctime(&stats.st_ctim)); // not sure ?
}

time_t NzFileImpl::GetLastAccessTime(const NzString& filePath)
{
	struct stat64 stats;
    stat64(filePath.GetConstBuffer(), &stats);

    return NzFileTimeToTime(std::ctime(&stats.st_atim));
}

time_t NzFileImpl::GetLastWriteTime(const NzString& filePath)
{
	struct stat64 stats;
    stat64(filePath.GetConstBuffer(), &stats);

    return NzFileTimeToTime(std::ctime(&stats.st_mtim));
}

nzUInt64 NzFileImpl::GetSize(const NzString& filePath)
{
	struct stat64 stats;
    stat64(filePath.GetConstBuffer(), &stats);

    return static_cast<nzUInt64>(stats.st_size);
}

bool NzFileImpl::Rename(const NzString& sourcePath, const NzString& targetPath)
{
	const char* path = sourcePath.GetConstBuffer();
	const char* newPath = targetPath.GetConstBuffer();

	bool success = std::rename(path, newPath) != -1;

	if (success)
		return true;
	else
	{
		NazaraError("Unable to rename file: " + NzGetLastSystemError());
		return false;
	}
}
<|endoftext|>


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

#include "vvMainWindow.h"
#include "ui_vvMainWindow.h"
#include "vvLoadDataReaction.h"
#include "vvToggleSpreadSheetReaction.h"

#include <vtkSMProxyManager.h>
#include <vtkSMSessionProxyManager.h>

#include "pqObjectPickingBehavior.h"
#include <pqActiveObjects.h>
#include <pqApplicationCore.h>
#include <pqAutoLoadPluginXMLBehavior.h>
#include <pqCommandLineOptionsBehavior.h>
#include <pqCrashRecoveryBehavior.h>
#include <pqDataTimeStepBehavior.h>
#include <pqDefaultViewBehavior.h>
#include <pqApplyBehavior.h>
#include <pqInterfaceTracker.h>
#include <pqObjectBuilder.h>
#include <pqPersistentMainWindowStateBehavior.h>
#include <pqPythonShellReaction.h>
#include <pqQtMessageHandlerBehavior.h>
#include <pqRenderView.h>
#include <pqRenderViewSelectionReaction.h>
#include <pqDeleteReaction.h>
#include <pqHelpReaction.h>
#include <pqServer.h>
#include <pqSettings.h>
#include <pqSpreadSheetView.h>
#include <pqSpreadSheetViewDecorator.h>
#include <pqSpreadSheetVisibilityBehavior.h>
#include <pqStandardPropertyWidgetInterface.h>
#include <pqStandardViewFrameActionsImplementation.h>
#include <pqVelodyneManager.h>
#include <pqParaViewMenuBuilders.h>
#include <pqTabbedMultiViewWidget.h>
#include <vtkPVPlugin.h>
#include <vtkSMPropertyHelper.h>

#include <QLabel>
#include <QSplitter>
#include <QToolBar>
#include <QShortcut>
#include <QDockWidget>
#include <QMenuBar>
#include <QMenu>

#include <cassert>
#include <iostream>
#include <sstream>

#include "vvConfig.h"

// Declare the plugin to load.
PV_PLUGIN_IMPORT_INIT(VelodyneHDLPlugin);
PV_PLUGIN_IMPORT_INIT(PythonQtPlugin);

class vvMainWindow::pqInternals
{
public:
  pqInternals(vvMainWindow* window)
    : Ui()
    , MainView(0)
  {
    this->Ui.setupUi(window);
    this->paraviewInit(window);
    this->setupUi(window);

    QActionGroup* dualReturnFilterActions = new QActionGroup(window);
    dualReturnFilterActions->addAction(this->Ui.actionDualReturnModeDual);
    dualReturnFilterActions->addAction(this->Ui.actionDualReturnDistanceNear);
    dualReturnFilterActions->addAction(this->Ui.actionDualReturnDistanceFar);
    dualReturnFilterActions->addAction(this->Ui.actionDualReturnIntensityHigh);
    dualReturnFilterActions->addAction(this->Ui.actionDualReturnIntensityLow);

    window->show();
    window->raise();
    window->activateWindow();
  }
  Ui::vvMainWindow Ui;
  pqRenderView* MainView;

private:
  void paraviewInit(vvMainWindow* window)
  {
    pqApplicationCore* core = pqApplicationCore::instance();

    // Register ParaView interfaces.
    pqInterfaceTracker* pgm = core->interfaceTracker();
    //    pgm->addInterface(new pqStandardViewModules(pgm));
    pgm->addInterface(new pqStandardPropertyWidgetInterface(pgm));
    pgm->addInterface(new pqStandardViewFrameActionsImplementation(pgm));

    // Define application behaviors.
    new pqQtMessageHandlerBehavior(window);
    new pqDataTimeStepBehavior(window);
    new pqSpreadSheetVisibilityBehavior(window);
    new pqObjectPickingBehavior(window);
    //    new pqDefaultViewBehavior(window);
    new pqCrashRecoveryBehavior(window);
    new pqAutoLoadPluginXMLBehavior(window);
    new pqCommandLineOptionsBehavior(window);
    pqApplyBehavior* applyBehaviors = new pqApplyBehavior(window);

    // Check if the settings are well formed i.e. if an OriginalMainWindow
    // state was previously saved. If not, we don't want to automatically
    // restore the settings state nor save it on quitting VeloView.
    // An OriginalMainWindow state will be force saved once the UI is completly
    // set up.
    pqSettings* const settings = pqApplicationCore::instance()->settings();
    bool shouldClearSettings = false;
    QStringList keys = settings->allKeys();

    if (keys.size() == 0)
    {
      // There were no settings before, let's save the current state as
      // OriginalMainWindow state
      shouldClearSettings = true;
    }
    else
    {
      // Checks if the existing settings are well formed and if not, clear them.
      // An original MainWindow state will be force saved later once the UI is
      // entirely set up
      for (int keyIndex = 0; keyIndex < keys.size(); ++keyIndex)
      {
        if (keys[keyIndex].contains("OriginalMainWindow"))
        {
          shouldClearSettings = true;
          break;
        }
      }
    }

    if (shouldClearSettings)
    {
      new pqPersistentMainWindowStateBehavior(window);
    }
    else
    {
      if (keys.size() > 0)
      {
        vtkGenericWarningMacro("Settings weren't set correctly. Clearing settings.")
      }

      // As pqPersistentMainWindowStateBehavior is not created right now,
      // we can clear the settings as the current bad state won't be saved on
      // closing VeloView
      settings->clear();
    }

    // Connect to builtin server.
    pqObjectBuilder* builder = core->getObjectBuilder();
    pqServer* server = builder->createServer(pqServerResource("builtin:"));
    pqActiveObjects::instance().setActiveServer(server);

    // Set default render view settings
    vtkSMSessionProxyManager* pxm =
      vtkSMProxyManager::GetProxyManager()->GetActiveSessionProxyManager();
    vtkSMProxy* renderviewsettings = pxm->GetProxy("RenderViewSettings");
    assert(renderviewsettings);

    vtkSMPropertyHelper(renderviewsettings, "ResolveCoincidentTopology").Set(0);

    // Create a overhead view
    pqView* overheadView = builder->createView(pqRenderView::renderViewType(), server);
    overheadView->getProxy()->UpdateVTKObjects();
    this->Ui.overheadViewDock->setWidget(overheadView->widget());
    new vvToggleSpreadSheetReaction(this->Ui.actionOverheadView, overheadView);

    // create SpreadSheet
    pqSpreadSheetView* spreadsheetView = qobject_cast<pqSpreadSheetView*>
        (builder->createView(pqSpreadSheetView::spreadsheetViewType(), server));
    assert(spreadsheetView);
    this->Ui.spreadSheetDock->setWidget(spreadsheetView->widget());
    spreadsheetView->getProxy()->UpdateVTKObjects();
    new vvToggleSpreadSheetReaction(this->Ui.actionSpreadsheet, spreadsheetView);
    pqSpreadSheetViewDecorator* dec = new pqSpreadSheetViewDecorator(spreadsheetView);
    dec->setPrecision(3);
    dec->setFixedRepresentation(true);

    // Create a default view.
    // Due to our old version of paraview, it's not possible to create a view in
    // detachedFromLayout mode. This feature was introduce by paraview 5.2.
    // So we need to create the pqTabbedMultiViewWidget after creating all other view but
    // before creating the main view, in order to have the main view in the pqTabbedMultiViewWidget.
    // This is because all view created are register by the pqTabbedMultiViewWidget.
    if (ENABLE_DEV_MODE_UI_VAR)
    {
      pqTabbedMultiViewWidget* mv = new pqTabbedMultiViewWidget;
      window->setCentralWidget(mv);
    }
    pqRenderView* view =
      qobject_cast<pqRenderView*>(builder->createView(pqRenderView::renderViewType(), server));
    assert(view);
    this->MainView = view;

    vtkSMPropertyHelper(view->getProxy(), "CenterAxesVisibility").Set(0);
    double bgcolor[3] = { 0, 0, 0 };
    vtkSMPropertyHelper(view->getProxy(), "Background").Set(bgcolor, 3);
    // MultiSamples doesn't work, we need to set that up before registering the proxy.
    // vtkSMPropertyHelper(view->getProxy(),"MultiSamples").Set(1);
    view->getProxy()->UpdateVTKObjects();
   if (!ENABLE_DEV_MODE_UI_VAR)
   {
     window->setCentralWidget(view->widget());
   }

    // properties panel
    // connect apply button
    applyBehaviors->registerPanel(this->Ui.propertiesPanel);
    // Enable help from the properties panel.
    QObject::connect(this->Ui.propertiesPanelDock->widget(),
      SIGNAL(helpRequested(const QString&, const QString&)),
      window, SLOT(showHelpForProxy(const QString&, const QString&)));

    /// hook delete to pqDeleteReaction.
    QAction* tempDeleteAction = new QAction(window);
    pqDeleteReaction* handler = new pqDeleteReaction(tempDeleteAction);
    handler->connect(this->Ui.propertiesPanelDock->widget(),
      SIGNAL(deleteRequested(pqPipelineSource*)),
      SLOT(deleteSource(pqPipelineSource*)));

    // specify how corner are occupied by the dockable widget
    window->setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea);
    window->setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
    window->setCorner(Qt::TopRightCorner, Qt::RightDockWidgetArea);
    window->setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea);

    // organize dockable widget in tab
    window->setTabPosition(Qt::LeftDockWidgetArea, QTabWidget::North);
    window->tabifyDockWidget(this->Ui.propertiesPanelDock, this->Ui.colorMapEditorDock);
    window->tabifyDockWidget(this->Ui.spreadSheetDock, this->Ui.informationDock);
    window->tabifyDockWidget(this->Ui.spreadSheetDock, this->Ui.memoryInspectorDock);

    // Setup the View menu. This must be setup after all toolbars and dockwidgets
    // have been created.
    pqParaViewMenuBuilders::buildViewMenu(*this->Ui.menuViews, *window);

    if (ENABLE_DEV_MODE_UI_VAR)
    {
      /// If you want to automatically add toolbars for sources as requested in the
      /// configuration pass in a non-null main window.
      QMenu* sourceMenu = window->menuBar()->addMenu(tr("&Sources"));
      pqParaViewMenuBuilders::buildSourcesMenu(*sourceMenu, nullptr);

      /// If you want to automatically add toolbars for filters as requested in the
      /// configuration pass in a non-null main window.
      QMenu* filterMenu = window->menuBar()->addMenu(tr("&Filters"));
      pqParaViewMenuBuilders:: buildFiltersMenu(*filterMenu, nullptr);
    }

      // add 'ctrl+space' shortcut for quickLaunch
      QShortcut *ctrlSpace = new QShortcut(Qt::CTRL + Qt::Key_Space, window);
      QObject::connect(ctrlSpace, SIGNAL(activated()), pqApplicationCore::instance(), SLOT(quickLaunch()));

    pqActiveObjects::instance().setActiveView(view);
  }

  //-----------------------------------------------------------------------------
  void setupUi(vvMainWindow* window)
  {
    new pqRenderViewSelectionReaction(this->Ui.actionSelect_Visible_Points, this->MainView,
      pqRenderViewSelectionReaction::SELECT_SURFACE_POINTS);
    new pqRenderViewSelectionReaction(this->Ui.actionSelect_All_Points, this->MainView,
      pqRenderViewSelectionReaction::SELECT_FRUSTUM_POINTS);

    new pqPythonShellReaction(this->Ui.actionPython_Console);

    pqVelodyneManager::instance()->setup();

    pqSettings* const settings = pqApplicationCore::instance()->settings();
    const QVariant& gridVisible =
      settings->value("VelodyneHDLPlugin/MeasurementGrid/Visibility", true);
    this->Ui.actionMeasurement_Grid->setChecked(gridVisible.toBool());

    this->Ui.actionEnableCrashAnalysis->setChecked(
      settings
        ->value("VelodyneHDLPlugin/MainWindow/EnableCrashAnalysis",
          this->Ui.actionEnableCrashAnalysis->isChecked())
        .toBool());

    new vvLoadDataReaction(this->Ui.actionOpenPcap, false);
    new vvLoadDataReaction(this->Ui.actionOpenApplanix, true);

    connect(this->Ui.actionOpen_Sensor_Stream, SIGNAL(triggered()), pqVelodyneManager::instance(),
      SLOT(onOpenSensor()));

    connect(this->Ui.actionMeasurement_Grid, SIGNAL(toggled(bool)), pqVelodyneManager::instance(),
      SLOT(onMeasurementGrid(bool)));

    connect(this->Ui.actionEnableCrashAnalysis, SIGNAL(toggled(bool)),
      pqVelodyneManager::instance(), SLOT(onEnableCrashAnalysis(bool)));

    connect(this->Ui.actionResetConfigurationFile, SIGNAL(triggered()),
      pqVelodyneManager::instance(), SLOT(onResetCalibrationFile()));

    connect(this->Ui.actionShowErrorDialog, SIGNAL(triggered()), pqApplicationCore::instance(),
      SLOT(showOutputWindow()));
  }
};

//-----------------------------------------------------------------------------
vvMainWindow::vvMainWindow()
  : Internals(new vvMainWindow::pqInternals(this))
{
  pqApplicationCore::instance()->registerManager(
    "COLOR_EDITOR_PANEL", this->Internals->Ui.colorMapEditorDock);
  this->Internals->Ui.colorMapEditorDock->hide();

  PV_PLUGIN_IMPORT(VelodyneHDLPlugin);
  PV_PLUGIN_IMPORT(PythonQtPlugin);

  // Branding
  std::stringstream ss;
  ss << "Reset " << SOFTWARE_NAME << " settings";
  QString text = QString(ss.str().c_str());
  this->Internals->Ui.actionResetConfigurationFile->setText(text);
  ss.str("");
  ss.clear();

  ss << "This will reset all " << SOFTWARE_NAME << " settings by default";
  text = QString(ss.str().c_str());
  this->Internals->Ui.actionResetConfigurationFile->setIconText(text);
  ss.str("");
  ss.clear();

  ss << "About " << SOFTWARE_NAME;
  text = QString(ss.str().c_str());
  this->Internals->Ui.actionAbout_VeloView->setText(text);
  ss.str("");
  ss.clear();

  ss << SOFTWARE_NAME << " Developer Guide";
  text = QString(ss.str().c_str());
  this->Internals->Ui.actionVeloViewDeveloperGuide->setText(text);
  ss.str("");
  ss.clear();

  ss << SOFTWARE_NAME << " User Guide";
  text = QString(ss.str().c_str());
  this->Internals->Ui.actionVeloViewUserGuide->setText(text);
}

//-----------------------------------------------------------------------------
vvMainWindow::~vvMainWindow()
{
  delete this->Internals;
  this->Internals = NULL;
}

//-----------------------------------------------------------------------------
void vvMainWindow::showHelpForProxy(const QString& groupname, const
  QString& proxyname)
{
  pqHelpReaction::showProxyHelp(groupname, proxyname);
}
<commit_msg>hide dock by default in the ui<commit_after>// Copyright 2013 Velodyne Acoustics, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Copyright 2013 Velodyne Acoustics, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "vvMainWindow.h"
#include "ui_vvMainWindow.h"
#include "vvLoadDataReaction.h"
#include "vvToggleSpreadSheetReaction.h"

#include <vtkSMProxyManager.h>
#include <vtkSMSessionProxyManager.h>

#include "pqObjectPickingBehavior.h"
#include <pqActiveObjects.h>
#include <pqApplicationCore.h>
#include <pqAutoLoadPluginXMLBehavior.h>
#include <pqCommandLineOptionsBehavior.h>
#include <pqCrashRecoveryBehavior.h>
#include <pqDataTimeStepBehavior.h>
#include <pqDefaultViewBehavior.h>
#include <pqApplyBehavior.h>
#include <pqInterfaceTracker.h>
#include <pqObjectBuilder.h>
#include <pqPersistentMainWindowStateBehavior.h>
#include <pqPythonShellReaction.h>
#include <pqQtMessageHandlerBehavior.h>
#include <pqRenderView.h>
#include <pqRenderViewSelectionReaction.h>
#include <pqDeleteReaction.h>
#include <pqHelpReaction.h>
#include <pqServer.h>
#include <pqSettings.h>
#include <pqSpreadSheetView.h>
#include <pqSpreadSheetViewDecorator.h>
#include <pqSpreadSheetVisibilityBehavior.h>
#include <pqStandardPropertyWidgetInterface.h>
#include <pqStandardViewFrameActionsImplementation.h>
#include <pqVelodyneManager.h>
#include <pqParaViewMenuBuilders.h>
#include <pqTabbedMultiViewWidget.h>
#include <vtkPVPlugin.h>
#include <vtkSMPropertyHelper.h>

#include <QLabel>
#include <QSplitter>
#include <QToolBar>
#include <QShortcut>
#include <QDockWidget>
#include <QMenuBar>
#include <QMenu>

#include <cassert>
#include <iostream>
#include <sstream>

#include "vvConfig.h"

// Declare the plugin to load.
PV_PLUGIN_IMPORT_INIT(VelodyneHDLPlugin);
PV_PLUGIN_IMPORT_INIT(PythonQtPlugin);

class vvMainWindow::pqInternals
{
public:
  pqInternals(vvMainWindow* window)
    : Ui()
    , MainView(0)
  {
    this->Ui.setupUi(window);
    this->paraviewInit(window);
    this->setupUi(window);

    QActionGroup* dualReturnFilterActions = new QActionGroup(window);
    dualReturnFilterActions->addAction(this->Ui.actionDualReturnModeDual);
    dualReturnFilterActions->addAction(this->Ui.actionDualReturnDistanceNear);
    dualReturnFilterActions->addAction(this->Ui.actionDualReturnDistanceFar);
    dualReturnFilterActions->addAction(this->Ui.actionDualReturnIntensityHigh);
    dualReturnFilterActions->addAction(this->Ui.actionDualReturnIntensityLow);

    window->show();
    window->raise();
    window->activateWindow();
  }
  Ui::vvMainWindow Ui;
  pqRenderView* MainView;

private:
  void paraviewInit(vvMainWindow* window)
  {
    pqApplicationCore* core = pqApplicationCore::instance();

    // Register ParaView interfaces.
    pqInterfaceTracker* pgm = core->interfaceTracker();
    //    pgm->addInterface(new pqStandardViewModules(pgm));
    pgm->addInterface(new pqStandardPropertyWidgetInterface(pgm));
    pgm->addInterface(new pqStandardViewFrameActionsImplementation(pgm));

    // Define application behaviors.
    new pqQtMessageHandlerBehavior(window);
    new pqDataTimeStepBehavior(window);
    new pqSpreadSheetVisibilityBehavior(window);
    new pqObjectPickingBehavior(window);
    //    new pqDefaultViewBehavior(window);
    new pqCrashRecoveryBehavior(window);
    new pqAutoLoadPluginXMLBehavior(window);
    new pqCommandLineOptionsBehavior(window);
    pqApplyBehavior* applyBehaviors = new pqApplyBehavior(window);

    // Check if the settings are well formed i.e. if an OriginalMainWindow
    // state was previously saved. If not, we don't want to automatically
    // restore the settings state nor save it on quitting VeloView.
    // An OriginalMainWindow state will be force saved once the UI is completly
    // set up.
    pqSettings* const settings = pqApplicationCore::instance()->settings();
    bool shouldClearSettings = false;
    QStringList keys = settings->allKeys();

    if (keys.size() == 0)
    {
      // There were no settings before, let's save the current state as
      // OriginalMainWindow state
      shouldClearSettings = true;
    }
    else
    {
      // Checks if the existing settings are well formed and if not, clear them.
      // An original MainWindow state will be force saved later once the UI is
      // entirely set up
      for (int keyIndex = 0; keyIndex < keys.size(); ++keyIndex)
      {
        if (keys[keyIndex].contains("OriginalMainWindow"))
        {
          shouldClearSettings = true;
          break;
        }
      }
    }

    if (shouldClearSettings)
    {
      new pqPersistentMainWindowStateBehavior(window);
    }
    else
    {
      if (keys.size() > 0)
      {
        vtkGenericWarningMacro("Settings weren't set correctly. Clearing settings.")
      }

      // As pqPersistentMainWindowStateBehavior is not created right now,
      // we can clear the settings as the current bad state won't be saved on
      // closing VeloView
      settings->clear();
    }

    // Connect to builtin server.
    pqObjectBuilder* builder = core->getObjectBuilder();
    pqServer* server = builder->createServer(pqServerResource("builtin:"));
    pqActiveObjects::instance().setActiveServer(server);

    // Set default render view settings
    vtkSMSessionProxyManager* pxm =
      vtkSMProxyManager::GetProxyManager()->GetActiveSessionProxyManager();
    vtkSMProxy* renderviewsettings = pxm->GetProxy("RenderViewSettings");
    assert(renderviewsettings);

    vtkSMPropertyHelper(renderviewsettings, "ResolveCoincidentTopology").Set(0);

    // Create a overhead view
    pqView* overheadView = builder->createView(pqRenderView::renderViewType(), server);
    overheadView->getProxy()->UpdateVTKObjects();
    this->Ui.overheadViewDock->setWidget(overheadView->widget());
    new vvToggleSpreadSheetReaction(this->Ui.actionOverheadView, overheadView);

    // create SpreadSheet
    pqSpreadSheetView* spreadsheetView = qobject_cast<pqSpreadSheetView*>
        (builder->createView(pqSpreadSheetView::spreadsheetViewType(), server));
    assert(spreadsheetView);
    this->Ui.spreadSheetDock->setWidget(spreadsheetView->widget());
    spreadsheetView->getProxy()->UpdateVTKObjects();
    new vvToggleSpreadSheetReaction(this->Ui.actionSpreadsheet, spreadsheetView);
    pqSpreadSheetViewDecorator* dec = new pqSpreadSheetViewDecorator(spreadsheetView);
    dec->setPrecision(3);
    dec->setFixedRepresentation(true);

    // Create a default view.
    // Due to our old version of paraview, it's not possible to create a view in
    // detachedFromLayout mode. This feature was introduce by paraview 5.2.
    // So we need to create the pqTabbedMultiViewWidget after creating all other view but
    // before creating the main view, in order to have the main view in the pqTabbedMultiViewWidget.
    // This is because all view created are register by the pqTabbedMultiViewWidget.
    if (ENABLE_DEV_MODE_UI_VAR)
    {
      pqTabbedMultiViewWidget* mv = new pqTabbedMultiViewWidget;
      window->setCentralWidget(mv);
    }
    pqRenderView* view =
      qobject_cast<pqRenderView*>(builder->createView(pqRenderView::renderViewType(), server));
    assert(view);
    this->MainView = view;

    vtkSMPropertyHelper(view->getProxy(), "CenterAxesVisibility").Set(0);
    double bgcolor[3] = { 0, 0, 0 };
    vtkSMPropertyHelper(view->getProxy(), "Background").Set(bgcolor, 3);
    // MultiSamples doesn't work, we need to set that up before registering the proxy.
    // vtkSMPropertyHelper(view->getProxy(),"MultiSamples").Set(1);
    view->getProxy()->UpdateVTKObjects();
   if (!ENABLE_DEV_MODE_UI_VAR)
   {
     window->setCentralWidget(view->widget());
   }

    // properties panel
    // connect apply button
    applyBehaviors->registerPanel(this->Ui.propertiesPanel);
    // Enable help from the properties panel.
    QObject::connect(this->Ui.propertiesPanelDock->widget(),
      SIGNAL(helpRequested(const QString&, const QString&)),
      window, SLOT(showHelpForProxy(const QString&, const QString&)));

    /// hook delete to pqDeleteReaction.
    QAction* tempDeleteAction = new QAction(window);
    pqDeleteReaction* handler = new pqDeleteReaction(tempDeleteAction);
    handler->connect(this->Ui.propertiesPanelDock->widget(),
      SIGNAL(deleteRequested(pqPipelineSource*)),
      SLOT(deleteSource(pqPipelineSource*)));

    // specify how corner are occupied by the dockable widget
    window->setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea);
    window->setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
    window->setCorner(Qt::TopRightCorner, Qt::RightDockWidgetArea);
    window->setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea);

    // organize dockable widget in tab
    window->setTabPosition(Qt::LeftDockWidgetArea, QTabWidget::North);
    window->tabifyDockWidget(this->Ui.propertiesPanelDock, this->Ui.colorMapEditorDock);
    window->tabifyDockWidget(this->Ui.spreadSheetDock, this->Ui.informationDock);
    window->tabifyDockWidget(this->Ui.spreadSheetDock, this->Ui.memoryInspectorDock);

    // hide docker by default
    this->Ui.pipelineBrowserDock->hide();
    this->Ui.propertiesPanelDock->hide();
    this->Ui.colorMapEditorDock->hide();
    this->Ui.spreadSheetDock->hide();
    this->Ui.informationDock->hide();
    this->Ui.memoryInspectorDock->hide();

    // Setup the View menu. This must be setup after all toolbars and dockwidgets
    // have been created.
    pqParaViewMenuBuilders::buildViewMenu(*this->Ui.menuViews, *window);

    if (ENABLE_DEV_MODE_UI_VAR)
    {
      /// If you want to automatically add toolbars for sources as requested in the
      /// configuration pass in a non-null main window.
      QMenu* sourceMenu = window->menuBar()->addMenu(tr("&Sources"));
      pqParaViewMenuBuilders::buildSourcesMenu(*sourceMenu, nullptr);

      /// If you want to automatically add toolbars for filters as requested in the
      /// configuration pass in a non-null main window.
      QMenu* filterMenu = window->menuBar()->addMenu(tr("&Filters"));
      pqParaViewMenuBuilders:: buildFiltersMenu(*filterMenu, nullptr);
    }

      // add 'ctrl+space' shortcut for quickLaunch
      QShortcut *ctrlSpace = new QShortcut(Qt::CTRL + Qt::Key_Space, window);
      QObject::connect(ctrlSpace, SIGNAL(activated()), pqApplicationCore::instance(), SLOT(quickLaunch()));

    pqActiveObjects::instance().setActiveView(view);
  }

  //-----------------------------------------------------------------------------
  void setupUi(vvMainWindow* window)
  {
    new pqRenderViewSelectionReaction(this->Ui.actionSelect_Visible_Points, this->MainView,
      pqRenderViewSelectionReaction::SELECT_SURFACE_POINTS);
    new pqRenderViewSelectionReaction(this->Ui.actionSelect_All_Points, this->MainView,
      pqRenderViewSelectionReaction::SELECT_FRUSTUM_POINTS);

    new pqPythonShellReaction(this->Ui.actionPython_Console);

    pqVelodyneManager::instance()->setup();

    pqSettings* const settings = pqApplicationCore::instance()->settings();
    const QVariant& gridVisible =
      settings->value("VelodyneHDLPlugin/MeasurementGrid/Visibility", true);
    this->Ui.actionMeasurement_Grid->setChecked(gridVisible.toBool());

    this->Ui.actionEnableCrashAnalysis->setChecked(
      settings
        ->value("VelodyneHDLPlugin/MainWindow/EnableCrashAnalysis",
          this->Ui.actionEnableCrashAnalysis->isChecked())
        .toBool());

    new vvLoadDataReaction(this->Ui.actionOpenPcap, false);
    new vvLoadDataReaction(this->Ui.actionOpenApplanix, true);

    connect(this->Ui.actionOpen_Sensor_Stream, SIGNAL(triggered()), pqVelodyneManager::instance(),
      SLOT(onOpenSensor()));

    connect(this->Ui.actionMeasurement_Grid, SIGNAL(toggled(bool)), pqVelodyneManager::instance(),
      SLOT(onMeasurementGrid(bool)));

    connect(this->Ui.actionEnableCrashAnalysis, SIGNAL(toggled(bool)),
      pqVelodyneManager::instance(), SLOT(onEnableCrashAnalysis(bool)));

    connect(this->Ui.actionResetConfigurationFile, SIGNAL(triggered()),
      pqVelodyneManager::instance(), SLOT(onResetCalibrationFile()));

    connect(this->Ui.actionShowErrorDialog, SIGNAL(triggered()), pqApplicationCore::instance(),
      SLOT(showOutputWindow()));
  }
};

//-----------------------------------------------------------------------------
vvMainWindow::vvMainWindow()
  : Internals(new vvMainWindow::pqInternals(this))
{
  pqApplicationCore::instance()->registerManager(
    "COLOR_EDITOR_PANEL", this->Internals->Ui.colorMapEditorDock);
  this->Internals->Ui.colorMapEditorDock->hide();

  PV_PLUGIN_IMPORT(VelodyneHDLPlugin);
  PV_PLUGIN_IMPORT(PythonQtPlugin);

  // Branding
  std::stringstream ss;
  ss << "Reset " << SOFTWARE_NAME << " settings";
  QString text = QString(ss.str().c_str());
  this->Internals->Ui.actionResetConfigurationFile->setText(text);
  ss.str("");
  ss.clear();

  ss << "This will reset all " << SOFTWARE_NAME << " settings by default";
  text = QString(ss.str().c_str());
  this->Internals->Ui.actionResetConfigurationFile->setIconText(text);
  ss.str("");
  ss.clear();

  ss << "About " << SOFTWARE_NAME;
  text = QString(ss.str().c_str());
  this->Internals->Ui.actionAbout_VeloView->setText(text);
  ss.str("");
  ss.clear();

  ss << SOFTWARE_NAME << " Developer Guide";
  text = QString(ss.str().c_str());
  this->Internals->Ui.actionVeloViewDeveloperGuide->setText(text);
  ss.str("");
  ss.clear();

  ss << SOFTWARE_NAME << " User Guide";
  text = QString(ss.str().c_str());
  this->Internals->Ui.actionVeloViewUserGuide->setText(text);
}

//-----------------------------------------------------------------------------
vvMainWindow::~vvMainWindow()
{
  delete this->Internals;
  this->Internals = NULL;
}

//-----------------------------------------------------------------------------
void vvMainWindow::showHelpForProxy(const QString& groupname, const
  QString& proxyname)
{
  pqHelpReaction::showProxyHelp(groupname, proxyname);
}
<|endoftext|>


<commit_before>/** \file   RegexMatcher.cc
 *  \brief  Implementation of the RegexMatcher class.
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2015,2017,2018 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program 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.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "RegexMatcher.h"
#include <unordered_map>
#include "Compiler.h"
#include "util.h"

bool RegexMatcher::utf8_configured_;

bool CompileRegex(const std::string &pattern, const unsigned options, ::pcre **pcre_arg,
                  ::pcre_extra **pcre_extra_arg, std::string * const err_msg)
{
    if (err_msg != nullptr)
        err_msg->clear();

    const char *errptr;
    int erroffset;

    int pcre_options(0);
    if (options & RegexMatcher::ENABLE_UTF8)
        pcre_options |= PCRE_UTF8;
    if (options & RegexMatcher::CASE_INSENSITIVE)
        pcre_options |= PCRE_CASELESS;
    if (options & RegexMatcher::MULTILINE)
        pcre_options |= PCRE_MULTILINE;

    *pcre_arg = ::pcre_compile(pattern.c_str(), pcre_options, &errptr, &erroffset, nullptr);
    if (*pcre_arg == nullptr) {
        *pcre_extra_arg = nullptr;
        if (err_msg != nullptr)
            *err_msg = "failed to compile invalid regular expression: \"" + pattern + "\"! ("
                       + std::string(errptr) + ")";
        return false;
    }

    // Can't use PCRE_STUDY_JIT_COMPILE because it's not thread safe.
    *pcre_extra_arg = ::pcre_study(*pcre_arg, 0, &errptr);
    if (*pcre_extra_arg == nullptr and errptr != nullptr) {
        ::pcre_free(*pcre_arg);
        *pcre_arg = nullptr;
        if (err_msg != nullptr)
            *err_msg = "failed to \"study\" the compiled pattern \"" + pattern + "\"! (" + std::string(errptr) + ")";
        return false;
    }

    return true;
}

RegexMatcher *RegexMatcher::RegexMatcherFactory(const std::string &pattern, std::string * const err_msg,
                                                const unsigned options)
{
    // Make sure the PCRE library supports UTF8:
    if ((options & RegexMatcher::ENABLE_UTF8) and not RegexMatcher::utf8_configured_) {
        int utf8_available;
        if (::pcre_config(PCRE_CONFIG_UTF8, reinterpret_cast<void *>(&utf8_available)) == PCRE_ERROR_BADOPTION) {
            if (err_msg != nullptr)
                *err_msg = "PCRE library does not know PCRE_CONFIG_UTF8!";
            return nullptr;
        }

        if (utf8_available != 1) {
            if (err_msg != nullptr)
                *err_msg = "This version of the PCRE library does not support UTF8!";
            return nullptr;
        }

        RegexMatcher::utf8_configured_ = true;
    }

    ::pcre *pcre_ptr;
    ::pcre_extra *pcre_extra_ptr;
    if (not CompileRegex(pattern, options, &pcre_ptr, &pcre_extra_ptr, err_msg)) {
        if (err_msg != nullptr and err_msg->empty())
            *err_msg = "failed to compile pattern: \"" + pattern + "\"";
        return nullptr;
    }

    return new RegexMatcher(pattern, options, pcre_ptr, pcre_extra_ptr);
}

RegexMatcher *RegexMatcher::RegexMatcherFactoryOrDie(const std::string &regex, const unsigned options) {
    std::string error_message;
    RegexMatcher *regex_matcher(RegexMatcher::RegexMatcherFactory(regex, &error_message, options));
    if (regex_matcher == nullptr or not error_message.empty())
        LOG_ERROR("failed to compile regex \"" + regex + "\": " + error_message);

    return regex_matcher;
}

RegexMatcher::RegexMatcher(const RegexMatcher &that): pattern_(that.pattern_) {
    if (this == &that)
        return;

    if (that.pcre_ == nullptr) {
        pcre_ = nullptr;
        pcre_extra_ = nullptr;
    } else {
        std::string err_msg;
        if (not CompileRegex(pattern_, that.options_, &pcre_, &pcre_extra_, &err_msg))
            logger->error("In RegexMatcher copy constructor: unexpected error: " + err_msg);
        substr_vector_    = that.substr_vector_;
        last_match_count_ = that.last_match_count_;
    }
}

RegexMatcher::RegexMatcher(RegexMatcher &&that)
    : pattern_(std::move(that.pattern_)), options_(that.options_), pcre_(that.pcre_),
      pcre_extra_(that.pcre_extra_), last_subject_(std::move(that.last_subject_)),
      substr_vector_(std::move(that.substr_vector_)), last_match_count_(that.last_match_count_)
{
    that.pcre_       = nullptr;
    that.pcre_extra_ = nullptr;
}

bool RegexMatcher::matched(const std::string &subject, const size_t subject_start_offset, std::string * const err_msg,
                           size_t * const start_pos, size_t * const end_pos)
{
    if (err_msg != nullptr)
        err_msg->clear();

    const int retcode(::pcre_exec(pcre_, pcre_extra_, subject.data(), subject.length(), subject_start_offset, 0,
                                    &substr_vector_[0], substr_vector_.size()));

    if (retcode == 0) {
        if (err_msg != nullptr)
            *err_msg = "Too many captured substrings! (We only support "
                       + std::to_string(substr_vector_.size() / 3 - 1) + " substrings.)";
        return false;
    }

    if (retcode > 0) {
        last_match_count_ = retcode;
        last_subject_     = subject;
        if (start_pos != nullptr)
            *start_pos = substr_vector_[0];
        if (end_pos != nullptr)
            *end_pos = substr_vector_[1];
        return true;
    }

    if (retcode != PCRE_ERROR_NOMATCH) {
        if (retcode == PCRE_ERROR_BADUTF8) {
            if (err_msg != nullptr)
                *err_msg = "A \"subject\" with invalid UTF-8 was passed into RegexMatcher::matched()!";
        } else if (err_msg != nullptr)
            *err_msg = "Unknown error!";
    }

    return false;
}

std::string RegexMatcher::replaceAll(const std::string &subject, const std::string &replacement) {
    if (not matched(subject))
        return subject;

    std::string replaced_string;
    // the matches need to be sequentially sorted from left to right
    size_t subject_start_offset(0), match_start_offset(0), match_end_offset(0);
    while (subject_start_offset < subject.length() and
           matched(subject, subject_start_offset, /* err_msg */ nullptr, &match_start_offset, &match_end_offset))
    {
        if (subject_start_offset == match_start_offset and subject_start_offset == match_end_offset) {
            replaced_string += subject[subject_start_offset++];
            continue;
        }

        replaced_string += subject.substr(subject_start_offset, match_start_offset - subject_start_offset);
        replaced_string += replacement;
        subject_start_offset = match_end_offset;
    }

    return replaced_string;
}

bool RegexMatcher::Matched(const std::string &regex, const std::string &subject, const unsigned options,
                           std::string * const err_msg, size_t * const start_pos, size_t * const end_pos)
{
    static std::unordered_map<std::string, RegexMatcher *> regex_to_matcher_map;
    const std::string KEY(regex + ":" + std::to_string(options));
    const auto regex_and_matcher(regex_to_matcher_map.find(KEY));
    if (regex_and_matcher != regex_to_matcher_map.cend())
        return regex_and_matcher->second->matched(subject, err_msg, start_pos, end_pos);

    RegexMatcher * const matcher(RegexMatcher::RegexMatcherFactory(regex, err_msg, options));
    if (matcher == nullptr)
        LOG_ERROR("Failed to compile pattern \"" + regex + "\": " + *err_msg);
    regex_to_matcher_map[KEY] = matcher;

    return matcher->matched(subject, err_msg, start_pos, end_pos);
}

std::string RegexMatcher::operator[](const unsigned group) const {
    if (unlikely(group >= last_match_count_))
        throw std::out_of_range("in RegexMatcher::operator[]: group(" + std::to_string(group) + ") >= "
                                + std::to_string(last_match_count_) + "!");

    const unsigned first_index(group * 2);
    const unsigned substring_length(substr_vector_[first_index + 1] - substr_vector_[first_index]);
    return (substring_length == 0) ? "" : last_subject_.substr(substr_vector_[first_index], substring_length);
}
<commit_msg>RegexMatcher: correctly copy remaining bytes into the target string after a replacement op<commit_after>/** \file   RegexMatcher.cc
 *  \brief  Implementation of the RegexMatcher class.
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2015,2017,2018 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program 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.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "RegexMatcher.h"
#include <unordered_map>
#include "Compiler.h"
#include "util.h"

bool RegexMatcher::utf8_configured_;

bool CompileRegex(const std::string &pattern, const unsigned options, ::pcre **pcre_arg,
                  ::pcre_extra **pcre_extra_arg, std::string * const err_msg)
{
    if (err_msg != nullptr)
        err_msg->clear();

    const char *errptr;
    int erroffset;

    int pcre_options(0);
    if (options & RegexMatcher::ENABLE_UTF8)
        pcre_options |= PCRE_UTF8;
    if (options & RegexMatcher::CASE_INSENSITIVE)
        pcre_options |= PCRE_CASELESS;
    if (options & RegexMatcher::MULTILINE)
        pcre_options |= PCRE_MULTILINE;

    *pcre_arg = ::pcre_compile(pattern.c_str(), pcre_options, &errptr, &erroffset, nullptr);
    if (*pcre_arg == nullptr) {
        *pcre_extra_arg = nullptr;
        if (err_msg != nullptr)
            *err_msg = "failed to compile invalid regular expression: \"" + pattern + "\"! ("
                       + std::string(errptr) + ")";
        return false;
    }

    // Can't use PCRE_STUDY_JIT_COMPILE because it's not thread safe.
    *pcre_extra_arg = ::pcre_study(*pcre_arg, 0, &errptr);
    if (*pcre_extra_arg == nullptr and errptr != nullptr) {
        ::pcre_free(*pcre_arg);
        *pcre_arg = nullptr;
        if (err_msg != nullptr)
            *err_msg = "failed to \"study\" the compiled pattern \"" + pattern + "\"! (" + std::string(errptr) + ")";
        return false;
    }

    return true;
}

RegexMatcher *RegexMatcher::RegexMatcherFactory(const std::string &pattern, std::string * const err_msg,
                                                const unsigned options)
{
    // Make sure the PCRE library supports UTF8:
    if ((options & RegexMatcher::ENABLE_UTF8) and not RegexMatcher::utf8_configured_) {
        int utf8_available;
        if (::pcre_config(PCRE_CONFIG_UTF8, reinterpret_cast<void *>(&utf8_available)) == PCRE_ERROR_BADOPTION) {
            if (err_msg != nullptr)
                *err_msg = "PCRE library does not know PCRE_CONFIG_UTF8!";
            return nullptr;
        }

        if (utf8_available != 1) {
            if (err_msg != nullptr)
                *err_msg = "This version of the PCRE library does not support UTF8!";
            return nullptr;
        }

        RegexMatcher::utf8_configured_ = true;
    }

    ::pcre *pcre_ptr;
    ::pcre_extra *pcre_extra_ptr;
    if (not CompileRegex(pattern, options, &pcre_ptr, &pcre_extra_ptr, err_msg)) {
        if (err_msg != nullptr and err_msg->empty())
            *err_msg = "failed to compile pattern: \"" + pattern + "\"";
        return nullptr;
    }

    return new RegexMatcher(pattern, options, pcre_ptr, pcre_extra_ptr);
}

RegexMatcher *RegexMatcher::RegexMatcherFactoryOrDie(const std::string &regex, const unsigned options) {
    std::string error_message;
    RegexMatcher *regex_matcher(RegexMatcher::RegexMatcherFactory(regex, &error_message, options));
    if (regex_matcher == nullptr or not error_message.empty())
        LOG_ERROR("failed to compile regex \"" + regex + "\": " + error_message);

    return regex_matcher;
}

RegexMatcher::RegexMatcher(const RegexMatcher &that): pattern_(that.pattern_) {
    if (this == &that)
        return;

    if (that.pcre_ == nullptr) {
        pcre_ = nullptr;
        pcre_extra_ = nullptr;
    } else {
        std::string err_msg;
        if (not CompileRegex(pattern_, that.options_, &pcre_, &pcre_extra_, &err_msg))
            logger->error("In RegexMatcher copy constructor: unexpected error: " + err_msg);
        substr_vector_    = that.substr_vector_;
        last_match_count_ = that.last_match_count_;
    }
}

RegexMatcher::RegexMatcher(RegexMatcher &&that)
    : pattern_(std::move(that.pattern_)), options_(that.options_), pcre_(that.pcre_),
      pcre_extra_(that.pcre_extra_), last_subject_(std::move(that.last_subject_)),
      substr_vector_(std::move(that.substr_vector_)), last_match_count_(that.last_match_count_)
{
    that.pcre_       = nullptr;
    that.pcre_extra_ = nullptr;
}

bool RegexMatcher::matched(const std::string &subject, const size_t subject_start_offset, std::string * const err_msg,
                           size_t * const start_pos, size_t * const end_pos)
{
    if (err_msg != nullptr)
        err_msg->clear();

    const int retcode(::pcre_exec(pcre_, pcre_extra_, subject.data(), subject.length(), subject_start_offset, 0,
                                    &substr_vector_[0], substr_vector_.size()));

    if (retcode == 0) {
        if (err_msg != nullptr)
            *err_msg = "Too many captured substrings! (We only support "
                       + std::to_string(substr_vector_.size() / 3 - 1) + " substrings.)";
        return false;
    }

    if (retcode > 0) {
        last_match_count_ = retcode;
        last_subject_     = subject;
        if (start_pos != nullptr)
            *start_pos = substr_vector_[0];
        if (end_pos != nullptr)
            *end_pos = substr_vector_[1];
        return true;
    }

    if (retcode != PCRE_ERROR_NOMATCH) {
        if (retcode == PCRE_ERROR_BADUTF8) {
            if (err_msg != nullptr)
                *err_msg = "A \"subject\" with invalid UTF-8 was passed into RegexMatcher::matched()!";
        } else if (err_msg != nullptr)
            *err_msg = "Unknown error!";
    }

    return false;
}

std::string RegexMatcher::replaceAll(const std::string &subject, const std::string &replacement) {
    if (not matched(subject))
        return subject;

    std::string replaced_string;
    // the matches need to be sequentially sorted from left to right
    size_t subject_start_offset(0), match_start_offset(0), match_end_offset(0);
    while (subject_start_offset < subject.length() and
           matched(subject, subject_start_offset, /* err_msg */ nullptr, &match_start_offset, &match_end_offset))
    {
        if (subject_start_offset == match_start_offset and subject_start_offset == match_end_offset) {
            replaced_string += subject[subject_start_offset++];
            continue;
        }

        replaced_string += subject.substr(subject_start_offset, match_start_offset - subject_start_offset);
        replaced_string += replacement;
        subject_start_offset = match_end_offset;
    }

    while (subject_start_offset < subject.length())
        replaced_string += subject[subject_start_offset++];

    return replaced_string;
}

bool RegexMatcher::Matched(const std::string &regex, const std::string &subject, const unsigned options,
                           std::string * const err_msg, size_t * const start_pos, size_t * const end_pos)
{
    static std::unordered_map<std::string, RegexMatcher *> regex_to_matcher_map;
    const std::string KEY(regex + ":" + std::to_string(options));
    const auto regex_and_matcher(regex_to_matcher_map.find(KEY));
    if (regex_and_matcher != regex_to_matcher_map.cend())
        return regex_and_matcher->second->matched(subject, err_msg, start_pos, end_pos);

    RegexMatcher * const matcher(RegexMatcher::RegexMatcherFactory(regex, err_msg, options));
    if (matcher == nullptr)
        LOG_ERROR("Failed to compile pattern \"" + regex + "\": " + *err_msg);
    regex_to_matcher_map[KEY] = matcher;

    return matcher->matched(subject, err_msg, start_pos, end_pos);
}

std::string RegexMatcher::operator[](const unsigned group) const {
    if (unlikely(group >= last_match_count_))
        throw std::out_of_range("in RegexMatcher::operator[]: group(" + std::to_string(group) + ") >= "
                                + std::to_string(last_match_count_) + "!");

    const unsigned first_index(group * 2);
    const unsigned substring_length(substr_vector_[first_index + 1] - substr_vector_[first_index]);
    return (substring_length == 0) ? "" : last_subject_.substr(substr_vector_[first_index], substring_length);
}
<|endoftext|>