<commit_before>/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "skirt.h"
#include "support.h"

#include <queue>

namespace cura
{

void generateSkirt(SliceDataStorage& storage, int distance, int count, int minLength)
{
    if (count == 0) return;

    bool externalOnly = (distance > 0); // whether to include holes or not

    int primary_extruder = 0; // TODO allow for other extruder to be primary
    int primary_extrusion_width = storage.meshgroup->getExtruderTrain(primary_extruder)->getSettingInMicrons("skirt_line_width");

    Polygons& skirt_primary_extruder = storage.skirt[primary_extruder];

    Polygons first_layer_outline;
    {// add support polygons
        if (storage.support.generated)
            first_layer_outline = storage.support.supportLayers[0].supportAreas
                    .unionPolygons(storage.support.supportLayers[0].roofs);
        { // get support polygons
            for(SliceMeshStorage& mesh : storage.meshes)
            {
                if (mesh.layers.size() < 1) continue;
                SliceLayer* layer = &mesh.layers[0];
                for(unsigned int i=0; i<layer->parts.size(); i++)
                    first_layer_outline = first_layer_outline.difference(layer->parts[i].outline);
            }

            // expand and contract to smooth the final polygon
            if (count == 1 && distance > 0)
            {
                int dist = primary_extrusion_width * 5;
                first_layer_outline = first_layer_outline.offset(dist).offset(-dist);
            }
        }
    }
    { // add prime tower and part outlines
        first_layer_outline = first_layer_outline.unionPolygons(storage.primeTower.ground_poly);
        for(SliceMeshStorage& mesh : storage.meshes)
        {
            if (mesh.layers.size() < 1) continue;
            for(SliceLayerPart& part : mesh.layers[0].parts)
            {
                if (externalOnly)
                {
                    Polygons p;
                    p.add(part.outline.outerPolygon());
                    first_layer_outline = first_layer_outline.unionPolygons(p);
                }
                else
                {
                    first_layer_outline = first_layer_outline.unionPolygons(part.outline);
                }
            }
        }
    }

    Polygons skirt_polygons;
    for(int skirtNr=0; skirtNr<count;skirtNr++)
    {
        int offsetDistance = distance + primary_extrusion_width * skirtNr + primary_extrusion_width / 2;

        skirt_polygons = first_layer_outline.offset(offsetDistance, ClipperLib::jtRound);

        //Remove small inner skirt holes. Holes have a negative area, remove anything smaller then 100x extrusion "area"
        for(unsigned int n=0; n<skirt_polygons.size(); n++)
        {
            double area = skirt_polygons[n].area();
            if (area < 0 && area > -primary_extrusion_width * primary_extrusion_width * 100)
                skirt_polygons.remove(n--);
        }

        if (count == 1 && distance > 0)
        {
            skirt_polygons = skirt_polygons.convexHull();
        }

        skirt_primary_extruder.add(skirt_polygons);

        int length = skirt_primary_extruder.polygonLength();
        if (skirtNr + 1 >= count && length > 0 && length < minLength) // make brim have more lines when total length is too small
            count++;
    }

    if (false) // the code below is for the old prime tower
    { //Add a skirt UNDER the prime tower to make it stick better.
        Polygons prime_tower = storage.primeTower.ground_poly.offset(-primary_extrusion_width / 2);
        std::queue<Polygons> prime_tower_insets;
        while(prime_tower.size() > 0)
        {
            prime_tower_insets.emplace(prime_tower);
            prime_tower = prime_tower.offset(-primary_extrusion_width);
        }
        while (!prime_tower_insets.empty())
        {
            Polygons& inset = prime_tower_insets.back();
            skirt_primary_extruder.add(inset);
            prime_tower_insets.pop();
        }
    }

    { // process other extruders' brim/skirt (as one brim line around the old brim)
        int offset_distance = 0;
        int last_width = primary_extrusion_width;
        for (int extruder = 0; extruder < storage.meshgroup->getExtruderCount(); extruder++)
        {
            if (extruder == primary_extruder) { continue; }
            int width = storage.meshgroup->getExtruderTrain(extruder)->getSettingInMicrons("skirt_line_width");
            offset_distance += last_width / 2 + width/2;
            last_width = width;
            while (storage.skirt[extruder].polygonLength() < minLength)
            {
                storage.skirt[extruder].add(skirt_polygons.offset(offset_distance, ClipperLib::jtRound));
                offset_distance += width;
            }
        }
    }
}

}//namespace cura
<commit_msg>bugfix: when skirt got doubled the second skirt line wasnt a convex hull<commit_after>/** Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License */
#include "skirt.h"
#include "support.h"

#include <queue>

namespace cura
{

void generateSkirt(SliceDataStorage& storage, int distance, int count, int minLength)
{
    if (count == 0) return;

    bool externalOnly = (distance > 0); // whether to include holes or not

    int primary_extruder = 0; // TODO allow for other extruder to be primary
    int primary_extrusion_width = storage.meshgroup->getExtruderTrain(primary_extruder)->getSettingInMicrons("skirt_line_width");

    Polygons& skirt_primary_extruder = storage.skirt[primary_extruder];

    bool get_convex_hull = count == 1 && distance > 0;

    Polygons first_layer_outline;
    {// add support polygons
        if (storage.support.generated)
            first_layer_outline = storage.support.supportLayers[0].supportAreas
                    .unionPolygons(storage.support.supportLayers[0].roofs);
        { // get support polygons
            for(SliceMeshStorage& mesh : storage.meshes)
            {
                if (mesh.layers.size() < 1) continue;
                SliceLayer* layer = &mesh.layers[0];
                for(unsigned int i=0; i<layer->parts.size(); i++)
                    first_layer_outline = first_layer_outline.difference(layer->parts[i].outline);
            }

            // expand and contract to smooth the final polygon
//             if (get_convex_hull)
//             {
//                 int dist = primary_extrusion_width * 5;
//                 first_layer_outline = first_layer_outline.offset(dist).offset(-dist);
//             }
        }
    }
    { // add prime tower and part outlines
        first_layer_outline = first_layer_outline.unionPolygons(storage.primeTower.ground_poly);
        for(SliceMeshStorage& mesh : storage.meshes)
        {
            if (mesh.layers.size() < 1) continue;
            for(SliceLayerPart& part : mesh.layers[0].parts)
            {
                if (externalOnly)
                {
                    Polygons p;
                    p.add(part.outline.outerPolygon());
                    first_layer_outline = first_layer_outline.unionPolygons(p);
                }
                else
                {
                    first_layer_outline = first_layer_outline.unionPolygons(part.outline);
                }
            }
        }
    }

    std::vector<Polygons> skirts;
    for(int skirtNr=0; skirtNr<count;skirtNr++)
    {
        int offsetDistance = distance + primary_extrusion_width * skirtNr + primary_extrusion_width / 2;

        skirts.emplace_back(first_layer_outline.offset(offsetDistance, ClipperLib::jtRound));
        Polygons& skirt_polygons = skirts.back();

        //Remove small inner skirt holes. Holes have a negative area, remove anything smaller then 100x extrusion "area"
        for(unsigned int n=0; n<skirt_polygons.size(); n++)
        {
            double area = skirt_polygons[n].area();
            if (area < 0 && area > -primary_extrusion_width * primary_extrusion_width * 100)
                skirt_polygons.remove(n--);
        }

        if (get_convex_hull)
        {
            skirt_polygons = skirt_polygons.convexHull();
        }

        skirt_primary_extruder.add(skirt_polygons);

        int length = skirt_primary_extruder.polygonLength();
        if (skirtNr + 1 >= count && length > 0 && length < minLength) // make brim have more lines when total length is too small
            count++;
    }

    if (false) // the code below is for the old prime tower
    { //Add a skirt UNDER the prime tower to make it stick better.
        Polygons prime_tower = storage.primeTower.ground_poly.offset(-primary_extrusion_width / 2);
        std::queue<Polygons> prime_tower_insets;
        while(prime_tower.size() > 0)
        {
            prime_tower_insets.emplace(prime_tower);
            prime_tower = prime_tower.offset(-primary_extrusion_width);
        }
        while (!prime_tower_insets.empty())
        {
            Polygons& inset = prime_tower_insets.back();
            skirt_primary_extruder.add(inset);
            prime_tower_insets.pop();
        }
    }

    { // process other extruders' brim/skirt (as one brim line around the old brim)
        int offset_distance = 0;
        int last_width = primary_extrusion_width;
        for (int extruder = 0; extruder < storage.meshgroup->getExtruderCount(); extruder++)
        {
            if (extruder == primary_extruder) { continue; }
            int width = storage.meshgroup->getExtruderTrain(extruder)->getSettingInMicrons("skirt_line_width");
            offset_distance += last_width / 2 + width/2;
            last_width = width;
            while (storage.skirt[extruder].polygonLength() < minLength)
            {
                storage.skirt[extruder].add(skirts.back().offset(offset_distance, ClipperLib::jtRound));
                offset_distance += width;
            }
        }
    }
}

}//namespace cura
<|endoftext|>


<commit_before>// ========================================================================== //
// This file is part of DO++, a basic set of libraries in C++ for computer
// vision.
//
// Copyright (C) 2013 David Ok <david.ok8@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License v. 2.0. If a copy of the MPL was not distributed with this file,
// you can obtain one at http://mozilla.org/MPL/2.0/.
// ========================================================================== //

#include "GraphicsApplicationImpl.hpp"
#include <QFileDialog>
#include <QDebug>

namespace DO {

  GraphicsApplication::Impl::
  Impl(int argc_, char **argv_)
    : QApplication(argc_, argv_)
    , argc(argc_)
    , argv(argv_)
    , activeWindow(0)
    //, graphicsView(0)
    , mutex(QMutex::NonRecursive)
  {
    // Register painting data types.
    qRegisterMetaType<PaintingWindow *>("PaintingWindow *");
    qRegisterMetaType<QPolygonF>("QPolygonF");

    // Register mesh data structure.
    qRegisterMetaType<SimpleTriangleMesh3f>("SimpleTriangleMesh3f");

    // Register graphics view data types.
    qRegisterMetaType<QGraphicsPixmapItem *>("QGraphicsPixmapItem *");
    //qRegisterMetaType<QGraphicsItem *>("QGraphicsItem *");

    // Register Event data types
    qRegisterMetaType<Event>("Event");

    // Make sure you quit after the user thread is finished.
    connect(&userThread, SIGNAL(finished()), this, SLOT(quit()));
    setQuitOnLastWindowClosed(false);
  }

   void
   GraphicsApplication::Impl::
   createPaintingWindow(int w, int h, const QString& windowTitle,
                        int x, int y)
   {
     createdWindows << new PaintingWindow(w, h, windowTitle, x, y);
     if (createdWindows.size() == 1)
     {
       activeWindow = createdWindows.front();
       setActiveWindow(activeWindow);
     }
   }

   void
   GraphicsApplication::Impl::
   createOpenGLWindow(int w, int h, const QString& windowTitle,
                      int x, int y)
   {
     createdWindows << new OpenGLWindow(w, h, windowTitle, x, y);
     if (createdWindows.size() == 1)
     {
       activeWindow = createdWindows.front();
       setActiveWindow(activeWindow);
     }
   }

   void
   GraphicsApplication::Impl::
   createGraphicsView(int w, int h, const QString& windowTitle,
                      int x, int y)
   {
     createdWindows << new GraphicsView(w, h, windowTitle, x, y);
     if (createdWindows.size() == 1)
     {
       activeWindow = createdWindows.front();
       setActiveWindow(activeWindow);
     }
   }

   void
   GraphicsApplication::Impl::
   setActiveWindow(QWidget *w)
   {
     if (w == 0)
     {
       qWarning() << "I can't make a null window active!";
       return;
     }

     // Disconnect the communication between:
     // All windows and user thread in both directions.
     // Why? I don't want the user thread to listens to communicate from any
     // window but the requested window 'w'.
     disconnectAllWindowsIOEventsToUserThread();

     // This is now our current active window
     activeWindow = w;
     connectWindowIOEventsToUserThread(w);
   }

   void
   GraphicsApplication::Impl::
   closeWindow(QWidget *w)
   {
     QList<QPointer<QWidget> >::iterator wi = qFind(
       createdWindows.begin(), createdWindows.end(), w);
     if (wi == createdWindows.end())
     {
       qFatal("Could not find window!");
       quit();
     }

     // Store closing result here.
     bool closed = false;
     // Close the painting window if it is one.
     PaintingWindow *paintingWindow = qobject_cast<PaintingWindow *>(*wi);
     if (paintingWindow)
       closed = paintingWindow->scrollArea()->close();
     else
       closed = (*wi)->close();
     // Check the closing is successful.
     if (!closed)
     {
       qFatal("Could not close window!");
       quit();
     }

     if (!(*wi).isNull())
       delete *wi;
     createdWindows.erase(wi);
   }

