<commit_before>#ifndef PERSISTENT_HPP
#define PERSISTENT_HPP

#include <sys/types.h>
#include <inttypes.h>
#include <string>
#include <iostream>
#include <memory>
#include <functional>
#include <pthread.h>
#include "HLC.hpp"
#include "PersistException.hpp"
#include "PersistLog.hpp"
#include "FilePersistLog.hpp"
#include "SerializationSupport.hpp"

using namespace mutils;

namespace ns_persistent {

  // #define DEFINE_PERSIST_VAR(_t,_n) DEFINE_PERSIST_VAR(_t,_n,ST_FILE)
  #define DEFINE_PERSIST_VAR(_t,_n,_s) \
    Persistent<_t, _s> _n(# _n)
  #define DECLARE_PERSIST_VAR(_t,_n,_s) \
    extern DEFINE_PERSIST_VAR(_t,_n,_s)

  /* deprecated
  // number of versions in the cache
  #define MAX_NUM_CACHED_VERSION        (1024)
  #define VERSION_HASH(v)               ( (int)((v) & (MAX_NUM_CACHED_VERSION-1)) )

  // invalid version:
  #define INVALID_VERSION (-1ll)

  // read from cache
  #define VERSION_IS_CACHED(v)  (this->m_aCache[VERSION_HASH(v)].ver == (v))
  #define GET_CACHED_OBJ_PTR(v) (this->m_aCache[VERSION_HASH(v)].obj)
  */

  // function types to be registered for create version
  // or persist version
  using VersionFunc = std::function<void(const __int128 &)>;
  using PersistFunc = std::function<void(void)>;
  using FuncRegisterCallback = std::function<void(VersionFunc,PersistFunc)>;

  // Persistent represents a variable backed up by persistent storage. The
  // backend is PersistLog class. PersistLog handles only raw bytes and this
  // class is repsonsible for converting it back and forth between raw bytes
  // and ObjectType. But, the serialization/deserialization functionality is
  // actually defined by ObjectType and provided by Persistent users.
  // - ObjectType: user-defined type of the variable it is required to support
  //   serialization and deserialization as follows:
  //   // serialize
  //   void * ObjectType::serialize(const ObjectType & obj, uint64_t *psize)
  //   - obj: obj is the reference to the object to be serialized
  //   - psize: psize is a uint64_t pointer to receive the size of the serialized
  //     data.
  //   - Return value is a pointer to a new malloced buffer with the serialized
  //     //TODO: this may not be efficient for large object...open to be changed.
  //   // deserialize
  //   ObjectType * ObjectType::deserialize(const void *pdata)
  //   - pdata: a buffer of the serialized data
  //   - Return value is a pointer to a new created ObjectType deserialized from
  //     'pdata' buffer.
  // - StorageType: storage type is defined in PersistLog. The value could be
  //   ST_FILE/ST_MEM/ST_3DXP ... I will start with ST_FILE and extend it to
  //   other persistent Storage.
  // TODO:comments
  template <typename ObjectType,
    StorageType storageType=ST_FILE>
  class Persistent{
  public:
      /** The constructor
       * @param func_register_cb Call this to register myself to Replicated<T>
       * @param object_name This name is used for persistent data in file.
       */
      Persistent(FuncRegisterCallback func_register_cb=nullptr,
        const char * object_name = (*Persistent::getNameMaker().make()).c_str())
        noexcept(false) {
         // Initialize log
        this->m_pLog = NULL;
        switch(storageType){
        // file system
        case ST_FILE:
          this->m_pLog = new FilePersistLog(object_name);
          if(this->m_pLog == NULL){
            throw PERSIST_EXP_NEW_FAILED_UNKNOWN;
          }
          break;
        // volatile
        case ST_MEM:
        {
          const string tmpfsPath = "/dev/shm/volatile_t";
          this->m_pLog = new FilePersistLog(object_name, tmpfsPath);
          if(this->m_pLog == NULL){
            throw PERSIST_EXP_NEW_FAILED_UNKNOWN;
          }
          break;
        }
        //default
        default:
          throw PERSIST_EXP_STORAGE_TYPE_UNKNOWN(storageType);
        }
        //register the version creator and persist callback
        if(func_register_cb != nullptr){
          func_register_cb(
            std::bind(&Persistent<ObjectType,storageType>::version,this,std::placeholders::_1),
            std::bind(&Persistent<ObjectType,storageType>::persist,this)
        );
        }
      }
      // destructor: release the resources
      virtual ~Persistent() noexcept(false){
        // destroy the in-memory log
        if(this->m_pLog != NULL){
          delete this->m_pLog;
        }
        //TODO:unregister the version creator and persist callback,
        // if the Persistent<T> is added to the pool dynamically.
      };

      /**
       * * operator to get the memory version
       * @return ObjectType&
       */
      ObjectType& operator * (){
        return this->wrapped_obj;
      }

      // get the latest Value of T. The user lambda will be fed with the latest object
      // zerocopy:this object will not live once it returns.
      // return value is decided by user lambda
      template <typename Func>
      auto get (
        const Func& fun,
        DeserializationManager *dm=nullptr)
        noexcept(false) {
        return this->getByIndex(-1L,fun,dm);
      };

      // get the latest Value of T, returns a unique pointer to the object
      std::unique_ptr<ObjectType> get ( DeserializationManager *dm=nullptr)
        noexcept(false) {
        return this->getByIndex(-1L,dm);
      };

      // get a version of Value T. the user lambda will be fed with the given object
      // zerocopy:this object will not live once it returns.
      // return value is decided by user lambda
      template <typename Func>
      auto getByIndex (
        int64_t idx,
        const Func& fun,
        DeserializationManager *dm=nullptr)
        noexcept(false) {
        return deserialize_and_run<ObjectType>(dm,(char *)this->m_pLog->getEntryByIndex(idx),fun);
      };

      // get a version of value T. returns a unique pointer to the object
      std::unique_ptr<ObjectType> getByIndex(
        int64_t idx,
        DeserializationManager *dm=nullptr)
        noexcept(false) {
        return from_bytes<ObjectType>(dm,(char const *)this->m_pLog->getEntryByIndex(idx));
      };

      // get a version of Value T, specified by version. the user lambda will be fed with
      // an object of T.
      // zerocopy: this object will not live once it returns.
      // return value is decided by the user lambda.
      template <typename Func>
      auto get (
        const __int128 & ver,
        const Func& fun,
        DeserializationManager *dm=nullptr)
        noexcept(false) {
        char * pdat = (char*)this->m_pLog->getEntry(ver);
        if (pdat == nullptr) {
          throw PERSIST_EXP_INV_VERSION;
        }
        return deserialize_and_run<ObjectType>(dm,pdat,fun);
      };

      // get a version of value T. specified version.
      // return a deserialized copy for the variable.
      std::unique_ptr<ObjectType> get(
        const __int128 & ver,
        DeserializationManager *dm=nullptr)
        noexcept(false) {
        char const * pdat = (char const *)this->m_pLog->getEntry(ver);
        if (pdat == nullptr) {
          throw PERSIST_EXP_INV_VERSION;
        }

        return from_bytes<ObjectType>(dm,pdat);
      }

      template <typename TKey>
      void trim (const TKey &k) noexcept(false) {
        dbg_trace("trim.");
        this->m_pLog->trim(k);
        dbg_trace("trim...done");
      }

      // get a version of Value T, specified by HLC clock. the user lambda will be fed with
      // an object of T.
      // zerocopy: this object will not live once it returns.
      // return value is decided by the user lambda.
      template <typename Func>
      auto get (
        const HLC& hlc,
        const Func& fun,
        DeserializationManager *dm=nullptr)
        noexcept(false) {
        char * pdat = (char*)this->m_pLog->getEntry(hlc);
        if (pdat == nullptr) {
          throw PERSIST_EXP_INV_HLC;
        }
        return deserialize_and_run<ObjectType>(dm,pdat,fun);
      };

      // get a version of value T. specified by HLC clock.
      std::unique_ptr<ObjectType> get(
        const HLC& hlc,
        DeserializationManager *dm=nullptr)
        noexcept(false) {
        char const * pdat = (char const *)this->m_pLog->getEntry(hlc);
        if (pdat == nullptr) {
          throw PERSIST_EXP_INV_HLC;
        }

        return from_bytes<ObjectType>(dm,pdat);
      }

      // syntax sugar: get a specified version of T without DSM
      std::unique_ptr<ObjectType> operator [](int64_t idx)
        noexcept(false) {
        return this->getByIndex(idx);
      }

      // syntax sugar: get a specified version of T without DSM
      std::unique_ptr<ObjectType> operator [](const __int128 ver)
        noexcept(false) {
        return this->get(ver);
      }

      // syntax sugar: get a specified version of T without DSM
      std::unique_ptr<ObjectType> operator [](const HLC & hlc)
        noexcept(false) {
        return this->get(hlc);
      }

      // get number of the versions
      virtual int64_t getNumOfVersions() noexcept(false) {
        return this->m_pLog->getLength();
      };

      virtual int64_t getEarliestIndex() noexcept(false) {
        return this->m_pLog->getEarliestIndex();
      }

      // make a version with version and mhlc clock
      virtual void set(const ObjectType &v, const __int128 & ver, const HLC &mhlc)
        noexcept(false) {
        auto size = bytes_size(v);
        char buf[size];
        bzero(buf,size);
        to_bytes(v,buf);
        this->m_pLog->append((void*)buf,size,ver,mhlc);
      };

      // make a version with version
      virtual void set(const ObjectType &v, const __int128 & ver)
        noexcept(false) {
        HLC mhlc; // generate a default timestamp for it.
        this->set(v,ver,mhlc);
      }

      // make a version
      virtual void version(const __int128 & ver)
        noexcept(false) {
        this->set(this->wrapped_obj,ver);
      }

      /** persist till version
       * @param ver version number
       * @return the given version to be persisted.
       */
      virtual const __int128 persist()
        noexcept(false){
        return this->m_pLog->persist();
      }

      // internal _NameMaker class
      class _NameMaker{
      public:
        // Constructor
        _NameMaker() noexcept(false):
        m_sObjectTypeName(typeid(ObjectType).name()) {
          this->m_iCounter = 0;
          if (pthread_spin_init(&this->m_oLck,PTHREAD_PROCESS_SHARED) != 0) {
            throw PERSIST_EXP_SPIN_INIT(errno);
          }
        }

        // Destructor
        virtual ~_NameMaker() noexcept(true) {
          pthread_spin_destroy(&this->m_oLck);
        }

        // guess a name
        std::unique_ptr<std::string> make() noexcept(false) {
          int cnt;
          if (pthread_spin_lock(&this->m_oLck) != 0) {
            throw PERSIST_EXP_SPIN_LOCK(errno);
          }
          cnt = this->m_iCounter++;
          if (pthread_spin_unlock(&this->m_oLck) != 0) {
            throw PERSIST_EXP_SPIN_UNLOCK(errno);
          }
          std::unique_ptr<std::string> ret = std::make_unique<std::string>();
          //char * buf = (char *)malloc((strlen(this->m_sObjectTypeName)+13)/8*8);
          char buf[256];
          sprintf(buf,"%s-%d",this->m_sObjectTypeName,cnt);
         // return std::make_shared<const char *>((const char*)buf);
         *ret = buf;
         return ret;
        }

      private:
        int m_iCounter;
        const char *m_sObjectTypeName;
        pthread_spinlock_t m_oLck;
      };

  private:
      // wrapped objected
      ObjectType wrapped_obj;

      // PersistLog
      PersistLog * m_pLog;

      // get the static name maker.
      static _NameMaker & getNameMaker();
  };

  // How many times the constructor was called.
  template <typename ObjectType, StorageType storageType>
  typename Persistent<ObjectType,storageType>::_NameMaker &
    Persistent<ObjectType,storageType>::getNameMaker() noexcept(false) {
    static Persistent<ObjectType,storageType>::_NameMaker nameMaker;
    return nameMaker;
  }

  template <typename ObjectType>
  class Volatile: public Persistent<ObjectType,ST_MEM>{
  public:
    // constructor: this will guess the objectname form ObjectType
    Volatile<ObjectType>() noexcept(false):
      Persistent<ObjectType,ST_MEM>(){
    };
    // destructor:
    virtual ~Volatile() noexcept(false){
      // do nothing
    };
  };
}

#endif//PERSIST_VAR_H
<commit_msg>added registry definition to Persistent<T>, TODO: add registry to Replicate<T>.<commit_after>#ifndef PERSISTENT_HPP
#define PERSISTENT_HPP

#include <sys/types.h>
#include <inttypes.h>
#include <string>
#include <iostream>
#include <memory>
#include <functional>
#include <pthread.h>
#include "HLC.hpp"
#include "PersistException.hpp"
#include "PersistLog.hpp"
#include "FilePersistLog.hpp"
#include "SerializationSupport.hpp"

using namespace mutils;

namespace ns_persistent {

