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

#include "vvMainWindow.h"
#include "ui_vvMainWindow.h"

#include "vvLoadDataReaction.h"
#include "vvToggleSpreadSheetReaction.h"

#include <pqActiveObjects.h>
#include <pqApplicationCore.h>
#include <pqAutoLoadPluginXMLBehavior.h>
#include <pqCommandLineOptionsBehavior.h>
#include <pqCrashRecoveryBehavior.h>
#include <pqInterfaceTracker.h>
#include <pqObjectBuilder.h>
#include <pqPersistentMainWindowStateBehavior.h>
#include <pqPythonShellReaction.h>
#include <pqQtMessageHandlerBehavior.h>
#include <pqRenderView.h>
#include <pqRenderViewSelectionReaction.h>
#include <pqServer.h>
#include <pqSettings.h>
#include <pqSpreadSheetView.h>
#include <pqSpreadSheetVisibilityBehavior.h>
#include <pqStandardPropertyWidgetInterface.h>
#include <pqVelodyneManager.h>
#include <vtkPVPlugin.h>
#include <vtkSMPropertyHelper.h>

#include <QLabel>
#include <QSplitter>

#include <cassert>

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

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

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

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

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

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

    // Define application behaviors.
    new pqAutoLoadPluginXMLBehavior(window);
    new pqCommandLineOptionsBehavior(window);
    new pqCrashRecoveryBehavior(window);
    new pqPersistentMainWindowStateBehavior(window);
    new pqQtMessageHandlerBehavior(window);
    new pqSpreadSheetVisibilityBehavior(window);

//    pqServer::setCoincidentTopologyResolutionModeSetting(0);

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

    // Create a default view.
    pqRenderView* view = qobject_cast<pqRenderView*>(builder->createView(pqRenderView::renderViewType(), server));
    assert(view);
    this->MainView = view;

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

    // Create a horizontal splitter as the central widget, add views to splitter
    QSplitter* splitter = new QSplitter(Qt::Horizontal);
    window->setCentralWidget(splitter);

    // Add the main widget to the left
    splitter->addWidget(view->getWidget());

    QSplitter* vSplitter = new QSplitter(Qt::Vertical);
    splitter->addWidget(vSplitter);

    pqView* overheadView = builder->createView(pqRenderView::renderViewType(), server);
//    overheadView->SetInteractionMode("2D");
    overheadView->getProxy()->UpdateVTKObjects();
    // dont add to the splitter just yet
    // TODO: These sizes should not be absolute things
    overheadView->getWidget()->setMinimumSize(300, 200);
    vSplitter->addWidget(overheadView->getWidget());
    new vvToggleSpreadSheetReaction(this->Ui.actionOverheadView, overheadView);

    pqView* spreadsheetView = builder->createView(pqSpreadSheetView::spreadsheetViewType(), server);
    spreadsheetView->getProxy()->UpdateVTKObjects();
    vSplitter->addWidget(spreadsheetView->getWidget());
    new vvToggleSpreadSheetReaction(this->Ui.actionSpreadsheet, spreadsheetView);

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

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

    new pqPythonShellReaction(this->Ui.actionPython_Console);

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

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

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

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

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

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

  PV_PLUGIN_IMPORT(VelodyneHDLPlugin);
  PV_PLUGIN_IMPORT(PythonQtPlugin);
}

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

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

#include "vvMainWindow.h"
#include "ui_vvMainWindow.h"

#include "vvLoadDataReaction.h"
#include "vvToggleSpreadSheetReaction.h"

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

#include <QLabel>
#include <QSplitter>

#include <cassert>

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

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

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

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

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

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

    // Define application behaviors.
    new pqQtMessageHandlerBehavior(window);
    new pqDataTimeStepBehavior(window);
    new pqSpreadSheetVisibilityBehavior(window);
    new pqObjectPickingBehavior(window);
//    new pqDefaultViewBehavior(window);
    new pqCrashRecoveryBehavior(window);
    new pqAutoLoadPluginXMLBehavior(window);
    new pqCommandLineOptionsBehavior(window);
    new pqPersistentMainWindowStateBehavior(window);

//    pqServer::setCoincidentTopologyResolutionModeSetting(0);

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

    // Create a default view.
    pqRenderView* view = qobject_cast<pqRenderView*>(builder->createView(pqRenderView::renderViewType(), server));
    assert(view);
    this->MainView = view;

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

    // Create a horizontal splitter as the central widget, add views to splitter
    QSplitter* splitter = new QSplitter(Qt::Horizontal);
    window->setCentralWidget(splitter);

    // Add the main widget to the left
    splitter->addWidget(view->getWidget());

    QSplitter* vSplitter = new QSplitter(Qt::Vertical);
    splitter->addWidget(vSplitter);

    pqView* overheadView = builder->createView(pqRenderView::renderViewType(), server);
//    overheadView->SetInteractionMode("2D");
    overheadView->getProxy()->UpdateVTKObjects();
    // dont add to the splitter just yet
    // TODO: These sizes should not be absolute things
    overheadView->getWidget()->setMinimumSize(300, 200);
    vSplitter->addWidget(overheadView->getWidget());
    new vvToggleSpreadSheetReaction(this->Ui.actionOverheadView, overheadView);

    pqView* spreadsheetView = builder->createView(pqSpreadSheetView::spreadsheetViewType(), server);
    spreadsheetView->getProxy()->UpdateVTKObjects();
    vSplitter->addWidget(spreadsheetView->getWidget());
    new vvToggleSpreadSheetReaction(this->Ui.actionSpreadsheet, spreadsheetView);
    pqSpreadSheetView* ssview = qobject_cast<pqSpreadSheetView*>(spreadsheetView);
    assert(spreadsheetView);
    new pqSpreadSheetViewDecorator(ssview);

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

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

    new pqPythonShellReaction(this->Ui.actionPython_Console);

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

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

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

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

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

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

  PV_PLUGIN_IMPORT(VelodyneHDLPlugin);
  PV_PLUGIN_IMPORT(PythonQtPlugin);
}

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

//-----------------------------------------------------------------------------
<|endoftext|>


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

#include <cstdlib>
#include <cerrno>

#include <unistd.h>

#include "frontend.h"
#include "constant.h"
#include "core.h"

namespace ydsh {

FrontEnd::FrontEnd(const char *scriptDir, Lexer &&lexer, SymbolTable &symbolTable,
                   DSExecMode mode, bool toplevel, const DumpTarget &target, bool ignore) :
        scriptDir(scriptDir), lexer(std::move(lexer)), mode(mode),
        parser(this->lexer), checker(symbolTable, toplevel),
        uastDumper(target.files[DS_DUMP_KIND_UAST].get(), symbolTable),
        astDumper(target.files[DS_DUMP_KIND_AST].get(), symbolTable), ignoreNotFoundMod(ignore) {
}

#define EACH_TERM_COLOR(C) \
    C(Reset,    0) \
    C(Bold,     1) \
    /*C(Black,   30)*/ \
    /*C(Red,     31)*/ \
    C(Green,   32) \
    /*C(Yellow,  33)*/ \
    C(Blue,    34) \
    C(Magenta, 35) \
    C(Cyan,    36) /*\
    C(white,   37)*/

enum class TermColor : unsigned int {   // ansi color code
#define GEN_ENUM(E, N) E,
    EACH_TERM_COLOR(GEN_ENUM)
#undef GEN_ENUM
};

/**
 * not allow dumb terminal
 */
static bool isSupportedTerminal(int fd) {
    const char *term = getenv(ENV_TERM);
    return term != nullptr && strcasecmp(term, "dumb") != 0 && isatty(fd) != 0;
}

struct ColorController {
    bool isatty;

    explicit ColorController(int fd) : isatty(isSupportedTerminal(fd)) {}

