<commit_before>// Copyright (2015) Gustav

#include "finans/core/commandline.h"

#include <cassert>
#include <algorithm>
#include <string>
#include <set>

namespace argparse {

  ParserError::ParserError(const std::string& error)
    : runtime_error("error: " + error), has_displayed_usage(false)
  {
  }

  void OnParserError(Running& running, const Parser* parser, ParserError& p) {
    if( p.has_displayed_usage == false ) {
      running.o << "Usage:";
      parser->WriteUsage(running);
      p.has_displayed_usage = true;
    }
  }

  Arguments::Arguments(int argc, char* argv[]) : name_(argv[0])
  {
    for (int i = 1; i < argc; ++i)
    {
      args_.push_back(argv[i]);
    }
  }
  Arguments::Arguments(const std::string& name, const std::vector<std::string>& args) : name_(name), args_(args) {
  }

  const std::string Arguments::operator[](int index) const
  {
    return args_[index];
  }
  const bool Arguments::is_empty() const
  {
    return args_.empty();
  }
  const std::string Arguments::name() const
  {
    return name_;
  }
  const size_t Arguments::size() const
  {
    return args_.size();
  }
  const std::string Arguments::ConsumeOne(const std::string& error)
  {
    if (is_empty()) throw ParserError(error);
    const std::string r = args_[0];
    args_.erase(args_.begin());
    return r;
  }

  Count::Count(size_t c)
    : count_(c)
    , type_(Const)
  {
  }

  Count::Count(Count::Type t)
    : count_(0)
    , type_(t)
  {
    assert(t != Const);
  }

  Count::Type Count::type() const
  {
    return type_;
  }
  size_t Count::count() const
  {
    return count_;
  }

  Running::Running(const std::string& aapp, std::ostream& ao, std::ostream& ae)
    : app(aapp)
    , o(ao)
    , e(ae)
    , quit(false)
  {
  }

  Argument::~Argument()
  {
  }

  Argument::Argument(const Count& co)
    : count_(co)
    , has_been_parsed_(false)
    , has_several_(false)
  {
  }

  bool Argument::has_been_parsed() const {
    return has_been_parsed_;
  }

  void Argument::set_has_been_parsed(bool v) {
    if (has_several_) return;
    has_been_parsed_ = v;
  }

  void Argument::ConsumeArguments(Running& r, Arguments& args, const std::string& argname) {
    switch (count_.type())
    {
    case Count::Const:
      for (size_t i = 0; i < count_.count(); ++i)
      {
        std::stringstream ss;
        ss << "argument " << argname << ": expected ";
        if (count_.count() == 1)
        {
          ss << "one argument";
        }
        else
        {
          ss << count_.count() << " argument(s), " << i << " already given";
        }
        OnArgument(r, args.ConsumeOne(ss.str()));

        // abort on optional?
      }
      return;
    case Count::MoreThanOne:
      OnArgument(r, args.ConsumeOne("argument " + argname + ": expected atleast one argument"));
      // fall through
    case Count::ZeroOrMore:
      while (args.is_empty() == false)
      {
        OnArgument(r, args.ConsumeOne("internal error"));
      }
      return;
    case Count::Optional:
      if (args.is_empty()) {
        OnArgument(r, "");
        return;
      }
      if (IsOptional(args[0])) {
        OnArgument(r, "");
        return;
      }
      OnArgument(r, args.ConsumeOne("internal error"));
      return;
    case Count::None:
      OnArgument(r, "");
      return;
    default:
      assert(0 && "internal error, ArgumentT::parse invalid Count");
      throw "internal error, ArgumentT::parse invalid Count";
    }
  }

  void Argument::set_has_several() {
    has_several_ = true;
  }

  bool IsOptional(const std::string& arg)
  {
    if (arg.empty()) return false; // todo: assert this?
    return arg[0] == '-';
  }

  Extra::Extra()
    : count_(1)
    , has_several_(false)
  {
  }

  Extra& Extra::help(const std::string& h)
  {
    help_ = h;
    return *this;
  }
  const std::string& Extra::help() const
  {
    return help_;
  }

  Extra& Extra::count(const Count c)
  {
    count_ = c;
    return *this;
  }
  const Count& Extra::count() const
  {
    return count_;
  }

  Extra& Extra::metavar(const std::string& metavar)
  {
    metaVar_ = metavar;
    return *this;
  }

  const std::string& Extra::metavar() const
  {
    return metaVar_;
  }

  Extra& Extra::several() {
    has_several_ = true;
    return *this;
  }
  bool Extra::has_several() const {
    return has_several_;
  }

  std::string ToUpper(const std::string& s)
  {
    std::string str = s;
    std::transform(str.begin(), str.end(), str.begin(), toupper);
    return str;
  }

  Help::Help(const std::string& name, const Extra& e)
    : name_(name)
    , help_(e.help())
    , metavar_(e.metavar())
    , count_(e.count().type())
    , countcount_(e.count().count())

  {
  }

  const std::string Help::GetUsage() const
  {
    if (IsOptional(name_))
    {
      const auto rep = GetMetavarReprestentation();
      if( rep.empty()) return "[" + name_ + "]";
      return "[" + name_ + " " + rep + "]";
    }
    else
    {
      return GetMetavarReprestentation();
    }
  }

  const std::string Help::GetMetavarReprestentation() const
  {
    switch (count_)
    {
    case Count::None:
      return "";
    case Count::MoreThanOne:
      return GetMetavarName() + " [" + GetMetavarName() + " ...]";
    case Count::Optional:
      return "[" + GetMetavarName() + "]";
    case Count::ZeroOrMore:
      return "[" + GetMetavarName() + " [" + GetMetavarName() + " ...]]";
    case Count::Const:
    {
      std::ostringstream ss;
      for (size_t i = 0; i < countcount_; ++i)
      {
        if (i != 0)
        {
          ss << " ";
        }
        ss << GetMetavarName();
      }
      return ss.str();
    }
    default:
      assert(false && "missing case");
      throw "invalid count type in " __FUNCTION__;
    }
  }

  const std::string Help::GetMetavarName() const
  {
    if (metavar_.empty() == false)
    {
      return metavar_;
    }
    else
    {
      if (IsOptional(name_))
      {
        return ToUpper(name_.substr(1));
      }
      else
      {
        return name_;
      }
    }
  }

  const std::string Help::GetHelpCommand() const
  {
    if (IsOptional(name_))
    {
      const auto meta = GetMetavarReprestentation();
      if (meta.empty()) {
        return name_;
      }
      else {
        return name_ + " " + meta;
      }
    }
    else
    {
      return GetMetavarName();
    }
  }

  const std::string& Help::help() const
  {
    return help_;
  }

  struct CallHelp : public Argument
  {
    CallHelp(Parser* on)
      : Argument( Count(Count::Optional) )
      , parser(on)
    {
    }

    void OnArgument(Running& r, const std::string& argname) override
    {
      parser->WriteHelp(r);
      r.quit = true;
    }

    Parser* parser;
  };

  class ParserChild : public Parser {
  public:
    ParserChild(const std::string& desc, const Parser* parent) : Parser(desc), parent_(parent) {}

    void WriteUsage(Running& r) const override {
      parent_->WriteUsage(r);
      Parser::WriteUsage(r);
    }

  private:
    const Parser* parent_;
  };

  Parser::Parser(const std::string& d, const std::string aappname)
    : positionalIndex_(0)
    , description_(d)
    , appname_(aappname)
    , sub_parsers_("subparser")
  {
    std::shared_ptr<Argument> arg(new CallHelp(this));
    AddArgument("-h", arg, Extra().count(Count(Count::None)).help("Show this help message and exit."));
  }

  Parser::ParseStatus Parser::ParseArgs(int argc, char* argv[], std::ostream& out, std::ostream& error) const
  {
    Arguments args(argc, argv);
    return ParseArgs(args, out, error);
  }

  void Parser::AddSubParser(const std::string& name, SubParser* parser) {
    sub_parsers_(name, parser);
  }

  Parser::ParseStatus Parser::ParseArgs(const Arguments& arguments, std::ostream& out, std::ostream& error) const {
    Arguments args = arguments;
    Running running(arguments.name(), out, error);

    try {
      return DoParseArgs(args, running);
    }
    catch (ParserError& p) {
      OnParserError(running, this, p);
      running.e << p.what() << "\n";
      running.o << "\n";
      return Parser::ParseStatus::ParseFailed;
    }
  }

  Parser::ParseStatus Parser::DoParseArgs(Arguments& args, Running& running) const {
    try
    {
      while (false == args.is_empty())
      {
        bool isParsed = false;
        const bool isParsingPositionals = positionalIndex_ < positionals_.size();

        if (IsOptional(args[0]))
        {
          // optional
          const std::string arg = args[0];
          Optionals::const_iterator r = optionals_.find(arg);
          if (r == optionals_.end())
          {
            if (isParsingPositionals == false) {
              throw ParserError("Unknown optional argument: " + arg); // todo: implement partial matching of arguments?
            }
          }
          else {
            if (false == r->second->has_been_parsed()) {
              isParsed = true;
              args.ConsumeOne(); // the optional command = arg[0}
              r->second->ConsumeArguments(running, args, arg);
              r->second->set_has_been_parsed(true);
              if (running.quit) return ParseStatus::ParseQuit;
            }
          }
        }

        bool consumed = false;

        if (isParsed == false) {
          if (positionalIndex_ >= positionals_.size())
          {
            if (sub_parsers_.empty()) {
              throw ParserError("All positional arguments have been consumed: " + args[0]);
            }
            else {
              std::string subname;
              SubParser* sub = sub_parsers_.Convert(args[0], &subname);
              sub_parser_used_ = subname;
              ParserChild parser(args[0], this);
              sub->AddParser(parser);
              consumed = true;
              args.ConsumeOne("SUBCOMMAND");
              try {
                return parser.DoParseArgs(args, running);
              }
              catch (ParserError& p) {
                OnParserError(running, &parser, p);
                running.e << "error: Failed to parse " << subname << ":\n";
                throw;
              }
            }
          }
          if (consumed == false) {
            ArgumentPtr p = positionals_[positionalIndex_];
            ++positionalIndex_;
            p->ConsumeArguments(running, args, "POSITIONAL"); // todo: give better name or something
          }
        }
      }

      if (positionalIndex_ != positionals_.size())
      {
        throw ParserError("too few arguments."); // todo: list a few missing arguments...
      }

      return ParseComplete;
    }
    catch (ParserError& p)
    {
      OnParserError(running, this, p);
      throw;
    }
  }

