<commit_before>// Licensed GNU LGPL v3 or later: http://www.gnu.org/licenses/lgpl.html
#ifndef __RAPICORN_CORE_OBJECTS_HH__
#define __RAPICORN_CORE_OBJECTS_HH__

#include <rcore/utilities.hh>
#include <rcore/thread.hh>
#include <rcore/aidaprops.hh>

namespace Rapicorn {

// == VirtualTypeid ==
class VirtualTypeid {
protected:
  virtual      ~VirtualTypeid      ();
public:
  String        typeid_name        ();
  static String cxx_demangle       (const char *mangled_identifier);
};

// == ClassDoctor (used for private class copies) ==
#ifdef  __RAPICORN_BUILD__
class ClassDoctor;
#else
class ClassDoctor {};
#endif

// == Deletable ==
/**
 * Deletable is a virtual base class that can be derived from (usually with
 * public virtual) to ensure an object has a vtable and a virtual destructor.
 * Also, it allows deletion hooks to be called during the objects destructor,
 * by deriving from Rapicorn::Deletable::DeletionHook. No extra per-object space is
 * consumed to allow deletion hooks, which makes Deletable a suitable base
 * type for classes that may or may not need this feature (e.g. objects that
 * can but often aren't used for signal handler connections).
 */
struct Deletable : public virtual VirtualTypeid {
  /**
   * DeletionHook is the base implementation class for hooks which are hooked
   * up into the deletion phase of a Rapicorn::Deletable.
   */
  class DeletionHook {
    DeletionHook    *prev;
    DeletionHook    *next;
    friend class Deletable;
  protected:
    virtual     ~DeletionHook          (); /* { if (deletable) deletable_remove_hook (deletable); deletable = NULL; } */
    virtual void monitoring_deletable  (Deletable &deletable) = 0;
    virtual void dismiss_deletable     () = 0;
  public:
    explicit     DeletionHook          () : prev (NULL), next (NULL) {}
    bool         deletable_add_hook    (void      *any)              { return false; }
    bool         deletable_add_hook    (Deletable *deletable);
    bool         deletable_remove_hook (void      *any)              { return false; }
    bool         deletable_remove_hook (Deletable *deletable);
  };
private:
  void           add_deletion_hook     (DeletionHook *hook);
  void           remove_deletion_hook  (DeletionHook *hook);
protected:
  void           invoke_deletion_hooks ();
  virtual       ~Deletable             ();
};

// == DataListContainer ==
/**
 * By using a DataKey, DataListContainer objects allow storage and retrieval of custom data members in a typesafe fashion.
 * The custom data members will initially default to DataKey::fallback and are deleted by the DataListContainer destructor.
 * Example: @snippet rcore/tests/datalist.cc DataListContainer-EXAMPLE
 */
class DataListContainer {
  DataList data_list;
public: /// @name Accessing custom data members
  /// Assign @a data to the custom keyed data member, deletes any previously set data.
  template<typename Type> inline void set_data    (DataKey<Type> *key, Type data) { data_list.set (key, data); }
  /// Retrieve contents of the custom keyed data member, returns DataKey::fallback if nothing was set.
  template<typename Type> inline Type get_data    (DataKey<Type> *key) const      { return data_list.get (key); }
  /// Swap @a data with the current contents of the custom keyed data member, returns the current contents.
  template<typename Type> inline Type swap_data   (DataKey<Type> *key, Type data) { return data_list.swap (key, data); }
  /// Removes and returns the current contents of the custom keyed data member without deleting it.
  template<typename Type> inline Type swap_data   (DataKey<Type> *key)            { return data_list.swap (key); }
  /// Delete the current contents of the custom keyed data member, invokes DataKey::destroy.
  template<typename Type> inline void delete_data (DataKey<Type> *key)            { data_list.del (key); }
};

// == BaseObject ==
class BaseObject : public virtual Deletable, public virtual Aida::PropertyHostInterface {
protected:
  class                    InterfaceMatcher;
  template<class C>  class InterfaceMatch;
  virtual void                 dispose   ();
public:
};
typedef Aida::PropertyList PropertyList; // import PropertyList from Aida namespace
typedef Aida::Property     Property;     // import Property from Aida namespace
class NullInterface : std::exception {};

struct BaseObject::InterfaceMatcher {
  explicit      InterfaceMatcher (const String &ident) : m_ident (ident), m_match_found (false) {}
  bool          done             () const { return m_match_found; }
  virtual  bool match            (BaseObject *object, const String &ident = String()) = 0;
  RAPICORN_CLASS_NON_COPYABLE (InterfaceMatcher);
protected:
  const String &m_ident;
  bool          m_match_found;
};

template<class C>
struct BaseObject::InterfaceMatch : BaseObject::InterfaceMatcher {
  typedef C&    Result;
  explicit      InterfaceMatch  (const String &ident) : InterfaceMatcher (ident), m_instance (NULL) {}
  C&            result          (bool may_throw) const;
  virtual bool  match           (BaseObject *obj, const String &ident);
protected:
  C            *m_instance;
};
template<class C>
struct BaseObject::InterfaceMatch<C&> : InterfaceMatch<C> {
  explicit      InterfaceMatch  (const String &ident) : InterfaceMatch<C> (ident) {}
};
template<class C>
struct BaseObject::InterfaceMatch<C*> : InterfaceMatch<C> {
  typedef C*    Result;
  explicit      InterfaceMatch  (const String &ident) : InterfaceMatch<C> (ident) {}
  C*            result          (bool may_throw) const { return InterfaceMatch<C>::m_instance; }
};

template<class C> bool
BaseObject::InterfaceMatch<C>::match (BaseObject *obj, const String &ident)
{
  if (!m_instance)
    {
      const String &id = m_ident;
      if (id.empty() || id == ident)
        {
          m_instance = dynamic_cast<C*> (obj);
          m_match_found = m_instance != NULL;
        }
    }
  return m_match_found;
}

template<class C> C&
BaseObject::InterfaceMatch<C>::result (bool may_throw) const
{
  if (!this->m_instance && may_throw)
    throw NullInterface();
  return *this->m_instance;
}

// == ReferenceCountable ==
class ReferenceCountable : public virtual BaseObject {
  volatile mutable uint32 ref_field;
  static const uint32     FLOATING_FLAG = 1 << 31;
  inline uint32 ref_get    () const                             { return Lib::atomic_load (&ref_field); }
  inline bool   ref_cas    (uint32 oldv, uint32 newv) const     { return __sync_bool_compare_and_swap (&ref_field, oldv, newv); }
  inline void   fast_ref   () const;
  inline void   fast_unref () const;
  void          real_unref () const;
  RAPICORN_CLASS_NON_COPYABLE (ReferenceCountable);
protected:
  virtual      ~ReferenceCountable ();
  virtual void  delete_this        ();
  virtual void  pre_finalize       ();
  virtual void  finalize           ();
  inline uint32 ref_count          () const                     { return ref_get() & ~FLOATING_FLAG; }
public:
  bool     floating           () const                          { return 0 != (ref_get() & FLOATING_FLAG); }
  void     ref                () const                          { fast_ref(); }
  void     ref_sink           () const;
  bool     finalizing         () const                          { return ref_count() < 1; }
  void     unref              () const                          { fast_unref(); }
  void     ref_diag           (const char *msg = NULL) const;
  explicit ReferenceCountable (uint allow_stack_magic = 0);
  template<class Obj> static Obj& ref      (Obj &obj) { obj.ref();       return obj; }
  template<class Obj> static Obj* ref      (Obj *obj) { obj->ref();      return obj; }
  template<class Obj> static Obj& ref_sink (Obj &obj) { obj.ref_sink();  return obj; }
  template<class Obj> static Obj* ref_sink (Obj *obj) { obj->ref_sink(); return obj; }
  template<class Obj> static void unref    (Obj &obj) { obj.unref(); }
  template<class Obj> static void unref    (Obj *obj) { obj->unref(); }
};
template<class Obj> static Obj& ref      (Obj &obj) { obj.ref();       return obj; }
template<class Obj> static Obj* ref      (Obj *obj) { obj->ref();      return obj; }
template<class Obj> static Obj& ref_sink (Obj &obj) { obj.ref_sink();  return obj; }
template<class Obj> static Obj* ref_sink (Obj *obj) { obj->ref_sink(); return obj; }
template<class Obj> static void unref    (Obj &obj) { obj.unref(); }
template<class Obj> static void unref    (Obj *obj) { obj->unref(); }

// == Implementation Details ==
inline void
ReferenceCountable::fast_ref () const
{
  // fast-path: use one atomic op and deferred checks
  uint32 old_ref = __sync_fetch_and_add (&ref_field, 1);
  uint32 new_ref = old_ref + 1;                       // ...and_add (,1)
  RAPICORN_ASSERT (old_ref & ~FLOATING_FLAG);         // check dead objects
  RAPICORN_ASSERT (new_ref & ~FLOATING_FLAG);         // check overflow
}

inline void
ReferenceCountable::fast_unref () const
{
  RAPICORN_CFENCE;
  uint32 old_ref = ref_field; // skip read-barrier for fast-path
  if (RAPICORN_LIKELY (old_ref & ~(FLOATING_FLAG | 1)) && // old_ref >= 2
      RAPICORN_LIKELY (ref_cas (old_ref, old_ref - 1)))
    return; // trying fast-path with single atomic op
  real_unref();
}

} // Rapicorn

