text: // Copyright 2017 PDFium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
  
  #include "core/fpdfapi/page/cpdf_psengine.h"
  
  #include <algorithm>
  #include <cmath>
  #include <limits>
  #include <utility>
  
  #include "core/fpdfapi/parser/cpdf_simple_parser.h"
  #include "core/fxcrt/fx_safe_types.h"
  #include "core/fxcrt/fx_string.h"
  #include "third_party/base/check.h"
  #include "third_party/base/notreached.h"
  
  namespace {
  
  struct PDF_PSOpName {
    // Inline string data reduces size for small strings.
    const char name[9];
    PDF_PSOP op;
  };
  
  constexpr PDF_PSOpName kPsOpNames[] = {
      {"abs", PSOP_ABS},
      {"add", PSOP_ADD},
      {"and", PSOP_AND},
      {"atan", PSOP_ATAN},
      {"bitshift", PSOP_BITSHIFT},
      {"ceiling", PSOP_CEILING},
      {"copy", PSOP_COPY},
      {"cos", PSOP_COS},
      {"cvi", PSOP_CVI},
      {"cvr", PSOP_CVR},
      {"div", PSOP_DIV},
      {"dup", PSOP_DUP},
      {"eq", PSOP_EQ},
      {"exch", PSOP_EXCH},
      {"exp", PSOP_EXP},
      {"false", PSOP_FALSE},
      {"floor", PSOP_FLOOR},
      {"ge", PSOP_GE},
      {"gt", PSOP_GT},
      {"idiv", PSOP_IDIV},
      {"if", PSOP_IF},
      {"ifelse", PSOP_IFELSE},
      {"index", PSOP_INDEX},
      {"le", PSOP_LE},
      {"ln", PSOP_LN},
      {"log", PSOP_LOG},
      {"lt", PSOP_LT},
      {"mod", PSOP_MOD},
      {"mul", PSOP_MUL},
      {"ne", PSOP_NE},
      {"neg", PSOP_NEG},
      {"not", PSOP_NOT},
      {"or", PSOP_OR},
      {"pop", PSOP_POP},
      {"roll", PSOP_ROLL},
      {"round", PSOP_ROUND},
      {"sin", PSOP_SIN},
      {"sqrt", PSOP_SQRT},
      {"sub", PSOP_SUB},
      {"true", PSOP_TRUE},
      {"truncate", PSOP_TRUNCATE},
      {"xor", PSOP_XOR},
  };
  
  // Round half up is a nearest integer round with half-way numbers always rounded
  // up. Example: -5.5 rounds to -5.
  float RoundHalfUp(float f) {
    if (std::isnan(f))
      return 0;
    if (f > std::numeric_limits<float>::max() - 0.5f)
      return std::numeric_limits<float>::max();
    return floor(f + 0.5f);
  }
  
  }  // namespace
  
  CPDF_PSOP::CPDF_PSOP()
      : m_op(PSOP_PROC), m_value(0), m_proc(std::make_unique<CPDF_PSProc>()) {}
  
  CPDF_PSOP::CPDF_PSOP(PDF_PSOP op) : m_op(op), m_value(0) {
    DCHECK(m_op != PSOP_CONST);
    DCHECK(m_op != PSOP_PROC);
  }
  
  CPDF_PSOP::CPDF_PSOP(float value) : m_op(PSOP_CONST), m_value(value) {}
  
  CPDF_PSOP::~CPDF_PSOP() = default;
  
  float CPDF_PSOP::GetFloatValue() const {
    if (m_op == PSOP_CONST)
      return m_value;
  
    NOTREACHED();
    return 0;
  }
  
  CPDF_PSProc* CPDF_PSOP::GetProc() const {
    if (m_op == PSOP_PROC)
      return m_proc.get();
    NOTREACHED();
    return nullptr;
  }
  
  bool CPDF_PSEngine::Execute() {
    return m_MainProc.Execute(this);
  }
  
  CPDF_PSProc::CPDF_PSProc() = default;
  
  CPDF_PSProc::~CPDF_PSProc() = default;
  
  bool CPDF_PSProc::Parse(CPDF_SimpleParser* parser, int depth) {
    if (depth > kMaxDepth)
      return false;
  
    while (1) {
      ByteStringView word = parser->GetWord();
      if (word.IsEmpty())
        return false;
  
      if (word == "}")
        return true;
  
      if (word == "{") {
        m_Operators.push_back(std::make_unique<CPDF_PSOP>());
        if (!m_Operators.back()->GetProc()->Parse(parser, depth + 1))
          return false;
        continue;
      }
  
      AddOperator(word);
    }
  }
  
  bool CPDF_PSProc::Execute(CPDF_PSEngine* pEngine) {
    for (size_t i = 0; i < m_Operators.size(); ++i) {
      const PDF_PSOP op = m_Operators[i]->GetOp();
      if (op == PSOP_PROC)
        continue;
  
      if (op == PSOP_CONST) {
        pEngine->Push(m_Operators[i]->GetFloatValue());
        continue;
      }
  
      if (op == PSOP_IF) {
        if (i == 0 || m_Operators[i - 1]->GetOp() != PSOP_PROC)
          return false;
  
        if (pEngine->PopInt())
          m_Operators[i - 1]->GetProc()->Execute(pEngine);
      } else if (op == PSOP_IFELSE) {
        if (i < 2 || m_Operators[i - 1]->GetOp() != PSOP_PROC ||
            m_Operators[i - 2]->GetOp() != PSOP_PROC) {
          return false;
        }
        size_t offset = pEngine->PopInt() ? 2 : 1;
        m_Operators[i - offset]->GetProc()->Execute(pEngine);
      } else {
        pEngine->DoOperator(op);
      }
    }
    return true;
  }
  
  void CPDF_PSProc::AddOperatorForTesting(ByteStringView word) {
    AddOperator(word);
  }
  
  void CPDF_PSProc::AddOperator(ByteStringView word) {
    const auto* pFound =
        std::lower_bound(std::begin(kPsOpNames), std::end(kPsOpNames), word,
                         [](const PDF_PSOpName& name, ByteStringView word) {
                           return name.name < word;
                         });
    if (pFound != std::end(kPsOpNames) && pFound->name == word)
      m_Operators.push_back(std::make_unique<CPDF_PSOP>(pFound->op));
    else
      m_Operators.push_back(std::make_unique<CPDF_PSOP>(StringToFloat(word)));
  }
  
  CPDF_PSEngine::CPDF_PSEngine() = default;
  
  CPDF_PSEngine::~CPDF_PSEngine() = default;
  
  void CPDF_PSEngine::Push(float v) {
    if (m_StackCount < kPSEngineStackSize)
      m_Stack[m_StackCount++] = v;
  }
  
  float CPDF_PSEngine::Pop() {
    return m_StackCount > 0 ? m_Stack[--m_StackCount] : 0;
  }
  
  int CPDF_PSEngine::PopInt() {
    return static_cast<int>(Pop());
  }
  
  bool CPDF_PSEngine::Parse(pdfium::span<const uint8_t> input) {
    CPDF_SimpleParser parser(input);
    return parser.GetWord() == "{" && m_MainProc.Parse(&parser, 0);
  }
  
  bool CPDF_PSEngine::DoOperator(PDF_PSOP op) {
    int i1;
    int i2;
    float d1;
    float d2;
    FX_SAFE_INT32 result;
    switch (op) {
      case PSOP_ADD:
        d1 = Pop();
        d2 = Pop();
        Push(d1 + d2);
        break;
      case PSOP_SUB:
        d2 = Pop();
        d1 = Pop();
        Push(d1 - d2);
        break;
      case PSOP_MUL:
        d1 = Pop();
        d2 = Pop();
        Push(d1 * d2);
        break;
      case PSOP_DIV:
        d2 = Pop();
        d1 = Pop();
        Push(d1 / d2);
        break;
      case PSOP_IDIV:
        i2 = PopInt();
        i1 = PopInt();
        if (i2) {
          result = i1;
          result /= i2;
          Push(result.ValueOrDefault(0));
        } else {
          Push(0);
        }
        break;
      case PSOP_MOD:
        i2 = PopInt();
        i1 = PopInt();
        if (i2) {
          result = i1;
          result %= i2;
          Push(result.ValueOrDefault(0));
        } else {
          Push(0);
        }
        break;
      case PSOP_NEG:
        d1 = Pop();
        Push(-d1);
        break;
      case PSOP_ABS:
        d1 = Pop();
        Push(fabs(d1));
        break;
      case PSOP_CEILING:
        d1 = Pop();
        Push(ceil(d1));
        break;
      case PSOP_FLOOR:
        d1 = Pop();
        Push(floor(d1));
        break;
      case PSOP_ROUND:
        d1 = Pop();
        Push(RoundHalfUp(d1));
        break;
      case PSOP_TRUNCATE:
        i1 = PopInt();
        Push(i1);
        break;
      case PSOP_SQRT:
        d1 = Pop();
        Push(sqrt(d1));
        break;
      case PSOP_SIN:
        d1 = Pop();
        Push(sin(d1 * FX_PI / 180.0f));
        break;
      case PSOP_COS:
        d1 = Pop();
        Push(cos(d1 * FX_PI / 180.0f));
        break;
      case PSOP_ATAN:
        d2 = Pop();
        d1 = Pop();
        d1 = atan2(d1, d2) * 180.0 / FX_PI;
        if (d1 < 0) {
          d1 += 360;
        }
        Push(d1);
        break;
      case PSOP_EXP:
        d2 = Pop();
        d1 = Pop();
        Push(FXSYS_pow(d1, d2));
        break;
      case PSOP_LN:
        d1 = Pop();
        Push(log(d1));
        break;
      case PSOP_LOG:
        d1 = Pop();
        Push(log10(d1));
        break;
      case PSOP_CVI:
        i1 = PopInt();
        Push(i1);
        break;
      case PSOP_CVR:
        break;
      case PSOP_EQ:
        d2 = Pop();
        d1 = Pop();
        Push(d1 == d2);
        break;
      case PSOP_NE:
        d2 = Pop();
        d1 = Pop();
        Push(d1 != d2);
        break;
      case PSOP_GT:
        d2 = Pop();
        d1 = Pop();
        Push(d1 > d2);
        break;
      case PSOP_GE:
        d2 = Pop();
        d1 = Pop();
        Push(d1 >= d2);
        break;
      case PSOP_LT:
        d2 = Pop();
        d1 = Pop();
        Push(d1 < d2);
        break;
      case PSOP_LE:
        d2 = Pop();
        d1 = Pop();
        Push(d1 <= d2);
        break;
      case PSOP_AND:
        i1 = PopInt();
        i2 = PopInt();
        Push(i1 & i2);
        break;
      case PSOP_OR:
        i1 = PopInt();
        i2 = PopInt();
        Push(i1 | i2);
        break;
      case PSOP_XOR:
        i1 = PopInt();
        i2 = PopInt();
        Push(i1 ^ i2);
        break;
      case PSOP_NOT:
        i1 = PopInt();
        Push(!i1);
        break;
      case PSOP_BITSHIFT: {
        int shift = PopInt();
        result = PopInt();
        if (shift > 0) {
          result <<= shift;
        } else {
          // Avoids unsafe negation of INT_MIN.
          FX_SAFE_INT32 safe_shift = shift;
          result >>= (-safe_shift).ValueOrDefault(0);
        }
        Push(result.ValueOrDefault(0));
        break;
      }
      case PSOP_TRUE:
        Push(1);
        break;
      case PSOP_FALSE:
        Push(0);
        break;
      case PSOP_POP:
        Pop();
        break;
      case PSOP_EXCH:
        d2 = Pop();
        d1 = Pop();
        Push(d2);
        Push(d1);
        break;
      case PSOP_DUP:
        d1 = Pop();
        Push(d1);
        Push(d1);
        break;
      case PSOP_COPY: {
        int n = PopInt();
        if (n < 0 || m_StackCount + n > kPSEngineStackSize ||
            n > static_cast<int>(m_StackCount))
          break;
        for (int i = 0; i < n; i++)
          m_Stack[m_StackCount + i] = m_Stack[m_StackCount + i - n];
        m_StackCount += n;
        break;
      }
      case PSOP_INDEX: {
        int n = PopInt();
        if (n < 0 || n >= static_cast<int>(m_StackCount))
          break;
        Push(m_Stack[m_StackCount - n - 1]);
        break;
      }
      case PSOP_ROLL: {
        int j = PopInt();
        int n = PopInt();
        if (j == 0 || n == 0 || m_StackCount == 0)
          break;
        if (n < 0 || n > static_cast<int>(m_StackCount))
          break;
  
        j %= n;
        if (j > 0)
          j -= n;
        auto* begin_it = std::begin(m_Stack) + m_StackCount - n;
        auto* middle_it = begin_it - j;
        auto* end_it = std::begin(m_Stack) + m_StackCount;
        std::rotate(begin_it, middle_it, end_it);
        break;
      }
      default:
        break;
    }
    return true;
  }
  