  void Parser::WriteHelp(Running& r) const
  {
    r.o << "Usage:";
    r.o << " " << (appname_.empty() ? r.app : appname_);
    WriteUsage(r);
    r.o << std::endl << description_ << std::endl << std::endl;

    const std::string sep = "\t";
    const std::string ins = "  ";

    if (helpPositional_.empty() == false)
    {
      r.o << "Positional arguments:" << std::endl;
      for (const Help& positional : helpPositional_)
      {
        r.o << ins << positional.GetHelpCommand();
        const auto h = positional.help();
        if (h.empty() == false) {
          r.o << sep << h;
        }
        r.o << std::endl;
      }

      r.o << std::endl;
    }

    if (helpOptional_.empty() == false)
    {
      r.o << "Optional arguments:" << std::endl;
      std::set<std::string> opts;
      for (const Help& optional : helpOptional_)
      {
        std::ostringstream ss;
        ss << ins << optional.GetHelpCommand();
        const auto h = optional.help();
        if( h.empty() == false) {
          ss << sep << h;
        }
        opts.insert(ss.str());
      }
      for (const auto& s : opts) {
        r.o << s << "\n";
      }
    }

    r.o << std::endl;
  }

  void Parser::WriteUsage(Running& r) const
  {
    for (const Help& optional : helpOptional_)
    {
      r.o << " " << optional.GetUsage();
    }

    if (false == sub_parsers_.empty()) {
      // if the sub-parser is set, use it instead of the array
      if (false == sub_parser_used_.empty()) {
        r.o << " " << sub_parser_used_;
      }
      else {
        const auto sp = sub_parsers_.names();
        r.o << " {";
        bool first = true;
        for (const auto& p : sp) {
          if (first == false) {
            r.o << "|";
          }
          first = false;
          r.o << p;
        }
        r.o << "}";
      }
    }

    for (const Help& positional : helpPositional_)
    {
      r.o << " " << positional.GetUsage();
    }
  }

  Parser& Parser::AddArgument(const std::string& commands, ArgumentPtr arg, const Extra& extra)
  {
    if( extra.has_several() ) {
      arg->set_has_several();
    }
    const auto names = Tokenize(commands, ",", true);
    std::string thename = "";
    int optionalcount = 0;
    int positionalcount = 0;
    for(const auto name: names) {
      if (IsOptional(name))
      {
        optionals_.insert(Optionals::value_type(name, arg));
        if (thename.empty()) thename = name.substr(1);
        ++optionalcount;
      }
      else
      {
        positionals_.push_back(arg);
        if( thename.empty() ) thename = name;
        ++positionalcount;
      }
    }
    Extra e = extra;
    if (e.metavar().empty()) {
      e.metavar(thename);
    }
    if (positionalcount > 0 && optionalcount > 0) {
      assert(false && "Optional and positional in argumentlist is not supported.");
    }
    if( positionalcount > 0 ) {
      helpPositional_.push_back(Help(commands, e));
    }
    else {
      assert(optionalcount > 0);
      helpOptional_.push_back(Help(commands, e));
    }
    return *this;
  }
}
<commit_msg>call parse completed when subparser is done<commit_after>// Copyright (2015) Gustav

#include "finans/core/commandline.h"

#include <cassert>
#include <algorithm>
#include <string>
#include <set>

namespace argparse {

  ParserError::ParserError(const std::string& error)
    : runtime_error("error: " + error), has_displayed_usage(false)
  {
  }

  void OnParserError(Running& running, const Parser* parser, ParserError& p) {
    if( p.has_displayed_usage == false ) {
      running.o << "Usage:";
      parser->WriteUsage(running);
      p.has_displayed_usage = true;
    }
  }

  Arguments::Arguments(int argc, char* argv[]) : name_(argv[0])
  {
    for (int i = 1; i < argc; ++i)
    {
      args_.push_back(argv[i]);
    }
  }
  Arguments::Arguments(const std::string& name, const std::vector<std::string>& args) : name_(name), args_(args) {
  }

  const std::string Arguments::operator[](int index) const
  {
    return args_[index];
  }
  const bool Arguments::is_empty() const
  {
    return args_.empty();
  }
  const std::string Arguments::name() const
  {
    return name_;
  }
  const size_t Arguments::size() const
  {
    return args_.size();
  }
  const std::string Arguments::ConsumeOne(const std::string& error)
  {
    if (is_empty()) throw ParserError(error);
    const std::string r = args_[0];
    args_.erase(args_.begin());
    return r;
  }

  Count::Count(size_t c)
    : count_(c)
    , type_(Const)
  {
  }

  Count::Count(Count::Type t)
    : count_(0)
    , type_(t)
  {
    assert(t != Const);
  }

  Count::Type Count::type() const
  {
    return type_;
  }
  size_t Count::count() const
  {
    return count_;
  }

  Running::Running(const std::string& aapp, std::ostream& ao, std::ostream& ae)
    : app(aapp)
    , o(ao)
    , e(ae)
    , quit(false)
  {
  }

  Argument::~Argument()
  {
  }

  Argument::Argument(const Count& co)
    : count_(co)
    , has_been_parsed_(false)
    , has_several_(false)
  {
  }

  bool Argument::has_been_parsed() const {
    return has_been_parsed_;
  }

  void Argument::set_has_been_parsed(bool v) {
    if (has_several_) return;
    has_been_parsed_ = v;
  }

  void Argument::ConsumeArguments(Running& r, Arguments& args, const std::string& argname) {
    switch (count_.type())
    {
    case Count::Const:
      for (size_t i = 0; i < count_.count(); ++i)
      {
        std::stringstream ss;
        ss << "argument " << argname << ": expected ";
        if (count_.count() == 1)
        {
          ss << "one argument";
        }
        else
        {
          ss << count_.count() << " argument(s), " << i << " already given";
        }
        OnArgument(r, args.ConsumeOne(ss.str()));

        // abort on optional?
      }
      return;
    case Count::MoreThanOne:
      OnArgument(r, args.ConsumeOne("argument " + argname + ": expected atleast one argument"));
      // fall through
    case Count::ZeroOrMore:
      while (args.is_empty() == false)
      {
        OnArgument(r, args.ConsumeOne("internal error"));
      }
      return;
    case Count::Optional:
      if (args.is_empty()) {
        OnArgument(r, "");
        return;
      }
      if (IsOptional(args[0])) {
        OnArgument(r, "");
        return;
      }
      OnArgument(r, args.ConsumeOne("internal error"));
      return;
    case Count::None:
      OnArgument(r, "");
      return;
    default:
      assert(0 && "internal error, ArgumentT::parse invalid Count");
      throw "internal error, ArgumentT::parse invalid Count";
    }
  }

  void Argument::set_has_several() {
    has_several_ = true;
  }

  bool IsOptional(const std::string& arg)
  {
    if (arg.empty()) return false; // todo: assert this?
    return arg[0] == '-';
  }

  Extra::Extra()
    : count_(1)
    , has_several_(false)
  {
  }

  Extra& Extra::help(const std::string& h)
  {
    help_ = h;
    return *this;
  }
  const std::string& Extra::help() const
  {
    return help_;
  }

  Extra& Extra::count(const Count c)
  {
    count_ = c;
    return *this;
  }
  const Count& Extra::count() const
  {
    return count_;
  }

  Extra& Extra::metavar(const std::string& metavar)
  {
    metaVar_ = metavar;
    return *this;
  }

  const std::string& Extra::metavar() const
  {
    return metaVar_;
  }

  Extra& Extra::several() {
    has_several_ = true;
    return *this;
  }
  bool Extra::has_several() const {
    return has_several_;
  }

  std::string ToUpper(const std::string& s)
  {
    std::string str = s;
    std::transform(str.begin(), str.end(), str.begin(), toupper);
    return str;
  }

  Help::Help(const std::string& name, const Extra& e)
    : name_(name)
    , help_(e.help())
    , metavar_(e.metavar())
    , count_(e.count().type())
    , countcount_(e.count().count())

  {
  }

  const std::string Help::GetUsage() const
  {
    if (IsOptional(name_))
    {
      const auto rep = GetMetavarReprestentation();
      if( rep.empty()) return "[" + name_ + "]";
      return "[" + name_ + " " + rep + "]";
    }
    else
    {
      return GetMetavarReprestentation();
    }
  }

  const std::string Help::GetMetavarReprestentation() const
  {
    switch (count_)
    {
    case Count::None:
      return "";
    case Count::MoreThanOne:
      return GetMetavarName() + " [" + GetMetavarName() + " ...]";
    case Count::Optional:
      return "[" + GetMetavarName() + "]";
    case Count::ZeroOrMore:
      return "[" + GetMetavarName() + " [" + GetMetavarName() + " ...]]";
    case Count::Const:
    {
      std::ostringstream ss;
      for (size_t i = 0; i < countcount_; ++i)
      {
        if (i != 0)
        {
          ss << " ";
        }
        ss << GetMetavarName();
      }
      return ss.str();
    }
    default:
      assert(false && "missing case");
      throw "invalid count type in " __FUNCTION__;
    }
  }

  const std::string Help::GetMetavarName() const
  {
    if (metavar_.empty() == false)
    {
      return metavar_;
    }
    else
    {
      if (IsOptional(name_))
      {
        return ToUpper(name_.substr(1));
      }
      else
      {
        return name_;
      }
    }
  }

  const std::string Help::GetHelpCommand() const
  {
    if (IsOptional(name_))
    {
      const auto meta = GetMetavarReprestentation();
      if (meta.empty()) {
        return name_;
      }
      else {
        return name_ + " " + meta;
      }
    }
    else
    {
      return GetMetavarName();
    }
  }

  const std::string& Help::help() const
  {
    return help_;
  }

  struct CallHelp : public Argument
  {
    CallHelp(Parser* on)
      : Argument( Count(Count::Optional) )
      , parser(on)
    {
    }

    void OnArgument(Running& r, const std::string& argname) override
    {
      parser->WriteHelp(r);
      r.quit = true;
    }

    Parser* parser;
  };