   void
   GraphicsApplication::Impl::
   getFileFromDialogBox()
   {
     dialogBoxInfo.filename =
       QFileDialog::getOpenFileName(0, "Open File", "/home",
       "Images (*.png *.xpm *.jpg)");
   }

  bool
  GraphicsApplication::Impl::
  activeWindowIsVisible()
  {
    mutex.lock();
    if (activeWindow.isNull())
    {
      mutex.unlock();
      qWarning() << "No active window!";
      return false;
    }
    if (activeWindow->isHidden())
    {
      mutex.unlock();
      qWarning() << "Active window is hidden!";
      return false;
    }
    mutex.unlock();
    return true;
  }

  void
  GraphicsApplication::Impl::
  connectWindowIOEventsToUserThread(QWidget *w)
  {
    // User thread listens to mouse events.
    connect(w, SIGNAL(releasedMouseButtons(int, int, Qt::MouseButtons)),
            &userThread, SLOT(pressedMouseButtons(int, int, Qt::MouseButtons)));
    // User thread listens to keyboard events.
    connect(w, SIGNAL(pressedKey(int)),
            &userThread, SLOT(pressedKey(int)));
    // User thread listens to a generic event.
    connect(w, SIGNAL(sendEvent(Event)),
            &userThread, SLOT(receivedEvent(Event)));
  }

  void
  GraphicsApplication::Impl::
  connectAllWindowsIOEventsToUserThread()
  {
    QList<QPointer<QWidget> >::iterator w = createdWindows.begin();
    for ( ; w != createdWindows.end(); )
    {
      if (w->isNull())
      {
        w = createdWindows.erase(w);
        continue;
      }
      connectWindowIOEventsToUserThread(*w);
      ++w;
    }
  }

  void
  GraphicsApplication::Impl::
  disconnectAllWindowsIOEventsToUserThread()
  {
    QList<QPointer<QWidget> >::iterator w = createdWindows.begin();
    for ( ; w != createdWindows.end(); )
    {
      if (w->isNull())
      {
        w = createdWindows.erase(w);
        continue;
      }
      disconnect(&userThread, 0, *w, 0);
      disconnect(*w, 0, &userThread, 0);
      ++w;
    }
  }

} /* namespace DO */<commit_msg>Use `deleteLater` method to destroy `QWidget` instead.<commit_after>// ========================================================================== //
// This file is part of DO++, a basic set of libraries in C++ for computer
// vision.
//
// Copyright (C) 2013 David Ok <david.ok8@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License v. 2.0. If a copy of the MPL was not distributed with this file,
// you can obtain one at http://mozilla.org/MPL/2.0/.
// ========================================================================== //

#include "GraphicsApplicationImpl.hpp"
#include <QFileDialog>
#include <QDebug>

namespace DO {

  GraphicsApplication::Impl::
  Impl(int argc_, char **argv_)
    : QApplication(argc_, argv_)
    , argc(argc_)
    , argv(argv_)
    , activeWindow(0)
    //, graphicsView(0)
    , mutex(QMutex::NonRecursive)
  {
    // Register painting data types.
    qRegisterMetaType<PaintingWindow *>("PaintingWindow *");
    qRegisterMetaType<QPolygonF>("QPolygonF");

    // Register mesh data structure.
    qRegisterMetaType<SimpleTriangleMesh3f>("SimpleTriangleMesh3f");

    // Register graphics view data types.
    qRegisterMetaType<QGraphicsPixmapItem *>("QGraphicsPixmapItem *");
    //qRegisterMetaType<QGraphicsItem *>("QGraphicsItem *");

    // Register Event data types
    qRegisterMetaType<Event>("Event");

    // Make sure you quit after the user thread is finished.
    connect(&userThread, SIGNAL(finished()), this, SLOT(quit()));
    setQuitOnLastWindowClosed(false);
  }

   void
   GraphicsApplication::Impl::
   createPaintingWindow(int w, int h, const QString& windowTitle,
                        int x, int y)
   {
     createdWindows << new PaintingWindow(w, h, windowTitle, x, y);
     if (createdWindows.size() == 1)
     {
       activeWindow = createdWindows.front();
       setActiveWindow(activeWindow);
     }
   }

   void
   GraphicsApplication::Impl::
   createOpenGLWindow(int w, int h, const QString& windowTitle,
                      int x, int y)
   {
     createdWindows << new OpenGLWindow(w, h, windowTitle, x, y);
     if (createdWindows.size() == 1)
     {
       activeWindow = createdWindows.front();
       setActiveWindow(activeWindow);
     }
   }

   void
   GraphicsApplication::Impl::
   createGraphicsView(int w, int h, const QString& windowTitle,
                      int x, int y)
   {
     createdWindows << new GraphicsView(w, h, windowTitle, x, y);
     if (createdWindows.size() == 1)
     {
       activeWindow = createdWindows.front();
       setActiveWindow(activeWindow);
     }
   }

   void
   GraphicsApplication::Impl::
   setActiveWindow(QWidget *w)
   {
     if (w == 0)
     {
       qWarning() << "I can't make a null window active!";
       return;
     }

     // Disconnect the communication between:
     // All windows and user thread in both directions.
     // Why? I don't want the user thread to listens to communicate from any
     // window but the requested window 'w'.
     disconnectAllWindowsIOEventsToUserThread();

     // This is now our current active window
     activeWindow = w;
     connectWindowIOEventsToUserThread(w);
   }

   void
   GraphicsApplication::Impl::
   closeWindow(QWidget *w)
   {
     QList<QPointer<QWidget> >::iterator wi = qFind(
       createdWindows.begin(), createdWindows.end(), w);
     if (wi == createdWindows.end())
     {
       qFatal("Could not find window!");
       quit();
     }

     // Store closing result here.
     bool closed = false;
     // Close the painting window if it is one.
     PaintingWindow *paintingWindow = qobject_cast<PaintingWindow *>(*wi);
     if (paintingWindow)
       closed = paintingWindow->scrollArea()->close();
     else
       closed = (*wi)->close();
     // Check the closing is successful.
     if (!closed)
     {
       qFatal("Could not close window!");
       quit();
     }

     if (!(*wi).isNull())
       (*wi)->deleteLater();
     createdWindows.erase(wi);
   }

   void
   GraphicsApplication::Impl::
   getFileFromDialogBox()
   {
     dialogBoxInfo.filename =
       QFileDialog::getOpenFileName(0, "Open File", "/home",
       "Images (*.png *.xpm *.jpg)");
   }

  bool
  GraphicsApplication::Impl::
  activeWindowIsVisible()
  {
    mutex.lock();
    if (activeWindow.isNull())
    {
      mutex.unlock();
      qWarning() << "No active window!";
      return false;
    }
    if (activeWindow->isHidden())
    {
      mutex.unlock();
      qWarning() << "Active window is hidden!";
      return false;
    }
    mutex.unlock();
    return true;
  }

  void
  GraphicsApplication::Impl::
  connectWindowIOEventsToUserThread(QWidget *w)
  {
    // User thread listens to mouse events.
    connect(w, SIGNAL(releasedMouseButtons(int, int, Qt::MouseButtons)),
            &userThread, SLOT(pressedMouseButtons(int, int, Qt::MouseButtons)));
    // User thread listens to keyboard events.
    connect(w, SIGNAL(pressedKey(int)),
            &userThread, SLOT(pressedKey(int)));
    // User thread listens to a generic event.
    connect(w, SIGNAL(sendEvent(Event)),
            &userThread, SLOT(receivedEvent(Event)));
  }

  void
  GraphicsApplication::Impl::
  connectAllWindowsIOEventsToUserThread()
  {
    QList<QPointer<QWidget> >::iterator w = createdWindows.begin();
    for ( ; w != createdWindows.end(); )
    {
      if (w->isNull())
      {
        w = createdWindows.erase(w);
        continue;
      }
      connectWindowIOEventsToUserThread(*w);
      ++w;
    }
  }

  void
  GraphicsApplication::Impl::
  disconnectAllWindowsIOEventsToUserThread()
  {
    QList<QPointer<QWidget> >::iterator w = createdWindows.begin();
    for ( ; w != createdWindows.end(); )
    {
      if (w->isNull())
      {
        w = createdWindows.erase(w);
        continue;
      }
      disconnect(&userThread, 0, *w, 0);
      disconnect(*w, 0, &userThread, 0);
      ++w;
    }
  }

} /* namespace DO */<|endoftext|>


<commit_before>#include <zorbatypes/utf8.h>
#include "compiler/parser/util.h"
#include "compiler/parser/query_loc.h"
#include "errors/error_factory.h"

using namespace std;
namespace zorba {

// TODO: reuse (better) code that is probably available elsewhere in Zorba
int decode_entity (const char *in, string *out) {
  QueryLoc loc;
  const char *start = in;
  if (in [0] == '#') {
    ++in;
    int base = 10;
    if (*in == 'x') { base = 16; ++in; }
    for (; *in == '0'; ++in);
    unsigned long n = strtoul (in, (char **) &in, base);
    if (*in++ != ';')
      ZORBA_ERROR_ALERT (ZorbaError::XPST0003, &loc);
    if (! is_code_point_valid (n))
      ZORBA_ERROR_ALERT (ZorbaError::XPST0003, &loc);
    UTF8Encode (n, back_inserter (*out));
    return in - start;
  }
#define M( str, len, r ) if (strncmp (in, str, len) == 0) { *out += r; return len; }
  M ("amp;", 4, "&") else
  M ("lt;", 3, "<") else
  M ("gt;", 3, ">") else
  M ("quot;", 5, "\"") else
  M ("apos;", 5, "'")
#undef M

  ZORBA_ERROR_ALERT (ZorbaError::XPST0003, &loc);
  return -1;
}

}
<commit_msg>XQST0090 for invalid char references<commit_after>#include <zorbatypes/utf8.h>
#include "compiler/parser/util.h"
#include "compiler/parser/query_loc.h"
#include "errors/error_factory.h"

using namespace std;
namespace zorba {

// TODO: reuse (better) code that is probably available elsewhere in Zorba
int decode_entity (const char *in, string *out) {
  QueryLoc loc;
  const char *start = in;
  if (in [0] == '#') {
    ++in;
    int base = 10;
    if (*in == 'x') { base = 16; ++in; }
    for (; *in == '0'; ++in);
    unsigned long n = strtoul (in, (char **) &in, base);
    if (*in++ != ';')
      ZORBA_ERROR_ALERT (ZorbaError::XPST0003, &loc);
    if (! is_code_point_valid (n))
      ZORBA_ERROR_ALERT (ZorbaError::XQST0090, &loc);
    UTF8Encode (n, back_inserter (*out));
    return in - start;
  }
#define M( str, len, r ) if (strncmp (in, str, len) == 0) { *out += r; return len; }
  M ("amp;", 4, "&") else
  M ("lt;", 3, "<") else
  M ("gt;", 3, ">") else
  M ("quot;", 5, "\"") else
  M ("apos;", 5, "'")
#undef M

  ZORBA_ERROR_ALERT (ZorbaError::XPST0003, &loc);
  return -1;
}

}
<|endoftext|>


<commit_before>#ifndef __terark_db_segment_hpp__
#define __terark_db_segment_hpp__

#include "db_index.hpp"
#include "db_store.hpp"
#include <terark/bitmap.hpp>
#include <terark/rank_select.hpp>
#include <tbb/spin_rw_mutex.h>
#include <tbb/tbb_thread.h>

namespace terark {
	class SortableStrVec;
}

namespace terark { namespace db {

typedef tbb::spin_rw_mutex        SpinRwMutex;
typedef SpinRwMutex::scoped_lock  SpinRwLock;

// This ReadableStore is used for return full-row
// A full-row is of one table, the table has multiple indices
class TERARK_DB_DLL ReadableSegment : public ReadableStore {
public:
	ReadableSegment();
	~ReadableSegment();
	virtual class ReadonlySegment* getReadonlySegment() const;
	virtual class WritableSegment* getWritableSegment() const;
	virtual llong totalStorageSize() const = 0;
	virtual llong numDataRows() const override final;

	// Index can use different implementation for different
	// index schema and index content features
	virtual ReadableIndex* openIndex(const Schema&, PathRef path) const = 0;