#endif // __RAPICORN_CORE_OBJECTS_HH__
<commit_msg>RCORE: objects member renames<commit_after>// Licensed GNU LGPL v3 or later: http://www.gnu.org/licenses/lgpl.html
#ifndef __RAPICORN_CORE_OBJECTS_HH__
#define __RAPICORN_CORE_OBJECTS_HH__

#include <rcore/utilities.hh>
#include <rcore/thread.hh>
#include <rcore/aidaprops.hh>

namespace Rapicorn {

// == VirtualTypeid ==
class VirtualTypeid {
protected:
  virtual      ~VirtualTypeid      ();
public:
  String        typeid_name        ();
  static String cxx_demangle       (const char *mangled_identifier);
};

// == ClassDoctor (used for private class copies) ==
#ifdef  __RAPICORN_BUILD__
class ClassDoctor;
#else
class ClassDoctor {};
#endif

// == Deletable ==
/**
 * Deletable is a virtual base class that can be derived from (usually with
 * public virtual) to ensure an object has a vtable and a virtual destructor.
 * Also, it allows deletion hooks to be called during the objects destructor,
 * by deriving from Rapicorn::Deletable::DeletionHook. No extra per-object space is
 * consumed to allow deletion hooks, which makes Deletable a suitable base
 * type for classes that may or may not need this feature (e.g. objects that
 * can but often aren't used for signal handler connections).
 */
struct Deletable : public virtual VirtualTypeid {
  /**
   * DeletionHook is the base implementation class for hooks which are hooked
   * up into the deletion phase of a Rapicorn::Deletable.
   */
  class DeletionHook {
    DeletionHook    *prev;
    DeletionHook    *next;
    friend class Deletable;
  protected:
    virtual     ~DeletionHook          (); /* { if (deletable) deletable_remove_hook (deletable); deletable = NULL; } */
    virtual void monitoring_deletable  (Deletable &deletable) = 0;
    virtual void dismiss_deletable     () = 0;
  public:
    explicit     DeletionHook          () : prev (NULL), next (NULL) {}
    bool         deletable_add_hook    (void      *any)              { return false; }
    bool         deletable_add_hook    (Deletable *deletable);
    bool         deletable_remove_hook (void      *any)              { return false; }
    bool         deletable_remove_hook (Deletable *deletable);
  };
private:
  void           add_deletion_hook     (DeletionHook *hook);
  void           remove_deletion_hook  (DeletionHook *hook);
protected:
  void           invoke_deletion_hooks ();
  virtual       ~Deletable             ();
};

// == DataListContainer ==
/**
 * By using a DataKey, DataListContainer objects allow storage and retrieval of custom data members in a typesafe fashion.
 * The custom data members will initially default to DataKey::fallback and are deleted by the DataListContainer destructor.
 * Example: @snippet rcore/tests/datalist.cc DataListContainer-EXAMPLE
 */
class DataListContainer {
  DataList data_list;
public: /// @name Accessing custom data members
  /// Assign @a data to the custom keyed data member, deletes any previously set data.
  template<typename Type> inline void set_data    (DataKey<Type> *key, Type data) { data_list.set (key, data); }
  /// Retrieve contents of the custom keyed data member, returns DataKey::fallback if nothing was set.
  template<typename Type> inline Type get_data    (DataKey<Type> *key) const      { return data_list.get (key); }
  /// Swap @a data with the current contents of the custom keyed data member, returns the current contents.
  template<typename Type> inline Type swap_data   (DataKey<Type> *key, Type data) { return data_list.swap (key, data); }
  /// Removes and returns the current contents of the custom keyed data member without deleting it.
  template<typename Type> inline Type swap_data   (DataKey<Type> *key)            { return data_list.swap (key); }
  /// Delete the current contents of the custom keyed data member, invokes DataKey::destroy.
  template<typename Type> inline void delete_data (DataKey<Type> *key)            { data_list.del (key); }
};

// == BaseObject ==
class BaseObject : public virtual Deletable, public virtual Aida::PropertyHostInterface {
protected:
  class                    InterfaceMatcher;
  template<class C>  class InterfaceMatch;
  virtual void                 dispose   ();
public:
};
typedef Aida::PropertyList PropertyList; // import PropertyList from Aida namespace
typedef Aida::Property     Property;     // import Property from Aida namespace
class NullInterface : std::exception {};

struct BaseObject::InterfaceMatcher {
  explicit      InterfaceMatcher (const String &ident) : ident_ (ident), match_found_ (false) {}
  bool          done             () const { return match_found_; }
  virtual  bool match            (BaseObject *object, const String &ident = String()) = 0;
  RAPICORN_CLASS_NON_COPYABLE (InterfaceMatcher);
protected:
  const String &ident_;
  bool          match_found_;
};

template<class C>
struct BaseObject::InterfaceMatch : BaseObject::InterfaceMatcher {
  typedef C&    Result;
  explicit      InterfaceMatch  (const String &ident) : InterfaceMatcher (ident), instance_ (NULL) {}
  C&            result          (bool may_throw) const;
  virtual bool  match           (BaseObject *obj, const String &ident);
protected:
  C            *instance_;
};
template<class C>
struct BaseObject::InterfaceMatch<C&> : InterfaceMatch<C> {
  explicit      InterfaceMatch  (const String &ident) : InterfaceMatch<C> (ident) {}
};
template<class C>
struct BaseObject::InterfaceMatch<C*> : InterfaceMatch<C> {
  typedef C*    Result;
  explicit      InterfaceMatch  (const String &ident) : InterfaceMatch<C> (ident) {}
  C*            result          (bool may_throw) const { return InterfaceMatch<C>::instance_; }
};

template<class C> bool
BaseObject::InterfaceMatch<C>::match (BaseObject *obj, const String &ident)
{
  if (!instance_)
    {
      const String &id = ident_;
      if (id.empty() || id == ident)
        {
          instance_ = dynamic_cast<C*> (obj);
          match_found_ = instance_ != NULL;
        }
    }
  return match_found_;
}

template<class C> C&
BaseObject::InterfaceMatch<C>::result (bool may_throw) const
{
  if (!this->instance_ && may_throw)
    throw NullInterface();
  return *this->instance_;
}

// == ReferenceCountable ==
class ReferenceCountable : public virtual BaseObject {
  volatile mutable uint32 ref_field;
  static const uint32     FLOATING_FLAG = 1 << 31;
  inline uint32 ref_get    () const                             { return Lib::atomic_load (&ref_field); }
  inline bool   ref_cas    (uint32 oldv, uint32 newv) const     { return __sync_bool_compare_and_swap (&ref_field, oldv, newv); }
  inline void   fast_ref   () const;
  inline void   fast_unref () const;
  void          real_unref () const;
  RAPICORN_CLASS_NON_COPYABLE (ReferenceCountable);
protected:
  virtual      ~ReferenceCountable ();
  virtual void  delete_this        ();
  virtual void  pre_finalize       ();
  virtual void  finalize           ();
  inline uint32 ref_count          () const                     { return ref_get() & ~FLOATING_FLAG; }
public:
  bool     floating           () const                          { return 0 != (ref_get() & FLOATING_FLAG); }
  void     ref                () const                          { fast_ref(); }
  void     ref_sink           () const;
  bool     finalizing         () const                          { return ref_count() < 1; }
  void     unref              () const                          { fast_unref(); }
  void     ref_diag           (const char *msg = NULL) const;
  explicit ReferenceCountable (uint allow_stack_magic = 0);
  template<class Obj> static Obj& ref      (Obj &obj) { obj.ref();       return obj; }
  template<class Obj> static Obj* ref      (Obj *obj) { obj->ref();      return obj; }
  template<class Obj> static Obj& ref_sink (Obj &obj) { obj.ref_sink();  return obj; }
  template<class Obj> static Obj* ref_sink (Obj *obj) { obj->ref_sink(); return obj; }
  template<class Obj> static void unref    (Obj &obj) { obj.unref(); }
  template<class Obj> static void unref    (Obj *obj) { obj->unref(); }
};
template<class Obj> static Obj& ref      (Obj &obj) { obj.ref();       return obj; }
template<class Obj> static Obj* ref      (Obj *obj) { obj->ref();      return obj; }
template<class Obj> static Obj& ref_sink (Obj &obj) { obj.ref_sink();  return obj; }
template<class Obj> static Obj* ref_sink (Obj *obj) { obj->ref_sink(); return obj; }
template<class Obj> static void unref    (Obj &obj) { obj.unref(); }
template<class Obj> static void unref    (Obj *obj) { obj->unref(); }

// == Implementation Details ==
inline void
ReferenceCountable::fast_ref () const
{
  // fast-path: use one atomic op and deferred checks
  uint32 old_ref = __sync_fetch_and_add (&ref_field, 1);
  uint32 new_ref = old_ref + 1;                       // ...and_add (,1)
  RAPICORN_ASSERT (old_ref & ~FLOATING_FLAG);         // check dead objects
  RAPICORN_ASSERT (new_ref & ~FLOATING_FLAG);         // check overflow
}

inline void
ReferenceCountable::fast_unref () const
{
  RAPICORN_CFENCE;
  uint32 old_ref = ref_field; // skip read-barrier for fast-path
  if (RAPICORN_LIKELY (old_ref & ~(FLOATING_FLAG | 1)) && // old_ref >= 2
      RAPICORN_LIKELY (ref_cas (old_ref, old_ref - 1)))
    return; // trying fast-path with single atomic op
  real_unref();
}

} // Rapicorn

