<commit_before>/***************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of mhome.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include <QHash>
#include "windowinfo.h"
#include "x11wrapper.h"
#include <QX11Info>

/*!
  The window data is explicitly shared between window info objects through this class
 */
class WindowData : public QSharedData
{

public:
    //! Constructs a window data object
    WindowData(Window id) :
            window(id)
    {
    }

    //! Copy constructor
    WindowData(const WindowData& source) : QSharedData(source),
            window(source.window), transientFor(source.transientFor),
            types(source.types), states(source.states) {}

    //! Destructor
    ~WindowData() {}

    //! The X window ID
    Window window;

    //! The ID of the window this window is transient for
    Window transientFor;

    //! The title of the window
    QString title;

    //! The window types associated with this window
    QList<Atom> types;

    //! The status atoms of this window
    QList<Atom> states;
};

static bool atomsInitialized;

Atom WindowInfo::TypeAtom;
Atom WindowInfo::StateAtom;
Atom WindowInfo::NormalAtom;
Atom WindowInfo::DesktopAtom;
Atom WindowInfo::NotificationAtom;
Atom WindowInfo::DialogAtom;
Atom WindowInfo::CallAtom;
Atom WindowInfo::DockAtom;
Atom WindowInfo::MenuAtom;
Atom WindowInfo::SkipTaskbarAtom;
Atom WindowInfo::NameAtom;
Atom WindowInfo::InputWindowAtom;

void WindowInfo::initializeAtoms()
{
    if (!atomsInitialized) {
        Display *dpy = QX11Info::display();
        TypeAtom = X11Wrapper::XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
        StateAtom = X11Wrapper::XInternAtom(dpy, "_NET_WM_STATE", False);
        NormalAtom = X11Wrapper::XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_NORMAL", False);
        DesktopAtom = X11Wrapper::XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DESKTOP", False);
        NotificationAtom = X11Wrapper::XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_NOTIFICATION", False);
        DialogAtom = X11Wrapper::XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
        CallAtom = X11Wrapper::XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_CALL", False);
        DockAtom = X11Wrapper::XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False);
        MenuAtom = X11Wrapper::XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_MENU", False);
        SkipTaskbarAtom = X11Wrapper::XInternAtom(dpy, "_NET_WM_STATE_SKIP_TASKBAR", False);
        NameAtom = X11Wrapper::XInternAtom(dpy, "_NET_WM_NAME", False);
        InputWindowAtom = X11Wrapper::XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_INPUT", False);
        atomsInitialized = true;
    }
}

QHash<Window, QExplicitlySharedDataPointer<WindowData> > WindowInfo::windowDatas;

WindowInfo::WindowInfo(Window window)
{
    if (QExplicitlySharedDataPointer<WindowData> data = windowDatas.value(window)) {
        d = data;
    } else {
        d = new WindowData(window);
        updateWindowTitle();
        updateWindowProperties();
        windowDatas[window] = d;
    }
}

WindowInfo::WindowInfo(const WindowInfo &other) :
        d(other.d)
{
}

WindowInfo::~WindowInfo()
{
    // If the data object's reference count is two, it means that the only alive
    // references are in this object and the global container. That means that this
    // object is the last WindowInfo object containing a reference to the data object.
    // We don't want to leave a dangling data object to the global container so we'll
    // remove the data object here.
    if (d->ref == 2) {
        windowDatas.remove(d->window);
    }
}

WindowInfo& WindowInfo::operator=(const WindowInfo &rhs)
{
    d = rhs.d;
    return *this;
}

const QString& WindowInfo::title() const
{
    return d->title;
}

Window WindowInfo::window() const
{
    return d->window;
}

Window WindowInfo::transientFor() const
{
    return d->transientFor;
}

QList<Atom> WindowInfo::types() const
{
    return d->types;
}

QList<Atom> WindowInfo::states() const
{
    return d->states;
}

bool operator==(const WindowInfo &wi1, const WindowInfo &wi2)
{
    return wi1.window() == wi2.window();
}

bool WindowInfo::updateWindowTitle()
{
    Display *dpy = QX11Info::display();
    XTextProperty textProperty;
    bool updated = false;
    int result = X11Wrapper::XGetTextProperty(dpy, d->window, &textProperty, WindowInfo::NameAtom);
    if (result == 0) {
        result = X11Wrapper::XGetWMName(dpy, d->window, &textProperty);
    }

    if (result != 0) {
        d->title = QString::fromUtf8((const char *)textProperty.value);
        X11Wrapper::XFree(textProperty.value);
        updated = true;
    }
    return updated;
}

void WindowInfo::updateWindowProperties()
{
    d->types = getWindowProperties(d->window, WindowInfo::TypeAtom);
    d->states = getWindowProperties(d->window, WindowInfo::StateAtom);

    if (!X11Wrapper::XGetTransientForHint(QX11Info::display(), d->window, &d->transientFor) || d->transientFor == d->window) {
        d->transientFor = 0;
    }
}

QList<Atom> WindowInfo::getWindowProperties(Window winId, Atom propertyAtom, long maxCount)
{
    QList<Atom> properties;
    Atom actualType;
    int actualFormat;
    unsigned long numTypeItems, bytesLeft;
    unsigned char *typeData = NULL;

    Status result = X11Wrapper::XGetWindowProperty(QX11Info::display(), winId, propertyAtom, 0L, maxCount, False, XA_ATOM, &actualType, &actualFormat, &numTypeItems, &bytesLeft, &typeData);
    if (result == Success) {
        Atom *type = (Atom *) typeData;
        for (unsigned int n = 0; n < numTypeItems; n++) {
            properties.append(type[n]);
        }
        X11Wrapper::XFree(typeData);
    }
    return properties;
}
<commit_msg>Changes: initialize member variabls in constructor of WindowData<commit_after>/***************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of mhome.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include <QHash>
#include "windowinfo.h"
#include "x11wrapper.h"
#include <QX11Info>

/*!
  The window data is explicitly shared between window info objects through this class
 */
class WindowData : public QSharedData
{

public:
    //! Constructs a window data object
    WindowData(Window id) :
            window(id),
            transientFor(0),
            title(),
            types(),
            states()
    {
    }

    //! Copy constructor
    WindowData(const WindowData& source) : QSharedData(source),
            window(source.window),
            transientFor(source.transientFor),
            types(source.types),
            states(source.states)
    {
    }

    //! Destructor
    ~WindowData()
    {
    }

    //! The X window ID
    Window window;

    //! The ID of the window this window is transient for
    Window transientFor;

    //! The title of the window
    QString title;

    //! The window types associated with this window
    QList<Atom> types;

    //! The status atoms of this window
    QList<Atom> states;
};

static bool atomsInitialized;

Atom WindowInfo::TypeAtom;
Atom WindowInfo::StateAtom;
Atom WindowInfo::NormalAtom;
Atom WindowInfo::DesktopAtom;
Atom WindowInfo::NotificationAtom;
Atom WindowInfo::DialogAtom;
Atom WindowInfo::CallAtom;
Atom WindowInfo::DockAtom;
Atom WindowInfo::MenuAtom;
Atom WindowInfo::SkipTaskbarAtom;
Atom WindowInfo::NameAtom;
Atom WindowInfo::InputWindowAtom;

void WindowInfo::initializeAtoms()
{
    if (!atomsInitialized) {
        Display *dpy = QX11Info::display();
        TypeAtom = X11Wrapper::XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
        StateAtom = X11Wrapper::XInternAtom(dpy, "_NET_WM_STATE", False);
        NormalAtom = X11Wrapper::XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_NORMAL", False);
        DesktopAtom = X11Wrapper::XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DESKTOP", False);
        NotificationAtom = X11Wrapper::XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_NOTIFICATION", False);
        DialogAtom = X11Wrapper::XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
        CallAtom = X11Wrapper::XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_CALL", False);
        DockAtom = X11Wrapper::XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False);
        MenuAtom = X11Wrapper::XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_MENU", False);
        SkipTaskbarAtom = X11Wrapper::XInternAtom(dpy, "_NET_WM_STATE_SKIP_TASKBAR", False);
        NameAtom = X11Wrapper::XInternAtom(dpy, "_NET_WM_NAME", False);
        InputWindowAtom = X11Wrapper::XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_INPUT", False);
        atomsInitialized = true;
    }
}

QHash<Window, QExplicitlySharedDataPointer<WindowData> > WindowInfo::windowDatas;

WindowInfo::WindowInfo(Window window)
{
    if (QExplicitlySharedDataPointer<WindowData> data = windowDatas.value(window)) {
        d = data;
    } else {
        d = new WindowData(window);
        updateWindowTitle();
        updateWindowProperties();
        windowDatas[window] = d;
    }
}

WindowInfo::WindowInfo(const WindowInfo &other) :
        d(other.d)
{
}

WindowInfo::~WindowInfo()
{
    // If the data object's reference count is two, it means that the only alive
    // references are in this object and the global container. That means that this
    // object is the last WindowInfo object containing a reference to the data object.
    // We don't want to leave a dangling data object to the global container so we'll
    // remove the data object here.
    if (d->ref == 2) {
        windowDatas.remove(d->window);
    }
}

WindowInfo& WindowInfo::operator=(const WindowInfo &rhs)
{
    d = rhs.d;
    return *this;
}

const QString& WindowInfo::title() const
{
    return d->title;
}

Window WindowInfo::window() const
{
    return d->window;
}

Window WindowInfo::transientFor() const
{
    return d->transientFor;
}

QList<Atom> WindowInfo::types() const
{
    return d->types;
}

QList<Atom> WindowInfo::states() const
{
    return d->states;
}

bool operator==(const WindowInfo &wi1, const WindowInfo &wi2)
{
    return wi1.window() == wi2.window();
}

bool WindowInfo::updateWindowTitle()
{
    Display *dpy = QX11Info::display();
    XTextProperty textProperty;
    bool updated = false;
    int result = X11Wrapper::XGetTextProperty(dpy, d->window, &textProperty, WindowInfo::NameAtom);
    if (result == 0) {
        result = X11Wrapper::XGetWMName(dpy, d->window, &textProperty);
    }

    if (result != 0) {
        d->title = QString::fromUtf8((const char *)textProperty.value);
        X11Wrapper::XFree(textProperty.value);
        updated = true;
    }
    return updated;
}

void WindowInfo::updateWindowProperties()
{
    d->types = getWindowProperties(d->window, WindowInfo::TypeAtom);
    d->states = getWindowProperties(d->window, WindowInfo::StateAtom);

    if (!X11Wrapper::XGetTransientForHint(QX11Info::display(), d->window, &d->transientFor) || d->transientFor == d->window) {
        d->transientFor = 0;
    }
}

QList<Atom> WindowInfo::getWindowProperties(Window winId, Atom propertyAtom, long maxCount)
{
    QList<Atom> properties;
    Atom actualType;
    int actualFormat;
    unsigned long numTypeItems, bytesLeft;
    unsigned char *typeData = NULL;

    Status result = X11Wrapper::XGetWindowProperty(QX11Info::display(), winId, propertyAtom, 0L, maxCount, False, XA_ATOM, &actualType, &actualFormat, &numTypeItems, &bytesLeft, &typeData);
    if (result == Success) {
        Atom *type = (Atom *) typeData;
        for (unsigned int n = 0; n < numTypeItems; n++) {
            properties.append(type[n]);
        }
        X11Wrapper::XFree(typeData);
    }
    return properties;
}
<|endoftext|>


<commit_before>/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved.

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 <string>
#include <vector>
#include "paddle/fluid/framework/op_registry.h"

