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

#ifdef HAVE_FFMPEG

#include "pandabase.h"
#include "config_movies.h"
#include "ffmpegVirtualFile.h"
#include "virtualFileSystem.h"
extern "C" {
  #include "libavformat/avio.h"
}

#ifndef AVSEEK_SIZE
  #define AVSEEK_SIZE 0x10000
#endif

////////////////////////////////////////////////////////////////////
// These functions need to use C calling conventions.
////////////////////////////////////////////////////////////////////
extern "C" {
  static int       pandavfs_open(URLContext *h, const char *filename, int flags);
  static int       pandavfs_read(URLContext *h, unsigned char *buf, int size);
// This change happened a few days before 52.68.0
#if LIBAVFORMAT_VERSION_INT < 3425280
  static int       pandavfs_write(URLContext *h, unsigned char *buf, int size);
#else
  static int       pandavfs_write(URLContext *h, const unsigned char *buf, int size);
#endif
  static int64_t   pandavfs_seek(URLContext *h, int64_t pos, int whence);
  static int       pandavfs_close(URLContext *h);
}

////////////////////////////////////////////////////////////////////
//     Function: pandavfs_open
//       Access: Static Function
//  Description: A hook to open a panda VFS file.
////////////////////////////////////////////////////////////////////
static int
pandavfs_open(URLContext *h, const char *filename, int flags) {
  if (flags != 0) {
    movies_cat.error() << "ffmpeg is trying to write to the VFS.\n";
    return -1;
  }
  filename += 9; // Skip over "pandavfs:"
  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
  istream *s = vfs->open_read_file(filename, true);
  if (s == 0) {
    return -1;
  }
  // Test whether seek works.
  s->seekg(1, ios::beg);
  int tel1 = s->tellg();
  s->seekg(0, ios::beg);
  int tel2 = s->tellg();
  if (s->fail() || (tel1!=1) || (tel2!=0)) {
    movies_cat.error() << "cannot play movie (not seekable): " << h->filename << "\n";
    delete s;
    return -1;
  }
  h->priv_data = s;
  return 0;
}

////////////////////////////////////////////////////////////////////
//     Function: pandavfs_read
//       Access: Static Function
//  Description: A hook to read a panda VFS file.
////////////////////////////////////////////////////////////////////
static int
pandavfs_read(URLContext *h, unsigned char *buf, int size) {
  istream *s = (istream*)(h->priv_data);
  s->read((char*)buf, size);
  int gc = s->gcount();
  s->clear();
  return gc;
}