	///@ if segDir==m_segDir, it is a flush
	virtual void loadRecordStore(PathRef segDir) = 0;
	virtual void saveRecordStore(PathRef segDir) const = 0;

	inline  void indexSearchExact(size_t mySegIdx, size_t indexId,
								  fstring key, valvec<llong>* recIdvec,
								  DbContext* ctx) const {
		recIdvec->erase_all();
		indexSearchExactAppend(mySegIdx, indexId, key, recIdvec, ctx);
	}

	virtual void indexSearchExactAppend(size_t mySegIdx, size_t indexId,
										fstring key, valvec<llong>* recIdvec,
										DbContext*) const = 0;

	virtual void selectColumns(llong recId, const size_t* colsId, size_t colsNum,
							   valvec<byte>* colsData, DbContext*) const = 0;
	virtual void selectOneColumn(llong recId, size_t columnId,
								 valvec<byte>* colsData, DbContext*) const = 0;

	virtual void selectColgroups(llong id, const size_t* cgIdvec, size_t cgIdvecSize,
								 valvec<byte>* cgDataVec, DbContext*) const = 0;

	void openIndices(PathRef dir);
	void saveIndices(PathRef dir) const;
	llong totalIndexSize() const;

	void saveIsDel(PathRef segDir) const;
	void loadIsDel(PathRef segDir);
	byte*loadIsDel_aux(PathRef segDir, febitvec& isDel) const;
	void closeIsDel();

	void deleteSegment();

	void load(PathRef segDir) override;
	void save(PathRef segDir) const override;

	size_t getPhysicRows() const;
	size_t getPhysicId(size_t logicId) const;
	size_t getLogicId(size_t physicId) const;

	void addtoUpdateList(size_t logicId);

	bool locked_testIsDel(size_t logicId) const {
		SpinRwLock wsLock(m_segMutex, false);
		return m_isDel[logicId];
	}

	SchemaConfigPtr         m_schema;
	valvec<ReadableIndexPtr> m_indices; // parallel with m_indexSchemaSet
	valvec<ReadableStorePtr> m_colgroups; // indices + pure_colgroups
	size_t      m_delcnt;
	febitvec    m_isDel;
	byte*       m_isDelMmap = nullptr;
	rank_select_se m_isPurged; // just for ReadonlySegment
	byte*          m_isPurgedMmap;
	boost::filesystem::path m_segDir;
	mutable SpinRwMutex m_segMutex;
	valvec<uint32_t> m_updateList; // including deletions
	febitvec    m_updateBits; // if m_updateList is too large, use updateBits
	ReadableStorePtr m_deletionTime; // for snapshot, an uint64 array
	bool        m_tobeDel;
	bool        m_isDirty;
	bool        m_isFreezed;
	bool        m_hasLockFreePointSearch;
	bool        m_bookUpdates;
	bool        m_withPurgeBits;  // just for ReadonlySegment
};
typedef boost::intrusive_ptr<ReadableSegment> ReadableSegmentPtr;

// Every index is a ReadableIndexStore
//
// The <<store>> is multi-part, because the <<store>> may be
// very large, and the compressible database algo need to fit
// all uncompressed data into memory during compression, split
// the whole data into multi-part reduce the memory usage during
// compression, because just a few part of <<store>> data need
// to fit into memory at a time for compression.
//
// The <<index>> is single-part, because index is much smaller
// than the whole <<store>> data.
//
class TERARK_DB_DLL ReadonlySegment : public ReadableSegment {
public:
	ReadonlySegment();
	~ReadonlySegment();

	ReadonlySegment* getReadonlySegment() const override;

	llong dataInflateSize() const override;
	llong dataStorageSize() const override;
	llong totalStorageSize() const override;

	void getValueAppend(llong id, valvec<byte>* val, DbContext*) const override;

	StoreIterator* createStoreIterForward(DbContext*) const override;
	StoreIterator* createStoreIterBackward(DbContext*) const override;

	void convFrom(class DbTable*, size_t segIdx);
	void purgeDeletedRecords(class DbTable*, size_t segIdx);

	void getValueByLogicId(size_t id, valvec<byte>* val, DbContext*) const;
	void getValueByPhysicId(size_t id, valvec<byte>* val, DbContext*) const;

	void indexSearchExactAppend(size_t mySegIdx, size_t indexId,
								fstring key, valvec<llong>* recIdvec,
								DbContext*) const override;

	void selectColumns(llong recId, const size_t* colsId, size_t colsNum,
					   valvec<byte>* colsData, DbContext*) const override;
	void selectOneColumn(llong recId, size_t columnId,
						 valvec<byte>* colsData, DbContext*) const override;

	void selectColgroups(llong id, const size_t* cgIdvec, size_t cgIdvecSize,
						 valvec<byte>* cgDataVec, DbContext*) const override;

	void load(PathRef segDir) override;
	void save(PathRef segDir) const override;

protected:
	virtual ReadableIndex*
			buildIndex(const Schema&, SortableStrVec& indexData)
			const = 0;

	virtual ReadableStore*
			buildStore(const Schema&, SortableStrVec& storeData)
			const = 0;

	virtual ReadableStore*
			buildDictZipStore(const Schema&, PathRef dir, StoreIterator& inputIter,
							  const bm_uint_t* isDel, const febitvec* isPurged)
			const;

	void completeAndReload(class DbTable*, size_t segIdx,
						   class ReadableSegment* input);
	void syncUpdateRecordNoLock(size_t dstBaseId, size_t logicId,
								const ReadableSegment* input);

	ReadableIndexPtr purgeIndex(size_t indexId, ReadonlySegment* input, DbContext* ctx);
	ReadableStorePtr purgeColgroup(size_t colgroupId, ReadonlySegment* input, DbContext* ctx, PathRef tmpSegDir);

	void loadRecordStore(PathRef segDir) override;
	void saveRecordStore(PathRef segDir) const override;

	void closeFiles();
	void removePurgeBitsForCompactIdspace(PathRef segDir);
	void savePurgeBits(PathRef segDir) const;

protected:
	friend class DbTable;
	friend class TableIndexIter;
	class MyStoreIterForward;  friend class MyStoreIterForward;
	class MyStoreIterBackward; friend class MyStoreIterBackward;
	llong  m_dataInflateSize;
	llong  m_dataMemSize;
	llong  m_totalStorageSize;
};
typedef boost::intrusive_ptr<ReadonlySegment> ReadonlySegmentPtr;

class DbTransaction : boost::noncopyable {
public:
	enum Status { started, committed, rollbacked } m_status;
	///@{ just for BatchWriter
	valvec<llong>   m_removeOnCommit;
	valvec<llong>   m_removeOnRollback; // the subId, must be in m_wrSeg
	// @}
	virtual void indexSearch(size_t indexId, fstring key, valvec<llong>* recIdvec) = 0;
	virtual void indexRemove(size_t indexId, fstring key, llong recId) = 0;
	virtual bool indexInsert(size_t indexId, fstring key, llong recId) = 0;
	virtual void indexUpsert(size_t indexId, fstring key, llong recId) = 0;
	virtual void storeRemove(llong recId) = 0;
	virtual void storeUpsert(llong recId, fstring row) = 0;
	virtual void storeGetRow(llong recId, valvec<byte>* row) = 0;
	void startTransaction();
	bool commit();
	void rollback();
	virtual const std::string& strError() const = 0;
	virtual ~DbTransaction();
	DbTransaction();

	virtual void do_startTransaction() = 0;
	virtual bool do_commit() = 0;
	virtual void do_rollback() = 0;
};
class TransactionGuard : boost::noncopyable {
protected:
	DbTransaction* m_txn;
public:
	explicit TransactionGuard(DbTransaction* txn) {
		assert(NULL != txn);
		txn->startTransaction();
		m_txn = txn;
	}
	~TransactionGuard() {
		assert(DbTransaction::committed == m_txn->m_status ||
			   DbTransaction::rollbacked == m_txn->m_status);
	}
	DbTransaction* getTxn() const { return m_txn; }
	void indexSearch(size_t indexId, fstring key, valvec<llong>* recIdvec) {
		m_txn->indexSearch(indexId, key, recIdvec);
	}
	void indexRemove(size_t indexId, fstring key, llong recId) {
		m_txn->indexRemove(indexId, key, recId);
	}
	bool indexInsert(size_t indexId, fstring key, llong recId) {
		return m_txn->indexInsert(indexId, key, recId);
	}
	void indexUpsert(size_t indexId, fstring key, llong recId) {
		m_txn->indexUpsert(indexId, key, recId);
	}
	void storeRemove(llong recId) {
		m_txn->storeRemove(recId);
	}
	void storeUpsert(llong recId, fstring row) {
		m_txn->storeUpsert(recId, row);
	}
	void storeGetRow(llong recId, valvec<byte>* row) {
		m_txn->storeGetRow(recId, row);
	}
	bool commit() {
		assert(DbTransaction::started == m_txn->m_status);
		return m_txn->commit();
	}
	void rollback() {
		assert(DbTransaction::started == m_txn->m_status);
		m_txn->rollback();
	}
	const std::string& strError() const { return m_txn->strError(); }
	const char* szError() const { return m_txn->strError().c_str(); }
};
class DefaultRollbackTransaction : public TransactionGuard {
public:
	explicit
	DefaultRollbackTransaction(DbTransaction* txn) : TransactionGuard(txn) {}
	~DefaultRollbackTransaction() {
		if (DbTransaction::started == m_txn->m_status) {
			m_txn->rollback();
		}
	}
};

// Concrete WritableSegment should not implement this class,
// should implement PlainWritableSegment or SmartWritableSegment
class TERARK_DB_DLL WritableSegment : public ReadableSegment, public WritableStore {
	class MyStoreIter;
public:
	virtual DbTransaction* createTransaction() = 0;

	WritableSegment();
	~WritableSegment();

	void pushIsDel(bool val);
	void popIsDel();

	WritableSegment* getWritableSegment() const override;

	llong totalStorageSize() const override;
	llong dataStorageSize() const override;
	llong dataInflateSize() const override;

	StoreIterator* createStoreIterForward(DbContext*) const override;
	StoreIterator* createStoreIterBackward(DbContext*) const override;

	AppendableStore* getAppendableStore() override;
	UpdatableStore* getUpdatableStore() override;
	WritableStore* getWritableStore() override;

	// Index can use different implementation for different
	// index schema and index content features
	virtual ReadableIndex* createIndex(const Schema&, PathRef path) const = 0;

	virtual void getValueAppend(llong recId, valvec<byte>* val, DbContext*) const override;

	virtual llong append(fstring row, DbContext*) override;
	virtual void update(llong id, fstring row, DbContext*) override;
	virtual void remove(llong id, DbContext* ctx) override;
	virtual void shrinkToFit() override;

	void indexSearchExactAppend(size_t mySegIdx, size_t indexId,
								fstring key, valvec<llong>* recIdvec,
								DbContext*) const override;

	void getCombineAppend(llong recId, valvec<byte>* val, valvec<byte>& wrtBuf, ColumnVec& cols1, ColumnVec& cols2) const;

	void selectColumns(llong recId, const size_t* colsId, size_t colsNum,
					   valvec<byte>* colsData, DbContext*) const override;
	void selectOneColumn(llong recId, size_t columnId,
						 valvec<byte>* colsData, DbContext*) const override;

	void selectColumnsByWhole(llong recId,
							  const size_t* colsId, size_t colsNum,
							  valvec<byte>* colsData, DbContext*) const;
	void selectColumnsCombine(llong recId,
							  const size_t* colsId, size_t colsNum,
							  valvec<byte>* colsData, DbContext*) const;

	void selectColgroups(llong id, const size_t* cgIdvec, size_t cgIdvecSize,
						 valvec<byte>* cgDataVec, DbContext*) const override;

	void flushSegment();

	void loadRecordStore(PathRef segDir) override;
	void saveRecordStore(PathRef segDir) const override;

	void getWrtStoreData(llong subId, valvec<byte>* buf, DbContext* ctx) const;

	ReadableStorePtr  m_wrtStore;
	valvec<uint32_t>  m_deletedWrIdSet;
};
typedef boost::intrusive_ptr<WritableSegment> WritableSegmentPtr;

class TERARK_DB_DLL PlainWritableSegment : public WritableSegment {
public:
protected:
};
typedef boost::intrusive_ptr<PlainWritableSegment> PlainWritableSegmentPtr;

// Every index is a WritableIndexStore
// But the <<store>> is not multi-part(such as ReadonlySegment)
class TERARK_DB_DLL SmartWritableSegment : public WritableSegment {
public:
protected:
	~SmartWritableSegment();