namespace paddle {
namespace operators {

class SqueezeOpInferShape : public framework::InferShapeBase {
 public:
  void operator()(framework::InferShapeContext *ctx) const override {
    PADDLE_ENFORCE(ctx->HasInput("X"),
                   "Input(X) of SqueezeOp should not be null.");
    PADDLE_ENFORCE(ctx->HasOutput("Out"),
                   "Output(Out) of SqueezeOp should not be null.");

    const auto &x_dims = ctx->GetInputDim("X");
    // Check input tensor dims (<6) Eigen limit.
    PADDLE_ENFORCE(x_dims.size() <= 6,
                   "Invalid dimnesions, dynamic dimensions must have "
                   "between [1, 6] dimensions (Eigen limit).");

    const auto &axes = ctx->Attrs().Get<std::vector<int>>("axes");
    for (int a : axes) {
      PADDLE_ENFORCE_LT(a, x_dims.size(),
                        "The axis must be less than input tensor's rank.");
    }

    auto out_dims = GetOutputShape(axes, x_dims);
    ctx->SetOutputDim("Out", out_dims);
    if (x_dims[0] == out_dims[0]) {
      // Only pass LoD when the first dimension of output and Input(X)
      // are the same.
      ctx->ShareLoD("X", "Out");
    }
  }

  static framework::DDim GetOutputShape(const std::vector<int> squeeze_dims,
                                        const framework::DDim &in_dims) {
    int num_squeeze_dims = squeeze_dims.size();
    int cnt_squeezed_dims = 0;
    bool should_squeeze[9] = {false};

    // Determines number of dimensions of output tensor after squeeze.
    // Mark and count the dimensions need to be squeezed
    if (num_squeeze_dims == 0) {
      for (int idx = 0; idx < in_dims.size(); ++idx) {
        if (in_dims[idx] == 1) {
          should_squeeze[idx] = true;
          ++cnt_squeezed_dims;
        }
      }
    } else {
      for (int idx = 0; idx < num_squeeze_dims; ++idx) {
        int current = squeeze_dims[idx] < 0 ? squeeze_dims[idx] + in_dims.size()
                                            : squeeze_dims[idx];
        // Check current index.
        PADDLE_ENFORCE(current >= 0,
                       "Invalid axis, negative axis is out of range.");
        // PADDLE_ENFORCE_LT(current, in_dims.size(), "Invalid axis is given.");
        PADDLE_ENFORCE(
            in_dims[current] == 1,
            "Invalid axis index, the axis will be squeezed should be 1.");

        if (!(should_squeeze[current])) {
          ++cnt_squeezed_dims;
        }
        should_squeeze[current] = true;
      }
    }

    // Make output dimensions
    std::vector<int64_t> output_shape(in_dims.size() - cnt_squeezed_dims, 0);
    for (int in_idx = 0, out_idx = 0; in_idx < in_dims.size(); ++in_idx) {
      if (!should_squeeze[in_idx]) {
        output_shape[out_idx++] = in_dims[in_idx];
      }
    }

    return framework::make_ddim(output_shape);
  }
};

class SqueezeOp : public framework::OperatorBase {
 public:
  SqueezeOp(const std::string &type, const framework::VariableNameMap &inputs,
            const framework::VariableNameMap &outputs,
            const framework::AttributeMap &attrs)
      : OperatorBase(type, inputs, outputs, attrs) {}

 private:
  void RunImpl(const framework::Scope &scope,
               const platform::Place &place) const override {
    auto &axes = Attr<std::vector<int>>("axes");
    auto x_dims = scope.FindVar(Input("X"))->Get<framework::LoDTensor>().dims();
    auto out_dims = SqueezeOpInferShape::GetOutputShape(axes, x_dims);

    framework::AttributeMap attrs;
    attrs["shape"] = framework::vectorize2int(out_dims);
    attrs["inplace"] = Attr<bool>("inplace");
    // Invoke Reshape Op
    auto reshape_op = framework::OpRegistry::CreateOp(
        "reshape", {{"X", {Input("X")}}, {"Shape", {}}},
        {{"Out", {Output("Out")}}}, attrs);
    reshape_op->Run(scope, place);
  }
};

class SqueezeOpMaker : public framework::OpProtoAndCheckerMaker {
 public:
  void Make() override {
    AddInput("X", "(Tensor). The input tensor of squeeze operator.");
    AddOutput("Out", "(Tensor). The output tensor of squeeze operator.");
    AddAttr<std::vector<int>>("axes",
                              "(std::vector<int>). List of positive integers,"
                              " indicate the dimensions to squeeze.")
        .SetDefault({});
    AddAttr<bool>("inplace",
                  "(default: false) Squeeze the source tensor's shape without "
                  "memory copy. When Attr(inplace) is set true, the output "
                  "tensor shares memory with Input(X), otherwise, a new output "
                  "tensor is created, and its data are copied from Input(x).")
        .SetDefault(false);
    AddComment(R"DOC(
        Squeeze Operator.

        Remove single-dimensional entries from the shape of a tensor.
        Takes a parameter axes with a list of axes to squeeze.
        If axes is not provided, all the single dimensions will be removed from the shape.
        If an axis is selected with shape entry not equal to one, an error is raised.

        Examples:
        Case 1:
          Given
            X.shape = (1, 3, 1, 5)
          and
            axes = [0]
          we get:
            Out.shape = (3, 1, 5)

        Case 2:
          Given
            X.shape = (1, 3, 1, 5)
          we get:
            Out.shape = (3, 5)
    )DOC");
  }
};

class SqueezeGradInferShape : public framework::InferShapeBase {
 public:
  void operator()(framework::InferShapeContext *context) const override {
    context->SetOutputDim(framework::GradVarName("X"),
                          context->GetInputDim("X"));
    context->ShareLoD("X", framework::GradVarName("X"));
  }
};

class SqueezeGradOp : public framework::OperatorBase {
 public:
  SqueezeGradOp(const std::string &type,
                const framework::VariableNameMap &inputs,
                const framework::VariableNameMap &outputs,
                const framework::AttributeMap &attrs)
      : OperatorBase(type, inputs, outputs, attrs) {}

 private:
  void RunImpl(const framework::Scope &scope,
               const platform::Place &place) const override {
    auto dx_name = Output(framework::GradVarName("X"));
    auto dout_name = Input(framework::GradVarName("Out"));
    auto x_dims = scope.FindVar(Input("X"))->Get<framework::LoDTensor>().dims();
    framework::AttributeMap attrs;
    attrs["shape"] = framework::vectorize2int(x_dims);
    attrs["inplace"] = Attr<bool>("inplace");

    auto reshape_op = framework::OpRegistry::CreateOp(
        "reshape", {{"X", {dout_name}}, {"Shape", {}}}, {{"Out", {dx_name}}},
        attrs);
    reshape_op->Run(scope, place);
  }
};

}  // namespace operators
}  // namespace paddle

// Tell linker to use reshape op
USE_OP(reshape);

namespace ops = paddle::operators;
REGISTER_OPERATOR(squeeze, ops::SqueezeOp, ops::SqueezeOpMaker,
                  ops::SqueezeOpInferShape,
                  paddle::framework::DefaultGradOpDescMaker<true>);
REGISTER_OPERATOR(squeeze_grad, ops::SqueezeGradOp, ops::SqueezeGradInferShape);
<commit_msg>simplify construct function<commit_after>/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved.

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 <string>
#include <vector>
#include "paddle/fluid/framework/op_registry.h"

namespace paddle {
namespace operators {

class SqueezeOpInferShape : public framework::InferShapeBase {
 public:
  void operator()(framework::InferShapeContext *ctx) const override {
    PADDLE_ENFORCE(ctx->HasInput("X"),
                   "Input(X) of SqueezeOp should not be null.");
    PADDLE_ENFORCE(ctx->HasOutput("Out"),
                   "Output(Out) of SqueezeOp should not be null.");

    const auto &x_dims = ctx->GetInputDim("X");
    // Check input tensor dims (<6) Eigen limit.
    PADDLE_ENFORCE(x_dims.size() <= 6,
                   "Invalid dimnesions, dynamic dimensions must have "
                   "between [1, 6] dimensions (Eigen limit).");

    const auto &axes = ctx->Attrs().Get<std::vector<int>>("axes");
    for (int a : axes) {
      PADDLE_ENFORCE_LT(a, x_dims.size(),
                        "The axis must be less than input tensor's rank.");
    }

    auto out_dims = GetOutputShape(axes, x_dims);
    ctx->SetOutputDim("Out", out_dims);
    if (x_dims[0] == out_dims[0]) {
      // Only pass LoD when the first dimension of output and Input(X)
      // are the same.
      ctx->ShareLoD("X", "Out");
    }
  }

  static framework::DDim GetOutputShape(const std::vector<int> squeeze_dims,
                                        const framework::DDim &in_dims) {
    int num_squeeze_dims = squeeze_dims.size();
    int cnt_squeezed_dims = 0;
    bool should_squeeze[9] = {false};

    // Determines number of dimensions of output tensor after squeeze.
    // Mark and count the dimensions need to be squeezed
    if (num_squeeze_dims == 0) {
      for (int idx = 0; idx < in_dims.size(); ++idx) {
        if (in_dims[idx] == 1) {
          should_squeeze[idx] = true;
          ++cnt_squeezed_dims;
        }
      }
    } else {
      for (int idx = 0; idx < num_squeeze_dims; ++idx) {
        int current = squeeze_dims[idx] < 0 ? squeeze_dims[idx] + in_dims.size()
                                            : squeeze_dims[idx];
        // Check current index.
        PADDLE_ENFORCE(current >= 0,
                       "Invalid axis, negative axis is out of range.");
        // PADDLE_ENFORCE_LT(current, in_dims.size(), "Invalid axis is given.");
        PADDLE_ENFORCE(
            in_dims[current] == 1,
            "Invalid axis index, the axis will be squeezed should be 1.");

        if (!(should_squeeze[current])) {
          ++cnt_squeezed_dims;
        }
        should_squeeze[current] = true;
      }
    }

    // Make output dimensions
    std::vector<int64_t> output_shape(in_dims.size() - cnt_squeezed_dims, 0);
    for (int in_idx = 0, out_idx = 0; in_idx < in_dims.size(); ++in_idx) {
      if (!should_squeeze[in_idx]) {
        output_shape[out_idx++] = in_dims[in_idx];
      }
    }

    return framework::make_ddim(output_shape);
  }
};

class SqueezeOp : public framework::OperatorBase {
 public:
  using OperatorBase::OperatorBase;

 private:
  void RunImpl(const framework::Scope &scope,
               const platform::Place &place) const override {
    auto &axes = Attr<std::vector<int>>("axes");
    auto x_dims = scope.FindVar(Input("X"))->Get<framework::LoDTensor>().dims();
    auto out_dims = SqueezeOpInferShape::GetOutputShape(axes, x_dims);

    framework::AttributeMap attrs;
    attrs["shape"] = framework::vectorize2int(out_dims);
    attrs["inplace"] = Attr<bool>("inplace");
    // Invoke Reshape Op
    auto reshape_op = framework::OpRegistry::CreateOp(
        "reshape", {{"X", {Input("X")}}, {"Shape", {}}},
        {{"Out", {Output("Out")}}}, attrs);
    reshape_op->Run(scope, place);
  }
};

class SqueezeOpMaker : public framework::OpProtoAndCheckerMaker {
 public:
  void Make() override {
    AddInput("X", "(Tensor). The input tensor of squeeze operator.");
    AddOutput("Out", "(Tensor). The output tensor of squeeze operator.");
    AddAttr<std::vector<int>>("axes",
                              "(std::vector<int>). List of positive integers,"
                              " indicate the dimensions to squeeze.")
        .SetDefault({});
    AddAttr<bool>("inplace",
                  "(default: false) Squeeze the source tensor's shape without "
                  "memory copy. When Attr(inplace) is set true, the output "
                  "tensor shares memory with Input(X), otherwise, a new output "
                  "tensor is created, and its data are copied from Input(x).")
        .SetDefault(false);
    AddComment(R"DOC(
        Squeeze Operator.

        Remove single-dimensional entries from the shape of a tensor.
        Takes a parameter axes with a list of axes to squeeze.
        If axes is not provided, all the single dimensions will be removed from the shape.
        If an axis is selected with shape entry not equal to one, an error is raised.

        Examples:
        Case 1:
          Given
            X.shape = (1, 3, 1, 5)
          and
            axes = [0]
          we get:
            Out.shape = (3, 1, 5)

        Case 2:
          Given
            X.shape = (1, 3, 1, 5)
          we get:
            Out.shape = (3, 5)
    )DOC");
  }
};

class SqueezeGradInferShape : public framework::InferShapeBase {
 public:
  void operator()(framework::InferShapeContext *context) const override {
    context->SetOutputDim(framework::GradVarName("X"),
                          context->GetInputDim("X"));
    context->ShareLoD("X", framework::GradVarName("X"));
  }
};

class SqueezeGradOp : public framework::OperatorBase {
 public:
  using OperatorBase::OperatorBase;