    const char *operator()(TermColor color) const {
        if(this->isatty) {
#define GEN_STR(E, C) "\033[" #C "m",
            const char *ansi[] = {
                    EACH_TERM_COLOR(GEN_STR)
            };
#undef GEN_STR
            return ansi[static_cast<unsigned int>(color)];
        }
        return "";
    }
};

static std::vector<std::string> split(const std::string &str) {
    std::vector<std::string> bufs;
    bufs.emplace_back();
    for(auto ch : str) {
        if(ch == '\n') {
            bufs.emplace_back();
        } else {
            bufs.back() += ch;
        }
    }
    return bufs;
}

static void formatErrorLine(ColorController cc, const Lexer &lexer, Token token) {
    if(token.pos + token.size == 0) {
        return;
    }

    Token errorToken = lexer.shiftEOS(token);
    Token lineToken = lexer.getLineToken(errorToken);
    auto line = lexer.formatTokenText(lineToken);
    auto marker = lexer.formatLineMarker(lineToken, errorToken);

    auto lines = split(line);
    auto markers = split(marker);
    unsigned int size = lines.size();
    assert(size == markers.size());
    for(unsigned int i = 0; i < size; i++) {
        // print error line
        fprintf(stderr, "%s%s%s\n", cc(TermColor::Cyan), lines[i].c_str(), cc(TermColor::Reset));

        // print line marker
        fprintf(stderr, "%s%s%s%s\n", cc(TermColor::Green), cc(TermColor::Bold),
                markers[i].c_str(), cc(TermColor::Reset));
    }

    fflush(stderr);
}

void printError(const Lexer &lex, const char *kind, Token token,
               ColorController cc, TermColor color, const char *message) {
    unsigned lineNumOffset = lex.getSourceInfo()->getLineNumOffset();
    fprintf(stderr, "%s:", lex.getSourceInfo()->getSourceName().c_str());
    if(lineNumOffset > 0) {
        unsigned int lineNum = lex.getSourceInfo()->getLineNum(token.pos);
        fprintf(stderr, "%d:", lineNum);
    }
    fprintf(stderr, " %s%s[%s]%s %s\n",
            cc(color), cc(TermColor::Bold), kind, cc(TermColor::Reset), message);

    if(lineNumOffset > 0) {
        formatErrorLine(cc, lex, token);
    }
}

DSError FrontEnd::handleError(DSErrorKind type, const char *errorKind,
                              Token errorToken, const std::string &message) const {
    if(!this->suppressError(errorKind)) {
        ColorController cc(STDERR_FILENO);

        /**
         * show error message
         */
        printError(*this->parser.getLexer(),
                   type == DS_ERROR_KIND_PARSE_ERROR ? "syntax error" : "semantic error",
                   errorToken, cc, TermColor::Magenta, message.c_str());

        for(int i = static_cast<int>(this->contexts.size()) - 1; i > -1; i--) {
            Token token = this->contexts[i].sourceNode->getPathNode()->getToken();
            auto &lex = i > 0 ? this->contexts[i - 1].lexer : this->lexer;
            printError(lex, "note", token, cc, TermColor::Blue, "at module import");
        }
    }

    unsigned int errorLineNum = this->getSourceInfo()->getLineNum(errorToken.pos);
    const char *sourceName = this->getSourceInfo()->getSourceName().c_str();
    return {
            .kind = type,
            .fileName = strdup(sourceName),
            .lineNum = errorLineNum,
            .name = strdup(errorKind)
    };
}

std::unique_ptr<Node> FrontEnd::tryToParse(DSError *dsError) {
    std::unique_ptr<Node> node;
    if(this->parser) {
        node = this->parser();
        if(this->parser.hasError()) {
            auto e = this->handleParseError();
            if(dsError != nullptr) {
                *dsError = e;
            } else {
                DSError_release(&e);
            }
        }
        if(this->uastDumper) {
            this->uastDumper(*node);
        }
    }
    return node;
}

void FrontEnd::tryToCheckType(std::unique_ptr<Node> &node) {
    if(this->mode == DS_EXEC_MODE_PARSE_ONLY) {
        return;
    }

    node = this->checker(this->prevType, std::move(node));
    this->prevType = &node->getType();

    if(this->astDumper) {
        this->astDumper(*node);
    }
}

std::pair<std::unique_ptr<Node>, FrontEnd::Status> FrontEnd::operator()(DSError *dsError) {
    // parse
    auto node = this->tryToParse(dsError);
    if(this->parser.hasError()) {
        return {nullptr, IN_MODULE};
    }

    // typecheck
    try {
        auto s = this->tryToCheckModule(node);
        if(s != IN_MODULE) {
            return {nullptr, s};
        }

        this->tryToCheckType(node);
    } catch(const TypeCheckError &e) {
        auto ret = this->handleTypeError(e);
        if(dsError != nullptr) {
            *dsError = ret;
        } else {
            DSError_release(&ret);
        }
        return {nullptr, IN_MODULE};
    }

    auto s = IN_MODULE;
    if(node->is(NodeKind::Source) && static_cast<SourceNode&>(*node).isFirstAppear()) {
        s = EXIT_MODULE;
    }
    return {std::move(node), s};
}

void FrontEnd::setupASTDump() {
    if(this->uastDumper) {
        this->uastDumper.initialize(this->getSourceInfo()->getSourceName(), "### dump untyped AST ###");
    }
    if(this->mode != DS_EXEC_MODE_PARSE_ONLY && this->astDumper) {
        this->astDumper.initialize(this->getSourceInfo()->getSourceName(), "### dump typed AST ###");
    }
}

void FrontEnd::teardownASTDump() {
    if(this->uastDumper) {
        this->uastDumper.finalize();
    }
    if(this->mode != DS_EXEC_MODE_PARSE_ONLY && this->astDumper) {
        this->astDumper.finalize();
    }
}

FrontEnd::Status FrontEnd::tryToCheckModule(std::unique_ptr<Node> &node) {
    if(!node) {
        node = this->exitModule();
        return IN_MODULE;
    }

    if(!node->is(NodeKind::Source)) {
        return IN_MODULE;
    }

    auto &srcNode = static_cast<SourceNode&>(*node);
    const char *modPath = srcNode.getPathStr().c_str();
    FilePtr filePtr;
    auto ret = this->getSymbolTable().tryToLoadModule(this->getCurScriptDir().c_str(), modPath, filePtr);
    if(is<ModLoadingError>(ret)) {
        switch(get<ModLoadingError>(ret)) {
        case ModLoadingError::CIRCULAR:
            RAISE_TC_ERROR(CircularMod, *srcNode.getPathNode(), modPath);
        case ModLoadingError::NOT_OPEN:
            RAISE_TC_ERROR(NotOpenMod, *srcNode.getPathNode(), srcNode.getPathStr().c_str(), strerror(errno));
        case ModLoadingError::NOT_FOUND:
            RAISE_TC_ERROR(NotFoundMod, *srcNode.getPathNode(), srcNode.getPathStr().c_str());
        }
    } else if(is<const char *>(ret)) {
        ByteBuffer buf;
        if(!readAll(filePtr.get(), buf)) {
            RAISE_TC_ERROR(NotOpenMod, *srcNode.getPathNode(), srcNode.getPathStr().c_str(), strerror(errno));
        }
        this->enterModule(get<const char*>(ret), std::move(buf),
                          std::unique_ptr<SourceNode>(static_cast<SourceNode *>(node.release())));
        return ENTER_MODULE;
    } else if(is<ModType *>(ret)) {
        srcNode.setModType(*get<ModType *>(ret));
        return IN_MODULE;
    }
    return IN_MODULE;   // normally unreachable, due to suppress gcc warning
}

void FrontEnd::enterModule(const char *fullPath, ByteBuffer &&buf, std::unique_ptr<SourceNode> &&node) {
    {
        assert(*fullPath == '/');
        Lexer lex(fullPath, std::move(buf));
        node->setFirstAppear(true);
        auto state = this->parser.saveLexicalState();
        auto scope = this->getSymbolTable().createModuleScope();
        this->contexts.emplace_back(std::move(lex), std::move(scope), std::move(state), std::move(node));
    }
    Token token{};
    TokenKind kind = this->contexts.back().lexer.nextToken(token);
    TokenKind ckind{};
    this->parser.restoreLexicalState(this->contexts.back().lexer, kind, token, ckind);

    if(this->uastDumper) {
        this->uastDumper.enterModule(fullPath);
    }
    if(this->mode != DS_EXEC_MODE_PARSE_ONLY && this->astDumper) {
        this->astDumper.enterModule(fullPath);
    }
}

std::unique_ptr<SourceNode> FrontEnd::exitModule() {
    auto &ctx = this->contexts.back();
    TokenKind kind = ctx.kind;
    Token token = ctx.token;
    TokenKind consumedKind = ctx.consumedKind;
    std::unique_ptr<SourceNode> node(ctx.sourceNode.release());
    auto &modType = this->getSymbolTable().createModType(ctx.lexer.getSourceInfo()->getSourceName());
    auto scope = std::move(ctx.scope);
    this->contexts.pop_back();

    auto &lex = this->contexts.empty() ? this->lexer : this->contexts.back().lexer;
    this->parser.restoreLexicalState(lex, kind, token, consumedKind);
    if(this->contexts.empty()) {
        this->getSymbolTable().resetCurModule();
    } else {
        this->getSymbolTable().setModuleScope(*this->contexts.back().scope);
    }

    if(this->mode != DS_EXEC_MODE_PARSE_ONLY) {
        unsigned int varNum = scope->getMaxVarIndex();
        node->setMaxVarNum(varNum);
        node->setModType(modType);
        if(prevType != nullptr && this->prevType->isNothingType()) {
            this->prevType = &this->getSymbolTable().get(TYPE::Void);
            node->setNothing(true);
        }
    }

    if(this->uastDumper) {
        this->uastDumper.leaveModule();
    }
    if(this->mode != DS_EXEC_MODE_PARSE_ONLY && this->astDumper) {
        this->astDumper.leaveModule();
    }
    return node;
}

} // namespace ydsh
<commit_msg>refactor parse/type error reporting<commit_after>/*
 * Copyright (C) 2018 Nagisa Sekiguchi
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <cstdlib>
#include <cerrno>

#include <unistd.h>

#include "frontend.h"
#include "constant.h"
#include "core.h"

namespace ydsh {

FrontEnd::FrontEnd(const char *scriptDir, Lexer &&lexer, SymbolTable &symbolTable,
                   DSExecMode mode, bool toplevel, const DumpTarget &target, bool ignore) :
        scriptDir(scriptDir), lexer(std::move(lexer)), mode(mode),
        parser(this->lexer), checker(symbolTable, toplevel),
        uastDumper(target.files[DS_DUMP_KIND_UAST].get(), symbolTable),
        astDumper(target.files[DS_DUMP_KIND_AST].get(), symbolTable), ignoreNotFoundMod(ignore) {
}

#define EACH_TERM_COLOR(C) \
    C(Reset,    0) \
    C(Bold,     1) \
    /*C(Black,   30)*/ \
    /*C(Red,     31)*/ \
    C(Green,   32) \
    /*C(Yellow,  33)*/ \
    C(Blue,    34) \
    C(Magenta, 35) \
    C(Cyan,    36) /*\
    C(white,   37)*/

enum class TermColor : unsigned int {   // ansi color code
#define GEN_ENUM(E, N) E,
    EACH_TERM_COLOR(GEN_ENUM)
#undef GEN_ENUM
};

/**
 * not allow dumb terminal
 */
static bool isSupportedTerminal(int fd) {
    const char *term = getenv(ENV_TERM);
    return term != nullptr && strcasecmp(term, "dumb") != 0 && isatty(fd) != 0;
}

static std::vector<std::string> split(const std::string &str) {
    std::vector<std::string> bufs;
    bufs.emplace_back();
    for(auto ch : str) {
        if(ch == '\n') {
            bufs.emplace_back();
        } else {
            bufs.back() += ch;
        }
    }
    return bufs;
}

class ErrorReporter {
private:
    FILE *fp;
    bool tty;

public:
    explicit ErrorReporter(FILE *fp) : fp(fp), tty(isSupportedTerminal(fileno(fp))) {}

    void operator()(const Lexer &lex, const char *kind, Token token, TermColor c, const char *message) const {
        unsigned lineNumOffset = lex.getSourceInfo()->getLineNumOffset();
        fprintf(this->fp, "%s:", lex.getSourceInfo()->getSourceName().c_str());
        if(lineNumOffset > 0) {
            unsigned int lineNum = lex.getSourceInfo()->getLineNum(token.pos);
            fprintf(this->fp, "%d:", lineNum);
        }
        fprintf(this->fp, " %s%s[%s]%s %s\n",
                this->color(c), this->color(TermColor::Bold), kind, this->color(TermColor::Reset), message);

        if(lineNumOffset > 0) {
            this->printErrorLine(lex, token);
        }
        fflush(this->fp);
    }

private:
    const char *color(TermColor c) const {
        if(this->tty) {
#define GEN_STR(E, C) "\033[" #C "m",
            const char *ansi[] = {
                    EACH_TERM_COLOR(GEN_STR)
            };
#undef GEN_STR
            return ansi[static_cast<unsigned int>(c)];
        }
        return "";
    }