	void getValueAppend(llong id, valvec<byte>* val, DbContext*)
	const override final;

	StoreIterator* createStoreIterForward(DbContext*) const override;
	StoreIterator* createStoreIterBackward(DbContext*) const override;

	void loadRecordStore(PathRef segDir) override;
	void saveRecordStore(PathRef segDir) const override;

	llong dataStorageSize() const override;
	llong totalStorageSize() const override;

	class MyStoreIterForward;  friend class MyStoreIterForward;
	class MyStoreIterBackward; friend class MyStoreIterBackward;
};
typedef boost::intrusive_ptr<SmartWritableSegment> SmartWritableSegmentPtr;

} } // namespace terark::db

#endif // __terark_db_segment_hpp__
<commit_msg>Fix a minor bug<commit_after>#ifndef __terark_db_segment_hpp__
#define __terark_db_segment_hpp__

#include "db_index.hpp"
#include "db_store.hpp"
#include <terark/bitmap.hpp>
#include <terark/rank_select.hpp>
#include <tbb/spin_rw_mutex.h>
#include <tbb/tbb_thread.h>

namespace terark {
	class SortableStrVec;
}

namespace terark { namespace db {

typedef tbb::spin_rw_mutex        SpinRwMutex;
typedef SpinRwMutex::scoped_lock  SpinRwLock;

// This ReadableStore is used for return full-row
// A full-row is of one table, the table has multiple indices
class TERARK_DB_DLL ReadableSegment : public ReadableStore {
public:
	ReadableSegment();
	~ReadableSegment();
	virtual class ReadonlySegment* getReadonlySegment() const;
	virtual class WritableSegment* getWritableSegment() const;
	virtual llong totalStorageSize() const = 0;
	virtual llong numDataRows() const override final;

	// Index can use different implementation for different
	// index schema and index content features
	virtual ReadableIndex* openIndex(const Schema&, PathRef path) const = 0;

	///@ if segDir==m_segDir, it is a flush
	virtual void loadRecordStore(PathRef segDir) = 0;
	virtual void saveRecordStore(PathRef segDir) const = 0;

	inline  void indexSearchExact(size_t mySegIdx, size_t indexId,
								  fstring key, valvec<llong>* recIdvec,
								  DbContext* ctx) const {
		recIdvec->erase_all();
		indexSearchExactAppend(mySegIdx, indexId, key, recIdvec, ctx);
	}

	virtual void indexSearchExactAppend(size_t mySegIdx, size_t indexId,
										fstring key, valvec<llong>* recIdvec,
										DbContext*) const = 0;

	virtual void selectColumns(llong recId, const size_t* colsId, size_t colsNum,
							   valvec<byte>* colsData, DbContext*) const = 0;
	virtual void selectOneColumn(llong recId, size_t columnId,
								 valvec<byte>* colsData, DbContext*) const = 0;

	virtual void selectColgroups(llong id, const size_t* cgIdvec, size_t cgIdvecSize,
								 valvec<byte>* cgDataVec, DbContext*) const = 0;

	void openIndices(PathRef dir);
	void saveIndices(PathRef dir) const;
	llong totalIndexSize() const;

	void saveIsDel(PathRef segDir) const;
	void loadIsDel(PathRef segDir);
	byte*loadIsDel_aux(PathRef segDir, febitvec& isDel) const;
	void closeIsDel();

	void deleteSegment();

	void load(PathRef segDir) override;
	void save(PathRef segDir) const override;

	size_t getPhysicRows() const;
	size_t getPhysicId(size_t logicId) const;
	size_t getLogicId(size_t physicId) const;

	void addtoUpdateList(size_t logicId);

	bool locked_testIsDel(size_t logicId) const {
		SpinRwLock wsLock(m_segMutex, false);
		return m_isDel[logicId];
	}

	SchemaConfigPtr         m_schema;
	valvec<ReadableIndexPtr> m_indices; // parallel with m_indexSchemaSet
	valvec<ReadableStorePtr> m_colgroups; // indices + pure_colgroups
	size_t      m_delcnt;
	febitvec    m_isDel;
	byte*       m_isDelMmap = nullptr;
	rank_select_se m_isPurged; // just for ReadonlySegment
	byte*          m_isPurgedMmap;
	boost::filesystem::path m_segDir;
	mutable SpinRwMutex m_segMutex;
	valvec<uint32_t> m_updateList; // including deletions
	febitvec    m_updateBits; // if m_updateList is too large, use updateBits
	ReadableStorePtr m_deletionTime; // for snapshot, an uint64 array
	bool        m_tobeDel;
	bool        m_isDirty;
	bool        m_isFreezed;
	bool        m_hasLockFreePointSearch;
	bool        m_bookUpdates;
	bool        m_withPurgeBits;  // just for ReadonlySegment
};
typedef boost::intrusive_ptr<ReadableSegment> ReadableSegmentPtr;

// Every index is a ReadableIndexStore
//
// The <<store>> is multi-part, because the <<store>> may be
// very large, and the compressible database algo need to fit
// all uncompressed data into memory during compression, split
// the whole data into multi-part reduce the memory usage during
// compression, because just a few part of <<store>> data need
// to fit into memory at a time for compression.
//
// The <<index>> is single-part, because index is much smaller
// than the whole <<store>> data.
//
class TERARK_DB_DLL ReadonlySegment : public ReadableSegment {
public:
	ReadonlySegment();
	~ReadonlySegment();

	ReadonlySegment* getReadonlySegment() const override;

	llong dataInflateSize() const override;
	llong dataStorageSize() const override;
	llong totalStorageSize() const override;

	void getValueAppend(llong id, valvec<byte>* val, DbContext*) const override;

	StoreIterator* createStoreIterForward(DbContext*) const override;
	StoreIterator* createStoreIterBackward(DbContext*) const override;

	void convFrom(class DbTable*, size_t segIdx);
	void purgeDeletedRecords(class DbTable*, size_t segIdx);

	void getValueByLogicId(size_t id, valvec<byte>* val, DbContext*) const;
	void getValueByPhysicId(size_t id, valvec<byte>* val, DbContext*) const;

	void indexSearchExactAppend(size_t mySegIdx, size_t indexId,
								fstring key, valvec<llong>* recIdvec,
								DbContext*) const override;

	void selectColumns(llong recId, const size_t* colsId, size_t colsNum,
					   valvec<byte>* colsData, DbContext*) const override;
	void selectOneColumn(llong recId, size_t columnId,
						 valvec<byte>* colsData, DbContext*) const override;

	void selectColgroups(llong id, const size_t* cgIdvec, size_t cgIdvecSize,
						 valvec<byte>* cgDataVec, DbContext*) const override;

	void load(PathRef segDir) override;
	void save(PathRef segDir) const override;

protected:
	virtual ReadableIndex* openIndex(const Schema&, PathRef path) const override = 0;

	virtual ReadableIndex*
			buildIndex(const Schema&, SortableStrVec& indexData)
			const = 0;

	virtual ReadableStore*
			buildStore(const Schema&, SortableStrVec& storeData)
			const = 0;

	virtual ReadableStore*
			buildDictZipStore(const Schema&, PathRef dir, StoreIterator& inputIter,
							  const bm_uint_t* isDel, const febitvec* isPurged)
			const;

	void completeAndReload(class DbTable*, size_t segIdx,
						   class ReadableSegment* input);
	void syncUpdateRecordNoLock(size_t dstBaseId, size_t logicId,
								const ReadableSegment* input);

	ReadableIndexPtr purgeIndex(size_t indexId, ReadonlySegment* input, DbContext* ctx);
	ReadableStorePtr purgeColgroup(size_t colgroupId, ReadonlySegment* input, DbContext* ctx, PathRef tmpSegDir);

	void loadRecordStore(PathRef segDir) override;
	void saveRecordStore(PathRef segDir) const override;

	void closeFiles();
	void removePurgeBitsForCompactIdspace(PathRef segDir);
	void savePurgeBits(PathRef segDir) const;

protected:
	friend class DbTable;
	friend class TableIndexIter;
	class MyStoreIterForward;  friend class MyStoreIterForward;
	class MyStoreIterBackward; friend class MyStoreIterBackward;
	llong  m_dataInflateSize;
	llong  m_dataMemSize;
	llong  m_totalStorageSize;
};
typedef boost::intrusive_ptr<ReadonlySegment> ReadonlySegmentPtr;

class DbTransaction : boost::noncopyable {
public:
	enum Status { started, committed, rollbacked } m_status;
	///@{ just for BatchWriter
	valvec<llong>   m_removeOnCommit;
	valvec<llong>   m_removeOnRollback; // the subId, must be in m_wrSeg
	// @}
	virtual void indexSearch(size_t indexId, fstring key, valvec<llong>* recIdvec) = 0;
	virtual void indexRemove(size_t indexId, fstring key, llong recId) = 0;
	virtual bool indexInsert(size_t indexId, fstring key, llong recId) = 0;
	virtual void indexUpsert(size_t indexId, fstring key, llong recId) = 0;
	virtual void storeRemove(llong recId) = 0;
	virtual void storeUpsert(llong recId, fstring row) = 0;
	virtual void storeGetRow(llong recId, valvec<byte>* row) = 0;
	void startTransaction();
	bool commit();
	void rollback();
	virtual const std::string& strError() const = 0;
	virtual ~DbTransaction();
	DbTransaction();

	virtual void do_startTransaction() = 0;
	virtual bool do_commit() = 0;
	virtual void do_rollback() = 0;
};
class TransactionGuard : boost::noncopyable {
protected:
	DbTransaction* m_txn;
public:
	explicit TransactionGuard(DbTransaction* txn) {
		assert(NULL != txn);
		txn->startTransaction();
		m_txn = txn;
	}
	~TransactionGuard() {
		assert(DbTransaction::committed == m_txn->m_status ||
			   DbTransaction::rollbacked == m_txn->m_status);
	}
	DbTransaction* getTxn() const { return m_txn; }
	void indexSearch(size_t indexId, fstring key, valvec<llong>* recIdvec) {
		m_txn->indexSearch(indexId, key, recIdvec);
	}
	void indexRemove(size_t indexId, fstring key, llong recId) {
		m_txn->indexRemove(indexId, key, recId);
	}
	bool indexInsert(size_t indexId, fstring key, llong recId) {
		return m_txn->indexInsert(indexId, key, recId);
	}
	void indexUpsert(size_t indexId, fstring key, llong recId) {
		m_txn->indexUpsert(indexId, key, recId);
	}
	void storeRemove(llong recId) {
		m_txn->storeRemove(recId);
	}
	void storeUpsert(llong recId, fstring row) {
		m_txn->storeUpsert(recId, row);
	}
	void storeGetRow(llong recId, valvec<byte>* row) {
		m_txn->storeGetRow(recId, row);
	}
	bool commit() {
		assert(DbTransaction::started == m_txn->m_status);
		return m_txn->commit();
	}
	void rollback() {
		assert(DbTransaction::started == m_txn->m_status);
		m_txn->rollback();
	}
	const std::string& strError() const { return m_txn->strError(); }
	const char* szError() const { return m_txn->strError().c_str(); }
};
class DefaultRollbackTransaction : public TransactionGuard {
public:
	explicit
	DefaultRollbackTransaction(DbTransaction* txn) : TransactionGuard(txn) {}
	~DefaultRollbackTransaction() {
		if (DbTransaction::started == m_txn->m_status) {
			m_txn->rollback();
		}
	}
};

// Concrete WritableSegment should not implement this class,
// should implement PlainWritableSegment or SmartWritableSegment
class TERARK_DB_DLL WritableSegment : public ReadableSegment, public WritableStore {
	class MyStoreIter;
public:
	virtual DbTransaction* createTransaction() = 0;

	WritableSegment();
	~WritableSegment();

	void pushIsDel(bool val);
	void popIsDel();

	WritableSegment* getWritableSegment() const override;

	llong totalStorageSize() const override;
	llong dataStorageSize() const override;
	llong dataInflateSize() const override;

	StoreIterator* createStoreIterForward(DbContext*) const override;
	StoreIterator* createStoreIterBackward(DbContext*) const override;

	AppendableStore* getAppendableStore() override;
	UpdatableStore* getUpdatableStore() override;
	WritableStore* getWritableStore() override;

	// Index can use different implementation for different
	// index schema and index content features
	virtual ReadableIndex* createIndex(const Schema&, PathRef path) const = 0;

	virtual void getValueAppend(llong recId, valvec<byte>* val, DbContext*) const override;

	virtual llong append(fstring row, DbContext*) override;
	virtual void update(llong id, fstring row, DbContext*) override;
	virtual void remove(llong id, DbContext* ctx) override;
	virtual void shrinkToFit() override;