 private:
  void RunImpl(const framework::Scope &scope,
               const platform::Place &place) const override {
    auto dx_name = Output(framework::GradVarName("X"));
    auto dout_name = Input(framework::GradVarName("Out"));
    auto x_dims = scope.FindVar(Input("X"))->Get<framework::LoDTensor>().dims();
    framework::AttributeMap attrs;
    attrs["shape"] = framework::vectorize2int(x_dims);
    attrs["inplace"] = Attr<bool>("inplace");

    auto reshape_op = framework::OpRegistry::CreateOp(
        "reshape", {{"X", {dout_name}}, {"Shape", {}}}, {{"Out", {dx_name}}},
        attrs);
    reshape_op->Run(scope, place);
  }
};

}  // namespace operators
}  // namespace paddle

// Tell linker to use reshape op
USE_OP(reshape);

namespace ops = paddle::operators;
REGISTER_OPERATOR(squeeze, ops::SqueezeOp, ops::SqueezeOpMaker,
                  ops::SqueezeOpInferShape,
                  paddle::framework::DefaultGradOpDescMaker<true>);
REGISTER_OPERATOR(squeeze_grad, ops::SqueezeGradOp, ops::SqueezeGradInferShape);
<|endoftext|>


<commit_before>/*
 * AscEmu Framework based on ArcEmu MMORPG Server
 * Copyright (c) 2014-2019 AscEmu Team <http://www.ascemu.org>
 * Copyright (C) 2008-2012 ArcEmu Team <http://www.ArcEmu.org/>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#include "LogonStdAfx.h"
#include "../shared/Auth/MD5.h"

#ifndef WIN32
#include <fcntl.h>
#include <dirent.h>
#include <sys/stat.h>
#endif

PatchMgr& PatchMgr::getInstance()
{
    static PatchMgr mInstance;
    return mInstance;
}

void PatchMgr::initialize()
{
    // load patches
#ifdef WIN32
    LogNotice("PatchMgr : Loading Patches...");
    char Buffer[MAX_PATH * 10];
    char Buffer2[MAX_PATH * 10];
    char Buffer3[MAX_PATH * 10];

    WIN32_FIND_DATA fd;
    HANDLE fHandle;
    MD5Hash md5;
    Patch* pPatch;
    DWORD size, sizehigh;
    HANDLE hFile;
    uint32 srcversion;
    char locality[5];
    uint32 i;

    if (!GetCurrentDirectory(MAX_PATH * 10, Buffer))
        return;

    strcpy(Buffer2, Buffer);
    strcat(Buffer, "\\ClientPatches\\*.*");
    fHandle = FindFirstFile(Buffer, &fd);
    if (fHandle == INVALID_HANDLE_VALUE)
        return;

    do
    {
        snprintf(Buffer3, MAX_PATH * 10, "%s\\ClientPatches\\%s", Buffer2, fd.cFileName);
        if (sscanf(fd.cFileName, "%4s%u.", locality, &srcversion) != 2)
            continue;

        hFile = CreateFile(Buffer3, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL);
        if (hFile == INVALID_HANDLE_VALUE)
            continue;

        LogNotice("PatchMgr : Found patch for %u locale `%s`.", srcversion, locality);
        pPatch = new Patch;
        size = GetFileSize(hFile, &sizehigh);
        pPatch->FileSize = size;
        pPatch->Data = new uint8[size];
        pPatch->Version = srcversion;
        for (i = 0; i < 4; ++i)
            pPatch->Locality[i] = static_cast<char>(tolower(locality[i]));
        pPatch->Locality[4] = 0;
        pPatch->uLocality = *(uint32*)pPatch->Locality;

        if (pPatch->Data == NULL)
        {
            // shouldn't really happen
            delete pPatch;
            CloseHandle(hFile);
            continue;
        }

        // read the whole file
        ASSERT(ReadFile(hFile, pPatch->Data, pPatch->FileSize, &size, NULL));
        ASSERT(size == pPatch->FileSize);

        // close the handle, no longer needed
        CloseHandle(hFile);

        // md5hash the file
        md5.Initialize();
        md5.UpdateData(pPatch->Data, pPatch->FileSize);
        md5.Finalize();
        memcpy(pPatch->MD5, md5.GetDigest(), MD5_DIGEST_LENGTH);

        // add the patch to the patchlist
        m_patches.push_back(pPatch);

    } while (FindNextFile(fHandle, &fd));
    FindClose(fHandle);
#else
    /*
     *nix patch loader
     */
    LogNotice("PatchMgr : Loading Patches...");
    char Buffer[MAX_PATH * 10];
    char Buffer2[MAX_PATH * 10];
    char Buffer3[MAX_PATH * 10];

    struct dirent** list;
    int filecount;
    int read_fd;
    MD5Hash md5;
    Patch* pPatch;
    int size;
    uint32 srcversion;
    char locality[5];
    uint32 i;
    struct stat sb;

    strcpy(Buffer, "./ClientPatches");
    strcpy(Buffer2, Buffer);

    filecount = scandir("./ClientPatches", &list, 0, 0);
    if (filecount <= 0 || list == NULL)
    {
        LOG_ERROR("No patches found.");
        return;
    }

    while (filecount--)
    {
        snprintf(Buffer3, MAX_PATH * 10, "./ClientPatches/%s", list[filecount]->d_name);
        if (sscanf(list[filecount]->d_name, "%4s%u.", locality, &srcversion) != 2)
            continue;

        read_fd = open(Buffer3, O_RDONLY);
        if (read_fd < 0)
        {
            LOG_ERROR("Cannot open %s", Buffer3);
            continue;
        }

        if (fstat(read_fd, &sb) < 0)
        {
            LOG_ERROR("Cannot stat %s", Buffer3);
            close(read_fd);
            continue;
        }

        LogNotice("PatchMgr : Found patch for b%u locale `%s` (%u bytes).", srcversion, locality, sb.st_size);
        pPatch = new Patch;
        size = sb.st_size;
        pPatch->FileSize = size;
        pPatch->Data = new uint8[size];
        pPatch->Version = srcversion;
        for (i = 0; i < 4; ++i)
            pPatch->Locality[i] = tolower(locality[i]);
        pPatch->Locality[4] = 0;
        pPatch->uLocality = *(uint32*)pPatch->Locality;

        if (pPatch->Data == NULL)
        {
            // shouldn't really happen
            delete pPatch;
            close(read_fd);
            continue;
        }

        // read the whole file
        ASSERT(read(read_fd, pPatch->Data, size) == size);

        // close handle
        close(read_fd);

        // md5hash the file
        md5.Initialize();
        md5.UpdateData(pPatch->Data, pPatch->FileSize);
        md5.Finalize();
        memcpy(pPatch->MD5, md5.GetDigest(), MD5_DIGEST_LENGTH);

        // add the patch to the patchlist
        m_patches.push_back(pPatch);
        free(list[filecount]);
    }
    free(list);
#endif
}

Patch* PatchMgr::FindPatchForClient(uint32 Version, const char* Locality)
{
    char tmplocality[5];
    uint32 ulocality;
    uint32 i;
    std::vector<Patch*>::iterator itr;
    Patch* fallbackPatch = NULL;
    for (i = 0; i < 4; ++i)
        tmplocality[i] = static_cast<char>(tolower(Locality[i]));
    tmplocality[4] = 0;
    ulocality = *(uint32*)tmplocality;

    for (itr = m_patches.begin(); itr != m_patches.end(); ++itr)
    {
        // since localities are always 4 bytes we can do a simple int compare,
        // saving a string compare ;)
        if ((*itr)->uLocality == ulocality)
        {
            if (fallbackPatch == NULL && (*itr)->Version == 0)
                fallbackPatch = (*itr);

            if ((*itr)->Version == Version)
                return (*itr);
        }
    }

    return fallbackPatch;
}

void PatchMgr::BeginPatchJob(Patch* pPatch, AuthSocket* pClient, uint32 Skip)
{
    PatchJob* pJob;

    pJob = new PatchJob(pPatch, pClient, Skip);
    pClient->m_patchJob = pJob;
    m_patchJobLock.Acquire();
    m_patchJobs.push_back(pJob);
    m_patchJobLock.Release();
}

void PatchMgr::UpdateJobs()
{
    std::list<PatchJob*>::iterator itr, itr2;
    m_patchJobLock.Acquire();
    for (itr = m_patchJobs.begin(); itr != m_patchJobs.end();)
    {
        itr2 = itr;
        ++itr;

        if (!(*itr2)->Update())
        {
            (*itr2)->GetClient()->m_patchJob = NULL;
            delete(*itr2);
            m_patchJobs.erase(itr2);
        }
    }
    m_patchJobLock.Release();
}

void PatchMgr::AbortPatchJob(PatchJob* pJob)
{
    std::list<PatchJob*>::iterator itr;
    m_patchJobLock.Acquire();
    for (itr = m_patchJobs.begin(); itr != m_patchJobs.end(); ++itr)
    {
        if ((*itr) == pJob)
        {
            m_patchJobs.erase(itr);
            break;
        }
    }
    delete pJob;
    m_patchJobLock.Release();
}

// this is what blizz sends.
// Data (1412 bytes)
// Data (91 bytes)
// 1412+91=1503 (minus header bytes, 3) = 1500

#define TRANSFER_CHUNK_SIZE 1500

#pragma pack(push,1)

struct TransferInitiatePacket
{
    uint8 cmd;
    uint8 strsize;
    char name[6];
    uint64 filesize;
    uint8 md5hash[MD5_DIGEST_LENGTH];
};

struct TransferDataPacket
{
    uint8 cmd;
    uint16 chunk_size;
};

#pragma pack(pop)

bool PatchJob::Update()
{
    // don't update unless the write buffer is empty
    m_client->BurstBegin();
    if (m_client->writeBuffer.GetSize() != 0)
    {
        m_client->BurstEnd();
        return true;
    }

    // send 1500 byte chunks
    TransferDataPacket header;
    bool result;
    header.cmd = 0x31;
    header.chunk_size = static_cast<uint16>((m_bytesLeft > 1500) ? 1500 : m_bytesLeft);
    //LogDebug("PatchJob : Sending %u byte chunk", header.chunk_size);

    result = m_client->BurstSend((const uint8*)&header, sizeof(TransferDataPacket));
    if (result)
    {
        result = m_client->BurstSend(m_dataPointer, header.chunk_size);
        if (result)
        {
            m_dataPointer += header.chunk_size;
            m_bytesSent += header.chunk_size;
            m_bytesLeft -= header.chunk_size;
        }
    }

    if (result)
        m_client->BurstPush();

    m_client->BurstEnd();

    // no need to check the result here, could just be a full buffer and not necessarily a fatal error.
    return (m_bytesLeft > 0) ? true : false;
}