    void printErrorLine(const Lexer &lexer, Token token) const {
        if(token.pos + token.size == 0) {
            return;
        }

        Token errorToken = lexer.shiftEOS(token);
        Token lineToken = lexer.getLineToken(errorToken);
        auto line = lexer.formatTokenText(lineToken);
        auto marker = lexer.formatLineMarker(lineToken, errorToken);

        auto lines = split(line);
        auto markers = split(marker);
        unsigned int size = lines.size();
        assert(size == markers.size());
        for(unsigned int i = 0; i < size; i++) {
            // print error line
            fprintf(this->fp, "%s%s%s\n", this->color(TermColor::Cyan), lines[i].c_str(), this->color(TermColor::Reset));

            // print line marker
            fprintf(this->fp, "%s%s%s%s\n", this->color(TermColor::Green), this->color(TermColor::Bold),
                    markers[i].c_str(), this->color(TermColor::Reset));
        }

        fflush(this->fp);
    }
};

DSError FrontEnd::handleError(DSErrorKind type, const char *errorKind,
                              Token errorToken, const std::string &message) const {
    if(!this->suppressError(errorKind)) {
        ErrorReporter stream(stderr);

        /**
         * show error message
         */
        stream(*this->parser.getLexer(),
                   type == DS_ERROR_KIND_PARSE_ERROR ? "syntax error" : "semantic error",
                   errorToken, TermColor::Magenta, message.c_str());

        for(int i = static_cast<int>(this->contexts.size()) - 1; i > -1; i--) {
            Token token = this->contexts[i].sourceNode->getPathNode()->getToken();
            auto &lex = i > 0 ? this->contexts[i - 1].lexer : this->lexer;
            stream(lex, "note", token, TermColor::Blue, "at module import");
        }
    }

    unsigned int errorLineNum = this->getSourceInfo()->getLineNum(errorToken.pos);
    const char *sourceName = this->getSourceInfo()->getSourceName().c_str();
    return {
            .kind = type,
            .fileName = strdup(sourceName),
            .lineNum = errorLineNum,
            .name = strdup(errorKind)
    };
}

std::unique_ptr<Node> FrontEnd::tryToParse(DSError *dsError) {
    std::unique_ptr<Node> node;
    if(this->parser) {
        node = this->parser();
        if(this->parser.hasError()) {
            auto e = this->handleParseError();
            if(dsError != nullptr) {
                *dsError = e;
            } else {
                DSError_release(&e);
            }
        }
        if(this->uastDumper) {
            this->uastDumper(*node);
        }
    }
    return node;
}

void FrontEnd::tryToCheckType(std::unique_ptr<Node> &node) {
    if(this->mode == DS_EXEC_MODE_PARSE_ONLY) {
        return;
    }

    node = this->checker(this->prevType, std::move(node));
    this->prevType = &node->getType();

    if(this->astDumper) {
        this->astDumper(*node);
    }
}

std::pair<std::unique_ptr<Node>, FrontEnd::Status> FrontEnd::operator()(DSError *dsError) {
    // parse
    auto node = this->tryToParse(dsError);
    if(this->parser.hasError()) {
        return {nullptr, IN_MODULE};
    }

    // typecheck
    try {
        auto s = this->tryToCheckModule(node);
        if(s != IN_MODULE) {
            return {nullptr, s};
        }

        this->tryToCheckType(node);
    } catch(const TypeCheckError &e) {
        auto ret = this->handleTypeError(e);
        if(dsError != nullptr) {
            *dsError = ret;
        } else {
            DSError_release(&ret);
        }
        return {nullptr, IN_MODULE};
    }

    auto s = IN_MODULE;
    if(node->is(NodeKind::Source) && static_cast<SourceNode&>(*node).isFirstAppear()) {
        s = EXIT_MODULE;
    }
    return {std::move(node), s};
}

void FrontEnd::setupASTDump() {
    if(this->uastDumper) {
        this->uastDumper.initialize(this->getSourceInfo()->getSourceName(), "### dump untyped AST ###");
    }
    if(this->mode != DS_EXEC_MODE_PARSE_ONLY && this->astDumper) {
        this->astDumper.initialize(this->getSourceInfo()->getSourceName(), "### dump typed AST ###");
    }
}

void FrontEnd::teardownASTDump() {
    if(this->uastDumper) {
        this->uastDumper.finalize();
    }
    if(this->mode != DS_EXEC_MODE_PARSE_ONLY && this->astDumper) {
        this->astDumper.finalize();
    }
}

FrontEnd::Status FrontEnd::tryToCheckModule(std::unique_ptr<Node> &node) {
    if(!node) {
        node = this->exitModule();
        return IN_MODULE;
    }

    if(!node->is(NodeKind::Source)) {
        return IN_MODULE;
    }

    auto &srcNode = static_cast<SourceNode&>(*node);
    const char *modPath = srcNode.getPathStr().c_str();
    FilePtr filePtr;
    auto ret = this->getSymbolTable().tryToLoadModule(this->getCurScriptDir().c_str(), modPath, filePtr);
    if(is<ModLoadingError>(ret)) {
        switch(get<ModLoadingError>(ret)) {
        case ModLoadingError::CIRCULAR:
            RAISE_TC_ERROR(CircularMod, *srcNode.getPathNode(), modPath);
        case ModLoadingError::NOT_OPEN:
            RAISE_TC_ERROR(NotOpenMod, *srcNode.getPathNode(), srcNode.getPathStr().c_str(), strerror(errno));
        case ModLoadingError::NOT_FOUND:
            RAISE_TC_ERROR(NotFoundMod, *srcNode.getPathNode(), srcNode.getPathStr().c_str());
        }
    } else if(is<const char *>(ret)) {
        ByteBuffer buf;
        if(!readAll(filePtr.get(), buf)) {
            RAISE_TC_ERROR(NotOpenMod, *srcNode.getPathNode(), srcNode.getPathStr().c_str(), strerror(errno));
        }
        this->enterModule(get<const char*>(ret), std::move(buf),
                          std::unique_ptr<SourceNode>(static_cast<SourceNode *>(node.release())));
        return ENTER_MODULE;
    } else if(is<ModType *>(ret)) {
        srcNode.setModType(*get<ModType *>(ret));
        return IN_MODULE;
    }
    return IN_MODULE;   // normally unreachable, due to suppress gcc warning
}

void FrontEnd::enterModule(const char *fullPath, ByteBuffer &&buf, std::unique_ptr<SourceNode> &&node) {
    {
        assert(*fullPath == '/');
        Lexer lex(fullPath, std::move(buf));
        node->setFirstAppear(true);
        auto state = this->parser.saveLexicalState();
        auto scope = this->getSymbolTable().createModuleScope();
        this->contexts.emplace_back(std::move(lex), std::move(scope), std::move(state), std::move(node));
    }
    Token token{};
    TokenKind kind = this->contexts.back().lexer.nextToken(token);
    TokenKind ckind{};
    this->parser.restoreLexicalState(this->contexts.back().lexer, kind, token, ckind);

    if(this->uastDumper) {
        this->uastDumper.enterModule(fullPath);
    }
    if(this->mode != DS_EXEC_MODE_PARSE_ONLY && this->astDumper) {
        this->astDumper.enterModule(fullPath);
    }
}

std::unique_ptr<SourceNode> FrontEnd::exitModule() {
    auto &ctx = this->contexts.back();
    TokenKind kind = ctx.kind;
    Token token = ctx.token;
    TokenKind consumedKind = ctx.consumedKind;
    std::unique_ptr<SourceNode> node(ctx.sourceNode.release());
    auto &modType = this->getSymbolTable().createModType(ctx.lexer.getSourceInfo()->getSourceName());
    auto scope = std::move(ctx.scope);
    this->contexts.pop_back();

    auto &lex = this->contexts.empty() ? this->lexer : this->contexts.back().lexer;
    this->parser.restoreLexicalState(lex, kind, token, consumedKind);
    if(this->contexts.empty()) {
        this->getSymbolTable().resetCurModule();
    } else {
        this->getSymbolTable().setModuleScope(*this->contexts.back().scope);
    }

    if(this->mode != DS_EXEC_MODE_PARSE_ONLY) {
        unsigned int varNum = scope->getMaxVarIndex();
        node->setMaxVarNum(varNum);
        node->setModType(modType);
        if(prevType != nullptr && this->prevType->isNothingType()) {
            this->prevType = &this->getSymbolTable().get(TYPE::Void);
            node->setNothing(true);
        }
    }

    if(this->uastDumper) {
        this->uastDumper.leaveModule();
    }
    if(this->mode != DS_EXEC_MODE_PARSE_ONLY && this->astDumper) {
        this->astDumper.leaveModule();
    }
    return node;
}

} // namespace ydsh
<|endoftext|>