#endif // __RAPICORN_CORE_OBJECTS_HH__
<|endoftext|>


<commit_before>#ifndef __CLUSTERING_ADMINISTRATION_ISSUES_NAME_CONFLICT_HPP__
#define __CLUSTERING_ADMINISTRATION_ISSUES_NAME_CONFLICT_HPP__

#include "clustering/administration/issues/global.hpp"
#include "clustering/administration/metadata.hpp"
#include "rpc/semilattice/view.hpp"

class name_conflict_issue_t : public global_issue_t {
public:
    name_conflict_issue_t(
            const std::string &_type,
            const std::string &_contested_name,
            const std::set<boost::uuids::uuid> &_contestants) :
        type(_type), contested_name(_contested_name), contestants(_contestants) { }

    std::string get_description() const {
        std::string message = "The following " + type + "s are all named '" + contested_name + "': ";
        for (std::set<boost::uuids::uuid>::iterator it = contestants.begin(); it != contestants.end(); it++) {
            message += uuid_to_str(*it) + "; ";
        }
        return message;
    }

    name_conflict_issue_t *clone() const {
        return new name_conflict_issue_t(type, contested_name, contestants);
    }

    std::string type;
    std::string contested_name;
    std::set<boost::uuids::uuid> contestants;
};

class name_conflict_issue_tracker_t : public global_issue_tracker_t {
public:
    name_conflict_issue_tracker_t(
            boost::shared_ptr<semilattice_read_view_t<cluster_semilattice_metadata_t> > _semilattice_view) :
        semilattice_view(_semilattice_view) { }

    std::list<clone_ptr_t<global_issue_t> > get_issues();

private:
    boost::shared_ptr<semilattice_read_view_t<cluster_semilattice_metadata_t> > semilattice_view;
};

#endif /* __CLUSTERING_ADMINISTRATION_ISSUES_NAME_CONFLICT_HPP__ */
<commit_msg>Don't use reserved compiler names<commit_after>#ifndef CLUSTERING_ADMINISTRATION_ISSUES_NAME_CONFLICT_HPP_
#define CLUSTERING_ADMINISTRATION_ISSUES_NAME_CONFLICT_HPP_

#include "clustering/administration/issues/global.hpp"
#include "clustering/administration/metadata.hpp"
#include "rpc/semilattice/view.hpp"

class name_conflict_issue_t : public global_issue_t {
public:
    name_conflict_issue_t(
            const std::string &_type,
            const std::string &_contested_name,
            const std::set<boost::uuids::uuid> &_contestants) :
        type(_type), contested_name(_contested_name), contestants(_contestants) { }

    std::string get_description() const {
        std::string message = "The following " + type + "s are all named '" + contested_name + "': ";
        for (std::set<boost::uuids::uuid>::iterator it = contestants.begin(); it != contestants.end(); it++) {
            message += uuid_to_str(*it) + "; ";
        }
        return message;
    }

    name_conflict_issue_t *clone() const {
        return new name_conflict_issue_t(type, contested_name, contestants);
    }

    std::string type;
    std::string contested_name;
    std::set<boost::uuids::uuid> contestants;
};

class name_conflict_issue_tracker_t : public global_issue_tracker_t {
public:
    name_conflict_issue_tracker_t(
            boost::shared_ptr<semilattice_read_view_t<cluster_semilattice_metadata_t> > _semilattice_view) :
        semilattice_view(_semilattice_view) { }

    std::list<clone_ptr_t<global_issue_t> > get_issues();

private:
    boost::shared_ptr<semilattice_read_view_t<cluster_semilattice_metadata_t> > semilattice_view;
};

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


<commit_before>// -*- c-style: gnu -*-

#include "act-activity.h"
#include "act-activity-accum.h"
#include "act-arguments.h"
#include "act-config.h"
#include "act-database.h"
#include "act-format.h"
#include "act-output-table.h"
#include "act-util.h"

#include <cmath>
#include <map>
#include <xlocale.h>

using namespace act;

namespace {

enum option_id
{
  opt_grep,
  opt_defines,
  opt_matches,
  opt_contains,
  opt_compare,
  opt_interval,
  opt_field,
  opt_course,
  opt_keywords,
  opt_equipment,
  opt_format,
  opt_table,
};

const arguments::option options[] =
{
  {opt_grep, "grep", 'g', "REGEXP", "Add grep-body query term."},
  {opt_defines, "defines", 'd', "FIELD", "Add defines-field query term."},
  {opt_matches, "matches", 'm', "FIELD:REGEXP", "Add re-match query term."},
  {opt_contains, "contains", 'c', "FIELD:KEYWORD", "Add keyword query term."},
  {opt_compare, "compare", 'C', "FIELDxVALUE",
   "Add compare query term. 'x' from: = != < > <= >="},
  {opt_interval, "interval", 'i', "INTERVAL", "Group by date-interval."},
  {opt_field, "field", 'f', "FIELD[:SIZE]", "Group by the named field."},
  {opt_course, "course", 0, nullptr, "Group by Course field."},
  {opt_keywords, "keywords", 'k', "FIELD", "Group by keyword."},
  {opt_equipment, "equipment", 0, nullptr, "Group by equipment."},
  {opt_format, "format", 'f', "FORMAT", "Format string."},
  {opt_table, "table", 't', "FORMAT", "Table format string."},
  {arguments::opt_eof},
};

void
print_usage(const arguments &args)
{
  fprintf(stderr, "usage: %s [OPTIONS...]\n\n", args.program_name());
  fputs("where OPTIONS are any of:\n\n", stderr);

  arguments::print_options(options, stderr);

  fputs("\n", stderr);
}

struct string_group
{
  const char *field;

  typedef std::map<std::string, activity_accum,
    case_insensitive_string_compare> group_map;

  group_map map;

  explicit string_group(const char *field);

  void insert(const activity &a);

  void format_key(std::string &buf, const std::string &key) const;
};

struct keyword_group
{
  field_id field;

  typedef std::map<std::string, activity_accum,
    case_insensitive_string_compare> group_map;

  group_map map;

  keyword_group(const char *field);

  void insert(const activity &a);

  void format_key(std::string &buf, const std::string &key) const;
};

struct value_group
{
  const char *field;
  field_id field_id;
  double bucket_size;
  double bucket_scale;

  typedef std::map<int, activity_accum> group_map;

  group_map map;

  explicit value_group(const char *field, double bucket_size);

  void insert(const activity &a);

  void format_key(std::string &buf, int key) const;
};

struct interval_group
{
  date_interval interval;

  typedef std::map<int, activity_accum> group_map;

  group_map map;

  explicit interval_group(const date_interval &interval);

  void insert(const activity &a);

  void format_key(std::string &buf, int key) const;
};

string_group::string_group(const char *f)
: field(f)
{
}

void
string_group::insert(const activity &a)
{
  if(const std::string *ptr = a.field_ptr(field))
    {
      map[*ptr].add(a);
    }
}

void
string_group::format_key(std::string &buf, const std::string &key) const
{
  buf.append(key);
}

keyword_group::keyword_group(const char *f)
: field(lookup_field_id(f))
{
}

void
keyword_group::insert(const activity &a)
{
  if(const std::vector<std::string> *ptr = a.field_keywords_ptr(field))
    {
      for (const auto &it : *ptr)
	map[it].add(a);
    }
}

void
keyword_group::format_key(std::string &buf, const std::string &key) const
{
  buf.append(key);
}

value_group::value_group(const char *f, double size)
: field(f),
  field_id(lookup_field_id(f)),
  bucket_size(size),
  bucket_scale(1 / size)
{
}

void
value_group::insert(const activity &a)
{
  double value = 0;

  if (field_id != field_custom)
    value = a.field_value(field_id);
  else if (const std::string *ptr = a.field_ptr(field))
    parse_number(*ptr, &value);

  if (value == 0)
    return;

  int idx = (int) floor(value * bucket_scale);

  map[idx].add(a);
}

void
value_group::format_key(std::string &buf, int key) const
{
  double min = key * bucket_size;
  double max = (key + 1) * bucket_size;

  field_data_type type = lookup_field_data_type(field_id);

  buf.push_back('[');
  format_value(buf, type, min, unit_unknown);
  buf.append(", ");
  format_value(buf, type, max, unit_unknown);
  buf.push_back(')');
}

interval_group::interval_group(const date_interval &i)
: interval(i)
{
}

void
interval_group::insert(const activity &a)
{
  time_t date = a.date();
  if (date == 0)
    return;

  int idx = interval.date_index(date);

  map[idx].add(a);
}

void
interval_group::format_key(std::string &buf, int key) const
{
  interval.append_date(buf, key);
}

template<typename T> void
output_group_format(T &g, const char *format)
{
  for (const auto &it : g.map)
    {
      std::string key;
      g.format_key(key, it.first);
      it.second.printf(format, key.c_str());
    }
}

template<typename T> void
output_group_table(T &g, const char *format)
{
  output_table tab;

  for (const auto &it : g.map)
    {
      tab.begin_row();

      std::string key;
      g.format_key(key, it.first);
      it.second.print_row(tab, format, key.c_str());

      tab.end_row();
    }

  tab.print();
}

template<typename T> void
apply_group(T &g, const std::vector<database::item_ref> &items,
	    const char *format, const char *table_format)
{
  for (const auto &it : items)
    g.insert(activity(it->storage()));

  if (format != nullptr)
    output_group_format(g, format);

  if (table_format != nullptr)
    output_group_table(g, table_format);
}

} // anonymous namespace