  class ParserChild : public Parser {
  public:
    ParserChild(const std::string& desc, const Parser* parent) : Parser(desc), parent_(parent) {}

    void WriteUsage(Running& r) const override {
      parent_->WriteUsage(r);
      Parser::WriteUsage(r);
    }

  private:
    const Parser* parent_;
  };

  Parser::Parser(const std::string& d, const std::string aappname)
    : positionalIndex_(0)
    , description_(d)
    , appname_(aappname)
    , sub_parsers_("subparser")
  {
    std::shared_ptr<Argument> arg(new CallHelp(this));
    AddArgument("-h", arg, Extra().count(Count(Count::None)).help("Show this help message and exit."));
  }

  Parser::ParseStatus Parser::ParseArgs(int argc, char* argv[], std::ostream& out, std::ostream& error) const
  {
    Arguments args(argc, argv);
    return ParseArgs(args, out, error);
  }

  void Parser::AddSubParser(const std::string& name, SubParser* parser) {
    sub_parsers_(name, parser);
  }

  Parser::ParseStatus Parser::ParseArgs(const Arguments& arguments, std::ostream& out, std::ostream& error) const {
    Arguments args = arguments;
    Running running(arguments.name(), out, error);

    try {
      return DoParseArgs(args, running);
    }
    catch (ParserError& p) {
      OnParserError(running, this, p);
      running.e << p.what() << "\n";
      running.o << "\n";
      return Parser::ParseStatus::ParseFailed;
    }
  }

  Parser::ParseStatus Parser::DoParseArgs(Arguments& args, Running& running) const {
    try
    {
      while (false == args.is_empty())
      {
        bool isParsed = false;
        const bool isParsingPositionals = positionalIndex_ < positionals_.size();

        if (IsOptional(args[0]))
        {
          // optional
          const std::string arg = args[0];
          Optionals::const_iterator r = optionals_.find(arg);
          if (r == optionals_.end())
          {
            if (isParsingPositionals == false) {
              throw ParserError("Unknown optional argument: " + arg); // todo: implement partial matching of arguments?
            }
          }
          else {
            if (false == r->second->has_been_parsed()) {
              isParsed = true;
              args.ConsumeOne(); // the optional command = arg[0}
              r->second->ConsumeArguments(running, args, arg);
              r->second->set_has_been_parsed(true);
              if (running.quit) return ParseStatus::ParseQuit;
            }
          }
        }

        bool consumed = false;

        if (isParsed == false) {
          if (positionalIndex_ >= positionals_.size())
          {
            if (sub_parsers_.empty()) {
              throw ParserError("All positional arguments have been consumed: " + args[0]);
            }
            else {
              std::string subname;
              SubParser* sub = sub_parsers_.Convert(args[0], &subname);
              sub_parser_used_ = subname;
              ParserChild parser(args[0], this);
              sub->AddParser(parser);
              consumed = true;
              args.ConsumeOne("SUBCOMMAND");
              try {
                auto parsestatus = parser.DoParseArgs(args, running);
                if (parsestatus == ParseComplete) {
                  sub->ParseCompleted();
                }
                return parsestatus;
              }
              catch (ParserError& p) {
                OnParserError(running, &parser, p);
                running.e << "error: Failed to parse " << subname << ":\n";
                throw;
              }
            }
          }
          if (consumed == false) {
            ArgumentPtr p = positionals_[positionalIndex_];
            ++positionalIndex_;
            p->ConsumeArguments(running, args, "POSITIONAL"); // todo: give better name or something
          }
        }
      }

      if (positionalIndex_ != positionals_.size())
      {
        throw ParserError("too few arguments."); // todo: list a few missing arguments...
      }

      return ParseComplete;
    }
    catch (ParserError& p)
    {
      OnParserError(running, this, p);
      throw;
    }
  }

  void Parser::WriteHelp(Running& r) const
  {
    r.o << "Usage:";
    r.o << " " << (appname_.empty() ? r.app : appname_);
    WriteUsage(r);
    r.o << std::endl << description_ << std::endl << std::endl;

    const std::string sep = "\t";
    const std::string ins = "  ";

    if (helpPositional_.empty() == false)
    {
      r.o << "Positional arguments:" << std::endl;
      for (const Help& positional : helpPositional_)
      {
        r.o << ins << positional.GetHelpCommand();
        const auto h = positional.help();
        if (h.empty() == false) {
          r.o << sep << h;
        }
        r.o << std::endl;
      }

      r.o << std::endl;
    }

    if (helpOptional_.empty() == false)
    {
      r.o << "Optional arguments:" << std::endl;
      std::set<std::string> opts;
      for (const Help& optional : helpOptional_)
      {
        std::ostringstream ss;
        ss << ins << optional.GetHelpCommand();
        const auto h = optional.help();
        if( h.empty() == false) {
          ss << sep << h;
        }
        opts.insert(ss.str());
      }
      for (const auto& s : opts) {
        r.o << s << "\n";
      }
    }

    r.o << std::endl;
  }

  void Parser::WriteUsage(Running& r) const
  {
    for (const Help& optional : helpOptional_)
    {
      r.o << " " << optional.GetUsage();
    }

    if (false == sub_parsers_.empty()) {
      // if the sub-parser is set, use it instead of the array
      if (false == sub_parser_used_.empty()) {
        r.o << " " << sub_parser_used_;
      }
      else {
        const auto sp = sub_parsers_.names();
        r.o << " {";
        bool first = true;
        for (const auto& p : sp) {
          if (first == false) {
            r.o << "|";
          }
          first = false;
          r.o << p;
        }
        r.o << "}";
      }
    }

    for (const Help& positional : helpPositional_)
    {
      r.o << " " << positional.GetUsage();
    }
  }

  Parser& Parser::AddArgument(const std::string& commands, ArgumentPtr arg, const Extra& extra)
  {
    if( extra.has_several() ) {
      arg->set_has_several();
    }
    const auto names = Tokenize(commands, ",", true);
    std::string thename = "";
    int optionalcount = 0;
    int positionalcount = 0;
    for(const auto name: names) {
      if (IsOptional(name))
      {
        optionals_.insert(Optionals::value_type(name, arg));
        if (thename.empty()) thename = name.substr(1);
        ++optionalcount;
      }
      else
      {
        positionals_.push_back(arg);
        if( thename.empty() ) thename = name;
        ++positionalcount;
      }
    }
    Extra e = extra;
    if (e.metavar().empty()) {
      e.metavar(thename);
    }
    if (positionalcount > 0 && optionalcount > 0) {
      assert(false && "Optional and positional in argumentlist is not supported.");
    }
    if( positionalcount > 0 ) {
      helpPositional_.push_back(Help(commands, e));
    }
    else {
      assert(optionalcount > 0);
      helpOptional_.push_back(Help(commands, e));
    }
    return *this;
  }
}
<|endoftext|>