<commit_before>/*
 * Copyright 2007-2018 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * 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.
 *
 * 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
 * FOUNDATION 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 "Lease.hxx"
#include "fb_pool.hxx"
#include "net/SocketProtocolError.hxx"

FilteredSocketLease::FilteredSocketLease(FilteredSocket &_socket, Lease &lease,
                                         const struct timeval *read_timeout,
                                         const struct timeval *write_timeout,
                                         BufferedSocketHandler &_handler) noexcept
    :socket(&_socket), handler(_handler)
{
    socket->Reinit(read_timeout, write_timeout, *this);
    lease_ref.Set(lease);
}

FilteredSocketLease::~FilteredSocketLease() noexcept
{
    assert(IsReleased());

    for (auto &i : input)
        i.FreeIfDefined(fb_pool_get());
}

void
FilteredSocketLease::Release(bool reuse) noexcept
{
    assert(!IsReleased());
    assert(!lease_ref.released);

    // TODO: move buffers instead of copying the data
    size_t i = 0;
    while (true) {
        auto r = WritableBuffer<uint8_t>::FromVoid(socket->ReadBuffer());
        if (r.empty())
            break;

        auto &dest = input[i];
        if (!dest.IsDefined())
            dest.Allocate(fb_pool_get());
        else if (dest.IsFull()) {
            ++i;
            assert(i < input.size());
            continue;
        }

        auto w = dest.Write();
        size_t n = std::min(r.size, w.size);
        assert(n > 0);
        std::move(r.data, r.data + n, w.data);
        socket->Consumed(n);
        dest.Append(n);
    }

    lease_ref.Release(reuse);
    socket = nullptr;
}

bool
FilteredSocketLease::IsEmpty() const noexcept
{
    if (IsReleased())
        return IsReleasedEmpty();
    else
        return socket->IsEmpty();
}

size_t
FilteredSocketLease::GetAvailable() const noexcept
{
    if (IsReleased()) {
        size_t result = 0;
        for (const auto &i : input)
            result += i.GetAvailable();
        return result;
    } else
        return socket->GetAvailable();
}

WritableBuffer<void>
FilteredSocketLease::ReadBuffer() const noexcept
{
    return IsReleased()
        ? input.front().Read().ToVoid()
        : socket->ReadBuffer();
}

void
FilteredSocketLease::Consumed(size_t nbytes) noexcept
{
    if (IsReleased()) {
        input.front().Consume(nbytes);
        MoveInput();
    } else
        socket->Consumed(nbytes);
}

bool
FilteredSocketLease::ReadReleased() noexcept
{
    while (!IsReleasedEmpty()) {
        switch (handler.OnBufferedData()) {
        case BufferedResult::OK:
            if (IsReleasedEmpty() && !handler.OnBufferedEnd())
                return false;
            break;

        case BufferedResult::BLOCKING:
            assert(!IsReleasedEmpty());
            return true;

        case BufferedResult::MORE:
        case BufferedResult::AGAIN_OPTIONAL:
        case BufferedResult::AGAIN_EXPECT:
            break;

        case BufferedResult::CLOSED:
            return false;
        }
    }

    return true;
}

bool
FilteredSocketLease::Read(bool expect_more) noexcept
{
    if (IsReleased())
        return ReadReleased();
    else
        return socket->Read(expect_more);
}

void
FilteredSocketLease::MoveInput() noexcept
{
    auto &dest = input.front();
    for (size_t i = 1; !dest.IsFull() && i < input.size(); ++i) {
        auto &src = input[i];
        dest.MoveFromAllowBothNull(src);
        src.FreeIfEmpty(fb_pool_get());
    }
}

BufferedResult
FilteredSocketLease::OnBufferedData()
{
    while (true) {
        const auto result = handler.OnBufferedData();
        if (result == BufferedResult::CLOSED)
            break;

        if (!IsReleased())
            return result;

        /* since the BufferedSocket is gone already, we must handle
           the AGAIN result codes here */

        if (result == BufferedResult::AGAIN_OPTIONAL && !IsEmpty())
            continue;
        else if (result == BufferedResult::AGAIN_EXPECT) {
            if (IsEmpty()) {
                handler.OnBufferedError(std::make_exception_ptr(SocketClosedPrematurelyError()));
                break;
            }

            continue;
        } else
            break;
    }

    /* if the socket has been released, we must always report CLOSED
       to the released BufferedSocket instance, even if our handler
       still wants to consume the remaining buffer */
    return BufferedResult::CLOSED;
}

DirectResult
FilteredSocketLease::OnBufferedDirect(SocketDescriptor fd, FdType fd_type)
{
    return handler.OnBufferedDirect(fd, fd_type);
}

bool
FilteredSocketLease::OnBufferedClosed() noexcept
{
    auto result = handler.OnBufferedClosed();
    if (result && IsReleased()) {
        result = false;

        if (handler.OnBufferedRemaining(GetAvailable()) &&
            IsEmpty() &&
            !handler.OnBufferedEnd())
            handler.OnBufferedError(std::make_exception_ptr(SocketClosedPrematurelyError()));
    }

    return result;
}

bool
FilteredSocketLease::OnBufferedRemaining(size_t remaining) noexcept
{
    auto result = handler.OnBufferedRemaining(remaining);
    if (result && IsReleased())
        result = false;
    return result;
}

bool
FilteredSocketLease::OnBufferedEnd() noexcept
{
    return handler.OnBufferedEnd();
}

bool
FilteredSocketLease::OnBufferedWrite()
{
    return handler.OnBufferedWrite();
}

bool
FilteredSocketLease::OnBufferedDrained() noexcept
{
    return handler.OnBufferedDrained();
}

bool
FilteredSocketLease::OnBufferedTimeout() noexcept
{
    return handler.OnBufferedTimeout();
}

enum write_result
FilteredSocketLease::OnBufferedBroken() noexcept
{
    return handler.OnBufferedBroken();
}

void
FilteredSocketLease::OnBufferedError(std::exception_ptr e) noexcept
{
    return handler.OnBufferedError(e);
}
<commit_msg>fs/Lease: use IsReleasedEmpty() once more<commit_after>/*
 * Copyright 2007-2018 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * 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.
 *
 * 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
 * FOUNDATION 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 "Lease.hxx"
#include "fb_pool.hxx"
#include "net/SocketProtocolError.hxx"

FilteredSocketLease::FilteredSocketLease(FilteredSocket &_socket, Lease &lease,
                                         const struct timeval *read_timeout,
                                         const struct timeval *write_timeout,
                                         BufferedSocketHandler &_handler) noexcept
    :socket(&_socket), handler(_handler)
{
    socket->Reinit(read_timeout, write_timeout, *this);
    lease_ref.Set(lease);
}

FilteredSocketLease::~FilteredSocketLease() noexcept
{
    assert(IsReleased());

    for (auto &i : input)
        i.FreeIfDefined(fb_pool_get());
}

void
FilteredSocketLease::Release(bool reuse) noexcept
{
    assert(!IsReleased());
    assert(!lease_ref.released);

    // TODO: move buffers instead of copying the data
    size_t i = 0;
    while (true) {
        auto r = WritableBuffer<uint8_t>::FromVoid(socket->ReadBuffer());
        if (r.empty())
            break;

        auto &dest = input[i];
        if (!dest.IsDefined())
            dest.Allocate(fb_pool_get());
        else if (dest.IsFull()) {
            ++i;
            assert(i < input.size());
            continue;
        }

        auto w = dest.Write();
        size_t n = std::min(r.size, w.size);
        assert(n > 0);
        std::move(r.data, r.data + n, w.data);
        socket->Consumed(n);
        dest.Append(n);
    }

    lease_ref.Release(reuse);
    socket = nullptr;
}

bool
FilteredSocketLease::IsEmpty() const noexcept
{
    if (IsReleased())
        return IsReleasedEmpty();
    else
        return socket->IsEmpty();
}

size_t
FilteredSocketLease::GetAvailable() const noexcept
{
    if (IsReleased()) {
        size_t result = 0;
        for (const auto &i : input)
            result += i.GetAvailable();
        return result;
    } else
        return socket->GetAvailable();
}

WritableBuffer<void>
FilteredSocketLease::ReadBuffer() const noexcept
{
    return IsReleased()
        ? input.front().Read().ToVoid()
        : socket->ReadBuffer();
}

void
FilteredSocketLease::Consumed(size_t nbytes) noexcept
{
    if (IsReleased()) {
        input.front().Consume(nbytes);
        MoveInput();
    } else
        socket->Consumed(nbytes);
}

bool
FilteredSocketLease::ReadReleased() noexcept
{
    while (!IsReleasedEmpty()) {
        switch (handler.OnBufferedData()) {
        case BufferedResult::OK:
            if (IsReleasedEmpty() && !handler.OnBufferedEnd())
                return false;
            break;

        case BufferedResult::BLOCKING:
            assert(!IsReleasedEmpty());
            return true;

        case BufferedResult::MORE:
        case BufferedResult::AGAIN_OPTIONAL:
        case BufferedResult::AGAIN_EXPECT:
            break;

        case BufferedResult::CLOSED:
            return false;
        }
    }

    return true;
}

bool
FilteredSocketLease::Read(bool expect_more) noexcept
{
    if (IsReleased())
        return ReadReleased();
    else
        return socket->Read(expect_more);
}

void
FilteredSocketLease::MoveInput() noexcept
{
    auto &dest = input.front();
    for (size_t i = 1; !dest.IsFull() && i < input.size(); ++i) {
        auto &src = input[i];
        dest.MoveFromAllowBothNull(src);
        src.FreeIfEmpty(fb_pool_get());
    }
}

BufferedResult
FilteredSocketLease::OnBufferedData()
{
    while (true) {
        const auto result = handler.OnBufferedData();
        if (result == BufferedResult::CLOSED)
            break;

        if (!IsReleased())
            return result;

        /* since the BufferedSocket is gone already, we must handle
           the AGAIN result codes here */

        if (result == BufferedResult::AGAIN_OPTIONAL && !IsEmpty())
            continue;
        else if (result == BufferedResult::AGAIN_EXPECT) {
            if (IsEmpty()) {
                handler.OnBufferedError(std::make_exception_ptr(SocketClosedPrematurelyError()));
                break;
            }

            continue;
        } else
            break;
    }

    /* if the socket has been released, we must always report CLOSED
       to the released BufferedSocket instance, even if our handler
       still wants to consume the remaining buffer */
    return BufferedResult::CLOSED;
}

DirectResult
FilteredSocketLease::OnBufferedDirect(SocketDescriptor fd, FdType fd_type)
{
    return handler.OnBufferedDirect(fd, fd_type);
}

bool
FilteredSocketLease::OnBufferedClosed() noexcept
{
    auto result = handler.OnBufferedClosed();
    if (result && IsReleased()) {
        result = false;

        if (handler.OnBufferedRemaining(GetAvailable()) &&
            IsReleasedEmpty() &&
            !handler.OnBufferedEnd())
            handler.OnBufferedError(std::make_exception_ptr(SocketClosedPrematurelyError()));
    }

    return result;
}

bool
FilteredSocketLease::OnBufferedRemaining(size_t remaining) noexcept
{
    auto result = handler.OnBufferedRemaining(remaining);
    if (result && IsReleased())
        result = false;
    return result;
}

