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

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 "paddle/operators/lstm_op.h"

namespace paddle {
namespace operators {

class LSTMOp : public framework::OperatorWithKernel {
 public:
  using framework::OperatorWithKernel::OperatorWithKernel;

  void InferShape(framework::InferShapeContext* ctx) const override {
    PADDLE_ENFORCE(ctx->HasInput("Input"),
                   "Input(Input) of LSTM should not be null.");
    PADDLE_ENFORCE(ctx->HasInput("Weight"),
                   "Input(Weight) of LSTM should not be null.");
    PADDLE_ENFORCE(ctx->HasInput("Bias"),
                   "Input(Bias) of LSTM should not be null.");

    PADDLE_ENFORCE(ctx->HasOutput("Hidden"),
                   "Output(Hidden) of LSTM should not be null.");
    PADDLE_ENFORCE(ctx->HasOutput("Cell"),
                   "Output(Cell) of LSTM should not be null.");
    PADDLE_ENFORCE(ctx->HasOutput("BatchGate"),
                   "Output(BatchGate) of LSTM should not be null.");
    PADDLE_ENFORCE(ctx->HasOutput("BatchCellPreAct"),
                   "Output(BatchGate) of LSTM should not be null.");

    auto in_dims = ctx->GetInputDim("Input");
    PADDLE_ENFORCE_EQ(in_dims.size(), 2, "Input(X)'s rank must be 2.");

    if (ctx->HasInput("H0")) {
      PADDLE_ENFORCE(ctx->HasInput("C0"),
                     "Input(Cell) and Input(Hidden) of LSTM should not "
                     "be null at the same time.");
      auto h_dims = ctx->GetInputDim("H0");
      auto c_dims = ctx->GetInputDim("C0");
      PADDLE_ENFORCE(h_dims == c_dims,
                     "The dimension of Input(H0) and Input(C0) "
                     "should be the same.");
    }

    int frame_size = in_dims[1] / 4;
    auto w_dims = ctx->GetInputDim("Weight");
    PADDLE_ENFORCE_EQ(w_dims.size(), 2,
                      "The rank of Input(Weight) should be 2.");
    PADDLE_ENFORCE_EQ(w_dims[0], frame_size,
                      "The first dimension of Input(Weight) "
                      "should be %d.",
                      frame_size);
    PADDLE_ENFORCE_EQ(w_dims[1], 4 * frame_size,
                      "The second dimension of Input(Weight) "
                      "should be 4 * %d.",
                      frame_size);

    auto b_dims = ctx->GetInputDim("Bias");
    PADDLE_ENFORCE_EQ(b_dims.size(), 2, "The rank of Input(Bias) should be 2.");
    PADDLE_ENFORCE_EQ(b_dims[0], 1,
                      "The first dimension of Input(Bias) should be 1.");

    if (ctx->Attrs().Get<bool>("use_peepholes")) {
      PADDLE_ENFORCE_EQ(b_dims[1], 7 * frame_size,
                        "The second dimension of Input(Bias) should be "
                        "7 * %d if enable peepholes connection",
                        frame_size);
    } else {
      PADDLE_ENFORCE_EQ(b_dims[1], 4 * frame_size,
                        "The second dimension of Input(Bias) should be "
                        "4 * %d if disable peepholes connection",
                        frame_size);
    }

    framework::DDim out_dims({in_dims[0], frame_size});
    ctx->SetOutputDim("Hidden", out_dims);
    ctx->SetOutputDim("Cell", out_dims);
    ctx->SetOutputDim("BatchGate", in_dims);
    ctx->SetOutputDim("BatchCellPreAct", out_dims);
    ctx->ShareLoD("Input", "Hidden");
    ctx->ShareLoD("Input", "Cell");
  }

 protected:
  framework::OpKernelType GetKernelType(
      const framework::ExecutionContext& ctx) const override {
    return framework::OpKernelType(
        framework::ToDataType(ctx.Input<framework::LoDTensor>("Input")->type()),
        ctx.device_context());
  }
};

class LSTMOpMaker : public framework::OpProtoAndCheckerMaker {
 public:
  LSTMOpMaker(framework::OpProto* proto, framework::OpAttrChecker* op_checker)
      : OpProtoAndCheckerMaker(proto, op_checker) {
    AddInput("Input",
             "(LoDTensor) the first input is a LodTensor, which support "
             "variable-time length input sequence. The underlying tensor in "
             "this LoDTensor is a matrix with shape (T X 4D), where T is the "
             "total time steps in this mini-batch, D is the hidden size.");
    AddInput("H0",
             "(Tensor, optional) the initial hidden state is an optional "
             "input. This is a tensor with shape (N x D), where N is the "
             "batch size and D is the hidden size.")
        .AsDispensable();
    AddInput("C0",
             "(Tensor, optional) the initial cell state is an optional "
             "input. This is a tensor with shape (N x D), where N is the "
             "batch size. `H0` and `C0` can be NULL but only at the same time")
        .AsDispensable();
    AddInput("Weight",
             "(Tensor) the learnable hidden-hidden weights."
             " - The shape is (D x 4D), where D is the hidden size. "
             " - Weight = {W_ch, W_ih, W_fh, W_oh}");
    AddInput("Bias",
             "(Tensor) the learnable weights, which contains two parts: "
             "input-hidden bias weight and peephole connections weight if "
             "setting `use_peepholes` True. "
             "1. `use_peepholes = False` "
             " - The shape is (1 x 4D). "
             " - Bias = {b_c, b_i, b_f, b_o}."
             "2. `use_peepholes = True` "
             " - The shape is (1 x 7D). "
             " - Bias = {b_c, b_i, b_f, b_o, W_ic, W_fc, W_oc}.");
    AddOutput("Hidden",
              "(LoDTensor) the hidden state of LSTM operator. "
              "The shape is (T x D), and lod is the same with the `Input`.");
    AddOutput("Cell",
              "(LoDTensor) the cell state of LSTM operator. "
              "The shape is (T x D), and lod is the same with the `Input`.");
    AddOutput("BatchGate",
              "(LoDTensor) This LoDTensor contains input gate, forget gate "
              "and output gate after the nonlinear computation. This "
              "LoDTensor has the same shape as the reorganized input, which "
              "is also be called batch input. The LoD size is 2. The first "
              "LoD is the batch offsets and the second LoD contains the "
              "indexes, which denote the position of reorganized sequence "
              "in the raw input.")
        .AsIntermediate();
    AddOutput("BatchCellPreAct",
              "(LoDTensor) This LoDTensor is obtained in the forward and used "
              "in the backward.")
        .AsIntermediate();
    AddAttr<bool>("use_peepholes",
                  "(bool, defalut: True) "
                  "whether to enable diagonal/peephole connections.")
        .SetDefault(true);
    AddAttr<bool>("is_reverse",
                  "(bool, defalut: False) "
                  "whether to compute reversed LSTM.")
        .SetDefault(false);
    AddAttr<std::string>(
        "gate_activation",
        "(string, default: sigmoid)"
        "The activation for input gate, forget gate and output "
        "gate, `sigmoid` by default.")
        .SetDefault("sigmoid")
        .InEnum({"sigmoid", "tanh", "relu", "identity"});
    AddAttr<std::string>("cell_activation",
                         "(string, default: tanh)"
                         "The activation for cell output, `tanh` by defalut.")
        .SetDefault("tanh")
        .InEnum({"sigmoid", "tanh", "relu", "identity"});
    AddAttr<std::string>("candidate_activation",
                         "(string, default: tanh)"
                         "The activation for candidate hidden state, "
                         "`tanh` by default.")
        .SetDefault("tanh")
        .InEnum({"sigmoid", "tanh", "relu", "identity"});
    AddComment(R"DOC(
Long-Short Term Memory (LSTM) Operator.

The defalut implementation is diagonal/peephole connection
(https://arxiv.org/pdf/1402.1128.pdf), the formula is as follows:

$$
i_t = \sigma(W_{ix}x_{t} + W_{ih}h_{t-1} + W_{ic}c_{t-1} + b_i) \\

f_t = \sigma(W_{fx}x_{t} + W_{fh}h_{t-1} + W_{fc}c_{t-1} + b_f) \\

\tilde{c_t} = act_g(W_{cx}x_t + W_{ch}h_{t-1} + b_c) \\

o_t = \sigma(W_{ox}x_{t} + W_{oh}h_{t-1} + W_{oc}c_t + b_o) \\

c_t = f_t \odot c_{t-1} + i_t \odot \tilde{c_t} \\

h_t = o_t \odot act_h(c_t)
$$

where the W terms denote weight matrices (e.g. \f$W_{xi}\f$ is the matrix
of weights from the input gate to the input), \f$W_{ic}, W_{fc}, W_{oc}\f$
are diagonal weight matrices for peephole connections. In our implementation,
we use vectors to reprenset these diagonal weight matrices. The b terms
denote bias vectors (\f$b_i\f$ is the input gate bias vector), \f$\sigma\f$
is the non-line activations, such as logistic sigmoid function, and
\f$i, f, o\f$ and \f$c\f$ are the input gate, forget gate, output gate,
and cell activation vectors, respectively, all of which have the same size as
the cell output activation vector \f$h\f$.

The \f$\odot\f$ is the element-wise product of the vectors. \f$act_g\f$ and \f$act_h\f$
are the cell input and cell output activation functions and `tanh` is usually
used for them. \f$\tilde{c_t}\f$ is also called candidate hidden state,
which is computed based on the current input and the previous hidden state.

Set `use_peepholes` False to disable peephole connection
(http://www.bioinf.jku.at/publications/older/2604.pdf). The formula
is omitted here.

Note that these \f$W_{xi}x_{t}, W_{xf}x_{t}, W_{xc}x_{t}, W_{xo}x_{t}\f$
operations on the input \f$x_{t}\f$ are NOT included in this operator.
Users can choose to use fully-connect operator before LSTM operator.

)DOC");
  }
};

class LSTMGradOp : public framework::OperatorWithKernel {
 public:
  using framework::OperatorWithKernel::OperatorWithKernel;

  void InferShape(framework::InferShapeContext* ctx) const override {
    PADDLE_ENFORCE(ctx->HasInput("Input"),
                   "Input(Input) of LSTM should not be null.");
    PADDLE_ENFORCE(ctx->HasInput("Hidden"),
                   "Input(Hidden) of LSTM should not be null.");
    PADDLE_ENFORCE(ctx->HasInput("Cell"),
                   "Input(Cell) of LSTM should not be null.");
    PADDLE_ENFORCE(ctx->HasInput("Weight"),
                   "Input(Weight) of LSTM should not be null.");
    PADDLE_ENFORCE(ctx->HasInput("Bias"),
                   "Input(Bias) of LSTM should not be null.");

    PADDLE_ENFORCE(ctx->HasInput("BatchGate"),
                   "Input(BatchGate) of LSTM should not be null.");
    PADDLE_ENFORCE(ctx->HasInput("BatchCellPreAct"),
                   "Input(BatchGate) of LSTM should not be null.");

    auto SetOutGradDim = [&ctx](const std::string& name) {
      auto g_name = framework::GradVarName(name);
      if (ctx->HasOutput(g_name))
        ctx->SetOutputDim(g_name, ctx->GetInputDim(name));
    };

    SetOutGradDim("Input");
    SetOutGradDim("Weight");
    SetOutGradDim("Bias");
    SetOutGradDim("H0");
    SetOutGradDim("C0");
  }

 protected:
  framework::OpKernelType GetKernelType(
      const framework::ExecutionContext& ctx) const override {
    return framework::OpKernelType(
        framework::ToDataType(ctx.Input<framework::LoDTensor>("Input")->type()),
        ctx.device_context());
  }
};

}  // namespace operators
}  // namespace paddle

namespace ops = paddle::operators;
REGISTER_OP(lstm, ops::LSTMOp, ops::LSTMOpMaker, lstm_grad, ops::LSTMGradOp);
REGISTER_OP_CPU_KERNEL(lstm, ops::LSTMKernel<paddle::platform::CPUPlace, float>,
                       ops::LSTMKernel<paddle::platform::CPUPlace, double>);
REGISTER_OP_CPU_KERNEL(lstm_grad,
                       ops::LSTMGradKernel<paddle::platform::CPUPlace, float>,
                       ops::LSTMGradKernel<paddle::platform::CPUPlace, double>);
<commit_msg>Fix the doc of LSTM operator.<commit_after>/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve.

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 "paddle/operators/lstm_op.h"

namespace paddle {
namespace operators {

class LSTMOp : public framework::OperatorWithKernel {
 public:
  using framework::OperatorWithKernel::OperatorWithKernel;