int
act_fold(arguments &args)
{
  database::query query;

  std::shared_ptr<database::and_term> query_and (new database::and_term);
  query.set_term(query_and);

  std::string group_field;
  bool group_keywords = false;
  double group_size = 0;
  date_interval interval(date_interval::days, 0);

  const char *format = nullptr;
  const char *table_format = nullptr;

  while (1)
    {
      const char *opt_arg = nullptr;
      int opt = args.getopt(options, &opt_arg);
      if (opt == arguments::opt_eof)
	break;

      switch (opt)
	{
	case opt_grep: {
	  std::string re(opt_arg);
	  std::shared_ptr<database::query_term>
	    term (new database::grep_term(re));
	  query_and->add_term(term);
	  break; }

	case opt_defines: {
	  std::string field(opt_arg);
	  std::shared_ptr<database::query_term>
	    term (new database::defines_term(field));
	  query_and->add_term(term);
	  break; }

	case opt_matches: {
	  const char *arg = strchr(opt_arg, ':');
	  if (arg)
	    {
	      std::string field(opt_arg, arg - opt_arg);
	      std::string re(arg + 1);
	      std::shared_ptr<database::query_term>
		term (new database::matches_term(field, re));
	      query_and->add_term(term);
	    }
	  else
	    {
	      print_usage(args);
	      return 1;
	    }
	  break; }

	case opt_contains: {
	  const char *arg = strchr(opt_arg, ':');
	  if (arg)
	    {
	      std::string field(opt_arg, arg - opt_arg);
	      std::string key(arg + 1);
	      std::shared_ptr<database::query_term>
		term (new database::contains_term(field, key));
	      query_and->add_term(term);
	    }
	  else
	    {
	      print_usage(args);
	      return 1;
	    }
	  break; }

	case opt_compare: {
	  const char *arg = opt_arg;
	  arg += strcspn(arg, "=!<>");
	  if (*arg)
	    {
	      std::string field(opt_arg, arg - opt_arg);
	      database::compare_term::compare_op op;
	      if (arg[0] == '=')
		op = database::compare_term::op_equal, arg += 1;
	      else if (arg[0] == '!' && arg[1] == '=')
		op = database::compare_term::op_not_equal, arg += 2;
	      else if (arg[0] == '<' && arg[1] == '=')
		op = database::compare_term::op_less_or_equal, arg += 2;
	      else if (arg[0] == '<')
		op = database::compare_term::op_less, arg += 1;
	      else if (arg[0] == '>' && arg[1] == '=')
		op = database::compare_term::op_greater_or_equal, arg += 2;
	      else if (arg[0] == '>')
		op = database::compare_term::op_greater, arg += 1;
	      else
		{
		  print_usage(args);
		  return 1;
		}
	      field_id id = lookup_field_id(field.c_str());
	      field_data_type type = lookup_field_data_type(id);
	      if (type == type_string)
		type = type_number;
	      std::string tem(arg);
	      double rhs;
	      if (parse_value(tem, type, &rhs, nullptr))
		{
		  std::shared_ptr<database::query_term>
		    term (new database::compare_term(field, op, rhs));
		  query_and->add_term(term);
		}
	      else
		{
		  print_usage(args);
		  return 1;
		}
	    }
	  else
	    {
	      print_usage(args);
	      return 1;
	    }
	  break; }

	case opt_interval:
	  if (!parse_date_interval(std::string(opt_arg), &interval))
	    {
	      fprintf(stderr, "Error: invalid time interval: %s\n", opt_arg);
	      return 1;
	    }
	  break;

	case opt_field:
	  if (const char *ptr = strchr(opt_arg, ':'))
	    {
	      if (group_keywords)
		{
		  fputs("Error: cannot specify both keywords and size\n",
			stderr);
		  return 1;
		}

	      group_field.clear();
	      group_field.append(opt_arg, ptr - opt_arg);

	      field_id id = lookup_field_id(group_field.c_str());
	      field_data_type type = lookup_field_data_type(id);

	      if (!parse_value(std::string(ptr+1), type, &group_size, nullptr))
		{
		  fprintf(stderr, "Error: invalid group size: %s\n", ptr+1);
		  return 1;
		}
	    }
	  else
	    group_field = opt_arg;
	  break;

	case opt_course:
	  group_field = "course";
	  break;

	case opt_keywords:
	  if (group_size != 0)
	    {
	      fputs("Error: cannot specify both keywords and size\n", stderr);
	      return 1;
	    }
	  group_field = opt_arg;
	  group_keywords = true;
	  break;

	case opt_equipment:
	  group_field = "equipment";
	  group_keywords = true;
	  break;

	case opt_format:
	  format = opt_arg;
	  break;

	case arguments::opt_error:
	  fprintf(stderr, "Error: invalid argument: %s\n\n", opt_arg);
	  print_usage(args);
	  return 1;
	}
    }

  if (format == nullptr && table_format == nullptr)
    table_format = "%-{key} %{distance} %@{distance}";

  if (args.argc() != 0)
    {
      std::vector<date_range> dates;

      if (!args.make_date_range(dates))
	return 1;

      query.set_date_ranges(dates);
    }
  else
    query.add_date_range(date_range(0, time(nullptr)));

  database db;

  std::vector<database::item_ref> items;
  db.execute_query(query, items);

  if (group_field.size() != 0)
    {
      if (group_keywords)
	{
	  keyword_group g(group_field.c_str());
	  apply_group(g, items, format, table_format);
	}
      else if (group_size > 0)
	{
	  value_group g(group_field.c_str(), group_size);
	  apply_group(g, items, format, table_format);
	}
      else
	{
	  string_group g(group_field.c_str());
	  apply_group(g, items, format, table_format);
	}
    }
  else if (interval.count > 0)
    {
      interval_group g(interval);
      apply_group(g, items, format, table_format);
    }

  return 0;
}

int
main(int argc, const char **argv)
{
  arguments args(argc, argv);

  const char *interval = nullptr;

  if (args.program_name_p("act-daily"))
    {
      interval = "day";
    }
  else if (args.program_name_p("act-weekly"))
    {
      interval = "week";
    }
  else if (args.program_name_p("act-monthly"))
    {
      interval = "month";
    }
  else if (args.program_name_p("act-yearly"))
    {
      interval = "year";
    }

  if (interval != nullptr)
    {
      args.push_front(interval);
      args.push_front("--interval");
    }

  return act_fold(args);
}
<commit_msg>fix fold's --table option<commit_after>// -*- c-style: gnu -*-

#include "act-activity.h"
#include "act-activity-accum.h"
#include "act-arguments.h"
#include "act-config.h"
#include "act-database.h"
#include "act-format.h"
#include "act-output-table.h"
#include "act-util.h"

#include <cmath>
#include <map>
#include <xlocale.h>

using namespace act;

namespace {

enum option_id
{
  opt_grep,
  opt_defines,
  opt_matches,
  opt_contains,
  opt_compare,
  opt_interval,
  opt_field,
  opt_course,
  opt_keywords,
  opt_equipment,
  opt_format,
  opt_table,
};

const arguments::option options[] =
{
  {opt_grep, "grep", 'g', "REGEXP", "Add grep-body query term."},
  {opt_defines, "defines", 'd', "FIELD", "Add defines-field query term."},
  {opt_matches, "matches", 'm', "FIELD:REGEXP", "Add re-match query term."},
  {opt_contains, "contains", 'c', "FIELD:KEYWORD", "Add keyword query term."},
  {opt_compare, "compare", 'C', "FIELDxVALUE",
   "Add compare query term. 'x' from: = != < > <= >="},
  {opt_interval, "interval", 'i', "INTERVAL", "Group by date-interval."},
  {opt_field, "field", 'f', "FIELD[:SIZE]", "Group by the named field."},
  {opt_course, "course", 0, nullptr, "Group by Course field."},
  {opt_keywords, "keywords", 'k', "FIELD", "Group by keyword."},
  {opt_equipment, "equipment", 0, nullptr, "Group by equipment."},
  {opt_format, "format", 'f', "FORMAT", "Format string."},
  {opt_table, "table", 't', "FORMAT", "Table format string."},
  {arguments::opt_eof},
};

void
print_usage(const arguments &args)
{
  fprintf(stderr, "usage: %s [OPTIONS...]\n\n", args.program_name());
  fputs("where OPTIONS are any of:\n\n", stderr);

  arguments::print_options(options, stderr);

  fputs("\n", stderr);
}

struct string_group
{
  const char *field;

  typedef std::map<std::string, activity_accum,
    case_insensitive_string_compare> group_map;

  group_map map;

  explicit string_group(const char *field);