bool
FilteredSocketLease::OnBufferedEnd() noexcept
{
    return handler.OnBufferedEnd();
}

bool
FilteredSocketLease::OnBufferedWrite()
{
    return handler.OnBufferedWrite();
}

bool
FilteredSocketLease::OnBufferedDrained() noexcept
{
    return handler.OnBufferedDrained();
}

bool
FilteredSocketLease::OnBufferedTimeout() noexcept
{
    return handler.OnBufferedTimeout();
}

enum write_result
FilteredSocketLease::OnBufferedBroken() noexcept
{
    return handler.OnBufferedBroken();
}

void
FilteredSocketLease::OnBufferedError(std::exception_ptr e) noexcept
{
    return handler.OnBufferedError(e);
}
<|endoftext|>


<commit_before>#pragma once

#include "core/enum.hh"
#include "bytes.hh"
#include "sstables/key.hh"

namespace sstables {

// Some in-disk structures have an associated integer (of varying sizes) that
// represents how large they are. They can be a byte-length, in the case of a
// string, number of elements, in the case of an array, etc.
//
// For those elements, we encapsulate the underlying type in an outter
// structure that embeds how large is the in-disk size. It is a lot more
// convenient to embed it in the size than explicitly writing it in the parser.
// This way, we don't need to encode this information in multiple places at
// once - it is already part of the type.
template <typename Size>
struct disk_string {
    bytes value;
};

template <typename Size, typename Members>
struct disk_array {
    static_assert(std::is_integral<Size>::value, "Length type must be convertible to integer");
    std::vector<Members> elements;
};

template <typename Size, typename Key, typename Value>
struct disk_hash {
    std::unordered_map<Key, Value, std::hash<Key>> map;
};

struct option {
    disk_string<uint16_t> key;
    disk_string<uint16_t> value;

    template <typename Describer>
    future<> describe_type(Describer f) { return f(key, value); }
};

struct filter {
    uint32_t hashes;
    disk_array<uint32_t, uint64_t> buckets;

    template <typename Describer>
    future<> describe_type(Describer f) { return f(hashes, buckets); }
};

struct index_entry {
    disk_string<uint16_t> key;
    uint64_t position;
    disk_string<uint32_t> promoted_index;
};

struct summary_entry {
    bytes key;
    uint64_t position;

    bool operator==(const summary_entry& x) const {
        return position ==  x.position && key == x.key;
    }

    template <typename Describer>
    future<> describe_type(Describer f) { return f(key, position); }
};

// Note: Sampling level is present in versions ka and higher. We ATM only support la,
// so it's always there. But we need to make this conditional if we ever want to support
// other formats (unlikely)
struct summary_la {
    struct header {
        // The minimum possible amount of indexes per group (sampling level)
        uint32_t min_index_interval;
        // The number of entries in the Summary File
        uint32_t size;
        // The memory to be consumed to map the whole Summary into memory.
        uint64_t memory_size;
        // The actual sampling level.
        uint32_t sampling_level;
        // The number of entries the Summary *would* have if the sampling
        // level would be equal to min_index_interval.
        uint32_t size_at_full_sampling;
    } header;
    // The position in the Summary file for each of the indexes.
    // NOTE1 that its actual size is determined by the "size" parameter, not
    // by its preceding size_at_full_sampling
    // NOTE2: They are laid out in *MEMORY* order, not BE.
    // NOTE3: The sizes in this array represent positions in the memory stream,
    // not the file. The memory stream effectively begins after the header,
    // so every position here has to be added of sizeof(header).
    std::vector<uint32_t> positions;
    std::vector<summary_entry> entries;

    disk_string<uint32_t> first_key;
    disk_string<uint32_t> last_key;

    // NOTE4: There is a structure written by Cassandra into the end of the Summary
    // file, after the field last_key, that we haven't understand yet, but we know
    // that its content isn't related to the summary itself.
    // The structure is basically as follow:
    // struct { disk_string<uint16_t>; uint32_t; uint64_t; disk_string<uint16_t>; }
    // Another interesting fact about this structure is that it is apparently always
    // filled with the same data. It's too early to judge that the data is useless.
    // However, it was tested that Cassandra loads successfully a Summary file with
    // this structure removed from it. Anyway, let's pay attention to it.
};
using summary = summary_la;

struct estimated_histogram {
    struct eh_elem {
        uint64_t offset;
        uint64_t bucket;

        template <typename Describer>
        future<> describe_type(Describer f) { return f(offset, bucket); }
    };

    disk_array<uint32_t, eh_elem> elements;

    template <typename Describer>
    future<> describe_type(Describer f) { return f(elements); }
};

struct replay_position {
    uint64_t segment;
    uint32_t position;

    template <typename Describer>
    future<> describe_type(Describer f) { return f(segment, position); }
};

struct streaming_histogram {
    uint32_t max_bin_size;
    disk_hash<uint32_t, double, uint64_t> hash;

    template <typename Describer>
    future<> describe_type(Describer f) { return f(max_bin_size, hash); }
};

struct metadata {
};

struct validation_metadata : public metadata {
    disk_string<uint16_t> partitioner;
    double filter_chance;

    template <typename Describer>
    future<> describe_type(Describer f) { return f(partitioner, filter_chance); }
};

struct compaction_metadata : public metadata {
    disk_array<uint32_t, uint32_t> ancestors;
    disk_array<uint32_t, uint8_t> cardinality;

    template <typename Describer>
    future<> describe_type(Describer f) { return f(ancestors, cardinality); }
};

struct la_stats_metadata : public metadata {
    estimated_histogram estimated_row_size;
    estimated_histogram estimated_column_count;
    replay_position position;
    uint64_t min_timestamp;
    uint64_t max_timestamp;
    uint32_t max_local_deletion_time;
    double compression_ratio;
    streaming_histogram estimated_tombstone_drop_time;
    uint32_t sstable_level;
    uint64_t repaired_at;
    disk_array<uint32_t, disk_string<uint16_t>> min_column_names;
    disk_array<uint32_t, disk_string<uint16_t>> max_column_names;
    bool has_legacy_counter_shards;

    template <typename Describer>
    future<> describe_type(Describer f) {
        return f(
            estimated_row_size,
            estimated_column_count,
            position,
            min_timestamp,
            max_timestamp,
            max_local_deletion_time,
            compression_ratio,
            estimated_tombstone_drop_time,
            sstable_level,
            repaired_at,
            min_column_names,
            max_column_names,
            has_legacy_counter_shards
        );
    }
};
using stats_metadata = la_stats_metadata;

// Numbers are found on disk, so they do matter. Also, setting their sizes of
// that of an uint32_t is a bit wasteful, but it simplifies the code a lot
// since we can now still use a strongly typed enum without introducing a
// notion of "disk-size" vs "memory-size".
enum class metadata_type : uint32_t {
    Validation = 0,
    Compaction = 1,
    Stats = 2,
};

}

namespace std {

template <>
struct hash<sstables::metadata_type> : enum_hash<sstables::metadata_type> {};

}

namespace sstables {

struct statistics {
    disk_hash<uint32_t, metadata_type, uint32_t> hash;
    std::unordered_map<metadata_type, std::unique_ptr<metadata>> contents;
};

struct deletion_time {
    uint32_t local_deletion_time;
    uint64_t marked_for_delete_at;
};

enum class column_mask : uint8_t {
    none = 0x0,
    deletion = 0x01,
    expiration = 0x02,
    counter = 0x04,
    counter_update = 0x08,
    range_tombstone = 0x10,
};

inline column_mask operator&(column_mask m1, column_mask m2) {
    return column_mask(static_cast<uint8_t>(m1) & static_cast<uint8_t>(m2));
}

inline column_mask operator|(column_mask m1, column_mask m2) {
    return column_mask(static_cast<uint8_t>(m1) | static_cast<uint8_t>(m2));
}
}

<commit_msg>sstable: convenient view for types<commit_after>#pragma once

#include "core/enum.hh"
#include "bytes.hh"
#include "sstables/key.hh"

namespace sstables {

// Some in-disk structures have an associated integer (of varying sizes) that
// represents how large they are. They can be a byte-length, in the case of a
// string, number of elements, in the case of an array, etc.
//
// For those elements, we encapsulate the underlying type in an outter
// structure that embeds how large is the in-disk size. It is a lot more
// convenient to embed it in the size than explicitly writing it in the parser.
// This way, we don't need to encode this information in multiple places at
// once - it is already part of the type.
template <typename Size>
struct disk_string {
    bytes value;
    explicit operator bytes_view() const {
        return value;
    }
};

template <typename Size, typename Members>
struct disk_array {
    static_assert(std::is_integral<Size>::value, "Length type must be convertible to integer");
    std::vector<Members> elements;
};

template <typename Size, typename Key, typename Value>
struct disk_hash {
    std::unordered_map<Key, Value, std::hash<Key>> map;
};

struct option {
    disk_string<uint16_t> key;
    disk_string<uint16_t> value;

    template <typename Describer>
    future<> describe_type(Describer f) { return f(key, value); }
};

struct filter {
    uint32_t hashes;
    disk_array<uint32_t, uint64_t> buckets;

    template <typename Describer>
    future<> describe_type(Describer f) { return f(hashes, buckets); }
};

struct index_entry {
    disk_string<uint16_t> key;
    uint64_t position;
    disk_string<uint32_t> promoted_index;

    explicit operator bytes_view() const {
        return bytes_view(key);
    }
};

struct summary_entry {
    bytes key;
    uint64_t position;

    explicit operator bytes_view() const {
        return key;
    }

    bool operator==(const summary_entry& x) const {
        return position ==  x.position && key == x.key;
    }

    template <typename Describer>
    future<> describe_type(Describer f) { return f(key, position); }
};

// Note: Sampling level is present in versions ka and higher. We ATM only support la,
// so it's always there. But we need to make this conditional if we ever want to support
// other formats (unlikely)
struct summary_la {
    struct header {
        // The minimum possible amount of indexes per group (sampling level)
        uint32_t min_index_interval;
        // The number of entries in the Summary File
        uint32_t size;
        // The memory to be consumed to map the whole Summary into memory.
        uint64_t memory_size;
        // The actual sampling level.
        uint32_t sampling_level;
        // The number of entries the Summary *would* have if the sampling
        // level would be equal to min_index_interval.
        uint32_t size_at_full_sampling;
    } header;
    // The position in the Summary file for each of the indexes.
    // NOTE1 that its actual size is determined by the "size" parameter, not
    // by its preceding size_at_full_sampling
    // NOTE2: They are laid out in *MEMORY* order, not BE.
    // NOTE3: The sizes in this array represent positions in the memory stream,
    // not the file. The memory stream effectively begins after the header,
    // so every position here has to be added of sizeof(header).
    std::vector<uint32_t> positions;
    std::vector<summary_entry> entries;