  // #define DEFINE_PERSIST_VAR(_t,_n) DEFINE_PERSIST_VAR(_t,_n,ST_FILE)
  #define DEFINE_PERSIST_VAR(_t,_n,_s) \
    Persistent<_t, _s> _n(# _n)
  #define DECLARE_PERSIST_VAR(_t,_n,_s) \
    extern DEFINE_PERSIST_VAR(_t,_n,_s)

  /* deprecated
  // number of versions in the cache
  #define MAX_NUM_CACHED_VERSION        (1024)
  #define VERSION_HASH(v)               ( (int)((v) & (MAX_NUM_CACHED_VERSION-1)) )

  // invalid version:
  #define INVALID_VERSION (-1ll)

  // read from cache
  #define VERSION_IS_CACHED(v)  (this->m_aCache[VERSION_HASH(v)].ver == (v))
  #define GET_CACHED_OBJ_PTR(v) (this->m_aCache[VERSION_HASH(v)].obj)
  */

  // function types to be registered for create version
  // , persist version, and trim a version
  using VersionFunc = std::function<void(const __int128 &)>;
  using PersistFunc = std::function<void(void)>;
  using TrimFunc = std::function<void(const __int128 &)>;
  using FuncRegisterCallback = std::function<void(VersionFunc,PersistFunc,TrimFunc)>;

  // Persistent represents a variable backed up by persistent storage. The
  // backend is PersistLog class. PersistLog handles only raw bytes and this
  // class is repsonsible for converting it back and forth between raw bytes
  // and ObjectType. But, the serialization/deserialization functionality is
  // actually defined by ObjectType and provided by Persistent users.
  // - ObjectType: user-defined type of the variable it is required to support
  //   serialization and deserialization as follows:
  //   // serialize
  //   void * ObjectType::serialize(const ObjectType & obj, uint64_t *psize)
  //   - obj: obj is the reference to the object to be serialized
  //   - psize: psize is a uint64_t pointer to receive the size of the serialized
  //     data.
  //   - Return value is a pointer to a new malloced buffer with the serialized
  //     //TODO: this may not be efficient for large object...open to be changed.
  //   // deserialize
  //   ObjectType * ObjectType::deserialize(const void *pdata)
  //   - pdata: a buffer of the serialized data
  //   - Return value is a pointer to a new created ObjectType deserialized from
  //     'pdata' buffer.
  // - StorageType: storage type is defined in PersistLog. The value could be
  //   ST_FILE/ST_MEM/ST_3DXP ... I will start with ST_FILE and extend it to
  //   other persistent Storage.
  // TODO:comments
  template <typename ObjectType,
    StorageType storageType=ST_FILE>
  class Persistent{
  public:
      /** The constructor
       * @param func_register_cb Call this to register myself to Replicated<T>
       * @param object_name This name is used for persistent data in file.
       */
      Persistent(FuncRegisterCallback func_register_cb=nullptr,
        const char * object_name = (*Persistent::getNameMaker().make()).c_str())
        noexcept(false) {
         // Initialize log
        this->m_pLog = NULL;
        switch(storageType){
        // file system
        case ST_FILE:
          this->m_pLog = new FilePersistLog(object_name);
          if(this->m_pLog == NULL){
            throw PERSIST_EXP_NEW_FAILED_UNKNOWN;
          }
          break;
        // volatile
        case ST_MEM:
        {
          const string tmpfsPath = "/dev/shm/volatile_t";
          this->m_pLog = new FilePersistLog(object_name, tmpfsPath);
          if(this->m_pLog == NULL){
            throw PERSIST_EXP_NEW_FAILED_UNKNOWN;
          }
          break;
        }
        //default
        default:
          throw PERSIST_EXP_STORAGE_TYPE_UNKNOWN(storageType);
        }
        //register the version creator and persist callback
        if(func_register_cb != nullptr){
          func_register_cb(
            std::bind(&Persistent<ObjectType,storageType>::version,this,std::placeholders::_1),
            std::bind(&Persistent<ObjectType,storageType>::persist,this),
            std::bind(&Persistent<ObjectType,storageType>::trim<const __int128>,this,std::placeholders::_1) //trim by version:(const __int128)
        );
        }
      }
      // destructor: release the resources
      virtual ~Persistent() noexcept(false){
        // destroy the in-memory log
        if(this->m_pLog != NULL){
          delete this->m_pLog;
        }
        //TODO:unregister the version creator and persist callback,
        // if the Persistent<T> is added to the pool dynamically.
      };

      /**
       * * operator to get the memory version
       * @return ObjectType&
       */
      ObjectType& operator * (){
        return this->wrapped_obj;
      }

      // get the latest Value of T. The user lambda will be fed with the latest object
      // zerocopy:this object will not live once it returns.
      // return value is decided by user lambda
      template <typename Func>
      auto get (
        const Func& fun,
        DeserializationManager *dm=nullptr)
        noexcept(false) {
        return this->getByIndex(-1L,fun,dm);
      };

      // get the latest Value of T, returns a unique pointer to the object
      std::unique_ptr<ObjectType> get ( DeserializationManager *dm=nullptr)
        noexcept(false) {
        return this->getByIndex(-1L,dm);
      };

      // get a version of Value T. the user lambda will be fed with the given object
      // zerocopy:this object will not live once it returns.
      // return value is decided by user lambda
      template <typename Func>
      auto getByIndex (
        int64_t idx,
        const Func& fun,
        DeserializationManager *dm=nullptr)
        noexcept(false) {
        return deserialize_and_run<ObjectType>(dm,(char *)this->m_pLog->getEntryByIndex(idx),fun);
      };

      // get a version of value T. returns a unique pointer to the object
      std::unique_ptr<ObjectType> getByIndex(
        int64_t idx,
        DeserializationManager *dm=nullptr)
        noexcept(false) {
        return from_bytes<ObjectType>(dm,(char const *)this->m_pLog->getEntryByIndex(idx));
      };

      // get a version of Value T, specified by version. the user lambda will be fed with
      // an object of T.
      // zerocopy: this object will not live once it returns.
      // return value is decided by the user lambda.
      template <typename Func>
      auto get (
        const __int128 & ver,
        const Func& fun,
        DeserializationManager *dm=nullptr)
        noexcept(false) {
        char * pdat = (char*)this->m_pLog->getEntry(ver);
        if (pdat == nullptr) {
          throw PERSIST_EXP_INV_VERSION;
        }
        return deserialize_and_run<ObjectType>(dm,pdat,fun);
      };

      // get a version of value T. specified version.
      // return a deserialized copy for the variable.
      std::unique_ptr<ObjectType> get(
        const __int128 & ver,
        DeserializationManager *dm=nullptr)
        noexcept(false) {
        char const * pdat = (char const *)this->m_pLog->getEntry(ver);
        if (pdat == nullptr) {
          throw PERSIST_EXP_INV_VERSION;
        }

        return from_bytes<ObjectType>(dm,pdat);
      }

      template <typename TKey>
      void trim (const TKey &k) noexcept(false) {
        dbg_trace("trim.");
        this->m_pLog->trim(k);
        dbg_trace("trim...done");
      }

      // get a version of Value T, specified by HLC clock. the user lambda will be fed with
      // an object of T.
      // zerocopy: this object will not live once it returns.
      // return value is decided by the user lambda.
      template <typename Func>
      auto get (
        const HLC& hlc,
        const Func& fun,
        DeserializationManager *dm=nullptr)
        noexcept(false) {
        char * pdat = (char*)this->m_pLog->getEntry(hlc);
        if (pdat == nullptr) {
          throw PERSIST_EXP_INV_HLC;
        }
        return deserialize_and_run<ObjectType>(dm,pdat,fun);
      };

      // get a version of value T. specified by HLC clock.
      std::unique_ptr<ObjectType> get(
        const HLC& hlc,
        DeserializationManager *dm=nullptr)
        noexcept(false) {
        char const * pdat = (char const *)this->m_pLog->getEntry(hlc);
        if (pdat == nullptr) {
          throw PERSIST_EXP_INV_HLC;
        }

        return from_bytes<ObjectType>(dm,pdat);
      }

      // syntax sugar: get a specified version of T without DSM
      std::unique_ptr<ObjectType> operator [](int64_t idx)
        noexcept(false) {
        return this->getByIndex(idx);
      }

      // syntax sugar: get a specified version of T without DSM
      std::unique_ptr<ObjectType> operator [](const __int128 ver)
        noexcept(false) {
        return this->get(ver);
      }

      // syntax sugar: get a specified version of T without DSM
      std::unique_ptr<ObjectType> operator [](const HLC & hlc)
        noexcept(false) {
        return this->get(hlc);
      }

      // get number of the versions
      virtual int64_t getNumOfVersions() noexcept(false) {
        return this->m_pLog->getLength();
      };

      virtual int64_t getEarliestIndex() noexcept(false) {
        return this->m_pLog->getEarliestIndex();
      }

      // make a version with version and mhlc clock
      virtual void set(const ObjectType &v, const __int128 & ver, const HLC &mhlc)
        noexcept(false) {
        auto size = bytes_size(v);
        char buf[size];
        bzero(buf,size);
        to_bytes(v,buf);
        this->m_pLog->append((void*)buf,size,ver,mhlc);
      };

      // make a version with version
      virtual void set(const ObjectType &v, const __int128 & ver)
        noexcept(false) {
        HLC mhlc; // generate a default timestamp for it.
        this->set(v,ver,mhlc);
      }

      // make a version
      virtual void version(const __int128 & ver)
        noexcept(false) {
        //TODO: compare if value has been changed?
        this->set(this->wrapped_obj,ver);
      }

      /** persist till version
       * @param ver version number
       * @return the given version to be persisted.
       */
      virtual const __int128 persist()
        noexcept(false){
        return this->m_pLog->persist();
      }

      // internal _NameMaker class
      class _NameMaker{
      public:
        // Constructor
        _NameMaker() noexcept(false):
        m_sObjectTypeName(typeid(ObjectType).name()) {
          this->m_iCounter = 0;
          if (pthread_spin_init(&this->m_oLck,PTHREAD_PROCESS_SHARED) != 0) {
            throw PERSIST_EXP_SPIN_INIT(errno);
          }
        }

        // Destructor
        virtual ~_NameMaker() noexcept(true) {
          pthread_spin_destroy(&this->m_oLck);
        }

        // guess a name
        std::unique_ptr<std::string> make() noexcept(false) {
          int cnt;
          if (pthread_spin_lock(&this->m_oLck) != 0) {
            throw PERSIST_EXP_SPIN_LOCK(errno);
          }
          cnt = this->m_iCounter++;
          if (pthread_spin_unlock(&this->m_oLck) != 0) {
            throw PERSIST_EXP_SPIN_UNLOCK(errno);
          }
          std::unique_ptr<std::string> ret = std::make_unique<std::string>();
          //char * buf = (char *)malloc((strlen(this->m_sObjectTypeName)+13)/8*8);
          char buf[256];
          sprintf(buf,"%s-%d",this->m_sObjectTypeName,cnt);
         // return std::make_shared<const char *>((const char*)buf);
         *ret = buf;
         return ret;
        }

      private:
        int m_iCounter;
        const char *m_sObjectTypeName;
        pthread_spinlock_t m_oLck;
      };

  protected:
      // wrapped objected
      ObjectType wrapped_obj;

      // PersistLog
      PersistLog * m_pLog;

      // get the static name maker.
      static _NameMaker & getNameMaker();
  };

  // How many times the constructor was called.
  template <typename ObjectType, StorageType storageType>
  typename Persistent<ObjectType,storageType>::_NameMaker &
    Persistent<ObjectType,storageType>::getNameMaker() noexcept(false) {
    static Persistent<ObjectType,storageType>::_NameMaker nameMaker;
    return nameMaker;
  }

  template <typename ObjectType>
  class Volatile: public Persistent<ObjectType,ST_MEM>{
  public:
    // constructor: this will guess the objectname form ObjectType
    Volatile (
      FuncRegisterCallback func_register_cb=nullptr,
      const char * object_name = (*Persistent<ObjectType,ST_MEM>::getNameMaker().make()).c_str())
      noexcept(false):
      Persistent<ObjectType,ST_MEM>(func_register_cb,object_name){
    };
    // destructor:
    virtual ~Volatile() noexcept(false){
      // do nothing
    };
  };

  /*
   * PersistentRegistry is a book for all the Persistent<T> or Volatile<T>
   * variables. Replicated<T> class should maintain such a registry to perform
   * the following operations:
   * - makeVersion(const __int128 & ver): create a version
   * - persist(): persist the existing versions
   * - trim(const __int128 & ver): trim all versions earlier than ver
   */
  class PersistentRegistry{
  public:
    PersistentRegistry() {
      //
    };
    virtual ~PersistentRegistry() {
      this->_registry.clear();
    };
    #define VERSION_FUNC_IDX (0)
    #define PERSIST_FUNC_IDX (1)
    #define TRIM_FUNC_IDX (2)
    void makeVersion(const __int128 & ver) noexcept(false) {
      callFunc<VERSION_FUNC_IDX>(ver);
    };
    void persist() noexcept(false) {
      callFunc<PERSIST_FUNC_IDX>();
    };
    void trim(const __int128 & ver) noexcept(false) {
      callFunc<TRIM_FUNC_IDX>(ver);
    };
    void registerPersist(const VersionFunc &vf,const PersistFunc &pf,const TrimFunc &tf) noexcept(false) {
      //this->_registry.push_back(std::make_tuple<VersionFunc,PersistFunc,TrimFunc>(
      this->_registry.push_back(std::make_tuple(vf,pf,tf));
    };
  protected:
    std::vector<std::tuple<VersionFunc,PersistFunc,TrimFunc>> _registry;
    template<int funcIdx,typename ... Args >
    void callFunc(Args ... args) {
      for (auto itr = this->_registry.begin();
        itr != this->_registry.end(); ++itr) {
        std::get<funcIdx>(*itr)(args ...);
      }
    };
  };
}

#endif//PERSIST_VAR_H
<|endoftext|>


<commit_before>/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * 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 <thrift/lib/cpp2/async/Interaction.h>

namespace apache {
namespace thrift {

bool Tile::__fbthrift_maybeEnqueue(
    std::unique_ptr<concurrency::Runnable>&&,
    const concurrency::ThreadManager::ExecutionScope&) {
  return false;
}

void Tile::decRef(folly::EventBase& eb, InteractionReleaseEvent event) {
  eb.dcheckIsInEventBaseThread();
  DCHECK_GT(refCount_, 0u);

  if (event != InteractionReleaseEvent::STREAM_END) {
    if (auto serial = dynamic_cast<SerialInteractionTile*>(this)) {
      auto& queue = serial->taskQueue_;
      if (!queue.empty()) {
        DCHECK_GT(
            refCount_ + (event == InteractionReleaseEvent::STREAM_TRANSFER),
            queue.size());
        auto& [task, scope] = queue.front();
        tm_->getKeepAlive(
               std::move(scope), concurrency::ThreadManager::Source::INTERNAL)
            ->add([task = std::move(task)]() { task->run(); });
        queue.pop();
      } else {
        serial->hasActiveRequest_ = false;
      }
    }
  }

  if (event != InteractionReleaseEvent::STREAM_TRANSFER && --refCount_ == 0) {
    if (tm_) {
      std::move(tm_).add([this](auto&&) { delete this; });
    } else {
      delete this;
    }
  }
}

#if FOLLY_HAS_COROUTINES
// Called as soon as termination signal is received
// Destructor may or may not run as soon as this completes
// Not called if connection closes before termination received
folly::coro::Task<void> Tile::co_onTermination() {
  co_return;
}
#endif

void Tile::__fbthrift_onTermination(TilePtr ptr, folly::EventBase& eb) {
#if FOLLY_HAS_COROUTINES
  eb.dcheckIsInEventBaseThread();
  auto* tile = ptr.get();
  if (tile->tm_) {
    tile->co_onTermination().scheduleOn(tile->tm_).start(
        [ptr = std::move(ptr)](auto&&) {});
  } else {
    tile->co_onTermination().scheduleOn(&eb).startInlineUnsafe(
        [ptr = std::move(ptr)](auto&&) {});
  }
#endif
}

bool TilePromise::__fbthrift_maybeEnqueue(
    std::unique_ptr<concurrency::Runnable>&& task,
    const concurrency::ThreadManager::ExecutionScope& scope) {
  // If the first request is a factory method we mustn't enqueue it
  if (std::exchange(factoryPending_, false)) {
    return false;
  }

  // If the factory failed, accept the task and immediately fail it
  if (factoryEx_) {
    dynamic_cast<InteractionTask&>(*task).failWith(
        factoryEx_->ew, factoryEx_->exCode);
    return true;
  }

  continuations_.emplace(std::move(task), scope);
  return true;
}

void TilePromise::fulfill(
    Tile& tile, concurrency::ThreadManager& tm, folly::EventBase& eb) {
  tile.tm_ = &tm;
  if (terminated_) {
    Tile::__fbthrift_onTermination({&tile, &eb}, eb);
  }

  // Inline destruction of this is possible at the setTile()
  auto continuations = std::move(continuations_);
  while (!continuations.empty()) {
    auto& [task, scope] = continuations.front();
    dynamic_cast<InteractionTask&>(*task).setTile({&tile, &eb});
    if (!tile.__fbthrift_maybeEnqueue(std::move(task), scope)) {
      tm.getKeepAlive(
            std::move(scope),
            concurrency::ThreadManager::Source::EXISTING_INTERACTION)
          ->add([task = std::move(task)]() mutable { task->run(); });
    }
    continuations.pop();
  }
}

void TilePromise::failWith(
    folly::exception_wrapper ew, const std::string& exCode) {
  auto continuations = std::move(continuations_);
  factoryEx_ =
      folly::copy_to_unique_ptr(TilePromise::FactoryException{ew, exCode});
  while (!continuations.empty()) {
    auto& [task, scope] = continuations.front();
    dynamic_cast<InteractionTask&>(*task).failWith(ew, exCode);
    continuations.pop();
  }
}

#if FOLLY_HAS_COROUTINES
folly::coro::Task<void> TilePromise::co_onTermination() {
  DCHECK(!tm_);
  terminated_ = true;
  co_return;
}
#endif

bool SerialInteractionTile::__fbthrift_maybeEnqueue(
    std::unique_ptr<concurrency::Runnable>&& task,
    const concurrency::ThreadManager::ExecutionScope& scope) {
  if (hasActiveRequest_) {
    taskQueue_.emplace(std::move(task), scope);
    return true;
  }

  hasActiveRequest_ = true;
  return false;
}

void TilePtr::release(InteractionReleaseEvent event) {
  if (tile_) {
    if (eb_->inRunningEventBaseThread()) {
      tile_->decRef(*eb_, event);
      tile_ = nullptr;
    } else {
      std::move(eb_).add([tile = std::exchange(tile_, nullptr),
                          event](auto&& eb) { tile->decRef(*eb, event); });
    }
  }
}

TileStreamGuard::TileStreamGuard(TilePtr&& ptr) : tile_(std::move(ptr)) {
  if (auto tile = tile_.get()) {
    tile_->decRef(*tile_.eb_, InteractionReleaseEvent::STREAM_TRANSFER);
  }
}

} // namespace thrift
} // namespace apache
<commit_msg>Add a missing MAYBE_UNUSED<commit_after>/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * 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 <thrift/lib/cpp2/async/Interaction.h>

namespace apache {
namespace thrift {

bool Tile::__fbthrift_maybeEnqueue(
    std::unique_ptr<concurrency::Runnable>&&,
    const concurrency::ThreadManager::ExecutionScope&) {
  return false;
}

void Tile::decRef(folly::EventBase& eb, InteractionReleaseEvent event) {
  eb.dcheckIsInEventBaseThread();
  DCHECK_GT(refCount_, 0u);

  if (event != InteractionReleaseEvent::STREAM_END) {
    if (auto serial = dynamic_cast<SerialInteractionTile*>(this)) {
      auto& queue = serial->taskQueue_;
      if (!queue.empty()) {
        DCHECK_GT(
            refCount_ + (event == InteractionReleaseEvent::STREAM_TRANSFER),
            queue.size());
        auto& [task, scope] = queue.front();
        tm_->getKeepAlive(
               std::move(scope), concurrency::ThreadManager::Source::INTERNAL)
            ->add([task = std::move(task)]() { task->run(); });
        queue.pop();
      } else {
        serial->hasActiveRequest_ = false;
      }
    }
  }

  if (event != InteractionReleaseEvent::STREAM_TRANSFER && --refCount_ == 0) {
    if (tm_) {
      std::move(tm_).add([this](auto&&) { delete this; });
    } else {
      delete this;
    }
  }
}

#if FOLLY_HAS_COROUTINES
// Called as soon as termination signal is received
// Destructor may or may not run as soon as this completes
// Not called if connection closes before termination received
folly::coro::Task<void> Tile::co_onTermination() {
  co_return;
}
#endif

void Tile::__fbthrift_onTermination(
    FOLLY_MAYBE_UNUSED TilePtr ptr, FOLLY_MAYBE_UNUSED folly::EventBase& eb) {
#if FOLLY_HAS_COROUTINES
  eb.dcheckIsInEventBaseThread();
  auto* tile = ptr.get();
  if (tile->tm_) {
    tile->co_onTermination().scheduleOn(tile->tm_).start(
        [ptr = std::move(ptr)](auto&&) {});
  } else {
    tile->co_onTermination().scheduleOn(&eb).startInlineUnsafe(
        [ptr = std::move(ptr)](auto&&) {});
  }
#endif
}

bool TilePromise::__fbthrift_maybeEnqueue(
    std::unique_ptr<concurrency::Runnable>&& task,
    const concurrency::ThreadManager::ExecutionScope& scope) {
  // If the first request is a factory method we mustn't enqueue it
  if (std::exchange(factoryPending_, false)) {
    return false;
  }

  // If the factory failed, accept the task and immediately fail it
  if (factoryEx_) {
    dynamic_cast<InteractionTask&>(*task).failWith(
        factoryEx_->ew, factoryEx_->exCode);
    return true;
  }

  continuations_.emplace(std::move(task), scope);
  return true;
}

void TilePromise::fulfill(
    Tile& tile, concurrency::ThreadManager& tm, folly::EventBase& eb) {
  tile.tm_ = &tm;
  if (terminated_) {
    Tile::__fbthrift_onTermination({&tile, &eb}, eb);
  }

  // Inline destruction of this is possible at the setTile()
  auto continuations = std::move(continuations_);
  while (!continuations.empty()) {
    auto& [task, scope] = continuations.front();
    dynamic_cast<InteractionTask&>(*task).setTile({&tile, &eb});
    if (!tile.__fbthrift_maybeEnqueue(std::move(task), scope)) {
      tm.getKeepAlive(
            std::move(scope),
            concurrency::ThreadManager::Source::EXISTING_INTERACTION)
          ->add([task = std::move(task)]() mutable { task->run(); });
    }
    continuations.pop();
  }
}

void TilePromise::failWith(
    folly::exception_wrapper ew, const std::string& exCode) {
  auto continuations = std::move(continuations_);
  factoryEx_ =
      folly::copy_to_unique_ptr(TilePromise::FactoryException{ew, exCode});
  while (!continuations.empty()) {
    auto& [task, scope] = continuations.front();
    dynamic_cast<InteractionTask&>(*task).failWith(ew, exCode);
    continuations.pop();
  }
}

#if FOLLY_HAS_COROUTINES
folly::coro::Task<void> TilePromise::co_onTermination() {
  DCHECK(!tm_);
  terminated_ = true;
  co_return;
}
#endif

bool SerialInteractionTile::__fbthrift_maybeEnqueue(
    std::unique_ptr<concurrency::Runnable>&& task,
    const concurrency::ThreadManager::ExecutionScope& scope) {
  if (hasActiveRequest_) {
    taskQueue_.emplace(std::move(task), scope);
    return true;
  }

  hasActiveRequest_ = true;
  return false;
}

void TilePtr::release(InteractionReleaseEvent event) {
  if (tile_) {
    if (eb_->inRunningEventBaseThread()) {
      tile_->decRef(*eb_, event);
      tile_ = nullptr;
    } else {
      std::move(eb_).add([tile = std::exchange(tile_, nullptr),
                          event](auto&& eb) { tile->decRef(*eb, event); });
    }
  }
}

TileStreamGuard::TileStreamGuard(TilePtr&& ptr) : tile_(std::move(ptr)) {
  if (auto tile = tile_.get()) {
    tile_->decRef(*tile_.eb_, InteractionReleaseEvent::STREAM_TRANSFER);
  }
}

} // namespace thrift
} // namespace apache
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2012 Tommi Maekitalo
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * As a special exception, you may use this file as part of a free
 * software library without restriction. Specifically, if other files
 * instantiate templates or use macros or inline functions from this
 * file, or you compile this file and link it with other files to
 * produce an executable, this file does not by itself cause the
 * resulting executable to be covered by the GNU General Public
 * License. This exception does not however invalidate any other
 * reasons why the executable file might be covered by the GNU Library
 * General Public License.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <tnt/tntconfig.h>

namespace tnt
{
  void operator>>= (const cxxtools::SerializationInfo& si, TntConfig::Mapping& mapping)
  {
    si.getMember("target", mapping.target);
    si.getMember("url") >>= mapping.url;
    si.getMember("vhost", mapping.vhost);
    si.getMember("method", mapping.method);
    si.getMember("pathinfo", mapping.pathinfo);
    bool ssl;
    if (si.getMember("ssl", ssl))
      mapping.ssl = ssl ? SSL_YES : SSL_NO;
    else
      mapping.ssl = SSL_ALL;

    si.getMember("args", mapping.args);
  }