  void insert(const activity &a);

  void format_key(std::string &buf, const std::string &key) const;
};

struct keyword_group
{
  field_id field;

  typedef std::map<std::string, activity_accum,
    case_insensitive_string_compare> group_map;

  group_map map;

  keyword_group(const char *field);

  void insert(const activity &a);

  void format_key(std::string &buf, const std::string &key) const;
};

struct value_group
{
  const char *field;
  field_id field_id;
  double bucket_size;
  double bucket_scale;

  typedef std::map<int, activity_accum> group_map;

  group_map map;

  explicit value_group(const char *field, double bucket_size);

  void insert(const activity &a);

  void format_key(std::string &buf, int key) const;
};

struct interval_group
{
  date_interval interval;

  typedef std::map<int, activity_accum> group_map;

  group_map map;

  explicit interval_group(const date_interval &interval);

  void insert(const activity &a);

  void format_key(std::string &buf, int key) const;
};

string_group::string_group(const char *f)
: field(f)
{
}

void
string_group::insert(const activity &a)
{
  if(const std::string *ptr = a.field_ptr(field))
    {
      map[*ptr].add(a);
    }
}

void
string_group::format_key(std::string &buf, const std::string &key) const
{
  buf.append(key);
}

keyword_group::keyword_group(const char *f)
: field(lookup_field_id(f))
{
}

void
keyword_group::insert(const activity &a)
{
  if(const std::vector<std::string> *ptr = a.field_keywords_ptr(field))
    {
      for (const auto &it : *ptr)
	map[it].add(a);
    }
}

void
keyword_group::format_key(std::string &buf, const std::string &key) const
{
  buf.append(key);
}

value_group::value_group(const char *f, double size)
: field(f),
  field_id(lookup_field_id(f)),
  bucket_size(size),
  bucket_scale(1 / size)
{
}

void
value_group::insert(const activity &a)
{
  double value = 0;

  if (field_id != field_custom)
    value = a.field_value(field_id);
  else if (const std::string *ptr = a.field_ptr(field))
    parse_number(*ptr, &value);

  if (value == 0)
    return;

  int idx = (int) floor(value * bucket_scale);

  map[idx].add(a);
}

void
value_group::format_key(std::string &buf, int key) const
{
  double min = key * bucket_size;
  double max = (key + 1) * bucket_size;

  field_data_type type = lookup_field_data_type(field_id);

  buf.push_back('[');
  format_value(buf, type, min, unit_unknown);
  buf.append(", ");
  format_value(buf, type, max, unit_unknown);
  buf.push_back(')');
}

interval_group::interval_group(const date_interval &i)
: interval(i)
{
}

void
interval_group::insert(const activity &a)
{
  time_t date = a.date();
  if (date == 0)
    return;

  int idx = interval.date_index(date);

  map[idx].add(a);
}

void
interval_group::format_key(std::string &buf, int key) const
{
  interval.append_date(buf, key);
}

template<typename T> void
output_group_format(T &g, const char *format)
{
  for (const auto &it : g.map)
    {
      std::string key;
      g.format_key(key, it.first);
      it.second.printf(format, key.c_str());
    }
}

template<typename T> void
output_group_table(T &g, const char *format)
{
  output_table tab;

  for (const auto &it : g.map)
    {
      tab.begin_row();

      std::string key;
      g.format_key(key, it.first);
      it.second.print_row(tab, format, key.c_str());

      tab.end_row();
    }

  tab.print();
}

template<typename T> void
apply_group(T &g, const std::vector<database::item_ref> &items,
	    const char *format, const char *table_format)
{
  for (const auto &it : items)
    g.insert(activity(it->storage()));

  if (format != nullptr)
    output_group_format(g, format);

  if (table_format != nullptr)
    output_group_table(g, table_format);
}

} // anonymous namespace

int
act_fold(arguments &args)
{
  database::query query;

  std::shared_ptr<database::and_term> query_and (new database::and_term);
  query.set_term(query_and);

  std::string group_field;
  bool group_keywords = false;
  double group_size = 0;
  date_interval interval(date_interval::days, 0);

  const char *format = nullptr;
  const char *table_format = nullptr;

  while (1)
    {
      const char *opt_arg = nullptr;
      int opt = args.getopt(options, &opt_arg);
      if (opt == arguments::opt_eof)
	break;

      switch (opt)
	{
	case opt_grep: {
	  std::string re(opt_arg);
	  std::shared_ptr<database::query_term>
	    term (new database::grep_term(re));
	  query_and->add_term(term);
	  break; }

	case opt_defines: {
	  std::string field(opt_arg);
	  std::shared_ptr<database::query_term>
	    term (new database::defines_term(field));
	  query_and->add_term(term);
	  break; }

	case opt_matches: {
	  const char *arg = strchr(opt_arg, ':');
	  if (arg)
	    {
	      std::string field(opt_arg, arg - opt_arg);
	      std::string re(arg + 1);
	      std::shared_ptr<database::query_term>
		term (new database::matches_term(field, re));
	      query_and->add_term(term);
	    }
	  else
	    {
	      print_usage(args);
	      return 1;
	    }
	  break; }

	case opt_contains: {
	  const char *arg = strchr(opt_arg, ':');
	  if (arg)
	    {
	      std::string field(opt_arg, arg - opt_arg);
	      std::string key(arg + 1);
	      std::shared_ptr<database::query_term>
		term (new database::contains_term(field, key));
	      query_and->add_term(term);
	    }
	  else
	    {
	      print_usage(args);
	      return 1;
	    }
	  break; }

	case opt_compare: {
	  const char *arg = opt_arg;
	  arg += strcspn(arg, "=!<>");
	  if (*arg)
	    {
	      std::string field(opt_arg, arg - opt_arg);
	      database::compare_term::compare_op op;
	      if (arg[0] == '=')
		op = database::compare_term::op_equal, arg += 1;
	      else if (arg[0] == '!' && arg[1] == '=')
		op = database::compare_term::op_not_equal, arg += 2;
	      else if (arg[0] == '<' && arg[1] == '=')
		op = database::compare_term::op_less_or_equal, arg += 2;
	      else if (arg[0] == '<')
		op = database::compare_term::op_less, arg += 1;
	      else if (arg[0] == '>' && arg[1] == '=')
		op = database::compare_term::op_greater_or_equal, arg += 2;
	      else if (arg[0] == '>')
		op = database::compare_term::op_greater, arg += 1;
	      else
		{
		  print_usage(args);
		  return 1;
		}
	      field_id id = lookup_field_id(field.c_str());
	      field_data_type type = lookup_field_data_type(id);
	      if (type == type_string)
		type = type_number;
	      std::string tem(arg);
	      double rhs;
	      if (parse_value(tem, type, &rhs, nullptr))
		{
		  std::shared_ptr<database::query_term>
		    term (new database::compare_term(field, op, rhs));
		  query_and->add_term(term);
		}
	      else
		{
		  print_usage(args);
		  return 1;
		}
	    }
	  else
	    {
	      print_usage(args);
	      return 1;
	    }
	  break; }

	case opt_interval:
	  if (!parse_date_interval(std::string(opt_arg), &interval))
	    {
	      fprintf(stderr, "Error: invalid time interval: %s\n", opt_arg);
	      return 1;
	    }
	  break;

	case opt_field:
	  if (const char *ptr = strchr(opt_arg, ':'))
	    {
	      if (group_keywords)
		{
		  fputs("Error: cannot specify both keywords and size\n",
			stderr);
		  return 1;
		}

	      group_field.clear();
	      group_field.append(opt_arg, ptr - opt_arg);

	      field_id id = lookup_field_id(group_field.c_str());
	      field_data_type type = lookup_field_data_type(id);

	      if (!parse_value(std::string(ptr+1), type, &group_size, nullptr))
		{
		  fprintf(stderr, "Error: invalid group size: %s\n", ptr+1);
		  return 1;
		}
	    }
	  else
	    group_field = opt_arg;
	  break;

	case opt_course:
	  group_field = "course";
	  break;

	case opt_keywords:
	  if (group_size != 0)
	    {
	      fputs("Error: cannot specify both keywords and size\n", stderr);
	      return 1;
	    }
	  group_field = opt_arg;
	  group_keywords = true;
	  break;

	case opt_equipment:
	  group_field = "equipment";
	  group_keywords = true;
	  break;

	case opt_format:
	  format = opt_arg;
	  break;

	case opt_table:
	  table_format = opt_arg;
	  break;

	case arguments::opt_error:
	  fprintf(stderr, "Error: invalid argument: %s\n\n", opt_arg);
	  print_usage(args);
	  return 1;
	}
    }

  if (format == nullptr && table_format == nullptr)
    table_format = "%-{key} %{distance} %@{distance}";

  if (args.argc() != 0)
    {
      std::vector<date_range> dates;

      if (!args.make_date_range(dates))
	return 1;

      query.set_date_ranges(dates);
    }
  else
    query.add_date_range(date_range(0, time(nullptr)));

  database db;

  std::vector<database::item_ref> items;
  db.execute_query(query, items);

  if (group_field.size() != 0)
    {
      if (group_keywords)
	{
	  keyword_group g(group_field.c_str());
	  apply_group(g, items, format, table_format);
	}
      else if (group_size > 0)
	{
	  value_group g(group_field.c_str(), group_size);
	  apply_group(g, items, format, table_format);
	}
      else
	{
	  string_group g(group_field.c_str());
	  apply_group(g, items, format, table_format);
	}
    }
  else if (interval.count > 0)
    {
      interval_group g(interval);
      apply_group(g, items, format, table_format);
    }

  return 0;
}