    disk_string<uint32_t> first_key;
    disk_string<uint32_t> last_key;

    // NOTE4: There is a structure written by Cassandra into the end of the Summary
    // file, after the field last_key, that we haven't understand yet, but we know
    // that its content isn't related to the summary itself.
    // The structure is basically as follow:
    // struct { disk_string<uint16_t>; uint32_t; uint64_t; disk_string<uint16_t>; }
    // Another interesting fact about this structure is that it is apparently always
    // filled with the same data. It's too early to judge that the data is useless.
    // However, it was tested that Cassandra loads successfully a Summary file with
    // this structure removed from it. Anyway, let's pay attention to it.
};
using summary = summary_la;

struct estimated_histogram {
    struct eh_elem {
        uint64_t offset;
        uint64_t bucket;

        template <typename Describer>
        future<> describe_type(Describer f) { return f(offset, bucket); }
    };

    disk_array<uint32_t, eh_elem> elements;

    template <typename Describer>
    future<> describe_type(Describer f) { return f(elements); }
};

struct replay_position {
    uint64_t segment;
    uint32_t position;

    template <typename Describer>
    future<> describe_type(Describer f) { return f(segment, position); }
};

struct streaming_histogram {
    uint32_t max_bin_size;
    disk_hash<uint32_t, double, uint64_t> hash;

    template <typename Describer>
    future<> describe_type(Describer f) { return f(max_bin_size, hash); }
};

struct metadata {
};

struct validation_metadata : public metadata {
    disk_string<uint16_t> partitioner;
    double filter_chance;

    template <typename Describer>
    future<> describe_type(Describer f) { return f(partitioner, filter_chance); }
};

struct compaction_metadata : public metadata {
    disk_array<uint32_t, uint32_t> ancestors;
    disk_array<uint32_t, uint8_t> cardinality;

    template <typename Describer>
    future<> describe_type(Describer f) { return f(ancestors, cardinality); }
};

struct la_stats_metadata : public metadata {
    estimated_histogram estimated_row_size;
    estimated_histogram estimated_column_count;
    replay_position position;
    uint64_t min_timestamp;
    uint64_t max_timestamp;
    uint32_t max_local_deletion_time;
    double compression_ratio;
    streaming_histogram estimated_tombstone_drop_time;
    uint32_t sstable_level;
    uint64_t repaired_at;
    disk_array<uint32_t, disk_string<uint16_t>> min_column_names;
    disk_array<uint32_t, disk_string<uint16_t>> max_column_names;
    bool has_legacy_counter_shards;

    template <typename Describer>
    future<> describe_type(Describer f) {
        return f(
            estimated_row_size,
            estimated_column_count,
            position,
            min_timestamp,
            max_timestamp,
            max_local_deletion_time,
            compression_ratio,
            estimated_tombstone_drop_time,
            sstable_level,
            repaired_at,
            min_column_names,
            max_column_names,
            has_legacy_counter_shards
        );
    }
};
using stats_metadata = la_stats_metadata;

// Numbers are found on disk, so they do matter. Also, setting their sizes of
// that of an uint32_t is a bit wasteful, but it simplifies the code a lot
// since we can now still use a strongly typed enum without introducing a
// notion of "disk-size" vs "memory-size".
enum class metadata_type : uint32_t {
    Validation = 0,
    Compaction = 1,
    Stats = 2,
};

}

namespace std {

template <>
struct hash<sstables::metadata_type> : enum_hash<sstables::metadata_type> {};

}

namespace sstables {

struct statistics {
    disk_hash<uint32_t, metadata_type, uint32_t> hash;
    std::unordered_map<metadata_type, std::unique_ptr<metadata>> contents;
};

struct deletion_time {
    uint32_t local_deletion_time;
    uint64_t marked_for_delete_at;
};

enum class column_mask : uint8_t {
    none = 0x0,
    deletion = 0x01,
    expiration = 0x02,
    counter = 0x04,
    counter_update = 0x08,
    range_tombstone = 0x10,
};

inline column_mask operator&(column_mask m1, column_mask m2) {
    return column_mask(static_cast<uint8_t>(m1) & static_cast<uint8_t>(m2));
}

inline column_mask operator|(column_mask m1, column_mask m2) {
    return column_mask(static_cast<uint8_t>(m1) | static_cast<uint8_t>(m2));
}
}

<|endoftext|>


<commit_before>#include "gl_setup.hpp"
#include "scan_utilities.hpp"

#include "util.hpp"
#include <glsl_algo/prefix_scan.h>
#include <benchmark/benchmark.h>

static void BM_FullScan(benchmark::State &state) {
    init_window_and_gl_context();

    glsl_algo_configuration conf = {GARWTint1, 1024, 32};
    glsl_algo_context ctx = glsl_algo_init(conf);

    const unsigned numBlocks = 1024;
    const unsigned n = 1024 * numBlocks;
    std::vector<unsigned> vec = generateIntegralRandomVector(n, 0u, 3u);
    GLuint inputBuffer = create_ssbo(n, vec.data());
    GLuint intermediateBuffer = create_ssbo(numBlocks);
    GLuint outputBuffer = create_ssbo(n);

    GLuint queryObject;
    glGenQueries(1, &queryObject);

    while (state.KeepRunning())
    {
        BENCHMARK_GPU(glsl_scan(&ctx, inputBuffer, intermediateBuffer, outputBuffer, n, 1, 1), queryObject);
    }

    destroy_window_and_gl_context();
}

static void BM_FullScanRW(benchmark::State &state)
{
    init_window_and_gl_context();

    const unsigned numBlocks = 1024;
    const unsigned n = 16 * 1024 * numBlocks;
    std::vector<unsigned> vec = generateIntegralRandomVector(n, 0u, 3u);
    GLuint inputBuffer = create_ssbo(n, vec.data());
    GLuint intermediateBuffer = create_ssbo(numBlocks);
    GLuint outputBuffer = create_ssbo(n);

    GLuint queryObject;
    glGenQueries(1, &queryObject);

    while (state.KeepRunning())
    {

        GLSL_ALGO_READ_WRITE_TYPE dataType = static_cast<GLSL_ALGO_READ_WRITE_TYPE>(state.range(0));

        glsl_algo_configuration conf = {dataType, 1024, 32};
        glsl_algo_context ctx = glsl_algo_init(conf);

        BENCHMARK_GPU(glsl_scan(&ctx, inputBuffer, intermediateBuffer, outputBuffer, n, 1, 1), queryObject);
    }
    destroy_window_and_gl_context();
}

static void BM_FullScanBlockSize(benchmark::State &state)
{
    init_window_and_gl_context();

    const unsigned numBlocks = 1024;
    const unsigned n = 16 * 1024 * numBlocks;
    std::vector<unsigned> vec = generateIntegralRandomVector(n, 0u, 3u);
    GLuint inputBuffer = create_ssbo(n, vec.data());
    GLuint intermediateBuffer = create_ssbo(numBlocks);
    GLuint outputBuffer = create_ssbo(n);

    GLuint queryObject;
    glGenQueries(1, &queryObject);

    while (state.KeepRunning())
    {
        unsigned int blockSize = static_cast<unsigned int>(state.range(0));
        glsl_algo_configuration conf = {GARWTint1, blockSize, 32};
        glsl_algo_context ctx = glsl_algo_init(conf);

        BENCHMARK_GPU(glsl_scan(&ctx, inputBuffer, intermediateBuffer, outputBuffer, n, 1, 1), queryObject);
    }
    destroy_window_and_gl_context();
}

static void BM_FullScanElementsPerThread(benchmark::State &state) {
    init_window_and_gl_context();

    glsl_algo_configuration conf = {GARWTint1, 1024, 32};
    glsl_algo_context ctx = glsl_algo_init(conf);

    const unsigned numBlocks = 16 * 1024;
    const unsigned n = 1024 * numBlocks;
    std::vector<unsigned> vec = generateIntegralRandomVector(n, 0u, 3u);
    GLuint inputBuffer = create_ssbo(n, vec.data());
    GLuint intermediateBuffer = create_ssbo(numBlocks);
    GLuint outputBuffer = create_ssbo(n);

    GLuint queryObject;
    glGenQueries(1, &queryObject);

    while (state.KeepRunning())
    {
        unsigned int elementsPerThread = static_cast<unsigned int>(state.range(0));
        BENCHMARK_GPU(glsl_scan(&ctx, inputBuffer, intermediateBuffer, outputBuffer, n, elementsPerThread, 1), queryObject);
    }

    destroy_window_and_gl_context();
}

BENCHMARK(BM_FullScan);
BENCHMARK(BM_FullScanRW)->Arg(GARWTint1)->Arg(GARWTint2)->Arg(GARWTint4);
BENCHMARK(BM_FullScanBlockSize)->Arg(128)->Arg(256)->Arg(384)->Arg(512)->Arg(768)->Arg(1024);
BENCHMARK(BM_FullScanElementsPerThread)->Arg(1)->Arg(2)->Arg(4)->Arg(8)->Arg(16)->Arg(32);
<commit_msg>Add benchmark with multiple parameters<commit_after>#include "gl_setup.hpp"
#include "scan_utilities.hpp"

#include "util.hpp"
#include <glsl_algo/prefix_scan.h>
#include <benchmark/benchmark.h>

static void BM_FullScan(benchmark::State &state) {
    init_window_and_gl_context();

    glsl_algo_configuration conf = {GARWTint1, 1024, 32};
    glsl_algo_context ctx = glsl_algo_init(conf);

    const unsigned numBlocks = 1024;
    const unsigned n = 1024 * numBlocks;
    std::vector<unsigned> vec = generateIntegralRandomVector(n, 0u, 3u);
    GLuint inputBuffer = create_ssbo(n, vec.data());
    GLuint intermediateBuffer = create_ssbo(numBlocks);
    GLuint outputBuffer = create_ssbo(n);

    GLuint queryObject;
    glGenQueries(1, &queryObject);

    while (state.KeepRunning())
    {
        BENCHMARK_GPU(glsl_scan(&ctx, inputBuffer, intermediateBuffer, outputBuffer, n, 1, 1), queryObject);
    }

    destroy_window_and_gl_context();
}