  void operator>>= (const cxxtools::SerializationInfo& si, TntConfig::Listener& listener)
  {
    si.getMember("ip") >>= listener.ip;

    if (!si.getMember("port", listener.port))
      listener.port = 80;
  }

  void operator>>= (const cxxtools::SerializationInfo& si, TntConfig::SslListener& ssllistener)
  {
    si.getMember("ip") >>= ssllistener.ip;

    if (!si.getMember("port", ssllistener.port))
      ssllistener.port = 443;

    si.getMember("certificate") >>= ssllistener.certificate;

    if (!si.getMember("key", ssllistener.key))
      ssllistener.key = ssllistener.certificate;
  }

  void operator>>= (const cxxtools::SerializationInfo& si, TntConfig& config)
  {
    TntConfig::MappingsType mappings;
    if (si.getMember("mappings", mappings))
      config.mappings.insert(config.mappings.end(), mappings.begin(), mappings.end());

    TntConfig::ListenersType listeners;
    TntConfig::SslListenersType ssllisteners;

    const cxxtools::SerializationInfo& lit = si.getMember("listeners");
    for (cxxtools::SerializationInfo::ConstIterator it = lit.begin(); it != lit.end(); ++it)
    {
      if (it->findMember("certificate") != 0)
      {
        ssllisteners.resize(ssllisteners.size() + 1);
        *it >>= ssllisteners.back();
      }
      else
      {
        listeners.resize(listeners.size() + 1);
        *it >>= listeners.back();
      }
    }

    config.listeners.insert(config.listeners.end(), listeners.begin(), listeners.end());
    config.ssllisteners.insert(config.ssllisteners.end(), ssllisteners.begin(), ssllisteners.end());

    if (config.listeners.empty() && config.ssllisteners.empty())
    {
      config.listeners.resize(1);
      config.listeners.back().port = 80;
    }

    si.getMember("maxRequestSize", config.maxRequestSize);
    si.getMember("maxRequestTime", config.maxRequestTime);
    si.getMember("user", config.user);
    si.getMember("group", config.group);
    si.getMember("dir", config.dir);
    si.getMember("chrootdir", config.chrootdir);
    si.getMember("pidfile", config.pidfile);
    si.getMember("daemon", config.daemon);
    si.getMember("minThreads", config.minThreads);
    si.getMember("maxThreads", config.maxThreads);
    si.getMember("threadStartDelay", config.threadStartDelay);
    si.getMember("queueSize", config.queueSize);
    si.getMember("compPath", config.compPath);
    si.getMember("socketBufferSize", config.socketBufferSize);
    si.getMember("socketReadTimeout", config.socketReadTimeout);
    si.getMember("socketWriteTimeout", config.socketWriteTimeout);
    si.getMember("keepAliveTimeout", config.keepAliveTimeout);
    si.getMember("keepAliveMax", config.keepAliveMax);
    si.getMember("sessionTimeout", config.sessionTimeout);
    si.getMember("listenBacklog", config.listenBacklog);
    si.getMember("listenRetry", config.listenRetry);
    si.getMember("enableCompression", config.enableCompression);
    si.getMember("minCompressSize", config.minCompressSize);
    si.getMember("mimeDb", config.mimeDb);
    si.getMember("maxUrlMapCache", config.maxUrlMapCache);
    si.getMember("defaultContentType", config.defaultContentType);
    si.getMember("accessLog", config.accessLog);
    si.getMember("errorLog", config.errorLog);
    si.getMember("backgroundTasks", config.backgroundTasks);
    si.getMember("timerSleep", config.timerSleep);
    si.getMember("mimeDb", config.mimeDb);
    si.getMember("documentRoot", config.documentRoot);
    si.getMember("includes", config.includes);

    config.config = si;

    const cxxtools::SerializationInfo* p = si.findMember("environment");
    if (p)
    {
      for (cxxtools::SerializationInfo::ConstIterator it = p->begin(); it != p->end(); ++it)
      {
        std::string value;
        it->getValue(value);
        config.environment[it->name()] = value;
      }
    }

  }