<commit_before>/* cclive
 * Copyright (C) 2010-2011  Toni Gundogdu <legatvs@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <ccinternal>

#include <fstream>
#include <cstring>

#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>

#ifndef foreach
#define foreach BOOST_FOREACH
#endif

#include <ccre>
#include <ccoptions>

namespace cc
{

namespace opts = boost::program_options;
namespace fs   = boost::filesystem;

void options::exec(int argc, char **argv)
{
  // Path to ccliverc.

#if BOOST_FILESYSTEM_VERSION > 2
  fs::path config_path(fs::current_path());
#else
  fs::path config_path(fs::current_path<fs::path>());
#endif

  const char *home = getenv("HOME");

  if (home && strlen(home) > 0)
    config_path = fs::system_complete(fs::path(home));

  config_path /=
#ifndef _WIN32
    std::string(".") +
#endif
    std::string("ccliverc");

  // Construct options.

  opts::options_description generic;

  generic.add_options()
  ("version",
   "Print version and exit")
  ("help",
   "Print help and exit")
  ("license",
   "Print license and exit")
  ("support",
   "Print supported websites and exit")
  ("verbose-libcurl",
   "Turn on libcurl verbose output")
  ("quiet,q",
   "Turn off all output, excl. errors")
#ifdef HAVE_FORK
  ("background,b",
   "Go to background")
#endif
  ("query-formats,F",
   "Query available formats to URL")
  ("format,f",
   opts::value<std::string>()->default_value("default"),
   "Download media format")
  ("continue,c",
   "Resume partially downloaded media")
  ("overwrite,W",
   "Overwrite existing media")
  ("output-file,O",
   opts::value<std::string>(),
   "Write media to arg")
  ("no-download,n",
   "Do not download media, print details")
  ("no-resolve,r",
   "Do not resolve URL redirections")
  ("no-proxy",
   "Do not use HTTP proxy")
  ("log-file",
   opts::value<std::string>()->default_value("cclive_log"),
   "Write log output to arg")
  ("progressbar",
   opts::value<std::string>()->default_value("normal"),
   "Use progressbar type arg")
  ("update-interval",
   opts::value<double>()->default_value(1.0),
   "Update interval of progressbar")
  ("config-file",
   opts::value<std::string>(&_config_file)
   ->default_value(config_path.string()),
   "Read args from arg")
  ;

  // Config.

  opts::options_description config("Configuration");

  config.add_options()
  ("filename-format",
   opts::value<std::string>()->default_value("%t.%s"),
   "Downloaded media filename format")
  ("output-dir",
   opts::value<std::string>(),
   "Write downloaded media to arg directory")
  ("regexp",
   opts::value<std::string>()->default_value("/(\\w|\\pL|\\s)/g"),
   "Regexp to cleanup media title")
  ("subst", opts::value<std::string>(),
   "Replace matched occurences in filename")
  ("exec", opts::value<std::string>(),
   "Invoke arg after each finished download")
  ("agent",
   opts::value<std::string>()->default_value("Mozilla/5.0"),
   "Identify as arg to HTTP servers")
  ("proxy", opts::value<std::string>(),
   "Use proxy for HTTP connections")
  ("throttle", opts::value<int>()->default_value(0),
   "Do not exceed transfer rate arg KB/s")
  ("connect-timeout", opts::value<int>()->default_value(30),
   "Seconds connecting allowed to take")
  ("transfer-timeout", opts::value<int>()->default_value(0),
   "Seconds transfer allowed to take")
  ("dns-cache-timeout", opts::value<int>()->default_value(60),
   "Seconds DNS resolves kept in memory")
  ("max-retries", opts::value<int>()->default_value(5),
   "Max download attempts before giving up")
  ("retry-wait", opts::value<int>()->default_value(5),
   "Time to wait before retrying")
  ;

  // Hidden.

  opts::options_description hidden;

  hidden.add_options()
  ("url", opts::value< std::vector<std::string> >(), "url");

  // Visible.

  _visible.add(generic).add(config);

  // Command line options.

  opts::options_description cmdline_options;

  cmdline_options.add(generic).add(config).add(hidden);

  // Config file options.

  opts::options_description config_file_options;

  config_file_options.add(config);

  // Positional.

  opts::positional_options_description p;
  p.add("url", -1);

  // Parse.

  store(opts::command_line_parser(argc,argv)
        .options(cmdline_options).positional(p).run(), _map);

  notify(_map);

  // Read config.

  std::ifstream ifs(_config_file.c_str());

  if (ifs)
    {
      store(parse_config_file(ifs, config_file_options), _map);
      notify(_map);
    }

  _verify();
}

const opts::variables_map& options::map() const
{
  return _map;
}

std::ostream& operator<<(std::ostream& os, const options& o)
{
  return os << o._visible;
}

void options::_verify()
{
  std::string empty;

  if (_map.count("regexp"))
    {
      std::string s = _map["regexp"].as<std::string>();

      if (!cc::re::match(s, empty))
        {
          std::stringstream b;

          b << "--regexp: expects "
            << "`/pattern/flags', for example: \"/(\\w|\\s)/g\"";

          throw std::runtime_error(b.str());
        }
    }

  if (_map.count("subst"))
    {
      std::istringstream iss( _map["subst"].as<std::string>());
      std::vector<std::string> v;

      std::copy(
        std::istream_iterator<std::string >(iss),
        std::istream_iterator<std::string >(),
        std::back_inserter<std::vector<std::string> >(v)
      );

      foreach (std::string s, v)
      {
        if (!cc::re::subst(s,empty))
          {
            std::stringstream b;

            b << "--subst: expects " << "`s{old}{new}flags'";

            throw std::runtime_error(b.str());
          }
      }
    }
}

} // namespace cc

// vim: set ts=2 sw=2 tw=72 expandtab:
<commit_msg>Make --progressbar, --update-interval configurable<commit_after>/* cclive
 * Copyright (C) 2010-2011  Toni Gundogdu <legatvs@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <ccinternal>

#include <fstream>
#include <cstring>

#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>

#ifndef foreach
#define foreach BOOST_FOREACH
#endif

#include <ccre>
#include <ccoptions>

namespace cc
{

namespace opts = boost::program_options;
namespace fs   = boost::filesystem;

void options::exec(int argc, char **argv)
{
  // Path to ccliverc.

#if BOOST_FILESYSTEM_VERSION > 2
  fs::path config_path(fs::current_path());
#else
  fs::path config_path(fs::current_path<fs::path>());
#endif

  const char *home = getenv("HOME");

  if (home && strlen(home) > 0)
    config_path = fs::system_complete(fs::path(home));

  config_path /=
#ifndef _WIN32
    std::string(".") +
#endif
    std::string("ccliverc");

  // Construct options.

  opts::options_description generic;

  generic.add_options()
  ("version",
   "Print version and exit")
  ("help",
   "Print help and exit")
  ("license",
   "Print license and exit")
  ("support",
   "Print supported websites and exit")
  ("verbose-libcurl",
   "Turn on libcurl verbose output")
  ("quiet,q",
   "Turn off all output, excl. errors")
#ifdef HAVE_FORK
  ("background,b",
   "Go to background")
#endif
  ("query-formats,F",
   "Query available formats to URL")
  ("format,f",
   opts::value<std::string>()->default_value("default"),
   "Download media format")
  ("continue,c",
   "Resume partially downloaded media")
  ("overwrite,W",
   "Overwrite existing media")
  ("output-file,O",
   opts::value<std::string>(),
   "Write media to arg")
  ("no-download,n",
   "Do not download media, print details")
  ("no-resolve,r",
   "Do not resolve URL redirections")
  ("no-proxy",
   "Do not use HTTP proxy")
  ("log-file",
   opts::value<std::string>()->default_value("cclive_log"),
   "Write log output to arg")
  ("config-file",
   opts::value<std::string>(&_config_file)
   ->default_value(config_path.string()),
   "Read args from arg")
  ;

  // Config.

  opts::options_description config("Configuration");

  config.add_options()
  ("progressbar",
   opts::value<std::string>()->default_value("normal"),
   "Use progressbar arg")
  ("update-interval",
   opts::value<double>()->default_value(1.0),
   "Update interval of progressbar")
  ("filename-format",
   opts::value<std::string>()->default_value("%t.%s"),
   "Downloaded media filename format")
  ("output-dir",
   opts::value<std::string>(),
   "Write downloaded media to arg directory")
  ("regexp",
   opts::value<std::string>()->default_value("/(\\w|\\pL|\\s)/g"),
   "Regexp to cleanup media title")
  ("subst", opts::value<std::string>(),
   "Replace matched occurences in filename")
  ("exec", opts::value<std::string>(),
   "Invoke arg after each finished download")
  ("agent",
   opts::value<std::string>()->default_value("Mozilla/5.0"),
   "Identify as arg to HTTP servers")
  ("proxy", opts::value<std::string>(),
   "Use proxy for HTTP connections")
  ("throttle", opts::value<int>()->default_value(0),
   "Do not exceed transfer rate arg KB/s")
  ("connect-timeout", opts::value<int>()->default_value(30),
   "Seconds connecting allowed to take")
  ("transfer-timeout", opts::value<int>()->default_value(0),
   "Seconds transfer allowed to take")
  ("dns-cache-timeout", opts::value<int>()->default_value(60),
   "Seconds DNS resolves kept in memory")
  ("max-retries", opts::value<int>()->default_value(5),
   "Max download attempts before giving up")
  ("retry-wait", opts::value<int>()->default_value(5),
   "Time to wait before retrying")
  ;

  // Hidden.

  opts::options_description hidden;

  hidden.add_options()
  ("url", opts::value< std::vector<std::string> >(), "url");

  // Visible.

  _visible.add(generic).add(config);

  // Command line options.

  opts::options_description cmdline_options;

  cmdline_options.add(generic).add(config).add(hidden);

  // Config file options.

  opts::options_description config_file_options;

  config_file_options.add(config);

  // Positional.

  opts::positional_options_description p;
  p.add("url", -1);

  // Parse.

  store(opts::command_line_parser(argc,argv)
        .options(cmdline_options).positional(p).run(), _map);

  notify(_map);

  // Read config.

  std::ifstream ifs(_config_file.c_str());

  if (ifs)
    {
      store(parse_config_file(ifs, config_file_options), _map);
      notify(_map);
    }

  _verify();
}

const opts::variables_map& options::map() const
{
  return _map;
}

std::ostream& operator<<(std::ostream& os, const options& o)
{
  return os << o._visible;
}

void options::_verify()
{
  std::string empty;

  if (_map.count("regexp"))
    {
      std::string s = _map["regexp"].as<std::string>();

      if (!cc::re::match(s, empty))
        {
          std::stringstream b;

          b << "--regexp: expects "
            << "`/pattern/flags', for example: \"/(\\w|\\s)/g\"";

          throw std::runtime_error(b.str());
        }
    }

  if (_map.count("subst"))
    {
      std::istringstream iss( _map["subst"].as<std::string>());
      std::vector<std::string> v;

      std::copy(
        std::istream_iterator<std::string >(iss),
        std::istream_iterator<std::string >(),
        std::back_inserter<std::vector<std::string> >(v)
      );

      foreach (std::string s, v)
      {
        if (!cc::re::subst(s,empty))
          {
            std::stringstream b;

            b << "--subst: expects " << "`s{old}{new}flags'";

            throw std::runtime_error(b.str());
          }
      }
    }
}

} // namespace cc

// vim: set ts=2 sw=2 tw=72 expandtab:
<|endoftext|>


<commit_before>//
// Copyright 2013 Jeff Bush
//
// 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 "assert.h"
#include "Barrier.h"
#include "Debug.h"
#include "Matrix.h"
#include "ParameterInterpolator.h"
#include "PixelShader.h"
#include "Rasterizer.h"
#include "RenderTarget.h"
#include "TextureSampler.h"
#include "utils.h"
#include "VertexShader.h"
#include "torus.h"
#include "cube.h"

class TextureVertexShader : public VertexShader
{
public:
	TextureVertexShader()
		:	VertexShader(5, 6)
	{
		const float kAspectRatio = 640.0f / 480.0f;
		const float kProjCoeff[] = {
			1.0f / kAspectRatio, 0.0f, 0.0f, 0.0f,
			0.0f, kAspectRatio, 0.0f, 0.0f,
			0.0f, 0.0f, 1.0f, 0.0f,
			0.0f, 0.0f, 1.0f, 0.0f,
		};

		fProjectionMatrix = Matrix(kProjCoeff);
		fMVPMatrix = fProjectionMatrix;
	}

	void applyTransform(const Matrix &mat)
	{
		fModelViewMatrix = fModelViewMatrix * mat;
		fMVPMatrix = fProjectionMatrix * fModelViewMatrix;
	}

	void shadeVertices(vecf16 outParams[kMaxVertexAttribs],
		const vecf16 inAttribs[kMaxVertexAttribs], int mask)
	{
		// Multiply by mvp matrix
		vecf16 coord[4];
		for (int i = 0; i < 3; i++)
			coord[i] = inAttribs[i];

		coord[3] = splatf(1.0f);
		fMVPMatrix.mulVec(outParams, coord);

		// Copy remaining parameters
		outParams[4] = inAttribs[3];
		outParams[5] = inAttribs[4];
	}

private:
	Matrix fMVPMatrix;
	Matrix fProjectionMatrix;
	Matrix fModelViewMatrix;
};

class TexturePixelShader : public PixelShader
{
public:
	TexturePixelShader(ParameterInterpolator *interp, RenderTarget *target)
		:	PixelShader(interp, target)
	{}

	void bindTexture(Surface *surface)
	{
		fSampler.bind(surface);
		fSampler.setEnableBilinearFiltering(true);
	}

	virtual void shadePixels(const vecf16 inParams[16], vecf16 outColor[4],
		unsigned short mask)
	{
		fSampler.readPixels(inParams[0], inParams[1], mask, outColor);
	}

private:
	TextureSampler fSampler;
};

class LightingVertexShader : public VertexShader
{
public:
	LightingVertexShader()
		:	VertexShader(6, 8)
	{
		const float kAspectRatio = 640.0f / 480.0f;
		const float kProjCoeff[] = {
			1.0f / kAspectRatio, 0.0f, 0.0f, 0.0f,
			0.0f, kAspectRatio, 0.0f, 0.0f,
			0.0f, 0.0f, 1.0f, 0.0f,
			0.0f, 0.0f, 1.0f, 0.0f,
		};

		fProjectionMatrix = Matrix(kProjCoeff);
		fMVPMatrix = fProjectionMatrix;
		fNormalMatrix = fMVPMatrix;	// XXX Actually should be transpose of inverse
	}

	void applyTransform(const Matrix &mat)
	{
		fModelViewMatrix = fModelViewMatrix * mat;
		fMVPMatrix = fProjectionMatrix * fModelViewMatrix;
		fNormalMatrix = fMVPMatrix;	// XXX Actually should be transpose of inverse
	}

	void shadeVertices(vecf16 outParams[kMaxVertexAttribs],
		const vecf16 inAttribs[kMaxVertexAttribs], int mask)
	{
		// Multiply by mvp matrix
		vecf16 coord[4];
		for (int i = 0; i < 3; i++)
			coord[i] = inAttribs[i];

		coord[3] = splatf(1.0f);
		fMVPMatrix.mulVec(outParams, coord);

		for (int i = 0; i < 3; i++)
			coord[i] = inAttribs[i + 3];

		coord[3] = splatf(1.0f);

		fNormalMatrix.mulVec(outParams + 4, coord);
	}

private:
	Matrix fMVPMatrix;
	Matrix fProjectionMatrix;
	Matrix fModelViewMatrix;
	Matrix fNormalMatrix;
};

class LightingPixelShader : public PixelShader
{
public:
	LightingPixelShader(ParameterInterpolator *interp, RenderTarget *target)
		:	PixelShader(interp, target)
	{
		fLightVector[0] = 0.0f;
		fLightVector[1] = 1.0f;
		fLightVector[2] = 0.0f;

		fDirectional = 0.4f;
		fAmbient = 0.5f;
	}

	virtual void shadePixels(const vecf16 inParams[16], vecf16 outColor[4],
		unsigned short mask)
	{
		// Dot product
		vecf16 dot = inParams[0] * splatf(fLightVector[0])
			+ inParams[1] * splatf(fLightVector[1])
			+ inParams[2] * splatf(fLightVector[2]);
		dot *= splatf(fDirectional);
		outColor[0] = outColor[1] = outColor[2] = clampvf(dot + splatf(fAmbient));
	}

private:
	float fLightVector[3];
	float fAmbient;
	float fDirectional;
};

const char kCheckerboard[] = {
	0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00,
	0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff,
	0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00,
	0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff
};

const int kFbWidth = 640;
const int kFbHeight = 480;
const int kTilesPerRow = kFbWidth / kTileSize;
const int kMaxTileIndex = kTilesPerRow * ((kFbHeight / 64) + 1);
Barrier<4> gGeometryBarrier;
Barrier<4> gPixelBarrier;
volatile int gNextTileIndex = 0;
float *gVertexParams;
Surface gZBuffer(0, kFbWidth, kFbHeight);
Surface gColorBuffer(0x100000, kFbWidth, kFbHeight);
#if 1
	Surface texture((unsigned int) kCheckerboard, 4, 4);
#else
	extern char *kImage;
	Surface texture((unsigned int) kImage, 128, 128);
#endif
Debug Debug::debug;

Matrix translate(float x, float y, float z)
{
	float kValues[] = {
		1.0f, 0.0f, 0.0f, x,
		0.0f, 1.0f, 0.0f, y,
		0.0f, 0.0f, 1.0f, z,
		0.0f, 0.0f, 0.0f, 1.0f,
	};

	return Matrix(kValues);
}

Matrix rotateXYZ(float x, float y, float z)
{
	float sinX = sin(x);
	float cosX = cos(x);
	float sinY = sin(y);
	float cosY = cos(y);
	float sinZ = sin(z);
	float cosZ = cos(z);

	float kMat1[] = {
		cosY * cosZ, cosZ * sinX * sinY - cosX * sinZ, cosX * cosZ * sinY + sinX * sinZ, 0,
		cosY * sinZ, cosX * cosZ + sinX * sinY * sinZ, -cosZ * sinX + cosX * sinY * sinZ, 0,
		-sinY, cosY * sinX, cosX * cosY, 0,
		0, 0, 0, 1
	};

	return Matrix(kMat1);
}

//
// All threads start execution here
//
int main()
{
	Rasterizer rasterizer;
	RenderTarget renderTarget;
	renderTarget.setColorBuffer(&gColorBuffer);
	renderTarget.setZBuffer(&gZBuffer);
	ParameterInterpolator interp(kFbWidth, kFbHeight);
#if 0
	TextureVertexShader vertexShader;
	TexturePixelShader pixelShader(&interp, &renderTarget);
	pixelShader.bindTexture(&texture);
	const float *vertices = kCubeVertices;
	int numVertices = kNumCubeVertices;
	const int *indices = kCubeIndices;
	int numIndices = kNumCubeIndices;
#else
	LightingVertexShader vertexShader;
	LightingPixelShader pixelShader(&interp, &renderTarget);
	const float *vertices = kTorusVertices;
	int numVertices = kNumTorusVertices;
	const int *indices = kTorusIndices;
	int numIndices = kNumTorusIndices;
#endif

	vertexShader.applyTransform(translate(0.0f, 0.0f, 1.5f));
	Matrix rotateStepMatrix = rotateXYZ(M_PI / 4.0f, M_PI / 5.0f, M_PI / 6.5f);

//	pixelShader.enableZBuffer(true);
//	pixelShader.enableBlend(true);

	int numVertexParams = vertexShader.getNumParams();

	for (int frame = 0; frame < 1; frame++)
	{
		//
		// Geometry phase
		//
		if (__builtin_vp_get_current_strand() == 0)
		{
			if (gVertexParams == 0)
				gVertexParams = (float*) allocMem(768 * sizeof(float));

			vertexShader.applyTransform(rotateStepMatrix);
			vertexShader.processVertexBuffer(gVertexParams, vertices, numVertices);
			gNextTileIndex = 0;
		}

		gGeometryBarrier.wait();

		//
		// Pixel phase
		//
		while (gNextTileIndex < kMaxTileIndex)
		{
			// Grab the next available tile to begin working on.
			int myTileIndex = __sync_fetch_and_add(&gNextTileIndex, 1);
			if (myTileIndex >= kMaxTileIndex)
				break;

			int tileX = (myTileIndex % kTilesPerRow) * kTileSize;
			int tileY = (myTileIndex / kTilesPerRow) * kTileSize;

			renderTarget.getColorBuffer()->clearTile(tileX, tileY, 0);
			if (pixelShader.isZBufferEnabled())
				renderTarget.getZBuffer()->clearTile(tileX, tileY, 0x7f800000);	// Infinity

			// Cycle through all triangles and attempt to render into this
			// 64x64 tile.
			for (int vidx = 0; vidx < numIndices; vidx += 3)
			{
				int offset0 = indices[vidx] * numVertexParams;
				int offset1 = indices[vidx + 1] * numVertexParams;
				int offset2 = indices[vidx + 2] * numVertexParams;

				float x0 = gVertexParams[offset0 + kParamX];
				float y0 = gVertexParams[offset0 + kParamY];
				float z0 = gVertexParams[offset0 + kParamZ];
				float x1 = gVertexParams[offset1 + kParamX];
				float y1 = gVertexParams[offset1 + kParamY];
				float z1 = gVertexParams[offset1 + kParamZ];
				float x2 = gVertexParams[offset2 + kParamX];
				float y2 = gVertexParams[offset2 + kParamY];
				float z2 = gVertexParams[offset2 + kParamZ];

				// Backface cull triangles that are facing away from camera.
				if ((x1 - x0) * (y2 - y0) - (y1 - y0) * (x2 - x0) < 0)
					continue;

				// Convert screen space coordinates to raster coordinates
				int x0Rast = x0 * kFbWidth / 2 + kFbWidth / 2;
				int y0Rast = y0 * kFbHeight / 2 + kFbHeight / 2;
				int x1Rast = x1 * kFbWidth / 2 + kFbWidth / 2;
				int y1Rast = y1 * kFbHeight / 2 + kFbHeight / 2;
				int x2Rast = x2 * kFbWidth / 2 + kFbWidth / 2;
				int y2Rast = y2 * kFbHeight / 2 + kFbHeight / 2;

				// Bounding box check.  If triangles are not within this tile,
				// skip them.
				int xMax = tileX + kTileSize;
				int yMax = tileY + kTileSize;
				if ((x0Rast < tileX && x1Rast < tileX && x2Rast < tileX)
					|| (y0Rast < tileY && y1Rast < tileY && y2Rast < tileY)
					|| (x0Rast > xMax && x1Rast > xMax && x2Rast > xMax)
					|| (y0Rast > yMax && y1Rast > yMax && y2Rast > yMax))
					continue;

				// Okay, it's visible.  Set up parameters and rasterize it.
				interp.setUpTriangle(x0, y0, z0, x1, y1, z1, x2, y2, z2);
				for (int paramI = 0; paramI < numVertexParams; paramI++)
				{
					interp.setUpParam(paramI,
						gVertexParams[offset0 + paramI + 4],
						gVertexParams[offset1 + paramI + 4],
						gVertexParams[offset2 + paramI + 4]);
				}

				rasterizer.rasterizeTriangle(&pixelShader, tileX, tileY,
					x0Rast, y0Rast, x1Rast, y1Rast, x2Rast, y2Rast);
			}

			renderTarget.getColorBuffer()->flushTile(tileX, tileY);
		}

		gPixelBarrier.wait();
	}

	return 0;
}
<commit_msg>Work around a hardware issue around comparisions with infinity. Initialize z buffer to a large number instead of infinity.<commit_after>//
// Copyright 2013 Jeff Bush
//
// 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 "assert.h"
#include "Barrier.h"
#include "Debug.h"
#include "Matrix.h"
#include "ParameterInterpolator.h"
#include "PixelShader.h"
#include "Rasterizer.h"
#include "RenderTarget.h"
#include "TextureSampler.h"
#include "utils.h"
#include "VertexShader.h"
#include "torus.h"
#include "cube.h"

class TextureVertexShader : public VertexShader
{
public:
	TextureVertexShader()
		:	VertexShader(5, 6)
	{
		const float kAspectRatio = 640.0f / 480.0f;
		const float kProjCoeff[] = {
			1.0f / kAspectRatio, 0.0f, 0.0f, 0.0f,
			0.0f, kAspectRatio, 0.0f, 0.0f,
			0.0f, 0.0f, 1.0f, 0.0f,
			0.0f, 0.0f, 1.0f, 0.0f,
		};

		fProjectionMatrix = Matrix(kProjCoeff);
		fMVPMatrix = fProjectionMatrix;
	}

	void applyTransform(const Matrix &mat)
	{
		fModelViewMatrix = fModelViewMatrix * mat;
		fMVPMatrix = fProjectionMatrix * fModelViewMatrix;
	}

	void shadeVertices(vecf16 outParams[kMaxVertexAttribs],
		const vecf16 inAttribs[kMaxVertexAttribs], int mask)
	{
		// Multiply by mvp matrix
		vecf16 coord[4];
		for (int i = 0; i < 3; i++)
			coord[i] = inAttribs[i];

		coord[3] = splatf(1.0f);
		fMVPMatrix.mulVec(outParams, coord);

		// Copy remaining parameters
		outParams[4] = inAttribs[3];
		outParams[5] = inAttribs[4];
	}

private:
	Matrix fMVPMatrix;
	Matrix fProjectionMatrix;
	Matrix fModelViewMatrix;
};

class TexturePixelShader : public PixelShader
{
public:
	TexturePixelShader(ParameterInterpolator *interp, RenderTarget *target)
		:	PixelShader(interp, target)
	{}

	void bindTexture(Surface *surface)
	{
		fSampler.bind(surface);
		fSampler.setEnableBilinearFiltering(true);
	}

	virtual void shadePixels(const vecf16 inParams[16], vecf16 outColor[4],
		unsigned short mask)
	{
		fSampler.readPixels(inParams[0], inParams[1], mask, outColor);
	}

private:
	TextureSampler fSampler;
};

class LightingVertexShader : public VertexShader
{
public:
	LightingVertexShader()
		:	VertexShader(6, 8)
	{
		const float kAspectRatio = 640.0f / 480.0f;
		const float kProjCoeff[] = {
			1.0f / kAspectRatio, 0.0f, 0.0f, 0.0f,
			0.0f, kAspectRatio, 0.0f, 0.0f,
			0.0f, 0.0f, 1.0f, 0.0f,
			0.0f, 0.0f, 1.0f, 0.0f,
		};

		fProjectionMatrix = Matrix(kProjCoeff);
		fMVPMatrix = fProjectionMatrix;
		fNormalMatrix = fMVPMatrix;	// XXX Actually should be transpose of inverse
	}

	void applyTransform(const Matrix &mat)
	{
		fModelViewMatrix = fModelViewMatrix * mat;
		fMVPMatrix = fProjectionMatrix * fModelViewMatrix;
		fNormalMatrix = fMVPMatrix;	// XXX Actually should be transpose of inverse
	}

	void shadeVertices(vecf16 outParams[kMaxVertexAttribs],
		const vecf16 inAttribs[kMaxVertexAttribs], int mask)
	{
		// Multiply by mvp matrix
		vecf16 coord[4];
		for (int i = 0; i < 3; i++)
			coord[i] = inAttribs[i];

		coord[3] = splatf(1.0f);
		fMVPMatrix.mulVec(outParams, coord);

		for (int i = 0; i < 3; i++)
			coord[i] = inAttribs[i + 3];

		coord[3] = splatf(1.0f);

		fNormalMatrix.mulVec(outParams + 4, coord);
	}

private:
	Matrix fMVPMatrix;
	Matrix fProjectionMatrix;
	Matrix fModelViewMatrix;
	Matrix fNormalMatrix;
};

class LightingPixelShader : public PixelShader
{
public:
	LightingPixelShader(ParameterInterpolator *interp, RenderTarget *target)
		:	PixelShader(interp, target)
	{
		fLightVector[0] = 0.0f;
		fLightVector[1] = 1.0f;
		fLightVector[2] = 0.0f;

		fDirectional = 0.4f;
		fAmbient = 0.5f;
	}

	virtual void shadePixels(const vecf16 inParams[16], vecf16 outColor[4],
		unsigned short mask)
	{
		// Dot product
		vecf16 dot = inParams[0] * splatf(fLightVector[0])
			+ inParams[1] * splatf(fLightVector[1])
			+ inParams[2] * splatf(fLightVector[2]);
		dot *= splatf(fDirectional);
		outColor[0] = outColor[1] = outColor[2] = clampvf(dot + splatf(fAmbient));
	}

private:
	float fLightVector[3];
	float fAmbient;
	float fDirectional;
};

const char kCheckerboard[] = {
	0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00,
	0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff,
	0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00,
	0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff
};

const int kFbWidth = 640;
const int kFbHeight = 480;
const int kTilesPerRow = kFbWidth / kTileSize;
const int kMaxTileIndex = kTilesPerRow * ((kFbHeight / 64) + 1);
Barrier<4> gGeometryBarrier;
Barrier<4> gPixelBarrier;
volatile int gNextTileIndex = 0;
float *gVertexParams;
Surface gZBuffer(0, kFbWidth, kFbHeight);
Surface gColorBuffer(0x100000, kFbWidth, kFbHeight);
#if 1
	Surface texture((unsigned int) kCheckerboard, 4, 4);
#else
	extern char *kImage;
	Surface texture((unsigned int) kImage, 128, 128);
#endif
Debug Debug::debug;

Matrix translate(float x, float y, float z)
{
	float kValues[] = {
		1.0f, 0.0f, 0.0f, x,
		0.0f, 1.0f, 0.0f, y,
		0.0f, 0.0f, 1.0f, z,
		0.0f, 0.0f, 0.0f, 1.0f,
	};

	return Matrix(kValues);
}

Matrix rotateXYZ(float x, float y, float z)
{
	float sinX = sin(x);
	float cosX = cos(x);
	float sinY = sin(y);
	float cosY = cos(y);
	float sinZ = sin(z);
	float cosZ = cos(z);

	float kMat1[] = {
		cosY * cosZ, cosZ * sinX * sinY - cosX * sinZ, cosX * cosZ * sinY + sinX * sinZ, 0,
		cosY * sinZ, cosX * cosZ + sinX * sinY * sinZ, -cosZ * sinX + cosX * sinY * sinZ, 0,
		-sinY, cosY * sinX, cosX * cosY, 0,
		0, 0, 0, 1
	};

	return Matrix(kMat1);
}

//
// All threads start execution here
//
int main()
{
	Rasterizer rasterizer;
	RenderTarget renderTarget;
	renderTarget.setColorBuffer(&gColorBuffer);
	renderTarget.setZBuffer(&gZBuffer);
	ParameterInterpolator interp(kFbWidth, kFbHeight);
#if 0
	TextureVertexShader vertexShader;
	TexturePixelShader pixelShader(&interp, &renderTarget);
	pixelShader.bindTexture(&texture);
	const float *vertices = kCubeVertices;
	int numVertices = kNumCubeVertices;
	const int *indices = kCubeIndices;
	int numIndices = kNumCubeIndices;
#else
	LightingVertexShader vertexShader;
	LightingPixelShader pixelShader(&interp, &renderTarget);
	const float *vertices = kTorusVertices;
	int numVertices = kNumTorusVertices;
	const int *indices = kTorusIndices;
	int numIndices = kNumTorusIndices;
#endif

	vertexShader.applyTransform(translate(0.0f, 0.0f, 1.5f));
	Matrix rotateStepMatrix = rotateXYZ(M_PI / 4.0f, M_PI / 5.0f, M_PI / 6.5f);

//	pixelShader.enableZBuffer(true);
//	pixelShader.enableBlend(true);

	int numVertexParams = vertexShader.getNumParams();

	for (int frame = 0; frame < 1; frame++)
	{
		//
		// Geometry phase
		//
		if (__builtin_vp_get_current_strand() == 0)
		{
			if (gVertexParams == 0)
				gVertexParams = (float*) allocMem(768 * sizeof(float));

			vertexShader.applyTransform(rotateStepMatrix);
			vertexShader.processVertexBuffer(gVertexParams, vertices, numVertices);
			gNextTileIndex = 0;
		}

		gGeometryBarrier.wait();

		//
		// Pixel phase
		//
		while (gNextTileIndex < kMaxTileIndex)
		{
			// Grab the next available tile to begin working on.
			int myTileIndex = __sync_fetch_and_add(&gNextTileIndex, 1);
			if (myTileIndex >= kMaxTileIndex)
				break;

			int tileX = (myTileIndex % kTilesPerRow) * kTileSize;
			int tileY = (myTileIndex / kTilesPerRow) * kTileSize;

			renderTarget.getColorBuffer()->clearTile(tileX, tileY, 0);
			if (pixelShader.isZBufferEnabled())
			{
				// XXX Ideally, we'd initialize to infinity, but comparisons
				// with infinity are broken in hardware.  For now, initialize
				// to a very large number
				renderTarget.getZBuffer()->clearTile(tileX, tileY, 0x7e000000);
			}

			// Cycle through all triangles and attempt to render into this
			// 64x64 tile.
			for (int vidx = 0; vidx < numIndices; vidx += 3)
			{
				int offset0 = indices[vidx] * numVertexParams;
				int offset1 = indices[vidx + 1] * numVertexParams;
				int offset2 = indices[vidx + 2] * numVertexParams;

				float x0 = gVertexParams[offset0 + kParamX];
				float y0 = gVertexParams[offset0 + kParamY];
				float z0 = gVertexParams[offset0 + kParamZ];
				float x1 = gVertexParams[offset1 + kParamX];
				float y1 = gVertexParams[offset1 + kParamY];
				float z1 = gVertexParams[offset1 + kParamZ];
				float x2 = gVertexParams[offset2 + kParamX];
				float y2 = gVertexParams[offset2 + kParamY];
				float z2 = gVertexParams[offset2 + kParamZ];

				// Backface cull triangles that are facing away from camera.
				if ((x1 - x0) * (y2 - y0) - (y1 - y0) * (x2 - x0) < 0)
					continue;

				// Convert screen space coordinates to raster coordinates
				int x0Rast = x0 * kFbWidth / 2 + kFbWidth / 2;
				int y0Rast = y0 * kFbHeight / 2 + kFbHeight / 2;
				int x1Rast = x1 * kFbWidth / 2 + kFbWidth / 2;
				int y1Rast = y1 * kFbHeight / 2 + kFbHeight / 2;
				int x2Rast = x2 * kFbWidth / 2 + kFbWidth / 2;
				int y2Rast = y2 * kFbHeight / 2 + kFbHeight / 2;

				// Bounding box check.  If triangles are not within this tile,
				// skip them.
				int xMax = tileX + kTileSize;
				int yMax = tileY + kTileSize;
				if ((x0Rast < tileX && x1Rast < tileX && x2Rast < tileX)
					|| (y0Rast < tileY && y1Rast < tileY && y2Rast < tileY)
					|| (x0Rast > xMax && x1Rast > xMax && x2Rast > xMax)
					|| (y0Rast > yMax && y1Rast > yMax && y2Rast > yMax))
					continue;

				// Okay, it's visible.  Set up parameters and rasterize it.
				interp.setUpTriangle(x0, y0, z0, x1, y1, z1, x2, y2, z2);
				for (int paramI = 0; paramI < numVertexParams; paramI++)
				{
					interp.setUpParam(paramI,
						gVertexParams[offset0 + paramI + 4],
						gVertexParams[offset1 + paramI + 4],
						gVertexParams[offset2 + paramI + 4]);
				}

				rasterizer.rasterizeTriangle(&pixelShader, tileX, tileY,
					x0Rast, y0Rast, x1Rast, y1Rast, x2Rast, y2Rast);
			}

			renderTarget.getColorBuffer()->flushTile(tileX, tileY);
		}

		gPixelBarrier.wait();
	}

	return 0;
}
<|endoftext|>


<commit_before>/**
 * This file is part of the "libfnord" project
 *   Copyright (c) 2015 Paul Asmuth
 *
 * FnordMetric is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License v3.0. You should have received a
 * copy of the GNU General Public License along with this program. If not, see
 * <http://www.gnu.org/licenses/>.
 */