static void BM_FullScanRW(benchmark::State &state)
{
    init_window_and_gl_context();

    const unsigned numBlocks = 1024;
    const unsigned n = 16 * 1024 * numBlocks;
    std::vector<unsigned> vec = generateIntegralRandomVector(n, 0u, 3u);
    GLuint inputBuffer = create_ssbo(n, vec.data());
    GLuint intermediateBuffer = create_ssbo(numBlocks);
    GLuint outputBuffer = create_ssbo(n);

    GLuint queryObject;
    glGenQueries(1, &queryObject);

    while (state.KeepRunning())
    {

        GLSL_ALGO_READ_WRITE_TYPE dataType = static_cast<GLSL_ALGO_READ_WRITE_TYPE>(state.range(0));

        glsl_algo_configuration conf = {dataType, 1024, 32};
        glsl_algo_context ctx = glsl_algo_init(conf);

        BENCHMARK_GPU(glsl_scan(&ctx, inputBuffer, intermediateBuffer, outputBuffer, n, 1, 1), queryObject);
    }
    destroy_window_and_gl_context();
}

static void BM_FullScanBlockSize(benchmark::State &state)
{
    init_window_and_gl_context();

    const unsigned numBlocks = 1024 * 128;
    const unsigned n = 16 * 1024 * 1024;
    std::vector<unsigned> vec = generateIntegralRandomVector(n, 0u, 3u);
    GLuint inputBuffer = create_ssbo(n, vec.data());
    GLuint intermediateBuffer = create_ssbo(numBlocks);
    GLuint outputBuffer = create_ssbo(n);

    GLuint queryObject;
    glGenQueries(1, &queryObject);

    while (state.KeepRunning())
    {
        unsigned int blockSize = static_cast<unsigned int>(state.range(0));
        glsl_algo_configuration conf = {GARWTint1, blockSize, 32};
        glsl_algo_context ctx = glsl_algo_init(conf);

        BENCHMARK_GPU(glsl_scan(&ctx, inputBuffer, intermediateBuffer, outputBuffer, n, 1, 1), queryObject);
    }
    destroy_window_and_gl_context();
}

static void BM_FullScanElementsPerThread(benchmark::State &state) {
    init_window_and_gl_context();

    glsl_algo_configuration conf = {GARWTint1, 1024, 32};
    glsl_algo_context ctx = glsl_algo_init(conf);

    const unsigned numBlocks = 128 * 1024;
    const unsigned n = 1024 * 1024 * 16;
    std::vector<unsigned> vec = generateIntegralRandomVector(n, 0u, 3u);
    GLuint inputBuffer = create_ssbo(n, vec.data());
    GLuint intermediateBuffer = create_ssbo(numBlocks);
    GLuint outputBuffer = create_ssbo(n);

    GLuint queryObject;
    glGenQueries(1, &queryObject);

    while (state.KeepRunning())
    {
        unsigned int elementsPerThread = static_cast<unsigned int>(state.range(0));
        BENCHMARK_GPU(glsl_scan(&ctx, inputBuffer, intermediateBuffer, outputBuffer, n, elementsPerThread, 1), queryObject);
    }

    destroy_window_and_gl_context();
}

static void BM_FullScanMultipleRanges(benchmark::State &state)
{
    init_window_and_gl_context();

    const unsigned n = 64 * 1024 * 1024;
    std::vector<unsigned> vec = generateIntegralRandomVector(n, 0u, 3u);
    GLuint inputBuffer = create_ssbo(n, vec.data());
    GLuint intermediateBuffer = create_ssbo(1024*128);
    GLuint outputBuffer = create_ssbo(n);

    GLuint queryObject;
    glGenQueries(1, &queryObject);

    while (state.KeepRunning())
    {
        GLSL_ALGO_READ_WRITE_TYPE type = static_cast<GLSL_ALGO_READ_WRITE_TYPE>(state.range(0));
        unsigned int blockSize = static_cast<unsigned int>(state.range(1));
        unsigned int readsPerThread = static_cast<unsigned int>(state.range(2));
        glsl_algo_configuration conf = {type, blockSize, 32};
        glsl_algo_context ctx = glsl_algo_init(conf);

        BENCHMARK_GPU(glsl_scan(&ctx, inputBuffer, intermediateBuffer, outputBuffer, n, readsPerThread, 1), queryObject);
    }
    destroy_window_and_gl_context();
}

BENCHMARK(BM_FullScan);
BENCHMARK(BM_FullScanRW)->Arg(GARWTint1)->Arg(GARWTint2)->Arg(GARWTint4);
BENCHMARK(BM_FullScanBlockSize)->Arg(128)->Arg(256)->Arg(384)->Arg(512)->Arg(768)->Arg(1024);
BENCHMARK(BM_FullScanElementsPerThread)->Arg(1)->Arg(2)->Arg(4)->Arg(8)->Arg(16)->Arg(32);

static void GenerateFullBenchmark(benchmark::internal::Benchmark* b) {
    GLSL_ALGO_READ_WRITE_TYPE rwTypes[] = {GARWTint1, GARWTint2, GARWTint4};

    for (unsigned int t = 0u; t < 3u; ++t)
    {
        GLSL_ALGO_READ_WRITE_TYPE type = rwTypes[t];
        for (unsigned int blockSize = 128u; blockSize <= 1024u; blockSize += 128u)
        {
            for (unsigned int readsPerThread = 1u; readsPerThread <= 32u; readsPerThread*=2)
            {
                b->Args({static_cast<int>(type), static_cast<int>(blockSize), static_cast<int>(readsPerThread)});
            }
        }
    }
}

BENCHMARK(BM_FullScanMultipleRanges)->Apply(GenerateFullBenchmark);<|endoftext|>


<commit_before>/*
 * generate.cpp
 *
 *  Created on: Nov 20, 2014
 *      Author: matheus
 */

#include <cstdio>

#include "ConcreteGraph.hpp"
#include "Helpers.hpp"

void generate(int argc, char** argv) {
  int order, max_weight; float edge_prob;
  scanf("%d %d %f", &order, &max_weight, &edge_prob);
  graph::IntArrayGraphMapNeighbourHood G;
  generateGraph(G, order, max_weight, edge_prob);
  printDirectedGraph(G, stdout);
}
<commit_msg>Adding source vertex in generate program<commit_after>/*
 * generate.cpp
 *
 *  Created on: Nov 20, 2014
 *      Author: matheus
 */

#include <cstdio>

#include "ConcreteGraph.hpp"
#include "Helpers.hpp"

void generate(int argc, char** argv) {
  int order, max_weight; float edge_prob; int source;
  scanf("%d %d %f %d", &order, &max_weight, &edge_prob, &source);
  graph::IntArrayGraphMapNeighbourHood G;
  generateGraph(G, order, max_weight, edge_prob);
  printDirectedGraph(G, stdout);
  printf("%d\n", source);
}
<|endoftext|>