int
main(int argc, const char **argv)
{
  arguments args(argc, argv);

  const char *interval = nullptr;

  if (args.program_name_p("act-daily"))
    {
      interval = "day";
    }
  else if (args.program_name_p("act-weekly"))
    {
      interval = "week";
    }
  else if (args.program_name_p("act-monthly"))
    {
      interval = "month";
    }
  else if (args.program_name_p("act-yearly"))
    {
      interval = "year";
    }

  if (interval != nullptr)
    {
      args.push_front(interval);
      args.push_front("--interval");
    }

  return act_fold(args);
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2019 ScyllaDB
 */

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

#pragma once

#include <seastar/core/shared_ptr.hh>
#include <seastar/core/file.hh>
#include "seastarx.hh"

#include "db/timeout_clock.hh"

struct reader_resources {
    int count = 0;
    ssize_t memory = 0;

    reader_resources() = default;

    reader_resources(int count, ssize_t memory)
        : count(count)
        , memory(memory) {
    }

    bool operator>=(const reader_resources& other) const {
        return count >= other.count && memory >= other.memory;
    }

    reader_resources& operator-=(const reader_resources& other) {
        count -= other.count;
        memory -= other.memory;
        return *this;
    }

    reader_resources& operator+=(const reader_resources& other) {
        count += other.count;
        memory += other.memory;
        return *this;
    }

    explicit operator bool() const {
        return count > 0 || memory > 0;
    }
};

class reader_concurrency_semaphore;

/// A permit for a specific read.
///
/// Used to track the read's resource consumption and wait for admission to read
/// from the disk.
/// Use `consume_memory()` to register memory usage. Use `wait_admission()` to
/// wait for admission, before reading from the disk. Both methods return a
/// `resource_units` RAII object that should be held onto while the respective
/// resources are in use.
class reader_permit {
    friend class reader_concurrency_semaphore;

public:
    class resource_units {
        reader_concurrency_semaphore* _semaphore;
        reader_resources _resources;

        friend class reader_permit;
        friend class reader_concurrency_semaphore;
    private:
        resource_units(reader_concurrency_semaphore& semaphore, reader_resources res) noexcept;
    public:
        resource_units(const resource_units&) = delete;
        resource_units(resource_units&&) noexcept;
        ~resource_units();
        resource_units& operator=(const resource_units&) = delete;
        resource_units& operator=(resource_units&&) noexcept;
        void add(resource_units&& o);
        void reset(reader_resources res = {});
    };

private:
    reader_concurrency_semaphore* _semaphore;

private:
    explicit reader_permit(reader_concurrency_semaphore& semaphore);

public:
    bool operator==(const reader_permit& o) const {
        return _semaphore == o._semaphore;
    }

    reader_concurrency_semaphore& semaphore() {
        return *_semaphore;
    }

    future<resource_units> wait_admission(size_t memory, db::timeout_clock::time_point timeout);

    resource_units consume_memory(size_t memory = 0);

    resource_units consume_resources(reader_resources res);

    void release();
};

template <typename Char>
temporary_buffer<Char> make_tracked_temporary_buffer(temporary_buffer<Char> buf, reader_permit& permit) {
    return temporary_buffer<Char>(buf.get_write(), buf.size(),
            make_deleter(buf.release(), [units = permit.consume_memory(buf.size())] () mutable { units.reset(); }));
}

file make_tracked_file(file f, reader_permit p);
<commit_msg>reader_permit: reader_resources: add operator- and operator+<commit_after>/*
 * Copyright (C) 2019 ScyllaDB
 */

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

#pragma once

#include <seastar/core/shared_ptr.hh>
#include <seastar/core/file.hh>
#include "seastarx.hh"

#include "db/timeout_clock.hh"

struct reader_resources {
    int count = 0;
    ssize_t memory = 0;

    reader_resources() = default;

    reader_resources(int count, ssize_t memory)
        : count(count)
        , memory(memory) {
    }

    bool operator>=(const reader_resources& other) const {
        return count >= other.count && memory >= other.memory;
    }

    reader_resources operator-(const reader_resources& other) const {
        return reader_resources{count - other.count, memory - other.memory};
    }

    reader_resources& operator-=(const reader_resources& other) {
        count -= other.count;
        memory -= other.memory;
        return *this;
    }

    reader_resources operator+(const reader_resources& other) const {
        return reader_resources{count + other.count, memory + other.memory};
    }

    reader_resources& operator+=(const reader_resources& other) {
        count += other.count;
        memory += other.memory;
        return *this;
    }

    explicit operator bool() const {
        return count > 0 || memory > 0;
    }
};

class reader_concurrency_semaphore;

/// A permit for a specific read.
///
/// Used to track the read's resource consumption and wait for admission to read
/// from the disk.
/// Use `consume_memory()` to register memory usage. Use `wait_admission()` to
/// wait for admission, before reading from the disk. Both methods return a
/// `resource_units` RAII object that should be held onto while the respective
/// resources are in use.
class reader_permit {
    friend class reader_concurrency_semaphore;

public:
    class resource_units {
        reader_concurrency_semaphore* _semaphore;
        reader_resources _resources;

        friend class reader_permit;
        friend class reader_concurrency_semaphore;
    private:
        resource_units(reader_concurrency_semaphore& semaphore, reader_resources res) noexcept;
    public:
        resource_units(const resource_units&) = delete;
        resource_units(resource_units&&) noexcept;
        ~resource_units();
        resource_units& operator=(const resource_units&) = delete;
        resource_units& operator=(resource_units&&) noexcept;
        void add(resource_units&& o);
        void reset(reader_resources res = {});
    };

private:
    reader_concurrency_semaphore* _semaphore;

private:
    explicit reader_permit(reader_concurrency_semaphore& semaphore);

public:
    bool operator==(const reader_permit& o) const {
        return _semaphore == o._semaphore;
    }

    reader_concurrency_semaphore& semaphore() {
        return *_semaphore;
    }

    future<resource_units> wait_admission(size_t memory, db::timeout_clock::time_point timeout);

    resource_units consume_memory(size_t memory = 0);

    resource_units consume_resources(reader_resources res);

    void release();
};

template <typename Char>
temporary_buffer<Char> make_tracked_temporary_buffer(temporary_buffer<Char> buf, reader_permit& permit) {
    return temporary_buffer<Char>(buf.get_write(), buf.size(),
            make_deleter(buf.release(), [units = permit.consume_memory(buf.size())] () mutable { units.reset(); }));
}

file make_tracked_file(file f, reader_permit p);
<|endoftext|>


<commit_before>//      sites.cpp
//
//      Copyright 2011 Florian Weber <florian.weber@sfz-bw.de>
//
//      This program is free software; you can redistribute it and/or modify
//      it under the terms of the GNU Affero General Public License as published by
//      the Free Software Foundation; either version 3 of the License, or
//      (at your option) any later version.
//
//      This program is distributed in the hope that it will be useful,
//      but WITHOUT ANY WARRANTY; without even the implied warranty of
//      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//      GNU Affero General Public License for more details.
//
//      You should have received a copy of the GNU Affero General Public License
//      along with this program; if not, write to the Free Software
//      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
//      MA 02110-1301, USA.
//
//

#include "actions.hpp"
#include "blogentry.hpp"
#include "date.hpp"
#include "files.hpp"
#include "lines.hpp"
#include "os_commands.hpp"

#include <iostream>
#include <cstdlib>

using namespace std;

int create_all(settings &S){
	read_entries(S,true);
	string filename;
	for(list<blogentry*>::iterator it=S.blogentries.begin();it!=S.blogentries.end();++it){
		filename=S.single_entries_dir+(*it)->get_id()+".html";
		write_page(*it,S,filename);

	}

	if(S.number_of_mainpageposts<=-1||int(S.blogentries.size())<=S.number_of_mainpageposts){
		write_page(S.blogentries,S,S.blog);
	}
	else{
		list<blogentry*> mainpageposts;
		int i=0;
		for(list<blogentry*>::iterator it=S.blogentries.begin();it!=S.blogentries.end();++it){
			if(i<S.number_of_mainpageposts){
				++i;
				mainpageposts.push_back(*it);
			}
			else break;
		}
		write_page(mainpageposts,S,S.blog);
	}

	create_rss(S);
	create_tags_page(S);
	return 0;
}

int create_latest(settings &S){
	if(S.number_of_mainpageposts<0){
		return create_all(S);
	}
	read_entries(S,false);
	list<blogentry*> mainpageposts;
	string filename;
	int i=0;
	for(list<blogentry*>::iterator it=S.blogentries.begin();it!=S.blogentries.end();++it){
		if(i>=S.number_of_mainpageposts){
			break;
		}
		++i;
		filename=S.single_entries_dir+(*it)->get_id()+".html";
		write_page(*it,S,filename);
		mainpageposts.push_back(*it);
	}
	write_page(mainpageposts,S,S.blog);
	create_rss(S);
	return 0;
}

int create(settings &S,ID id){
	string filename;
	blogentry *entry=NULL;
	read_entries(S,false);
	int n=0,i=0;
	for(list<blogentry*>::iterator it=S.blogentries.begin();it!=S.blogentries.end();++it){
		if(i>=S.number_of_mainpageposts&&i>=int(S.blogentries.size())){
			break;
		}
		++i;
		if((*it)->id()==id){
			n=i;
			entry=*it;
			break;
		}
	}
	if(entry==NULL){
		return 1;
	}
	if(n<=S.number_of_mainpageposts){
		return create_latest(S);
	}
	filename=S.single_entries_dir+entry->get_id()+".html";
	write_page(entry,S,filename);
	return 0;
}

int create_rss(settings &S){
	deque<string> tags;
	if(S.rss_feed.empty()){
		return 1;
	}
	LINES feed,content;
	feed.push_back(string("<?xml version=\"1.0\" encoding=\"utf-8\"?>"));
	feed.push_back(string("<rss version=\"2.0\">\n\t<channel>"));
	feed+=read_file(S.rss_channel_description_file);
	int i=0;
	for(list<blogentry*>::iterator it=S.blogentries.begin();it!=S.blogentries.end();++it){
		if(i>=S.number_of_mainpageposts){
			break;
		}
		++i;
		content=push_string_to_front_of_every_line((*it)->content(),"\t\t\t\t");
		replace(content,"href=\"/","href=\""+S.url+"/");
		replace(content,"src=\"/","src=\""+S.url+"/");

		feed.push_back(string("\t\t<item>"));
		feed.push_back(string("\t\t\t<title>")+(*it)->get_heading()+"</title>");
		feed.push_back(string("\t\t\t<link>")+(*it)->get_url(S)+".html</link>");
		feed.push_back(string("\t\t\t<description><![CDATA["));
		feed+=content;
		feed.push_back(string("\t\t\t]]></description>"));

		///optional data:
		if(!(*it)->get_iso_date().empty()){
			feed.push_back(string("\t\t\t<pubDate>")+(*it)->get_iso_date()+"</pubDate>");
		}
		feed.push_back(string("\t\t\t<guid>")+(*it)->get_url(S)+".html</guid>");

		tags=(*it)->get_tags();
		for(deque<string>::iterator it=tags.begin();it!=tags.end();++it){
			if(!it->empty()){
				feed.push_back(string("\t\t\t<category>")+*it+"</category>");
			}
		}

		feed.push_back(string("\t\t</item>"));
	}
	feed.push_back(string("\t</channel>\n</rss>"));
	write_file(S.rss_feed,feed);
	return 0;
}

int import(settings &S,string filename){
	string heading, date, content_file, comment_file, conf_file, tags;
	LINES data=read_file(filename), conf_file_content;
	list<string> conf_lines;
	int number_of_conf_lines=0;

	for(LINES::iterator it=data.begin();it!=data.end();++it){
		if((*it)[0]=='#'){
			conf_lines.push_back(*it);
			++number_of_conf_lines;
		}
		else{
			break;
		}
	}
	for(list<string>::iterator it=conf_lines.begin();it!=conf_lines.end();++it){
		if(it->find("#HEADING=")==0){
			heading=cut(*it,"=").second;
		}
		else if(it->find("#TAGS")==0){
			if(tags.empty()){
				tags=cut(*it,"=").second;
			}
			else{
				tags+=","+cut(*it,"=").second;
			}
		}
	}

	if(number_of_conf_lines){
		data.erase(data.begin(),data.begin()+number_of_conf_lines);
	}

	while(heading==""){
		cout<<">>> ";
		getline(cin,heading);
	}
	++S.last_id;
	content_file=S.datadir+S.last_id.get()+".html";
	comment_file=S.datadir+S.last_id.get()+"-comments.html";
	conf_file=S.datadir+S.last_id.get()+".conf";

	conf_file_content.push_back(ID_SETTER+'='+S.last_id.get());
	conf_file_content.push_back(HEADING_SETTER+'='+heading);
	conf_file_content.push_back(CONTENT_FILE_SETTER+'='+content_file);
	conf_file_content.push_back(COMMENTS_FILE_SETTER+'='+comment_file);
	conf_file_content.push_back(DISPLAY_DATE_SETTER+'='+get_localdate(S));
	conf_file_content.push_back(ISO_DATE_SETTER+'='+get_isodate());
	conf_file_content.push_back(TAGS_SETTER+'='+tags);

	write_file(conf_file,conf_file_content);

	insert_to_begin_of_file(S.list_of_entries,conf_file);

	write_file(content_file,data);
	cout<<S.last_id.get()<<": "<<heading<<endl;
	if(S.toc_in_singleentries){
		create_all(S);
	}
	else{
		create_latest(S);
	}
	change_rights(content_file,"a+w");
	write_file(comment_file,"");
	change_rights(comment_file,"a+w");
	change_rights(S.single_entries_dir+S.last_id.get()+".html","a+w");

	create_tags_page(S);
	return 0;
}

int edit(settings &S, ID id){
	string filename="", command;
	read_entries(S,false);
	for(list<blogentry*>::iterator it=S.blogentries.begin();it!=S.blogentries.end();++it){
		if((*it)->id()==id){
			filename=(*it)->get_filename();
			break;
		}
	}
	if(filename==""){
		cerr<<"Error: Entry not found."<<endl;
		return 1;
	}
	if(S.editor==""){
		cerr<<"Error: No editor specified."<<endl;
		return 2;
	}
	command=S.editor+" "+filename;

	system(command.c_str());

	return create(S,id);
}

int comment(settings &S,ID id,string filename){
	read_entries(S,false);
	for(list<blogentry*>::iterator it=S.blogentries.begin();it!=S.blogentries.end();++it){
		if((*it)->id()==id){
			(*it)->new_comment(filename,S);
			return create(S,id);
		}
	}
	cerr<<"Not found"<<endl;
	return 1;
}

int edit_comment(settings &S, ID id){
	read_entries(S,false);
	for(list<blogentry*>::iterator it=S.blogentries.begin();it!=S.blogentries.end();++it){
		if((*it)->id()==id){
			system((S.editor+" "+(*it)->get_comments_filename()).c_str());
			return create(S,id);
		}
	}
	cerr<<"Not found"<<endl;
	return 1;
}

int list_entries(settings &S){
	read_entries(S,false);
	for(list<blogentry*>::reverse_iterator it=S.blogentries.rbegin();it!=S.blogentries.rend();++it){
		cout<<(*it)->get_id()<<":\t"<<(*it)->get_heading()<<"\n";
	}
	cout<<flush;
	return 0;
}

list<blogentry*> search(settings &S, string phrase){
	list<blogentry*> results;
	LINES content;
	for(list<blogentry *>::iterator it=S.blogentries.begin();it!=S.blogentries.end();++it){
		if((*it)->get_heading().find(phrase)!=string::npos){
			results.push_back(*it);
			continue;
		}
		else{
			content=(*it)->content();
			for(LINES::iterator content_it=content.begin();content_it!=content.end();++content_it){
				if(content_it->find(phrase)!=string::npos){
					results.push_back(*it);
					break;
				}
			}
		}
	}
	return results;
}

void print_search(settings &S,string phrase){
	read_entries(S,true);
	list<blogentry*> results=search(S,phrase);
	for(list<blogentry *>::iterator it=results.begin();it!=results.end();++it){
		cout<<(*it)->get_id()<<":\t"<<(*it)->get_heading()<<endl;
	}
}

void print_html_search(settings &S,string phrase){
	read_entries(S,true);
	list<blogentry*> results=search(S,phrase);
	if(results.empty()){
		cout<<"
No results
"<<endl;
		return;
	}
	cout<<"
"<<endl;
	for(list<blogentry *>::iterator it=results.begin();it!=results.end();++it){
		cout<<"
<a href=\""<<(*it)->get_url(S)<<"\">"<<(*it)->get_heading()<<"
"<<endl;
	}
	cout<<"
"<<endl;
}<commit_msg>small performance improvement<commit_after>//      sites.cpp
//
//      Copyright 2011 Florian Weber <florian.weber@sfz-bw.de>
//
//      This program is free software; you can redistribute it and/or modify
//      it under the terms of the GNU Affero General Public License as published by
//      the Free Software Foundation; either version 3 of the License, or
//      (at your option) any later version.
//
//      This program is distributed in the hope that it will be useful,
//      but WITHOUT ANY WARRANTY; without even the implied warranty of
//      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//      GNU Affero General Public License for more details.
//
//      You should have received a copy of the GNU Affero General Public License
//      along with this program; if not, write to the Free Software
//      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
//      MA 02110-1301, USA.
//
//

#include "actions.hpp"
#include "blogentry.hpp"
#include "date.hpp"
#include "files.hpp"
#include "lines.hpp"
#include "os_commands.hpp"

#include <iostream>
#include <cstdlib>

using namespace std;

int create_all(settings &S){
	read_entries(S,true);
	string filename;
	for(list<blogentry*>::iterator it=S.blogentries.begin();it!=S.blogentries.end();++it){
		filename=S.single_entries_dir+(*it)->get_id()+".html";
		write_page(*it,S,filename);

	}

	if(S.number_of_mainpageposts<=-1||int(S.blogentries.size())<=S.number_of_mainpageposts){
		write_page(S.blogentries,S,S.blog);
	}
	else{
		list<blogentry*> mainpageposts;
		int i=0;
		for(list<blogentry*>::iterator it=S.blogentries.begin();it!=S.blogentries.end();++it){
			if(i<S.number_of_mainpageposts){
				++i;
				mainpageposts.push_back(*it);
			}
			else break;
		}
		write_page(mainpageposts,S,S.blog);
	}

	create_rss(S);
	create_tags_page(S);
	return 0;
}

int create_latest(settings &S){
	if(S.number_of_mainpageposts<0){
		return create_all(S);
	}
	read_entries(S,false);
	list<blogentry*> mainpageposts;
	string filename;
	int i=0;
	for(list<blogentry*>::iterator it=S.blogentries.begin();it!=S.blogentries.end();++it){
		if(i>=S.number_of_mainpageposts){
			break;
		}
		++i;
		filename=S.single_entries_dir+(*it)->get_id()+".html";
		write_page(*it,S,filename);
		mainpageposts.push_back(*it);
	}
	write_page(mainpageposts,S,S.blog);
	create_rss(S);
	return 0;
}

int create(settings &S,ID id){
	string filename;
	blogentry *entry=NULL;
	read_entries(S,false);
	int n=0,i=0;
	for(list<blogentry*>::iterator it=S.blogentries.begin();it!=S.blogentries.end();++it){
		if(i>=S.number_of_mainpageposts&&i>=int(S.blogentries.size())){
			break;
		}
		++i;
		if((*it)->id()==id){
			n=i;
			entry=*it;
			break;
		}
	}
	if(entry==NULL){
		return 1;
	}
	if(n<=S.number_of_mainpageposts){
		return create_latest(S);
	}
	filename=S.single_entries_dir+entry->get_id()+".html";
	write_page(entry,S,filename);
	return 0;
}

int create_rss(settings &S){
	deque<string> tags;
	if(S.rss_feed.empty()){
		return 1;
	}
	LINES feed,content;
	feed.push_back(string("<?xml version=\"1.0\" encoding=\"utf-8\"?>"));
	feed.push_back(string("<rss version=\"2.0\">\n\t<channel>"));
	feed+=read_file(S.rss_channel_description_file);
	int i=0;
	for(list<blogentry*>::iterator it=S.blogentries.begin();it!=S.blogentries.end();++it){
		if(i>=S.number_of_mainpageposts){
			break;
		}
		++i;
		content=push_string_to_front_of_every_line((*it)->content(),"\t\t\t\t");
		replace(content,"href=\"/","href=\""+S.url+"/");
		replace(content,"src=\"/","src=\""+S.url+"/");

		feed.push_back(string("\t\t<item>"));
		feed.push_back(string("\t\t\t<title>")+(*it)->get_heading()+"</title>");
		feed.push_back(string("\t\t\t<link>")+(*it)->get_url(S)+".html</link>");
		feed.push_back(string("\t\t\t<description><![CDATA["));
		feed+=content;
		feed.push_back(string("\t\t\t]]></description>"));

		///optional data:
		if(!(*it)->get_iso_date().empty()){
			feed.push_back(string("\t\t\t<pubDate>")+(*it)->get_iso_date()+"</pubDate>");
		}
		feed.push_back(string("\t\t\t<guid>")+(*it)->get_url(S)+".html</guid>");

		tags=(*it)->get_tags();
		for(deque<string>::iterator it=tags.begin();it!=tags.end();++it){
			if(!it->empty()){
				feed.push_back(string("\t\t\t<category>")+*it+"</category>");
			}
		}

		feed.push_back(string("\t\t</item>"));
	}
	feed.push_back(string("\t</channel>\n</rss>"));
	write_file(S.rss_feed,feed);
	return 0;
}

int import(settings &S,string filename){
	string heading, date, content_file, comment_file, conf_file, tags;
	LINES data=read_file(filename), conf_file_content;
	list<string> conf_lines;
	int number_of_conf_lines=0;

	for(LINES::iterator it=data.begin();it!=data.end();++it){
		if((*it)[0]=='#'){
			conf_lines.push_back(*it);
			++number_of_conf_lines;
		}
		else{
			break;
		}
	}
	for(list<string>::iterator it=conf_lines.begin();it!=conf_lines.end();++it){
		if(it->find("#HEADING=")==0){
			heading=cut(*it,"=").second;
		}
		else if(it->find("#TAGS")==0){
			if(tags.empty()){
				tags=cut(*it,"=").second;
			}
			else{
				tags+=","+cut(*it,"=").second;
			}
		}
	}

	if(number_of_conf_lines){
		data.erase(data.begin(),data.begin()+number_of_conf_lines);
	}

	while(heading==""){
		cout<<">>> ";
		getline(cin,heading);
	}
	++S.last_id;
	content_file=S.datadir+S.last_id.get()+".html";
	comment_file=S.datadir+S.last_id.get()+"-comments.html";
	conf_file=S.datadir+S.last_id.get()+".conf";

	conf_file_content.push_back(ID_SETTER+'='+S.last_id.get());
	conf_file_content.push_back(HEADING_SETTER+'='+heading);
	conf_file_content.push_back(CONTENT_FILE_SETTER+'='+content_file);
	conf_file_content.push_back(COMMENTS_FILE_SETTER+'='+comment_file);
	conf_file_content.push_back(DISPLAY_DATE_SETTER+'='+get_localdate(S));
	conf_file_content.push_back(ISO_DATE_SETTER+'='+get_isodate());
	conf_file_content.push_back(TAGS_SETTER+'='+tags);

	write_file(conf_file,conf_file_content);

	insert_to_begin_of_file(S.list_of_entries,conf_file);

	write_file(content_file,data);
	cout<<S.last_id.get()<<": "<<heading<<endl;
	if(S.toc_in_singleentries){
		create_all(S);
	}
	else{
		create_latest(S);
	}
	change_rights(content_file,"a+w");
	write_file(comment_file,"");
	change_rights(comment_file,"a+w");
	change_rights(S.single_entries_dir+S.last_id.get()+".html","a+w");

	create_tags_page(S);
	return 0;
}

int edit(settings &S, ID id){
	string filename="", command;
	read_entries(S,false);
	for(list<blogentry*>::iterator it=S.blogentries.begin();it!=S.blogentries.end();++it){
		if((*it)->id()==id){
			filename=(*it)->get_filename();
			break;
		}
	}
	if(filename==""){
		cerr<<"Error: Entry not found."<<endl;
		return 1;
	}
	if(S.editor==""){
		cerr<<"Error: No editor specified."<<endl;
		return 2;
	}
	command=S.editor+" "+filename;

	system(command.c_str());

	return create(S,id);
}

int comment(settings &S,ID id,string filename){
	read_entries(S,false);
	for(list<blogentry*>::iterator it=S.blogentries.begin();it!=S.blogentries.end();++it){
		if((*it)->id()==id){
			(*it)->new_comment(filename,S);
			return create(S,id);
		}
	}
	cerr<<"Not found"<<endl;
	return 1;
}

int edit_comment(settings &S, ID id){
	read_entries(S,false);
	for(list<blogentry*>::iterator it=S.blogentries.begin();it!=S.blogentries.end();++it){
		if((*it)->id()==id){
			system((S.editor+" "+(*it)->get_comments_filename()).c_str());
			return create(S,id);
		}
	}
	cerr<<"Not found"<<endl;
	return 1;
}

int list_entries(settings &S){
	read_entries(S,false);
	for(list<blogentry*>::reverse_iterator it=S.blogentries.rbegin();it!=S.blogentries.rend();++it){
		cout<<(*it)->get_id()<<":\t"<<(*it)->get_heading()<<"\n";
	}
	cout<<flush;
	return 0;
}

list<blogentry*> search(settings &S, string phrase){
	list<blogentry*> results;
	LINES content;
	for(list<blogentry *>::iterator it=S.blogentries.begin();it!=S.blogentries.end();++it){
		if((*it)->get_heading().find(phrase)!=string::npos){
			results.push_back(*it);
			continue;
		}
		else{
			content=(*it)->content();
			for(LINES::iterator content_it=content.begin();content_it!=content.end();++content_it){
				if(content_it->find(phrase)!=string::npos){
					results.push_back(*it);
					break;
				}
			}
		}
	}
	return results;
}

void print_search(settings &S,string phrase){
	read_entries(S,false);
	list<blogentry*> results=search(S,phrase);
	for(list<blogentry *>::iterator it=results.begin();it!=results.end();++it){
		cout<<(*it)->get_id()<<":\t"<<(*it)->get_heading()<<endl;
	}
}

void print_html_search(settings &S,string phrase){
	read_entries(S,true);
	list<blogentry*> results=search(S,phrase);
	if(results.empty()){
		cout<<"
No results
"<<endl;
		return;
	}
	cout<<"
"<<endl;
	for(list<blogentry *>::iterator it=results.begin();it!=results.end();++it){
		cout<<"
<a href=\""<<(*it)->get_url(S)<<"\">"<<(*it)->get_heading()<<"
"<<endl;
	}
	cout<<"
"<<endl;
}<|endoftext|>