#include <fnord-base/stringutil.h>
#include <fnord-base/exception.h>
#include <fnord-base/inspect.h>
#include <fnord-msg/MessageSchema.h>
#include <fnord-msg/CodingOptions.pb.h>
#include <fnord-msg/msg.h>

namespace fnord {
namespace msg {

RefPtr<MessageSchema> MessageSchema::fromProtobuf(
    const google::protobuf::Descriptor* dsc) {
  Vector<msg::MessageSchemaField> fields;

  auto nfields = dsc->field_count();
  for (size_t i = 0; i < nfields; ++i) {
    auto field = dsc->field(i);

    CodingOptions copts = field->options().GetExtension(coding);

    EncodingHint enc_hint = EncodingHint::NONE;
    if (copts.encoding() == "BITPACK") enc_hint = EncodingHint::BITPACK;
    if (copts.encoding() == "LEB128") enc_hint = EncodingHint::LEB128;

    switch (field->type()) {

      case google::protobuf::FieldDescriptor::TYPE_BOOL:
        fields.emplace_back(
            field->number(),
            field->name(),
            msg::FieldType::BOOLEAN,
            0,
            field->is_repeated(),
            field->is_optional(),
            enc_hint);
        break;

      case google::protobuf::FieldDescriptor::TYPE_STRING:
        fields.emplace_back(
            field->number(),
            field->name(),
            msg::FieldType::STRING,
            copts.has_maxval() ? copts.maxval() : 0xffffffff,
            field->is_repeated(),
            field->is_optional(),
            enc_hint);
        break;

      case google::protobuf::FieldDescriptor::TYPE_UINT64:
        fields.emplace_back(
            field->number(),
            field->name(),
            msg::FieldType::UINT64,
            copts.has_maxval() ?
                copts.maxval() : std::numeric_limits<uint64_t>::max(),
            field->is_repeated(),
            field->is_optional(),
            enc_hint);
        break;

      case google::protobuf::FieldDescriptor::TYPE_UINT32:
        fields.emplace_back(
            field->number(),
            field->name(),
            msg::FieldType::UINT32,
            copts.has_maxval() ?
                copts.maxval() : std::numeric_limits<uint32_t>::max(),
            field->is_repeated(),
            field->is_optional(),
            enc_hint);
        break;

      case google::protobuf::FieldDescriptor::TYPE_ENUM: {
        size_t maxval = 0;
        auto enum_dsc = field->enum_type();
        auto nvals = enum_dsc->value_count();
        for (int j = 0; j < nvals; ++j) {
          auto enum_val = enum_dsc->value(j);
          if (enum_val->number() > maxval) {
            maxval = enum_val->number();
          }
        }

        fields.emplace_back(
            field->number(),
            field->name(),
            msg::FieldType::UINT64,
            maxval,
            field->is_repeated(),
            field->is_optional(),
            maxval < 250 ? EncodingHint::BITPACK : EncodingHint::LEB128);
        break;
      }

      case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
        fields.emplace_back(
            MessageSchemaField::mkObjectField(
                field->number(),
                field->name(),
                field->is_repeated(),
                field->is_optional(),
                MessageSchema::fromProtobuf(field->message_type())));
        break;

      default:
        RAISEF(
            kNotImplementedError,
            "field type not implemented: $0",
            field->type_name());

    }
  }

  return new msg::MessageSchema(dsc->full_name(), fields);
}

static void schemaNodeToString(
    size_t level,
    const MessageSchemaField& field,
    String* str) {
  String ws(level * 2, ' ');
  String type_name;
  String attrs;

  String type_prefix = "";
  String type_suffix = "";

  if (field.optional) {
    type_prefix += "optional[";
    type_suffix += "]";
  }

  if (field.repeated) {
    type_prefix += "list[";
    type_suffix += "]";
  }

  switch (field.type) {

    case FieldType::OBJECT:
      str->append(StringUtil::format(
          "$0$1object$2 $3 = $4 {\n",
          ws,
          type_prefix,
          type_suffix,
          field.name,
          field.id));

      for (const auto& f : field.schema->fields()) {
        schemaNodeToString(level + 1, f, str);
      }

      str->append(ws + "}\n");
      return;

    case FieldType::BOOLEAN:
      type_name = "bool";
      break;

    case FieldType::UINT32:
      type_name = "uint32";
      attrs += StringUtil::format(" @maxval=$0", field.type_size);
      break;

    case FieldType::UINT64:
      type_name = "uint64";
      attrs += StringUtil::format(" @maxval=$0", field.type_size);
      break;

    case FieldType::STRING:
      type_name = "string";
      attrs += StringUtil::format(" @maxlen=$0", field.type_size);
      break;

  }

  switch (field.encoding) {

    case EncodingHint::NONE:
      break;

    case EncodingHint::BITPACK:
      attrs += " @encoding=BITPACK";
      break;

    case EncodingHint::LEB128:
      attrs += " @encoding=LEB128";
      break;

  }

  str->append(StringUtil::format(
      "$0$1$2$3 $4 = $5$6;\n",
      ws,
      type_prefix,
      type_name,
      type_suffix,
      field.name,
      field.id,
      attrs));
}

MessageSchemaField MessageSchemaField::mkObjectField(
    uint32_t id,
    String name,
    bool repeated,
    bool optional,
    RefPtr<msg::MessageSchema> schema) {
  MessageSchemaField field(
      id,
      name,
      FieldType::OBJECT,
      0,
      repeated,
      optional);

  field.schema = schema;
  return field;
}

MessageSchema::MessageSchema(
    const String& name,
    Vector<MessageSchemaField> fields) :
    name_(name),
    fields_(fields) {
  for (const auto& field : fields_) {
    field_ids_.emplace(field.name, field.id);
    field_types_.emplace(field.id, field.type);
    field_names_.emplace(field.id, field.name);
  }
}

MessageSchema::MessageSchema(const MessageSchema& other) :
    name_(other.name_),
    fields_(other.fields_),
    field_ids_(other.field_ids_),
    field_types_(other.field_types_),
    field_names_(other.field_names_) {}

const String& MessageSchema::name() const {
  return name_;
}

const Vector<MessageSchemaField>& MessageSchema::fields() const {
  return fields_;
}

String MessageSchema::toString() const {
  String str = StringUtil::format("object $0 {\n", name_);

  for (const auto& f : fields_) {
    schemaNodeToString(1, f, &str);
  }

  str += "}";
  return str;
}

uint32_t MessageSchema::fieldId(const String& path) const {
  auto id = field_ids_.find(path);
  if (id == field_ids_.end()) {
    RAISEF(kIndexError, "field not found: $0", path);
  } else {
    return id->second;
  }
}

FieldType MessageSchema::fieldType(uint32_t id) const {
  if (id == 0) {
    return FieldType::OBJECT;
  }

  auto type = field_types_.find(id);
  if (type == field_types_.end()) {
    RAISEF(kIndexError, "field not found: $0", id);
  } else {
    return type->second;
  }
}

const String& MessageSchema::fieldName(uint32_t id) const {
  if (id == 0) {
    return name_;
  }

  auto name = field_names_.find(id);
  if (name == field_names_.end()) {
    RAISEF(kIndexError, "field not found: $0", id);
  } else {
    return name->second;
  }
}

RefPtr<MessageSchema> MessageSchema::fieldSchema(uint32_t id) const {
  for (const auto& field : fields_) {
    if (field.id == id) {
      return field.schema;
    }
  }

  RAISEF(kIndexError, "field not found: $0", id);
}

Set<String> MessageSchema::columns() const {
  Set<String> columns;

  for (const auto& c : field_names_) {
    columns.emplace(c.second);
  }

  return columns;
}

RefPtr<MessageSchema> MessageSchemaRepository::getSchema(
    const String& name) const {
  auto iter = schemas_.find(name);
  if (iter == schemas_.end()) {
    RAISEF(kRuntimeError, "schema not found: '$0'", name);
  }

  return iter->second;
}

void MessageSchemaRepository::registerSchema(RefPtr<MessageSchema> schema) {
  schemas_.emplace(schema->name(), schema);
}

} // namespace msg
} // namespace fnord
<commit_msg>encode ENUMS as uint32<commit_after>/**
 * This file is part of the "libfnord" project
 *   Copyright (c) 2015 Paul Asmuth
 *
 * FnordMetric is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License v3.0. You should have received a
 * copy of the GNU General Public License along with this program. If not, see
 * <http://www.gnu.org/licenses/>.
 */