  void InferShape(framework::InferShapeContext* ctx) const override {
    PADDLE_ENFORCE(ctx->HasInput("Input"),
                   "Input(Input) of LSTM should not be null.");
    PADDLE_ENFORCE(ctx->HasInput("Weight"),
                   "Input(Weight) of LSTM should not be null.");
    PADDLE_ENFORCE(ctx->HasInput("Bias"),
                   "Input(Bias) of LSTM should not be null.");

    PADDLE_ENFORCE(ctx->HasOutput("Hidden"),
                   "Output(Hidden) of LSTM should not be null.");
    PADDLE_ENFORCE(ctx->HasOutput("Cell"),
                   "Output(Cell) of LSTM should not be null.");
    PADDLE_ENFORCE(ctx->HasOutput("BatchGate"),
                   "Output(BatchGate) of LSTM should not be null.");
    PADDLE_ENFORCE(ctx->HasOutput("BatchCellPreAct"),
                   "Output(BatchGate) of LSTM should not be null.");

    auto in_dims = ctx->GetInputDim("Input");
    PADDLE_ENFORCE_EQ(in_dims.size(), 2, "Input(X)'s rank must be 2.");

    if (ctx->HasInput("H0")) {
      PADDLE_ENFORCE(ctx->HasInput("C0"),
                     "Input(Cell) and Input(Hidden) of LSTM should not "
                     "be null at the same time.");
      auto h_dims = ctx->GetInputDim("H0");
      auto c_dims = ctx->GetInputDim("C0");
      PADDLE_ENFORCE(h_dims == c_dims,
                     "The dimension of Input(H0) and Input(C0) "
                     "should be the same.");
    }

    int frame_size = in_dims[1] / 4;
    auto w_dims = ctx->GetInputDim("Weight");
    PADDLE_ENFORCE_EQ(w_dims.size(), 2,
                      "The rank of Input(Weight) should be 2.");
    PADDLE_ENFORCE_EQ(w_dims[0], frame_size,
                      "The first dimension of Input(Weight) "
                      "should be %d.",
                      frame_size);
    PADDLE_ENFORCE_EQ(w_dims[1], 4 * frame_size,
                      "The second dimension of Input(Weight) "
                      "should be 4 * %d.",
                      frame_size);

    auto b_dims = ctx->GetInputDim("Bias");
    PADDLE_ENFORCE_EQ(b_dims.size(), 2, "The rank of Input(Bias) should be 2.");
    PADDLE_ENFORCE_EQ(b_dims[0], 1,
                      "The first dimension of Input(Bias) should be 1.");

    if (ctx->Attrs().Get<bool>("use_peepholes")) {
      PADDLE_ENFORCE_EQ(b_dims[1], 7 * frame_size,
                        "The second dimension of Input(Bias) should be "
                        "7 * %d if enable peepholes connection",
                        frame_size);
    } else {
      PADDLE_ENFORCE_EQ(b_dims[1], 4 * frame_size,
                        "The second dimension of Input(Bias) should be "
                        "4 * %d if disable peepholes connection",
                        frame_size);
    }

    framework::DDim out_dims({in_dims[0], frame_size});
    ctx->SetOutputDim("Hidden", out_dims);
    ctx->SetOutputDim("Cell", out_dims);
    ctx->SetOutputDim("BatchGate", in_dims);
    ctx->SetOutputDim("BatchCellPreAct", out_dims);
    ctx->ShareLoD("Input", "Hidden");
    ctx->ShareLoD("Input", "Cell");
  }

 protected:
  framework::OpKernelType GetKernelType(
      const framework::ExecutionContext& ctx) const override {
    return framework::OpKernelType(
        framework::ToDataType(ctx.Input<framework::LoDTensor>("Input")->type()),
        ctx.device_context());
  }
};

class LSTMOpMaker : public framework::OpProtoAndCheckerMaker {
 public:
  LSTMOpMaker(framework::OpProto* proto, framework::OpAttrChecker* op_checker)
      : OpProtoAndCheckerMaker(proto, op_checker) {
    AddInput("Input",
             "(LoDTensor) the first input is a LodTensor, which support "
             "variable-time length input sequence. The underlying tensor in "
             "this LoDTensor is a matrix with shape (T X 4D), where T is the "
             "total time steps in this mini-batch, D is the hidden size.");
    AddInput("H0",
             "(Tensor, optional) the initial hidden state is an optional "
             "input. This is a tensor with shape (N x D), where N is the "
             "batch size and D is the hidden size.")
        .AsDispensable();
    AddInput("C0",
             "(Tensor, optional) the initial cell state is an optional "
             "input. This is a tensor with shape (N x D), where N is the "
             "batch size. `H0` and `C0` can be NULL but only at the same time")
        .AsDispensable();
    AddInput("Weight",
             "(Tensor) the learnable hidden-hidden weights."
             " - The shape is (D x 4D), where D is the hidden size. "
             " - Weight = {W_ch, W_ih, W_fh, W_oh}");
    AddInput("Bias",
             "(Tensor) the learnable weights, which contains two parts: "
             "input-hidden bias weight and peephole connections weight if "
             "setting `use_peepholes` True. "
             "1. `use_peepholes = False` "
             " - The shape is (1 x 4D). "
             " - Bias = {b_c, b_i, b_f, b_o}."
             "2. `use_peepholes = True` "
             " - The shape is (1 x 7D). "
             " - Bias = {b_c, b_i, b_f, b_o, W_ic, W_fc, W_oc}.");
    AddOutput("Hidden",
              "(LoDTensor) the hidden state of LSTM operator. "
              "The shape is (T x D), and lod is the same with the `Input`.");
    AddOutput("Cell",
              "(LoDTensor) the cell state of LSTM operator. "
              "The shape is (T x D), and lod is the same with the `Input`.");
    AddOutput("BatchGate",
              "(LoDTensor) This LoDTensor contains input gate, forget gate "
              "and output gate after the nonlinear computation. This "
              "LoDTensor has the same shape as the reorganized input, which "
              "is also be called batch input. The LoD size is 2. The first "
              "LoD is the batch offsets and the second LoD contains the "
              "indexes, which denote the position of reorganized sequence "
              "in the raw input.")
        .AsIntermediate();
    AddOutput("BatchCellPreAct",
              "(LoDTensor) This LoDTensor is obtained in the forward and used "
              "in the backward.")
        .AsIntermediate();
    AddAttr<bool>("use_peepholes",
                  "(bool, defalut: True) "
                  "whether to enable diagonal/peephole connections.")
        .SetDefault(true);
    AddAttr<bool>("is_reverse",
                  "(bool, defalut: False) "
                  "whether to compute reversed LSTM.")
        .SetDefault(false);
    AddAttr<std::string>(
        "gate_activation",
        "(string, default: sigmoid)"
        "The activation for input gate, forget gate and output "
        "gate, `sigmoid` by default.")
        .SetDefault("sigmoid")
        .InEnum({"sigmoid", "tanh", "relu", "identity"});
    AddAttr<std::string>("cell_activation",
                         "(string, default: tanh)"
                         "The activation for cell output, `tanh` by defalut.")
        .SetDefault("tanh")
        .InEnum({"sigmoid", "tanh", "relu", "identity"});
    AddAttr<std::string>("candidate_activation",
                         "(string, default: tanh)"
                         "The activation for candidate hidden state, "
                         "`tanh` by default.")
        .SetDefault("tanh")
        .InEnum({"sigmoid", "tanh", "relu", "identity"});
    AddComment(R"DOC(
Long-Short Term Memory (LSTM) Operator.

The defalut implementation is diagonal/peephole connection
(https://arxiv.org/pdf/1402.1128.pdf), the formula is as follows:

$$
i_t = \sigma(W_{ix}x_{t} + W_{ih}h_{t-1} + W_{ic}c_{t-1} + b_i) \\

f_t = \sigma(W_{fx}x_{t} + W_{fh}h_{t-1} + W_{fc}c_{t-1} + b_f) \\

\tilde{c_t} = act_g(W_{cx}x_t + W_{ch}h_{t-1} + b_c) \\

o_t = \sigma(W_{ox}x_{t} + W_{oh}h_{t-1} + W_{oc}c_t + b_o) \\

c_t = f_t \odot c_{t-1} + i_t \odot \tilde{c_t} \\

h_t = o_t \odot act_h(c_t)
$$

where the W terms denote weight matrices (e.g. $W_{xi}$ is the matrix
of weights from the input gate to the input), $W_{ic}, W_{fc}, W_{oc}$
are diagonal weight matrices for peephole connections. In our implementation,
we use vectors to reprenset these diagonal weight matrices. The b terms
denote bias vectors ($b_i$ is the input gate bias vector), $\sigma$
is the non-line activations, such as logistic sigmoid function, and
$i, f, o$ and $c$ are the input gate, forget gate, output gate,
and cell activation vectors, respectively, all of which have the same size as
the cell output activation vector $h$.

The $\odot$ is the element-wise product of the vectors. $act_g$ and $act_h$
are the cell input and cell output activation functions and `tanh` is usually
used for them. $\tilde{c_t}$ is also called candidate hidden state,
which is computed based on the current input and the previous hidden state.

Set `use_peepholes` False to disable peephole connection. The formula
is omitted here, please refer to the paper
http://www.bioinf.jku.at/publications/older/2604.pdf for details.

Note that these $W_{xi}x_{t}, W_{xf}x_{t}, W_{xc}x_{t}, W_{xo}x_{t}$
operations on the input $x_{t}$ are NOT included in this operator.
Users can choose to use fully-connect operator before LSTM operator.

)DOC");
  }
};

class LSTMGradOp : public framework::OperatorWithKernel {
 public:
  using framework::OperatorWithKernel::OperatorWithKernel;