	void indexSearchExactAppend(size_t mySegIdx, size_t indexId,
								fstring key, valvec<llong>* recIdvec,
								DbContext*) const override;

	void getCombineAppend(llong recId, valvec<byte>* val, valvec<byte>& wrtBuf, ColumnVec& cols1, ColumnVec& cols2) const;

	void selectColumns(llong recId, const size_t* colsId, size_t colsNum,
					   valvec<byte>* colsData, DbContext*) const override;
	void selectOneColumn(llong recId, size_t columnId,
						 valvec<byte>* colsData, DbContext*) const override;

	void selectColumnsByWhole(llong recId,
							  const size_t* colsId, size_t colsNum,
							  valvec<byte>* colsData, DbContext*) const;
	void selectColumnsCombine(llong recId,
							  const size_t* colsId, size_t colsNum,
							  valvec<byte>* colsData, DbContext*) const;

	void selectColgroups(llong id, const size_t* cgIdvec, size_t cgIdvecSize,
						 valvec<byte>* cgDataVec, DbContext*) const override;

	void flushSegment();

	void loadRecordStore(PathRef segDir) override;
	void saveRecordStore(PathRef segDir) const override;

	void getWrtStoreData(llong subId, valvec<byte>* buf, DbContext* ctx) const;

	ReadableStorePtr  m_wrtStore;
	valvec<uint32_t>  m_deletedWrIdSet;
};
typedef boost::intrusive_ptr<WritableSegment> WritableSegmentPtr;

class TERARK_DB_DLL PlainWritableSegment : public WritableSegment {
public:
protected:
};
typedef boost::intrusive_ptr<PlainWritableSegment> PlainWritableSegmentPtr;

// Every index is a WritableIndexStore
// But the <<store>> is not multi-part(such as ReadonlySegment)
class TERARK_DB_DLL SmartWritableSegment : public WritableSegment {
public:
protected:
	~SmartWritableSegment();

	void getValueAppend(llong id, valvec<byte>* val, DbContext*)
	const override final;

	StoreIterator* createStoreIterForward(DbContext*) const override;
	StoreIterator* createStoreIterBackward(DbContext*) const override;

	void loadRecordStore(PathRef segDir) override;
	void saveRecordStore(PathRef segDir) const override;

	llong dataStorageSize() const override;
	llong totalStorageSize() const override;

	class MyStoreIterForward;  friend class MyStoreIterForward;
	class MyStoreIterBackward; friend class MyStoreIterBackward;
};
typedef boost::intrusive_ptr<SmartWritableSegment> SmartWritableSegmentPtr;

} } // namespace terark::db

#endif // __terark_db_segment_hpp__
<|endoftext|>


<commit_before>// The Art of C++ / PostgreSQL
// Copyright (c) 2016 Daniel Frey

#include "../macros.hpp"

#include <tao/utility/getenv.hpp>
#include <tao/postgres/connection.hpp>
#include <tao/postgres/result_traits_optional.hpp>
#include <tao/postgres/result_traits_pair.hpp>
#include <tao/postgres/result_traits_tuple.hpp>

int main()
{
  const auto connection = tao::postgres::connection::create( tao::utility::getenv( "TEST_DATABASE", "dbname=template1" ) );

  TEST_ASSERT( connection->execute( "SELECT 42" ).as< int >() == 42 );
  TEST_ASSERT( connection->execute( "SELECT 1764" ).optional< int >() == 1764 );
  TEST_ASSERT( !connection->execute( "SELECT 64 WHERE FALSE" ).optional< int >() );
  TEST_ASSERT( !connection->execute( "SELECT NULL" ).as< tao::optional< int > >() );

  TEST_ASSERT( connection->execute( "SELECT $1::INTEGER", tao::optional< int >( 42 ) ).as< tao::optional< int > >() == 42 );
  TEST_ASSERT( !connection->execute( "SELECT $1::INTEGER", tao::optional< int >() ).as< tao::optional< int > >() );

  TEST_ASSERT( connection->execute( "SELECT 1, 2" ).pair< int, int >() == std::pair< int, int >( 1, 2 ) );
  TEST_ASSERT( connection->execute( "SELECT 1, 2, 3, 4" ).tuple< int, int, int, int >() == std::tuple< int, int, int, int >( 1, 2, 3, 4 ) );

  TEST_ASSERT( connection->execute( "SELECT 42" ).columns() == 1 );
  TEST_ASSERT( connection->execute( "SELECT 42" ).vector< int >().size() == 1 );
  TEST_ASSERT( connection->execute( "SELECT 42" ).list< int >().size() == 1 );
  TEST_ASSERT( connection->execute( "SELECT 42" ).set< int >().size() == 1 );
  TEST_ASSERT( connection->execute( "SELECT 42" ).multiset< int >().size() == 1 );
  TEST_ASSERT( connection->execute( "SELECT 42" ).unordered_set< int >().size() == 1 );
  TEST_ASSERT( connection->execute( "SELECT 42" ).unordered_multiset< int >().size() == 1 );

  TEST_THROWS( connection->execute( "SELECT 42" ).as< bool >() );

  TEST_ASSERT( connection->execute( "SELECT 1, 2" ).columns() == 2 );
  TEST_ASSERT( connection->execute( "SELECT 1, 2" ).map< int, int >().size() == 1 );
  TEST_ASSERT( connection->execute( "SELECT 1, 2" ).multimap< int, int >().size() == 1 );
  TEST_ASSERT( connection->execute( "SELECT 1, 2" ).unordered_map< int, int >().size() == 1 );
  TEST_ASSERT( connection->execute( "SELECT 1, 2" ).unordered_multimap< int, int >().size() == 1 );

  TEST_ASSERT( connection->execute( "SELECT 1 UNION ALL SELECT 2" ).list< int >().size() == 2 );
  TEST_ASSERT( connection->execute( "SELECT 1, 2, 3, 4 UNION ALL SELECT 5, 6, 7, 8" ).list< std::tuple< int, int, int, int > >().size() == 2 );
  TEST_ASSERT( connection->execute( "SELECT 1, 2 UNION ALL SELECT 2, 5 UNION ALL SELECT 3, 42" ).map< int, int >().size() == 3 );

  const auto result = connection->execute( "SELECT 1 AS a, 2 AS B, 3 AS \"C\"");

  TEST_ASSERT( result.has_rows_affected() );
  TEST_ASSERT( !result.empty() );
  TEST_ASSERT( result.size() == 1 );
  TEST_ASSERT( result.columns() == 3 );
  TEST_ASSERT( result.at( 0 ).columns() == 3 );
  TEST_THROWS( result.at( 1 ) );

  TEST_ASSERT( result.name( 0 ) == "a" );
  TEST_ASSERT( result.name( 1 ) == "b" );
  TEST_ASSERT( result.name( 2 ) == "C" );

  TEST_ASSERT( result.index( "a" ) == 0 );
  TEST_ASSERT( result.index( "A" ) == 0 );
  TEST_ASSERT( result.index( "\"a\"" ) == 0 );
  TEST_THROWS( result.index( "\"A\"" ) );

  TEST_ASSERT( result.index( "b" ) == 1 );
  TEST_ASSERT( result.index( "B" ) == 1 );
  TEST_ASSERT( result.index( "\"b\"" ) == 1 );
  TEST_THROWS( result.index( "\"B\"" ) );

  TEST_THROWS( result.index( "c" ) );
  TEST_THROWS( result.index( "C" ) );
  TEST_THROWS( result.index( "\"c\"" ) );
  TEST_ASSERT( result.index( "\"C\"" ) == 2 );

  TEST_THROWS( connection->execute( "SELECT 42 WHERE FALSE" ).as< int >() );
  TEST_THROWS( connection->execute( "SELECT 1 UNION ALL SELECT 2" ).as< int >() );

  TEST_THROWS( connection->execute( "SELECT 42" ).pair< int, int >() );
  TEST_THROWS( connection->execute( "SELECT 1, 2" ).as< int >() );

  int count = 0;
  for( const auto& row : connection->execute( "SELECT 1 UNION ALL SELECT 2" ) ) {
    TEST_ASSERT( row.as< int >() == ++count );
  }
  TEST_ASSERT( count == 2 );
}
<commit_msg>Test result traits<commit_after>// The Art of C++ / PostgreSQL
// Copyright (c) 2016 Daniel Frey

#include "../macros.hpp"

#include <tao/utility/getenv.hpp>
#include <tao/postgres/connection.hpp>
#include <tao/postgres/result_traits_optional.hpp>
#include <tao/postgres/result_traits_pair.hpp>
#include <tao/postgres/result_traits_tuple.hpp>

int main()
{
  const auto connection = tao::postgres::connection::create( tao::utility::getenv( "TEST_DATABASE", "dbname=template1" ) );

  TEST_ASSERT( connection->execute( "SELECT 42" ).as< int >() == 42 );
  TEST_ASSERT( connection->execute( "SELECT 1764" ).optional< int >() == 1764 );
  TEST_ASSERT( !connection->execute( "SELECT 64 WHERE FALSE" ).optional< int >() );
  TEST_ASSERT( !connection->execute( "SELECT NULL" ).as< tao::optional< int > >() );

  TEST_ASSERT( connection->execute( "SELECT $1::INTEGER", tao::optional< int >( 42 ) ).as< tao::optional< int > >() == 42 );
  TEST_ASSERT( !connection->execute( "SELECT $1::INTEGER", tao::optional< int >() ).as< tao::optional< int > >() );

  TEST_ASSERT( connection->execute( "SELECT 1, 2" ).pair< int, int >() == std::pair< int, int >( 1, 2 ) );
  TEST_ASSERT( connection->execute( "SELECT 1, 2, 3, 4" ).tuple< int, int, int, int >() == std::tuple< int, int, int, int >( 1, 2, 3, 4 ) );

  TEST_ASSERT( connection->execute( "SELECT 42" ).columns() == 1 );
  TEST_ASSERT( connection->execute( "SELECT 42" ).vector< int >().size() == 1 );
  TEST_ASSERT( connection->execute( "SELECT 42" ).list< int >().size() == 1 );
  TEST_ASSERT( connection->execute( "SELECT 42" ).set< int >().size() == 1 );
  TEST_ASSERT( connection->execute( "SELECT 42" ).multiset< int >().size() == 1 );
  TEST_ASSERT( connection->execute( "SELECT 42" ).unordered_set< int >().size() == 1 );
  TEST_ASSERT( connection->execute( "SELECT 42" ).unordered_multiset< int >().size() == 1 );

  TEST_THROWS( connection->execute( "SELECT 42" ).as< bool >() );

  TEST_ASSERT( connection->execute( "SELECT 1, 2" ).columns() == 2 );
  TEST_ASSERT( connection->execute( "SELECT 1, 2" ).map< int, int >().size() == 1 );
  TEST_ASSERT( connection->execute( "SELECT 1, 2" ).multimap< int, int >().size() == 1 );
  TEST_ASSERT( connection->execute( "SELECT 1, 2" ).unordered_map< int, int >().size() == 1 );
  TEST_ASSERT( connection->execute( "SELECT 1, 2" ).unordered_multimap< int, int >().size() == 1 );

  TEST_ASSERT( connection->execute( "SELECT 1 UNION ALL SELECT 2" ).list< int >().size() == 2 );
  TEST_ASSERT( connection->execute( "SELECT 1, 2, 3, 4 UNION ALL SELECT 5, 6, 7, 8" ).list< std::tuple< int, int, int, int > >().size() == 2 );
  TEST_ASSERT( connection->execute( "SELECT 1, 2 UNION ALL SELECT 2, 5 UNION ALL SELECT 3, 42" ).map< int, int >().size() == 3 );

  const auto result = connection->execute( "SELECT 1 AS a, 2 AS B, 3 AS \"C\"");

  TEST_ASSERT( result.has_rows_affected() );
  TEST_ASSERT( !result.empty() );
  TEST_ASSERT( result.size() == 1 );
  TEST_ASSERT( result.columns() == 3 );
  TEST_ASSERT( result.at( 0 ).columns() == 3 );
  TEST_THROWS( result.at( 1 ) );

  TEST_ASSERT( result.name( 0 ) == "a" );
  TEST_ASSERT( result.name( 1 ) == "b" );
  TEST_ASSERT( result.name( 2 ) == "C" );

  TEST_ASSERT( result.index( "a" ) == 0 );
  TEST_ASSERT( result.index( "A" ) == 0 );
  TEST_ASSERT( result.index( "\"a\"" ) == 0 );
  TEST_THROWS( result.index( "\"A\"" ) );

  TEST_ASSERT( result.index( "b" ) == 1 );
  TEST_ASSERT( result.index( "B" ) == 1 );
  TEST_ASSERT( result.index( "\"b\"" ) == 1 );
  TEST_THROWS( result.index( "\"B\"" ) );

  TEST_THROWS( result.index( "c" ) );
  TEST_THROWS( result.index( "C" ) );
  TEST_THROWS( result.index( "\"c\"" ) );
  TEST_ASSERT( result.index( "\"C\"" ) == 2 );

  TEST_THROWS( connection->execute( "SELECT 42 WHERE FALSE" ).as< int >() );
  TEST_THROWS( connection->execute( "SELECT 1 UNION ALL SELECT 2" ).as< int >() );

  TEST_THROWS( connection->execute( "SELECT 42" ).pair< int, int >() );
  TEST_THROWS( connection->execute( "SELECT 1, 2" ).as< int >() );

  TEST_THROWS( connection->execute( "SELECT ''" ).as< char >() );
  TEST_THROWS( connection->execute( "SELECT 'Hallo'" ).as< char >() );

  TEST_THROWS( connection->execute( "SELECT -129" ).as< signed char >() );
  TEST_ASSERT( connection->execute( "SELECT -128" ).as< signed char >() == -128 );
  TEST_ASSERT( connection->execute( "SELECT 127" ).as< signed char >() == 127 );
  TEST_THROWS( connection->execute( "SELECT 128" ).as< signed char >() );

  TEST_ASSERT( connection->execute( "SELECT 255" ).as< unsigned char >() == 255 );
  TEST_THROWS( connection->execute( "SELECT 256" ).as< unsigned char >() );

  TEST_THROWS( connection->execute( "SELECT -32769" ).as< short >() );
  TEST_ASSERT( connection->execute( "SELECT -32768" ).as< short >() == -32768 );
  TEST_ASSERT( connection->execute( "SELECT 32767" ).as< short >() == 32767 );
  TEST_THROWS( connection->execute( "SELECT 32768" ).as< short >() );

  TEST_ASSERT( connection->execute( "SELECT 65535" ).as< unsigned short >() == 65535 );
  TEST_THROWS( connection->execute( "SELECT 65536" ).as< unsigned short >() );

  TEST_THROWS( connection->execute( "SELECT -2147483649" ).as< int >() );
  TEST_ASSERT( connection->execute( "SELECT -2147483648" ).as< int >() == -2147483648 );
  TEST_ASSERT( connection->execute( "SELECT 2147483647" ).as< int >() == 2147483647 );
  TEST_THROWS( connection->execute( "SELECT 2147483648" ).as< int >() );

  TEST_ASSERT( connection->execute( "SELECT 4294967295" ).as< unsigned >() == 4294967295 );
  TEST_THROWS( connection->execute( "SELECT 4294967296" ).as< unsigned >() );

  int count = 0;
  for( const auto& row : connection->execute( "SELECT 1 UNION ALL SELECT 2" ) ) {
    TEST_ASSERT( row.as< int >() == ++count );
  }
  TEST_ASSERT( count == 2 );
}
<|endoftext|>