#include <fnord-base/stringutil.h>
#include <fnord-base/exception.h>
#include <fnord-base/inspect.h>
#include <fnord-msg/MessageSchema.h>
#include <fnord-msg/CodingOptions.pb.h>
#include <fnord-msg/msg.h>

namespace fnord {
namespace msg {

RefPtr<MessageSchema> MessageSchema::fromProtobuf(
    const google::protobuf::Descriptor* dsc) {
  Vector<msg::MessageSchemaField> fields;

  auto nfields = dsc->field_count();
  for (size_t i = 0; i < nfields; ++i) {
    auto field = dsc->field(i);

    CodingOptions copts = field->options().GetExtension(coding);

    EncodingHint enc_hint = EncodingHint::NONE;
    if (copts.encoding() == "BITPACK") enc_hint = EncodingHint::BITPACK;
    if (copts.encoding() == "LEB128") enc_hint = EncodingHint::LEB128;

    switch (field->type()) {

      case google::protobuf::FieldDescriptor::TYPE_BOOL:
        fields.emplace_back(
            field->number(),
            field->name(),
            msg::FieldType::BOOLEAN,
            0,
            field->is_repeated(),
            field->is_optional(),
            enc_hint);
        break;

      case google::protobuf::FieldDescriptor::TYPE_STRING:
        fields.emplace_back(
            field->number(),
            field->name(),
            msg::FieldType::STRING,
            copts.has_maxval() ? copts.maxval() : 0xffffffff,
            field->is_repeated(),
            field->is_optional(),
            enc_hint);
        break;

      case google::protobuf::FieldDescriptor::TYPE_UINT64:
        fields.emplace_back(
            field->number(),
            field->name(),
            msg::FieldType::UINT64,
            copts.has_maxval() ?
                copts.maxval() : std::numeric_limits<uint64_t>::max(),
            field->is_repeated(),
            field->is_optional(),
            enc_hint);
        break;

      case google::protobuf::FieldDescriptor::TYPE_UINT32:
        fields.emplace_back(
            field->number(),
            field->name(),
            msg::FieldType::UINT32,
            copts.has_maxval() ?
                copts.maxval() : std::numeric_limits<uint32_t>::max(),
            field->is_repeated(),
            field->is_optional(),
            enc_hint);
        break;

      case google::protobuf::FieldDescriptor::TYPE_ENUM: {
        size_t maxval = 0;
        auto enum_dsc = field->enum_type();
        auto nvals = enum_dsc->value_count();
        for (int j = 0; j < nvals; ++j) {
          auto enum_val = enum_dsc->value(j);
          if (enum_val->number() > maxval) {
            maxval = enum_val->number();
          }
        }

        fields.emplace_back(
            field->number(),
            field->name(),
            msg::FieldType::UINT32,
            maxval,
            field->is_repeated(),
            field->is_optional(),
            maxval < 250 ? EncodingHint::BITPACK : EncodingHint::LEB128);
        break;
      }

      case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
        fields.emplace_back(
            MessageSchemaField::mkObjectField(
                field->number(),
                field->name(),
                field->is_repeated(),
                field->is_optional(),
                MessageSchema::fromProtobuf(field->message_type())));
        break;

      default:
        RAISEF(
            kNotImplementedError,
            "field type not implemented: $0",
            field->type_name());

    }
  }

  return new msg::MessageSchema(dsc->full_name(), fields);
}

static void schemaNodeToString(
    size_t level,
    const MessageSchemaField& field,
    String* str) {
  String ws(level * 2, ' ');
  String type_name;
  String attrs;

  String type_prefix = "";
  String type_suffix = "";

  if (field.optional) {
    type_prefix += "optional[";
    type_suffix += "]";
  }

  if (field.repeated) {
    type_prefix += "list[";
    type_suffix += "]";
  }

  switch (field.type) {

    case FieldType::OBJECT:
      str->append(StringUtil::format(
          "$0$1object$2 $3 = $4 {\n",
          ws,
          type_prefix,
          type_suffix,
          field.name,
          field.id));

      for (const auto& f : field.schema->fields()) {
        schemaNodeToString(level + 1, f, str);
      }

      str->append(ws + "}\n");
      return;

    case FieldType::BOOLEAN:
      type_name = "bool";
      break;

    case FieldType::UINT32:
      type_name = "uint32";
      attrs += StringUtil::format(" @maxval=$0", field.type_size);
      break;

    case FieldType::UINT64:
      type_name = "uint64";
      attrs += StringUtil::format(" @maxval=$0", field.type_size);
      break;

    case FieldType::STRING:
      type_name = "string";
      attrs += StringUtil::format(" @maxlen=$0", field.type_size);
      break;

  }

  switch (field.encoding) {

    case EncodingHint::NONE:
      break;

    case EncodingHint::BITPACK:
      attrs += " @encoding=BITPACK";
      break;

    case EncodingHint::LEB128:
      attrs += " @encoding=LEB128";
      break;

  }

  str->append(StringUtil::format(
      "$0$1$2$3 $4 = $5$6;\n",
      ws,
      type_prefix,
      type_name,
      type_suffix,
      field.name,
      field.id,
      attrs));
}

MessageSchemaField MessageSchemaField::mkObjectField(
    uint32_t id,
    String name,
    bool repeated,
    bool optional,
    RefPtr<msg::MessageSchema> schema) {
  MessageSchemaField field(
      id,
      name,
      FieldType::OBJECT,
      0,
      repeated,
      optional);

  field.schema = schema;
  return field;
}

MessageSchema::MessageSchema(
    const String& name,
    Vector<MessageSchemaField> fields) :
    name_(name),
    fields_(fields) {
  for (const auto& field : fields_) {
    field_ids_.emplace(field.name, field.id);
    field_types_.emplace(field.id, field.type);
    field_names_.emplace(field.id, field.name);
  }
}

MessageSchema::MessageSchema(const MessageSchema& other) :
    name_(other.name_),
    fields_(other.fields_),
    field_ids_(other.field_ids_),
    field_types_(other.field_types_),
    field_names_(other.field_names_) {}

const String& MessageSchema::name() const {
  return name_;
}

const Vector<MessageSchemaField>& MessageSchema::fields() const {
  return fields_;
}

String MessageSchema::toString() const {
  String str = StringUtil::format("object $0 {\n", name_);

  for (const auto& f : fields_) {
    schemaNodeToString(1, f, &str);
  }

  str += "}";
  return str;
}

uint32_t MessageSchema::fieldId(const String& path) const {
  auto id = field_ids_.find(path);
  if (id == field_ids_.end()) {
    RAISEF(kIndexError, "field not found: $0", path);
  } else {
    return id->second;
  }
}

FieldType MessageSchema::fieldType(uint32_t id) const {
  if (id == 0) {
    return FieldType::OBJECT;
  }

  auto type = field_types_.find(id);
  if (type == field_types_.end()) {
    RAISEF(kIndexError, "field not found: $0", id);
  } else {
    return type->second;
  }
}

const String& MessageSchema::fieldName(uint32_t id) const {
  if (id == 0) {
    return name_;
  }

  auto name = field_names_.find(id);
  if (name == field_names_.end()) {
    RAISEF(kIndexError, "field not found: $0", id);
  } else {
    return name->second;
  }
}

RefPtr<MessageSchema> MessageSchema::fieldSchema(uint32_t id) const {
  for (const auto& field : fields_) {
    if (field.id == id) {
      return field.schema;
    }
  }

  RAISEF(kIndexError, "field not found: $0", id);
}

Set<String> MessageSchema::columns() const {
  Set<String> columns;

  for (const auto& c : field_names_) {
    columns.emplace(c.second);
  }

  return columns;
}

RefPtr<MessageSchema> MessageSchemaRepository::getSchema(
    const String& name) const {
  auto iter = schemas_.find(name);
  if (iter == schemas_.end()) {
    RAISEF(kRuntimeError, "schema not found: '$0'", name);
  }

  return iter->second;
}

void MessageSchemaRepository::registerSchema(RefPtr<MessageSchema> schema) {
  schemas_.emplace(schema->name(), schema);
}

} // namespace msg
} // namespace fnord
<|endoftext|>