  void InferShape(framework::InferShapeContext* ctx) const override {
    PADDLE_ENFORCE(ctx->HasInput("Input"),
                   "Input(Input) of LSTM should not be null.");
    PADDLE_ENFORCE(ctx->HasInput("Hidden"),
                   "Input(Hidden) of LSTM should not be null.");
    PADDLE_ENFORCE(ctx->HasInput("Cell"),
                   "Input(Cell) of LSTM should not be null.");
    PADDLE_ENFORCE(ctx->HasInput("Weight"),
                   "Input(Weight) of LSTM should not be null.");
    PADDLE_ENFORCE(ctx->HasInput("Bias"),
                   "Input(Bias) of LSTM should not be null.");

    PADDLE_ENFORCE(ctx->HasInput("BatchGate"),
                   "Input(BatchGate) of LSTM should not be null.");
    PADDLE_ENFORCE(ctx->HasInput("BatchCellPreAct"),
                   "Input(BatchGate) of LSTM should not be null.");

    auto SetOutGradDim = [&ctx](const std::string& name) {
      auto g_name = framework::GradVarName(name);
      if (ctx->HasOutput(g_name))
        ctx->SetOutputDim(g_name, ctx->GetInputDim(name));
    };

    SetOutGradDim("Input");
    SetOutGradDim("Weight");
    SetOutGradDim("Bias");
    SetOutGradDim("H0");
    SetOutGradDim("C0");
  }