////////////////////////////////////////////////////////////////////
//     Function: pandavfs_write
//       Access: Static Function
//  Description: A hook to write a panda VFS file.
////////////////////////////////////////////////////////////////////
static int
#if LIBAVFORMAT_VERSION_INT < 3425280
pandavfs_write(URLContext *h, unsigned char *buf, int size) {
#else
pandavfs_write(URLContext *h, const unsigned char *buf, int size) {
#endif
  movies_cat.error() << "ffmpeg is trying to write to the VFS.\n";
  return -1;
}

////////////////////////////////////////////////////////////////////
//     Function: pandavfs_seek
//       Access: Static Function
//  Description: A hook to seek a panda VFS file.
////////////////////////////////////////////////////////////////////
static PN_int64
pandavfs_seek(URLContext *h, PN_int64 pos, int whence) {
  istream *s = (istream*)(h->priv_data);
  switch(whence) {
  case SEEK_SET: s->seekg(pos, ios::beg); break;
  case SEEK_CUR: s->seekg(pos, ios::cur); break;
  case SEEK_END: s->seekg(pos, ios::end); break;
  case AVSEEK_SIZE: {
    s->seekg(0, ios::cur);
    int p = s->tellg();
    s->seekg(-1, ios::end);
    int size = s->tellg();
    if (size < 0) {
      movies_cat.error() << "Failed to determine filesize in ffmpegVirtualFile\n";
      s->clear();
      return -1;
    }
    size++;
    s->seekg(p, ios::beg);
    s->clear();
    return size; }
  default:
    movies_cat.error() << "Illegal parameter to seek in ffmpegVirtualFile\n";
    s->clear();
    return -1;
  }
  s->clear();
  int tl = s->tellg();
  return tl;
}

////////////////////////////////////////////////////////////////////
//     Function: pandavfs_close
//       Access: Static Function
//  Description: A hook to close a panda VFS file.
////////////////////////////////////////////////////////////////////
static int
pandavfs_close(URLContext *h) {
  istream *s = (istream*)(h->priv_data);
  delete s;
  h->priv_data = 0;
  return 0;
}

////////////////////////////////////////////////////////////////////
//     Function: FfmpegVirtualFile::register_protocol
//       Access: Public, Static
//  Description: Enables ffmpeg to access panda's VFS.
//
//               After calling this method, ffmpeg will be
//               able to open "URLs" that look like this:
//
//               pandavfs:/c/mygame/foo.avi
//
////////////////////////////////////////////////////////////////////
void FfmpegVirtualFile::
register_protocol() {
  static bool initialized = false;
  if (initialized) {
    return;
  }
  static URLProtocol protocol;
  protocol.name = "pandavfs";
  protocol.url_open  = pandavfs_open;
  protocol.url_read  = pandavfs_read;
  protocol.url_write = pandavfs_write;
  protocol.url_seek  = pandavfs_seek;
  protocol.url_close = pandavfs_close;
#if LIBAVFORMAT_VERSION_INT < 3415296
  ::register_protocol(&protocol);
#else
  av_register_protocol(&protocol);
#endif
}

#endif // HAVE_FFMPEG
<commit_msg>let's not forget this<commit_after>// Filename: ffmpegVirtualFile.cxx
// Created by: jyelon (02Jul07)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University.  All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license.  You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////

#ifdef HAVE_FFMPEG

#include "pandabase.h"
#include "config_movies.h"
#include "ffmpegVirtualFile.h"
#include "virtualFileSystem.h"
extern "C" {
  #include "libavformat/avio.h"
}

#ifndef AVSEEK_SIZE
  #define AVSEEK_SIZE 0x10000
#endif

////////////////////////////////////////////////////////////////////
// These functions need to use C calling conventions.
////////////////////////////////////////////////////////////////////
extern "C" {
  static int       pandavfs_open(URLContext *h, const char *filename, int flags);
  static int       pandavfs_read(URLContext *h, unsigned char *buf, int size);
// This change happened a few days before 52.68.0
#if LIBAVFORMAT_VERSION_INT < 3425280
  static int       pandavfs_write(URLContext *h, unsigned char *buf, int size);
#else
  static int       pandavfs_write(URLContext *h, const unsigned char *buf, int size);
#endif
  static int64_t   pandavfs_seek(URLContext *h, int64_t pos, int whence);
  static int       pandavfs_close(URLContext *h);
}

////////////////////////////////////////////////////////////////////
//     Function: pandavfs_open
//       Access: Static Function
//  Description: A hook to open a panda VFS file.
////////////////////////////////////////////////////////////////////
static int
pandavfs_open(URLContext *h, const char *filename, int flags) {
  if (flags != 0) {
    movies_cat.error() << "ffmpeg is trying to write to the VFS.\n";
    return -1;
  }
  filename += 9; // Skip over "pandavfs:"
  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
  istream *s = vfs->open_read_file(filename, true);
  if (s == 0) {
    return -1;
  }
  // Test whether seek works.
  s->seekg(1, ios::beg);
  int tel1 = s->tellg();
  s->seekg(0, ios::beg);
  int tel2 = s->tellg();
  if (s->fail() || (tel1!=1) || (tel2!=0)) {
    movies_cat.error() << "cannot play movie (not seekable): " << h->filename << "\n";
    delete s;
    return -1;
  }
  h->priv_data = s;
  return 0;
}

////////////////////////////////////////////////////////////////////
//     Function: pandavfs_read
//       Access: Static Function
//  Description: A hook to read a panda VFS file.
////////////////////////////////////////////////////////////////////
static int
pandavfs_read(URLContext *h, unsigned char *buf, int size) {
  istream *s = (istream*)(h->priv_data);
  s->read((char*)buf, size);
  int gc = s->gcount();
  s->clear();
  return gc;
}

////////////////////////////////////////////////////////////////////
//     Function: pandavfs_write
//       Access: Static Function
//  Description: A hook to write a panda VFS file.
////////////////////////////////////////////////////////////////////
static int
#if LIBAVFORMAT_VERSION_INT < 3425280
pandavfs_write(URLContext *h, unsigned char *buf, int size) {
#else
pandavfs_write(URLContext *h, const unsigned char *buf, int size) {
#endif
  movies_cat.error() << "ffmpeg is trying to write to the VFS.\n";
  return -1;
}

////////////////////////////////////////////////////////////////////
//     Function: pandavfs_seek
//       Access: Static Function
//  Description: A hook to seek a panda VFS file.
////////////////////////////////////////////////////////////////////
static int64_t
pandavfs_seek(URLContext *h, int64_t pos, int whence) {
  istream *s = (istream*)(h->priv_data);
  switch(whence) {
  case SEEK_SET: s->seekg(pos, ios::beg); break;
  case SEEK_CUR: s->seekg(pos, ios::cur); break;
  case SEEK_END: s->seekg(pos, ios::end); break;
  case AVSEEK_SIZE: {
    s->seekg(0, ios::cur);
    int p = s->tellg();
    s->seekg(-1, ios::end);
    int size = s->tellg();
    if (size < 0) {
      movies_cat.error() << "Failed to determine filesize in ffmpegVirtualFile\n";
      s->clear();
      return -1;
    }
    size++;
    s->seekg(p, ios::beg);
    s->clear();
    return size; }
  default:
    movies_cat.error() << "Illegal parameter to seek in ffmpegVirtualFile\n";
    s->clear();
    return -1;
  }
  s->clear();
  int tl = s->tellg();
  return tl;
}

////////////////////////////////////////////////////////////////////
//     Function: pandavfs_close
//       Access: Static Function
//  Description: A hook to close a panda VFS file.
////////////////////////////////////////////////////////////////////
static int
pandavfs_close(URLContext *h) {
  istream *s = (istream*)(h->priv_data);
  delete s;
  h->priv_data = 0;
  return 0;
}

////////////////////////////////////////////////////////////////////
//     Function: FfmpegVirtualFile::register_protocol
//       Access: Public, Static
//  Description: Enables ffmpeg to access panda's VFS.
//
//               After calling this method, ffmpeg will be
//               able to open "URLs" that look like this:
//
//               pandavfs:/c/mygame/foo.avi
//
////////////////////////////////////////////////////////////////////
void FfmpegVirtualFile::
register_protocol() {
  static bool initialized = false;
  if (initialized) {
    return;
  }
  static URLProtocol protocol;
  protocol.name = "pandavfs";
  protocol.url_open  = pandavfs_open;
  protocol.url_read  = pandavfs_read;
  protocol.url_write = pandavfs_write;
  protocol.url_seek  = pandavfs_seek;
  protocol.url_close = pandavfs_close;
#if LIBAVFORMAT_VERSION_INT < 3415296
  ::register_protocol(&protocol);
#else
  av_register_protocol(&protocol);
#endif
}

#endif // HAVE_FFMPEG
<|endoftext|>


<commit_before>/**
 * PANDA 3D SOFTWARE
 * Copyright (c) Carnegie Mellon University.  All rights reserved.
 *
 * All use of this software is subject to the terms of the revised BSD
 * license.  You should have received a copy of this license along
 * with this source code in a file named "LICENSE."
 *
 * @file movieTypeRegistry.cxx
 * @author rdb
 * @date 2013-08-24
 */

#include "movieTypeRegistry.h"
#include "string_utils.h"
#include "config_movies.h"
#include "config_putil.h"
#include "load_dso.h"

using std::endl;
using std::string;

MovieTypeRegistry *MovieTypeRegistry::_global_ptr = nullptr;

/**
 * Obtains a MovieVideo that references a file.
 */
PT(MovieAudio) MovieTypeRegistry::
make_audio(const Filename &name) {
  string ext = downcase(name.get_extension());

  _audio_lock.lock();

  // Make sure that the list of audio types has been read in.
  load_audio_types();

  // Maybe we need to load a module?
  if (_deferred_audio_types.count(ext)) {
    load_movie_library(_deferred_audio_types[ext]);
    _deferred_audio_types.erase(ext);
  }

  // Explicit extension is preferred over catch-all.
  if (_audio_type_registry.count(ext)) {
    MakeAudioFunc func = _audio_type_registry[ext];
    _audio_lock.unlock();
    return (*func)(name);
  }

  // If we didn't find it, see if there was a type registered with '*' as
  // extension.  This is a catch-all loader.
  if (_deferred_audio_types.count("*")) {
    load_movie_library(_deferred_audio_types["*"]);
    _deferred_audio_types.erase("*");
  }

  if (_audio_type_registry.count("*")) {
    MakeAudioFunc func = _audio_type_registry["*"];
    _audio_lock.unlock();
    return (*func)(name);
  }

  movies_cat.error()
    << "Support for audio files with extension ." << ext << " was not enabled.\n";

  _audio_lock.unlock();
  return new MovieAudio("Load-Failure Stub");
}

/**
 * Registers a MovieAudio type, so that files with any of the given extensions
 * will be loaded as this type.  You may use * as a catch-all extension.
 */
void MovieTypeRegistry::
register_audio_type(MakeAudioFunc func, const string &extensions) {
  ReMutexHolder holder(_audio_lock);
  vector_string words;
  extract_words(downcase(extensions), words);

  vector_string::const_iterator wi;
  for (wi = words.begin(); wi != words.end(); ++wi) {
    if (_audio_type_registry.count(*wi)) {
      movies_cat->warning()
        << "Attempt to register multiple audio types with extension " << (*wi) << "\n";
    } else {
      movies_cat->debug()
        << "Registered audio type with extension " << (*wi) << "\n";
    }
    _audio_type_registry[*wi] = func;
  }
}

/**
 * Loads the list with audio types, if we haven't already.
 */
void MovieTypeRegistry::
load_audio_types() {
  ReMutexHolder holder(_audio_lock);
  static bool audio_types_loaded = false;

  if (!audio_types_loaded) {
    int num_unique_values = load_audio_type.get_num_unique_values();

    for (int i = 0; i < num_unique_values; i++) {
      string param = load_audio_type.get_unique_value(i);

      vector_string words;
      extract_words(param, words);

      if (words.size() == 1) {
        // Exactly one word: load the named library immediately.
        string name = words[0];
        Filename dlname = Filename::dso_filename("lib" + name + ".so");
        movies_cat.info()
          << "loading audio type module: " << name << endl;
        void *tmp = load_dso(get_plugin_path().get_value(), dlname);
        if (tmp == nullptr) {
          movies_cat.warning()
            << "Unable to load " << dlname.to_os_specific()
            << ": " << load_dso_error() << endl;
        } else if (movies_cat.is_debug()) {
          movies_cat.debug()
            << "done loading audio type module: " << name << endl;
        }

      } else if (words.size() > 1) {
        // Multiple words: the first n words are filename extensions, and the
        // last word is the name of the library to load should any of those
        // filename extensions be encountered.
        size_t num_extensions = words.size() - 1;
        string library_name = words[num_extensions];

        for (size_t i = 0; i < num_extensions; i++) {
          string extension = downcase(words[i]);
          if (extension[0] == '.') {
            extension = extension.substr(1);
          }

          _deferred_audio_types[extension] = library_name;
        }
      }
    }

    audio_types_loaded = true;
  }
}

/**
 * Obtains a MovieVideo that references a file.
 */
PT(MovieVideo) MovieTypeRegistry::
make_video(const Filename &name) {
  string ext = downcase(name.get_extension());

  _video_lock.lock();

  // Make sure that the list of video types has been read in.
  load_video_types();

  // Maybe we need to load a module?
  if (_deferred_video_types.count(ext)) {
    load_movie_library(_deferred_video_types[ext]);
    _deferred_video_types.erase(ext);
  }

  // Explicit extension is preferred over catch-all.
  if (_video_type_registry.count(ext)) {
    MakeVideoFunc func = _video_type_registry[ext];
    _video_lock.unlock();
    return (*func)(name);
  }

  // If we didn't find it, see if there was a type registered with '*' as
  // extension.  This is a catch-all loader.
  if (_deferred_video_types.count("*")) {
    load_movie_library(_deferred_video_types["*"]);
    _deferred_video_types.erase("*");
  }

  if (_video_type_registry.count("*")) {
    MakeVideoFunc func = _video_type_registry["*"];
    _video_lock.unlock();
    return (*func)(name);
  }

  movies_cat.error()
    << "Support for video files with extension ." << ext << " was not enabled.\n";

  _video_lock.unlock();
  return new MovieVideo("Load-Failure Stub");
}

/**
 * Registers a MovieVideo type, so that files with any of the given extensions
 * will be loaded as this type.  You may use * as a catch-all extension.
 */
void MovieTypeRegistry::
register_video_type(MakeVideoFunc func, const string &extensions) {
  ReMutexHolder holder(_video_lock);
  vector_string words;
  extract_words(downcase(extensions), words);

  vector_string::const_iterator wi;
  for (wi = words.begin(); wi != words.end(); ++wi) {
    if (_video_type_registry.count(*wi)) {
      movies_cat->warning()
        << "Attempt to register multiple video types with extension " << (*wi) << "\n";
    } else {
      movies_cat->debug()
        << "Registered video type with extension " << (*wi) << "\n";
    }
    _video_type_registry[*wi] = func;
  }
}

/**
 * Loads the list with video types, if we haven't already.
 */
void MovieTypeRegistry::
load_video_types() {
  ReMutexHolder holder(_video_lock);
  static bool video_types_loaded = false;

  if (!video_types_loaded) {
    int num_unique_values = load_video_type.get_num_unique_values();

    for (int i = 0; i < num_unique_values; i++) {
      string param = load_video_type.get_unique_value(i);

      vector_string words;
      extract_words(param, words);

      if (words.size() == 1) {
        // Exactly one word: load the named library immediately.
        string name = words[0];
        Filename dlname = Filename::dso_filename("lib" + name + ".so");
        movies_cat.info()
          << "loading video type module: " << name << endl;
        void *tmp = load_dso(get_plugin_path().get_value(), dlname);
        if (tmp == nullptr) {
          movies_cat.warning()
            << "Unable to load " << dlname.to_os_specific()
            << ": " << load_dso_error() << endl;
        } else if (movies_cat.is_debug()) {
          movies_cat.debug()
            << "done loading video type module: " << name << endl;
        }

      } else if (words.size() > 1) {
        // Multiple words: the first n words are filename extensions, and the
        // last word is the name of the library to load should any of those
        // filename extensions be encountered.
        size_t num_extensions = words.size() - 1;
        string library_name = words[num_extensions];

        for (size_t i = 0; i < num_extensions; i++) {
          string extension = downcase(words[i]);
          if (extension[0] == '.') {
            extension = extension.substr(1);
          }

          _deferred_video_types[extension] = library_name;
        }
      }
    }

    video_types_loaded = true;
  }
}

/**
 * Loads the module.
 */
void MovieTypeRegistry::
load_movie_library(const string &name) {
  ReMutexHolder holder(_video_lock);
  Filename dlname = Filename::dso_filename("lib" + name + ".so");
  movies_cat.info()
    << "loading video type module: " << name << endl;
  void *tmp = load_dso(get_plugin_path().get_value(), dlname);

  if (tmp == nullptr) {
    movies_cat.warning()
      << "Unable to load " << dlname.to_os_specific()
      << ": " << load_dso_error() << endl;
  } else if (movies_cat.is_debug()) {
    movies_cat.debug()
      << "done loading video type module: " << name << endl;
  }
}
<commit_msg>movies: Fix missing include<commit_after>/**
 * PANDA 3D SOFTWARE
 * Copyright (c) Carnegie Mellon University.  All rights reserved.
 *
 * All use of this software is subject to the terms of the revised BSD
 * license.  You should have received a copy of this license along
 * with this source code in a file named "LICENSE."
 *
 * @file movieTypeRegistry.cxx
 * @author rdb
 * @date 2013-08-24
 */

#include "movieTypeRegistry.h"

#include "string_utils.h"
#include "config_movies.h"
#include "config_putil.h"
#include "load_dso.h"
#include "reMutexHolder.h"

using std::endl;
using std::string;

MovieTypeRegistry *MovieTypeRegistry::_global_ptr = nullptr;

/**
 * Obtains a MovieVideo that references a file.
 */
PT(MovieAudio) MovieTypeRegistry::
make_audio(const Filename &name) {
  string ext = downcase(name.get_extension());

  _audio_lock.lock();

  // Make sure that the list of audio types has been read in.
  load_audio_types();

  // Maybe we need to load a module?
  if (_deferred_audio_types.count(ext)) {
    load_movie_library(_deferred_audio_types[ext]);
    _deferred_audio_types.erase(ext);
  }

  // Explicit extension is preferred over catch-all.
  if (_audio_type_registry.count(ext)) {
    MakeAudioFunc func = _audio_type_registry[ext];
    _audio_lock.unlock();
    return (*func)(name);
  }

  // If we didn't find it, see if there was a type registered with '*' as
  // extension.  This is a catch-all loader.
  if (_deferred_audio_types.count("*")) {
    load_movie_library(_deferred_audio_types["*"]);
    _deferred_audio_types.erase("*");
  }

  if (_audio_type_registry.count("*")) {
    MakeAudioFunc func = _audio_type_registry["*"];
    _audio_lock.unlock();
    return (*func)(name);
  }

  movies_cat.error()
    << "Support for audio files with extension ." << ext << " was not enabled.\n";

  _audio_lock.unlock();
  return new MovieAudio("Load-Failure Stub");
}

/**
 * Registers a MovieAudio type, so that files with any of the given extensions
 * will be loaded as this type.  You may use * as a catch-all extension.
 */
void MovieTypeRegistry::
register_audio_type(MakeAudioFunc func, const string &extensions) {
  ReMutexHolder holder(_audio_lock);
  vector_string words;
  extract_words(downcase(extensions), words);

  vector_string::const_iterator wi;
  for (wi = words.begin(); wi != words.end(); ++wi) {
    if (_audio_type_registry.count(*wi)) {
      movies_cat->warning()
        << "Attempt to register multiple audio types with extension " << (*wi) << "\n";
    } else {
      movies_cat->debug()
        << "Registered audio type with extension " << (*wi) << "\n";
    }
    _audio_type_registry[*wi] = func;
  }
}

/**
 * Loads the list with audio types, if we haven't already.
 */
void MovieTypeRegistry::
load_audio_types() {
  ReMutexHolder holder(_audio_lock);
  static bool audio_types_loaded = false;

  if (!audio_types_loaded) {
    int num_unique_values = load_audio_type.get_num_unique_values();

    for (int i = 0; i < num_unique_values; i++) {
      string param = load_audio_type.get_unique_value(i);

      vector_string words;
      extract_words(param, words);

      if (words.size() == 1) {
        // Exactly one word: load the named library immediately.
        string name = words[0];
        Filename dlname = Filename::dso_filename("lib" + name + ".so");
        movies_cat.info()
          << "loading audio type module: " << name << endl;
        void *tmp = load_dso(get_plugin_path().get_value(), dlname);
        if (tmp == nullptr) {
          movies_cat.warning()
            << "Unable to load " << dlname.to_os_specific()
            << ": " << load_dso_error() << endl;
        } else if (movies_cat.is_debug()) {
          movies_cat.debug()
            << "done loading audio type module: " << name << endl;
        }

      } else if (words.size() > 1) {
        // Multiple words: the first n words are filename extensions, and the
        // last word is the name of the library to load should any of those
        // filename extensions be encountered.
        size_t num_extensions = words.size() - 1;
        string library_name = words[num_extensions];

        for (size_t i = 0; i < num_extensions; i++) {
          string extension = downcase(words[i]);
          if (extension[0] == '.') {
            extension = extension.substr(1);
          }

          _deferred_audio_types[extension] = library_name;
        }
      }
    }

    audio_types_loaded = true;
  }
}

/**
 * Obtains a MovieVideo that references a file.
 */
PT(MovieVideo) MovieTypeRegistry::
make_video(const Filename &name) {
  string ext = downcase(name.get_extension());

  _video_lock.lock();

  // Make sure that the list of video types has been read in.
  load_video_types();

  // Maybe we need to load a module?
  if (_deferred_video_types.count(ext)) {
    load_movie_library(_deferred_video_types[ext]);
    _deferred_video_types.erase(ext);
  }

  // Explicit extension is preferred over catch-all.
  if (_video_type_registry.count(ext)) {
    MakeVideoFunc func = _video_type_registry[ext];
    _video_lock.unlock();
    return (*func)(name);
  }

  // If we didn't find it, see if there was a type registered with '*' as
  // extension.  This is a catch-all loader.
  if (_deferred_video_types.count("*")) {
    load_movie_library(_deferred_video_types["*"]);
    _deferred_video_types.erase("*");
  }

  if (_video_type_registry.count("*")) {
    MakeVideoFunc func = _video_type_registry["*"];
    _video_lock.unlock();
    return (*func)(name);
  }

  movies_cat.error()
    << "Support for video files with extension ." << ext << " was not enabled.\n";

  _video_lock.unlock();
  return new MovieVideo("Load-Failure Stub");
}

/**
 * Registers a MovieVideo type, so that files with any of the given extensions
 * will be loaded as this type.  You may use * as a catch-all extension.
 */
void MovieTypeRegistry::
register_video_type(MakeVideoFunc func, const string &extensions) {
  ReMutexHolder holder(_video_lock);
  vector_string words;
  extract_words(downcase(extensions), words);

  vector_string::const_iterator wi;
  for (wi = words.begin(); wi != words.end(); ++wi) {
    if (_video_type_registry.count(*wi)) {
      movies_cat->warning()
        << "Attempt to register multiple video types with extension " << (*wi) << "\n";
    } else {
      movies_cat->debug()
        << "Registered video type with extension " << (*wi) << "\n";
    }
    _video_type_registry[*wi] = func;
  }
}

/**
 * Loads the list with video types, if we haven't already.
 */
void MovieTypeRegistry::
load_video_types() {
  ReMutexHolder holder(_video_lock);
  static bool video_types_loaded = false;

  if (!video_types_loaded) {
    int num_unique_values = load_video_type.get_num_unique_values();

    for (int i = 0; i < num_unique_values; i++) {
      string param = load_video_type.get_unique_value(i);

      vector_string words;
      extract_words(param, words);

      if (words.size() == 1) {
        // Exactly one word: load the named library immediately.
        string name = words[0];
        Filename dlname = Filename::dso_filename("lib" + name + ".so");
        movies_cat.info()
          << "loading video type module: " << name << endl;
        void *tmp = load_dso(get_plugin_path().get_value(), dlname);
        if (tmp == nullptr) {
          movies_cat.warning()
            << "Unable to load " << dlname.to_os_specific()
            << ": " << load_dso_error() << endl;
        } else if (movies_cat.is_debug()) {
          movies_cat.debug()
            << "done loading video type module: " << name << endl;
        }

      } else if (words.size() > 1) {
        // Multiple words: the first n words are filename extensions, and the
        // last word is the name of the library to load should any of those
        // filename extensions be encountered.
        size_t num_extensions = words.size() - 1;
        string library_name = words[num_extensions];

        for (size_t i = 0; i < num_extensions; i++) {
          string extension = downcase(words[i]);
          if (extension[0] == '.') {
            extension = extension.substr(1);
          }

          _deferred_video_types[extension] = library_name;
        }
      }
    }

    video_types_loaded = true;
  }
}

/**
 * Loads the module.
 */
void MovieTypeRegistry::
load_movie_library(const string &name) {
  ReMutexHolder holder(_video_lock);
  Filename dlname = Filename::dso_filename("lib" + name + ".so");
  movies_cat.info()
    << "loading video type module: " << name << endl;
  void *tmp = load_dso(get_plugin_path().get_value(), dlname);

  if (tmp == nullptr) {
    movies_cat.warning()
      << "Unable to load " << dlname.to_os_specific()
      << ": " << load_dso_error() << endl;
  } else if (movies_cat.is_debug()) {
    movies_cat.debug()
      << "done loading video type module: " << name << endl;
  }
}
<|endoftext|>


<commit_before>/*
 *  Phusion Passenger - http://www.modrails.com/
 *  Copyright (c) 2010 Phusion
 *
 *  "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *  THE SOFTWARE.
 */
#include <oxt/system_calls.hpp>
#include <oxt/backtrace.hpp>
#include <oxt/thread.hpp>

#include <sys/types.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cerrno>
#include <signal.h>

#include "../AgentBase.h"
#include "../AccountsDatabase.h"
#include "../Account.h"
#include "../ServerInstanceDir.h"
#include "LoggingServer.h"
#include "../Exceptions.h"
#include "../Utils.h"
#include "../Utils/IOUtils.h"
#include "../Utils/Base64.h"
#include "../Utils/VariantMap.h"

using namespace oxt;
using namespace Passenger;

static struct ev_loop *eventLoop;

static struct ev_loop *
createEventLoop() {
	struct ev_loop *loop;

	// libev doesn't like choosing epoll and kqueue because the author thinks they're broken,
	// so let's try to force it.
	loop = ev_default_loop(EVBACKEND_EPOLL);
	if (loop == NULL) {
		loop = ev_default_loop(EVBACKEND_KQUEUE);
	}
	if (loop == NULL) {
		loop = ev_default_loop(0);
	}
	if (loop == NULL) {
		throw RuntimeException("Cannot create an event loop");
	} else {
		return loop;
	}
}

static void
lowerPrivilege(const string &username, const struct passwd *user, const struct group *group) {
	int e;

	if (initgroups(username.c_str(), group->gr_gid) != 0) {
		e = errno;
		P_WARN("WARNING: Unable to set supplementary groups for " <<
			"PassengerLoggingAgent: " << strerror(e) << " (" << e << ")");
	}
	if (setgid(group->gr_gid) != 0) {
		e = errno;
		P_WARN("WARNING: Unable to lower PassengerLoggingAgent's "
			"privilege to that of user '" << username <<
			"': cannot set group ID to " << group->gr_gid <<
			": " << strerror(e) <<
			" (" << e << ")");
	}
	if (setuid(user->pw_uid) != 0) {
		e = errno;
		P_WARN("WARNING: Unable to lower PassengerLoggingAgent's "
			"privilege to that of user '" << username <<
			"': cannot set user ID: " << strerror(e) <<
			" (" << e << ")");
	}
}

void
feedbackFdBecameReadable(ev::io &watcher, int revents) {
	/* This event indicates that the watchdog has been killed.
	 * In this case we'll kill all descendant
	 * processes and exit. There's no point in keeping this agent
	 * running because we can't detect when the web server exits,
	 * and because this agent doesn't own the server instance
	 * directory. As soon as passenger-status is run, the server
	 * instance directory will be cleaned up, making this agent's
	 * services inaccessible.
	 */
	syscalls::killpg(getpgrp(), SIGKILL);
	_exit(2); // In case killpg() fails.
}

void
caughtExitSignal(ev::sig &watcher, int revents) {
	P_DEBUG("Caught signal, exiting...");
	ev_unloop(eventLoop, EVUNLOOP_ONE);
}

static string
myself() {
	struct passwd *entry = getpwuid(geteuid());
	if (entry != NULL) {
		return entry->pw_name;
	} else {
		throw NonExistentUserException(string("The current user, UID ") +
			toString(geteuid()) + ", doesn't have a corresponding " +
			"entry in the system's user database. Please fix your " +
			"system's user database first.");
	}
}

int
main(int argc, char *argv[]) {
	VariantMap options        = initializeAgent(argc, argv, "PassengerLoggingAgent");
	string socketAddress      = options.get("logging_agent_address");
	string loggingDir         = options.get("analytics_log_dir");
	string password           = options.get("logging_agent_password");
	string username           = options.get("analytics_log_user",
		false, myself());
	string groupname          = options.get("analytics_log_group", false);
	string permissions        = options.get("analytics_log_permissions",
		false, DEFAULT_ANALYTICS_LOG_PERMISSIONS);
	string unionStationServiceAddress = options.get("union_station_service_address",
		false, DEFAULT_UNION_STATION_SERVICE_ADDRESS);
	int    unionStationServicePort = options.getInt("union_station_service_port",
		false, DEFAULT_UNION_STATION_SERVICE_PORT);
	string unionStationServiceCert = options.get("union_station_service_cert", false);

	curl_global_init(CURL_GLOBAL_ALL);

	try {
		/********** Now begins the real initialization **********/

		/* Create all the necessary objects and sockets... */
		AccountsDatabasePtr  accountsDatabase;
		FileDescriptor       serverSocketFd;
		struct passwd       *user;
		struct group        *group;
		int                  ret;

		eventLoop = createEventLoop();
		accountsDatabase = ptr(new AccountsDatabase());
		serverSocketFd = createServer(socketAddress.c_str());
		if (getSocketAddressType(socketAddress) == SAT_UNIX) {
			do {
				ret = chmod(parseUnixSocketAddress(socketAddress).c_str(),
					S_ISVTX |
					S_IRUSR | S_IWUSR | S_IXUSR |
					S_IRGRP | S_IWGRP | S_IXGRP |
					S_IROTH | S_IWOTH | S_IXOTH);
			} while (ret == -1 && errno == EINTR);
		}

		/* Sanity check user accounts. */

		user = getpwnam(username.c_str());
		if (user == NULL) {
			throw NonExistentUserException(string("The configuration option ") +
				"'PassengerAnalyticsLogUser' (Apache) or " +
				"'passenger_analytics_log_user' (Nginx) was set to '" +
				username + "', but this user doesn't exist. Please fix " +
				"the configuration option.");
		}

		if (groupname.empty()) {
			group = getgrgid(user->pw_gid);
			if (group == NULL) {
				throw NonExistentGroupException(string("The configuration option ") +
					"'PassengerAnalyticsLogGroup' (Apache) or " +
					"'passenger_analytics_log_group' (Nginx) wasn't set, " +
					"so PassengerLoggingAgent tried to use the default group " +
					"for user '" + username + "' - which is GID #" +
					toString(user->pw_gid) + " - as the group for the analytics " +
					"log dir, but this GID doesn't exist. " +
					"You can solve this problem by explicitly " +
					"setting PassengerAnalyticsLogGroup (Apache) or " +
					"passenger_analytics_log_group (Nginx) to a group that " +
					"does exist. In any case, it looks like your system's user " +
					"database is broken; Phusion Passenger can work fine even " +
					"with this broken user database, but you should still fix it.");
			} else {
				groupname = group->gr_name;
			}
		} else {
			group = getgrnam(groupname.c_str());
			if (group == NULL) {
				throw NonExistentGroupException(string("The configuration option ") +
					"'PassengerAnalyticsLogGroup' (Apache) or " +
					"'passenger_analytics_log_group' (Nginx) was set to '" +
					groupname + "', but this group doesn't exist. Please fix " +
					"the configuration option.");
			}
		}

		/* Create the logging directory if necessary. */
		if (getFileType(loggingDir) == FT_NONEXISTANT) {
			if (geteuid() == 0) {
				makeDirTree(loggingDir, permissions, user->pw_uid, group->gr_gid);
			} else {
				makeDirTree(loggingDir, permissions);
			}
		}

		/* Now's a good time to lower the privilege. */
		if (geteuid() == 0) {
			lowerPrivilege(username, user, group);
		}

		/* Now setup the actual logging server. */
		accountsDatabase->add("logging", password, false);
		LoggingServer server(eventLoop, serverSocketFd,
			accountsDatabase, loggingDir,
			"u=rwx,g=rx,o=rx", GROUP_NOT_GIVEN,
			unionStationServiceAddress,
			unionStationServicePort,
			unionStationServiceCert);

		ev::io feedbackFdWatcher(eventLoop);
		ev::sig sigintWatcher(eventLoop);
		ev::sig sigtermWatcher(eventLoop);

		if (feedbackFdAvailable()) {
			MessageChannel feedbackChannel(FEEDBACK_FD);
			feedbackFdWatcher.set<&feedbackFdBecameReadable>();
			feedbackFdWatcher.start(FEEDBACK_FD, ev::READ);
			feedbackChannel.write("initialized", NULL);
		}
		sigintWatcher.set<&caughtExitSignal>();
		sigintWatcher.start(SIGINT);
		sigtermWatcher.set<&caughtExitSignal>();
		sigtermWatcher.start(SIGTERM);

		/********** Initialized! Enter main loop... **********/

		ev_loop(eventLoop, 0);
		return 0;
	} catch (const tracable_exception &e) {
		P_ERROR("*** ERROR: " << e.what() << "\n" << e.backtrace());
		return 1;
	}
}
<commit_msg>Logging agent: print debugging information on SIGINFO<commit_after>/*
 *  Phusion Passenger - http://www.modrails.com/
 *  Copyright (c) 2010 Phusion
 *
 *  "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *  THE SOFTWARE.
 */
#include <oxt/system_calls.hpp>
#include <oxt/backtrace.hpp>
#include <oxt/thread.hpp>

#include <sys/types.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cerrno>
#include <signal.h>

#include "../AgentBase.h"
#include "../AccountsDatabase.h"
#include "../Account.h"
#include "../ServerInstanceDir.h"
#include "LoggingServer.h"
#include "../Exceptions.h"
#include "../Utils.h"
#include "../Utils/IOUtils.h"
#include "../Utils/Base64.h"
#include "../Utils/VariantMap.h"

using namespace oxt;
using namespace Passenger;

static struct ev_loop *eventLoop;
static LoggingServer *loggingServer;

static struct ev_loop *
createEventLoop() {
	struct ev_loop *loop;

	// libev doesn't like choosing epoll and kqueue because the author thinks they're broken,
	// so let's try to force it.
	loop = ev_default_loop(EVBACKEND_EPOLL);
	if (loop == NULL) {
		loop = ev_default_loop(EVBACKEND_KQUEUE);
	}
	if (loop == NULL) {
		loop = ev_default_loop(0);
	}
	if (loop == NULL) {
		throw RuntimeException("Cannot create an event loop");
	} else {
		return loop;
	}
}

static void
lowerPrivilege(const string &username, const struct passwd *user, const struct group *group) {
	int e;

	if (initgroups(username.c_str(), group->gr_gid) != 0) {
		e = errno;
		P_WARN("WARNING: Unable to set supplementary groups for " <<
			"PassengerLoggingAgent: " << strerror(e) << " (" << e << ")");
	}
	if (setgid(group->gr_gid) != 0) {
		e = errno;
		P_WARN("WARNING: Unable to lower PassengerLoggingAgent's "
			"privilege to that of user '" << username <<
			"': cannot set group ID to " << group->gr_gid <<
			": " << strerror(e) <<
			" (" << e << ")");
	}
	if (setuid(user->pw_uid) != 0) {
		e = errno;
		P_WARN("WARNING: Unable to lower PassengerLoggingAgent's "
			"privilege to that of user '" << username <<
			"': cannot set user ID: " << strerror(e) <<
			" (" << e << ")");
	}
}

void
feedbackFdBecameReadable(ev::io &watcher, int revents) {
	/* This event indicates that the watchdog has been killed.
	 * In this case we'll kill all descendant
	 * processes and exit. There's no point in keeping this agent
	 * running because we can't detect when the web server exits,
	 * and because this agent doesn't own the server instance
	 * directory. As soon as passenger-status is run, the server
	 * instance directory will be cleaned up, making this agent's
	 * services inaccessible.
	 */
	syscalls::killpg(getpgrp(), SIGKILL);
	_exit(2); // In case killpg() fails.
}

void
caughtExitSignal(ev::sig &watcher, int revents) {
	P_DEBUG("Caught signal, exiting...");
	ev_unloop(eventLoop, EVUNLOOP_ONE);
}

void
printInfo(ev::sig &watcher, int revents) {
	loggingServer->dump(cout);
	cout.flush();
}

static string
myself() {
	struct passwd *entry = getpwuid(geteuid());
	if (entry != NULL) {
		return entry->pw_name;
	} else {
		throw NonExistentUserException(string("The current user, UID ") +
			toString(geteuid()) + ", doesn't have a corresponding " +
			"entry in the system's user database. Please fix your " +
			"system's user database first.");
	}
}

int
main(int argc, char *argv[]) {
	VariantMap options        = initializeAgent(argc, argv, "PassengerLoggingAgent");
	string socketAddress      = options.get("logging_agent_address");
	string loggingDir         = options.get("analytics_log_dir");
	string password           = options.get("logging_agent_password");
	string username           = options.get("analytics_log_user",
		false, myself());
	string groupname          = options.get("analytics_log_group", false);
	string permissions        = options.get("analytics_log_permissions",
		false, DEFAULT_ANALYTICS_LOG_PERMISSIONS);
	string unionStationServiceAddress = options.get("union_station_service_address",
		false, DEFAULT_UNION_STATION_SERVICE_ADDRESS);
	int    unionStationServicePort = options.getInt("union_station_service_port",
		false, DEFAULT_UNION_STATION_SERVICE_PORT);
	string unionStationServiceCert = options.get("union_station_service_cert", false);

	curl_global_init(CURL_GLOBAL_ALL);

	try {
		/********** Now begins the real initialization **********/

		/* Create all the necessary objects and sockets... */
		AccountsDatabasePtr  accountsDatabase;
		FileDescriptor       serverSocketFd;
		struct passwd       *user;
		struct group        *group;
		int                  ret;

		eventLoop = createEventLoop();
		accountsDatabase = ptr(new AccountsDatabase());
		serverSocketFd = createServer(socketAddress.c_str());
		if (getSocketAddressType(socketAddress) == SAT_UNIX) {
			do {
				ret = chmod(parseUnixSocketAddress(socketAddress).c_str(),
					S_ISVTX |
					S_IRUSR | S_IWUSR | S_IXUSR |
					S_IRGRP | S_IWGRP | S_IXGRP |
					S_IROTH | S_IWOTH | S_IXOTH);
			} while (ret == -1 && errno == EINTR);
		}

		/* Sanity check user accounts. */

		user = getpwnam(username.c_str());
		if (user == NULL) {
			throw NonExistentUserException(string("The configuration option ") +
				"'PassengerAnalyticsLogUser' (Apache) or " +
				"'passenger_analytics_log_user' (Nginx) was set to '" +
				username + "', but this user doesn't exist. Please fix " +
				"the configuration option.");
		}

		if (groupname.empty()) {
			group = getgrgid(user->pw_gid);
			if (group == NULL) {
				throw NonExistentGroupException(string("The configuration option ") +
					"'PassengerAnalyticsLogGroup' (Apache) or " +
					"'passenger_analytics_log_group' (Nginx) wasn't set, " +
					"so PassengerLoggingAgent tried to use the default group " +
					"for user '" + username + "' - which is GID #" +
					toString(user->pw_gid) + " - as the group for the analytics " +
					"log dir, but this GID doesn't exist. " +
					"You can solve this problem by explicitly " +
					"setting PassengerAnalyticsLogGroup (Apache) or " +
					"passenger_analytics_log_group (Nginx) to a group that " +
					"does exist. In any case, it looks like your system's user " +
					"database is broken; Phusion Passenger can work fine even " +
					"with this broken user database, but you should still fix it.");
			} else {
				groupname = group->gr_name;
			}
		} else {
			group = getgrnam(groupname.c_str());
			if (group == NULL) {
				throw NonExistentGroupException(string("The configuration option ") +
					"'PassengerAnalyticsLogGroup' (Apache) or " +
					"'passenger_analytics_log_group' (Nginx) was set to '" +
					groupname + "', but this group doesn't exist. Please fix " +
					"the configuration option.");
			}
		}

		/* Create the logging directory if necessary. */
		if (getFileType(loggingDir) == FT_NONEXISTANT) {
			if (geteuid() == 0) {
				makeDirTree(loggingDir, permissions, user->pw_uid, group->gr_gid);
			} else {
				makeDirTree(loggingDir, permissions);
			}
		}

		/* Now's a good time to lower the privilege. */
		if (geteuid() == 0) {
			lowerPrivilege(username, user, group);
		}

		/* Now setup the actual logging server. */
		accountsDatabase->add("logging", password, false);
		LoggingServer server(eventLoop, serverSocketFd,
			accountsDatabase, loggingDir,
			"u=rwx,g=rx,o=rx", GROUP_NOT_GIVEN,
			unionStationServiceAddress,
			unionStationServicePort,
			unionStationServiceCert);
		loggingServer = &server;

		ev::io feedbackFdWatcher(eventLoop);
		ev::sig sigintWatcher(eventLoop);
		ev::sig sigtermWatcher(eventLoop);
		ev::sig siginfoWatcher(eventLoop);

		if (feedbackFdAvailable()) {
			MessageChannel feedbackChannel(FEEDBACK_FD);
			feedbackFdWatcher.set<&feedbackFdBecameReadable>();
			feedbackFdWatcher.start(FEEDBACK_FD, ev::READ);
			feedbackChannel.write("initialized", NULL);
		}
		sigintWatcher.set<&caughtExitSignal>();
		sigintWatcher.start(SIGINT);
		sigtermWatcher.set<&caughtExitSignal>();
		sigtermWatcher.start(SIGTERM);
		siginfoWatcher.set<&printInfo>();
		siginfoWatcher.start(SIGINFO);

		/********** Initialized! Enter main loop... **********/

		ev_loop(eventLoop, 0);
		return 0;
	} catch (const tracable_exception &e) {
		P_ERROR("*** ERROR: " << e.what() << "\n" << e.backtrace());
		return 1;
	}
}
<|endoftext|>


<commit_before>/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2003 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xerces" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache\@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation, and was
 * originally based on software copyright (c) 1999, International
 * Business Machines, Inc., http://www.ibm.com .  For more information
 * on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

/*
 * $Id$
 * $Log$
 * Revision 1.1  2003/12/16 16:56:51  peiyongz
 * BinMemOutputStream
 *
 *
 */

#include <xercesc/internal/BinMemOutputStream.hpp>
#include <xercesc/util/XMLString.hpp>
#include <string.h>

XERCES_CPP_NAMESPACE_BEGIN

BinMemOutputStream::BinMemOutputStream( int                  initCapacity
                                      , MemoryManager* const manager)
: fMemoryManager(manager)
, fDataBuf(0)
, fIndex(0)
, fCapacity(initCapacity)
{
    // Buffer is one larger than capacity, to allow for zero term
    fDataBuf = (XMLByte*) fMemoryManager->allocate
    (
        (fCapacity + 4) * sizeof(XMLByte)
    );

    // Keep it null terminated
    fDataBuf[0] = XMLByte(0);
}

BinMemOutputStream::~BinMemOutputStream()
{
    fMemoryManager->deallocate(fDataBuf);
}

void BinMemOutputStream::writeBytes(       XMLByte*     const      toGo
                                   , const unsigned int            maxToWrite)
{

    if (maxToWrite)
    {
        insureCapacity(maxToWrite);
        memcpy(&fDataBuf[fIndex], toGo, maxToWrite * sizeof(XMLByte));
        fIndex += maxToWrite;
    }

}

const XMLByte* BinMemOutputStream::getRawBuffer() const
{
    fDataBuf[fIndex] = 0;
    fDataBuf[fIndex + 1] = 0;
    fDataBuf[fIndex + 2] = 0;
    fDataBuf[fIndex + 3] = 0;
    return fDataBuf;
}

void BinMemOutputStream::reset()
{
    fIndex = 0;

    for (int i = 0; i < 4; i++)
    {
        fDataBuf[fIndex + i] = 0;
    }
}

unsigned int BinMemOutputStream::curPos() const
{
    return fIndex;
}

// ---------------------------------------------------------------------------
//  BinMemOutputStream: Private helper methods
// ---------------------------------------------------------------------------
void BinMemOutputStream::insureCapacity(const unsigned int extraNeeded)
{
    // If we can handle it, do nothing yet
    if (fIndex + extraNeeded < fCapacity)
        return;

    // Oops, not enough room. Calc new capacity and allocate new buffer
    const unsigned int newCap = (unsigned int)((fIndex + extraNeeded) * 2);
    XMLByte* newBuf = (XMLByte*) fMemoryManager->allocate
    (
        (newCap+4) * sizeof(XMLByte)
    );

    // Copy over the old stuff
    memcpy(newBuf, fDataBuf, fCapacity * sizeof(XMLByte) + 4);

    // Clean up old buffer and store new stuff
    fMemoryManager->deallocate(fDataBuf);
    fDataBuf = newBuf;
    fCapacity = newCap;
}

XERCES_CPP_NAMESPACE_END

<commit_msg>getSize()<commit_after>/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2003 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xerces" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache\@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation, and was
 * originally based on software copyright (c) 1999, International
 * Business Machines, Inc., http://www.ibm.com .  For more information
 * on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

/*
 * $Id$
 * $Log$
 * Revision 1.2  2003/12/17 00:01:44  peiyongz
 * getSize()
 *
 * Revision 1.1  2003/12/16 16:56:51  peiyongz
 * BinMemOutputStream
 *
 *
 */

#include <xercesc/internal/BinMemOutputStream.hpp>
#include <xercesc/util/XMLString.hpp>
#include <string.h>

XERCES_CPP_NAMESPACE_BEGIN

BinMemOutputStream::BinMemOutputStream( int                  initCapacity
                                      , MemoryManager* const manager)
: fMemoryManager(manager)
, fDataBuf(0)
, fIndex(0)
, fCapacity(initCapacity)
{
    // Buffer is one larger than capacity, to allow for zero term
    fDataBuf = (XMLByte*) fMemoryManager->allocate
    (
        (fCapacity + 4) * sizeof(XMLByte)
    );

    // Keep it null terminated
    fDataBuf[0] = XMLByte(0);
}

BinMemOutputStream::~BinMemOutputStream()
{
    fMemoryManager->deallocate(fDataBuf);
}

void BinMemOutputStream::writeBytes(       XMLByte*     const      toGo
                                   , const unsigned int            maxToWrite)
{

    if (maxToWrite)
    {
        insureCapacity(maxToWrite);
        memcpy(&fDataBuf[fIndex], toGo, maxToWrite * sizeof(XMLByte));
        fIndex += maxToWrite;
    }

}

const XMLByte* BinMemOutputStream::getRawBuffer() const
{
    fDataBuf[fIndex] = 0;
    fDataBuf[fIndex + 1] = 0;
    fDataBuf[fIndex + 2] = 0;
    fDataBuf[fIndex + 3] = 0;
    return fDataBuf;
}

void BinMemOutputStream::reset()
{
    fIndex = 0;

    for (int i = 0; i < 4; i++)
    {
        fDataBuf[fIndex + i] = 0;
    }
}

unsigned int BinMemOutputStream::curPos() const
{
    return fIndex;
}

unsigned int BinMemOutputStream::getSize() const
{
    return fIndex;
}

// ---------------------------------------------------------------------------
//  BinMemOutputStream: Private helper methods
// ---------------------------------------------------------------------------
void BinMemOutputStream::insureCapacity(const unsigned int extraNeeded)
{
    // If we can handle it, do nothing yet
    if (fIndex + extraNeeded < fCapacity)
        return;

    // Oops, not enough room. Calc new capacity and allocate new buffer
    const unsigned int newCap = (unsigned int)((fIndex + extraNeeded) * 2);
    XMLByte* newBuf = (XMLByte*) fMemoryManager->allocate
    (
        (newCap+4) * sizeof(XMLByte)
    );

    // Copy over the old stuff
    memcpy(newBuf, fDataBuf, fCapacity * sizeof(XMLByte) + 4);

    // Clean up old buffer and store new stuff
    fMemoryManager->deallocate(fDataBuf);
    fDataBuf = newBuf;
    fCapacity = newCap;
}

XERCES_CPP_NAMESPACE_END

<|endoftext|>


<commit_before>/*
 * Copyright 2020-2022 F4PGA Authors
 *
 * 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.
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 */

#include "uhdmcommonfrontend.h"

YOSYS_NAMESPACE_BEGIN

/* Stub for AST::process */
static void set_line_num(int) {}

/* Stub for AST::process */
static int get_line_num(void) { return 1; }

void UhdmCommonFrontend::print_read_options()
{
    log("    -noassert\n");
    log("        ignore assert() statements");
    log("\n");
    log("    -debug\n");
    log("        alias for -dump_ast1 -dump_ast2 -dump_vlog1 -dump_vlog2\n");
    log("\n");
    log("    -dump_ast1\n");
    log("        dump abstract syntax tree (before simplification)\n");
    log("\n");
    log("    -dump_ast2\n");
    log("        dump abstract syntax tree (after simplification)\n");
    log("\n");
    log("    -no_dump_ptr\n");
    log("        do not include hex memory addresses in dump (easier to diff dumps)\n");
    log("\n");
    log("    -dump_vlog1\n");
    log("        dump ast as Verilog code (before simplification)\n");
    log("\n");
    log("    -dump_vlog2\n");
    log("        dump ast as Verilog code (after simplification)\n");
    log("\n");
    log("    -dump_rtlil\n");
    log("        dump generated RTLIL netlist\n");
    log("\n");
    log("    -report [directory]\n");
    log("        write a coverage report for the UHDM file\n");
    log("\n");
    log("    -defer\n");
    log("        only read the abstract syntax tree and defer actual compilation\n");
    log("        to a later 'hierarchy' command. Useful in cases where the default\n");
    log("        parameters of modules yield invalid or not synthesizable code.\n");
    log("        Needs to be followed by read_systemverilog -link after reading\n");
    log("        all files.\n");
    log("    -link\n");
    log("        performs actual elaboration of the files read with -defer\n");
    log("    -parse-only\n");
    log("        this parameter only applies to read_systemverilog command,\n");
    log("        it runs only Surelog to parse design, but doesn't load generated\n");
    log("        tree into Yosys.\n");
    log("\n");
}

void UhdmCommonFrontend::execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
{
    this->call_log_header(design);
    this->args = args;

    bool defer = false;
    bool dump_ast1 = false;
    bool dump_ast2 = false;
    bool dump_vlog1 = false;
    bool dump_vlog2 = false;
    bool no_dump_ptr = false;
    bool dump_rtlil = false;
    std::vector<std::string> unhandled_args;

    for (size_t i = 0; i < args.size(); i++) {
        if (args[i] == "-debug") {
            dump_ast1 = true;
            dump_ast2 = true;
            dump_vlog1 = true;
            dump_vlog2 = true;
            this->shared.debug_flag = true;
        } else if (args[i] == "-report" && ++i < args.size()) {
            this->report_directory = args[i];
            this->shared.stop_on_error = false;
        } else if (args[i] == "-noassert") {
            this->shared.no_assert = true;
        } else if (args[i] == "-defer") {
            this->shared.defer = true;
        } else if (args[i] == "-dump_ast1") {
            dump_ast1 = true;
        } else if (args[i] == "-dump_ast2") {
            dump_ast2 = true;
        } else if (args[i] == "-dump_vlog1") {
            dump_vlog1 = true;
        } else if (args[i] == "-dump_vlog2") {
            dump_vlog2 = true;
        } else if (args[i] == "-no_dump_ptr") {
            no_dump_ptr = true;
        } else if (args[i] == "-dump_rtlil") {
            dump_rtlil = true;
        } else if (args[i] == "-parse-only") {
            this->shared.parse_only = true;
        } else if (args[i] == "-link") {
            this->shared.link = true;
            // Surelog needs it in the command line to link correctly
            unhandled_args.push_back(args[i]);
        } else {
            unhandled_args.push_back(args[i]);
        }
    }
    // pass only unhandled args to Surelog
    // unhandled args starts with command name,
    // but Surelog expects args[0] to be program name
    // and skips it
    this->args = unhandled_args;

    AST::current_filename = filename;
    AST::set_line_num = &set_line_num;
    AST::get_line_num = &get_line_num;

    bool dont_redefine = false;
    bool default_nettype_wire = true;

    AST::AstNode *current_ast = parse(filename);

    if (current_ast) {
        AST::process(design, current_ast, dump_ast1, dump_ast2, no_dump_ptr, dump_vlog1, dump_vlog2, dump_rtlil, false, false, false, false, false,
                     false, false, false, false, false, dont_redefine, false, defer, default_nettype_wire);
        delete current_ast;
    }
}

YOSYS_NAMESPACE_END
<commit_msg>Pass extra_args unless linking UHDM<commit_after>/*
 * Copyright 2020-2022 F4PGA Authors
 *
 * 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.
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 */

#include "uhdmcommonfrontend.h"

YOSYS_NAMESPACE_BEGIN

/* Stub for AST::process */
static void set_line_num(int) {}

/* Stub for AST::process */
static int get_line_num(void) { return 1; }

void UhdmCommonFrontend::print_read_options()
{
    log("    -noassert\n");
    log("        ignore assert() statements");
    log("\n");
    log("    -debug\n");
    log("        alias for -dump_ast1 -dump_ast2 -dump_vlog1 -dump_vlog2\n");
    log("\n");
    log("    -dump_ast1\n");
    log("        dump abstract syntax tree (before simplification)\n");
    log("\n");
    log("    -dump_ast2\n");
    log("        dump abstract syntax tree (after simplification)\n");
    log("\n");
    log("    -no_dump_ptr\n");
    log("        do not include hex memory addresses in dump (easier to diff dumps)\n");
    log("\n");
    log("    -dump_vlog1\n");
    log("        dump ast as Verilog code (before simplification)\n");
    log("\n");
    log("    -dump_vlog2\n");
    log("        dump ast as Verilog code (after simplification)\n");
    log("\n");
    log("    -dump_rtlil\n");
    log("        dump generated RTLIL netlist\n");
    log("\n");
    log("    -report [directory]\n");
    log("        write a coverage report for the UHDM file\n");
    log("\n");
    log("    -defer\n");
    log("        only read the abstract syntax tree and defer actual compilation\n");
    log("        to a later 'hierarchy' command. Useful in cases where the default\n");
    log("        parameters of modules yield invalid or not synthesizable code.\n");
    log("        Needs to be followed by read_systemverilog -link after reading\n");
    log("        all files.\n");
    log("    -link\n");
    log("        performs actual elaboration of the files read with -defer\n");
    log("    -parse-only\n");
    log("        this parameter only applies to read_systemverilog command,\n");
    log("        it runs only Surelog to parse design, but doesn't load generated\n");
    log("        tree into Yosys.\n");
    log("\n");
}

void UhdmCommonFrontend::execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design)
{
    this->call_log_header(design);
    this->args = args;

    bool defer = false;
    bool dump_ast1 = false;
    bool dump_ast2 = false;
    bool dump_vlog1 = false;
    bool dump_vlog2 = false;
    bool no_dump_ptr = false;
    bool dump_rtlil = false;
    std::vector<std::string> unhandled_args;

    for (size_t i = 0; i < args.size(); i++) {
        if (args[i] == "-debug") {
            dump_ast1 = true;
            dump_ast2 = true;
            dump_vlog1 = true;
            dump_vlog2 = true;
            this->shared.debug_flag = true;
        } else if (args[i] == "-report" && ++i < args.size()) {
            this->report_directory = args[i];
            this->shared.stop_on_error = false;
        } else if (args[i] == "-noassert") {
            this->shared.no_assert = true;
        } else if (args[i] == "-defer") {
            this->shared.defer = true;
        } else if (args[i] == "-dump_ast1") {
            dump_ast1 = true;
        } else if (args[i] == "-dump_ast2") {
            dump_ast2 = true;
        } else if (args[i] == "-dump_vlog1") {
            dump_vlog1 = true;
        } else if (args[i] == "-dump_vlog2") {
            dump_vlog2 = true;
        } else if (args[i] == "-no_dump_ptr") {
            no_dump_ptr = true;
        } else if (args[i] == "-dump_rtlil") {
            dump_rtlil = true;
        } else if (args[i] == "-parse-only") {
            this->shared.parse_only = true;
        } else if (args[i] == "-link") {
            this->shared.link = true;
            // Surelog needs it in the command line to link correctly
            unhandled_args.push_back(args[i]);
        } else {
            unhandled_args.push_back(args[i]);
        }
    }
    // Yosys gets confused when extra_args are passed with -link or no option
    // It's done fully by Surelog, so skip it in this case
    if (!this->shared.link)
        extra_args(f, filename, args, args.size() - 1);
    // pass only unhandled args to Surelog
    // unhandled args starts with command name,
    // but Surelog expects args[0] to be program name
    // and skips it
    this->args = unhandled_args;

    AST::current_filename = filename;
    AST::set_line_num = &set_line_num;
    AST::get_line_num = &get_line_num;

    bool dont_redefine = false;
    bool default_nettype_wire = true;

    AST::AstNode *current_ast = parse(filename);

    if (current_ast) {
        AST::process(design, current_ast, dump_ast1, dump_ast2, no_dump_ptr, dump_vlog1, dump_vlog2, dump_rtlil, false, false, false, false, false,
                     false, false, false, false, false, dont_redefine, false, defer, default_nettype_wire);
        delete current_ast;
    }
}

YOSYS_NAMESPACE_END
<|endoftext|>


<commit_before><commit_msg>coverity#707918 Uninitialized scalar field<commit_after><|endoftext|>


<commit_before>//
// Copyright (c) 2019, Wolfgang Merkt
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//  * Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
//  * Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//  * Neither the name of  nor the names of its contributors may be used to
//    endorse or promote products derived from this software without specific
//    prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//

#include <exotica_core/dynamics_solver.h>
#include <exotica_core/scene.h>

namespace exotica
{
template class AbstractDynamicsSolver<double, Eigen::Dynamic, Eigen::Dynamic>;

template <typename T, int NX, int NU>
AbstractDynamicsSolver<T, NX, NU>::AbstractDynamicsSolver() = default;

template <typename T, int NX, int NU>
AbstractDynamicsSolver<T, NX, NU>::~AbstractDynamicsSolver() = default;

template <typename T, int NX, int NU>
void AbstractDynamicsSolver<T, NX, NU>::InstantiateBase(const Initializer& init)
{
    Object::InstantiateObject(init);
    DynamicsSolverInitializer dynamics_solver_initializer = DynamicsSolverInitializer(init);
    this->SetDt(dynamics_solver_initializer.dt);
    this->SetIntegrator(dynamics_solver_initializer.Integrator);

    // Just store the control limits supplied.
    //  They will need to be reshaped to the correct size when num_velocities_ becomes known.
    raw_control_limits_low_ = dynamics_solver_initializer.ControlLimitsLow;
    raw_control_limits_high_ = dynamics_solver_initializer.ControlLimitsHigh;

    if (debug_) INFO_NAMED(object_name_, "Initialized DynamicsSolver of type " << GetObjectName() << " with dt=" << dynamics_solver_initializer.dt << " and integrator=" << dynamics_solver_initializer.Integrator);
}

template <typename T, int NX, int NU>
void AbstractDynamicsSolver<T, NX, NU>::AssignScene(std::shared_ptr<Scene> scene_in)
{
}

template <typename T, int NX, int NU>
void AbstractDynamicsSolver<T, NX, NU>::SetDt(double dt_in)
{
    if (dt_in < 0.0001) ThrowPretty("dt needs to be strictly greater than 0. Provided: " << dt_in);
    dt_ = dt_in;
}

template <typename T, int NX, int NU>
Eigen::Matrix<T, NX, 1> AbstractDynamicsSolver<T, NX, NU>::SimulateOneStep(const StateVector& x, const ControlVector& u)
{
    switch (integrator_)
    {
        // Forward Euler (RK1)
        case Integrator::RK1:
        {
            StateVector xdot = f(x, u);
            // TODO: This is the explicit RK1. We can switch to semi-implicit.
            return x + dt_ * xdot;
        }
        // Explicit trapezoid rule (RK2)
        case Integrator::RK2:
        {
            StateVector xdot0 = f(x, u);
            StateVector x1est = x + dt_ * xdot0;  // explicit Euler step
            StateVector xdot1 = f(x1est, u);

            // 2nd order result: x = x0 + dt (xd0+xd1)/2.
            return x + (dt_ / 2.) * (xdot0 + xdot1);
        }
        // Runge-Kutta 4
        case Integrator::RK4:
        {
            StateVector k1 = dt_ * f(x, u);
            StateVector k2 = dt_ * f(x + 0.5 * k1, u);
            StateVector k3 = dt_ * f(x + 0.5 * k2, u);
            StateVector k4 = dt_ * f(x + k3, u);
            StateVector dx = (k1 + k4) / 6. + (k2 + k3) / 3.;

            return x + dx;
        }
    };
    ThrowPretty("Not implemented!");
}

template <typename T, int NX, int NU>
void AbstractDynamicsSolver<T, NX, NU>::Integrate(const StateVector& x, const StateVector& dx, const double dt, StateVector& xout)
{
    switch (integrator_)
    {
        // Forward Euler (RK1) - explicit
        case Integrator::RK1:
        {
            xout = x + dt * dx;
        }
        break;

        default:
            ThrowPretty("Not implemented!");
            // TODO implement the other solvers, but how to get dx updated?!
    };
}

template <typename T, int NX, int NU>
Eigen::Matrix<T, NX, 1> AbstractDynamicsSolver<T, NX, NU>::Simulate(const StateVector& x, const ControlVector& u, T t)
{
    const int num_timesteps = static_cast<int>(t / dt_);
    StateVector x_t_plus_1 = x;
    for (int i = 0; i < num_timesteps; ++i)
    {
        x_t_plus_1 = SimulateOneStep(x_t_plus_1, u);
    }
    return x_t_plus_1;
}

template <typename T, int NX, int NU>
Eigen::Matrix<T, Eigen::Dynamic, 1> AbstractDynamicsSolver<T, NX, NU>::GetPosition(Eigen::VectorXdRefConst x_in)
{
    assert(x_in.size() == get_num_state());
    return x_in.head(num_positions_).eval();
}

template <typename T, int NX, int NU>
int AbstractDynamicsSolver<T, NX, NU>::get_num_controls() const
{
    return num_controls_;
}

template <typename T, int NX, int NU>
int AbstractDynamicsSolver<T, NX, NU>::get_num_positions() const
{
    return num_positions_;
}

template <typename T, int NX, int NU>
int AbstractDynamicsSolver<T, NX, NU>::get_num_velocities() const
{
    return num_velocities_;
}

template <typename T, int NX, int NU>
int AbstractDynamicsSolver<T, NX, NU>::get_num_state() const
{
    if (num_state_ == -1)
        return num_positions_ + num_velocities_;
    else
        return num_state_;
}

template <typename T, int NX, int NU>
int AbstractDynamicsSolver<T, NX, NU>::get_num_state_derivative() const
{
    if (num_state_derivative_ == -1)
        return 2 * num_velocities_;
    else
        return num_state_derivative_;
}

template <typename T, int NX, int NU>
T AbstractDynamicsSolver<T, NX, NU>::get_dt() const
{
    return dt_;
}

template <typename T, int NX, int NU>
Integrator AbstractDynamicsSolver<T, NX, NU>::get_integrator() const
{
    return integrator_;
}

template <typename T, int NX, int NU>
void AbstractDynamicsSolver<T, NX, NU>::set_integrator(Integrator integrator_in)
{
    integrator_ = integrator_in;
}

template <typename T, int NX, int NU>
void AbstractDynamicsSolver<T, NX, NU>::SetIntegrator(std::string integrator_in)
{
    if (integrator_in == "RK1")
        integrator_ = Integrator::RK1;
    else if (integrator_in == "RK2")
        integrator_ = Integrator::RK2;
    else if (integrator_in == "RK4")
        integrator_ = Integrator::RK4;
    else
        ThrowPretty("Unknown integrator: " << integrator_in);
}

template <typename T, int NX, int NU>
const Eigen::MatrixXd& AbstractDynamicsSolver<T, NX, NU>::get_control_limits()
{
    if (!control_limits_initialized_)
        set_control_limits(raw_control_limits_low_, raw_control_limits_high_);
    return control_limits_;
}

template <typename T, int NX, int NU>
void AbstractDynamicsSolver<T, NX, NU>::set_control_limits(Eigen::VectorXdRefConst control_limits_low, Eigen::VectorXdRefConst control_limits_high)
{
    if (num_controls_ == -1)
        ThrowPretty("Attempting to set control limits before num_controls is set.");

    control_limits_initialized_ = true;
    control_limits_ = Eigen::MatrixXd(num_controls_, 2);

    if (control_limits_low.size() == num_controls_)
        control_limits_.col(0) = control_limits_low;
    else if (control_limits_low.size() == 1)
        control_limits_.col(0) = Eigen::VectorXd::Constant(num_controls_, control_limits_low(0));
    else
        ThrowPretty("Wrong control limits (low) size. Should either be 1 or " << num_controls_);

    if (control_limits_high.size() == num_controls_)
        control_limits_.col(1) = control_limits_high;
    else if (control_limits_high.size() == 1)
        control_limits_.col(1) = Eigen::VectorXd::Constant(num_controls_, control_limits_high(0));
    else
        ThrowPretty("Wrong control limits (high) size. Should either be 1 or " << num_controls_);
}

template <typename T, int NX, int NU>
void AbstractDynamicsSolver<T, NX, NU>::InitializeSecondOrderDerivatives()
{
    if (second_order_derivatives_initialized_)
        return;

    const int nx = get_num_state();
    const int ndx = get_num_state_derivative();

    fxx_default_ = Eigen::Tensor<T, 3>(ndx, ndx, ndx);
    fxx_default_.setZero();

    fuu_default_ = Eigen::Tensor<T, 3>(ndx, num_controls_, num_controls_);
    fuu_default_.setZero();

    fxu_default_ = Eigen::Tensor<T, 3>(ndx, ndx, num_controls_);
    fxu_default_.setZero();

    second_order_derivatives_initialized_ = true;
}

template <typename T, int NX, int NU>
Eigen::Tensor<T, 3> AbstractDynamicsSolver<T, NX, NU>::fxx(const StateVector& x, const ControlVector& u)
{
    if (!second_order_derivatives_initialized_) InitializeSecondOrderDerivatives();
    return fxx_default_;
}

template <typename T, int NX, int NU>
Eigen::Tensor<T, 3> AbstractDynamicsSolver<T, NX, NU>::fuu(const StateVector& x, const ControlVector& u)
{
    if (!second_order_derivatives_initialized_) InitializeSecondOrderDerivatives();
    return fuu_default_;
}

template <typename T, int NX, int NU>
Eigen::Tensor<T, 3> AbstractDynamicsSolver<T, NX, NU>::fxu(const StateVector& x, const ControlVector& u)
{
    if (!second_order_derivatives_initialized_) InitializeSecondOrderDerivatives();
    return fxu_default_;
}

template <typename T, int NX, int NU>
Eigen::Matrix<T, NU, 1> AbstractDynamicsSolver<T, NX, NU>::InverseDynamics(const StateVector& state)
{
    ThrowPretty("This dynamics solver does not support inverse dynamics!");
}

template <typename T, int NX, int NU>
Eigen::Matrix<T, NX, NX> AbstractDynamicsSolver<T, NX, NU>::fx(const StateVector& x, const ControlVector& u)
{
    const int nx = get_num_state();
    const int ndx = get_num_state_derivative();

    // Finite differences
    Eigen::MatrixXd fx_fd(ndx, ndx);
    constexpr double eps = 1e-6;
    Eigen::VectorXd x_low(nx), x_high(nx), xdiff(ndx);
    for (int i = 0; i < ndx; ++i)
    {
        xdiff.setZero();
        xdiff(i) = eps / 2.0;

        Integrate(x, xdiff, -1., x_low);
        Integrate(x, xdiff, 1., x_high);

        fx_fd.col(i) = StateDelta(f(x_high, u), f(x_low, u)) / eps;
    }

    return fx_fd;
}

template <typename T, int NX, int NU>
Eigen::Matrix<T, NX, NU> AbstractDynamicsSolver<T, NX, NU>::fu(const StateVector& x, const ControlVector& u)
{
    const int ndx = get_num_state_derivative();

    // Finite differences
    constexpr double eps = 1e-6;
    Eigen::MatrixXd fu_fd(ndx, num_controls_);
    Eigen::VectorXd u_low(num_controls_), u_high(num_controls_);
    for (int i = 0; i < num_controls_; ++i)
    {
        u_low = u;
        u_high = u;
        u_low(i) -= eps / 2.0;
        u_high(i) += eps / 2.0;

        fu_fd.col(i) = StateDelta(f(x, u_high), f(x, u_low)) / eps;
    }

    return fu_fd;
}

template <typename T, int NX, int NU>
void AbstractDynamicsSolver<T, NX, NU>::ComputeDerivatives(const StateVector& x, const ControlVector& u)
{
    fx_ = fx(x, u);
    fu_ = fu(x, u);
}

template <typename T, int NX, int NU>
const Eigen::Matrix<T, NX, NX>& AbstractDynamicsSolver<T, NX, NU>::get_fx() const
{
    return fx_;
}

template <typename T, int NX, int NU>
const Eigen::Matrix<T, NX, NU>& AbstractDynamicsSolver<T, NX, NU>::get_fu() const
{
    return fu_;
}

}  // namespace exotica
<commit_msg>[exotica_core] DynamicsSolver: Fix finite differencing bug<commit_after>//
// Copyright (c) 2019, Wolfgang Merkt
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//  * Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
//  * Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//  * Neither the name of  nor the names of its contributors may be used to
//    endorse or promote products derived from this software without specific
//    prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//

#include <exotica_core/dynamics_solver.h>
#include <exotica_core/scene.h>

namespace exotica
{
template class AbstractDynamicsSolver<double, Eigen::Dynamic, Eigen::Dynamic>;

template <typename T, int NX, int NU>
AbstractDynamicsSolver<T, NX, NU>::AbstractDynamicsSolver() = default;

template <typename T, int NX, int NU>
AbstractDynamicsSolver<T, NX, NU>::~AbstractDynamicsSolver() = default;

template <typename T, int NX, int NU>
void AbstractDynamicsSolver<T, NX, NU>::InstantiateBase(const Initializer& init)
{
    Object::InstantiateObject(init);
    DynamicsSolverInitializer dynamics_solver_initializer = DynamicsSolverInitializer(init);
    this->SetDt(dynamics_solver_initializer.dt);
    this->SetIntegrator(dynamics_solver_initializer.Integrator);

    // Just store the control limits supplied.
    //  They will need to be reshaped to the correct size when num_velocities_ becomes known.
    raw_control_limits_low_ = dynamics_solver_initializer.ControlLimitsLow;
    raw_control_limits_high_ = dynamics_solver_initializer.ControlLimitsHigh;

    if (debug_) INFO_NAMED(object_name_, "Initialized DynamicsSolver of type " << GetObjectName() << " with dt=" << dynamics_solver_initializer.dt << " and integrator=" << dynamics_solver_initializer.Integrator);
}

template <typename T, int NX, int NU>
void AbstractDynamicsSolver<T, NX, NU>::AssignScene(std::shared_ptr<Scene> scene_in)
{
}

template <typename T, int NX, int NU>
void AbstractDynamicsSolver<T, NX, NU>::SetDt(double dt_in)
{
    if (dt_in < 0.0001) ThrowPretty("dt needs to be strictly greater than 0. Provided: " << dt_in);
    dt_ = dt_in;
}

template <typename T, int NX, int NU>
Eigen::Matrix<T, NX, 1> AbstractDynamicsSolver<T, NX, NU>::SimulateOneStep(const StateVector& x, const ControlVector& u)
{
    switch (integrator_)
    {
        // Forward Euler (RK1)
        case Integrator::RK1:
        {
            StateVector xdot = f(x, u);
            // TODO: This is the explicit RK1. We can switch to semi-implicit.
            return x + dt_ * xdot;
        }
        // Explicit trapezoid rule (RK2)
        case Integrator::RK2:
        {
            StateVector xdot0 = f(x, u);
            StateVector x1est = x + dt_ * xdot0;  // explicit Euler step
            StateVector xdot1 = f(x1est, u);

            // 2nd order result: x = x0 + dt (xd0+xd1)/2.
            return x + (dt_ / 2.) * (xdot0 + xdot1);
        }
        // Runge-Kutta 4
        case Integrator::RK4:
        {
            StateVector k1 = dt_ * f(x, u);
            StateVector k2 = dt_ * f(x + 0.5 * k1, u);
            StateVector k3 = dt_ * f(x + 0.5 * k2, u);
            StateVector k4 = dt_ * f(x + k3, u);
            StateVector dx = (k1 + k4) / 6. + (k2 + k3) / 3.;

            return x + dx;
        }
    };
    ThrowPretty("Not implemented!");
}

template <typename T, int NX, int NU>
void AbstractDynamicsSolver<T, NX, NU>::Integrate(const StateVector& x, const StateVector& dx, const double dt, StateVector& xout)
{
    switch (integrator_)
    {
        // Forward Euler (RK1) - explicit
        case Integrator::RK1:
        {
            xout = x + dt * dx;
        }
        break;

        default:
            ThrowPretty("Not implemented!");
            // TODO implement the other solvers, but how to get dx updated?!
    };
}

template <typename T, int NX, int NU>
Eigen::Matrix<T, NX, 1> AbstractDynamicsSolver<T, NX, NU>::Simulate(const StateVector& x, const ControlVector& u, T t)
{
    const int num_timesteps = static_cast<int>(t / dt_);
    StateVector x_t_plus_1 = x;
    for (int i = 0; i < num_timesteps; ++i)
    {
        x_t_plus_1 = SimulateOneStep(x_t_plus_1, u);
    }
    return x_t_plus_1;
}

template <typename T, int NX, int NU>
Eigen::Matrix<T, Eigen::Dynamic, 1> AbstractDynamicsSolver<T, NX, NU>::GetPosition(Eigen::VectorXdRefConst x_in)
{
    assert(x_in.size() == get_num_state());
    return x_in.head(num_positions_).eval();
}

template <typename T, int NX, int NU>
int AbstractDynamicsSolver<T, NX, NU>::get_num_controls() const
{
    return num_controls_;
}

template <typename T, int NX, int NU>
int AbstractDynamicsSolver<T, NX, NU>::get_num_positions() const
{
    return num_positions_;
}

template <typename T, int NX, int NU>
int AbstractDynamicsSolver<T, NX, NU>::get_num_velocities() const
{
    return num_velocities_;
}

template <typename T, int NX, int NU>
int AbstractDynamicsSolver<T, NX, NU>::get_num_state() const
{
    if (num_state_ == -1)
        return num_positions_ + num_velocities_;
    else
        return num_state_;
}

template <typename T, int NX, int NU>
int AbstractDynamicsSolver<T, NX, NU>::get_num_state_derivative() const
{
    if (num_state_derivative_ == -1)
        return 2 * num_velocities_;
    else
        return num_state_derivative_;
}

template <typename T, int NX, int NU>
T AbstractDynamicsSolver<T, NX, NU>::get_dt() const
{
    return dt_;
}

template <typename T, int NX, int NU>
Integrator AbstractDynamicsSolver<T, NX, NU>::get_integrator() const
{
    return integrator_;
}

template <typename T, int NX, int NU>
void AbstractDynamicsSolver<T, NX, NU>::set_integrator(Integrator integrator_in)
{
    integrator_ = integrator_in;
}

template <typename T, int NX, int NU>
void AbstractDynamicsSolver<T, NX, NU>::SetIntegrator(std::string integrator_in)
{
    if (integrator_in == "RK1")
        integrator_ = Integrator::RK1;
    else if (integrator_in == "RK2")
        integrator_ = Integrator::RK2;
    else if (integrator_in == "RK4")
        integrator_ = Integrator::RK4;
    else
        ThrowPretty("Unknown integrator: " << integrator_in);
}

template <typename T, int NX, int NU>
const Eigen::MatrixXd& AbstractDynamicsSolver<T, NX, NU>::get_control_limits()
{
    if (!control_limits_initialized_)
        set_control_limits(raw_control_limits_low_, raw_control_limits_high_);
    return control_limits_;
}

template <typename T, int NX, int NU>
void AbstractDynamicsSolver<T, NX, NU>::set_control_limits(Eigen::VectorXdRefConst control_limits_low, Eigen::VectorXdRefConst control_limits_high)
{
    if (num_controls_ == -1)
        ThrowPretty("Attempting to set control limits before num_controls is set.");

    control_limits_initialized_ = true;
    control_limits_ = Eigen::MatrixXd(num_controls_, 2);

    if (control_limits_low.size() == num_controls_)
        control_limits_.col(0) = control_limits_low;
    else if (control_limits_low.size() == 1)
        control_limits_.col(0) = Eigen::VectorXd::Constant(num_controls_, control_limits_low(0));
    else
        ThrowPretty("Wrong control limits (low) size. Should either be 1 or " << num_controls_);

    if (control_limits_high.size() == num_controls_)
        control_limits_.col(1) = control_limits_high;
    else if (control_limits_high.size() == 1)
        control_limits_.col(1) = Eigen::VectorXd::Constant(num_controls_, control_limits_high(0));
    else
        ThrowPretty("Wrong control limits (high) size. Should either be 1 or " << num_controls_);
}

template <typename T, int NX, int NU>
void AbstractDynamicsSolver<T, NX, NU>::InitializeSecondOrderDerivatives()
{
    if (second_order_derivatives_initialized_)
        return;

    const int nx = get_num_state();
    const int ndx = get_num_state_derivative();

    fxx_default_ = Eigen::Tensor<T, 3>(ndx, ndx, ndx);
    fxx_default_.setZero();

    fuu_default_ = Eigen::Tensor<T, 3>(ndx, num_controls_, num_controls_);
    fuu_default_.setZero();

    fxu_default_ = Eigen::Tensor<T, 3>(ndx, ndx, num_controls_);
    fxu_default_.setZero();

    second_order_derivatives_initialized_ = true;
}

template <typename T, int NX, int NU>
Eigen::Tensor<T, 3> AbstractDynamicsSolver<T, NX, NU>::fxx(const StateVector& x, const ControlVector& u)
{
    if (!second_order_derivatives_initialized_) InitializeSecondOrderDerivatives();
    return fxx_default_;
}

template <typename T, int NX, int NU>
Eigen::Tensor<T, 3> AbstractDynamicsSolver<T, NX, NU>::fuu(const StateVector& x, const ControlVector& u)
{
    if (!second_order_derivatives_initialized_) InitializeSecondOrderDerivatives();
    return fuu_default_;
}

template <typename T, int NX, int NU>
Eigen::Tensor<T, 3> AbstractDynamicsSolver<T, NX, NU>::fxu(const StateVector& x, const ControlVector& u)
{
    if (!second_order_derivatives_initialized_) InitializeSecondOrderDerivatives();
    return fxu_default_;
}

template <typename T, int NX, int NU>
Eigen::Matrix<T, NU, 1> AbstractDynamicsSolver<T, NX, NU>::InverseDynamics(const StateVector& state)
{
    ThrowPretty("This dynamics solver does not support inverse dynamics!");
}

template <typename T, int NX, int NU>
Eigen::Matrix<T, NX, NX> AbstractDynamicsSolver<T, NX, NU>::fx(const StateVector& x, const ControlVector& u)
{
    const int nx = get_num_state();
    const int ndx = get_num_state_derivative();

    // Finite differences
    Eigen::MatrixXd fx_fd(ndx, ndx);
    constexpr double eps = 1e-6;
    Eigen::VectorXd x_low(nx), x_high(nx), xdiff(ndx);
    for (int i = 0; i < ndx; ++i)
    {
        xdiff.setZero();
        xdiff(i) = eps / 2.0;

        Integrate(x, xdiff, -1., x_low);
        Integrate(x, xdiff, 1., x_high);

        fx_fd.col(i) = (f(x_high, u) - f(x_low, u)) / eps;
    }

    return fx_fd;
}

template <typename T, int NX, int NU>
Eigen::Matrix<T, NX, NU> AbstractDynamicsSolver<T, NX, NU>::fu(const StateVector& x, const ControlVector& u)
{
    const int ndx = get_num_state_derivative();

    // Finite differences
    constexpr double eps = 1e-6;
    Eigen::MatrixXd fu_fd(ndx, num_controls_);
    Eigen::VectorXd u_low(num_controls_), u_high(num_controls_);
    for (int i = 0; i < num_controls_; ++i)
    {
        u_low = u;
        u_high = u;
        u_low(i) -= eps / 2.0;
        u_high(i) += eps / 2.0;

        fu_fd.col(i) = (f(x, u_high) - f(x, u_low)) / eps;
    }

    return fu_fd;
}

template <typename T, int NX, int NU>
void AbstractDynamicsSolver<T, NX, NU>::ComputeDerivatives(const StateVector& x, const ControlVector& u)
{
    fx_ = fx(x, u);
    fu_ = fu(x, u);
}

template <typename T, int NX, int NU>
const Eigen::Matrix<T, NX, NX>& AbstractDynamicsSolver<T, NX, NU>::get_fx() const
{
    return fx_;
}

template <typename T, int NX, int NU>
const Eigen::Matrix<T, NX, NU>& AbstractDynamicsSolver<T, NX, NU>::get_fu() const
{
    return fu_;
}

}  // namespace exotica
<|endoftext|>