<commit_before>/*
Copyright (c) 2017 InversePalindrome
Memento Mori - Entity.cpp
InversePalindrome.com
*/

#include "Entity.hpp"
#include "EntityManager.hpp"

#include <algorithm>

Entity::Entity(std::size_t id, const BitMask& entityComposition, EntityManager& entityManager) :
	id(id),
	entityComposition(entityComposition),
	components()
{
	for (std::size_t i = 0; i < entityComposition.size(); ++i)
	{
		if (entityComposition[i])
		{
			entityManager.addComponent(id, static_cast<Component::ID>(i));
		}
	}
}

std::size_t Entity::getID() const
{
	return this->id;
}

Entity::BitMask Entity::getEntityComposition() const
{
	return this->entityComposition;
}

void Entity::addComponent(ComponentPtr& component)
{
	if (!this->entityComposition[static_cast<std::size_t>(component->getID())])
	{
		this->components.push_back(std::move(component));
		this->entityComposition[static_cast<std::size_t>(component->getID())] = 1u;
	}
}

void Entity::removeComponent(Component::ID componentID)
{
	this->components.erase(std::remove_if(std::begin(this->components), std::end(this->components),
		[&](const auto& component) { return component->getID() == componentID; }), std::end(this->components));

	this->entityComposition[static_cast<std::size_t>(componentID)] = 0u;
}<commit_msg>Delete Entity.cpp<commit_after><|endoftext|>