<commit_before>// Copyright (c) 2012-2020 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <random.h>
#include <scheduler.h>
#include <util/time.h>

#include <boost/test/unit_test.hpp>
#include <boost/thread.hpp>

BOOST_AUTO_TEST_SUITE(scheduler_tests)

static void microTask(CScheduler& s, boost::mutex& mutex, int& counter, int delta, std::chrono::system_clock::time_point rescheduleTime)
{
    {
        boost::unique_lock<boost::mutex> lock(mutex);
        counter += delta;
    }
    std::chrono::system_clock::time_point noTime = std::chrono::system_clock::time_point::min();
    if (rescheduleTime != noTime) {
        CScheduler::Function f = std::bind(&microTask, std::ref(s), std::ref(mutex), std::ref(counter), -delta + 1, noTime);
        s.schedule(f, rescheduleTime);
    }
}

BOOST_AUTO_TEST_CASE(manythreads)
{
    // Stress test: hundreds of microsecond-scheduled tasks,
    // serviced by 10 threads.
    //
    // So... ten shared counters, which if all the tasks execute
    // properly will sum to the number of tasks done.
    // Each task adds or subtracts a random amount from one of the
    // counters, and then schedules another task 0-1000
    // microseconds in the future to subtract or add from
    // the counter -random_amount+1, so in the end the shared
    // counters should sum to the number of initial tasks performed.
    CScheduler microTasks;

    boost::mutex counterMutex[10];
    int counter[10] = { 0 };
    FastRandomContext rng{/* fDeterministic */ true};
    auto zeroToNine = [](FastRandomContext& rc) -> int { return rc.randrange(10); }; // [0, 9]
    auto randomMsec = [](FastRandomContext& rc) -> int { return -11 + (int)rc.randrange(1012); }; // [-11, 1000]
    auto randomDelta = [](FastRandomContext& rc) -> int { return -1000 + (int)rc.randrange(2001); }; // [-1000, 1000]

    std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
    std::chrono::system_clock::time_point now = start;
    std::chrono::system_clock::time_point first, last;
    size_t nTasks = microTasks.getQueueInfo(first, last);
    BOOST_CHECK(nTasks == 0);

    for (int i = 0; i < 100; ++i) {
        std::chrono::system_clock::time_point t = now + std::chrono::microseconds(randomMsec(rng));
        std::chrono::system_clock::time_point tReschedule = now + std::chrono::microseconds(500 + randomMsec(rng));
        int whichCounter = zeroToNine(rng);
        CScheduler::Function f = std::bind(&microTask, std::ref(microTasks),
                                             std::ref(counterMutex[whichCounter]), std::ref(counter[whichCounter]),
                                             randomDelta(rng), tReschedule);
        microTasks.schedule(f, t);
    }
    nTasks = microTasks.getQueueInfo(first, last);
    BOOST_CHECK(nTasks == 100);
    BOOST_CHECK(first < last);
    BOOST_CHECK(last > now);

    // As soon as these are created they will start running and servicing the queue
    boost::thread_group microThreads;
    for (int i = 0; i < 5; i++)
        microThreads.create_thread(std::bind(&CScheduler::serviceQueue, &microTasks));

    UninterruptibleSleep(std::chrono::microseconds{600});
    now = std::chrono::system_clock::now();

    // More threads and more tasks:
    for (int i = 0; i < 5; i++)
        microThreads.create_thread(std::bind(&CScheduler::serviceQueue, &microTasks));
    for (int i = 0; i < 100; i++) {
        std::chrono::system_clock::time_point t = now + std::chrono::microseconds(randomMsec(rng));
        std::chrono::system_clock::time_point tReschedule = now + std::chrono::microseconds(500 + randomMsec(rng));
        int whichCounter = zeroToNine(rng);
        CScheduler::Function f = std::bind(&microTask, std::ref(microTasks),
                                             std::ref(counterMutex[whichCounter]), std::ref(counter[whichCounter]),
                                             randomDelta(rng), tReschedule);
        microTasks.schedule(f, t);
    }

    // Drain the task queue then exit threads
    microTasks.stop(true);
    microThreads.join_all(); // ... wait until all the threads are done

    int counterSum = 0;
    for (int i = 0; i < 10; i++) {
        BOOST_CHECK(counter[i] != 0);
        counterSum += counter[i];
    }
    BOOST_CHECK_EQUAL(counterSum, 200);
}

BOOST_AUTO_TEST_CASE(wait_until_past)
{
    std::condition_variable condvar;
    Mutex mtx;
    WAIT_LOCK(mtx, lock);

    const auto no_wait= [&](const std::chrono::seconds& d) {
        return condvar.wait_until(lock, std::chrono::system_clock::now() - d);
    };

    BOOST_CHECK(std::cv_status::timeout == no_wait(std::chrono::seconds{1}));
    BOOST_CHECK(std::cv_status::timeout == no_wait(std::chrono::minutes{1}));
    BOOST_CHECK(std::cv_status::timeout == no_wait(std::chrono::hours{1}));
    BOOST_CHECK(std::cv_status::timeout == no_wait(std::chrono::hours{10}));
    BOOST_CHECK(std::cv_status::timeout == no_wait(std::chrono::hours{100}));
    BOOST_CHECK(std::cv_status::timeout == no_wait(std::chrono::hours{1000}));
}

BOOST_AUTO_TEST_CASE(singlethreadedscheduler_ordered)
{
    CScheduler scheduler;

    // each queue should be well ordered with respect to itself but not other queues
    SingleThreadedSchedulerClient queue1(&scheduler);
    SingleThreadedSchedulerClient queue2(&scheduler);

    // create more threads than queues
    // if the queues only permit execution of one task at once then
    // the extra threads should effectively be doing nothing
    // if they don't we'll get out of order behaviour
    boost::thread_group threads;
    for (int i = 0; i < 5; ++i) {
        threads.create_thread(std::bind(&CScheduler::serviceQueue, &scheduler));
    }

    // these are not atomic, if SinglethreadedSchedulerClient prevents
    // parallel execution at the queue level no synchronization should be required here
    int counter1 = 0;
    int counter2 = 0;

    // just simply count up on each queue - if execution is properly ordered then
    // the callbacks should run in exactly the order in which they were enqueued
    for (int i = 0; i < 100; ++i) {
        queue1.AddToProcessQueue([i, &counter1]() {
            bool expectation = i == counter1++;
            assert(expectation);
        });

        queue2.AddToProcessQueue([i, &counter2]() {
            bool expectation = i == counter2++;
            assert(expectation);
        });
    }

    // finish up
    scheduler.stop(true);
    threads.join_all();

    BOOST_CHECK_EQUAL(counter1, 100);
    BOOST_CHECK_EQUAL(counter2, 100);
}