 protected:
  framework::OpKernelType GetKernelType(
      const framework::ExecutionContext& ctx) const override {
    return framework::OpKernelType(
        framework::ToDataType(ctx.Input<framework::LoDTensor>("Input")->type()),
        ctx.device_context());
  }
};

}  // namespace operators
}  // namespace paddle

namespace ops = paddle::operators;
REGISTER_OP(lstm, ops::LSTMOp, ops::LSTMOpMaker, lstm_grad, ops::LSTMGradOp);
REGISTER_OP_CPU_KERNEL(lstm, ops::LSTMKernel<paddle::platform::CPUPlace, float>,
                       ops::LSTMKernel<paddle::platform::CPUPlace, double>);
REGISTER_OP_CPU_KERNEL(lstm_grad,
                       ops::LSTMGradKernel<paddle::platform::CPUPlace, float>,
                       ops::LSTMGradKernel<paddle::platform::CPUPlace, double>);
<|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/audio/audio_input_controller.h"
#include "media/base/limits.h"

namespace {

const int kMaxInputChannels = 2;

}  // namespace

namespace media {

// static
AudioInputController::Factory* AudioInputController::factory_ = NULL;

AudioInputController::AudioInputController(EventHandler* handler)
    : handler_(handler),
      stream_(NULL),
      state_(kEmpty),
      thread_("AudioInputControllerThread") {
}

AudioInputController::~AudioInputController() {
  DCHECK(kClosed == state_ || kCreated == state_ || kEmpty == state_);
}

// static
scoped_refptr<AudioInputController> AudioInputController::Create(
    EventHandler* event_handler,
    AudioParameters params) {
  if (!params.IsValid() || (params.channels > kMaxInputChannels))
    return NULL;

  if (factory_) {
    return factory_->Create(event_handler, params);
  }

  scoped_refptr<AudioInputController> controller(new AudioInputController(
      event_handler));

  // Start the thread and post a task to create the audio input stream.
  controller->thread_.Start();
  controller->thread_.message_loop()->PostTask(
      FROM_HERE,
      NewRunnableMethod(controller.get(), &AudioInputController::DoCreate,
                        params));
  return controller;
}

void AudioInputController::Record() {
  DCHECK(thread_.IsRunning());
  thread_.message_loop()->PostTask(
      FROM_HERE,
      NewRunnableMethod(this, &AudioInputController::DoRecord));
}

void AudioInputController::Close() {
  if (!thread_.IsRunning()) {
    // If the thread is not running make sure we are stopped.
    DCHECK_EQ(kClosed, state_);
    return;
  }

  // Wait for all tasks to complete on the audio thread.
  thread_.message_loop()->PostTask(
      FROM_HERE,
      NewRunnableMethod(this, &AudioInputController::DoClose));
  thread_.Stop();
}

void AudioInputController::DoCreate(AudioParameters params) {
  stream_ = AudioManager::GetAudioManager()->MakeAudioInputStream(params);

  if (!stream_) {
    // TODO(satish): Define error types.
    handler_->OnError(this, 0);
    return;
  }

  if (stream_ && !stream_->Open()) {
    stream_->Close();
    stream_ = NULL;
    // TODO(satish): Define error types.
    handler_->OnError(this, 0);
    return;
  }

  state_ = kCreated;
  handler_->OnCreated(this);
}

void AudioInputController::DoRecord() {
  DCHECK_EQ(thread_.message_loop(), MessageLoop::current());

  if (state_ != kCreated)
    return;

  {
    AutoLock auto_lock(lock_);
    state_ = kRecording;
  }

  stream_->Start(this);
  handler_->OnRecording(this);
}

void AudioInputController::DoClose() {
  DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
  DCHECK_NE(kClosed, state_);

  // |stream_| can be null if creating the device failed in DoCreate().
  if (stream_) {
    stream_->Stop();
    stream_->Close();
    // After stream is closed it is destroyed, so don't keep a reference to it.
    stream_ = NULL;
  }

  // Since the stream is closed at this point there's no other threads reading
  // |state_| so we don't need to lock.
  state_ = kClosed;
}

void AudioInputController::DoReportError(int code) {
  DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
  handler_->OnError(this, code);
}

void AudioInputController::OnData(AudioInputStream* stream, const uint8* data,
                                  uint32 size) {
  {
    AutoLock auto_lock(lock_);
    if (state_ != kRecording)
      return;
  }
  handler_->OnData(this, data, size);
}

void AudioInputController::OnClose(AudioInputStream* stream) {
  // TODO(satish): Sometimes the device driver closes the input stream without
  // us asking for it (may be if the device was unplugged?). Check how to handle
  // such cases here.
}

void AudioInputController::OnError(AudioInputStream* stream, int code) {
  // Handle error on the audio controller thread.
  thread_.message_loop()->PostTask(
      FROM_HERE,
      NewRunnableMethod(this, &AudioInputController::DoReportError, code));
}

}  // namespace media
<commit_msg>Set scoped IO access when closing the audio input thread to allow joining.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/audio/audio_input_controller.h"

#include "base/thread_restrictions.h"
#include "media/base/limits.h"

namespace {

const int kMaxInputChannels = 2;

}  // namespace

namespace media {

// static
AudioInputController::Factory* AudioInputController::factory_ = NULL;

AudioInputController::AudioInputController(EventHandler* handler)
    : handler_(handler),
      stream_(NULL),
      state_(kEmpty),
      thread_("AudioInputControllerThread") {
}

AudioInputController::~AudioInputController() {
  DCHECK(kClosed == state_ || kCreated == state_ || kEmpty == state_);
}

// static
scoped_refptr<AudioInputController> AudioInputController::Create(
    EventHandler* event_handler,
    AudioParameters params) {
  if (!params.IsValid() || (params.channels > kMaxInputChannels))
    return NULL;

  if (factory_) {
    return factory_->Create(event_handler, params);
  }

  scoped_refptr<AudioInputController> controller(new AudioInputController(
      event_handler));

  // Start the thread and post a task to create the audio input stream.
  controller->thread_.Start();
  controller->thread_.message_loop()->PostTask(
      FROM_HERE,
      NewRunnableMethod(controller.get(), &AudioInputController::DoCreate,
                        params));
  return controller;
}

void AudioInputController::Record() {
  DCHECK(thread_.IsRunning());
  thread_.message_loop()->PostTask(
      FROM_HERE,
      NewRunnableMethod(this, &AudioInputController::DoRecord));
}

void AudioInputController::Close() {
  if (!thread_.IsRunning()) {
    // If the thread is not running make sure we are stopped.
    DCHECK_EQ(kClosed, state_);
    return;
  }

  // Wait for all tasks to complete on the audio thread.
  thread_.message_loop()->PostTask(
      FROM_HERE,
      NewRunnableMethod(this, &AudioInputController::DoClose));

  // A ScopedAllowIO object is required to join the thread when calling Stop.
  // This is because as joining threads may be a long operation it's now
  // not allowed in threads without IO access, which is the case of the IO
  // thread (it is missnamed) being used here. This object overrides
  // temporarily this restriction and should be used only in specific
  // infrequent cases where joining is guaranteed to be fast.
  // Bug: http://code.google.com/p/chromium/issues/detail?id=67806
  base::ThreadRestrictions::ScopedAllowIO allow_io_for_thread_join;
  thread_.Stop();
}

void AudioInputController::DoCreate(AudioParameters params) {
  stream_ = AudioManager::GetAudioManager()->MakeAudioInputStream(params);

  if (!stream_) {
    // TODO(satish): Define error types.
    handler_->OnError(this, 0);
    return;
  }

  if (stream_ && !stream_->Open()) {
    stream_->Close();
    stream_ = NULL;
    // TODO(satish): Define error types.
    handler_->OnError(this, 0);
    return;
  }

  state_ = kCreated;
  handler_->OnCreated(this);
}

void AudioInputController::DoRecord() {
  DCHECK_EQ(thread_.message_loop(), MessageLoop::current());

  if (state_ != kCreated)
    return;

  {
    AutoLock auto_lock(lock_);
    state_ = kRecording;
  }

  stream_->Start(this);
  handler_->OnRecording(this);
}

void AudioInputController::DoClose() {
  DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
  DCHECK_NE(kClosed, state_);

  // |stream_| can be null if creating the device failed in DoCreate().
  if (stream_) {
    stream_->Stop();
    stream_->Close();
    // After stream is closed it is destroyed, so don't keep a reference to it.
    stream_ = NULL;
  }

  // Since the stream is closed at this point there's no other threads reading
  // |state_| so we don't need to lock.
  state_ = kClosed;
}

void AudioInputController::DoReportError(int code) {
  DCHECK_EQ(thread_.message_loop(), MessageLoop::current());
  handler_->OnError(this, code);
}

void AudioInputController::OnData(AudioInputStream* stream, const uint8* data,
                                  uint32 size) {
  {
    AutoLock auto_lock(lock_);
    if (state_ != kRecording)
      return;
  }
  handler_->OnData(this, data, size);
}

void AudioInputController::OnClose(AudioInputStream* stream) {
  // TODO(satish): Sometimes the device driver closes the input stream without
  // us asking for it (may be if the device was unplugged?). Check how to handle
  // such cases here.
}

void AudioInputController::OnError(AudioInputStream* stream, int code) {
  // Handle error on the audio controller thread.
  thread_.message_loop()->PostTask(
      FROM_HERE,
      NewRunnableMethod(this, &AudioInputController::DoReportError, code));
}

}  // namespace media
<|endoftext|>


<commit_before><commit_msg>Remove the folding define macro.<commit_after><|endoftext|>


<commit_before><commit_msg>Add comment to unit tests previously disabled<commit_after><|endoftext|>


<commit_before>/*
 *  yosys -- Yosys Open SYnthesis Suite
 *
 *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include <algorithm>
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
#include "kernel/ffinit.h"

USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN

struct MemoryDffWorker
{
	Module *module;
	SigMap sigmap;

	vector<Cell*> dff_cells;
	dict<SigBit, SigBit> invbits;
	dict<SigBit, int> sigbit_users_count;
	FfInitVals initvals;

	MemoryDffWorker(Module *module) : module(module), sigmap(module)
	{
		initvals.set(&sigmap, module);
	}

	bool find_sig_before_dff(RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity)
	{
		sigmap.apply(sig);

		dict<SigBit, SigBit> cache;

		for (auto &bit : sig)
		{
			if (cache.count(bit)) {
				bit = cache[bit];
				continue;
			}

			if (bit.wire == NULL)
				continue;

			if (initvals(bit) != State::Sx)
				return false;

			for (auto cell : dff_cells)
			{
				SigSpec this_clk = cell->getPort(ID::CLK);
				bool this_clk_polarity = cell->parameters[ID::CLK_POLARITY].as_bool();

				if (invbits.count(this_clk)) {
					this_clk = invbits.at(this_clk);
					this_clk_polarity = !this_clk_polarity;
				}

				if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) {
					if (this_clk != clk)
						continue;
					if (this_clk_polarity != clk_polarity)
						continue;
				}

				RTLIL::SigSpec q_norm = cell->getPort(ID::Q);
				sigmap.apply(q_norm);

				RTLIL::SigSpec d = q_norm.extract(bit, &cell->getPort(ID::D));
				if (d.size() != 1)
					continue;

				if (cell->type == ID($sdffce)) {
					SigSpec rval = cell->parameters[ID::SRST_VALUE];
					SigSpec rbit = q_norm.extract(bit, &rval);
					if (cell->parameters[ID::SRST_POLARITY].as_bool())
						d = module->Mux(NEW_ID, d, rbit, cell->getPort(ID::SRST));
					else
						d = module->Mux(NEW_ID, rbit, d, cell->getPort(ID::SRST));
				}

				if (cell->type.in(ID($dffe), ID($sdffe), ID($sdffce))) {
					if (cell->parameters[ID::EN_POLARITY].as_bool())
						d = module->Mux(NEW_ID, bit, d, cell->getPort(ID::EN));
					else
						d = module->Mux(NEW_ID, d, bit, cell->getPort(ID::EN));
				}

				if (cell->type.in(ID($sdff), ID($sdffe))) {
					SigSpec rval = cell->parameters[ID::SRST_VALUE];
					SigSpec rbit = q_norm.extract(bit, &rval);
					if (cell->parameters[ID::SRST_POLARITY].as_bool())
						d = module->Mux(NEW_ID, d, rbit, cell->getPort(ID::SRST));
					else
						d = module->Mux(NEW_ID, rbit, d, cell->getPort(ID::SRST));
				}

				cache[bit] = d;
				bit = d;
				clk = this_clk;
				clk_polarity = this_clk_polarity;
				goto replaced_this_bit;
			}

			return false;
		replaced_this_bit:;
		}

		return true;
	}

	bool find_sig_after_dffe(RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity, RTLIL::SigSpec &en, bool &en_polarity)
	{
		sigmap.apply(sig);

		for (auto &bit : sig)
		{
			if (bit.wire == NULL)
				continue;

			for (auto cell : dff_cells)
			{
				if (!cell->type.in(ID($dff), ID($dffe)))
					continue;

				SigSpec this_clk = cell->getPort(ID::CLK);
				bool this_clk_polarity = cell->parameters[ID::CLK_POLARITY].as_bool();
				SigSpec this_en = State::S1;
				bool this_en_polarity = true;

				if (cell->type == ID($dffe)) {
					this_en = cell->getPort(ID::EN);
					this_en_polarity = cell->parameters[ID::EN_POLARITY].as_bool();
				}

				if (invbits.count(this_clk)) {
					this_clk = invbits.at(this_clk);
					this_clk_polarity = !this_clk_polarity;
				}

				if (invbits.count(this_en)) {
					this_en = invbits.at(this_en);
					this_en_polarity = !this_en_polarity;
				}

				if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) {
					if (this_clk != clk)
						continue;
					if (this_clk_polarity != clk_polarity)
						continue;
					if (this_en != en)
						continue;
					if (this_en_polarity != en_polarity)
						continue;
				}

				RTLIL::SigSpec q_norm = cell->getPort(ID::D);
				sigmap.apply(q_norm);

				RTLIL::SigSpec d = q_norm.extract(bit, &cell->getPort(ID::Q));
				if (d.size() != 1)
					continue;

				if (initvals(d) != State::Sx)
					return false;

				bit = d;
				clk = this_clk;
				clk_polarity = this_clk_polarity;
				en = this_en;
				en_polarity = this_en_polarity;
				goto replaced_this_bit;
			}

			return false;
		replaced_this_bit:;
		}

		return true;
	}

	void disconnect_dff(RTLIL::SigSpec sig)
	{
		sigmap.apply(sig);
		sig.sort_and_unify();

		std::stringstream sstr;
		sstr << "$memory_dff_disconnected$" << (autoidx++);

		RTLIL::SigSpec new_sig = module->addWire(sstr.str(), sig.size());

		for (auto cell : module->cells())
			if (cell->type.in(ID($dff), ID($dffe))) {
				RTLIL::SigSpec new_q = cell->getPort(ID::Q);
				new_q.replace(sig, new_sig);
				cell->setPort(ID::Q, new_q);
			}
	}

	void handle_rd_cell(RTLIL::Cell *cell)
	{
		log("Checking cell `%s' in module `%s': ", cell->name.c_str(), module->name.c_str());

		bool clk_polarity = 0;
		bool en_polarity = 0;

		RTLIL::SigSpec clk_data = RTLIL::SigSpec(RTLIL::State::Sx);
		RTLIL::SigSpec en_data;
		RTLIL::SigSpec sig_data = cell->getPort(ID::DATA);

		for (auto bit : sigmap(sig_data))
			if (sigbit_users_count[bit] > 1)
				goto skip_ff_after_read_merging;

		if (find_sig_after_dffe(sig_data, clk_data, clk_polarity, en_data, en_polarity) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx))
		{
			if (!en_polarity)
				en_data = module->LogicNot(NEW_ID, en_data);
			disconnect_dff(sig_data);
			cell->setPort(ID::CLK, clk_data);
			cell->setPort(ID::EN, en_data);
			cell->setPort(ID::DATA, sig_data);
			cell->parameters[ID::CLK_ENABLE] = RTLIL::Const(1);
			cell->parameters[ID::CLK_POLARITY] = RTLIL::Const(clk_polarity);
			cell->parameters[ID::TRANSPARENT] = RTLIL::Const(0);
			log("merged data $dff to cell.\n");
			return;
		}

	skip_ff_after_read_merging:;
		RTLIL::SigSpec clk_addr = RTLIL::SigSpec(RTLIL::State::Sx);
		RTLIL::SigSpec sig_addr = cell->getPort(ID::ADDR);
		if (find_sig_before_dff(sig_addr, clk_addr, clk_polarity) &&
				clk_addr != RTLIL::SigSpec(RTLIL::State::Sx))
		{
			cell->setPort(ID::CLK, clk_addr);
			cell->setPort(ID::EN, State::S1);
			cell->setPort(ID::ADDR, sig_addr);
			cell->parameters[ID::CLK_ENABLE] = RTLIL::Const(1);
			cell->parameters[ID::CLK_POLARITY] = RTLIL::Const(clk_polarity);
			cell->parameters[ID::TRANSPARENT] = RTLIL::Const(1);
			log("merged address $dff to cell.\n");
			return;
		}

		log("no (compatible) $dff found.\n");
	}

	void run()
	{
		for (auto wire : module->wires()) {
			if (wire->port_output)
				for (auto bit : sigmap(wire))
					sigbit_users_count[bit]++;
		}

		for (auto cell : module->cells()) {
			if (cell->type.in(ID($dff), ID($dffe), ID($sdff), ID($sdffe), ID($sdffce)))
				dff_cells.push_back(cell);
			if (cell->type.in(ID($not), ID($_NOT_)) || (cell->type == ID($logic_not) && GetSize(cell->getPort(ID::A)) == 1)) {
				SigSpec sig_a = cell->getPort(ID::A);
				SigSpec sig_y = cell->getPort(ID::Y);
				if (cell->type == ID($not))
					sig_a.extend_u0(GetSize(sig_y), cell->getParam(ID::A_SIGNED).as_bool());
				if (cell->type == ID($logic_not))
					sig_y.extend_u0(1);
				for (int i = 0; i < GetSize(sig_y); i++)
					invbits[sig_y[i]] = sig_a[i];
			}
			for (auto &conn : cell->connections())
				if (!cell->known() || cell->input(conn.first))
					for (auto bit : sigmap(conn.second))
						sigbit_users_count[bit]++;
		}

		for (auto cell : module->selected_cells())
			if (cell->type == ID($memrd) && !cell->parameters[ID::CLK_ENABLE].as_bool())
				handle_rd_cell(cell);
	}
};

struct MemoryDffPass : public Pass {
	MemoryDffPass() : Pass("memory_dff", "merge input/output DFFs into memory read ports") { }
	void help() override
	{
		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
		log("\n");
		log("    memory_dff [options] [selection]\n");
		log("\n");
		log("This pass detects DFFs at memory read ports and merges them into the memory port.\n");
		log("I.e. it consumes an asynchronous memory port and the flip-flops at its\n");
		log("interface and yields a synchronous memory port.\n");
		log("\n");
	}
	void execute(std::vector<std::string> args, RTLIL::Design *design) override
	{
		log_header(design, "Executing MEMORY_DFF pass (merging $dff cells to $memrd).\n");

		size_t argidx;
		for (argidx = 1; argidx < args.size(); argidx++) {
			break;
		}
		extra_args(args, argidx, design);

		for (auto mod : design->selected_modules()) {
			MemoryDffWorker worker(mod);
			worker.run();
		}
	}
} MemoryDffPass;

PRIVATE_NAMESPACE_END
<commit_msg>memory_dff: Use Mem helper.<commit_after>/*
 *  yosys -- Yosys Open SYnthesis Suite
 *
 *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include <algorithm>
#include "kernel/yosys.h"
#include "kernel/sigtools.h"
#include "kernel/ffinit.h"
#include "kernel/mem.h"

USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN

struct MemoryDffWorker
{
	Module *module;
	SigMap sigmap;

	vector<Cell*> dff_cells;
	dict<SigBit, SigBit> invbits;
	dict<SigBit, int> sigbit_users_count;
	FfInitVals initvals;

	MemoryDffWorker(Module *module) : module(module), sigmap(module)
	{
		initvals.set(&sigmap, module);
	}

	bool find_sig_before_dff(RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity)
	{
		sigmap.apply(sig);

		dict<SigBit, SigBit> cache;

		for (auto &bit : sig)
		{
			if (cache.count(bit)) {
				bit = cache[bit];
				continue;
			}

			if (bit.wire == NULL)
				continue;

			if (initvals(bit) != State::Sx)
				return false;

			for (auto cell : dff_cells)
			{
				SigSpec this_clk = cell->getPort(ID::CLK);
				bool this_clk_polarity = cell->parameters[ID::CLK_POLARITY].as_bool();

				if (invbits.count(this_clk)) {
					this_clk = invbits.at(this_clk);
					this_clk_polarity = !this_clk_polarity;
				}

				if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) {
					if (this_clk != clk)
						continue;
					if (this_clk_polarity != clk_polarity)
						continue;
				}

				RTLIL::SigSpec q_norm = cell->getPort(ID::Q);
				sigmap.apply(q_norm);

				RTLIL::SigSpec d = q_norm.extract(bit, &cell->getPort(ID::D));
				if (d.size() != 1)
					continue;

				if (cell->type == ID($sdffce)) {
					SigSpec rval = cell->parameters[ID::SRST_VALUE];
					SigSpec rbit = q_norm.extract(bit, &rval);
					if (cell->parameters[ID::SRST_POLARITY].as_bool())
						d = module->Mux(NEW_ID, d, rbit, cell->getPort(ID::SRST));
					else
						d = module->Mux(NEW_ID, rbit, d, cell->getPort(ID::SRST));
				}

				if (cell->type.in(ID($dffe), ID($sdffe), ID($sdffce))) {
					if (cell->parameters[ID::EN_POLARITY].as_bool())
						d = module->Mux(NEW_ID, bit, d, cell->getPort(ID::EN));
					else
						d = module->Mux(NEW_ID, d, bit, cell->getPort(ID::EN));
				}

				if (cell->type.in(ID($sdff), ID($sdffe))) {
					SigSpec rval = cell->parameters[ID::SRST_VALUE];
					SigSpec rbit = q_norm.extract(bit, &rval);
					if (cell->parameters[ID::SRST_POLARITY].as_bool())
						d = module->Mux(NEW_ID, d, rbit, cell->getPort(ID::SRST));
					else
						d = module->Mux(NEW_ID, rbit, d, cell->getPort(ID::SRST));
				}

				cache[bit] = d;
				bit = d;
				clk = this_clk;
				clk_polarity = this_clk_polarity;
				goto replaced_this_bit;
			}

			return false;
		replaced_this_bit:;
		}

		return true;
	}

	bool find_sig_after_dffe(RTLIL::SigSpec &sig, RTLIL::SigSpec &clk, bool &clk_polarity, RTLIL::SigSpec &en, bool &en_polarity)
	{
		sigmap.apply(sig);

		for (auto &bit : sig)
		{
			if (bit.wire == NULL)
				continue;

			for (auto cell : dff_cells)
			{
				if (!cell->type.in(ID($dff), ID($dffe)))
					continue;

				SigSpec this_clk = cell->getPort(ID::CLK);
				bool this_clk_polarity = cell->parameters[ID::CLK_POLARITY].as_bool();
				SigSpec this_en = State::S1;
				bool this_en_polarity = true;

				if (cell->type == ID($dffe)) {
					this_en = cell->getPort(ID::EN);
					this_en_polarity = cell->parameters[ID::EN_POLARITY].as_bool();
				}

				if (invbits.count(this_clk)) {
					this_clk = invbits.at(this_clk);
					this_clk_polarity = !this_clk_polarity;
				}

				if (invbits.count(this_en)) {
					this_en = invbits.at(this_en);
					this_en_polarity = !this_en_polarity;
				}

				if (clk != RTLIL::SigSpec(RTLIL::State::Sx)) {
					if (this_clk != clk)
						continue;
					if (this_clk_polarity != clk_polarity)
						continue;
					if (this_en != en)
						continue;
					if (this_en_polarity != en_polarity)
						continue;
				}

				RTLIL::SigSpec q_norm = cell->getPort(ID::D);
				sigmap.apply(q_norm);

				RTLIL::SigSpec d = q_norm.extract(bit, &cell->getPort(ID::Q));
				if (d.size() != 1)
					continue;

				if (initvals(d) != State::Sx)
					return false;

				bit = d;
				clk = this_clk;
				clk_polarity = this_clk_polarity;
				en = this_en;
				en_polarity = this_en_polarity;
				goto replaced_this_bit;
			}

			return false;
		replaced_this_bit:;
		}

		return true;
	}

	void disconnect_dff(RTLIL::SigSpec sig)
	{
		sigmap.apply(sig);
		sig.sort_and_unify();

		std::stringstream sstr;
		sstr << "$memory_dff_disconnected$" << (autoidx++);

		RTLIL::SigSpec new_sig = module->addWire(sstr.str(), sig.size());

		for (auto cell : module->cells())
			if (cell->type.in(ID($dff), ID($dffe))) {
				RTLIL::SigSpec new_q = cell->getPort(ID::Q);
				new_q.replace(sig, new_sig);
				cell->setPort(ID::Q, new_q);
			}
	}

	void handle_rd_port(Mem &mem, int idx)
	{
		auto &port = mem.rd_ports[idx];
		log("Checking read port `%s'[%d] in module `%s': ", mem.memid.c_str(), idx, module->name.c_str());

		bool clk_polarity = 0;
		bool en_polarity = 0;

		RTLIL::SigSpec clk_data = RTLIL::SigSpec(RTLIL::State::Sx);
		RTLIL::SigSpec en_data;
		RTLIL::SigSpec sig_data = port.data;

		for (auto bit : sigmap(sig_data))
			if (sigbit_users_count[bit] > 1)
				goto skip_ff_after_read_merging;

		if (find_sig_after_dffe(sig_data, clk_data, clk_polarity, en_data, en_polarity) && clk_data != RTLIL::SigSpec(RTLIL::State::Sx))
		{
			if (!en_polarity)
				en_data = module->LogicNot(NEW_ID, en_data);
			disconnect_dff(sig_data);
			port.clk = clk_data;
			port.en = en_data;
			port.data = sig_data;
			port.clk_enable = true;
			port.clk_polarity = clk_polarity;
			port.transparent = false;
			mem.emit();
			log("merged data $dff to cell.\n");
			return;
		}

	skip_ff_after_read_merging:;
		RTLIL::SigSpec clk_addr = RTLIL::SigSpec(RTLIL::State::Sx);
		RTLIL::SigSpec sig_addr = port.addr;
		if (find_sig_before_dff(sig_addr, clk_addr, clk_polarity) &&
				clk_addr != RTLIL::SigSpec(RTLIL::State::Sx))
		{
			port.clk = clk_addr;
			port.en = State::S1;
			port.addr = sig_addr;
			port.clk_enable = true;
			port.clk_polarity = clk_polarity;
			port.transparent = true;
			mem.emit();
			log("merged address $dff to cell.\n");
			return;
		}

		log("no (compatible) $dff found.\n");
	}

	void run()
	{
		for (auto wire : module->wires()) {
			if (wire->port_output)
				for (auto bit : sigmap(wire))
					sigbit_users_count[bit]++;
		}

		for (auto cell : module->cells()) {
			if (cell->type.in(ID($dff), ID($dffe), ID($sdff), ID($sdffe), ID($sdffce)))
				dff_cells.push_back(cell);
			if (cell->type.in(ID($not), ID($_NOT_)) || (cell->type == ID($logic_not) && GetSize(cell->getPort(ID::A)) == 1)) {
				SigSpec sig_a = cell->getPort(ID::A);
				SigSpec sig_y = cell->getPort(ID::Y);
				if (cell->type == ID($not))
					sig_a.extend_u0(GetSize(sig_y), cell->getParam(ID::A_SIGNED).as_bool());
				if (cell->type == ID($logic_not))
					sig_y.extend_u0(1);
				for (int i = 0; i < GetSize(sig_y); i++)
					invbits[sig_y[i]] = sig_a[i];
			}
			for (auto &conn : cell->connections())
				if (!cell->known() || cell->input(conn.first))
					for (auto bit : sigmap(conn.second))
						sigbit_users_count[bit]++;
		}

		for (auto &mem : Mem::get_selected_memories(module)) {
			for (int i = 0; i < GetSize(mem.rd_ports); i++) {
				if (!mem.rd_ports[i].clk_enable)
					handle_rd_port(mem, i);
			}
		}
	}
};

struct MemoryDffPass : public Pass {
	MemoryDffPass() : Pass("memory_dff", "merge input/output DFFs into memory read ports") { }
	void help() override
	{
		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
		log("\n");
		log("    memory_dff [options] [selection]\n");
		log("\n");
		log("This pass detects DFFs at memory read ports and merges them into the memory port.\n");
		log("I.e. it consumes an asynchronous memory port and the flip-flops at its\n");
		log("interface and yields a synchronous memory port.\n");
		log("\n");
	}
	void execute(std::vector<std::string> args, RTLIL::Design *design) override
	{
		log_header(design, "Executing MEMORY_DFF pass (merging $dff cells to $memrd).\n");

		size_t argidx;
		for (argidx = 1; argidx < args.size(); argidx++) {
			break;
		}
		extra_args(args, argidx, design);

		for (auto mod : design->selected_modules()) {
			MemoryDffWorker worker(mod);
			worker.run();
		}
	}
} MemoryDffPass;

PRIVATE_NAMESPACE_END
<|endoftext|>


<commit_before>/*
 Copyright (c) 2014 Arduino.  All right reserved.

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

 This library is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 See the GNU Lesser General Public License for more details.

 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <stdlib.h>
extern "C" {
#include "ets_sys.h"
#include "os_type.h"
#include "osapi.h"
#include "mem.h"
#include "user_interface.h"
}

void *operator new(size_t size) {
    size = ((size + 3) & ~((size_t)0x3));
    return os_malloc(size);
}

void *operator new[](size_t size) {
    size = ((size + 3) & ~((size_t)0x3));
    return os_malloc(size);
}

void operator delete(void * ptr) {
    os_free(ptr);
}

void operator delete[](void * ptr) {
    os_free(ptr);
}

extern "C" void __cxa_pure_virtual(void) __attribute__ ((__noreturn__));
extern "C" void __cxa_deleted_virtual(void) __attribute__ ((__noreturn__));

void __cxa_pure_virtual(void) {
    abort();
}

void __cxa_deleted_virtual(void) {
    abort();
}

namespace std {
void __throw_bad_function_call() {
    abort();
}
}

// TODO: rebuild windows toolchain to make this unnecessary:
void* __dso_handle;

<commit_msg>Fix linker errors with WiFiMulti<commit_after>/*
 Copyright (c) 2014 Arduino.  All right reserved.

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

 This library is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 See the GNU Lesser General Public License for more details.

 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <stdlib.h>
extern "C" {
#include "ets_sys.h"
#include "os_type.h"
#include "osapi.h"
#include "mem.h"
#include "user_interface.h"
}

void *operator new(size_t size) {
    size = ((size + 3) & ~((size_t)0x3));
    return os_malloc(size);
}

void *operator new[](size_t size) {
    size = ((size + 3) & ~((size_t)0x3));
    return os_malloc(size);
}

void operator delete(void * ptr) {
    os_free(ptr);
}

void operator delete[](void * ptr) {
    os_free(ptr);
}

extern "C" void __cxa_pure_virtual(void) __attribute__ ((__noreturn__));
extern "C" void __cxa_deleted_virtual(void) __attribute__ ((__noreturn__));

void __cxa_pure_virtual(void) {
    abort();
}

void __cxa_deleted_virtual(void) {
    abort();
}

namespace std {
void __throw_bad_function_call() {
    abort();
}

void __throw_length_error(char const*) {
	abort();
}

void __throw_bad_alloc() {
	abort();
}
}

// TODO: rebuild windows toolchain to make this unnecessary:
void* __dso_handle;

<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

/** Cheap and cheesy replacement code for liblangtag on systems that do not
    allow / want LGPL code or dependencies on glib.

    XXX NOTE: This code does not check language tags for validity or if they
    are registered with IANA, does not canonicalize or strip default script
    tags if included nor does it do any other fancy stuff that liblangtag is
    capable of. It just makes depending code work without.
 */

#include <cstdlib>
#include <cstring>
#include <cstdio>

namespace {

typedef int lt_bool_t;
typedef void* lt_pointer_t;

struct lt_error_t {
    void *something;
    lt_error_t() : something(NULL) {}
};

static void* g_malloc(size_t s)
{
    return malloc(s);
}

static void g_free(void* p)
{
    if (p)
        free(p);
}

static void lt_error_unref(lt_error_t *error)
{
    if (error)
    {
        g_free( error->something);
        g_free( error);
    }
}

struct my_ref
{
    sal_uInt32 mnRef;
    explicit my_ref() : mnRef(1) {}
    virtual ~my_ref() {}
    void incRef() { ++mnRef; }
    void decRef() { if (--mnRef == 0) delete this; }
};

struct my_t_impl : public my_ref
{
    char* mpStr;
    explicit my_t_impl() : my_ref(), mpStr(NULL) {}
    virtual ~my_t_impl() { g_free( mpStr); }
    explicit my_t_impl( const my_t_impl& r )
        :
            my_ref(),
            mpStr(r.mpStr ? strdup( r.mpStr) : NULL)
    {
    }
    my_t_impl& operator=( const my_t_impl& r )
    {
        if (this == &r)
            return *this;
        g_free( mpStr);
        mpStr = (r.mpStr ? strdup( r.mpStr) : NULL);
        return *this;
    }
    void assign( const char* str )
    {
        g_free( mpStr);
        mpStr = (str ? strdup( str) : NULL);
    }
    void assign( const char* str, const char* stop )
    {
        g_free( mpStr);
        if (str && str < stop)
        {
            mpStr = static_cast<char*>(g_malloc( stop - str + 1));
            memcpy( mpStr, str, stop - str);
            mpStr[stop - str] = 0;
        }
        else
            mpStr = NULL;
    }
    void append( const char* str, const char* stop )
    {
        if (str && str < stop)
        {
            size_t nOld = mpStr ? strlen( mpStr) : 0;
            size_t nNew = nOld + (stop - str) + 1;
            char* p = static_cast<char*>(g_malloc( nNew));
            if (nOld)
                memcpy( p, mpStr, nOld);
            memcpy( p + nOld, str, stop - str);
            p[nNew-1] = 0;
            g_free( mpStr);
            mpStr = p;
        }
    }
    void zero()
    {
        g_free( mpStr);
        mpStr = NULL;
    }
};

struct lt_lang_t : public my_t_impl
{
    explicit lt_lang_t() : my_t_impl() {}
    virtual ~lt_lang_t() {}
};

struct lt_script_t : public my_t_impl
{
    explicit lt_script_t() : my_t_impl() {}
    virtual ~lt_script_t() {}
};

struct lt_region_t : public my_t_impl
{
    explicit lt_region_t() : my_t_impl() {}
    virtual ~lt_region_t() {}
};

struct lt_variant_t : public my_t_impl
{
    explicit lt_variant_t() : my_t_impl() {}
    virtual ~lt_variant_t() {}
};

struct lt_list_t : public my_t_impl
{
    lt_list_t* mpPrev;
    lt_list_t* mpNext;
    explicit lt_list_t() : my_t_impl(), mpPrev(NULL), mpNext(NULL) {}
    explicit lt_list_t( const lt_list_t& r )
        :
            my_t_impl( r), mpPrev(NULL), mpNext(NULL)
    {
    }
    virtual ~lt_list_t()
    {
        if (mpPrev)
            mpPrev->mpNext = mpNext;
        if (mpNext)
            mpNext->mpPrev = mpPrev;
    }
};

static lt_pointer_t lt_list_value( const lt_list_t* p )
{
    // Assuming only char* here.
    return p ? p->mpStr : NULL;
}

static const lt_list_t* lt_list_next( const lt_list_t* p )
{
    return p ? p->mpNext : NULL;
}

static lt_list_t* my_copyList( const lt_list_t * pList )
{
    lt_list_t* pNewList = NULL;
    lt_list_t* pLast = NULL;
    while (pList)
    {
        lt_list_t* p = new lt_list_t( *pList);
        if (!pNewList)
            pNewList = p;
        if (pLast)
        {
            pLast->mpNext = p;
            p->mpPrev = pLast;
        }
        pLast = p;
        pList = pList->mpNext;
    }
    return pNewList;
}

static void my_unrefList( lt_list_t* pList )
{
    while (pList)
    {
        lt_list_t* pNext = pList->mpNext;
        pList->decRef();
        pList = pNext;
    }
}

struct lt_tag_t : public my_t_impl
{
    lt_lang_t   maLanguage;
    lt_script_t maScript;
    lt_region_t maRegion;
    lt_list_t*  mpVariants;
    explicit lt_tag_t() : my_t_impl(), maLanguage(), maScript(), maRegion(), mpVariants(NULL) {}
    virtual ~lt_tag_t() {}
    explicit lt_tag_t( const lt_tag_t& r )
        :
            my_t_impl( r),
            maLanguage( r.maLanguage),
            maScript( r.maScript),
            maRegion( r.maRegion),
            mpVariants( my_copyList( r.mpVariants))
    {
    }
    lt_tag_t& operator=( const lt_tag_t& r )
    {
        if (this == &r)
            return *this;
        my_t_impl::operator=( r);
        maLanguage = r.maLanguage;
        maScript = r.maScript;
        maRegion = r.maRegion;
        my_unrefList( mpVariants);
        mpVariants = my_copyList( r.mpVariants);
        return *this;
    }
    void assign( const char* str )
    {
        maLanguage.zero();
        maScript.zero();
        maRegion.zero();
        my_unrefList( mpVariants);
        mpVariants = NULL;
        my_t_impl::assign( str);
    }
};

static void lt_db_initialize() { }
static void lt_db_finalize() { }
static void lt_db_set_datadir( const char* /* dir */ ) { }

static lt_tag_t* lt_tag_new(void)
{
    return new lt_tag_t;
}

static lt_tag_t* lt_tag_copy(lt_tag_t *tag)
{
    return (tag ? new lt_tag_t( *tag) : NULL);
}

static void lt_tag_unref(lt_tag_t *tag)
{
    if (tag)
        tag->decRef();
}

/** See http://tools.ietf.org/html/rfc5646

    We are simply ignorant of grandfathered (irregular and regular) subtags and
    may either bail out or accept them, sorry (or not). However, we do accept
    any i-* irregular and x-* privateuse. Subtags are not checked for validity
    (alpha, digit, registered, ...).
 */
static lt_bool_t lt_tag_parse(lt_tag_t *tag,
                              const char *tag_string,
                              lt_error_t **error)
{
    (void) error;
    if (!tag)
        return 0;
    tag->assign( tag_string);
    if (!tag_string)
        return 0;
    // In case we supported other subtags this would get more complicated.
    /* TODO: variants */
    my_t_impl* aSubtags[] = { &tag->maLanguage, &tag->maScript, &tag->maRegion, NULL };
    my_t_impl** ppSub = &aSubtags[0];
    const char* pStart = tag_string;
    const char* p = pStart;
    const char* pEnd = pStart + strlen( pStart);   // scanning includes \0
    bool bStartLang = true;
    bool bPrivate = false;
    for ( ; p <= pEnd && ppSub && *ppSub; ++p)
    {
        if (p == pEnd || *p == '-')
        {
            size_t nLen = p - pStart;
            if (*ppSub == &tag->maLanguage)
            {
                if (bStartLang)
                {
                    bStartLang = false;
                    switch (nLen)
                    {
                        case 1:     // irregular or privateuse
                            if (*pStart == 'i' || *pStart == 'x')
                            {
                                (*ppSub)->assign( pStart, p);
                                bPrivate = true;
                            }
                            else
                                return 0;   // bad
                            break;
                        case 2:     // ISO 639 alpha-2
                        case 3:     // ISO 639 alpha-3
                            (*ppSub)->assign( pStart, p);
                            break;
                        case 4:     // reserved for future use
                            return 0;   // bad
                            break;
                        case 5:
                        case 6:
                        case 7:
                        case 8:     // registered language subtag
                            (*ppSub++)->assign( pStart, p);
                            break;
                        default:
                            return 0;   // bad
                    }
                }
                else
                {
                    if (nLen > 8)
                        return 0;   // bad
                    if (bPrivate)
                    {
                        // Any combination of  "x" 1*("-" (2*8alphanum))
                        // allowed, store first as language and return ok.
                        // For i-* simply assume the same.
                        (*ppSub)->append( pStart-1, p);
                        return !0;  // ok
                    }
                    else if (nLen == 3)
                    {
                        // extlang subtag, 1 to 3 allowed we don't check that.
                        // But if it's numeric it's a region UN M.49 code
                        // instead and no script subtag is present, so advance.
                        if ('0' <= *pStart && *pStart <= '9')
                        {
                            ppSub += 2; // &tag->maRegion XXX watch this when inserting fields
                            --p;
                            continue;   // for
                        }
                        else
                            (*ppSub)->append( pStart-1, p);
                    }
                    else
                    {
                        // Not part of language subtag, advance.
                        ++ppSub;
                        --p;
                        continue;   // for
                    }
                }
            }
            else if (*ppSub == &tag->maScript)
            {
                switch (nLen)
                {
                    case 4:
                        // script subtag, or a (DIGIT 3alphanum) variant with
                        // no script and no region in which case we stop
                        // parsing.
                        if ('0' <= *pStart && *pStart <= '9')
                            ppSub = NULL;
                        else
                            (*ppSub++)->assign( pStart, p);
                        break;
                    case 3:
                        // This may be a region UN M.49 code if 3DIGIT and no
                        // script code present. Just check first character and
                        // advance.
                        if ('0' <= *pStart && *pStart <= '9')
                        {
                            ++ppSub;
                            --p;
                            continue;   // for
                        }
                        else
                            return 0;   // bad
                        break;
                    case 2:
                        // script omitted, region subtag, advance.
                        ++ppSub;
                        --p;
                        continue;   // for
                        break;
                    case 1:
                        // script omitted, region omitted, extension subtag
                        // with singleton, stop parsing
                        ppSub = NULL;
                        break;
                    case 5:
                    case 6:
                    case 7:
                    case 8:
                        // script omitted, region omitted, variant subtag, stop
                        // parsing.
                        ppSub = NULL;
                    default:
                        return 0;   // bad
                }
            }
            else if (*ppSub == &tag->maRegion)
            {
                if (nLen == 2 || nLen == 3)
                    (*ppSub++)->assign( pStart, p);
                else
                    return 0;   // bad
            }
            pStart = p+1;
        }
    }
    return !0;
}

static char* lt_tag_canonicalize(lt_tag_t *tag,
                                 lt_error_t **error)
{
    (void) error;
    return tag && tag->mpStr ? strdup( tag->mpStr) : NULL;
}

static const lt_lang_t* lt_tag_get_language(const lt_tag_t  *tag)
{
    return tag && tag->maLanguage.mpStr ? &tag->maLanguage : NULL;
}

static const lt_script_t *lt_tag_get_script(const lt_tag_t  *tag)
{
    return tag && tag->maScript.mpStr ? &tag->maScript : NULL;
}

static const lt_region_t *lt_tag_get_region(const lt_tag_t  *tag)
{
    return tag && tag->maRegion.mpStr ? &tag->maRegion : NULL;
}

static const lt_list_t *lt_tag_get_variants(const lt_tag_t  *tag)
{
    return tag ? tag->mpVariants : NULL;
}

static const char *lt_lang_get_tag(const lt_lang_t *lang)
{
    return lang ? lang->mpStr : NULL;
}

static const char *lt_script_get_tag(const lt_script_t *script)
{
    return script ? script->mpStr : NULL;
}

static const char *lt_region_get_tag(const lt_region_t *region)
{
    return region ? region->mpStr : NULL;
}

static const char *lt_variant_get_tag(const lt_variant_t *variant)
{
    return variant ? variant->mpStr : NULL;
}

#ifdef erDEBUG
static void lt_tag_dump(const lt_tag_t *tag)
{
    fprintf( stderr, "\n");
    fprintf( stderr, "SimpleLangtag  langtag: %s\n", tag->mpStr);
    fprintf( stderr, "SimpleLangtag language: %s\n", tag->maLanguage.mpStr);
    fprintf( stderr, "SimpleLangtag   script: %s\n", tag->maScript.mpStr);
    fprintf( stderr, "SimpleLangtag   region: %s\n", tag->maRegion.mpStr);
}
#endif

}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<commit_msg>don't leak variants<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

/** Cheap and cheesy replacement code for liblangtag on systems that do not
    allow / want LGPL code or dependencies on glib.

    XXX NOTE: This code does not check language tags for validity or if they
    are registered with IANA, does not canonicalize or strip default script
    tags if included nor does it do any other fancy stuff that liblangtag is
    capable of. It just makes depending code work without.
 */

#include <cstdlib>
#include <cstring>
#include <cstdio>

namespace {

typedef int lt_bool_t;
typedef void* lt_pointer_t;

struct lt_error_t {
    void *something;
    lt_error_t() : something(NULL) {}
};

static void* g_malloc(size_t s)
{
    return malloc(s);
}

static void g_free(void* p)
{
    if (p)
        free(p);
}

static void lt_error_unref(lt_error_t *error)
{
    if (error)
    {
        g_free( error->something);
        g_free( error);
    }
}

struct my_ref
{
    sal_uInt32 mnRef;
    explicit my_ref() : mnRef(1) {}
    virtual ~my_ref() {}
    void incRef() { ++mnRef; }
    void decRef() { if (--mnRef == 0) delete this; }
};

struct my_t_impl : public my_ref
{
    char* mpStr;
    explicit my_t_impl() : my_ref(), mpStr(NULL) {}
    virtual ~my_t_impl() { g_free( mpStr); }
    explicit my_t_impl( const my_t_impl& r )
        :
            my_ref(),
            mpStr(r.mpStr ? strdup( r.mpStr) : NULL)
    {
    }
    my_t_impl& operator=( const my_t_impl& r )
    {
        if (this == &r)
            return *this;
        g_free( mpStr);
        mpStr = (r.mpStr ? strdup( r.mpStr) : NULL);
        return *this;
    }
    void assign( const char* str )
    {
        g_free( mpStr);
        mpStr = (str ? strdup( str) : NULL);
    }
    void assign( const char* str, const char* stop )
    {
        g_free( mpStr);
        if (str && str < stop)
        {
            mpStr = static_cast<char*>(g_malloc( stop - str + 1));
            memcpy( mpStr, str, stop - str);
            mpStr[stop - str] = 0;
        }
        else
            mpStr = NULL;
    }
    void append( const char* str, const char* stop )
    {
        if (str && str < stop)
        {
            size_t nOld = mpStr ? strlen( mpStr) : 0;
            size_t nNew = nOld + (stop - str) + 1;
            char* p = static_cast<char*>(g_malloc( nNew));
            if (nOld)
                memcpy( p, mpStr, nOld);
            memcpy( p + nOld, str, stop - str);
            p[nNew-1] = 0;
            g_free( mpStr);
            mpStr = p;
        }
    }
    void zero()
    {
        g_free( mpStr);
        mpStr = NULL;
    }
};

struct lt_lang_t : public my_t_impl
{
    explicit lt_lang_t() : my_t_impl() {}
    virtual ~lt_lang_t() {}
};

struct lt_script_t : public my_t_impl
{
    explicit lt_script_t() : my_t_impl() {}
    virtual ~lt_script_t() {}
};

struct lt_region_t : public my_t_impl
{
    explicit lt_region_t() : my_t_impl() {}
    virtual ~lt_region_t() {}
};

struct lt_variant_t : public my_t_impl
{
    explicit lt_variant_t() : my_t_impl() {}
    virtual ~lt_variant_t() {}
};

struct lt_list_t : public my_t_impl
{
    lt_list_t* mpPrev;
    lt_list_t* mpNext;
    explicit lt_list_t() : my_t_impl(), mpPrev(NULL), mpNext(NULL) {}
    explicit lt_list_t( const lt_list_t& r )
        :
            my_t_impl( r), mpPrev(NULL), mpNext(NULL)
    {
    }
    virtual ~lt_list_t()
    {
        if (mpPrev)
            mpPrev->mpNext = mpNext;
        if (mpNext)
            mpNext->mpPrev = mpPrev;
    }
};

static lt_pointer_t lt_list_value( const lt_list_t* p )
{
    // Assuming only char* here.
    return p ? p->mpStr : NULL;
}

static const lt_list_t* lt_list_next( const lt_list_t* p )
{
    return p ? p->mpNext : NULL;
}

static lt_list_t* my_copyList( const lt_list_t * pList )
{
    lt_list_t* pNewList = NULL;
    lt_list_t* pLast = NULL;
    while (pList)
    {
        lt_list_t* p = new lt_list_t( *pList);
        if (!pNewList)
            pNewList = p;
        if (pLast)
        {
            pLast->mpNext = p;
            p->mpPrev = pLast;
        }
        pLast = p;
        pList = pList->mpNext;
    }
    return pNewList;
}

static void my_unrefList( lt_list_t* pList )
{
    while (pList)
    {
        lt_list_t* pNext = pList->mpNext;
        pList->decRef();
        pList = pNext;
    }
}

struct lt_tag_t : public my_t_impl
{
    lt_lang_t   maLanguage;
    lt_script_t maScript;
    lt_region_t maRegion;
    lt_list_t*  mpVariants;
    explicit lt_tag_t() : my_t_impl(), maLanguage(), maScript(), maRegion(), mpVariants(NULL) {}
    virtual ~lt_tag_t()
    {
        my_unrefList( mpVariants);
    }
    explicit lt_tag_t( const lt_tag_t& r )
        :
            my_t_impl( r),
            maLanguage( r.maLanguage),
            maScript( r.maScript),
            maRegion( r.maRegion),
            mpVariants( my_copyList( r.mpVariants))
    {
    }
    lt_tag_t& operator=( const lt_tag_t& r )
    {
        if (this == &r)
            return *this;
        my_t_impl::operator=( r);
        maLanguage = r.maLanguage;
        maScript = r.maScript;
        maRegion = r.maRegion;
        my_unrefList( mpVariants);
        mpVariants = my_copyList( r.mpVariants);
        return *this;
    }
    void assign( const char* str )
    {
        maLanguage.zero();
        maScript.zero();
        maRegion.zero();
        my_unrefList( mpVariants);
        mpVariants = NULL;
        my_t_impl::assign( str);
    }
};

static void lt_db_initialize() { }
static void lt_db_finalize() { }
static void lt_db_set_datadir( const char* /* dir */ ) { }

static lt_tag_t* lt_tag_new(void)
{
    return new lt_tag_t;
}

static lt_tag_t* lt_tag_copy(lt_tag_t *tag)
{
    return (tag ? new lt_tag_t( *tag) : NULL);
}

static void lt_tag_unref(lt_tag_t *tag)
{
    if (tag)
        tag->decRef();
}

/** See http://tools.ietf.org/html/rfc5646

    We are simply ignorant of grandfathered (irregular and regular) subtags and
    may either bail out or accept them, sorry (or not). However, we do accept
    any i-* irregular and x-* privateuse. Subtags are not checked for validity
    (alpha, digit, registered, ...).
 */
static lt_bool_t lt_tag_parse(lt_tag_t *tag,
                              const char *tag_string,
                              lt_error_t **error)
{
    (void) error;
    if (!tag)
        return 0;
    tag->assign( tag_string);
    if (!tag_string)
        return 0;
    // In case we supported other subtags this would get more complicated.
    /* TODO: variants */
    my_t_impl* aSubtags[] = { &tag->maLanguage, &tag->maScript, &tag->maRegion, NULL };
    my_t_impl** ppSub = &aSubtags[0];
    const char* pStart = tag_string;
    const char* p = pStart;
    const char* pEnd = pStart + strlen( pStart);   // scanning includes \0
    bool bStartLang = true;
    bool bPrivate = false;
    for ( ; p <= pEnd && ppSub && *ppSub; ++p)
    {
        if (p == pEnd || *p == '-')
        {
            size_t nLen = p - pStart;
            if (*ppSub == &tag->maLanguage)
            {
                if (bStartLang)
                {
                    bStartLang = false;
                    switch (nLen)
                    {
                        case 1:     // irregular or privateuse
                            if (*pStart == 'i' || *pStart == 'x')
                            {
                                (*ppSub)->assign( pStart, p);
                                bPrivate = true;
                            }
                            else
                                return 0;   // bad
                            break;
                        case 2:     // ISO 639 alpha-2
                        case 3:     // ISO 639 alpha-3
                            (*ppSub)->assign( pStart, p);
                            break;
                        case 4:     // reserved for future use
                            return 0;   // bad
                            break;
                        case 5:
                        case 6:
                        case 7:
                        case 8:     // registered language subtag
                            (*ppSub++)->assign( pStart, p);
                            break;
                        default:
                            return 0;   // bad
                    }
                }
                else
                {
                    if (nLen > 8)
                        return 0;   // bad
                    if (bPrivate)
                    {
                        // Any combination of  "x" 1*("-" (2*8alphanum))
                        // allowed, store first as language and return ok.
                        // For i-* simply assume the same.
                        (*ppSub)->append( pStart-1, p);
                        return !0;  // ok
                    }
                    else if (nLen == 3)
                    {
                        // extlang subtag, 1 to 3 allowed we don't check that.
                        // But if it's numeric it's a region UN M.49 code
                        // instead and no script subtag is present, so advance.
                        if ('0' <= *pStart && *pStart <= '9')
                        {
                            ppSub += 2; // &tag->maRegion XXX watch this when inserting fields
                            --p;
                            continue;   // for
                        }
                        else
                            (*ppSub)->append( pStart-1, p);
                    }
                    else
                    {
                        // Not part of language subtag, advance.
                        ++ppSub;
                        --p;
                        continue;   // for
                    }
                }
            }
            else if (*ppSub == &tag->maScript)
            {
                switch (nLen)
                {
                    case 4:
                        // script subtag, or a (DIGIT 3alphanum) variant with
                        // no script and no region in which case we stop
                        // parsing.
                        if ('0' <= *pStart && *pStart <= '9')
                            ppSub = NULL;
                        else
                            (*ppSub++)->assign( pStart, p);
                        break;
                    case 3:
                        // This may be a region UN M.49 code if 3DIGIT and no
                        // script code present. Just check first character and
                        // advance.
                        if ('0' <= *pStart && *pStart <= '9')
                        {
                            ++ppSub;
                            --p;
                            continue;   // for
                        }
                        else
                            return 0;   // bad
                        break;
                    case 2:
                        // script omitted, region subtag, advance.
                        ++ppSub;
                        --p;
                        continue;   // for
                        break;
                    case 1:
                        // script omitted, region omitted, extension subtag
                        // with singleton, stop parsing
                        ppSub = NULL;
                        break;
                    case 5:
                    case 6:
                    case 7:
                    case 8:
                        // script omitted, region omitted, variant subtag, stop
                        // parsing.
                        ppSub = NULL;
                    default:
                        return 0;   // bad
                }
            }
            else if (*ppSub == &tag->maRegion)
            {
                if (nLen == 2 || nLen == 3)
                    (*ppSub++)->assign( pStart, p);
                else
                    return 0;   // bad
            }
            pStart = p+1;
        }
    }
    return !0;
}

static char* lt_tag_canonicalize(lt_tag_t *tag,
                                 lt_error_t **error)
{
    (void) error;
    return tag && tag->mpStr ? strdup( tag->mpStr) : NULL;
}

static const lt_lang_t* lt_tag_get_language(const lt_tag_t  *tag)
{
    return tag && tag->maLanguage.mpStr ? &tag->maLanguage : NULL;
}

static const lt_script_t *lt_tag_get_script(const lt_tag_t  *tag)
{
    return tag && tag->maScript.mpStr ? &tag->maScript : NULL;
}

static const lt_region_t *lt_tag_get_region(const lt_tag_t  *tag)
{
    return tag && tag->maRegion.mpStr ? &tag->maRegion : NULL;
}

static const lt_list_t *lt_tag_get_variants(const lt_tag_t  *tag)
{
    return tag ? tag->mpVariants : NULL;
}

static const char *lt_lang_get_tag(const lt_lang_t *lang)
{
    return lang ? lang->mpStr : NULL;
}

static const char *lt_script_get_tag(const lt_script_t *script)
{
    return script ? script->mpStr : NULL;
}

static const char *lt_region_get_tag(const lt_region_t *region)
{
    return region ? region->mpStr : NULL;
}

static const char *lt_variant_get_tag(const lt_variant_t *variant)
{
    return variant ? variant->mpStr : NULL;
}

#ifdef erDEBUG
static void lt_tag_dump(const lt_tag_t *tag)
{
    fprintf( stderr, "\n");
    fprintf( stderr, "SimpleLangtag  langtag: %s\n", tag->mpStr);
    fprintf( stderr, "SimpleLangtag language: %s\n", tag->maLanguage.mpStr);
    fprintf( stderr, "SimpleLangtag   script: %s\n", tag->maScript.mpStr);
    fprintf( stderr, "SimpleLangtag   region: %s\n", tag->maRegion.mpStr);
}
#endif

}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<|endoftext|>