<commit_before>/*

Copyright (c) 2003, 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"

#ifdef _WIN32
// windows part
#include "libtorrent/utf8.hpp"

#include <io.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>

#ifndef _MODE_T_
typedef int mode_t;
#endif

#ifdef UNICODE
#include "libtorrent/storage.hpp"
#endif

#else
// unix part
#define _FILE_OFFSET_BITS 64
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>

#include <boost/static_assert.hpp>
// make sure the _FILE_OFFSET_BITS define worked
// on this platform
BOOST_STATIC_ASSERT(sizeof(lseek(0, 0, 0)) >= 8);

#endif

#include <boost/filesystem/operations.hpp>
#include "libtorrent/file.hpp"
#include <sstream>

#ifndef O_BINARY
#define O_BINARY 0
#endif

#ifndef O_RANDOM
#define O_RANDOM 0
#endif

#ifdef UNICODE
#include "libtorrent/storage.hpp"
#endif

namespace fs = boost::filesystem;

namespace
{
	enum { mode_in = 1, mode_out = 2 };

	mode_t map_open_mode(int m)
	{
		if (m == (mode_in | mode_out)) return O_RDWR | O_CREAT | O_BINARY | O_RANDOM;
		if (m == mode_out) return O_WRONLY | O_CREAT | O_BINARY | O_RANDOM;
		if (m == mode_in) return O_RDONLY | O_BINARY | O_RANDOM;
		assert(false);
		return 0;
	}

#ifdef WIN32
	std::string utf8_native(std::string const& s)
	{
		try
		{
			std::wstring ws;
			libtorrent::utf8_wchar(s, ws);
			std::size_t size = wcstombs(0, ws.c_str(), 0);
			if (size == std::size_t(-1)) return s;
			std::string ret;
			ret.resize(size);
			size = wcstombs(&ret[0], ws.c_str(), size + 1);
			if (size == wchar_t(-1)) return s;
			ret.resize(size);
			return ret;
		}
		catch(std::exception)
		{
			return s;
		}
	}
#else
	std::string utf8_native(std::string const& s)
	{
		return s;
	}
#endif

}

namespace libtorrent
{

	const file::open_mode file::in(mode_in);
	const file::open_mode file::out(mode_out);

	const file::seek_mode file::begin(1);
	const file::seek_mode file::end(2);

	struct file::impl
	{
		impl()
			: m_fd(-1)
			, m_open_mode(0)
		{}

		impl(fs::path const& path, int mode)
			: m_fd(-1)
			, m_open_mode(0)
		{
			open(path, mode);
		}

		~impl()
		{
			close();
		}

		void open(fs::path const& path, int mode)
		{
			assert(path.is_complete());
			close();
#if defined(_WIN32) && defined(UNICODE)
			std::wstring wpath(safe_convert(path.native_file_string()));
			m_fd = ::_wopen(
				wpath.c_str()
				, map_open_mode(mode)
				, S_IREAD | S_IWRITE);
#else
#ifdef _WIN32
			m_fd = ::_open(
#else
			m_fd = ::open(
#endif
				utf8_native(path.native_file_string()).c_str()
				, map_open_mode(mode)
#ifdef _WIN32
				, S_IREAD | S_IWRITE);
#else
				, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
#endif
#endif
			if (m_fd == -1)
			{
				std::stringstream msg;
				msg << "open failed: '" << path.native_file_string() << "'. "
					<< strerror(errno);
				throw file_error(msg.str());
			}
			m_open_mode = mode;
		}

		void close()
		{
			if (m_fd == -1) return;

#ifdef _WIN32
			::_close(m_fd);
#else
			::close(m_fd);
#endif
			m_fd = -1;
			m_open_mode = 0;
		}

		size_type read(char* buf, size_type num_bytes)
		{
			assert(m_open_mode & mode_in);
			assert(m_fd != -1);

#ifdef _WIN32
			size_type ret = ::_read(m_fd, buf, num_bytes);
#else
			size_type ret = ::read(m_fd, buf, num_bytes);
#endif
			if (ret == -1)
			{
				std::stringstream msg;
				msg << "read failed: " << strerror(errno);
				throw file_error(msg.str());
			}
			return ret;
		}

		size_type write(const char* buf, size_type num_bytes)
		{
			assert(m_open_mode & mode_out);
			assert(m_fd != -1);

			// TODO: Test this a bit more, what happens with random failures in
			// the files?
//			if ((rand() % 100) > 80)
//				throw file_error("debug");

#ifdef _WIN32
			size_type ret = ::_write(m_fd, buf, num_bytes);
#else
			size_type ret = ::write(m_fd, buf, num_bytes);
#endif
			if (ret == -1)
			{
				std::stringstream msg;
				msg << "write failed: " << strerror(errno);
				throw file_error(msg.str());
			}
			return ret;
		}

		size_type seek(size_type offset, int m)
		{
			assert(m_open_mode);
			assert(m_fd != -1);

			int seekdir = (m == 1)?SEEK_SET:SEEK_END;
#ifdef _WIN32
			size_type ret = _lseeki64(m_fd, offset, seekdir);
#else
			size_type ret = lseek(m_fd, offset, seekdir);
#endif

			// For some strange reason this fails
			// on win32. Use windows specific file
			// wrapper instead.
			if (ret == -1)
			{
				std::stringstream msg;
				msg << "seek failed: '" << strerror(errno)
					<< "' fd: " << m_fd
					<< " offset: " << offset
					<< " seekdir: " << seekdir;
				throw file_error(msg.str());
			}
			return ret;
		}

		size_type tell()
		{
			assert(m_open_mode);
			assert(m_fd != -1);

#ifdef _WIN32
			return _telli64(m_fd);
#else
			return lseek(m_fd, 0, SEEK_CUR);
#endif
		}

		int m_fd;
		int m_open_mode;
	};

	// pimpl forwardings

	file::file() : m_impl(new impl()) {}

	file::file(boost::filesystem::path const& p, file::open_mode m)
		: m_impl(new impl(p, m.m_mask))
	{}

	file::~file() {}

	void file::open(boost::filesystem::path const& p, file::open_mode m)
	{
		m_impl->open(p, m.m_mask);
	}

	void file::close()
	{
		m_impl->close();
	}

	size_type file::write(const char* buf, size_type num_bytes)
	{
		return m_impl->write(buf, num_bytes);
	}

	size_type file::read(char* buf, size_type num_bytes)
	{
		return m_impl->read(buf, num_bytes);
	}

	size_type file::seek(size_type pos, file::seek_mode m)
	{
		return m_impl->seek(pos, m.m_val);
	}

	size_type file::tell()
	{
		return m_impl->tell();
	}

}
<commit_msg>file did not throw exception properly when file was trunkated and read<commit_after>/*

Copyright (c) 2003, 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"

#ifdef _WIN32
// windows part
#include "libtorrent/utf8.hpp"

#include <io.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>

#ifndef _MODE_T_
typedef int mode_t;
#endif

#ifdef UNICODE
#include "libtorrent/storage.hpp"
#endif

#else
// unix part
#define _FILE_OFFSET_BITS 64
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>

#include <boost/static_assert.hpp>
// make sure the _FILE_OFFSET_BITS define worked
// on this platform
BOOST_STATIC_ASSERT(sizeof(lseek(0, 0, 0)) >= 8);

#endif

#include <boost/filesystem/operations.hpp>
#include "libtorrent/file.hpp"
#include <sstream>

#ifndef O_BINARY
#define O_BINARY 0
#endif

#ifndef O_RANDOM
#define O_RANDOM 0
#endif

#ifdef UNICODE
#include "libtorrent/storage.hpp"
#endif

namespace fs = boost::filesystem;

namespace
{
	enum { mode_in = 1, mode_out = 2 };

	mode_t map_open_mode(int m)
	{
		if (m == (mode_in | mode_out)) return O_RDWR | O_CREAT | O_BINARY | O_RANDOM;
		if (m == mode_out) return O_WRONLY | O_CREAT | O_BINARY | O_RANDOM;
		if (m == mode_in) return O_RDONLY | O_BINARY | O_RANDOM;
		assert(false);
		return 0;
	}

#ifdef WIN32
	std::string utf8_native(std::string const& s)
	{
		try
		{
			std::wstring ws;
			libtorrent::utf8_wchar(s, ws);
			std::size_t size = wcstombs(0, ws.c_str(), 0);
			if (size == std::size_t(-1)) return s;
			std::string ret;
			ret.resize(size);
			size = wcstombs(&ret[0], ws.c_str(), size + 1);
			if (size == wchar_t(-1)) return s;
			ret.resize(size);
			return ret;
		}
		catch(std::exception)
		{
			return s;
		}
	}
#else
	std::string utf8_native(std::string const& s)
	{
		return s;
	}
#endif

}

namespace libtorrent
{

	const file::open_mode file::in(mode_in);
	const file::open_mode file::out(mode_out);

	const file::seek_mode file::begin(1);
	const file::seek_mode file::end(2);

	struct file::impl
	{
		impl()
			: m_fd(-1)
			, m_open_mode(0)
		{}

		impl(fs::path const& path, int mode)
			: m_fd(-1)
			, m_open_mode(0)
		{
			open(path, mode);
		}

		~impl()
		{
			close();
		}

		void open(fs::path const& path, int mode)
		{
			assert(path.is_complete());
			close();
#if defined(_WIN32) && defined(UNICODE)
			std::wstring wpath(safe_convert(path.native_file_string()));
			m_fd = ::_wopen(
				wpath.c_str()
				, map_open_mode(mode)
				, S_IREAD | S_IWRITE);
#else
#ifdef _WIN32
			m_fd = ::_open(
#else
			m_fd = ::open(
#endif
				utf8_native(path.native_file_string()).c_str()
				, map_open_mode(mode)
#ifdef _WIN32
				, S_IREAD | S_IWRITE);
#else
				, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
#endif
#endif
			if (m_fd == -1)
			{
				std::stringstream msg;
				msg << "open failed: '" << path.native_file_string() << "'. "
					<< strerror(errno);
				throw file_error(msg.str());
			}
			m_open_mode = mode;
		}

		void close()
		{
			if (m_fd == -1) return;

#ifdef _WIN32
			::_close(m_fd);
#else
			::close(m_fd);
#endif
			m_fd = -1;
			m_open_mode = 0;
		}

		size_type read(char* buf, size_type num_bytes)
		{
			assert(num_bytes > 0);
			assert(m_open_mode & mode_in);
			assert(m_fd != -1);

#ifdef _WIN32
			size_type ret = ::_read(m_fd, buf, num_bytes);
#else
			size_type ret = ::read(m_fd, buf, num_bytes);
#endif
			if (ret == -1 || ret == 0)
			{
				std::stringstream msg;
				msg << "read failed: " << strerror(errno);
				throw file_error(msg.str());
			}
			return ret;
		}

		size_type write(const char* buf, size_type num_bytes)
		{
			assert(m_open_mode & mode_out);
			assert(m_fd != -1);

			// TODO: Test this a bit more, what happens with random failures in
			// the files?
//			if ((rand() % 100) > 80)
//				throw file_error("debug");

#ifdef _WIN32
			size_type ret = ::_write(m_fd, buf, num_bytes);
#else
			size_type ret = ::write(m_fd, buf, num_bytes);
#endif
			if (ret == -1)
			{
				std::stringstream msg;
				msg << "write failed: " << strerror(errno);
				throw file_error(msg.str());
			}
			return ret;
		}

		size_type seek(size_type offset, int m)
		{
			assert(m_open_mode);
			assert(m_fd != -1);

			int seekdir = (m == 1)?SEEK_SET:SEEK_END;
#ifdef _WIN32
			size_type ret = _lseeki64(m_fd, offset, seekdir);
#else
			size_type ret = lseek(m_fd, offset, seekdir);
#endif

			// For some strange reason this fails
			// on win32. Use windows specific file
			// wrapper instead.
			if (ret == -1)
			{
				std::stringstream msg;
				msg << "seek failed: '" << strerror(errno)
					<< "' fd: " << m_fd
					<< " offset: " << offset
					<< " seekdir: " << seekdir;
				throw file_error(msg.str());
			}
			return ret;
		}

		size_type tell()
		{
			assert(m_open_mode);
			assert(m_fd != -1);

#ifdef _WIN32
			return _telli64(m_fd);
#else
			return lseek(m_fd, 0, SEEK_CUR);
#endif
		}

		int m_fd;
		int m_open_mode;
	};

	// pimpl forwardings

	file::file() : m_impl(new impl()) {}

	file::file(boost::filesystem::path const& p, file::open_mode m)
		: m_impl(new impl(p, m.m_mask))
	{}

	file::~file() {}

	void file::open(boost::filesystem::path const& p, file::open_mode m)
	{
		m_impl->open(p, m.m_mask);
	}

	void file::close()
	{
		m_impl->close();
	}

	size_type file::write(const char* buf, size_type num_bytes)
	{
		return m_impl->write(buf, num_bytes);
	}

	size_type file::read(char* buf, size_type num_bytes)
	{
		return m_impl->read(buf, num_bytes);
	}

	size_type file::seek(size_type pos, file::seek_mode m)
	{
		return m_impl->seek(pos, m.m_val);
	}

	size_type file::tell()
	{
		return m_impl->tell();
	}

}
<|endoftext|>