BOOST_AUTO_TEST_CASE(mockforward)
{
    CScheduler scheduler;

    int counter{0};
    CScheduler::Function dummy = [&counter]{counter++;};

    // schedule jobs for 2, 5 & 8 minutes into the future

    scheduler.scheduleFromNow(dummy, std::chrono::minutes{2});
    scheduler.scheduleFromNow(dummy, std::chrono::minutes{5});
    scheduler.scheduleFromNow(dummy, std::chrono::minutes{8});

    // check taskQueue
    std::chrono::system_clock::time_point first, last;
    size_t num_tasks = scheduler.getQueueInfo(first, last);
    BOOST_CHECK_EQUAL(num_tasks, 3ul);

    std::thread scheduler_thread([&]() { scheduler.serviceQueue(); });

    // bump the scheduler forward 5 minutes
    scheduler.MockForward(std::chrono::minutes{5});

    // ensure scheduler has chance to process all tasks queued for before 1 ms from now.
    scheduler.scheduleFromNow([&scheduler] { scheduler.stop(false); }, std::chrono::milliseconds{1});
    scheduler_thread.join();

    // check that the queue only has one job remaining
    num_tasks = scheduler.getQueueInfo(first, last);
    BOOST_CHECK_EQUAL(num_tasks, 1ul);

    // check that the dummy function actually ran
    BOOST_CHECK_EQUAL(counter, 2);

    // check that the time of the remaining job has been updated
    std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
    int delta = std::chrono::duration_cast<std::chrono::seconds>(first - now).count();
    // should be between 2 & 3 minutes from now
    BOOST_CHECK(delta > 2*60 && delta < 3*60);
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>test: Replace boost::mutex with std::mutex<commit_after>// Copyright (c) 2012-2020 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <random.h>
#include <scheduler.h>
#include <util/time.h>

#include <boost/test/unit_test.hpp>
#include <boost/thread.hpp>

#include <mutex>

BOOST_AUTO_TEST_SUITE(scheduler_tests)

static void microTask(CScheduler& s, std::mutex& mutex, int& counter, int delta, std::chrono::system_clock::time_point rescheduleTime)
{
    {
        std::lock_guard<std::mutex> lock(mutex);
        counter += delta;
    }
    std::chrono::system_clock::time_point noTime = std::chrono::system_clock::time_point::min();
    if (rescheduleTime != noTime) {
        CScheduler::Function f = std::bind(&microTask, std::ref(s), std::ref(mutex), std::ref(counter), -delta + 1, noTime);
        s.schedule(f, rescheduleTime);
    }
}

BOOST_AUTO_TEST_CASE(manythreads)
{
    // Stress test: hundreds of microsecond-scheduled tasks,
    // serviced by 10 threads.
    //
    // So... ten shared counters, which if all the tasks execute
    // properly will sum to the number of tasks done.
    // Each task adds or subtracts a random amount from one of the
    // counters, and then schedules another task 0-1000
    // microseconds in the future to subtract or add from
    // the counter -random_amount+1, so in the end the shared
    // counters should sum to the number of initial tasks performed.
    CScheduler microTasks;

    std::mutex counterMutex[10];
    int counter[10] = { 0 };
    FastRandomContext rng{/* fDeterministic */ true};
    auto zeroToNine = [](FastRandomContext& rc) -> int { return rc.randrange(10); }; // [0, 9]
    auto randomMsec = [](FastRandomContext& rc) -> int { return -11 + (int)rc.randrange(1012); }; // [-11, 1000]
    auto randomDelta = [](FastRandomContext& rc) -> int { return -1000 + (int)rc.randrange(2001); }; // [-1000, 1000]

    std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
    std::chrono::system_clock::time_point now = start;
    std::chrono::system_clock::time_point first, last;
    size_t nTasks = microTasks.getQueueInfo(first, last);
    BOOST_CHECK(nTasks == 0);

    for (int i = 0; i < 100; ++i) {
        std::chrono::system_clock::time_point t = now + std::chrono::microseconds(randomMsec(rng));
        std::chrono::system_clock::time_point tReschedule = now + std::chrono::microseconds(500 + randomMsec(rng));
        int whichCounter = zeroToNine(rng);
        CScheduler::Function f = std::bind(&microTask, std::ref(microTasks),
                                             std::ref(counterMutex[whichCounter]), std::ref(counter[whichCounter]),
                                             randomDelta(rng), tReschedule);
        microTasks.schedule(f, t);
    }
    nTasks = microTasks.getQueueInfo(first, last);
    BOOST_CHECK(nTasks == 100);
    BOOST_CHECK(first < last);
    BOOST_CHECK(last > now);

    // As soon as these are created they will start running and servicing the queue
    boost::thread_group microThreads;
    for (int i = 0; i < 5; i++)
        microThreads.create_thread(std::bind(&CScheduler::serviceQueue, &microTasks));

    UninterruptibleSleep(std::chrono::microseconds{600});
    now = std::chrono::system_clock::now();

    // More threads and more tasks:
    for (int i = 0; i < 5; i++)
        microThreads.create_thread(std::bind(&CScheduler::serviceQueue, &microTasks));
    for (int i = 0; i < 100; i++) {
        std::chrono::system_clock::time_point t = now + std::chrono::microseconds(randomMsec(rng));
        std::chrono::system_clock::time_point tReschedule = now + std::chrono::microseconds(500 + randomMsec(rng));
        int whichCounter = zeroToNine(rng);
        CScheduler::Function f = std::bind(&microTask, std::ref(microTasks),
                                             std::ref(counterMutex[whichCounter]), std::ref(counter[whichCounter]),
                                             randomDelta(rng), tReschedule);
        microTasks.schedule(f, t);
    }

    // Drain the task queue then exit threads
    microTasks.stop(true);
    microThreads.join_all(); // ... wait until all the threads are done

    int counterSum = 0;
    for (int i = 0; i < 10; i++) {
        BOOST_CHECK(counter[i] != 0);
        counterSum += counter[i];
    }
    BOOST_CHECK_EQUAL(counterSum, 200);
}

BOOST_AUTO_TEST_CASE(wait_until_past)
{
    std::condition_variable condvar;
    Mutex mtx;
    WAIT_LOCK(mtx, lock);

    const auto no_wait= [&](const std::chrono::seconds& d) {
        return condvar.wait_until(lock, std::chrono::system_clock::now() - d);
    };

    BOOST_CHECK(std::cv_status::timeout == no_wait(std::chrono::seconds{1}));
    BOOST_CHECK(std::cv_status::timeout == no_wait(std::chrono::minutes{1}));
    BOOST_CHECK(std::cv_status::timeout == no_wait(std::chrono::hours{1}));
    BOOST_CHECK(std::cv_status::timeout == no_wait(std::chrono::hours{10}));
    BOOST_CHECK(std::cv_status::timeout == no_wait(std::chrono::hours{100}));
    BOOST_CHECK(std::cv_status::timeout == no_wait(std::chrono::hours{1000}));
}

BOOST_AUTO_TEST_CASE(singlethreadedscheduler_ordered)
{
    CScheduler scheduler;

    // each queue should be well ordered with respect to itself but not other queues
    SingleThreadedSchedulerClient queue1(&scheduler);
    SingleThreadedSchedulerClient queue2(&scheduler);

    // create more threads than queues
    // if the queues only permit execution of one task at once then
    // the extra threads should effectively be doing nothing
    // if they don't we'll get out of order behaviour
    boost::thread_group threads;
    for (int i = 0; i < 5; ++i) {
        threads.create_thread(std::bind(&CScheduler::serviceQueue, &scheduler));
    }

    // these are not atomic, if SinglethreadedSchedulerClient prevents
    // parallel execution at the queue level no synchronization should be required here
    int counter1 = 0;
    int counter2 = 0;

    // just simply count up on each queue - if execution is properly ordered then
    // the callbacks should run in exactly the order in which they were enqueued
    for (int i = 0; i < 100; ++i) {
        queue1.AddToProcessQueue([i, &counter1]() {
            bool expectation = i == counter1++;
            assert(expectation);
        });

        queue2.AddToProcessQueue([i, &counter2]() {
            bool expectation = i == counter2++;
            assert(expectation);
        });
    }

    // finish up
    scheduler.stop(true);
    threads.join_all();

    BOOST_CHECK_EQUAL(counter1, 100);
    BOOST_CHECK_EQUAL(counter2, 100);
}

BOOST_AUTO_TEST_CASE(mockforward)
{
    CScheduler scheduler;

    int counter{0};
    CScheduler::Function dummy = [&counter]{counter++;};

    // schedule jobs for 2, 5 & 8 minutes into the future

    scheduler.scheduleFromNow(dummy, std::chrono::minutes{2});
    scheduler.scheduleFromNow(dummy, std::chrono::minutes{5});
    scheduler.scheduleFromNow(dummy, std::chrono::minutes{8});

    // check taskQueue
    std::chrono::system_clock::time_point first, last;
    size_t num_tasks = scheduler.getQueueInfo(first, last);
    BOOST_CHECK_EQUAL(num_tasks, 3ul);

    std::thread scheduler_thread([&]() { scheduler.serviceQueue(); });

    // bump the scheduler forward 5 minutes
    scheduler.MockForward(std::chrono::minutes{5});

    // ensure scheduler has chance to process all tasks queued for before 1 ms from now.
    scheduler.scheduleFromNow([&scheduler] { scheduler.stop(false); }, std::chrono::milliseconds{1});
    scheduler_thread.join();

    // check that the queue only has one job remaining
    num_tasks = scheduler.getQueueInfo(first, last);
    BOOST_CHECK_EQUAL(num_tasks, 1ul);

    // check that the dummy function actually ran
    BOOST_CHECK_EQUAL(counter, 2);

    // check that the time of the remaining job has been updated
    std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
    int delta = std::chrono::duration_cast<std::chrono::seconds>(first - now).count();
    // should be between 2 & 3 minutes from now
    BOOST_CHECK(delta > 2*60 && delta < 3*60);
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


<commit_before>/*

Copyright (c) 2007, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/pch.hpp"

#include "libtorrent/lsd.hpp"
#include "libtorrent/io.hpp"
#include "libtorrent/http_tracker_connection.hpp"
#include "libtorrent/buffer.hpp"
#include "libtorrent/http_parser.hpp"
#include "libtorrent/escape_string.hpp"
#include "libtorrent/socket_io.hpp" // for print_address

#include <boost/bind.hpp>
#include <boost/ref.hpp>
#if BOOST_VERSION < 103500
#include <asio/ip/host_name.hpp>
#include <asio/ip/multicast.hpp>
#else
#include <boost/asio/ip/host_name.hpp>
#include <boost/asio/ip/multicast.hpp>
#endif
#include <cstdlib>
#include <boost/config.hpp>

using boost::bind;
using namespace libtorrent;

namespace libtorrent
{
	// defined in broadcast_socket.cpp
	address guess_local_address(io_service&);
}

static error_code ec;

lsd::lsd(io_service& ios, address const& listen_interface
	, peer_callback_t const& cb)
	: m_callback(cb)
	, m_retry_count(1)
	, m_socket(ios, udp::endpoint(address_v4::from_string("239.192.152.143", ec), 6771)
		, bind(&lsd::on_announce, self(), _1, _2, _3))
	, m_broadcast_timer(ios)
	, m_disabled(false)
{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log.open("lsd.log", std::ios::in | std::ios::out | std::ios::trunc);
#endif
}

lsd::~lsd() {}

void lsd::announce(sha1_hash const& ih, int listen_port)
{
	if (m_disabled) return;

	char ih_hex[41];
	to_hex((char const*)&ih[0], 20, ih_hex);
	char msg[200];
	int msg_len = snprintf(msg, 200,
		"BT-SEARCH * HTTP/1.1\r\n"
		"Host: 239.192.152.143:6771\r\n"
		"Port: %d\r\n"
		"Infohash: %s\r\n"
		"\r\n\r\n", listen_port, ih_hex);

	m_retry_count = 1;
	error_code ec;
	m_socket.send(msg, msg_len, ec);
	if (ec)
	{
		m_disabled = true;
		return;
	}

#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	snprintf(msg, 200, "%s ==> announce: ih: %s port: %u"
		, time_now_string(), ih_hex, listen_port);
	m_log << msg << std::endl;
#endif

	m_broadcast_timer.expires_from_now(milliseconds(250 * m_retry_count), ec);
	m_broadcast_timer.async_wait(bind(&lsd::resend_announce, self(), _1, msg));
}

void lsd::resend_announce(error_code const& e, std::string msg)
{
	if (e) return;

	error_code ec;
	m_socket.send(msg.c_str(), int(msg.size()), ec);

	++m_retry_count;
	if (m_retry_count >= 5)
		return;

	m_broadcast_timer.expires_from_now(milliseconds(250 * m_retry_count), ec);
	m_broadcast_timer.async_wait(bind(&lsd::resend_announce, self(), _1, msg));
}

void lsd::on_announce(udp::endpoint const& from, char* buffer
	, std::size_t bytes_transferred)
{
	using namespace libtorrent::detail;

	http_parser p;

	bool error = false;
	p.incoming(buffer::const_interval(buffer, buffer + bytes_transferred)
		, error);

	if (!p.header_finished() || error)
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << time_now_string()
		<< " <== announce: incomplete HTTP message" << std::endl;
#endif
		return;
	}

	if (p.method() != "bt-search")
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << time_now_string()
		<< " <== announce: invalid HTTP method: " << p.method() << std::endl;
#endif
		return;
	}

	std::string const& port_str = p.header("port");
	if (port_str.empty())
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << time_now_string()
		<< " <== announce: invalid BT-SEARCH, missing port" << std::endl;
#endif
		return;
	}

	std::string const& ih_str = p.header("infohash");
	if (ih_str.empty())
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << time_now_string()
		<< " <== announce: invalid BT-SEARCH, missing infohash" << std::endl;
#endif
		return;
	}

	sha1_hash ih(0);
	from_hex(ih_str.c_str(), 40, (char*)&ih[0]);
	int port = std::atoi(port_str.c_str());

	if (!ih.is_all_zeros() && port != 0)
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
		char msg[200];
		snprintf(msg, 200, "%s *** incoming local announce %s:%d ih: %s\n"
			, time_now_string(), print_address(from.address()).c_str()
			, port, ih_str.c_str());
#endif
		// we got an announce, pass it on through the callback
#ifndef BOOST_NO_EXCEPTIONS
		try {
#endif
			m_callback(tcp::endpoint(from.address(), port), ih);
#ifndef BOOST_NO_EXCEPTIONS
		}
		catch (std::exception&) {}
#endif
	}
}

void lsd::close()
{
	m_socket.close();
	error_code ec;
	m_broadcast_timer.cancel(ec);
	m_disabled = true;
	m_callback.clear();
}

<commit_msg>local service discovery fix<commit_after>/*

Copyright (c) 2007, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/pch.hpp"

#include "libtorrent/lsd.hpp"
#include "libtorrent/io.hpp"
#include "libtorrent/http_tracker_connection.hpp"
#include "libtorrent/buffer.hpp"
#include "libtorrent/http_parser.hpp"
#include "libtorrent/escape_string.hpp"
#include "libtorrent/socket_io.hpp" // for print_address

#include <boost/bind.hpp>
#include <boost/ref.hpp>
#if BOOST_VERSION < 103500
#include <asio/ip/host_name.hpp>
#include <asio/ip/multicast.hpp>
#else
#include <boost/asio/ip/host_name.hpp>
#include <boost/asio/ip/multicast.hpp>
#endif
#include <cstdlib>
#include <boost/config.hpp>

using boost::bind;
using namespace libtorrent;

namespace libtorrent
{
	// defined in broadcast_socket.cpp
	address guess_local_address(io_service&);
}

static error_code ec;

lsd::lsd(io_service& ios, address const& listen_interface
	, peer_callback_t const& cb)
	: m_callback(cb)
	, m_retry_count(1)
	, m_socket(ios, udp::endpoint(address_v4::from_string("239.192.152.143", ec), 6771)
		, bind(&lsd::on_announce, self(), _1, _2, _3))
	, m_broadcast_timer(ios)
	, m_disabled(false)
{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log.open("lsd.log", std::ios::in | std::ios::out | std::ios::trunc);
#endif
}

lsd::~lsd() {}

void lsd::announce(sha1_hash const& ih, int listen_port)
{
	if (m_disabled) return;

	char ih_hex[41];
	to_hex((char const*)&ih[0], 20, ih_hex);
	char msg[200];
	int msg_len = snprintf(msg, 200,
		"BT-SEARCH * HTTP/1.1\r\n"
		"Host: 239.192.152.143:6771\r\n"
		"Port: %d\r\n"
		"Infohash: %s\r\n"
		"\r\n\r\n", listen_port, ih_hex);

	m_retry_count = 1;
	error_code ec;
	m_socket.send(msg, msg_len, ec);
	if (ec)
	{
		m_disabled = true;
		return;
	}

#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	snprintf(msg, 200, "%s ==> announce: ih: %s port: %u"
		, time_now_string(), ih_hex, listen_port);
	m_log << msg << std::endl;
#endif

	m_broadcast_timer.expires_from_now(milliseconds(250 * m_retry_count), ec);
	m_broadcast_timer.async_wait(bind(&lsd::resend_announce, self(), _1, std::string(msg)));
}

void lsd::resend_announce(error_code const& e, std::string msg)
{
	if (e) return;

	error_code ec;
	m_socket.send(msg.c_str(), int(msg.size()), ec);

	++m_retry_count;
	if (m_retry_count >= 5)
		return;

	m_broadcast_timer.expires_from_now(milliseconds(250 * m_retry_count), ec);
	m_broadcast_timer.async_wait(bind(&lsd::resend_announce, self(), _1, msg));
}

void lsd::on_announce(udp::endpoint const& from, char* buffer
	, std::size_t bytes_transferred)
{
	using namespace libtorrent::detail;

	http_parser p;

	bool error = false;
	p.incoming(buffer::const_interval(buffer, buffer + bytes_transferred)
		, error);

	if (!p.header_finished() || error)
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << time_now_string()
		<< " <== announce: incomplete HTTP message" << std::endl;
#endif
		return;
	}

	if (p.method() != "bt-search")
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << time_now_string()
		<< " <== announce: invalid HTTP method: " << p.method() << std::endl;
#endif
		return;
	}

	std::string const& port_str = p.header("port");
	if (port_str.empty())
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << time_now_string()
		<< " <== announce: invalid BT-SEARCH, missing port" << std::endl;
#endif
		return;
	}

	std::string const& ih_str = p.header("infohash");
	if (ih_str.empty())
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
	m_log << time_now_string()
		<< " <== announce: invalid BT-SEARCH, missing infohash" << std::endl;
#endif
		return;
	}

	sha1_hash ih(0);
	from_hex(ih_str.c_str(), 40, (char*)&ih[0]);
	int port = std::atoi(port_str.c_str());

	if (!ih.is_all_zeros() && port != 0)
	{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
		char msg[200];
		snprintf(msg, 200, "%s *** incoming local announce %s:%d ih: %s\n"
			, time_now_string(), print_address(from.address()).c_str()
			, port, ih_str.c_str());
#endif
		// we got an announce, pass it on through the callback
#ifndef BOOST_NO_EXCEPTIONS
		try {
#endif
			m_callback(tcp::endpoint(from.address(), port), ih);
#ifndef BOOST_NO_EXCEPTIONS
		}
		catch (std::exception&) {}
#endif
	}
}

void lsd::close()
{
	m_socket.close();
	error_code ec;
	m_broadcast_timer.cancel(ec);
	m_disabled = true;
	m_callback.clear();
}

<|endoftext|>


<commit_before>#include <getopt.h>

#include <iostream>

#include "error_handling.h"
#include "game.h"
#include "command.h"
#include "io.h"
#include "score.h"
#include "misc.h"
#include "level_rooms.h"
#include "player.h"
#include "options.h"
#include "os.h"
#include "move.h"
#include "rogue.h"
#include "wizard.h"

using namespace std;

// Parse command-line arguments
static void
parse_args(int argc, char* const* argv, std::string& whoami)
{
  string const game_version = "Misty Mountains v2.0-alpha1 - Based on Rogue5.4.4";
  int option_index = 0;
  struct option const long_options[] = {
    {"no-colors", no_argument,       0, 'c'},
    {"escdelay",  optional_argument, 0, 'E'},
    {"flush",     no_argument,       0, 'f'},
    {"hide-floor",no_argument,       0, 'F'},
    {"no-jump",   no_argument,       0, 'j'},
    {"name",      required_argument, 0, 'n'},
    {"passgo",    no_argument,       0, 'p'},
    {"restore",   no_argument,       0, 'r'},
    {"score",     no_argument,       0, 's'},
    {"seed",      required_argument, 0, 'S'},
    {"wizard",    no_argument,       0, 'W'},
    {"help",      no_argument,       0, '0'},
    {"version",   no_argument,       0, '1'},
    {0,           0,                 0,  0 }
  };

  // Global default options
  ESCDELAY = 0;

  // Set seed and dungeon number
  os_rand_seed = static_cast<unsigned>(time(nullptr) + getpid());

  for (;;)
  {
    int c = getopt_long(argc, argv, "cE::fjn:prsS:W",
                        long_options, &option_index);
    if (c == -1)
      break;

    switch (c)
    {
      case 'c': use_colors = false; break;
      case 'E': ESCDELAY = optarg == nullptr ? 64 : atoi(optarg); break;
      case 'f': fight_flush = true; break;
      case 'j': jump = false; break;
      case 'n': if (optarg != nullptr) {
                  whoami = optarg;
                } break;
      case 'p': passgo = true; break;
      case 's': score_show_and_exit(0, -1, 0); // does not return
      case 'S': if (wizard && optarg != nullptr) {
                  os_rand_seed = static_cast<unsigned>(stoul(optarg));
                } break;
      case 'W': wizard = true; break;
      case '0':
        cout << "Usage: " << argv[0] << " [OPTIONS] [FILE]\n"
             << "Run Rogue14 with selected options or a savefile\n\n"
             << "  -c, --no-colors      remove colors from the game\n"
             << "  -E, --escdelay=[NUM] set escdelay in ms. Not setting this\n"
             << "                       defaults to 0. If you do not give a NUM\n"
             << "                       argument, it's set to 64 (old standard)\n"
             << "  -f, --flush          flush typeahead during battle\n"
             << "  -j, --no-jump        draw each player step separately\n"
             << "  -n, --name=NAME      set highscore name\n"
             << "  -p, --passgo         follow the turnings in passageways\n"
             << "  -r, --restore        restore game instead of creating a new\n"
             << "  -s, --score          display the highscore and exit\n"
             << "  -W, --wizard         run the game in debug-mode\n"
             << "  -S, --seed=NUMBER    (wizard) set map seed to NUMBER\n"
             << "      --help           display this help and exit\n"
             << "      --version        display game version and exit\n\n"
             << game_version
             << endl;
        exit(0);
      case '1':
        cout << game_version << endl;
        exit(0);
      default:
        cerr << "Try '" << argv[0] << " --help' for more information\n";
        exit(1);
    }
  }

  if (optind < argc)
  {
    cerr << "Try '" << argv[0] << " --help' for more information\n";
    exit(1);
  }
}

/** main:
 * The main program, of course */
int
main(int argc, char** argv)
{
  /* Open scoreboard, so we can modify the score later */
  score_open();

  /* Parse args and then init new (or old) game */
  string whoami;
  parse_args(argc, argv, whoami);
  if (whoami.empty()) {
    whoami = os_whoami();
  }
  Game* new_game = new Game(whoami);
  return new_game->run();
}

<commit_msg>Set version to -dev since this is the default branch now<commit_after>#include <getopt.h>

#include <iostream>

#include "error_handling.h"
#include "game.h"
#include "command.h"
#include "io.h"
#include "score.h"
#include "misc.h"
#include "level_rooms.h"
#include "player.h"
#include "options.h"
#include "os.h"
#include "move.h"
#include "rogue.h"
#include "wizard.h"

using namespace std;

// Parse command-line arguments
static void
parse_args(int argc, char* const* argv, std::string& whoami)
{
  string const game_version = "Misty Mountains v2.0-alpha1-dev - Based on Rogue5.4.4";
  int option_index = 0;
  struct option const long_options[] = {
    {"no-colors", no_argument,       0, 'c'},
    {"escdelay",  optional_argument, 0, 'E'},
    {"flush",     no_argument,       0, 'f'},
    {"hide-floor",no_argument,       0, 'F'},
    {"no-jump",   no_argument,       0, 'j'},
    {"name",      required_argument, 0, 'n'},
    {"passgo",    no_argument,       0, 'p'},
    {"restore",   no_argument,       0, 'r'},
    {"score",     no_argument,       0, 's'},
    {"seed",      required_argument, 0, 'S'},
    {"wizard",    no_argument,       0, 'W'},
    {"help",      no_argument,       0, '0'},
    {"version",   no_argument,       0, '1'},
    {0,           0,                 0,  0 }
  };

  // Global default options
  ESCDELAY = 0;

  // Set seed and dungeon number
  os_rand_seed = static_cast<unsigned>(time(nullptr) + getpid());

  for (;;)
  {
    int c = getopt_long(argc, argv, "cE::fjn:prsS:W",
                        long_options, &option_index);
    if (c == -1)
      break;

    switch (c)
    {
      case 'c': use_colors = false; break;
      case 'E': ESCDELAY = optarg == nullptr ? 64 : atoi(optarg); break;
      case 'f': fight_flush = true; break;
      case 'j': jump = false; break;
      case 'n': if (optarg != nullptr) {
                  whoami = optarg;
                } break;
      case 'p': passgo = true; break;
      case 's': score_show_and_exit(0, -1, 0); // does not return
      case 'S': if (wizard && optarg != nullptr) {
                  os_rand_seed = static_cast<unsigned>(stoul(optarg));
                } break;
      case 'W': wizard = true; break;
      case '0':
        cout << "Usage: " << argv[0] << " [OPTIONS] [FILE]\n"
             << "Run Rogue14 with selected options or a savefile\n\n"
             << "  -c, --no-colors      remove colors from the game\n"
             << "  -E, --escdelay=[NUM] set escdelay in ms. Not setting this\n"
             << "                       defaults to 0. If you do not give a NUM\n"
             << "                       argument, it's set to 64 (old standard)\n"
             << "  -f, --flush          flush typeahead during battle\n"
             << "  -j, --no-jump        draw each player step separately\n"
             << "  -n, --name=NAME      set highscore name\n"
             << "  -p, --passgo         follow the turnings in passageways\n"
             << "  -r, --restore        restore game instead of creating a new\n"
             << "  -s, --score          display the highscore and exit\n"
             << "  -W, --wizard         run the game in debug-mode\n"
             << "  -S, --seed=NUMBER    (wizard) set map seed to NUMBER\n"
             << "      --help           display this help and exit\n"
             << "      --version        display game version and exit\n\n"
             << game_version
             << endl;
        exit(0);
      case '1':
        cout << game_version << endl;
        exit(0);
      default:
        cerr << "Try '" << argv[0] << " --help' for more information\n";
        exit(1);
    }
  }

  if (optind < argc)
  {
    cerr << "Try '" << argv[0] << " --help' for more information\n";
    exit(1);
  }
}

/** main:
 * The main program, of course */
int
main(int argc, char** argv)
{
  /* Open scoreboard, so we can modify the score later */
  score_open();

  /* Parse args and then init new (or old) game */
  string whoami;
  parse_args(argc, argv, whoami);
  if (whoami.empty()) {
    whoami = os_whoami();
  }
  Game* new_game = new Game(whoami);
  return new_game->run();
}

<|endoftext|>