bool PatchMgr::InitiatePatch(Patch* pPatch, AuthSocket* pClient)
{
    // send initiate packet
    TransferInitiatePacket init;
    bool result;

    init.cmd = 0x30;
    init.strsize = 5;
    init.name[0] = 'P';
    init.name[1] = 'a';
    init.name[2] = 't';
    init.name[3] = 'c';
    init.name[4] = 'h';
    init.name[5] = '\0';
    init.filesize = pPatch->FileSize;
    memcpy(init.md5hash, pPatch->MD5, MD5_DIGEST_LENGTH);

    // send it to the client
    pClient->BurstBegin();
    result = pClient->BurstSend((const uint8*)&init, sizeof(TransferInitiatePacket));
    if (result)
        pClient->BurstPush();
    pClient->BurstEnd();
    return result;
}
<commit_msg>Resolved CID 351586 (#1 of 1): Invalid type in argument to printf format specifier<commit_after>/*
 * AscEmu Framework based on ArcEmu MMORPG Server
 * Copyright (c) 2014-2019 AscEmu Team <http://www.ascemu.org>
 * Copyright (C) 2008-2012 ArcEmu Team <http://www.ArcEmu.org/>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#include "LogonStdAfx.h"
#include "../shared/Auth/MD5.h"

#ifndef WIN32
#include <fcntl.h>
#include <dirent.h>
#include <sys/stat.h>
#endif

PatchMgr& PatchMgr::getInstance()
{
    static PatchMgr mInstance;
    return mInstance;
}

void PatchMgr::initialize()
{
    // load patches
#ifdef WIN32
    LogNotice("PatchMgr : Loading Patches...");
    char Buffer[MAX_PATH * 10];
    char Buffer2[MAX_PATH * 10];
    char Buffer3[MAX_PATH * 10];

    WIN32_FIND_DATA fd;
    HANDLE fHandle;
    MD5Hash md5;
    Patch* pPatch;
    DWORD size, sizehigh;
    HANDLE hFile;
    uint32 srcversion;
    char locality[5];
    uint32 i;

    if (!GetCurrentDirectory(MAX_PATH * 10, Buffer))
        return;

    strcpy(Buffer2, Buffer);
    strcat(Buffer, "\\ClientPatches\\*.*");
    fHandle = FindFirstFile(Buffer, &fd);
    if (fHandle == INVALID_HANDLE_VALUE)
        return;

    do
    {
        snprintf(Buffer3, MAX_PATH * 10, "%s\\ClientPatches\\%s", Buffer2, fd.cFileName);
        if (sscanf(fd.cFileName, "%4s%u.", locality, &srcversion) != 2)
            continue;

        hFile = CreateFile(Buffer3, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL);
        if (hFile == INVALID_HANDLE_VALUE)
            continue;

        LogNotice("PatchMgr : Found patch for %u locale `%s`.", srcversion, locality);
        pPatch = new Patch;
        size = GetFileSize(hFile, &sizehigh);
        pPatch->FileSize = size;
        pPatch->Data = new uint8[size];
        pPatch->Version = srcversion;
        for (i = 0; i < 4; ++i)
            pPatch->Locality[i] = static_cast<char>(tolower(locality[i]));
        pPatch->Locality[4] = 0;
        pPatch->uLocality = *(uint32*)pPatch->Locality;

        if (pPatch->Data == NULL)
        {
            // shouldn't really happen
            delete pPatch;
            CloseHandle(hFile);
            continue;
        }

        // read the whole file
        ASSERT(ReadFile(hFile, pPatch->Data, pPatch->FileSize, &size, NULL));
        ASSERT(size == pPatch->FileSize);

        // close the handle, no longer needed
        CloseHandle(hFile);

        // md5hash the file
        md5.Initialize();
        md5.UpdateData(pPatch->Data, pPatch->FileSize);
        md5.Finalize();
        memcpy(pPatch->MD5, md5.GetDigest(), MD5_DIGEST_LENGTH);

        // add the patch to the patchlist
        m_patches.push_back(pPatch);

    } while (FindNextFile(fHandle, &fd));
    FindClose(fHandle);
#else
    /*
     *nix patch loader
     */
    LogNotice("PatchMgr : Loading Patches...");
    char Buffer[MAX_PATH * 10];
    char Buffer2[MAX_PATH * 10];
    char Buffer3[MAX_PATH * 10];

    struct dirent** list;
    int filecount;
    int read_fd;
    MD5Hash md5;
    Patch* pPatch;
    int size;
    uint32 srcversion;
    char locality[5];
    uint32 i;
    struct stat sb;

    strcpy(Buffer, "./ClientPatches");
    strcpy(Buffer2, Buffer);

    filecount = scandir("./ClientPatches", &list, 0, 0);
    if (filecount <= 0 || list == NULL)
    {
        LOG_ERROR("No patches found.");
        return;
    }

    while (filecount--)
    {
        snprintf(Buffer3, MAX_PATH * 10, "./ClientPatches/%s", list[filecount]->d_name);
        if (sscanf(list[filecount]->d_name, "%4s%u.", locality, &srcversion) != 2)
            continue;

        read_fd = open(Buffer3, O_RDONLY);
        if (read_fd < 0)
        {
            LOG_ERROR("Cannot open %s", Buffer3);
            continue;
        }

        if (fstat(read_fd, &sb) < 0)
        {
            LOG_ERROR("Cannot stat %s", Buffer3);
            close(read_fd);
            continue;
        }

        LogNotice("PatchMgr : Found patch for b%u locale `%s` (%u bytes).", srcversion, locality, static_cast<uint32_t>(sb.st_size));
        pPatch = new Patch;
        size = sb.st_size;
        pPatch->FileSize = size;
        pPatch->Data = new uint8[size];
        pPatch->Version = srcversion;
        for (i = 0; i < 4; ++i)
            pPatch->Locality[i] = tolower(locality[i]);
        pPatch->Locality[4] = 0;
        pPatch->uLocality = *(uint32*)pPatch->Locality;

        if (pPatch->Data == NULL)
        {
            // shouldn't really happen
            delete pPatch;
            close(read_fd);
            continue;
        }

        // read the whole file
        ASSERT(read(read_fd, pPatch->Data, size) == size);

        // close handle
        close(read_fd);

        // md5hash the file
        md5.Initialize();
        md5.UpdateData(pPatch->Data, pPatch->FileSize);
        md5.Finalize();
        memcpy(pPatch->MD5, md5.GetDigest(), MD5_DIGEST_LENGTH);

        // add the patch to the patchlist
        m_patches.push_back(pPatch);
        free(list[filecount]);
    }
    free(list);
#endif
}

Patch* PatchMgr::FindPatchForClient(uint32 Version, const char* Locality)
{
    char tmplocality[5];
    uint32 ulocality;
    uint32 i;
    std::vector<Patch*>::iterator itr;
    Patch* fallbackPatch = NULL;
    for (i = 0; i < 4; ++i)
        tmplocality[i] = static_cast<char>(tolower(Locality[i]));
    tmplocality[4] = 0;
    ulocality = *(uint32*)tmplocality;

    for (itr = m_patches.begin(); itr != m_patches.end(); ++itr)
    {
        // since localities are always 4 bytes we can do a simple int compare,
        // saving a string compare ;)
        if ((*itr)->uLocality == ulocality)
        {
            if (fallbackPatch == NULL && (*itr)->Version == 0)
                fallbackPatch = (*itr);

            if ((*itr)->Version == Version)
                return (*itr);
        }
    }

    return fallbackPatch;
}

void PatchMgr::BeginPatchJob(Patch* pPatch, AuthSocket* pClient, uint32 Skip)
{
    PatchJob* pJob;

    pJob = new PatchJob(pPatch, pClient, Skip);
    pClient->m_patchJob = pJob;
    m_patchJobLock.Acquire();
    m_patchJobs.push_back(pJob);
    m_patchJobLock.Release();
}

void PatchMgr::UpdateJobs()
{
    std::list<PatchJob*>::iterator itr, itr2;
    m_patchJobLock.Acquire();
    for (itr = m_patchJobs.begin(); itr != m_patchJobs.end();)
    {
        itr2 = itr;
        ++itr;

        if (!(*itr2)->Update())
        {
            (*itr2)->GetClient()->m_patchJob = NULL;
            delete(*itr2);
            m_patchJobs.erase(itr2);
        }
    }
    m_patchJobLock.Release();
}

void PatchMgr::AbortPatchJob(PatchJob* pJob)
{
    std::list<PatchJob*>::iterator itr;
    m_patchJobLock.Acquire();
    for (itr = m_patchJobs.begin(); itr != m_patchJobs.end(); ++itr)
    {
        if ((*itr) == pJob)
        {
            m_patchJobs.erase(itr);
            break;
        }
    }
    delete pJob;
    m_patchJobLock.Release();
}

// this is what blizz sends.
// Data (1412 bytes)
// Data (91 bytes)
// 1412+91=1503 (minus header bytes, 3) = 1500

#define TRANSFER_CHUNK_SIZE 1500

#pragma pack(push,1)

struct TransferInitiatePacket
{
    uint8 cmd;
    uint8 strsize;
    char name[6];
    uint64 filesize;
    uint8 md5hash[MD5_DIGEST_LENGTH];
};

struct TransferDataPacket
{
    uint8 cmd;
    uint16 chunk_size;
};

#pragma pack(pop)

bool PatchJob::Update()
{
    // don't update unless the write buffer is empty
    m_client->BurstBegin();
    if (m_client->writeBuffer.GetSize() != 0)
    {
        m_client->BurstEnd();
        return true;
    }

    // send 1500 byte chunks
    TransferDataPacket header;
    bool result;
    header.cmd = 0x31;
    header.chunk_size = static_cast<uint16>((m_bytesLeft > 1500) ? 1500 : m_bytesLeft);
    //LogDebug("PatchJob : Sending %u byte chunk", header.chunk_size);

    result = m_client->BurstSend((const uint8*)&header, sizeof(TransferDataPacket));
    if (result)
    {
        result = m_client->BurstSend(m_dataPointer, header.chunk_size);
        if (result)
        {
            m_dataPointer += header.chunk_size;
            m_bytesSent += header.chunk_size;
            m_bytesLeft -= header.chunk_size;
        }
    }

    if (result)
        m_client->BurstPush();

    m_client->BurstEnd();

    // no need to check the result here, could just be a full buffer and not necessarily a fatal error.
    return (m_bytesLeft > 0) ? true : false;
}

bool PatchMgr::InitiatePatch(Patch* pPatch, AuthSocket* pClient)
{
    // send initiate packet
    TransferInitiatePacket init;
    bool result;

    init.cmd = 0x30;
    init.strsize = 5;
    init.name[0] = 'P';
    init.name[1] = 'a';
    init.name[2] = 't';
    init.name[3] = 'c';
    init.name[4] = 'h';
    init.name[5] = '\0';
    init.filesize = pPatch->FileSize;
    memcpy(init.md5hash, pPatch->MD5, MD5_DIGEST_LENGTH);

    // send it to the client
    pClient->BurstBegin();
    result = pClient->BurstSend((const uint8*)&init, sizeof(TransferInitiatePacket));
    if (result)
        pClient->BurstPush();
    pClient->BurstEnd();
    return result;
}
<|endoftext|>


<commit_before>// Filename: asyncTaskBase.cxx
// Created by:  drose (09Feb10)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University.  All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license.  You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////

#include "asyncTaskBase.h"
#include "thread.h"
#include "atomicAdjust.h"

TypeHandle AsyncTaskBase::_type_handle;

////////////////////////////////////////////////////////////////////
//     Function: AsyncTaskBase::Constructor
//       Access: Protected
//  Description:
////////////////////////////////////////////////////////////////////
AsyncTaskBase::
AsyncTaskBase() {
}

////////////////////////////////////////////////////////////////////
//     Function: AsyncTaskBase::Destructor
//       Access: Published, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
AsyncTaskBase::
~AsyncTaskBase() {
}

////////////////////////////////////////////////////////////////////
//     Function: AsyncTaskBase::record_task
//       Access: Protected
//  Description: Indicates that this task is now the current task
//               running on the indicated thread, presumably the
//               current thread.
////////////////////////////////////////////////////////////////////
void AsyncTaskBase::
record_task(Thread *current_thread) {
  nassertv(current_thread->_current_task == NULL);

  void *result = AtomicAdjust::compare_and_exchange_ptr
    ((void * TVOLATILE &)current_thread->_current_task,
     (void *)NULL, (void *)this);

  // If the return value is other than NULL, someone else must have
  // assigned the task first, in another thread.  That shouldn't be
  // possible.
  nassertv(result == NULL);
  nassertv(current_thread->_current_task == this);
}

////////////////////////////////////////////////////////////////////
//     Function: AsyncTaskBase::clear_task
//       Access: Protected
//  Description: Indicates that this task is no longer running on the
//               indicated thread.
////////////////////////////////////////////////////////////////////
void AsyncTaskBase::
clear_task(Thread *current_thread) {
  nassertv(current_thread->_current_task == this);

  void *result = AtomicAdjust::compare_and_exchange_ptr
    ((void * TVOLATILE &)current_thread->_current_task,
     (void *)this, (void *)NULL);

  // If the return value is other than this, someone else must have
  // assigned the task first, in another thread.  That shouldn't be
  // possible.
  nassertv(result == this);
  nassertv(current_thread->_current_task == NULL);
}
<commit_msg>Work around compiler optimisation bug on FreeBSD by swapping two lines of code :-/<commit_after>// Filename: asyncTaskBase.cxx
// Created by:  drose (09Feb10)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University.  All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license.  You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////

#include "asyncTaskBase.h"
#include "thread.h"
#include "atomicAdjust.h"

TypeHandle AsyncTaskBase::_type_handle;

////////////////////////////////////////////////////////////////////
//     Function: AsyncTaskBase::Constructor
//       Access: Protected
//  Description:
////////////////////////////////////////////////////////////////////
AsyncTaskBase::
AsyncTaskBase() {
}

////////////////////////////////////////////////////////////////////
//     Function: AsyncTaskBase::Destructor
//       Access: Published, Virtual
//  Description:
////////////////////////////////////////////////////////////////////
AsyncTaskBase::
~AsyncTaskBase() {
}

////////////////////////////////////////////////////////////////////
//     Function: AsyncTaskBase::record_task
//       Access: Protected
//  Description: Indicates that this task is now the current task
//               running on the indicated thread, presumably the
//               current thread.
////////////////////////////////////////////////////////////////////
void AsyncTaskBase::
record_task(Thread *current_thread) {
  nassertv(current_thread->_current_task == NULL);

  void *result = AtomicAdjust::compare_and_exchange_ptr
    ((void * TVOLATILE &)current_thread->_current_task,
     (void *)NULL, (void *)this);

  // If the return value is other than NULL, someone else must have
  // assigned the task first, in another thread.  That shouldn't be
  // possible.
  nassertv(current_thread->_current_task == this);
  nassertv(result == NULL);
}

////////////////////////////////////////////////////////////////////
//     Function: AsyncTaskBase::clear_task
//       Access: Protected
//  Description: Indicates that this task is no longer running on the
//               indicated thread.
////////////////////////////////////////////////////////////////////
void AsyncTaskBase::
clear_task(Thread *current_thread) {
  nassertv(current_thread->_current_task == this);

  void *result = AtomicAdjust::compare_and_exchange_ptr
    ((void * TVOLATILE &)current_thread->_current_task,
     (void *)this, (void *)NULL);

  // If the return value is other than this, someone else must have
  // assigned the task first, in another thread.  That shouldn't be
  // possible.
  nassertv(current_thread->_current_task == NULL);
  nassertv(result == this);
}
<|endoftext|>


<commit_before>/*
    yahoochatsession.cpp - Yahoo! Message Manager

    Copyright (c) 2005 by Andr Duffeck        <andre@duffeck.de>

    *************************************************************************
    *                                                                       *
    * This program is free software; you can redistribute it and/or modify  *
    * it under the terms of the GNU General Public License as published by  *
    * the Free Software Foundation; either version 2 of the License, or     *
    * (at your option) any later version.                                   *
    *                                                                       *
    *************************************************************************
*/

#include "yahoochatsession.h"

#include <qlabel.h>
#include <qimage.h>
#include <qtooltip.h>
#include <qfile.h>
#include <qiconset.h>

#include <kconfig.h>
#include <kdebug.h>
#include <kinputdialog.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kpopupmenu.h>
#include <ktempfile.h>
#include <kmainwindow.h>
#include <ktoolbar.h>
#include <krun.h>
#include <kiconloader.h>

#include "kopetecontactaction.h"
#include "kopetemetacontact.h"
#include "kopetecontactlist.h"
#include "kopetemessagemanagerfactory.h"
#include "kopeteuiglobal.h"
#include "kopeteglobal.h"
#include "kopeteview.h"

#include "yahoocontact.h"
#include "yahooaccount.h"

YahooChatSession::YahooChatSession( Kopete::Protocol *protocol, const Kopete::Contact *user,
	Kopete::ContactPtrList others, const char *name )
: Kopete::ChatSession( user, others, protocol,  name )
{
	kdDebug(14180) << k_funcinfo << endl;
	Kopete::ChatSessionManager::self()->registerChatSession( this );
	setInstance(protocol->instance());

	// Add Actions
	new KAction( i18n( "Buzz Contact" ), QIconSet(BarIcon("bell")), "Ctrl+G", this, SLOT( slotBuzzContact() ), actionCollection(), "yahooBuzz" ) ;
	new KAction( i18n( "Show User Info" ), QIconSet(BarIcon("idea")), 0, this, SLOT( slotUserInfo() ), actionCollection(), "yahooShowInfo" ) ;
	new KAction( i18n( "Request Webcam" ), QIconSet(BarIcon("image")), 0, this, SLOT( slotRequestWebcam() ), actionCollection(), "yahooRequestWebcam" ) ;

	YahooContact *c = static_cast<YahooContact*>( others.first() );
	connect( c, SIGNAL( displayPictureChanged() ), this, SLOT( slotDisplayPictureChanged() ) );
	m_image = new QLabel( 0L, "kde toolbar widget" );
	new KWidgetAction( m_image, i18n( "Yahoo Display Picture" ), 0, this, SLOT( slotDisplayPictureChanged() ), actionCollection(), "yahooDisplayPicture" );
	if(c->hasProperty(Kopete::Global::Properties::self()->photo().key())  )
	{
		connect( Kopete::ChatSessionManager::self() , SIGNAL(viewActivated(KopeteView* )) , this, SLOT(slotDisplayPictureChanged()) );
	}
	else
	{
		m_image = 0L;
	}

	setXMLFile("yahoochatui.rc");
}

YahooChatSession::~YahooChatSession()
{
	delete m_image;
}

void YahooChatSession::slotBuzzContact()
{
	kdDebug(14180) << k_funcinfo << endl;
	QPtrList<Kopete::Contact>contacts = members();
	static_cast<YahooContact *>(contacts.first())->buzzContact();
}

void YahooChatSession::slotUserInfo()
{
	kdDebug(14180) << k_funcinfo << endl;
	QPtrList<Kopete::Contact>contacts = members();
	static_cast<YahooContact *>(contacts.first())->slotUserInfo();
}

void YahooChatSession::slotRequestWebcam()
{
	kdDebug(14180) << k_funcinfo << endl;
	QPtrList<Kopete::Contact>contacts = members();
	static_cast<YahooContact *>(contacts.first())->requestWebcam();
}

void YahooChatSession::slotDisplayPictureChanged()
{
	kdDebug(14180) << k_funcinfo << endl;
	QPtrList<Kopete::Contact> mb=members();
	YahooContact *c = static_cast<YahooContact *>( mb.first() );
	if ( c && m_image )
	{
		if(c->hasProperty(Kopete::Global::Properties::self()->photo().key()))
		{
			int sz=22;
			// get the size of the toolbar were the aciton is plugged.
			//  if you know a better way to get the toolbar, let me know
			KMainWindow *w= view(false) ? dynamic_cast<KMainWindow*>( view(false)->mainWidget()->topLevelWidget() ) : 0L;
			if(w)
			{
				//We connected that in the constructor.  we don't need to keep this slot active.
				disconnect( Kopete::ChatSessionManager::self() , SIGNAL(viewActivated(KopeteView* )) , this, SLOT(slotDisplayPictureChanged()) );

				QPtrListIterator<KToolBar>  it=w->toolBarIterator() ;
				KAction *imgAction=actionCollection()->action("yahooDisplayPicture");
				if(imgAction)  while(it)
				{
					KToolBar *tb=*it;
					if(imgAction->isPlugged(tb))
					{
						sz=tb->iconSize();
						//ipdate if the size of the toolbar change.
						disconnect(tb, SIGNAL(modechange()), this, SLOT(slotDisplayPictureChanged()));
						connect(tb, SIGNAL(modechange()), this, SLOT(slotDisplayPictureChanged()));
						break;
					}
					++it;
				}
			}
			QString imgURL=c->property(Kopete::Global::Properties::self()->photo()).value().toString();
			QImage scaledImg = QPixmap( imgURL ).convertToImage().smoothScale( sz, sz );
			if(!scaledImg.isNull())
				m_image->setPixmap( scaledImg );
			else
			{ //the image has maybe not been transfered correctly..  force to download again
				c->removeProperty(Kopete::Global::Properties::self()->photo());
				//slotDisplayPictureChanged(); //don't do that or we might end in a infinite loop
			}
			QToolTip::add( m_image, "<qt></qt>" );
		}
	}
}

#include "yahoochatsession.moc"
<commit_msg>SVN_SILENT Fix a warning<commit_after>/*
    yahoochatsession.cpp - Yahoo! Message Manager

    Copyright (c) 2005 by Andr Duffeck        <andre@duffeck.de>

    *************************************************************************
    *                                                                       *
    * This program is free software; you can redistribute it and/or modify  *
    * it under the terms of the GNU General Public License as published by  *
    * the Free Software Foundation; either version 2 of the License, or     *
    * (at your option) any later version.                                   *
    *                                                                       *
    *************************************************************************
*/

#include "yahoochatsession.h"

#include <qlabel.h>
#include <qimage.h>
#include <qtooltip.h>
#include <qfile.h>
#include <qiconset.h>

#include <kconfig.h>
#include <kdebug.h>
#include <kinputdialog.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kpopupmenu.h>
#include <ktempfile.h>
#include <kmainwindow.h>
#include <ktoolbar.h>
#include <krun.h>
#include <kiconloader.h>

#include "kopetecontactaction.h"
#include "kopetemetacontact.h"
#include "kopetecontactlist.h"
#include "kopetechatsessionmanager.h"
#include "kopeteuiglobal.h"
#include "kopeteglobal.h"
#include "kopeteview.h"

#include "yahoocontact.h"
#include "yahooaccount.h"

YahooChatSession::YahooChatSession( Kopete::Protocol *protocol, const Kopete::Contact *user,
	Kopete::ContactPtrList others, const char *name )
: Kopete::ChatSession( user, others, protocol,  name )
{
	kdDebug(14180) << k_funcinfo << endl;
	Kopete::ChatSessionManager::self()->registerChatSession( this );
	setInstance(protocol->instance());

	// Add Actions
	new KAction( i18n( "Buzz Contact" ), QIconSet(BarIcon("bell")), "Ctrl+G", this, SLOT( slotBuzzContact() ), actionCollection(), "yahooBuzz" ) ;
	new KAction( i18n( "Show User Info" ), QIconSet(BarIcon("idea")), 0, this, SLOT( slotUserInfo() ), actionCollection(), "yahooShowInfo" ) ;
	new KAction( i18n( "Request Webcam" ), QIconSet(BarIcon("image")), 0, this, SLOT( slotRequestWebcam() ), actionCollection(), "yahooRequestWebcam" ) ;

	YahooContact *c = static_cast<YahooContact*>( others.first() );
	connect( c, SIGNAL( displayPictureChanged() ), this, SLOT( slotDisplayPictureChanged() ) );
	m_image = new QLabel( 0L, "kde toolbar widget" );
	new KWidgetAction( m_image, i18n( "Yahoo Display Picture" ), 0, this, SLOT( slotDisplayPictureChanged() ), actionCollection(), "yahooDisplayPicture" );
	if(c->hasProperty(Kopete::Global::Properties::self()->photo().key())  )
	{
		connect( Kopete::ChatSessionManager::self() , SIGNAL(viewActivated(KopeteView* )) , this, SLOT(slotDisplayPictureChanged()) );
	}
	else
	{
		m_image = 0L;
	}

	setXMLFile("yahoochatui.rc");
}

YahooChatSession::~YahooChatSession()
{
	delete m_image;
}

void YahooChatSession::slotBuzzContact()
{
	kdDebug(14180) << k_funcinfo << endl;
	QPtrList<Kopete::Contact>contacts = members();
	static_cast<YahooContact *>(contacts.first())->buzzContact();
}

void YahooChatSession::slotUserInfo()
{
	kdDebug(14180) << k_funcinfo << endl;
	QPtrList<Kopete::Contact>contacts = members();
	static_cast<YahooContact *>(contacts.first())->slotUserInfo();
}

void YahooChatSession::slotRequestWebcam()
{
	kdDebug(14180) << k_funcinfo << endl;
	QPtrList<Kopete::Contact>contacts = members();
	static_cast<YahooContact *>(contacts.first())->requestWebcam();
}

void YahooChatSession::slotDisplayPictureChanged()
{
	kdDebug(14180) << k_funcinfo << endl;
	QPtrList<Kopete::Contact> mb=members();
	YahooContact *c = static_cast<YahooContact *>( mb.first() );
	if ( c && m_image )
	{
		if(c->hasProperty(Kopete::Global::Properties::self()->photo().key()))
		{
			int sz=22;
			// get the size of the toolbar were the aciton is plugged.
			//  if you know a better way to get the toolbar, let me know
			KMainWindow *w= view(false) ? dynamic_cast<KMainWindow*>( view(false)->mainWidget()->topLevelWidget() ) : 0L;
			if(w)
			{
				//We connected that in the constructor.  we don't need to keep this slot active.
				disconnect( Kopete::ChatSessionManager::self() , SIGNAL(viewActivated(KopeteView* )) , this, SLOT(slotDisplayPictureChanged()) );

				QPtrListIterator<KToolBar>  it=w->toolBarIterator() ;
				KAction *imgAction=actionCollection()->action("yahooDisplayPicture");
				if(imgAction)  while(it)
				{
					KToolBar *tb=*it;
					if(imgAction->isPlugged(tb))
					{
						sz=tb->iconSize();
						//ipdate if the size of the toolbar change.
						disconnect(tb, SIGNAL(modechange()), this, SLOT(slotDisplayPictureChanged()));
						connect(tb, SIGNAL(modechange()), this, SLOT(slotDisplayPictureChanged()));
						break;
					}
					++it;
				}
			}
			QString imgURL=c->property(Kopete::Global::Properties::self()->photo()).value().toString();
			QImage scaledImg = QPixmap( imgURL ).convertToImage().smoothScale( sz, sz );
			if(!scaledImg.isNull())
				m_image->setPixmap( scaledImg );
			else
			{ //the image has maybe not been transfered correctly..  force to download again
				c->removeProperty(Kopete::Global::Properties::self()->photo());
				//slotDisplayPictureChanged(); //don't do that or we might end in a infinite loop
			}
			QToolTip::add( m_image, "<qt></qt>" );
		}
	}
}

#include "yahoochatsession.moc"
<|endoftext|>


<commit_before>//===-- GCSE.cpp - SSA based Global Common Subexpr Elimination ------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This pass is designed to be a very quick global transformation that
// eliminates global common subexpressions from a function.  It does this by
// using an existing value numbering implementation to identify the common
// subexpressions, eliminating them when possible.
//
//===----------------------------------------------------------------------===//

#include "llvm/Transforms/Scalar.h"
#include "llvm/BasicBlock.h"
#include "llvm/Constant.h"
#include "llvm/Instructions.h"
#include "llvm/Type.h"
#include "llvm/Analysis/Dominators.h"
#include "llvm/Analysis/ValueNumbering.h"
#include "llvm/Transforms/Utils/Local.h"
#include "Support/DepthFirstIterator.h"
#include "Support/Statistic.h"
#include <algorithm>
using namespace llvm;

namespace {
  Statistic<> NumInstRemoved("gcse", "Number of instructions removed");
  Statistic<> NumLoadRemoved("gcse", "Number of loads removed");
  Statistic<> NumCallRemoved("gcse", "Number of calls removed");
  Statistic<> NumNonInsts   ("gcse", "Number of instructions removed due "
                             "to non-instruction values");

  struct GCSE : public FunctionPass {
    virtual bool runOnFunction(Function &F);

  private:
    void ReplaceInstructionWith(Instruction *I, Value *V);

    // This transformation requires dominator and immediate dominator info
    virtual void getAnalysisUsage(AnalysisUsage &AU) const {
      AU.setPreservesCFG();
      AU.addRequired<DominatorSet>();
      AU.addRequired<DominatorTree>();
      AU.addRequired<ValueNumbering>();
    }
  };

  RegisterOpt<GCSE> X("gcse", "Global Common Subexpression Elimination");
}

// createGCSEPass - The public interface to this file...
FunctionPass *llvm::createGCSEPass() { return new GCSE(); }

// GCSE::runOnFunction - This is the main transformation entry point for a
// function.
//
bool GCSE::runOnFunction(Function &F) {
  bool Changed = false;

  // Get pointers to the analysis results that we will be using...
  DominatorSet &DS = getAnalysis<DominatorSet>();
  ValueNumbering &VN = getAnalysis<ValueNumbering>();
  DominatorTree &DT = getAnalysis<DominatorTree>();

  std::vector<Value*> EqualValues;

  // Traverse the CFG of the function in dominator order, so that we see each
  // instruction after we see its operands.
  for (df_iterator<DominatorTree::Node*> DI = df_begin(DT.getRootNode()),
         E = df_end(DT.getRootNode()); DI != E; ++DI) {
    BasicBlock *BB = DI->getBlock();

    // Remember which instructions we've seen in this basic block as we scan.
    std::set<Instruction*> BlockInsts;

    for (BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ) {
      Instruction *Inst = I++;

      // If this instruction computes a value, try to fold together common
      // instructions that compute it.
      //
      if (Inst->getType() != Type::VoidTy) {
        VN.getEqualNumberNodes(Inst, EqualValues);

        // If this instruction computes a value that is already computed
        // elsewhere, try to recycle the old value.
        if (!EqualValues.empty()) {
          if (Inst == &*BB->begin())
            I = BB->end();
          else {
            I = Inst; --I;
          }

          // First check to see if we were able to value number this instruction
          // to a non-instruction value.  If so, prefer that value over other
          // instructions which may compute the same thing.
          for (unsigned i = 0, e = EqualValues.size(); i != e; ++i)
            if (!isa<Instruction>(EqualValues[i])) {
              ++NumNonInsts;      // Keep track of # of insts repl with values

              // Change all users of Inst to use the replacement and remove it
              // from the program.
              ReplaceInstructionWith(Inst, EqualValues[i]);
              Inst = 0;
              EqualValues.clear();  // don't enter the next loop
              break;
            }

          // If there were no non-instruction values that this instruction
          // produces, find a dominating instruction that produces the same
          // value.  If we find one, use it's value instead of ours.
          for (unsigned i = 0, e = EqualValues.size(); i != e; ++i) {
            Instruction *OtherI = cast<Instruction>(EqualValues[i]);
            bool Dominates = false;
            if (OtherI->getParent() == BB)
              Dominates = BlockInsts.count(OtherI);
            else
              Dominates = DS.dominates(OtherI->getParent(), BB);

            if (Dominates) {
              // Okay, we found an instruction with the same value as this one
              // and that dominates this one.  Replace this instruction with the
              // specified one.
              ReplaceInstructionWith(Inst, OtherI);
              Inst = 0;
              break;
            }
          }

          EqualValues.clear();

          if (Inst) {
            I = Inst; ++I;             // Deleted no instructions
          } else if (I == BB->end()) { // Deleted first instruction
            I = BB->begin();
          } else {                     // Deleted inst in middle of block.
            ++I;
          }
        }

        if (Inst)
          BlockInsts.insert(Inst);
      }
    }
  }

  // When the worklist is empty, return whether or not we changed anything...
  return Changed;
}

void GCSE::ReplaceInstructionWith(Instruction *I, Value *V) {
  if (isa<LoadInst>(I))
    ++NumLoadRemoved; // Keep track of loads eliminated
  if (isa<CallInst>(I))
    ++NumCallRemoved; // Keep track of calls eliminated
  ++NumInstRemoved;   // Keep track of number of insts eliminated

  // Update value numbering
  getAnalysis<ValueNumbering>().deleteInstruction(I);

  // If we are not replacing the instruction with a constant, we cannot do
  // anything special.
  if (!isa<Constant>(V)) {
    I->replaceAllUsesWith(V);

    // Erase the instruction from the program.
    I->getParent()->getInstList().erase(I);
    return;
  }

  Constant *C = cast<Constant>(V);
  std::vector<User*> Users(I->use_begin(), I->use_end());

  // Perform the replacement.
  I->replaceAllUsesWith(C);

  // Erase the instruction from the program.
  I->getParent()->getInstList().erase(I);

  // Check each user to see if we can constant fold it.
  while (!Users.empty()) {
    Instruction *U = cast<Instruction>(Users.back());
    Users.pop_back();

    if (Constant *C = ConstantFoldInstruction(U)) {
      ReplaceInstructionWith(U, C);

      // If the instruction used I more than once, it could be on the user list
      // multiple times.  Make sure we don't reprocess it.
      std::vector<User*>::iterator It = std::find(Users.begin(), Users.end(),U);
      while (It != Users.end()) {
        Users.erase(It);
        It = std::find(Users.begin(), Users.end(), U);
      }
    }
  }
}
<commit_msg>Add support for removing invoke instructions<commit_after>//===-- GCSE.cpp - SSA based Global Common Subexpr Elimination ------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This pass is designed to be a very quick global transformation that
// eliminates global common subexpressions from a function.  It does this by
// using an existing value numbering implementation to identify the common
// subexpressions, eliminating them when possible.
//
//===----------------------------------------------------------------------===//

#include "llvm/Transforms/Scalar.h"
#include "llvm/BasicBlock.h"
#include "llvm/Constant.h"
#include "llvm/Instructions.h"
#include "llvm/Type.h"
#include "llvm/Analysis/Dominators.h"
#include "llvm/Analysis/ValueNumbering.h"
#include "llvm/Transforms/Utils/Local.h"
#include "Support/DepthFirstIterator.h"
#include "Support/Statistic.h"
#include <algorithm>
using namespace llvm;

namespace {
  Statistic<> NumInstRemoved("gcse", "Number of instructions removed");
  Statistic<> NumLoadRemoved("gcse", "Number of loads removed");
  Statistic<> NumCallRemoved("gcse", "Number of calls removed");
  Statistic<> NumNonInsts   ("gcse", "Number of instructions removed due "
                             "to non-instruction values");

  struct GCSE : public FunctionPass {
    virtual bool runOnFunction(Function &F);

  private:
    void ReplaceInstructionWith(Instruction *I, Value *V);

    // This transformation requires dominator and immediate dominator info
    virtual void getAnalysisUsage(AnalysisUsage &AU) const {
      AU.setPreservesCFG();
      AU.addRequired<DominatorSet>();
      AU.addRequired<DominatorTree>();
      AU.addRequired<ValueNumbering>();
    }
  };

  RegisterOpt<GCSE> X("gcse", "Global Common Subexpression Elimination");
}

// createGCSEPass - The public interface to this file...
FunctionPass *llvm::createGCSEPass() { return new GCSE(); }

// GCSE::runOnFunction - This is the main transformation entry point for a
// function.
//
bool GCSE::runOnFunction(Function &F) {
  bool Changed = false;

  // Get pointers to the analysis results that we will be using...
  DominatorSet &DS = getAnalysis<DominatorSet>();
  ValueNumbering &VN = getAnalysis<ValueNumbering>();
  DominatorTree &DT = getAnalysis<DominatorTree>();

  std::vector<Value*> EqualValues;

  // Traverse the CFG of the function in dominator order, so that we see each
  // instruction after we see its operands.
  for (df_iterator<DominatorTree::Node*> DI = df_begin(DT.getRootNode()),
         E = df_end(DT.getRootNode()); DI != E; ++DI) {
    BasicBlock *BB = DI->getBlock();

    // Remember which instructions we've seen in this basic block as we scan.
    std::set<Instruction*> BlockInsts;

    for (BasicBlock::iterator I = BB->begin(), E = BB->end(); I != E; ) {
      Instruction *Inst = I++;

      // If this instruction computes a value, try to fold together common
      // instructions that compute it.
      //
      if (Inst->getType() != Type::VoidTy) {
        VN.getEqualNumberNodes(Inst, EqualValues);

        // If this instruction computes a value that is already computed
        // elsewhere, try to recycle the old value.
        if (!EqualValues.empty()) {
          if (Inst == &*BB->begin())
            I = BB->end();
          else {
            I = Inst; --I;
          }

          // First check to see if we were able to value number this instruction
          // to a non-instruction value.  If so, prefer that value over other
          // instructions which may compute the same thing.
          for (unsigned i = 0, e = EqualValues.size(); i != e; ++i)
            if (!isa<Instruction>(EqualValues[i])) {
              ++NumNonInsts;      // Keep track of # of insts repl with values

              // Change all users of Inst to use the replacement and remove it
              // from the program.
              ReplaceInstructionWith(Inst, EqualValues[i]);
              Inst = 0;
              EqualValues.clear();  // don't enter the next loop
              break;
            }

          // If there were no non-instruction values that this instruction
          // produces, find a dominating instruction that produces the same
          // value.  If we find one, use it's value instead of ours.
          for (unsigned i = 0, e = EqualValues.size(); i != e; ++i) {
            Instruction *OtherI = cast<Instruction>(EqualValues[i]);
            bool Dominates = false;
            if (OtherI->getParent() == BB)
              Dominates = BlockInsts.count(OtherI);
            else
              Dominates = DS.dominates(OtherI->getParent(), BB);

            if (Dominates) {
              // Okay, we found an instruction with the same value as this one
              // and that dominates this one.  Replace this instruction with the
              // specified one.
              ReplaceInstructionWith(Inst, OtherI);
              Inst = 0;
              break;
            }
          }

          EqualValues.clear();

          if (Inst) {
            I = Inst; ++I;             // Deleted no instructions
          } else if (I == BB->end()) { // Deleted first instruction
            I = BB->begin();
          } else {                     // Deleted inst in middle of block.
            ++I;
          }
        }

        if (Inst)
          BlockInsts.insert(Inst);
      }
    }
  }

  // When the worklist is empty, return whether or not we changed anything...
  return Changed;
}

void GCSE::ReplaceInstructionWith(Instruction *I, Value *V) {
  if (isa<LoadInst>(I))
    ++NumLoadRemoved; // Keep track of loads eliminated
  if (isa<CallInst>(I))
    ++NumCallRemoved; // Keep track of calls eliminated
  ++NumInstRemoved;   // Keep track of number of insts eliminated

  // Update value numbering
  getAnalysis<ValueNumbering>().deleteInstruction(I);

  // If we are not replacing the instruction with a constant, we cannot do
  // anything special.
  if (!isa<Constant>(V)) {
    I->replaceAllUsesWith(V);

    if (InvokeInst *II = dyn_cast<InvokeInst>(I)) {
      // Removing an invoke instruction requires adding a branch to the normal
      // destination and removing PHI node entries in the exception destination.
      new BranchInst(II->getNormalDest(), II);
      II->getUnwindDest()->removePredecessor(II->getParent());
    }

    // Erase the instruction from the program.
    I->getParent()->getInstList().erase(I);
    return;
  }

  Constant *C = cast<Constant>(V);
  std::vector<User*> Users(I->use_begin(), I->use_end());

  // Perform the replacement.
  I->replaceAllUsesWith(C);

  if (InvokeInst *II = dyn_cast<InvokeInst>(I)) {
    // Removing an invoke instruction requires adding a branch to the normal
    // destination and removing PHI node entries in the exception destination.
    new BranchInst(II->getNormalDest(), II);
    II->getUnwindDest()->removePredecessor(II->getParent());
  }

  // Erase the instruction from the program.
  I->getParent()->getInstList().erase(I);

  // Check each user to see if we can constant fold it.
  while (!Users.empty()) {
    Instruction *U = cast<Instruction>(Users.back());
    Users.pop_back();

    if (Constant *C = ConstantFoldInstruction(U)) {
      ReplaceInstructionWith(U, C);

      // If the instruction used I more than once, it could be on the user list
      // multiple times.  Make sure we don't reprocess it.
      std::vector<User*>::iterator It = std::find(Users.begin(), Users.end(),U);
      while (It != Users.end()) {
        Users.erase(It);
        It = std::find(Users.begin(), Users.end(), U);
      }
    }
  }
}
<|endoftext|>


<commit_before>
/*=============================================================================
 *
 *
 * Runtime
 *
 *
 =============================================================================*/

//----------------------------------------------------------------------------
// Debug routines
//----------------------------------------------------------------------------
#ifndef DEBUG_SERIAL
#  define DEBUG_SERIAL Serial
#endif

#ifdef XOD_DEBUG
#  define XOD_TRACE(x)      { DEBUG_SERIAL.print(x); DEBUG_SERIAL.flush(); }
#  define XOD_TRACE_LN(x)   { DEBUG_SERIAL.println(x); DEBUG_SERIAL.flush(); }
#  define XOD_TRACE_F(x)    XOD_TRACE(F(x))
#  define XOD_TRACE_FLN(x)  XOD_TRACE_LN(F(x))
#else
#  define XOD_TRACE(x)
#  define XOD_TRACE_LN(x)
#  define XOD_TRACE_F(x)
#  define XOD_TRACE_FLN(x)
#endif

//----------------------------------------------------------------------------
// PGM space utilities
//----------------------------------------------------------------------------
#define pgm_read_nodeid(address) (pgm_read_word(address))

/*
 * Workaround for bugs:
 * https://github.com/arduino/ArduinoCore-sam/pull/43
 * https://github.com/arduino/ArduinoCore-samd/pull/253
 * Remove after the PRs merge
 */
#if !defined(ARDUINO_ARCH_AVR) && defined(pgm_read_ptr)
#  undef pgm_read_ptr
#  define pgm_read_ptr(addr) (*(const void **)(addr))
#endif

namespace xod {
//----------------------------------------------------------------------------
// Type definitions
//----------------------------------------------------------------------------
#define NO_NODE                 ((NodeId)-1)

typedef double Number;
typedef bool Logic;

// TODO: optimize, we should choose uint8_t if there are less than 255 nodes in total
// and uint32_t if there are more than 65535
typedef uint16_t NodeId;

typedef NodeId Context;

// TODO: optimize, we should choose a proper type with a minimal enough capacity
/*
 * LSB of a dirty flag shows whether a particular node is dirty or not
 * Other bits shows dirtieness of its particular outputs:
 * - 1-st bit for 0-th output
 * - 2-nd bit for 1-st output
 * - etc
 */
typedef uint16_t DirtyFlags;

typedef unsigned long TimeMs;
typedef void (*EvalFuncPtr)(Context ctx);

typedef xod::List<char>::ListPtr XString;

/*
 * Each input stores a reference to its upstream node so that we can get values
 * on input pins. Having a direct pointer to the value is not enough because we
 * want to know dirty’ness as well. So we have to use this structure instead of
 * a pointer.
 */
struct UpstreamPinRef {
    // Upstream node ID
    NodeId nodeId;
    // Index of the upstream node’s output.
    // Use 3 bits as it just enough to store values 0..7
    uint16_t pinIndex : 3;
    // Byte offset in a storage of the upstream node where the actual pin value
    // is stored
    uint16_t storageOffset : 13;
};

/*
 * Input descriptor is a metaprogramming structure used to enforce an
 * input’s type and store its wiring data location as a zero-RAM constant.
 *
 * A specialized descriptor is required by `getValue` function. Every
 * input of every type node gets its own descriptor in generated code that
 * can be accessed as input_FOO. Where FOO is a pin identifier.
 */
template<typename ValueT_, size_t wiringOffset>
struct InputDescriptor {
    typedef ValueT_ ValueT;
    enum {
        WIRING_OFFSET = wiringOffset
    };
};

/*
 * Output descriptor serve the same purpose as InputDescriptor but for
 * ouputs.
 *
 * In addition to wiring data location it keeps storage data location (where
 * actual value is stored) and own zero-based index among outputs of a particular
 * node
 */
template<typename ValueT_, size_t wiringOffset, size_t storageOffset, uint8_t index>
struct OutputDescriptor {
    typedef ValueT_ ValueT;
    enum {
        WIRING_OFFSET = wiringOffset,
        STORAGE_OFFSET = storageOffset,
        INDEX = index
    };
};

//----------------------------------------------------------------------------
// Forward declarations
//----------------------------------------------------------------------------
extern void* const g_storages[NODE_COUNT];
extern const void* const g_wiring[NODE_COUNT];
extern DirtyFlags g_dirtyFlags[NODE_COUNT];

// TOOD: get rid of an extra indirection layer completely
// would save 2 bytes per node
extern NodeId g_topology[NODE_COUNT];

// TODO: replace with a compact list
extern TimeMs g_schedule[NODE_COUNT];

void clearTimeout(NodeId nid);

//----------------------------------------------------------------------------
// Engine (private API)
//----------------------------------------------------------------------------

void* getStoragePtr(NodeId nid, size_t offset) {
    return (uint8_t*)pgm_read_ptr(&g_storages[nid]) + offset;
}

template<typename T>
T getStorageValue(NodeId nid, size_t offset) {
    return *reinterpret_cast<T*>(getStoragePtr(nid, offset));
}

void* getWiringPgmPtr(NodeId nid, size_t offset) {
    return (uint8_t*)pgm_read_ptr(&g_wiring[nid]) + offset;
}

template<typename T>
T getWiringValue(NodeId nid, size_t offset) {
    T result;
    memcpy_P(&result, getWiringPgmPtr(nid, offset), sizeof(T));
    return result;
}

bool isOutputDirty(NodeId nid, uint8_t index) {
    return g_dirtyFlags[nid] & (1 << (index + 1));
}

bool isInputDirtyImpl(NodeId nid, size_t wiringOffset) {
    UpstreamPinRef ref = getWiringValue<UpstreamPinRef>(nid, wiringOffset);
    if (ref.nodeId == NO_NODE)
        return false;

    return isOutputDirty(ref.nodeId, ref.pinIndex);
}

template<typename InputT>
bool isInputDirty(NodeId nid) {
    return isInputDirtyImpl(nid, InputT::WIRING_OFFSET);
}

void markPinDirty(NodeId nid, uint8_t index) {
    g_dirtyFlags[nid] |= 1 << (index + 1);
}

void markNodeDirty(NodeId nid) {
    g_dirtyFlags[nid] |= 0x1;
}

bool isNodeDirty(NodeId nid) {
    return g_dirtyFlags[nid] & 0x1;
}

template<typename T>
T getOutputValueImpl(NodeId nid, size_t storageOffset) {
    return getStorageValue<T>(nid, storageOffset);
}

template<typename T>
T getInputValueImpl(NodeId nid, size_t wiringOffset) {
    UpstreamPinRef ref = getWiringValue<UpstreamPinRef>(nid, wiringOffset);
    if (ref.nodeId == NO_NODE)
        return (T)0;

    return getOutputValueImpl<T>(ref.nodeId, ref.storageOffset);
}

template<typename T>
void emitValueImpl(
        NodeId nid,
        size_t storageOffset,
        size_t wiringOffset,
        uint8_t index,
        T value) {

    // Store new value and make the node itself dirty
    T* storedValue = reinterpret_cast<T*>(getStoragePtr(nid, storageOffset));
    *storedValue = value;
    markPinDirty(nid, index);

    // Notify downstream nodes about changes
    // NB: linked nodes array is in PGM space
    const NodeId* pDownstreamNid = getWiringValue<const NodeId*>(nid, wiringOffset);
    NodeId downstreamNid = pgm_read_nodeid(pDownstreamNid);

    while (downstreamNid != NO_NODE) {
        markNodeDirty(downstreamNid);
        downstreamNid = pgm_read_nodeid(pDownstreamNid++);
    }
}

void evaluateNode(NodeId nid) {
    XOD_TRACE_F("eval #");
    XOD_TRACE_LN(nid);
    EvalFuncPtr eval = getWiringValue<EvalFuncPtr>(nid, 0);
    eval(nid);
}

void runTransaction() {
    XOD_TRACE_F("Transaction started, t=");
    XOD_TRACE_LN(millis());
    for (NodeId nid : g_topology) {
        if (isNodeDirty(nid))
            evaluateNode(nid);
    }

    memset(g_dirtyFlags, 0, sizeof(g_dirtyFlags));
    XOD_TRACE_F("Transaction completed, t=");
    XOD_TRACE_LN(millis());
}

void idle() {
    TimeMs now = millis();
    for (NodeId nid = 0; nid < NODE_COUNT; ++nid) {
        TimeMs t = g_schedule[nid];
        if (t && t <= now) {
            markNodeDirty(nid);
            clearTimeout(nid);
            return;
        }
    }
}

//----------------------------------------------------------------------------
// Public API (can be used by native nodes’ `evaluate` functions)
//----------------------------------------------------------------------------

template<typename InputT>
typename InputT::ValueT getValue(NodeId nid) {
    return getInputValueImpl<typename InputT::ValueT>(nid, InputT::WIRING_OFFSET);
}

template<typename OutputT>
void emitValue(NodeId nid, typename OutputT::ValueT value) {
    emitValueImpl(
            nid,
            OutputT::STORAGE_OFFSET,
            OutputT::WIRING_OFFSET,
            OutputT::INDEX,
            value);
}

TimeMs transactionTime() {
    return millis();
}

void setTimeout(NodeId nid, TimeMs timeout) {
    g_schedule[nid] = transactionTime() + timeout;
}

void clearTimeout(NodeId nid) {
    g_schedule[nid] = 0;
}

} // namespace xod

//----------------------------------------------------------------------------
// Entry point
//----------------------------------------------------------------------------
void setup() {
    // FIXME: looks like there is a rounding bug. Waiting for 1 second fights it
    delay(1000);
#ifdef XOD_DEBUG
    DEBUG_SERIAL.begin(9600);
#endif
    XOD_TRACE_FLN("\n\nProgram started");
}

void loop() {
    xod::idle();
    xod::runTransaction();
}
<commit_msg>optimize(rt/c++): adjust data type sizes to improve RAM compactness<commit_after>
/*=============================================================================
 *
 *
 * Runtime
 *
 *
 =============================================================================*/

//----------------------------------------------------------------------------
// Debug routines
//----------------------------------------------------------------------------
#ifndef DEBUG_SERIAL
#  define DEBUG_SERIAL Serial
#endif

#ifdef XOD_DEBUG
#  define XOD_TRACE(x)      { DEBUG_SERIAL.print(x); DEBUG_SERIAL.flush(); }
#  define XOD_TRACE_LN(x)   { DEBUG_SERIAL.println(x); DEBUG_SERIAL.flush(); }
#  define XOD_TRACE_F(x)    XOD_TRACE(F(x))
#  define XOD_TRACE_FLN(x)  XOD_TRACE_LN(F(x))
#else
#  define XOD_TRACE(x)
#  define XOD_TRACE_LN(x)
#  define XOD_TRACE_F(x)
#  define XOD_TRACE_FLN(x)
#endif

//----------------------------------------------------------------------------
// PGM space utilities
//----------------------------------------------------------------------------
#define pgm_read_nodeid(address) (pgm_read_word(address))

/*
 * Workaround for bugs:
 * https://github.com/arduino/ArduinoCore-sam/pull/43
 * https://github.com/arduino/ArduinoCore-samd/pull/253
 * Remove after the PRs merge
 */
#if !defined(ARDUINO_ARCH_AVR) && defined(pgm_read_ptr)
#  undef pgm_read_ptr
#  define pgm_read_ptr(addr) (*(const void **)(addr))
#endif

namespace xod {
//----------------------------------------------------------------------------
// Type definitions
//----------------------------------------------------------------------------
#define NO_NODE                 ((NodeId)-1)

typedef double Number;
typedef bool Logic;

#if NODE_COUNT < 256
typedef uint8_t NodeId;
#elif NODE_COUNT < 65536
typedef uint16_t NodeId;
#else
typedef uint32_t NodeId;
#endif

/*
 * Context is a handle passed to each node `evaluate` function. Currently, it’s
 * alias for NodeId but likely will be changed in future to support list
 * lifting and other features
 */
typedef NodeId Context;

/*
 * LSB of a dirty flag shows whether a particular node is dirty or not
 * Other bits shows dirtieness of its particular outputs:
 * - 1-st bit for 0-th output
 * - 2-nd bit for 1-st output
 * - etc
 *
 * An outcome limitation is that a native node must not have more than 7 output
 * pins.
 */
typedef uint8_t DirtyFlags;

typedef unsigned long TimeMs;
typedef void (*EvalFuncPtr)(Context ctx);

typedef xod::List<char>::ListPtr XString;

/*
 * Each input stores a reference to its upstream node so that we can get values
 * on input pins. Having a direct pointer to the value is not enough because we
 * want to know dirty’ness as well. So we have to use this structure instead of
 * a pointer.
 */
struct UpstreamPinRef {
    // Upstream node ID
    NodeId nodeId;
    // Index of the upstream node’s output.
    // Use 3 bits as it just enough to store values 0..7
    uint16_t pinIndex : 3;
    // Byte offset in a storage of the upstream node where the actual pin value
    // is stored
    uint16_t storageOffset : 13;
};

/*
 * Input descriptor is a metaprogramming structure used to enforce an
 * input’s type and store its wiring data location as a zero-RAM constant.
 *
 * A specialized descriptor is required by `getValue` function. Every
 * input of every type node gets its own descriptor in generated code that
 * can be accessed as input_FOO. Where FOO is a pin identifier.
 */
template<typename ValueT_, size_t wiringOffset>
struct InputDescriptor {
    typedef ValueT_ ValueT;
    enum {
        WIRING_OFFSET = wiringOffset
    };
};

/*
 * Output descriptor serve the same purpose as InputDescriptor but for
 * ouputs.
 *
 * In addition to wiring data location it keeps storage data location (where
 * actual value is stored) and own zero-based index among outputs of a particular
 * node
 */
template<typename ValueT_, size_t wiringOffset, size_t storageOffset, uint8_t index>
struct OutputDescriptor {
    typedef ValueT_ ValueT;
    enum {
        WIRING_OFFSET = wiringOffset,
        STORAGE_OFFSET = storageOffset,
        INDEX = index
    };
};

//----------------------------------------------------------------------------
// Forward declarations
//----------------------------------------------------------------------------
extern void* const g_storages[NODE_COUNT];
extern const void* const g_wiring[NODE_COUNT];
extern DirtyFlags g_dirtyFlags[NODE_COUNT];

// TODO: get rid of an extra indirection layer completely
// would save 2 bytes per node
extern NodeId g_topology[NODE_COUNT];

// TODO: replace with a compact list
extern TimeMs g_schedule[NODE_COUNT];

void clearTimeout(NodeId nid);

//----------------------------------------------------------------------------
// Engine (private API)
//----------------------------------------------------------------------------

void* getStoragePtr(NodeId nid, size_t offset) {
    return (uint8_t*)pgm_read_ptr(&g_storages[nid]) + offset;
}

template<typename T>
T getStorageValue(NodeId nid, size_t offset) {
    return *reinterpret_cast<T*>(getStoragePtr(nid, offset));
}

void* getWiringPgmPtr(NodeId nid, size_t offset) {
    return (uint8_t*)pgm_read_ptr(&g_wiring[nid]) + offset;
}

template<typename T>
T getWiringValue(NodeId nid, size_t offset) {
    T result;
    memcpy_P(&result, getWiringPgmPtr(nid, offset), sizeof(T));
    return result;
}

bool isOutputDirty(NodeId nid, uint8_t index) {
    return g_dirtyFlags[nid] & (1 << (index + 1));
}

bool isInputDirtyImpl(NodeId nid, size_t wiringOffset) {
    UpstreamPinRef ref = getWiringValue<UpstreamPinRef>(nid, wiringOffset);
    if (ref.nodeId == NO_NODE)
        return false;

    return isOutputDirty(ref.nodeId, ref.pinIndex);
}

template<typename InputT>
bool isInputDirty(NodeId nid) {
    return isInputDirtyImpl(nid, InputT::WIRING_OFFSET);
}

void markPinDirty(NodeId nid, uint8_t index) {
    g_dirtyFlags[nid] |= 1 << (index + 1);
}

void markNodeDirty(NodeId nid) {
    g_dirtyFlags[nid] |= 0x1;
}

bool isNodeDirty(NodeId nid) {
    return g_dirtyFlags[nid] & 0x1;
}

template<typename T>
T getOutputValueImpl(NodeId nid, size_t storageOffset) {
    return getStorageValue<T>(nid, storageOffset);
}

template<typename T>
T getInputValueImpl(NodeId nid, size_t wiringOffset) {
    UpstreamPinRef ref = getWiringValue<UpstreamPinRef>(nid, wiringOffset);
    if (ref.nodeId == NO_NODE)
        return (T)0;

    return getOutputValueImpl<T>(ref.nodeId, ref.storageOffset);
}

template<typename T>
void emitValueImpl(
        NodeId nid,
        size_t storageOffset,
        size_t wiringOffset,
        uint8_t index,
        T value) {

    // Store new value and make the node itself dirty
    T* storedValue = reinterpret_cast<T*>(getStoragePtr(nid, storageOffset));
    *storedValue = value;
    markPinDirty(nid, index);

    // Notify downstream nodes about changes
    // NB: linked nodes array is in PGM space
    const NodeId* pDownstreamNid = getWiringValue<const NodeId*>(nid, wiringOffset);
    NodeId downstreamNid = pgm_read_nodeid(pDownstreamNid);

    while (downstreamNid != NO_NODE) {
        markNodeDirty(downstreamNid);
        downstreamNid = pgm_read_nodeid(pDownstreamNid++);
    }
}

void evaluateNode(NodeId nid) {
    XOD_TRACE_F("eval #");
    XOD_TRACE_LN(nid);
    EvalFuncPtr eval = getWiringValue<EvalFuncPtr>(nid, 0);
    eval(nid);
}

void runTransaction() {
    XOD_TRACE_F("Transaction started, t=");
    XOD_TRACE_LN(millis());
    for (NodeId nid : g_topology) {
        if (isNodeDirty(nid))
            evaluateNode(nid);
    }

    memset(g_dirtyFlags, 0, sizeof(g_dirtyFlags));
    XOD_TRACE_F("Transaction completed, t=");
    XOD_TRACE_LN(millis());
}

void idle() {
    TimeMs now = millis();
    for (NodeId nid = 0; nid < NODE_COUNT; ++nid) {
        TimeMs t = g_schedule[nid];
        if (t && t <= now) {
            markNodeDirty(nid);
            clearTimeout(nid);
            return;
        }
    }
}

//----------------------------------------------------------------------------
// Public API (can be used by native nodes’ `evaluate` functions)
//----------------------------------------------------------------------------

template<typename InputT>
typename InputT::ValueT getValue(NodeId nid) {
    return getInputValueImpl<typename InputT::ValueT>(nid, InputT::WIRING_OFFSET);
}

template<typename OutputT>
void emitValue(NodeId nid, typename OutputT::ValueT value) {
    emitValueImpl(
            nid,
            OutputT::STORAGE_OFFSET,
            OutputT::WIRING_OFFSET,
            OutputT::INDEX,
            value);
}

TimeMs transactionTime() {
    return millis();
}

void setTimeout(NodeId nid, TimeMs timeout) {
    g_schedule[nid] = transactionTime() + timeout;
}

void clearTimeout(NodeId nid) {
    g_schedule[nid] = 0;
}

} // namespace xod

//----------------------------------------------------------------------------
// Entry point
//----------------------------------------------------------------------------
void setup() {
    // FIXME: looks like there is a rounding bug. Waiting for 1 second fights it
    delay(1000);
#ifdef XOD_DEBUG
    DEBUG_SERIAL.begin(9600);
#endif
    XOD_TRACE_FLN("\n\nProgram started");
}

void loop() {
    xod::idle();
    xod::runTransaction();
}
<|endoftext|>