  TntConfig::TntConfig()
    : maxRequestSize(0),
      maxRequestTime(600),
      daemon(false),
      minThreads(5),
      maxThreads(100),
      threadStartDelay(10),
      queueSize(1000),
      socketBufferSize(16384),
      socketReadTimeout(10),
      socketWriteTimeout(10000),
      keepAliveTimeout(15000),
      keepAliveMax(1000),
      sessionTimeout(300),
      listenBacklog(512),
      listenRetry(5),
      enableCompression(true),
      minCompressSize(1024),
      mimeDb("/etc/mime.types"),
      maxUrlMapCache(8192),
      defaultContentType("text/html; charset=UTF-8"),
      backgroundTasks(5),
      timerSleep(10)
  { }

  TntConfig& TntConfig::it()
  {
    static TntConfig theConfig;
    return theConfig;
  }
}
<commit_msg>remove duplicate entry from tntconfig<commit_after>/*
 * Copyright (C) 2012 Tommi Maekitalo
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * As a special exception, you may use this file as part of a free
 * software library without restriction. Specifically, if other files
 * instantiate templates or use macros or inline functions from this
 * file, or you compile this file and link it with other files to
 * produce an executable, this file does not by itself cause the
 * resulting executable to be covered by the GNU General Public
 * License. This exception does not however invalidate any other
 * reasons why the executable file might be covered by the GNU Library
 * General Public License.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <tnt/tntconfig.h>

namespace tnt
{
  void operator>>= (const cxxtools::SerializationInfo& si, TntConfig::Mapping& mapping)
  {
    si.getMember("target", mapping.target);
    si.getMember("url") >>= mapping.url;
    si.getMember("vhost", mapping.vhost);
    si.getMember("method", mapping.method);
    si.getMember("pathinfo", mapping.pathinfo);
    bool ssl;
    if (si.getMember("ssl", ssl))
      mapping.ssl = ssl ? SSL_YES : SSL_NO;
    else
      mapping.ssl = SSL_ALL;

    si.getMember("args", mapping.args);
  }

  void operator>>= (const cxxtools::SerializationInfo& si, TntConfig::Listener& listener)
  {
    si.getMember("ip") >>= listener.ip;

    if (!si.getMember("port", listener.port))
      listener.port = 80;
  }

  void operator>>= (const cxxtools::SerializationInfo& si, TntConfig::SslListener& ssllistener)
  {
    si.getMember("ip") >>= ssllistener.ip;

    if (!si.getMember("port", ssllistener.port))
      ssllistener.port = 443;

    si.getMember("certificate") >>= ssllistener.certificate;

    if (!si.getMember("key", ssllistener.key))
      ssllistener.key = ssllistener.certificate;
  }

  void operator>>= (const cxxtools::SerializationInfo& si, TntConfig& config)
  {
    TntConfig::MappingsType mappings;
    if (si.getMember("mappings", mappings))
      config.mappings.insert(config.mappings.end(), mappings.begin(), mappings.end());

    TntConfig::ListenersType listeners;
    TntConfig::SslListenersType ssllisteners;

    const cxxtools::SerializationInfo& lit = si.getMember("listeners");
    for (cxxtools::SerializationInfo::ConstIterator it = lit.begin(); it != lit.end(); ++it)
    {
      if (it->findMember("certificate") != 0)
      {
        ssllisteners.resize(ssllisteners.size() + 1);
        *it >>= ssllisteners.back();
      }
      else
      {
        listeners.resize(listeners.size() + 1);
        *it >>= listeners.back();
      }
    }

    config.listeners.insert(config.listeners.end(), listeners.begin(), listeners.end());
    config.ssllisteners.insert(config.ssllisteners.end(), ssllisteners.begin(), ssllisteners.end());

    if (config.listeners.empty() && config.ssllisteners.empty())
    {
      config.listeners.resize(1);
      config.listeners.back().port = 80;
    }

    si.getMember("maxRequestSize", config.maxRequestSize);
    si.getMember("maxRequestTime", config.maxRequestTime);
    si.getMember("user", config.user);
    si.getMember("group", config.group);
    si.getMember("dir", config.dir);
    si.getMember("chrootdir", config.chrootdir);
    si.getMember("pidfile", config.pidfile);
    si.getMember("daemon", config.daemon);
    si.getMember("minThreads", config.minThreads);
    si.getMember("maxThreads", config.maxThreads);
    si.getMember("threadStartDelay", config.threadStartDelay);
    si.getMember("queueSize", config.queueSize);
    si.getMember("compPath", config.compPath);
    si.getMember("socketBufferSize", config.socketBufferSize);
    si.getMember("socketReadTimeout", config.socketReadTimeout);
    si.getMember("socketWriteTimeout", config.socketWriteTimeout);
    si.getMember("keepAliveTimeout", config.keepAliveTimeout);
    si.getMember("keepAliveMax", config.keepAliveMax);
    si.getMember("sessionTimeout", config.sessionTimeout);
    si.getMember("listenBacklog", config.listenBacklog);
    si.getMember("listenRetry", config.listenRetry);
    si.getMember("enableCompression", config.enableCompression);
    si.getMember("minCompressSize", config.minCompressSize);
    si.getMember("mimeDb", config.mimeDb);
    si.getMember("maxUrlMapCache", config.maxUrlMapCache);
    si.getMember("defaultContentType", config.defaultContentType);
    si.getMember("accessLog", config.accessLog);
    si.getMember("errorLog", config.errorLog);
    si.getMember("backgroundTasks", config.backgroundTasks);
    si.getMember("timerSleep", config.timerSleep);
    si.getMember("documentRoot", config.documentRoot);
    si.getMember("includes", config.includes);

    config.config = si;

    const cxxtools::SerializationInfo* p = si.findMember("environment");
    if (p)
    {
      for (cxxtools::SerializationInfo::ConstIterator it = p->begin(); it != p->end(); ++it)
      {
        std::string value;
        it->getValue(value);
        config.environment[it->name()] = value;
      }
    }

  }

  TntConfig::TntConfig()
    : maxRequestSize(0),
      maxRequestTime(600),
      daemon(false),
      minThreads(5),
      maxThreads(100),
      threadStartDelay(10),
      queueSize(1000),
      socketBufferSize(16384),
      socketReadTimeout(10),
      socketWriteTimeout(10000),
      keepAliveTimeout(15000),
      keepAliveMax(1000),
      sessionTimeout(300),
      listenBacklog(512),
      listenRetry(5),
      enableCompression(true),
      minCompressSize(1024),
      mimeDb("/etc/mime.types"),
      maxUrlMapCache(8192),
      defaultContentType("text/html; charset=UTF-8"),
      backgroundTasks(5),
      timerSleep(10)
  { }

  TntConfig& TntConfig::it()
  {
    static TntConfig theConfig;
    return theConfig;
  }
}
<|endoftext|>


<commit_before>#include "geometry_utils.hpp"
#include "search_engine.hpp"
#include "search_query.hpp"
#include "suggest.hpp"

#include "storage/country_info_getter.hpp"

#include "indexer/categories_holder.hpp"
#include "indexer/search_string_utils.hpp"
#include "indexer/mercator.hpp"
#include "indexer/scales.hpp"
#include "indexer/classificator.hpp"

#include "platform/platform.hpp"

#include "geometry/distance_on_sphere.hpp"

#include "base/stl_add.hpp"

#include "std/map.hpp"
#include "std/vector.hpp"
#include "std/bind.hpp"

#include "3party/Alohalytics/src/alohalytics.h"

namespace search
{

double const DIST_EQUAL_QUERY = 100.0;

using TSuggestsContainer = vector<Suggest>;

class EngineData
{
public:
  EngineData(Reader * categoriesR) : m_categories(categoriesR) {}

  CategoriesHolder m_categories;
  TSuggestsContainer m_suggests;
};

namespace
{

class InitSuggestions
{
  using TSuggestMap = map<pair<strings::UniString, int8_t>, uint8_t>;
  TSuggestMap m_suggests;

public:
  void operator() (CategoriesHolder::Category::Name const & name)
  {
    if (name.m_prefixLengthToSuggest != CategoriesHolder::Category::EMPTY_PREFIX_LENGTH)
    {
      strings::UniString const uniName = NormalizeAndSimplifyString(name.m_name);

      uint8_t & score = m_suggests[make_pair(uniName, name.m_locale)];
      if (score == 0 || score > name.m_prefixLengthToSuggest)
        score = name.m_prefixLengthToSuggest;
    }
  }

  void GetSuggests(TSuggestsContainer & cont) const
  {
    cont.reserve(m_suggests.size());
    for (TSuggestMap::const_iterator i = m_suggests.begin(); i != m_suggests.end(); ++i)
      cont.push_back(Suggest(i->first.first, i->second, i->first.second));
  }
};

}

Engine::Engine(Index & index, Reader * categoriesR, storage::CountryInfoGetter const & infoGetter,
               string const & locale, unique_ptr<SearchQueryFactory> && factory)
  : m_factory(move(factory))
  , m_data(make_unique<EngineData>(categoriesR))
{
  m_isReadyThread.clear();

  InitSuggestions doInit;
  m_data->m_categories.ForEachName(bind<void>(ref(doInit), _1));
  doInit.GetSuggests(m_data->m_suggests);

  m_query =
      m_factory->BuildSearchQuery(index, m_data->m_categories, m_data->m_suggests, infoGetter);
  m_query->SetPreferredLocale(locale);
}

Engine::~Engine()
{
}

void Engine::SupportOldFormat(bool b)
{
  m_query->SupportOldFormat(b);
}

void Engine::PrepareSearch(m2::RectD const & viewport)
{
  // bind does copy of all rects
  GetPlatform().RunAsync(bind(&Engine::SetViewportAsync, this, viewport));
}

bool Engine::Search(SearchParams const & params, m2::RectD const & viewport)
{
  // Check for equal query.
  // There is no need to synchronize here for reading m_params,
  // because this function is always called from main thread (one-by-one for queries).

  if (!params.IsForceSearch() &&
      m_params.IsEqualCommon(params) &&
      (m_viewport.IsValid() && IsEqualMercator(m_viewport, viewport, DIST_EQUAL_QUERY)))
  {
    if (m_params.IsSearchAroundPosition() &&
        ms::DistanceOnEarth(m_params.m_lat, m_params.m_lon, params.m_lat, params.m_lon) > DIST_EQUAL_QUERY)
    {
      // Go forward only if we search around position and it's changed significantly.
    }
    else
    {
      // Skip this query in all other cases.
      return false;
    }
  }

  {
    // Assign new search params.
    // Put the synch here, because this params are reading in search threads.
    threads::MutexGuard guard(m_updateMutex);
    m_params = params;
    m_viewport = viewport;
  }

  // Run task.
  GetPlatform().RunAsync(bind(&Engine::SearchAsync, this));

  return true;
}

void Engine::SetViewportAsync(m2::RectD const & viewport)
{
  // First of all - cancel previous query.
  m_query->Cancel();

  // Enter to run new search.
  threads::MutexGuard searchGuard(m_searchMutex);

  m2::RectD r(viewport);
  (void)GetInflatedViewport(r);
  m_query->SetViewport(r, true);
}

void Engine::EmitResults(SearchParams const & params, Results & res)
{
  // Basic test of our statistics engine.
  alohalytics::LogEvent("searchEmitResults",
                        alohalytics::TStringMap({{params.m_query, strings::to_string(res.GetCount())}}));

  params.m_callback(res);
}

void Engine::SetRankPivot(SearchParams const & params,
                          m2::RectD const & viewport, bool viewportSearch)
{
  if (!viewportSearch && params.IsValidPosition())
  {
    m2::PointD const pos = MercatorBounds::FromLatLon(params.m_lat, params.m_lon);
    if (m2::Inflate(viewport, viewport.SizeX() / 4.0, viewport.SizeY() / 4.0).IsPointInside(pos))
    {
      m_query->SetRankPivot(pos);
      return;
    }
  }

  m_query->SetRankPivot(viewport.Center());
}

void Engine::SearchAsync()
{
  if (m_isReadyThread.test_and_set())
    return;

  // First of all - cancel previous query.
  m_query->Cancel();

  // Enter to run new search.
  threads::MutexGuard searchGuard(m_searchMutex);

  m_isReadyThread.clear();

  // Get current search params.
  SearchParams params;
  m2::RectD viewport;
  bool oneTimeSearch = false;

  {
    threads::MutexGuard updateGuard(m_updateMutex);
    params = m_params;

    if (params.GetSearchRect(viewport))
      oneTimeSearch = true;
    else
      viewport = m_viewport;
  }

  bool const viewportSearch = params.HasSearchMode(SearchParams::IN_VIEWPORT_ONLY);

  // Initialize query.
  m_query->Init(viewportSearch);

  SetRankPivot(params, viewport, viewportSearch);

  m_query->SetSearchInWorld(params.HasSearchMode(SearchParams::SEARCH_WORLD));

  // Language validity is checked inside
  m_query->SetInputLocale(params.m_inputLocale);

  ASSERT(!params.m_query.empty(), ());
  m_query->SetQuery(params.m_query);

  Results res;

  // Call m_query->IsCancelled() everywhere it needed without storing
  // return value.  This flag can be changed from another thread.

  m_query->SearchCoordinates(params.m_query, res);

  try
  {
    // Do search for address in all modes.
    // params.HasSearchMode(SearchParams::SEARCH_ADDRESS)

    if (viewportSearch)
    {
      m_query->SetViewport(viewport, true);
      m_query->SearchViewportPoints(res);

      if (res.GetCount() > 0)
        EmitResults(params, res);
    }
    else
    {
      while (!m_query->IsCancelled())
      {
        bool const isInflated = GetInflatedViewport(viewport);
        size_t const oldCount = res.GetCount();

        m_query->SetViewport(viewport, oneTimeSearch);
        m_query->Search(res, RESULTS_COUNT);

        size_t const newCount = res.GetCount();
        bool const exit = (oneTimeSearch || !isInflated || newCount >= RESULTS_COUNT);

        if (exit || oldCount != newCount)
          EmitResults(params, res);

        if (exit)
          break;
      }
    }
  }
  catch (Query::CancelException const &)
  {
  }

  // Make additional search in whole mwm when not enough results (only for non-empty query).
  size_t const count = res.GetCount();
  if (!viewportSearch && !m_query->IsCancelled() && count < RESULTS_COUNT)
  {
    try
    {
      m_query->SearchAdditional(res, RESULTS_COUNT);
    }
    catch (Query::CancelException const &)
    {
    }

    // Emit if we have more results.
    if (res.GetCount() > count)
      EmitResults(params, res);
  }

  // Emit finish marker to client.
  params.m_callback(Results::GetEndMarker(m_query->IsCancelled()));
}

bool Engine::GetNameByType(uint32_t type, int8_t locale, string & name) const
{
  uint8_t level = ftype::GetLevel(type);
  ASSERT_GREATER(level, 0, ());

  while (true)
  {
    if (m_data->m_categories.GetNameByType(type, locale, name))
      return true;

    if (--level == 0)
      break;

    ftype::TruncValue(type, level);
  }

  return false;
}

void Engine::ClearViewportsCache()
{
  threads::MutexGuard guard(m_searchMutex);

  m_query->ClearCaches();
}

void Engine::ClearAllCaches()
{
  //threads::MutexGuard guard(m_searchMutex);

  // Trying to lock mutex, because this function calls on memory warning notification.
  // So that allows to prevent lock of UI while search query wouldn't be processed.
  if (m_searchMutex.TryLock())
  {
    m_query->ClearCaches();

    m_searchMutex.Unlock();
  }
}

}  // namespace search
<commit_msg>[search] Hot fix for tablet layout search (viewport only mode). Will increase search viewport until get at least 30 results.<commit_after>#include "geometry_utils.hpp"
#include "search_engine.hpp"
#include "search_query.hpp"
#include "suggest.hpp"

#include "storage/country_info_getter.hpp"

#include "indexer/categories_holder.hpp"
#include "indexer/search_string_utils.hpp"
#include "indexer/mercator.hpp"
#include "indexer/scales.hpp"
#include "indexer/classificator.hpp"

#include "platform/platform.hpp"

#include "geometry/distance_on_sphere.hpp"

#include "base/stl_add.hpp"

#include "std/map.hpp"
#include "std/vector.hpp"
#include "std/bind.hpp"

#include "3party/Alohalytics/src/alohalytics.h"

namespace search
{

double const DIST_EQUAL_QUERY = 100.0;

using TSuggestsContainer = vector<Suggest>;

class EngineData
{
public:
  EngineData(Reader * categoriesR) : m_categories(categoriesR) {}

  CategoriesHolder m_categories;
  TSuggestsContainer m_suggests;
};

namespace
{

class InitSuggestions
{
  using TSuggestMap = map<pair<strings::UniString, int8_t>, uint8_t>;
  TSuggestMap m_suggests;

public:
  void operator() (CategoriesHolder::Category::Name const & name)
  {
    if (name.m_prefixLengthToSuggest != CategoriesHolder::Category::EMPTY_PREFIX_LENGTH)
    {
      strings::UniString const uniName = NormalizeAndSimplifyString(name.m_name);

      uint8_t & score = m_suggests[make_pair(uniName, name.m_locale)];
      if (score == 0 || score > name.m_prefixLengthToSuggest)
        score = name.m_prefixLengthToSuggest;
    }
  }

  void GetSuggests(TSuggestsContainer & cont) const
  {
    cont.reserve(m_suggests.size());
    for (TSuggestMap::const_iterator i = m_suggests.begin(); i != m_suggests.end(); ++i)
      cont.push_back(Suggest(i->first.first, i->second, i->first.second));
  }
};

}

Engine::Engine(Index & index, Reader * categoriesR, storage::CountryInfoGetter const & infoGetter,
               string const & locale, unique_ptr<SearchQueryFactory> && factory)
  : m_factory(move(factory))
  , m_data(make_unique<EngineData>(categoriesR))
{
  m_isReadyThread.clear();

  InitSuggestions doInit;
  m_data->m_categories.ForEachName(bind<void>(ref(doInit), _1));
  doInit.GetSuggests(m_data->m_suggests);

  m_query =
      m_factory->BuildSearchQuery(index, m_data->m_categories, m_data->m_suggests, infoGetter);
  m_query->SetPreferredLocale(locale);
}

Engine::~Engine()
{
}

void Engine::SupportOldFormat(bool b)
{
  m_query->SupportOldFormat(b);
}

void Engine::PrepareSearch(m2::RectD const & viewport)
{
  // bind does copy of all rects
  GetPlatform().RunAsync(bind(&Engine::SetViewportAsync, this, viewport));
}

bool Engine::Search(SearchParams const & params, m2::RectD const & viewport)
{
  // Check for equal query.
  // There is no need to synchronize here for reading m_params,
  // because this function is always called from main thread (one-by-one for queries).

  if (!params.IsForceSearch() &&
      m_params.IsEqualCommon(params) &&
      (m_viewport.IsValid() && IsEqualMercator(m_viewport, viewport, DIST_EQUAL_QUERY)))
  {
    if (m_params.IsSearchAroundPosition() &&
        ms::DistanceOnEarth(m_params.m_lat, m_params.m_lon, params.m_lat, params.m_lon) > DIST_EQUAL_QUERY)
    {
      // Go forward only if we search around position and it's changed significantly.
    }
    else
    {
      // Skip this query in all other cases.
      return false;
    }
  }

  {
    // Assign new search params.
    // Put the synch here, because this params are reading in search threads.
    threads::MutexGuard guard(m_updateMutex);
    m_params = params;
    m_viewport = viewport;
  }

  // Run task.
  GetPlatform().RunAsync(bind(&Engine::SearchAsync, this));

  return true;
}

void Engine::SetViewportAsync(m2::RectD const & viewport)
{
  // First of all - cancel previous query.
  m_query->Cancel();

  // Enter to run new search.
  threads::MutexGuard searchGuard(m_searchMutex);

  m2::RectD r(viewport);
  (void)GetInflatedViewport(r);
  m_query->SetViewport(r, true);
}

void Engine::EmitResults(SearchParams const & params, Results & res)
{
  // Basic test of our statistics engine.
  alohalytics::LogEvent("searchEmitResults",
                        alohalytics::TStringMap({{params.m_query, strings::to_string(res.GetCount())}}));

  params.m_callback(res);
}

void Engine::SetRankPivot(SearchParams const & params,
                          m2::RectD const & viewport, bool viewportSearch)
{
  if (!viewportSearch && params.IsValidPosition())
  {
    m2::PointD const pos = MercatorBounds::FromLatLon(params.m_lat, params.m_lon);
    if (m2::Inflate(viewport, viewport.SizeX() / 4.0, viewport.SizeY() / 4.0).IsPointInside(pos))
    {
      m_query->SetRankPivot(pos);
      return;
    }
  }

  m_query->SetRankPivot(viewport.Center());
}

void Engine::SearchAsync()
{
  if (m_isReadyThread.test_and_set())
    return;

  // First of all - cancel previous query.
  m_query->Cancel();

  // Enter to run new search.
  threads::MutexGuard searchGuard(m_searchMutex);

  m_isReadyThread.clear();

  // Get current search params.
  SearchParams params;
  m2::RectD viewport;
  bool oneTimeSearch = false;

  {
    threads::MutexGuard updateGuard(m_updateMutex);
    params = m_params;

    if (params.GetSearchRect(viewport))
      oneTimeSearch = true;
    else
      viewport = m_viewport;
  }

  bool const viewportSearch = params.HasSearchMode(SearchParams::IN_VIEWPORT_ONLY);

  // Initialize query.
  m_query->Init(viewportSearch);

  SetRankPivot(params, viewport, viewportSearch);

  m_query->SetSearchInWorld(params.HasSearchMode(SearchParams::SEARCH_WORLD));

  // Language validity is checked inside
  m_query->SetInputLocale(params.m_inputLocale);

  ASSERT(!params.m_query.empty(), ());
  m_query->SetQuery(params.m_query);

  Results res;

  // Call m_query->IsCancelled() everywhere it needed without storing
  // return value.  This flag can be changed from another thread.

  m_query->SearchCoordinates(params.m_query, res);

  try
  {
    // Do search for address in all modes.
    // params.HasSearchMode(SearchParams::SEARCH_ADDRESS)

    if (viewportSearch)
    {
      m_query->SetViewport(viewport, true);
      m_query->SearchViewportPoints(res);

      if (res.GetCount() > 0)
        EmitResults(params, res);
    }

    if (res.GetCount() < RESULTS_COUNT)
    {
      while (!m_query->IsCancelled())
      {
        bool const isInflated = GetInflatedViewport(viewport);
        size_t const oldCount = res.GetCount();

        m_query->SetViewport(viewport, oneTimeSearch);
        m_query->Search(res, RESULTS_COUNT);

        size_t const newCount = res.GetCount();
        bool const exit = (oneTimeSearch || !isInflated || newCount >= RESULTS_COUNT);

        if (exit || oldCount != newCount)
          EmitResults(params, res);

        if (exit)
          break;
      }
    }
  }
  catch (Query::CancelException const &)
  {
  }

  // Make additional search in whole mwm when not enough results (only for non-empty query).
  size_t const count = res.GetCount();
  if (!m_query->IsCancelled() && count < RESULTS_COUNT)
  {
    try
    {
      m_query->SearchAdditional(res, RESULTS_COUNT);
    }
    catch (Query::CancelException const &)
    {
    }

    // Emit if we have more results.
    if (res.GetCount() > count)
      EmitResults(params, res);
  }

  // Emit finish marker to client.
  params.m_callback(Results::GetEndMarker(m_query->IsCancelled()));
}

bool Engine::GetNameByType(uint32_t type, int8_t locale, string & name) const
{
  uint8_t level = ftype::GetLevel(type);
  ASSERT_GREATER(level, 0, ());

  while (true)
  {
    if (m_data->m_categories.GetNameByType(type, locale, name))
      return true;

    if (--level == 0)
      break;

    ftype::TruncValue(type, level);
  }

  return false;
}

void Engine::ClearViewportsCache()
{
  threads::MutexGuard guard(m_searchMutex);

  m_query->ClearCaches();
}

void Engine::ClearAllCaches()
{
  //threads::MutexGuard guard(m_searchMutex);

  // Trying to lock mutex, because this function calls on memory warning notification.
  // So that allows to prevent lock of UI while search query wouldn't be processed.
  if (m_searchMutex.TryLock())
  {
    m_query->ClearCaches();

    m_searchMutex.Unlock();
  }
}

}  // namespace search
<|endoftext|>


<commit_before>#ifndef _MZ_NET_INCLUDE_HPP_
#define _MZ_NET_INCLUDE_HPP_

#include "Metazion/Overall.hpp"

#define DECL_NAMESPACE_MZ_NET_BEGIN     namespace mz { namespace net {
#define DECL_NAMESPACE_MZ_NET_END       } }
#define USING_NAMESPACE_MZ_NET          using namespace mz::net;
#define USING_NAMESPACE_NET             using namespace net;
#define NS_MZ_NET                       mz::net
#define NS_NET                          net

#if defined(MZ_PLATFORM_LINUX1)
#   define NETWORK_USE_EPOLL_MODEL
#elif defined(MZ_PLATFORM_WINDOWS1)
#   define NETWORK_USE_IOCP_MODEL
#else
#   define NETWORK_USE_SELECT_MODEL
#endif

#endif // _MZ_NET_INCLUDE_HPP_
<commit_msg>Network multiple platform condition macros<commit_after>#ifndef _MZ_NET_INCLUDE_HPP_
#define _MZ_NET_INCLUDE_HPP_

#include "Metazion/Overall.hpp"

#define DECL_NAMESPACE_MZ_NET_BEGIN     namespace mz { namespace net {
#define DECL_NAMESPACE_MZ_NET_END       } }
#define USING_NAMESPACE_MZ_NET          using namespace mz::net;
#define USING_NAMESPACE_NET             using namespace net;
#define NS_MZ_NET                       mz::net
#define NS_NET                          net

#if defined(MZ_PLATFORM_LINUX)
#   define NETWORK_USE_EPOLL_MODEL
#elif defined(MZ_PLATFORM_WINDOWS)
#   define NETWORK_USE_IOCP_MODEL
#else
#   define NETWORK_USE_SELECT_MODEL
#endif

#endif // _MZ_NET_INCLUDE_HPP_
<|endoftext|>


<commit_before>//===- llvm/Analysis/BasicAliasAnalysis.h - Alias Analysis Impl -*- C++ -*-===//
//
// This file defines the default implementation of the Alias Analysis interface
// that simply implements a few identities (two different globals cannot alias,
// etc), but otherwise does no analysis.
//
//===----------------------------------------------------------------------===//

#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Pass.h"
#include "llvm/iMemory.h"
#include "llvm/iOther.h"
#include "llvm/ConstantHandling.h"
#include "llvm/GlobalValue.h"
#include "llvm/DerivedTypes.h"
#include "llvm/Target/TargetData.h"

// Make sure that anything that uses AliasAnalysis pulls in this file...
void BasicAAStub() {}

class GetElementPtrInst;
namespace {
  struct BasicAliasAnalysis : public ImmutablePass, public AliasAnalysis {

    virtual void getAnalysisUsage(AnalysisUsage &AU) const {
      AliasAnalysis::getAnalysisUsage(AU);
    }

    virtual void initializePass();

    // alias - This is the only method here that does anything interesting...
    //
    AliasResult alias(const Value *V1, unsigned V1Size,
                      const Value *V2, unsigned V2Size);
  private:
    // CheckGEPInstructions - Check two GEP instructions of compatible types and
    // equal number of arguments.  This checks to see if the index expressions
    // preclude the pointers from aliasing...
    AliasResult CheckGEPInstructions(GetElementPtrInst *GEP1, unsigned G1Size,
                                     GetElementPtrInst *GEP2, unsigned G2Size);
  };

  // Register this pass...
  RegisterOpt<BasicAliasAnalysis>
  X("basicaa", "Basic Alias Analysis (default AA impl)");

  // Declare that we implement the AliasAnalysis interface
  RegisterAnalysisGroup<AliasAnalysis, BasicAliasAnalysis, true> Y;
}  // End of anonymous namespace

void BasicAliasAnalysis::initializePass() {
  InitializeAliasAnalysis(this);
}

// hasUniqueAddress - Return true if the
static inline bool hasUniqueAddress(const Value *V) {
  return isa<GlobalValue>(V) || isa<MallocInst>(V) || isa<AllocaInst>(V);
}

static const Value *getUnderlyingObject(const Value *V) {
  if (!isa<PointerType>(V->getType())) return 0;

  // If we are at some type of object... return it.
  if (hasUniqueAddress(V)) return V;

  // Traverse through different addressing mechanisms...
  if (const Instruction *I = dyn_cast<Instruction>(V)) {
    if (isa<CastInst>(I) || isa<GetElementPtrInst>(I))
      return getUnderlyingObject(I->getOperand(0));
  }
  return 0;
}

// alias - Provide a bunch of ad-hoc rules to disambiguate in common cases, such
// as array references.  Note that this function is heavily tail recursive.
// Hopefully we have a smart C++ compiler.  :)
//
AliasAnalysis::AliasResult
BasicAliasAnalysis::alias(const Value *V1, unsigned V1Size,
                          const Value *V2, unsigned V2Size) {
  // Strip off constant pointer refs if they exist
  if (const ConstantPointerRef *CPR = dyn_cast<ConstantPointerRef>(V1))
    V1 = CPR->getValue();
  if (const ConstantPointerRef *CPR = dyn_cast<ConstantPointerRef>(V2))
    V2 = CPR->getValue();

  // Are we checking for alias of the same value?
  if (V1 == V2) return MustAlias;

  if ((!isa<PointerType>(V1->getType()) || !isa<PointerType>(V2->getType())) &&
      V1->getType() != Type::LongTy && V2->getType() != Type::LongTy)
    return NoAlias;  // Scalars cannot alias each other

  // Strip off cast instructions...
  if (const Instruction *I = dyn_cast<CastInst>(V1))
    return alias(I->getOperand(0), V1Size, V2, V2Size);
  if (const Instruction *I = dyn_cast<CastInst>(V2))
    return alias(V1, V1Size, I->getOperand(0), V2Size);

  // Figure out what objects these things are pointing to if we can...
  const Value *O1 = getUnderlyingObject(V1);
  const Value *O2 = getUnderlyingObject(V2);

  // Pointing at a discernable object?
  if (O1 && O2) {
    // If they are two different objects, we know that we have no alias...
    if (O1 != O2) return NoAlias;

    // If they are the same object, they we can look at the indexes.  If they
    // index off of the object is the same for both pointers, they must alias.
    // If they are provably different, they must not alias.  Otherwise, we can't
    // tell anything.
  } else if (O1 && isa<ConstantPointerNull>(V2)) {
    return NoAlias;                    // Unique values don't alias null
  } else if (O2 && isa<ConstantPointerNull>(V1)) {
    return NoAlias;                    // Unique values don't alias null
  }

  // If we have two gep instructions with identical indices, return an alias
  // result equal to the alias result of the original pointer...
  //
  if (const GetElementPtrInst *GEP1 = dyn_cast<GetElementPtrInst>(V1))
    if (const GetElementPtrInst *GEP2 = dyn_cast<GetElementPtrInst>(V2))
      if (GEP1->getNumOperands() == GEP2->getNumOperands() &&
          GEP1->getOperand(0)->getType() == GEP2->getOperand(0)->getType()) {
        AliasResult GAlias =
          CheckGEPInstructions((GetElementPtrInst*)GEP1, V1Size,
                               (GetElementPtrInst*)GEP2, V2Size);
        if (GAlias != MayAlias)
          return GAlias;
      }

  // Check to see if these two pointers are related by a getelementptr
  // instruction.  If one pointer is a GEP with a non-zero index of the other
  // pointer, we know they cannot alias.
  //
  if (isa<GetElementPtrInst>(V2)) {
    std::swap(V1, V2);
    std::swap(V1Size, V2Size);
  }

  if (V1Size != ~0U && V2Size != ~0U)
    if (const GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(V1)) {
      AliasResult R = alias(GEP->getOperand(0), V1Size, V2, V2Size);
      if (R == NoAlias) return NoAlias;
      if (R == MustAlias) {
        // If there is at least one non-zero constant index, we know they cannot
        // alias.
        bool ConstantFound = false;
        for (unsigned i = 1, e = GEP->getNumOperands(); i != e; ++i)
          if (const Constant *C = dyn_cast<Constant>(GEP->getOperand(i)))
            if (!C->isNullValue()) {
              ConstantFound = true;
              break;
          }
        if (ConstantFound) {
          if (V2Size <= 1 && V1Size <= 1)  // Just pointer check?
            return NoAlias;

          // Otherwise we have to check to see that the distance is more than
          // the size of the argument... build an index vector that is equal to
          // the arguments provided, except substitute 0's for any variable
          // indexes we find...

          std::vector<Value*> Indices;
          Indices.reserve(GEP->getNumOperands()-1);
          for (unsigned i = 1; i != GEP->getNumOperands(); ++i)
            if (const Constant *C = dyn_cast<Constant>(GEP->getOperand(i)))
              Indices.push_back((Value*)C);
            else
              Indices.push_back(Constant::getNullValue(Type::LongTy));
          const Type *Ty = GEP->getOperand(0)->getType();
          int Offset = getTargetData().getIndexedOffset(Ty, Indices);
          if (Offset >= (int)V2Size || Offset <= -(int)V1Size)
            return NoAlias;
        }
      }
    }

  return MayAlias;
}

// CheckGEPInstructions - Check two GEP instructions of compatible types and
// equal number of arguments.  This checks to see if the index expressions
// preclude the pointers from aliasing...
//
AliasAnalysis::AliasResult
BasicAliasAnalysis::CheckGEPInstructions(GetElementPtrInst *GEP1, unsigned G1S,
                                         GetElementPtrInst *GEP2, unsigned G2S){
  // Do the base pointers alias?
  AliasResult BaseAlias = alias(GEP1->getOperand(0), G1S,
                                GEP2->getOperand(0), G2S);
  if (BaseAlias != MustAlias)   // No or May alias: We cannot add anything...
    return BaseAlias;

  // Find the (possibly empty) initial sequence of equal values...
  unsigned NumGEPOperands = GEP1->getNumOperands();
  unsigned UnequalOper = 1;
  while (UnequalOper != NumGEPOperands &&
         GEP1->getOperand(UnequalOper) == GEP2->getOperand(UnequalOper))
    ++UnequalOper;

  // If all operands equal each other, then the derived pointers must
  // alias each other...
  if (UnequalOper == NumGEPOperands) return MustAlias;

  // So now we know that the indexes derived from the base pointers,
  // which are known to alias, are different.  We can still determine a
  // no-alias result if there are differing constant pairs in the index
  // chain.  For example:
  //        A[i][0] != A[j][1] iff (&A[0][1]-&A[0][0] >= std::max(G1S, G2S))
  //
  unsigned SizeMax = std::max(G1S, G2S);
  if (SizeMax == ~0U) return MayAlias; // Avoid frivolous work...

  // Scan for the first operand that is constant and unequal in the
  // two getelemenptrs...
  unsigned FirstConstantOper = UnequalOper;
  for (; FirstConstantOper != NumGEPOperands; ++FirstConstantOper) {
    const Value *G1Oper = GEP1->getOperand(FirstConstantOper);
    const Value *G2Oper = GEP2->getOperand(FirstConstantOper);
    if (G1Oper != G2Oper &&   // Found non-equal constant indexes...
        isa<Constant>(G1Oper) && isa<Constant>(G2Oper)) {
      // Make sure they are comparable...  and make sure the GEP with
      // the smaller leading constant is GEP1.
      ConstantBool *Compare =
        *cast<Constant>(GEP1->getOperand(FirstConstantOper)) >
        *cast<Constant>(GEP2->getOperand(FirstConstantOper));
      if (Compare) {  // If they are comparable...
        if (Compare->getValue())
          std::swap(GEP1, GEP2);  // Make GEP1 < GEP2
        break;
      }
    }
  }

  // No constant operands, we cannot tell anything...
  if (FirstConstantOper == NumGEPOperands) return MayAlias;

  // If there are non-equal constants arguments, then we can figure
  // out a minimum known delta between the two index expressions... at
  // this point we know that the first constant index of GEP1 is less
  // than the first constant index of GEP2.
  //
  std::vector<Value*> Indices1;
  Indices1.reserve(NumGEPOperands-1);
  for (unsigned i = 1; i != FirstConstantOper; ++i)
    if (GEP1->getOperand(i)->getType() == Type::UByteTy)
      Indices1.push_back(GEP1->getOperand(i));
    else
      Indices1.push_back(Constant::getNullValue(Type::LongTy));
  std::vector<Value*> Indices2;
  Indices2.reserve(NumGEPOperands-1);
  Indices2 = Indices1;           // Copy the zeros prefix...

  // Add the two known constant operands...
  Indices1.push_back((Value*)GEP1->getOperand(FirstConstantOper));
  Indices2.push_back((Value*)GEP2->getOperand(FirstConstantOper));

  const Type *GEPPointerTy = GEP1->getOperand(0)->getType();

  // Loop over the rest of the operands...
  for (unsigned i = FirstConstantOper+1; i!=NumGEPOperands; ++i){
    const Value *Op1 = GEP1->getOperand(i);
    const Value *Op2 = GEP2->getOperand(i);
    if (Op1 == Op2) {   // If they are equal, use a zero index...
      Indices1.push_back(Constant::getNullValue(Op1->getType()));
      Indices2.push_back(Indices1.back());
    } else {
      if (isa<Constant>(Op1))
        Indices1.push_back((Value*)Op1);
      else {
        // GEP1 is known to produce a value less than GEP2.  To be
        // conservatively correct, we must assume the largest
        // possible constant is used in this position.  This cannot
        // be the initial index to the GEP instructions (because we
        // know we have at least one element before this one with
        // the different constant arguments), so we know that the
        // current index must be into either a struct or array.
        // Because of this, we can calculate the maximum value
        // possible.
        //
        const Type *ElTy = GEP1->getIndexedType(GEPPointerTy,
                                                Indices1, true);
        if (const StructType *STy = dyn_cast<StructType>(ElTy)) {
          Indices1.push_back(ConstantUInt::get(Type::UByteTy,
                                               STy->getNumContainedTypes()));
        } else {
          Indices1.push_back(ConstantSInt::get(Type::LongTy,
                                               cast<ArrayType>(ElTy)->getNumElements()));
        }
      }

      if (isa<Constant>(Op2))
        Indices2.push_back((Value*)Op2);
      else // Conservatively assume the minimum value for this index
        Indices2.push_back(Constant::getNullValue(Op1->getType()));
    }
  }

  int Offset1 = getTargetData().getIndexedOffset(GEPPointerTy, Indices1);
  int Offset2 = getTargetData().getIndexedOffset(GEPPointerTy, Indices2);
  assert(Offset1 < Offset2 &&"There is at least one different constant here!");

  if ((unsigned)(Offset2-Offset1) >= SizeMax) {
    //std::cerr << "Determined that these two GEP's don't alias ["
    //          << SizeMax << " bytes]: \n" << *GEP1 << *GEP2;
    return NoAlias;
  }
  return MayAlias;
}

<commit_msg>Fix bug: BasicAA/2003-03-04-GEPCrash.ll<commit_after>//===- llvm/Analysis/BasicAliasAnalysis.h - Alias Analysis Impl -*- C++ -*-===//
//
// This file defines the default implementation of the Alias Analysis interface
// that simply implements a few identities (two different globals cannot alias,
// etc), but otherwise does no analysis.
//
//===----------------------------------------------------------------------===//

#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Pass.h"
#include "llvm/iMemory.h"
#include "llvm/iOther.h"
#include "llvm/ConstantHandling.h"
#include "llvm/GlobalValue.h"
#include "llvm/DerivedTypes.h"
#include "llvm/Target/TargetData.h"

// Make sure that anything that uses AliasAnalysis pulls in this file...
void BasicAAStub() {}

class GetElementPtrInst;
namespace {
  struct BasicAliasAnalysis : public ImmutablePass, public AliasAnalysis {

    virtual void getAnalysisUsage(AnalysisUsage &AU) const {
      AliasAnalysis::getAnalysisUsage(AU);
    }

    virtual void initializePass();

    // alias - This is the only method here that does anything interesting...
    //
    AliasResult alias(const Value *V1, unsigned V1Size,
                      const Value *V2, unsigned V2Size);
  private:
    // CheckGEPInstructions - Check two GEP instructions of compatible types and
    // equal number of arguments.  This checks to see if the index expressions
    // preclude the pointers from aliasing...
    AliasResult CheckGEPInstructions(GetElementPtrInst *GEP1, unsigned G1Size,
                                     GetElementPtrInst *GEP2, unsigned G2Size);
  };

  // Register this pass...
  RegisterOpt<BasicAliasAnalysis>
  X("basicaa", "Basic Alias Analysis (default AA impl)");

  // Declare that we implement the AliasAnalysis interface
  RegisterAnalysisGroup<AliasAnalysis, BasicAliasAnalysis, true> Y;
}  // End of anonymous namespace

void BasicAliasAnalysis::initializePass() {
  InitializeAliasAnalysis(this);
}

// hasUniqueAddress - Return true if the
static inline bool hasUniqueAddress(const Value *V) {
  return isa<GlobalValue>(V) || isa<MallocInst>(V) || isa<AllocaInst>(V);
}

static const Value *getUnderlyingObject(const Value *V) {
  if (!isa<PointerType>(V->getType())) return 0;

  // If we are at some type of object... return it.
  if (hasUniqueAddress(V)) return V;

  // Traverse through different addressing mechanisms...
  if (const Instruction *I = dyn_cast<Instruction>(V)) {
    if (isa<CastInst>(I) || isa<GetElementPtrInst>(I))
      return getUnderlyingObject(I->getOperand(0));
  }
  return 0;
}

// alias - Provide a bunch of ad-hoc rules to disambiguate in common cases, such
// as array references.  Note that this function is heavily tail recursive.
// Hopefully we have a smart C++ compiler.  :)
//
AliasAnalysis::AliasResult
BasicAliasAnalysis::alias(const Value *V1, unsigned V1Size,
                          const Value *V2, unsigned V2Size) {
  // Strip off constant pointer refs if they exist
  if (const ConstantPointerRef *CPR = dyn_cast<ConstantPointerRef>(V1))
    V1 = CPR->getValue();
  if (const ConstantPointerRef *CPR = dyn_cast<ConstantPointerRef>(V2))
    V2 = CPR->getValue();

  // Are we checking for alias of the same value?
  if (V1 == V2) return MustAlias;

  if ((!isa<PointerType>(V1->getType()) || !isa<PointerType>(V2->getType())) &&
      V1->getType() != Type::LongTy && V2->getType() != Type::LongTy)
    return NoAlias;  // Scalars cannot alias each other

  // Strip off cast instructions...
  if (const Instruction *I = dyn_cast<CastInst>(V1))
    return alias(I->getOperand(0), V1Size, V2, V2Size);
  if (const Instruction *I = dyn_cast<CastInst>(V2))
    return alias(V1, V1Size, I->getOperand(0), V2Size);

  // Figure out what objects these things are pointing to if we can...
  const Value *O1 = getUnderlyingObject(V1);
  const Value *O2 = getUnderlyingObject(V2);

  // Pointing at a discernable object?
  if (O1 && O2) {
    // If they are two different objects, we know that we have no alias...
    if (O1 != O2) return NoAlias;

    // If they are the same object, they we can look at the indexes.  If they
    // index off of the object is the same for both pointers, they must alias.
    // If they are provably different, they must not alias.  Otherwise, we can't
    // tell anything.
  } else if (O1 && isa<ConstantPointerNull>(V2)) {
    return NoAlias;                    // Unique values don't alias null
  } else if (O2 && isa<ConstantPointerNull>(V1)) {
    return NoAlias;                    // Unique values don't alias null
  }

  // If we have two gep instructions with identical indices, return an alias
  // result equal to the alias result of the original pointer...
  //
  if (const GetElementPtrInst *GEP1 = dyn_cast<GetElementPtrInst>(V1))
    if (const GetElementPtrInst *GEP2 = dyn_cast<GetElementPtrInst>(V2))
      if (GEP1->getNumOperands() == GEP2->getNumOperands() &&
          GEP1->getOperand(0)->getType() == GEP2->getOperand(0)->getType()) {
        AliasResult GAlias =
          CheckGEPInstructions((GetElementPtrInst*)GEP1, V1Size,
                               (GetElementPtrInst*)GEP2, V2Size);
        if (GAlias != MayAlias)
          return GAlias;
      }

  // Check to see if these two pointers are related by a getelementptr
  // instruction.  If one pointer is a GEP with a non-zero index of the other
  // pointer, we know they cannot alias.
  //
  if (isa<GetElementPtrInst>(V2)) {
    std::swap(V1, V2);
    std::swap(V1Size, V2Size);
  }

  if (V1Size != ~0U && V2Size != ~0U)
    if (const GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(V1)) {
      AliasResult R = alias(GEP->getOperand(0), V1Size, V2, V2Size);
      if (R == NoAlias) return NoAlias;
      if (R == MustAlias) {
        // If there is at least one non-zero constant index, we know they cannot
        // alias.
        bool ConstantFound = false;
        for (unsigned i = 1, e = GEP->getNumOperands(); i != e; ++i)
          if (const Constant *C = dyn_cast<Constant>(GEP->getOperand(i)))
            if (!C->isNullValue()) {
              ConstantFound = true;
              break;
          }
        if (ConstantFound) {
          if (V2Size <= 1 && V1Size <= 1)  // Just pointer check?
            return NoAlias;

          // Otherwise we have to check to see that the distance is more than
          // the size of the argument... build an index vector that is equal to
          // the arguments provided, except substitute 0's for any variable
          // indexes we find...

          std::vector<Value*> Indices;
          Indices.reserve(GEP->getNumOperands()-1);
          for (unsigned i = 1; i != GEP->getNumOperands(); ++i)
            if (const Constant *C = dyn_cast<Constant>(GEP->getOperand(i)))
              Indices.push_back((Value*)C);
            else
              Indices.push_back(Constant::getNullValue(Type::LongTy));
          const Type *Ty = GEP->getOperand(0)->getType();
          int Offset = getTargetData().getIndexedOffset(Ty, Indices);
          if (Offset >= (int)V2Size || Offset <= -(int)V1Size)
            return NoAlias;
        }
      }
    }

  return MayAlias;
}

// CheckGEPInstructions - Check two GEP instructions of compatible types and
// equal number of arguments.  This checks to see if the index expressions
// preclude the pointers from aliasing...
//
AliasAnalysis::AliasResult
BasicAliasAnalysis::CheckGEPInstructions(GetElementPtrInst *GEP1, unsigned G1S,
                                         GetElementPtrInst *GEP2, unsigned G2S){
  // Do the base pointers alias?
  AliasResult BaseAlias = alias(GEP1->getOperand(0), G1S,
                                GEP2->getOperand(0), G2S);
  if (BaseAlias != MustAlias)   // No or May alias: We cannot add anything...
    return BaseAlias;

  // Find the (possibly empty) initial sequence of equal values...
  unsigned NumGEPOperands = GEP1->getNumOperands();
  unsigned UnequalOper = 1;
  while (UnequalOper != NumGEPOperands &&
         GEP1->getOperand(UnequalOper) == GEP2->getOperand(UnequalOper))
    ++UnequalOper;

  // If all operands equal each other, then the derived pointers must
  // alias each other...
  if (UnequalOper == NumGEPOperands) return MustAlias;

  // So now we know that the indexes derived from the base pointers,
  // which are known to alias, are different.  We can still determine a
  // no-alias result if there are differing constant pairs in the index
  // chain.  For example:
  //        A[i][0] != A[j][1] iff (&A[0][1]-&A[0][0] >= std::max(G1S, G2S))
  //
  unsigned SizeMax = std::max(G1S, G2S);
  if (SizeMax == ~0U) return MayAlias; // Avoid frivolous work...

  // Scan for the first operand that is constant and unequal in the
  // two getelemenptrs...
  unsigned FirstConstantOper = UnequalOper;
  for (; FirstConstantOper != NumGEPOperands; ++FirstConstantOper) {
    const Value *G1Oper = GEP1->getOperand(FirstConstantOper);
    const Value *G2Oper = GEP2->getOperand(FirstConstantOper);
    if (G1Oper != G2Oper &&   // Found non-equal constant indexes...
        isa<Constant>(G1Oper) && isa<Constant>(G2Oper)) {
      // Make sure they are comparable...  and make sure the GEP with
      // the smaller leading constant is GEP1.
      ConstantBool *Compare =
        *cast<Constant>(GEP1->getOperand(FirstConstantOper)) >
        *cast<Constant>(GEP2->getOperand(FirstConstantOper));
      if (Compare) {  // If they are comparable...
        if (Compare->getValue())
          std::swap(GEP1, GEP2);  // Make GEP1 < GEP2
        break;
      }
    }
  }

  // No constant operands, we cannot tell anything...
  if (FirstConstantOper == NumGEPOperands) return MayAlias;

  // If there are non-equal constants arguments, then we can figure
  // out a minimum known delta between the two index expressions... at
  // this point we know that the first constant index of GEP1 is less
  // than the first constant index of GEP2.
  //
  std::vector<Value*> Indices1;
  Indices1.reserve(NumGEPOperands-1);
  for (unsigned i = 1; i != FirstConstantOper; ++i)
    if (GEP1->getOperand(i)->getType() == Type::UByteTy)
      Indices1.push_back(GEP1->getOperand(i));
    else
      Indices1.push_back(Constant::getNullValue(Type::LongTy));
  std::vector<Value*> Indices2;
  Indices2.reserve(NumGEPOperands-1);
  Indices2 = Indices1;           // Copy the zeros prefix...

  // Add the two known constant operands...
  Indices1.push_back((Value*)GEP1->getOperand(FirstConstantOper));
  Indices2.push_back((Value*)GEP2->getOperand(FirstConstantOper));

  const Type *GEPPointerTy = GEP1->getOperand(0)->getType();

  // Loop over the rest of the operands...
  for (unsigned i = FirstConstantOper+1; i!=NumGEPOperands; ++i){
    const Value *Op1 = GEP1->getOperand(i);
    const Value *Op2 = GEP2->getOperand(i);
    if (Op1 == Op2) {   // If they are equal, use a zero index...
      Indices1.push_back(Constant::getNullValue(Op1->getType()));
      Indices2.push_back(Indices1.back());
    } else {
      if (isa<Constant>(Op1))
        Indices1.push_back((Value*)Op1);
      else {
        // GEP1 is known to produce a value less than GEP2.  To be
        // conservatively correct, we must assume the largest possible constant
        // is used in this position.  This cannot be the initial index to the
        // GEP instructions (because we know we have at least one element before
        // this one with the different constant arguments), so we know that the
        // current index must be into either a struct or array.  Because we know
        // it's not constant, this cannot be a structure index.  Because of
        // this, we can calculate the maximum value possible.
        //
        const ArrayType *ElTy =
          cast<ArrayType>(GEP1->getIndexedType(GEPPointerTy, Indices1, true));
        Indices1.push_back(ConstantSInt::get(Type::LongTy,
                                             ElTy->getNumElements()-1));
      }

      if (isa<Constant>(Op2))
        Indices2.push_back((Value*)Op2);
      else // Conservatively assume the minimum value for this index
        Indices2.push_back(Constant::getNullValue(Op1->getType()));
    }
  }

  int Offset1 = getTargetData().getIndexedOffset(GEPPointerTy, Indices1);
  int Offset2 = getTargetData().getIndexedOffset(GEPPointerTy, Indices2);
  assert(Offset1 < Offset2 &&"There is at least one different constant here!");

  if ((unsigned)(Offset2-Offset1) >= SizeMax) {
    //std::cerr << "Determined that these two GEP's don't alias ["
    //          << SizeMax << " bytes]: \n" << *GEP1 << *GEP2;
    return NoAlias;
  }
  return MayAlias;
}

<|endoftext|>


<commit_before>// Name: Yeh, MinHsuan
// Date: April 6, 2017
// Last Update: April 7, 2017
// Problem statement: The details of Formula class

#include "Formula.h"

Formula::Formula() {

}

Formula::Formula(string& _formulaStr) {
    this->formulaStr = _formulaStr;

	try {
		this->postfix();
	}
	catch (const char* errMsg) {
		throw errMsg;
	}
}

Formula::~Formula() {

}

void Formula::check() {
	int left = 0, right = 0;
	bool flagContinous = false, flagSign = false;

	this->formulaStr.erase(remove_if(this->formulaStr.begin(), this->formulaStr.end(), isspace), this->formulaStr.end());
	//erase the spaces

	for (auto &i : this->formulaStr) {
		if (i == '(')
			left += 1;
		else if (i == ')')
			right += 1;
	}
	if (left != right)
		throw "formate is illegal";
	//left and right brackets

	for (unsigned long long pos = 0; pos < this->formulaStr.length(); pos++) {
		if (this->formulaStr[pos] == '+' || this->formulaStr[pos] == '-' || this->formulaStr[pos] == '*' || this->formulaStr[pos] == '/') {
			bool flagcurrentSign = ((this->formulaStr[pos] == '+') ? true : false);
			if (this->formulaStr[pos + 1] == '*' || this->formulaStr[pos + 1] == '/' || this->formulaStr[pos + 1] == ')' || this->formulaStr[pos + 1] == '!')
				throw "format is illegal";
			// /* management
			if (flagContinous == false && (this->formulaStr[pos] == '+' || this->formulaStr[pos] == '-')) {
				flagSign = ((this->formulaStr[pos] == '+') ? true : false);
				flagContinous = true;
			}
			else if (flagContinous == true) {
				flagSign = !(flagSign^flagcurrentSign);
				if (this->formulaStr[pos] == '+' || this->formulaStr[pos] == '-')
					this->formulaStr.erase(this->formulaStr.begin() + pos);
				pos -= 1;
				if (this->formulaStr[pos + 1] != '+'&&this->formulaStr[pos + 1] != '-') {
					this->formulaStr.replace(pos, 1, ((flagSign) ? "+" : "-"));
				}
			}
		}
		if (this->formulaStr[pos] == 'i' || (this->formulaStr[pos] >= '0'&&this->formulaStr[pos] <= '9')) {
			flagContinous = false;
		}
	}
	//clean out the continous operater

}

void Formula::postfix() {
	bool flagContinous = false;
	for (unsigned long long pos = 0; pos < this->formulaStr.length(); pos++) {
		if (this->formulaStr[pos] == 'i' || (this->formulaStr[pos] >= '0'&&this->formulaStr[pos] <= '9')) {
			if (this->formulaStr[pos + 1] != 'i' || (this->formulaStr[pos] <= '0' || this->formulaStr[pos] >= '9'))
				this->formulaStr.insert(pos + 1, " ");
		}
		else {
			if (this->formulaStr[pos + 1] == 'i' || (this->formulaStr[pos + 1] >= '0'&&this->formulaStr[pos + 1] <= '9'))
				this->formulaStr.insert(pos + 1, " ");
		}
	}
	//insert the spaces

}

NumberObject Formula::cal(string& _str) {

	return NumberObject();
}

string Formula::setFormula(string& _formulaStr) {
    this->formulaStr = _formulaStr;

	try {
		this->postfix();
	}
	catch (const char* errMsg) {
		throw errMsg;
	}
}

NumberObject Formula::caculate() {
	try {
		return this->cal(this->formulaStr);
	}
	catch (const char* errMsg) {
		throw errMsg;
	}
}

Formula Formula::operator=(const string& _formulaStr) {
	this->formulaStr = _formulaStr;

	try {
		this->postfix();
	}
	catch (const char* errMsg) {
		throw errMsg;
	}
}<commit_msg>insert space<commit_after>// Name: Yeh, MinHsuan
// Date: April 6, 2017
// Last Update: April 7, 2017
// Problem statement: The details of Formula class

#include "Formula.h"

Formula::Formula() {

}

Formula::Formula(string& _formulaStr) {
    this->formulaStr = _formulaStr;

	try {
		this->postfix();
	}
	catch (const char* errMsg) {
		throw errMsg;
	}
}

Formula::~Formula() {

}

void Formula::check() {
	string str = this->formulaStr;
	int left = 0, right = 0;
	bool flagContinous = false, flagSign = false;

	str.erase(remove_if(str.begin(), str.end(), ::isspace), str.end());

	for (auto &i : str) {
		if (i == '(')
			left += 1;
		else if (i == ')')
			right += 1;
	}
	if (left != right)
		throw "formate is illegal";

	for (unsigned long long i = 0; i < str.length(); i++) {
		if (str[i] == '+' || str[i] == '-' || str[i] == '*' || str[i] == '/') {
			bool flagcurrentSign = ((str[i] == '+') ? true : false);
			if (str[i + 1] == '*' || str[i + 1] == '/' || str[i + 1] == ')' || str[i + 1] == '!')
				throw "format is illegal";
			if (flagContinous == false && (str[i] == '+' || str[i] == '-')) {
				flagSign = ((str[i] == '+') ? true : false);
				flagContinous = true;
			}
			else if (flagContinous == true) {
				flagSign = !(flagSign^flagcurrentSign);
				if (str[i] == '+' || str[i] == '-')
					str.erase(str.begin() + i);
				i -= 1;
				if (str[i + 1] != '+'&&str[i + 1] != '-') {
					str.replace(i, 1, ((flagSign) ? "+" : "-"));
				}
			}
		}
		if (str[i] == 'i' || (str[i] >= '0'&&str[i] <= '9')) {
			flagContinous = false;
		}
	}

	stringstream ss;
	ss.str("");
	ss.clear();

	for (unsigned long long i = 0; i < str.length(); i++) {
    	if (str[i] == '(')
        	ss << str[i] << " ";
    	else if (str[i] == '+' || str[i] == '-' || str[i] == '*' || str[i] == '/' || str[i] == ')') {
        	if (str[i - 1] == '+' || str[i - 1] == '-' || str[i - 1] == '*' || str[i - 1] == '/' || str[i - 1] == '(' || str[i - 1] == ')')
            	ss << str[i];
			else
				ss << " " << str[i] << " ";
		} else
			ss << str[i];
	}
	getline(ss, this->formulaStr);

}

void Formula::postfix() {

}

NumberObject Formula::cal(string& _str) {

	return NumberObject();
}

string Formula::setFormula(string& _formulaStr) {
    this->formulaStr = _formulaStr;

	try {
		this->postfix();
	}
	catch (const char* errMsg) {
		throw errMsg;
	}
}

NumberObject Formula::caculate() {
	try {
		return this->cal(this->formulaStr);
	}
	catch (const char* errMsg) {
		throw errMsg;
	}
}

Formula Formula::operator=(const string& _formulaStr) {
	this->formulaStr = _formulaStr;

	try {
		this->postfix();
	}
	catch (const char* errMsg) {
		throw errMsg;
	}
}<|endoftext|>