<commit_before>#include <semaphore.h>
#include <fcntl.h>
#include <sys/stat.h>

#include <mist/defines.h>
#include "input.h"
#include <sstream>
#include <fstream>
#include <iterator>

namespace Mist {
  Input * Input::singleton = NULL;

  void Input::userCallback(char * data, size_t len, unsigned int id){
    long tid = ((long)(data[0]) << 24) | ((long)(data[1]) << 16) | ((long)(data[2]) << 8) | ((long)(data[3]));
    long keyNum = ((long)(data[4]) << 8) | ((long)(data[5]));
    bufferFrame(tid, keyNum + 1);//Try buffer next frame
  }

  void Input::doNothing(char * data, size_t len, unsigned int id){
    DEBUG_MSG(DLVL_DONTEVEN, "Doing 'nothing'");
    for (int i = 0; i < 5; i++){
      int tmp = ((long)(data[i*6]) << 24) | ((long)(data[i*6 + 1]) << 16) | ((long)(data[i*6 + 2]) << 8) | data[i*6 + 3];
      if (tmp){
        singleton->userCallback(data + (i*6), 6, id);//call the userCallback for this input
      }
    }
  }

  Input::Input(Util::Config * cfg) {
    config = cfg;
    JSON::Value option;
    option["long"] = "json";
    option["short"] = "j";
    option["help"] = "Output MistIn info in JSON format, then exit.";
    option["value"].append(0ll);
    config->addOption("json", option);
    option.null();
    option["arg_num"] = 1ll;
    option["arg"] = "string";
    option["help"] = "Name of the input file or - for stdin";
    option["value"].append("-");
    config->addOption("input", option);
    option.null();
    option["arg_num"] = 2ll;
    option["arg"] = "string";
    option["help"] = "Name of the output file or - for stdout";
    option["value"].append("-");
    config->addOption("output", option);
    option.null();
    option["arg"] = "string";
    option["short"] = "s";
    option["long"] = "stream";
    option["help"] = "The name of the stream that this connector will transmit.";
    config->addOption("streamname", option);
    option.null();
    option["short"] = "p";
    option["long"] = "player";
    option["help"] = "Makes this connector into a player";
    config->addOption("player", option);

    packTime = 0;
    lastActive = Util::epoch();
    playing = 0;
    playUntil = 0;

    singleton = this;
    isBuffer = false;
  }

  int Input::run() {
    if (config->getBool("json")) {
      std::cerr << capa.toString() << std::endl;
      return 0;
    }
    if (!setup()) {
      std::cerr << config->getString("cmd") << " setup failed." << std::endl;
      return 0;
    }
    if (!readHeader()) {
      std::cerr << "Reading header for " << config->getString("input") << " failed." << std::endl;
      return 0;
    }
    parseHeader();

    if (!config->getBool("player")){
      //check filename for no -
      if (config->getString("output") != "-"){
        //output to dtsc
        DTSC::Meta newMeta = myMeta;
        newMeta.reset();
        JSON::Value tempVal;
        std::ofstream file(config->getString("output").c_str());
        long long int bpos = 0;
        seek(0);
        getNext();
        while (lastPack){
          tempVal = lastPack.toJSON();
          tempVal["bpos"] = bpos;
          newMeta.update(tempVal);
          file << std::string(lastPack.getData(), lastPack.getDataLen());
          bpos += lastPack.getDataLen();
          getNext();
        }
        //close file
        file.close();
        //create header
        file.open((config->getString("output")+".dtsh").c_str());
        file << newMeta.toJSON().toNetPacked();
        file.close();
      }else{
        DEBUG_MSG(DLVL_FAIL,"No filename specified, exiting");
      }
    }else{
      //after this player functionality

      metaPage.init(config->getString("streamname"), (isBuffer ? 8388608 : myMeta.getSendLen()), true);
      myMeta.writeTo(metaPage.mapped);
      userPage.init(config->getString("streamname") + "_users", 30, true);

      if (!isBuffer){
        for (std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
          bufferFrame(it->first, 1);
        }
      }

      sem_t * waiting = sem_open(std::string("/wait_" + config->getString("streamname")).c_str(), O_CREAT | O_RDWR, ACCESSPERMS, 0);
      if (waiting == SEM_FAILED){
        DEBUG_MSG(DLVL_FAIL, "Failed to open semaphore - cancelling");
        return -1;
      }
      sem_post(waiting);
      sem_close(waiting);

      DEBUG_MSG(DLVL_HIGH,"Pre-While");

      long long int activityCounter = Util::getMS();
      while ((Util::getMS() - activityCounter) < 10000){//1minute timeout
        DEBUG_MSG(DLVL_HIGH, "Timer running");
        Util::sleep(1000);
        removeUnused();
        userPage.parseEach(doNothing);
        if (userPage.amount){
          activityCounter = Util::getMS();
          DEBUG_MSG(DLVL_HIGH, "Connected users: %d", userPage.amount);
        }
      }
      DEBUG_MSG(DLVL_DEVEL,"Closing clean");
      //end player functionality
    }
    return 0;
  }

  void Input::removeUnused(){
    for (std::map<unsigned int, std::map<unsigned int, unsigned int> >::iterator it = pageCounter.begin(); it != pageCounter.end(); it++){
      for (std::map<unsigned int, unsigned int>::iterator it2 = it->second.begin(); it2 != it->second.end(); it2++){
        it2->second--;
      }
      bool change = true;
      while (change){
        change = false;
        for (std::map<unsigned int, unsigned int>::iterator it2 = it->second.begin(); it2 != it->second.end(); it2++){
          if (!it2->second){
            pageCounter[it->first].erase(it2->first);
            dataPages[it->first].erase(it2->first);
            change = true;
            break;
          }
        }
      }
    }
  }

  void Input::parseHeader(){
    DEBUG_MSG(DLVL_DEVEL,"Parsing the header");
    //Select all tracks for parsing header
    selectedTracks.clear();
    std::stringstream trackSpec;
    for (std::map<int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) {
      DEBUG_MSG(DLVL_VERYHIGH, "Track %d encountered", it->first);
      //selectedTracks.insert(it->first);
      if (trackSpec.str() != ""){
        trackSpec << " ";
      }
      trackSpec << it->first;
      DEBUG_MSG(DLVL_VERYHIGH, "Trackspec now %s", trackSpec.str().c_str());
      for (std::deque<DTSC::Key>::iterator it2 = it->second.keys.begin(); it2 != it->second.keys.end(); it2++){
        keyTimes[it->first].insert(it2->getTime());
      }
    }
    trackSelect(trackSpec.str());

    std::map<int, DTSCPageData> curData;
    std::map<int, booking> bookKeeping;

    seek(0);
    getNext();

    while(lastPack){//loop through all
      int tid = lastPack.getTrackId();
      if (!bookKeeping.count(tid)){
        bookKeeping[tid].first = 1;
        bookKeeping[tid].curPart = 0;
        bookKeeping[tid].curKey = 0;

        curData[tid].lastKeyTime = 0xFFFFFFFF;
        curData[tid].keyNum = 1;
        curData[tid].partNum = 0;
        curData[tid].dataSize = 0;
        curData[tid].curOffset = 0;
        curData[tid].firstTime = myMeta.tracks[tid].keys[0].getTime();

        char tmpId[20];
        sprintf(tmpId, "%d", tid);
        indexPages[tid].init(config->getString("streamname") + tmpId, 8192, true);//Pages of 8kb in size, room for 512 parts.
      }
      if (myMeta.tracks[tid].keys[bookKeeping[tid].curKey].getParts() == curData[tid].partNum){
        if (curData[tid].dataSize > 8388608) {
          pagesByTrack[tid][bookKeeping[tid].first] = curData[tid];
          bookKeeping[tid].first += curData[tid].keyNum;
          curData[tid].keyNum = 0;
          curData[tid].dataSize = 0;
          curData[tid].firstTime = myMeta.tracks[tid].keys[bookKeeping[tid].curKey].getTime();
        }
        bookKeeping[tid].curKey++;
        curData[tid].keyNum++;
        curData[tid].partNum = 0;
      }
      curData[tid].dataSize += lastPack.getDataLen();
      curData[tid].partNum ++;
      bookKeeping[tid].curPart ++;
      getNext(false);
    }
    for (std::map<int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) {
      if (curData.count(it->first) && !pagesByTrack[it->first].count(bookKeeping[it->first].first)){
        pagesByTrack[it->first][bookKeeping[it->first].first] = curData[it->first];
      }
      if (!pagesByTrack.count(it->first)){
        DEBUG_MSG(DLVL_WARN, "No pages for track %d found", it->first);
      }else{
        DEBUG_MSG(DLVL_DEVEL, "Track %d (%s) split into %lu pages", it->first, myMeta.tracks[it->first].codec.c_str(), pagesByTrack[it->first].size());
        for (std::map<int, DTSCPageData>::iterator it2 = pagesByTrack[it->first].begin(); it2 != pagesByTrack[it->first].end(); it2++){
          DEBUG_MSG(DLVL_DEVEL, "Page %u-%u", it2->first, it2->first + it2->second.keyNum - 1);
        }
      }
    }
  }

  bool Input::bufferFrame(int track, int keyNum){
    if (keyNum < 1){keyNum = 1;}
    if (!pagesByTrack.count(track)){
      return false;
    }
    std::map<int, DTSCPageData>::iterator it = pagesByTrack[track].upper_bound(keyNum);
    if (it != pagesByTrack[track].begin()){
      it--;
    }
    int pageNum = it->first;
    pageCounter[track][pageNum] = 15;///Keep page 15seconds in memory after last use

    DEBUG_MSG(DLVL_DONTEVEN, "Attempting to buffer page %d key %d->%d", track, keyNum, pageNum);
    if (dataPages[track].count(pageNum)){
      return true;
    }
    char pageId[100];
    int pageIdLen = sprintf(pageId, "%s%d_%d", config->getString("streamname").c_str(), track, pageNum);
    std::string tmpString(pageId, pageIdLen);
    dataPages[track][pageNum].init(tmpString, it->second.dataSize, true);
    DEBUG_MSG(DLVL_DEVEL, "Buffering page %d through %d / %lu", pageNum, pageNum-1 + it->second.keyNum, myMeta.tracks[track].keys.size());

    std::stringstream trackSpec;
    trackSpec << track;
    trackSelect(trackSpec.str());
    seek(myMeta.tracks[track].keys[pageNum-1].getTime());
    long long unsigned int stopTime = myMeta.tracks[track].lastms + 1;
    if ((int)myMeta.tracks[track].keys.size() > pageNum-1 + it->second.keyNum){
      stopTime = myMeta.tracks[track].keys[pageNum-1 + it->second.keyNum].getTime();
    }
    DEBUG_MSG(DLVL_HIGH, "Playing from %ld to %llu", myMeta.tracks[track].keys[pageNum-1].getTime(), stopTime);
    it->second.curOffset = 0;
    getNext();
    while (lastPack && lastPack.getTime() < stopTime){
      if (it->second.curOffset + lastPack.getDataLen() > pagesByTrack[track][pageNum].dataSize){
        DEBUG_MSG(DLVL_WARN, "Trying to write %u bytes on pos %llu where size is %llu (time: %llu / %llu, track %u page %u)", lastPack.getDataLen(), it->second.curOffset, pagesByTrack[track][pageNum].dataSize, lastPack.getTime(), stopTime, track, pageNum);
        break;
      }else{
        memcpy(dataPages[track][pageNum].mapped + it->second.curOffset, lastPack.getData(), lastPack.getDataLen());
        it->second.curOffset += lastPack.getDataLen();
      }
      getNext();
    }
    for (int i = 0; i < indexPages[track].len / 8; i++){
      if (((long long int*)indexPages[track].mapped)[i] == 0){
        ((long long int*)indexPages[track].mapped)[i] = (((long long int)htonl(pageNum)) << 32) | htonl(it->second.keyNum);
        break;
      }
    }
    return true;
  }

  bool Input::atKeyFrame(){
    static std::map<int, int> lastSeen;
    //not in keyTimes? We're not at a keyframe.
    unsigned int c = keyTimes[lastPack.getTrackId()].count(lastPack.getTime());
    if (!c){
      return false;
    }
    //skip double times
    if (lastSeen.count(lastPack.getTrackId()) && lastSeen[lastPack.getTrackId()] == lastPack.getTime()){
      return false;
    }
    //set last seen, and return true
    lastSeen[lastPack.getTrackId()] = lastPack.getTime();
    return true;
  }

  void Input::play(int until) {
    playing = -1;
    playUntil = until;
    initialTime = 0;
    benchMark = Util::getMS();
  }

  void Input::playOnce() {
    if (playing <= 0) {
      playing = 1;
    }
    ++playing;
    benchMark = Util::getMS();
  }

  void Input::quitPlay() {
    playing = 0;
  }
}

<commit_msg>Debug message tweaks.<commit_after>#include <semaphore.h>
#include <fcntl.h>
#include <sys/stat.h>

#include <mist/defines.h>
#include "input.h"
#include <sstream>
#include <fstream>
#include <iterator>

namespace Mist {
  Input * Input::singleton = NULL;

  void Input::userCallback(char * data, size_t len, unsigned int id){
    long tid = ((long)(data[0]) << 24) | ((long)(data[1]) << 16) | ((long)(data[2]) << 8) | ((long)(data[3]));
    long keyNum = ((long)(data[4]) << 8) | ((long)(data[5]));
    bufferFrame(tid, keyNum + 1);//Try buffer next frame
  }

  void Input::doNothing(char * data, size_t len, unsigned int id){
    DEBUG_MSG(DLVL_DONTEVEN, "Doing 'nothing'");
    for (int i = 0; i < 5; i++){
      int tmp = ((long)(data[i*6]) << 24) | ((long)(data[i*6 + 1]) << 16) | ((long)(data[i*6 + 2]) << 8) | data[i*6 + 3];
      if (tmp){
        singleton->userCallback(data + (i*6), 6, id);//call the userCallback for this input
      }
    }
  }

  Input::Input(Util::Config * cfg) {
    config = cfg;
    JSON::Value option;
    option["long"] = "json";
    option["short"] = "j";
    option["help"] = "Output MistIn info in JSON format, then exit.";
    option["value"].append(0ll);
    config->addOption("json", option);
    option.null();
    option["arg_num"] = 1ll;
    option["arg"] = "string";
    option["help"] = "Name of the input file or - for stdin";
    option["value"].append("-");
    config->addOption("input", option);
    option.null();
    option["arg_num"] = 2ll;
    option["arg"] = "string";
    option["help"] = "Name of the output file or - for stdout";
    option["value"].append("-");
    config->addOption("output", option);
    option.null();
    option["arg"] = "string";
    option["short"] = "s";
    option["long"] = "stream";
    option["help"] = "The name of the stream that this connector will transmit.";
    config->addOption("streamname", option);
    option.null();
    option["short"] = "p";
    option["long"] = "player";
    option["help"] = "Makes this connector into a player";
    config->addOption("player", option);

    packTime = 0;
    lastActive = Util::epoch();
    playing = 0;
    playUntil = 0;

    singleton = this;
    isBuffer = false;
  }

  int Input::run() {
    if (config->getBool("json")) {
      std::cerr << capa.toString() << std::endl;
      return 0;
    }
    if (!setup()) {
      std::cerr << config->getString("cmd") << " setup failed." << std::endl;
      return 0;
    }
    if (!readHeader()) {
      std::cerr << "Reading header for " << config->getString("input") << " failed." << std::endl;
      return 0;
    }
    parseHeader();

    if (!config->getBool("player")){
      //check filename for no -
      if (config->getString("output") != "-"){
        //output to dtsc
        DTSC::Meta newMeta = myMeta;
        newMeta.reset();
        JSON::Value tempVal;
        std::ofstream file(config->getString("output").c_str());
        long long int bpos = 0;
        seek(0);
        getNext();
        while (lastPack){
          tempVal = lastPack.toJSON();
          tempVal["bpos"] = bpos;
          newMeta.update(tempVal);
          file << std::string(lastPack.getData(), lastPack.getDataLen());
          bpos += lastPack.getDataLen();
          getNext();
        }
        //close file
        file.close();
        //create header
        file.open((config->getString("output")+".dtsh").c_str());
        file << newMeta.toJSON().toNetPacked();
        file.close();
      }else{
        DEBUG_MSG(DLVL_FAIL,"No filename specified, exiting");
      }
    }else{
      //after this player functionality

      metaPage.init(config->getString("streamname"), (isBuffer ? 8388608 : myMeta.getSendLen()), true);
      myMeta.writeTo(metaPage.mapped);
      userPage.init(config->getString("streamname") + "_users", 30, true);

      if (!isBuffer){
        for (std::map<int,DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++){
          bufferFrame(it->first, 1);
        }
      }

      sem_t * waiting = sem_open(std::string("/wait_" + config->getString("streamname")).c_str(), O_CREAT | O_RDWR, ACCESSPERMS, 0);
      if (waiting == SEM_FAILED){
        DEBUG_MSG(DLVL_FAIL, "Failed to open semaphore - cancelling");
        return -1;
      }
      sem_post(waiting);
      sem_close(waiting);

      DEBUG_MSG(DLVL_HIGH,"Pre-While");

      long long int activityCounter = Util::getMS();
      while ((Util::getMS() - activityCounter) < 10000){//1minute timeout
        DEBUG_MSG(DLVL_HIGH, "Timer running");
        Util::sleep(1000);
        removeUnused();
        userPage.parseEach(doNothing);
        if (userPage.amount){
          activityCounter = Util::getMS();
          DEBUG_MSG(DLVL_HIGH, "Connected users: %d", userPage.amount);
        }
      }
      DEBUG_MSG(DLVL_DEVEL,"Closing clean");
      //end player functionality
    }
    return 0;
  }

  void Input::removeUnused(){
    for (std::map<unsigned int, std::map<unsigned int, unsigned int> >::iterator it = pageCounter.begin(); it != pageCounter.end(); it++){
      for (std::map<unsigned int, unsigned int>::iterator it2 = it->second.begin(); it2 != it->second.end(); it2++){
        it2->second--;
      }
      bool change = true;
      while (change){
        change = false;
        for (std::map<unsigned int, unsigned int>::iterator it2 = it->second.begin(); it2 != it->second.end(); it2++){
          if (!it2->second){
            pageCounter[it->first].erase(it2->first);
            dataPages[it->first].erase(it2->first);
            change = true;
            break;
          }
        }
      }
    }
  }

  void Input::parseHeader(){
    DEBUG_MSG(DLVL_DEVEL,"Parsing the header");
    //Select all tracks for parsing header
    selectedTracks.clear();
    std::stringstream trackSpec;
    for (std::map<int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) {
      DEBUG_MSG(DLVL_VERYHIGH, "Track %d encountered", it->first);
      //selectedTracks.insert(it->first);
      if (trackSpec.str() != ""){
        trackSpec << " ";
      }
      trackSpec << it->first;
      DEBUG_MSG(DLVL_VERYHIGH, "Trackspec now %s", trackSpec.str().c_str());
      for (std::deque<DTSC::Key>::iterator it2 = it->second.keys.begin(); it2 != it->second.keys.end(); it2++){
        keyTimes[it->first].insert(it2->getTime());
      }
    }
    trackSelect(trackSpec.str());

    std::map<int, DTSCPageData> curData;
    std::map<int, booking> bookKeeping;

    seek(0);
    getNext();

    while(lastPack){//loop through all
      int tid = lastPack.getTrackId();
      if (!bookKeeping.count(tid)){
        bookKeeping[tid].first = 1;
        bookKeeping[tid].curPart = 0;
        bookKeeping[tid].curKey = 0;

        curData[tid].lastKeyTime = 0xFFFFFFFF;
        curData[tid].keyNum = 1;
        curData[tid].partNum = 0;
        curData[tid].dataSize = 0;
        curData[tid].curOffset = 0;
        curData[tid].firstTime = myMeta.tracks[tid].keys[0].getTime();

        char tmpId[20];
        sprintf(tmpId, "%d", tid);
        indexPages[tid].init(config->getString("streamname") + tmpId, 8192, true);//Pages of 8kb in size, room for 512 parts.
      }
      if (myMeta.tracks[tid].keys[bookKeeping[tid].curKey].getParts() == curData[tid].partNum){
        if (curData[tid].dataSize > 8388608) {
          pagesByTrack[tid][bookKeeping[tid].first] = curData[tid];
          bookKeeping[tid].first += curData[tid].keyNum;
          curData[tid].keyNum = 0;
          curData[tid].dataSize = 0;
          curData[tid].firstTime = myMeta.tracks[tid].keys[bookKeeping[tid].curKey].getTime();
        }
        bookKeeping[tid].curKey++;
        curData[tid].keyNum++;
        curData[tid].partNum = 0;
      }
      curData[tid].dataSize += lastPack.getDataLen();
      curData[tid].partNum ++;
      bookKeeping[tid].curPart ++;
      getNext(false);
    }
    for (std::map<int, DTSC::Track>::iterator it = myMeta.tracks.begin(); it != myMeta.tracks.end(); it++) {
      if (curData.count(it->first) && !pagesByTrack[it->first].count(bookKeeping[it->first].first)){
        pagesByTrack[it->first][bookKeeping[it->first].first] = curData[it->first];
      }
      if (!pagesByTrack.count(it->first)){
        DEBUG_MSG(DLVL_WARN, "No pages for track %d found", it->first);
      }else{
        DEBUG_MSG(DLVL_DEVEL, "Track %d (%s) split into %lu pages", it->first, myMeta.tracks[it->first].codec.c_str(), pagesByTrack[it->first].size());
        for (std::map<int, DTSCPageData>::iterator it2 = pagesByTrack[it->first].begin(); it2 != pagesByTrack[it->first].end(); it2++){
          DEBUG_MSG(DLVL_DEVEL, "Page %u-%u", it2->first, it2->first + it2->second.keyNum - 1);
        }
      }
    }
  }

  bool Input::bufferFrame(int track, int keyNum){
    if (keyNum < 1){keyNum = 1;}
    if (!pagesByTrack.count(track)){
      return false;
    }
    std::map<int, DTSCPageData>::iterator it = pagesByTrack[track].upper_bound(keyNum);
    if (it != pagesByTrack[track].begin()){
      it--;
    }
    int pageNum = it->first;
    pageCounter[track][pageNum] = 15;///Keep page 15seconds in memory after last use

    DEBUG_MSG(DLVL_DONTEVEN, "Attempting to buffer page %d key %d->%d", track, keyNum, pageNum);
    if (dataPages[track].count(pageNum)){
      return true;
    }
    char pageId[100];
    int pageIdLen = sprintf(pageId, "%s%d_%d", config->getString("streamname").c_str(), track, pageNum);
    std::string tmpString(pageId, pageIdLen);
    dataPages[track][pageNum].init(tmpString, it->second.dataSize, true);
    DEBUG_MSG(DLVL_DEVEL, "Buffering track %d page %d through %d", track, pageNum, pageNum-1 + it->second.keyNum);

    std::stringstream trackSpec;
    trackSpec << track;
    trackSelect(trackSpec.str());
    seek(myMeta.tracks[track].keys[pageNum-1].getTime());
    long long unsigned int stopTime = myMeta.tracks[track].lastms + 1;
    if ((int)myMeta.tracks[track].keys.size() > pageNum-1 + it->second.keyNum){
      stopTime = myMeta.tracks[track].keys[pageNum-1 + it->second.keyNum].getTime();
    }
    DEBUG_MSG(DLVL_HIGH, "Playing from %ld to %llu", myMeta.tracks[track].keys[pageNum-1].getTime(), stopTime);
    it->second.curOffset = 0;
    getNext();
    while (lastPack && lastPack.getTime() < stopTime){
      if (it->second.curOffset + lastPack.getDataLen() > pagesByTrack[track][pageNum].dataSize){
        DEBUG_MSG(DLVL_WARN, "Trying to write %u bytes on pos %llu where size is %llu (time: %llu / %llu, track %u page %u)", lastPack.getDataLen(), it->second.curOffset, pagesByTrack[track][pageNum].dataSize, lastPack.getTime(), stopTime, track, pageNum);
        break;
      }else{
        memcpy(dataPages[track][pageNum].mapped + it->second.curOffset, lastPack.getData(), lastPack.getDataLen());
        it->second.curOffset += lastPack.getDataLen();
      }
      getNext();
    }
    for (int i = 0; i < indexPages[track].len / 8; i++){
      if (((long long int*)indexPages[track].mapped)[i] == 0){
        ((long long int*)indexPages[track].mapped)[i] = (((long long int)htonl(pageNum)) << 32) | htonl(it->second.keyNum);
        break;
      }
    }
    DEBUG_MSG(DLVL_DEVEL, "Buffering track %d page %d through %d, completed", track, pageNum, pageNum-1 + it->second.keyNum);
    return true;
  }

  bool Input::atKeyFrame(){
    static std::map<int, int> lastSeen;
    //not in keyTimes? We're not at a keyframe.
    unsigned int c = keyTimes[lastPack.getTrackId()].count(lastPack.getTime());
    if (!c){
      return false;
    }
    //skip double times
    if (lastSeen.count(lastPack.getTrackId()) && lastSeen[lastPack.getTrackId()] == lastPack.getTime()){
      return false;
    }
    //set last seen, and return true
    lastSeen[lastPack.getTrackId()] = lastPack.getTime();
    return true;
  }

  void Input::play(int until) {
    playing = -1;
    playUntil = until;
    initialTime = 0;
    benchMark = Util::getMS();
  }

  void Input::playOnce() {
    if (playing <= 0) {
      playing = 1;
    }
    ++playing;
    benchMark = Util::getMS();
  }

  void Input::quitPlay() {
    playing = 0;
  }
}

<|endoftext|>


<commit_before>/*
 * File:   UralJudger.cpp
 * Author: payper
 *
 * Created on 2014年3月25日, 下午1:22
 */

#include "UralJudger.h"

/**
 * Create a Ural Judger
 * @param _info Should be a pointer of a JudgerInfo
 */
UralJudger::UralJudger(JudgerInfo * _info) : VirtualJudger(_info) {
  language_table["1"] = "26";
  language_table["2"] = "25";
  language_table["3"] = "32";
  language_table["4"] = "31";
  language_table["6"] = "11";

  if (!RE2::PartialMatch(info->GetUsername(), "([0-9]*)", &author_id)) {
      throw Exception("Cannot retrieve author id from username");
  }
}

UralJudger::~UralJudger() {
}

void UralJudger::initHandShake(){
  socket->sendMessage(CONFIG->GetJudge_connect_string() + "\nUral");
}

/**
 * Ural doesn't require login, it uses judgeID to identify the submitter
 */
void UralJudger::login() {
}

/**
 * Submit a run
 * @param bott      Bott file for Run info
 * @return Submit status
 */
int UralJudger::submit(Bott * bott) {
    // prepare form for post
  struct curl_httppost * formpost = NULL;
  struct curl_httppost * lastptr = NULL;
  curl_formadd(&formpost, &lastptr,
               CURLFORM_COPYNAME, "Action",
               CURLFORM_COPYCONTENTS, "submit",
               CURLFORM_END);
  curl_formadd(&formpost, &lastptr,
               CURLFORM_COPYNAME, "SpaceID",
               CURLFORM_COPYCONTENTS, "1",
               CURLFORM_END);
  curl_formadd(&formpost, &lastptr,
               CURLFORM_COPYNAME, "JudgeID",
               CURLFORM_COPYCONTENTS, info->GetUsername().c_str(),
               CURLFORM_END);
  curl_formadd(&formpost, &lastptr,
               CURLFORM_COPYNAME, "Language",
               CURLFORM_COPYCONTENTS, bott->Getlanguage().c_str(),
               CURLFORM_END);
  curl_formadd(&formpost, &lastptr,
               CURLFORM_COPYNAME, "ProblemNum",
               CURLFORM_COPYCONTENTS, bott->Getvid().c_str(),
               CURLFORM_END);
  curl_formadd(&formpost, &lastptr,
               CURLFORM_COPYNAME, "Source",
               CURLFORM_COPYCONTENTS, bott->Getsrc().c_str(),
               CURLFORM_END);

  prepareCurl();
  curl_easy_setopt(curl, CURLOPT_URL,
                   "http://acm.timus.ru/submit.aspx?space=1");
  curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);

  // have to fetch header for runid
  string headerfilename = tmpfilename + "_header";
  FILE * headerfile = fopen(headerfilename.c_str(), "w");
  curl_easy_setopt(curl, CURLOPT_HEADERDATA, headerfile);

  performCurl();
  curl_formfree(formpost);
  fclose(headerfile);

  // check submit status
  string html = loadAllFromFile(headerfilename);
  if (html.find("303 See Other") == string::npos) {
    return SUBMIT_OTHER_ERROR;
  }

  return VirtualJudger::SUBMIT_NORMAL;
}

/**
 * Get result and related info
 * @param bott  Original Bott info
 * @return Result Bott file
 */
Bott * UralJudger::getStatus(Bott * bott) {
    time_t begin_time = time(NULL);
  int count = 10;

  Bott * result_bott;

  // fetch runid
  string runid, header = loadAllFromFile(tmpfilename + "_header");
  if (!RE2::PartialMatch(header, "(?s)X-SubmitID: ([0-9]*)", &runid)) {
    throw Exception("Failed to get remote runid.");
  }

  while (true) {
    // check wait time
    if (time(NULL) - begin_time > info->GetMax_wait_time()) {
      throw Exception("Failed to get current result, judge time out.");
    }

    prepareCurl();
    // count can be set to 100 if Ural is very busy
    curl_easy_setopt(
        curl, CURLOPT_URL,
        ((string) "http://acm.timus.ru/status.aspx?space=1&count=" +
            intToString(count)).c_str());
    performCurl();

    string html = loadAllFromFile(tmpfilename);
    string status;
    string result, time_used, memory_used;

    // get the row for runid, if not found, try fetch more runs
    if (!RE2::PartialMatch(html, "(?s)(
" + runid + ".*?
)",
                           &status)) {
      if (count == 100) {
        throw Exception("Failed to find current submission in the last 100.");
      }
      log("Trying to fetch more runs...");
      count += 10;
      continue;
    }

    // get result
    if (!RE2::PartialMatch(status, "(?s)(.*?)	",
                           &result)) {
      throw Exception("Failed to get current result.");
    }
    result = trim(result);
    if (isFinalResult(result)) {
      // result is the final one
      result = convertResult(result);
      if (result != "Compile Error") {
        // no details for CE results
        if (!RE2::PartialMatch(
            status,
            "(.*?)	"
                "(.*?) KB	",
            &time_used, &memory_used)) {
          throw Exception("Failed to parse details from status row.");
        }
        int time_ms = stringToDouble(time_used) * 1000 + 0.001;
        time_used = intToString(time_ms);
      } else {
        memory_used = time_used = "0";
      }
      result_bott = new Bott;
      result_bott->Settype(RESULT_REPORT);
      result_bott->Setresult(result);
      result_bott->Settime_used(trim(time_used));
      result_bott->Setmemory_used(trim(memory_used));
      result_bott->Setremote_runid(trim(runid));
      break;
    }
  }
  return result_bott;
}

/**
 * Get compile error info from Ural
 * @param bott      Result bott file
 * @return Compile error info
 */
string UralJudger::getCEinfo(Bott * bott) {
  prepareCurl();
  curl_easy_setopt(
      curl, CURLOPT_URL,
      ((string) "http://acm.timus.ru/ce.aspx?id=" +
          bott->Getremote_runid()).c_str());
  performCurl();

  string info = loadAllFromFile(tmpfilename);
  return info;
}

/**
 * Check whether the result is final
 * @param result        Current result
 * @return Is final one or not
 */
string UralJudger::convertResult(string result) {
  if (result.find("Compilation error") != string::npos)
    return "Compile Error";
  if (result.find("Time limit exceeded") != string::npos)
    return "Time Limit Exceed";
  if (result.find("Memory limit exceeded") != string::npos)
    return "Memory Limit Exceed";
  if (result.find("Output limit exceeded") != string::npos)
    return "Output Limit Exceed";
  if (result.find("Wrong answer") != string::npos)
    return "Wrong Answer";
  if (result.find("Crash") != string::npos)
    return "Runtime Error";
  if (result.find("Runtime error") != string::npos)
    return "Runtime Error";
  return trim(result);
}
<commit_msg>Ural: revert to original way when fetching result<commit_after>/*
 * File:   UralJudger.cpp
 * Author: payper
 *
 * Created on 2014年3月25日, 下午1:22
 */

#include "UralJudger.h"

/**
 * Create a Ural Judger
 * @param _info Should be a pointer of a JudgerInfo
 */
UralJudger::UralJudger(JudgerInfo * _info) : VirtualJudger(_info) {
  language_table["1"] = "26";
  language_table["2"] = "25";
  language_table["3"] = "32";
  language_table["4"] = "31";
  language_table["6"] = "11";

  if (!RE2::PartialMatch(info->GetUsername(), "([0-9]*)", &author_id)) {
      throw Exception("Cannot retrieve author id from username");
  }
}

UralJudger::~UralJudger() {
}

void UralJudger::initHandShake(){
  socket->sendMessage(CONFIG->GetJudge_connect_string() + "\nUral");
}

/**
 * Ural doesn't require login, it uses judgeID to identify the submitter
 */
void UralJudger::login() {
}

/**
 * Submit a run
 * @param bott      Bott file for Run info
 * @return Submit status
 */
int UralJudger::submit(Bott * bott) {
    // prepare form for post
  struct curl_httppost * formpost = NULL;
  struct curl_httppost * lastptr = NULL;
  curl_formadd(&formpost, &lastptr,
               CURLFORM_COPYNAME, "Action",
               CURLFORM_COPYCONTENTS, "submit",
               CURLFORM_END);
  curl_formadd(&formpost, &lastptr,
               CURLFORM_COPYNAME, "SpaceID",
               CURLFORM_COPYCONTENTS, "1",
               CURLFORM_END);
  curl_formadd(&formpost, &lastptr,
               CURLFORM_COPYNAME, "JudgeID",
               CURLFORM_COPYCONTENTS, info->GetUsername().c_str(),
               CURLFORM_END);
  curl_formadd(&formpost, &lastptr,
               CURLFORM_COPYNAME, "Language",
               CURLFORM_COPYCONTENTS, bott->Getlanguage().c_str(),
               CURLFORM_END);
  curl_formadd(&formpost, &lastptr,
               CURLFORM_COPYNAME, "ProblemNum",
               CURLFORM_COPYCONTENTS, bott->Getvid().c_str(),
               CURLFORM_END);
  curl_formadd(&formpost, &lastptr,
               CURLFORM_COPYNAME, "Source",
               CURLFORM_COPYCONTENTS, bott->Getsrc().c_str(),
               CURLFORM_END);

  prepareCurl();
  curl_easy_setopt(curl, CURLOPT_URL,
                   "http://acm.timus.ru/submit.aspx?space=1");
  curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
  performCurl();
  curl_formfree(formpost);

  // check submit status
  string html = loadAllFromFile(tmpfilename);
  if (html.find("
Solutions judgement results
") ==
      string::npos) {
    return SUBMIT_OTHER_ERROR;
  }

  return VirtualJudger::SUBMIT_NORMAL;
}

/**
 * Get result and related info
 * @param bott  Original Bott info
 * @return Result Bott file
 */
Bott * UralJudger::getStatus(Bott * bott) {
  time_t begin_time = time(NULL);

  Bott * result_bott;

  while (true) {
    // check wait time
    if (time(NULL) - begin_time > info->GetMax_wait_time()) {
      throw Exception("Failed to get current result, judge time out.");
    }

    prepareCurl();
    // count can be set to 100 if Ural is very busy
    curl_easy_setopt(
        curl, CURLOPT_URL,
        ("http://acm.timus.ru/status.aspx?author=" + author_id).c_str());
    performCurl();

    string html = loadAllFromFile(tmpfilename);
    string status;
    string runid, result, time_used, memory_used;

    // get first row
    if (!RE2::PartialMatch(html, "(?s)(
.*?
)",
        &status)) {
      throw Exception("Failed to get status row.");
    }

    // get result
    if (!RE2::PartialMatch(status,
        "(?s)([0-9]*).*"
        "(.*?)	", &runid, &result)) {
      throw Exception("Failed to get current result.");
    }
    result = trim(result);
    if (isFinalResult(result)) {
      // result is the final one
      result = convertResult(result);
      if (result != "Compile Error") {
        // no details for CE results
        if (!RE2::PartialMatch(
            status,
            "(.*?)	"
                "(.*?) KB	",
            &time_used, &memory_used)) {
          throw Exception("Failed to parse details from status row.");
        }
        int time_ms = stringToDouble(time_used) * 1000 + 0.001;
        time_used = intToString(time_ms);
      } else {
        memory_used = time_used = "0";
      }
      result_bott = new Bott;
      result_bott->Settype(RESULT_REPORT);
      result_bott->Setresult(result);
      result_bott->Settime_used(trim(time_used));
      result_bott->Setmemory_used(trim(memory_used));
      result_bott->Setremote_runid(trim(runid));
      break;
    }
  }
  return result_bott;
}

/**
 * Get compile error info from Ural
 * @param bott      Result bott file
 * @return Compile error info
 */
string UralJudger::getCEinfo(Bott * bott) {
  prepareCurl();
  curl_easy_setopt(
      curl, CURLOPT_URL,
      ((string) "http://acm.timus.ru/ce.aspx?id=" +
          bott->Getremote_runid()).c_str());
  performCurl();

  string info = loadAllFromFile(tmpfilename);
  return info;
}

/**
 * Check whether the result is final
 * @param result        Current result
 * @return Is final one or not
 */
string UralJudger::convertResult(string result) {
  if (result.find("Compilation error") != string::npos)
    return "Compile Error";
  if (result.find("Time limit exceeded") != string::npos)
    return "Time Limit Exceed";
  if (result.find("Memory limit exceeded") != string::npos)
    return "Memory Limit Exceed";
  if (result.find("Output limit exceeded") != string::npos)
    return "Output Limit Exceed";
  if (result.find("Wrong answer") != string::npos)
    return "Wrong Answer";
  if (result.find("Crash") != string::npos)
    return "Runtime Error";
  if (result.find("Runtime error") != string::npos)
    return "Runtime Error";
  return trim(result);
}
<|endoftext|>


<commit_before>#pragma once

#include "types.hh"
#include "serialise.hh"

namespace nix {

MakeError(BadHash, Error);

enum HashType : char { htUnknown, htMD5, htSHA1, htSHA256, htSHA512 };

const int md5HashSize = 16;
const int sha1HashSize = 20;
const int sha256HashSize = 32;
const int sha512HashSize = 64;

extern const string base32Chars;

enum Base : int { Base64, Base32, Base16, SRI };

struct Hash
{
    static const unsigned int maxHashSize = 64;
    unsigned int hashSize = 0;
    unsigned char hash[maxHashSize] = {};

    HashType type = htUnknown;

    /* Create an unset hash object. */
    Hash() { };

    /* Create a zero-filled hash object. */
    Hash(HashType type) : type(type) { init(); };

    /* Initialize the hash from a string representation, in the format
       "[<type>:]<base16|base32|base64>" or "<type>-<base64>" (a
       Subresource Integrity hash expression). If the 'type' argument
       is htUnknown, then the hash type must be specified in the
       string. */
    Hash(std::string_view s, HashType type = htUnknown);

    Hash(const Hash &) = default;

    Hash(Hash &&) = default;

    Hash & operator = (const Hash &) = default;

    void init();

    /* Check whether a hash is set. */
    operator bool () const { return type != htUnknown; }

    /* Check whether two hash are equal. */
    bool operator == (const Hash & h2) const;

    /* Check whether two hash are not equal. */
    bool operator != (const Hash & h2) const;

    /* For sorting. */
    bool operator < (const Hash & h) const;

    /* Returns the length of a base-16 representation of this hash. */
    size_t base16Len() const
    {
        return hashSize * 2;
    }

    /* Returns the length of a base-32 representation of this hash. */
    size_t base32Len() const
    {
        return (hashSize * 8 - 1) / 5 + 1;
    }

    /* Returns the length of a base-64 representation of this hash. */
    size_t base64Len() const
    {
        return ((4 * hashSize / 3) + 3) & ~3;
    }

    /* Return a string representation of the hash, in base-16, base-32
       or base-64. By default, this is prefixed by the hash type
       (e.g. "sha256:"). */
    std::string to_string(Base base, bool includeType) const;

    std::string gitRev() const
    {
        assert(type == htSHA1);
        return to_string(Base16, false);
    }

    std::string gitShortRev() const
    {
        assert(type == htSHA1);
        return std::string(to_string(Base16, false), 0, 7);
    }
};

/* Helper that defaults empty hashes to the 0 hash. */
Hash newHashAllowEmpty(std::string hashStr, HashType ht);

/* Print a hash in base-16 if it's MD5, or base-32 otherwise. */
string printHash16or32(const Hash & hash);

/* Compute the hash of the given string. */
Hash hashString(HashType ht, const string & s);

/* Compute the hash of the given file. */
Hash hashFile(HashType ht, const Path & path);

/* Compute the hash of the given path.  The hash is defined as
   (essentially) hashString(ht, dumpPath(path)). */
typedef std::pair<Hash, unsigned long long> HashResult;
HashResult hashPath(HashType ht, const Path & path,
    PathFilter & filter = defaultPathFilter);

/* Compress a hash to the specified number of bytes by cyclically
   XORing bytes together. */
Hash compressHash(const Hash & hash, unsigned int newSize);

/* Parse a string representing a hash type. */
HashType parseHashType(const string & s);

/* And the reverse. */
string printHashType(HashType ht);

union Ctx;

struct AbstractHashSink : virtual Sink
{
    virtual HashResult finish() = 0;
};

class HashSink : public BufferedSink, public AbstractHashSink
{
private:
    HashType ht;
    Ctx * ctx;
    unsigned long long bytes;

public:
    HashSink(HashType ht);
    HashSink(const HashSink & h);
    ~HashSink();
    void write(const unsigned char * data, size_t len) override;
    HashResult finish() override;
    HashResult currentHash();
};

}
<commit_msg>Remove uneeded `= default` for Hash<commit_after>#pragma once

#include "types.hh"
#include "serialise.hh"

namespace nix {

MakeError(BadHash, Error);

enum HashType : char { htUnknown, htMD5, htSHA1, htSHA256, htSHA512 };

const int md5HashSize = 16;
const int sha1HashSize = 20;
const int sha256HashSize = 32;
const int sha512HashSize = 64;

extern const string base32Chars;

enum Base : int { Base64, Base32, Base16, SRI };

struct Hash
{
    static const unsigned int maxHashSize = 64;
    unsigned int hashSize = 0;
    unsigned char hash[maxHashSize] = {};

    HashType type = htUnknown;

    /* Create an unset hash object. */
    Hash() { };

    /* Create a zero-filled hash object. */
    Hash(HashType type) : type(type) { init(); };

    /* Initialize the hash from a string representation, in the format
       "[<type>:]<base16|base32|base64>" or "<type>-<base64>" (a
       Subresource Integrity hash expression). If the 'type' argument
       is htUnknown, then the hash type must be specified in the
       string. */
    Hash(std::string_view s, HashType type = htUnknown);

    void init();

    /* Check whether a hash is set. */
    operator bool () const { return type != htUnknown; }

    /* Check whether two hash are equal. */
    bool operator == (const Hash & h2) const;

    /* Check whether two hash are not equal. */
    bool operator != (const Hash & h2) const;

    /* For sorting. */
    bool operator < (const Hash & h) const;

    /* Returns the length of a base-16 representation of this hash. */
    size_t base16Len() const
    {
        return hashSize * 2;
    }

    /* Returns the length of a base-32 representation of this hash. */
    size_t base32Len() const
    {
        return (hashSize * 8 - 1) / 5 + 1;
    }

    /* Returns the length of a base-64 representation of this hash. */
    size_t base64Len() const
    {
        return ((4 * hashSize / 3) + 3) & ~3;
    }

    /* Return a string representation of the hash, in base-16, base-32
       or base-64. By default, this is prefixed by the hash type
       (e.g. "sha256:"). */
    std::string to_string(Base base, bool includeType) const;

    std::string gitRev() const
    {
        assert(type == htSHA1);
        return to_string(Base16, false);
    }

    std::string gitShortRev() const
    {
        assert(type == htSHA1);
        return std::string(to_string(Base16, false), 0, 7);
    }
};

/* Helper that defaults empty hashes to the 0 hash. */
Hash newHashAllowEmpty(std::string hashStr, HashType ht);

/* Print a hash in base-16 if it's MD5, or base-32 otherwise. */
string printHash16or32(const Hash & hash);

/* Compute the hash of the given string. */
Hash hashString(HashType ht, const string & s);

/* Compute the hash of the given file. */
Hash hashFile(HashType ht, const Path & path);

/* Compute the hash of the given path.  The hash is defined as
   (essentially) hashString(ht, dumpPath(path)). */
typedef std::pair<Hash, unsigned long long> HashResult;
HashResult hashPath(HashType ht, const Path & path,
    PathFilter & filter = defaultPathFilter);

/* Compress a hash to the specified number of bytes by cyclically
   XORing bytes together. */
Hash compressHash(const Hash & hash, unsigned int newSize);

/* Parse a string representing a hash type. */
HashType parseHashType(const string & s);

/* And the reverse. */
string printHashType(HashType ht);

union Ctx;

struct AbstractHashSink : virtual Sink
{
    virtual HashResult finish() = 0;
};

class HashSink : public BufferedSink, public AbstractHashSink
{
private:
    HashType ht;
    Ctx * ctx;
    unsigned long long bytes;

public:
    HashSink(HashType ht);
    HashSink(const HashSink & h);
    ~HashSink();
    void write(const unsigned char * data, size_t len) override;
    HashResult finish() override;
    HashResult currentHash();
};

}
<|endoftext|>


<commit_before>#include <nan.h>

#include "../kv-types.h"

#include "txn.h"
#include "db.h"
#include "cursor.h"

using namespace v8;
using namespace kv;
using namespace kv::lmdb;

#define CURSOR_EXPORT(KT, VT) cursor<KT, VT>::setup_export(exports);
void kv::lmdb::setup_cursor_export(v8::Handle<v8::Object>& exports) {
	KV_TYPE_EACH(CURSOR_EXPORT);
}

template<class K, class V> void cursor<K, V>::setup_export(Handle<Object>& exports) {
	char class_name[64];
	sprintf(class_name, "Cursor_%s_%s", K::type_name, V::type_name);

	// Prepare constructor template
	Local<FunctionTemplate> cursorTpl = NanNew<FunctionTemplate>(cursor::ctor);
	cursorTpl->SetClassName(NanNew(class_name));
	cursorTpl->InstanceTemplate()->SetInternalFieldCount(1);

	// Add functions to the prototype
	NODE_SET_METHOD(cursorTpl->PrototypeTemplate(), "close", cursor::close);

	NODE_SET_METHOD(cursorTpl->PrototypeTemplate(), "del", cursor::del);
	NODE_SET_METHOD(cursorTpl->PrototypeTemplate(), "current", cursor::current);
	NODE_SET_METHOD(cursorTpl->PrototypeTemplate(), "next", cursor::next);
	NODE_SET_METHOD(cursorTpl->PrototypeTemplate(), "prev", cursor::prev);
	NODE_SET_METHOD(cursorTpl->PrototypeTemplate(), "nextDup", cursor::nextDup);
	NODE_SET_METHOD(cursorTpl->PrototypeTemplate(), "prevDup", cursor::prevDup);

	NODE_SET_METHOD(cursorTpl->PrototypeTemplate(), "seek", cursor::seek);
	NODE_SET_METHOD(cursorTpl->PrototypeTemplate(), "lowerBound", cursor::lowerBound);
	NODE_SET_METHOD(cursorTpl->PrototypeTemplate(), "first", cursor::first);
	NODE_SET_METHOD(cursorTpl->PrototypeTemplate(), "last", cursor::last);

	// Set exports
	exports->Set(NanNew(class_name), cursorTpl->GetFunction());
}

#define KVCURSOR cursor<K, V>
#define KVCURSOR_METHOD(fn) template <class K, class V> NAN_METHOD(KVCURSOR::fn)

KVCURSOR_METHOD(ctor) {
	NanScope();

	db<K, V> *dw = ObjectWrap::Unwrap<db<K, V> >(args[0]->ToObject());
	txn *tw = ObjectWrap::Unwrap<txn>(args[1]->ToObject());

	MDB_cursor *cur = NULL;
	int rc = mdb_cursor_open(tw->_txn, dw->_dbi, &cur);
	if (rc != 0) {
		NanThrowError(mdb_strerror(rc));
		NanReturnUndefined();
	}

	cursor *ptr = new cursor(cur);
	ptr->Wrap(args.This());
	NanReturnValue(args.This());
}

KVCURSOR_METHOD(close) {
	NanScope();

	cursor *cw = ObjectWrap::Unwrap<cursor>(args.This());
	mdb_cursor_close(cw->_cursor);
	cw->_cursor = NULL;

	NanReturnUndefined();
}

KVCURSOR_METHOD(del) {
	NanScope();

	cursor *cw = ObjectWrap::Unwrap<cursor>(args.This());
	int rc = mdb_cursor_del(cw->_cursor, 0);
	if (rc != 0) {
		NanThrowError(mdb_strerror(rc));
		NanReturnUndefined();
	}

	NanReturnUndefined();
}

template<class K, class V> template<MDB_cursor_op OP> NAN_METHOD(KVCURSOR::cursorOp) {
	NanScope();

	cursor *cw = ObjectWrap::Unwrap<cursor>(args.This());

	MDB_val key = { 0, 0 }, data = { 0, 0 };
	int rc = mdb_cursor_get(cw->_cursor, &key, &data, OP);
	if (rc != 0) {
		NanThrowError(mdb_strerror(rc));
		NanReturnUndefined();
	}

	Local<Array> ret = NanNew<Array>(2);
	ret->Set(0, K((const char*)key.mv_data, key.mv_size).v8value());
	ret->Set(1, V((const char*)data.mv_data, data.mv_size).v8value());
	NanReturnValue(ret);
}

template<class K, class V> template<MDB_cursor_op OP> NAN_METHOD(KVCURSOR::cursorKeyOp) {
	NanScope();

	cursor *cw = ObjectWrap::Unwrap<cursor>(args.This());

	K k = K(args[0]);

	MDB_val key = { 0, 0 }, data = { 0, 0 };
	key.mv_data = (void*)k.data();
	key.mv_size = k.size();

	int rc = mdb_cursor_get(cw->_cursor, &key, &data, OP);
	if (rc == MDB_NOTFOUND) {
		NanReturnNull();
	} else if (rc != 0) {
		NanThrowError(mdb_strerror(rc));
		NanReturnUndefined();
	}

	Local<Array> ret = NanNew<Array>(2);
	ret->Set(0, args[0]);
	ret->Set(1, V((const char*)data.mv_data, data.mv_size).v8value());
	NanReturnValue(ret);
}

template<class K, class V> template<MDB_cursor_op OP> NAN_METHOD(KVCURSOR::cursorKeyValOp) {
	NanScope();

	cursor *cw = ObjectWrap::Unwrap<cursor>(args.This());

	K k = K(args[0]);
	V v = V(args[1]);

	MDB_val key = { 0, 0 }, data = { 0, 0 };
	key.mv_data = (void*)k.data();
	key.mv_size = k.size();
	data.mv_data = (void*)v.data();
	data.mv_size = v.size();

	int rc = mdb_cursor_get(cw->_cursor, &key, &data, OP);
	if (rc == MDB_NOTFOUND) {
		NanReturnNull();
	} else if (rc != 0) {
		NanThrowError(mdb_strerror(rc));
		NanReturnUndefined();
	}

	Local<Array> ret = NanNew<Array>(2);
	ret->Set(0, K((const char*)key.mv_data, key.mv_size).v8value());
	ret->Set(1, V((const char*)data.mv_data, data.mv_size).v8value());
	NanReturnValue(ret);
}

KVCURSOR_METHOD(current) { return cursorOp<MDB_GET_CURRENT>(args); }
KVCURSOR_METHOD(next) { return cursorOp<MDB_NEXT>(args); }
KVCURSOR_METHOD(prev) { return cursorOp<MDB_PREV>(args); }
KVCURSOR_METHOD(nextDup) { return cursorOp<MDB_NEXT_DUP>(args); }
KVCURSOR_METHOD(prevDup) { return cursorOp<MDB_PREV_DUP>(args); }

KVCURSOR_METHOD(first) { return cursorOp<MDB_FIRST>(args); }
KVCURSOR_METHOD(last) { return cursorOp<MDB_LAST>(args); }
KVCURSOR_METHOD(firstDup) { return cursorOp<MDB_FIRST_DUP>(args); }
KVCURSOR_METHOD(lastDup) { return cursorOp<MDB_LAST_DUP>(args); }

KVCURSOR_METHOD(seek) {
	return args.Length() == 1 ? cursorKeyOp<MDB_SET>(args) : cursorKeyValOp<MDB_GET_BOTH>(args);
}

KVCURSOR_METHOD(lowerBound) {
	return args.Length() == 1 ? cursorKeyOp<MDB_SET_RANGE>(args) : cursorKeyValOp<MDB_GET_BOTH_RANGE>(args);
}

template <class K, class V> cursor<K, V>::cursor(MDB_cursor *cur) : _cursor(cur) {

}

template <class K, class V> cursor<K, V>::~cursor() {
	if (_cursor) mdb_cursor_close(_cursor);
}
<commit_msg>fix cursor:cpp return.<commit_after>#include <nan.h>

#include "../kv-types.h"

#include "txn.h"
#include "db.h"
#include "cursor.h"

using namespace v8;
using namespace kv;
using namespace kv::lmdb;

#define CURSOR_EXPORT(KT, VT) cursor<KT, VT>::setup_export(exports);
void kv::lmdb::setup_cursor_export(v8::Handle<v8::Object>& exports) {
	KV_TYPE_EACH(CURSOR_EXPORT);
}

template<class K, class V> void cursor<K, V>::setup_export(Handle<Object>& exports) {
	char class_name[64];
	sprintf(class_name, "Cursor_%s_%s", K::type_name, V::type_name);

	// Prepare constructor template
	Local<FunctionTemplate> cursorTpl = NanNew<FunctionTemplate>(cursor::ctor);
	cursorTpl->SetClassName(NanNew(class_name));
	cursorTpl->InstanceTemplate()->SetInternalFieldCount(1);

	// Add functions to the prototype
	NODE_SET_METHOD(cursorTpl->PrototypeTemplate(), "close", cursor::close);

	NODE_SET_METHOD(cursorTpl->PrototypeTemplate(), "del", cursor::del);
	NODE_SET_METHOD(cursorTpl->PrototypeTemplate(), "current", cursor::current);
	NODE_SET_METHOD(cursorTpl->PrototypeTemplate(), "next", cursor::next);
	NODE_SET_METHOD(cursorTpl->PrototypeTemplate(), "prev", cursor::prev);
	NODE_SET_METHOD(cursorTpl->PrototypeTemplate(), "nextDup", cursor::nextDup);
	NODE_SET_METHOD(cursorTpl->PrototypeTemplate(), "prevDup", cursor::prevDup);

	NODE_SET_METHOD(cursorTpl->PrototypeTemplate(), "seek", cursor::seek);
	NODE_SET_METHOD(cursorTpl->PrototypeTemplate(), "lowerBound", cursor::lowerBound);
	NODE_SET_METHOD(cursorTpl->PrototypeTemplate(), "first", cursor::first);
	NODE_SET_METHOD(cursorTpl->PrototypeTemplate(), "last", cursor::last);

	// Set exports
	exports->Set(NanNew(class_name), cursorTpl->GetFunction());
}

#define KVCURSOR cursor<K, V>
#define KVCURSOR_METHOD(fn) template <class K, class V> NAN_METHOD(KVCURSOR::fn)

KVCURSOR_METHOD(ctor) {
	NanScope();

	db<K, V> *dw = ObjectWrap::Unwrap<db<K, V> >(args[0]->ToObject());
	txn *tw = ObjectWrap::Unwrap<txn>(args[1]->ToObject());

	MDB_cursor *cur = NULL;
	int rc = mdb_cursor_open(tw->_txn, dw->_dbi, &cur);
	if (rc != 0) {
		NanThrowError(mdb_strerror(rc));
		NanReturnUndefined();
	}

	cursor *ptr = new cursor(cur);
	ptr->Wrap(args.This());
	NanReturnValue(args.This());
}

KVCURSOR_METHOD(close) {
	NanScope();

	cursor *cw = ObjectWrap::Unwrap<cursor>(args.This());
	mdb_cursor_close(cw->_cursor);
	cw->_cursor = NULL;

	NanReturnUndefined();
}

KVCURSOR_METHOD(del) {
	NanScope();

	cursor *cw = ObjectWrap::Unwrap<cursor>(args.This());
	int rc = mdb_cursor_del(cw->_cursor, 0);
	if (rc != 0) {
		NanThrowError(mdb_strerror(rc));
		NanReturnUndefined();
	}

	NanReturnUndefined();
}

template<class K, class V> template<MDB_cursor_op OP> NAN_METHOD(KVCURSOR::cursorOp) {
	NanScope();

	cursor *cw = ObjectWrap::Unwrap<cursor>(args.This());

	MDB_val key = { 0, 0 }, data = { 0, 0 };
	int rc = mdb_cursor_get(cw->_cursor, &key, &data, OP);
	if (rc == MDB_NOTFOUND) {
		NanReturnNull();
	} if (rc != 0) {
		NanThrowError(mdb_strerror(rc));
		NanReturnUndefined();
	}

	Local<Array> ret = NanNew<Array>(2);
	ret->Set(0, K((const char*)key.mv_data, key.mv_size).v8value());
	ret->Set(1, V((const char*)data.mv_data, data.mv_size).v8value());
	NanReturnValue(ret);
}

template<class K, class V> template<MDB_cursor_op OP> NAN_METHOD(KVCURSOR::cursorKeyOp) {
	NanScope();

	cursor *cw = ObjectWrap::Unwrap<cursor>(args.This());

	K k = K(args[0]);

	MDB_val key = { 0, 0 }, data = { 0, 0 };
	key.mv_data = (void*)k.data();
	key.mv_size = k.size();

	int rc = mdb_cursor_get(cw->_cursor, &key, &data, OP);
	if (rc == MDB_NOTFOUND) {
		NanReturnNull();
	} else if (rc != 0) {
		NanThrowError(mdb_strerror(rc));
		NanReturnUndefined();
	}

	Local<Array> ret = NanNew<Array>(2);
	ret->Set(0, K((const char*)key.mv_data, key.mv_size).v8value());
	ret->Set(1, V((const char*)data.mv_data, data.mv_size).v8value());
	NanReturnValue(ret);
}

template<class K, class V> template<MDB_cursor_op OP> NAN_METHOD(KVCURSOR::cursorKeyValOp) {
	NanScope();

	cursor *cw = ObjectWrap::Unwrap<cursor>(args.This());

	K k = K(args[0]);
	V v = V(args[1]);

	MDB_val key = { 0, 0 }, data = { 0, 0 };
	key.mv_data = (void*)k.data();
	key.mv_size = k.size();
	data.mv_data = (void*)v.data();
	data.mv_size = v.size();

	int rc = mdb_cursor_get(cw->_cursor, &key, &data, OP);
	if (rc == MDB_NOTFOUND) {
		NanReturnNull();
	} else if (rc != 0) {
		NanThrowError(mdb_strerror(rc));
		NanReturnUndefined();
	}

	Local<Array> ret = NanNew<Array>(2);
	ret->Set(0, K((const char*)key.mv_data, key.mv_size).v8value());
	ret->Set(1, V((const char*)data.mv_data, data.mv_size).v8value());
	NanReturnValue(ret);
}

KVCURSOR_METHOD(current) { return cursorOp<MDB_GET_CURRENT>(args); }
KVCURSOR_METHOD(next) { return cursorOp<MDB_NEXT>(args); }
KVCURSOR_METHOD(prev) { return cursorOp<MDB_PREV>(args); }
KVCURSOR_METHOD(nextDup) { return cursorOp<MDB_NEXT_DUP>(args); }
KVCURSOR_METHOD(prevDup) { return cursorOp<MDB_PREV_DUP>(args); }

KVCURSOR_METHOD(first) { return cursorOp<MDB_FIRST>(args); }
KVCURSOR_METHOD(last) { return cursorOp<MDB_LAST>(args); }
KVCURSOR_METHOD(firstDup) { return cursorOp<MDB_FIRST_DUP>(args); }
KVCURSOR_METHOD(lastDup) { return cursorOp<MDB_LAST_DUP>(args); }

KVCURSOR_METHOD(seek) {
	return args.Length() == 1 ? cursorKeyOp<MDB_SET>(args) : cursorKeyValOp<MDB_GET_BOTH>(args);
}

KVCURSOR_METHOD(lowerBound) {
	return args.Length() == 1 ? cursorKeyOp<MDB_SET_RANGE>(args) : cursorKeyValOp<MDB_GET_BOTH_RANGE>(args);
}

template <class K, class V> cursor<K, V>::cursor(MDB_cursor *cur) : _cursor(cur) {

}

template <class K, class V> cursor<K, V>::~cursor() {
	if (_cursor) {
		printf("LMDB_cursor warning: unclosed cursor closed by dtor.\n");
		mdb_cursor_close(_cursor);
	}
}
<|endoftext|>


<commit_before>/***********************************************************************************************************************
**
** Copyright (c) 2011, 2015 ETH Zurich
** 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 the ETH Zurich 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 HOLDER 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 "DefaultVisualizer.h"

#include "ModelBase/src/nodes/Node.h"

#include "VisualizationBase/src/Scene.h"
#include "VisualizationBase/src/items/Item.h"
#include "VisualizationBase/src/overlays/ArrowOverlay.h"
#include "VisualizationBase/src/overlays/SelectionOverlay.h"

#include "../dataformat/TupleSet.h"

namespace InformationScripting {

const QString DefaultVisualizer::HIGHLIGHT_OVERLAY_GROUP = {"default graph highlight"};
const QString DefaultVisualizer::ARROW_OVERLAY_GROUP = {"default arrow"};

DefaultVisualizer& DefaultVisualizer::instance()
{
	static DefaultVisualizer instance;
	return instance;
}

void DefaultVisualizer::visualize(const TupleSet& ts)
{
	// First remove all existing overlays:
	for (auto scene : Visualization::Scene::allScenes())
	{
		scene->removeOverlayGroup(HIGHLIGHT_OVERLAY_GROUP);
		scene->removeOverlayGroup(ARROW_OVERLAY_GROUP);
	}
	// Now draw the new stuff
	auto calls = ts.tuples("calls");
	if (!calls.empty())
	{
		for (auto t : calls)
		{
			auto allAsts = t.valuesOfType<Model::Node*>();
			if (allAsts.size() > 1)
			{
				Model::Node* fromNode = allAsts[0];
				Model::Node* toNode = allAsts[1];

				auto fromVisualization = Visualization::Item::nodeItemsMap().find(fromNode);
				Q_ASSERT(fromVisualization != Visualization::Item::nodeItemsMap().end());
				auto toVisualization = Visualization::Item::nodeItemsMap().find(toNode);
				Q_ASSERT(toVisualization != Visualization::Item::nodeItemsMap().end());
				auto fromVisualizationItem = *fromVisualization;

				auto overlay = new Visualization::ArrowOverlay(
				fromVisualizationItem, *toVisualization, Visualization::ArrowOverlay::itemStyles().get());
				fromVisualizationItem->addOverlay(overlay, ARROW_OVERLAY_GROUP);
			}
		}
	}
	else
	{
		auto astTuples = ts.tuples([](const Tuple& t) { return t.tag() == "ast"; });

		QHash<Model::Node*, QString> colors;

		for (auto t : astTuples)
		{
			Model::Node* node = t["ast"];
			QString& color = colors[node];
			if (color.isEmpty())
			{
				auto colorIt = t.find("color");
				if (colorIt != t.end())
				{
					QString c = colorIt->second;
					color = c;
				}
				else
					color = "red";
			}
		}

		for (auto it = colors.begin(); it != colors.end(); ++it)
		{
			Model::Node* node = it.key();
			Q_ASSERT(node);
			auto nodeVisualization = Visualization::Item::nodeItemsMap().find(node);
			if (nodeVisualization != Visualization::Item::nodeItemsMap().end())
			{
				auto item = *nodeVisualization;
				auto overlay = new Visualization::SelectionOverlay(
							item, Visualization::SelectionOverlay::itemStyles().get(it.value() + "Highlight"));
				item->addOverlay(overlay, HIGHLIGHT_OVERLAY_GROUP);
			}
			else
				qWarning() << "no visualization for" << node->typeName();
		}
	}
}

} /* namespace InformationScripting */
<commit_msg>Show all visualizations for a node in DefaultVisualizer<commit_after>/***********************************************************************************************************************
**
** Copyright (c) 2011, 2015 ETH Zurich
** 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 the ETH Zurich 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 HOLDER 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 "DefaultVisualizer.h"

#include "ModelBase/src/nodes/Node.h"

#include "VisualizationBase/src/Scene.h"
#include "VisualizationBase/src/items/Item.h"
#include "VisualizationBase/src/overlays/ArrowOverlay.h"
#include "VisualizationBase/src/overlays/SelectionOverlay.h"

#include "../dataformat/TupleSet.h"

namespace InformationScripting {

const QString DefaultVisualizer::HIGHLIGHT_OVERLAY_GROUP = {"default graph highlight"};
const QString DefaultVisualizer::ARROW_OVERLAY_GROUP = {"default arrow"};

DefaultVisualizer& DefaultVisualizer::instance()
{
	static DefaultVisualizer instance;
	return instance;
}

void DefaultVisualizer::visualize(const TupleSet& ts)
{
	// First remove all existing overlays:
	for (auto scene : Visualization::Scene::allScenes())
	{
		scene->removeOverlayGroup(HIGHLIGHT_OVERLAY_GROUP);
		scene->removeOverlayGroup(ARROW_OVERLAY_GROUP);
	}
	// Now draw the new stuff
	auto calls = ts.tuples("calls");
	if (!calls.empty())
	{
		for (auto t : calls)
		{
			auto allAsts = t.valuesOfType<Model::Node*>();
			if (allAsts.size() > 1)
			{
				Model::Node* fromNode = allAsts[0];
				Model::Node* toNode = allAsts[1];

				auto fromVisualization = Visualization::Item::nodeItemsMap().find(fromNode);
				Q_ASSERT(fromVisualization != Visualization::Item::nodeItemsMap().end());
				auto toVisualization = Visualization::Item::nodeItemsMap().find(toNode);
				Q_ASSERT(toVisualization != Visualization::Item::nodeItemsMap().end());
				auto fromVisualizationItem = *fromVisualization;

				auto overlay = new Visualization::ArrowOverlay(
				fromVisualizationItem, *toVisualization, Visualization::ArrowOverlay::itemStyles().get());
				fromVisualizationItem->addOverlay(overlay, ARROW_OVERLAY_GROUP);
			}
		}
	}
	else
	{
		auto astTuples = ts.tuples([](const Tuple& t) { return t.tag() == "ast"; });

		QHash<Model::Node*, QString> colors;

		for (auto t : astTuples)
		{
			Model::Node* node = t["ast"];
			QString& color = colors[node];
			if (color.isEmpty())
			{
				auto colorIt = t.find("color");
				if (colorIt != t.end())
				{
					QString c = colorIt->second;
					color = c;
				}
				else
					color = "red";
			}
		}

		for (auto it = colors.begin(); it != colors.end(); ++it)
		{
			Model::Node* node = it.key();
			Q_ASSERT(node);
			auto nodeVisualizationIt = Visualization::Item::nodeItemsMap().find(node);
			if (nodeVisualizationIt == Visualization::Item::nodeItemsMap().end())
				qWarning() << "no visualization for" << node->typeName();
			while (nodeVisualizationIt != Visualization::Item::nodeItemsMap().end())
			{
				auto item = *nodeVisualizationIt++;
				auto overlay = new Visualization::SelectionOverlay(
							item, Visualization::SelectionOverlay::itemStyles().get(it.value() + "Highlight"));
				item->addOverlay(overlay, HIGHLIGHT_OVERLAY_GROUP);
			}
		}
	}
}

} /* namespace InformationScripting */
<|endoftext|>


<commit_before>/* FasTC
 * Copyright (c) 2013 University of North Carolina at Chapel Hill.
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for educational, research, and non-profit purposes, without
 * fee, and without a written agreement is hereby granted, provided that the
 * above copyright notice, this paragraph, and the following four paragraphs
 * appear in all copies.
 *
 * Permission to incorporate this software into commercial products may be
 * obtained by contacting the authors or the Office of Technology Development
 * at the University of North Carolina at Chapel Hill <otd@unc.edu>.
 *
 * This software program and documentation are copyrighted by the University of
 * North Carolina at Chapel Hill. The software program and documentation are
 * supplied "as is," without any accompanying services from the University of
 * North Carolina at Chapel Hill or the authors. The University of North
 * Carolina at Chapel Hill and the authors do not warrant that the operation of
 * the program will be uninterrupted or error-free. The end-user understands
 * that the program was developed for research purposes and is advised not to
 * rely exclusively on the program for any reason.
 *
 * IN NO EVENT SHALL THE UNIVERSITY OF NORTH CAROLINA AT CHAPEL HILL OR THE
 * AUTHORS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL,
 * OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF
 * THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF NORTH CAROLINA
 * AT CHAPEL HILL OR THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 *
 * THE UNIVERSITY OF NORTH CAROLINA AT CHAPEL HILL AND THE AUTHORS SPECIFICALLY
 * DISCLAIM ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE AND ANY
 * STATUTORY WARRANTY OF NON-INFRINGEMENT. THE SOFTWARE PROVIDED HEREUNDER IS ON
 * AN "AS IS" BASIS, AND THE UNIVERSITY  OF NORTH CAROLINA AT CHAPEL HILL AND
 * THE AUTHORS HAVE NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
 * ENHANCEMENTS, OR MODIFICATIONS.
 *
 * Please send all BUG REPORTS to <pavel@cs.unc.edu>.
 *
 * The authors may be contacted via:
 *
 * Pavel Krajcevski
 * Dept of Computer Science
 * 201 S Columbia St
 * Frederick P. Brooks, Jr. Computer Science Bldg
 * Chapel Hill, NC 27599-3175
 * USA
 *
 * <http://gamma.cs.unc.edu/FasTC/>
 */

#include "Image.h"

#include <cassert>
#include <cstring>
#include <cstdio>

#include "Pixel.h"

#include "Core/include/Image.h"
#include "IO/include/ImageFile.h"

namespace PVRTCC {

Image::Image(uint32 height, uint32 width)
  : m_Width(width)
  , m_Height(height)
  , m_Pixels(new Pixel[width * height])
  , m_FractionalPixels(new Pixel[width * height]) {
  assert(width > 0);
  assert(height > 0);
}

Image::Image(uint32 height, uint32 width, const Pixel *pixels)
  : m_Width(width)
  , m_Height(height)
  , m_Pixels(new Pixel[width * height])
  , m_FractionalPixels(new Pixel[width * height]) {
  assert(width > 0);
  assert(height > 0);
  memcpy(m_Pixels, pixels, width * height * sizeof(Pixel));
}

Image::Image(const Image &other)
  : m_Width(other.m_Width)
  , m_Height(other.m_Height)
  , m_Pixels(new Pixel[other.m_Width * other.m_Height])
  , m_FractionalPixels(new Pixel[other.m_Width * other.m_Height]) {
  memcpy(m_Pixels, other.m_Pixels, m_Width * m_Height * sizeof(Pixel));
}

Image &Image::operator=(const Image &other) {
  m_Width = other.m_Width;
  m_Height = other.m_Height;

  assert(m_Pixels);
  delete m_Pixels;
  m_Pixels = new Pixel[other.m_Width * other.m_Height];
  memcpy(m_Pixels, other.m_Pixels, m_Width * m_Height * sizeof(Pixel));

  assert(m_FractionalPixels);
  delete m_FractionalPixels;
  m_FractionalPixels = new Pixel[other.m_Width * other.m_Height];
  memcpy(m_FractionalPixels, other.m_FractionalPixels,
         m_Width * m_Height * sizeof(Pixel));

  return *this;
}

Image::~Image() {
  assert(m_Pixels);
  delete [] m_Pixels;

  assert(m_FractionalPixels);
  delete [] m_FractionalPixels;
}

#ifndef NDEBUG
static bool CompareBitDepths(const uint8 (&depth1)[4],
                             const uint8 (&depth2)[4]) {
  bool ok = true;
  for(uint32 i = 0; i < 4; i++) {
    ok = ok && depth1[i] == depth2[i];
  }
  return ok;
}
#endif

void Image::BilinearUpscale(uint32 times, EWrapMode wrapMode) {
  const uint32 newWidth = m_Width << times;
  const uint32 newHeight = m_Height << times;

  const uint32 scale = 1 << times;
  const uint32 offset = scale >> 1;

  Pixel *upscaledPixels = new Pixel[newWidth * newHeight];

  assert(m_FractionalPixels);
  delete m_FractionalPixels;
  m_FractionalPixels = new Pixel[newWidth * newHeight];

  for(uint32 j = 0; j < newHeight; j++) {
    for(uint32 i = 0; i < newWidth; i++) {

      const uint32 pidx = j * newWidth + i;
      Pixel &p = upscaledPixels[pidx];
      Pixel &fp = m_FractionalPixels[pidx];

      const int32 highXIdx = (i + offset) / scale;
      const int32 lowXIdx = highXIdx - 1;
      const int32 highYIdx = (j + offset) / scale;
      const int32 lowYIdx = highYIdx - 1;

      const uint32 highXWeight = (i + offset) % scale;
      const uint32 lowXWeight = scale - highXWeight;
      const uint32 highYWeight = (j + offset) % scale;
      const uint32 lowYWeight = scale - highYWeight;

      const uint32 topLeftWeight = lowXWeight * lowYWeight;
      const uint32 topRightWeight = highXWeight * lowYWeight;
      const uint32 bottomLeftWeight = lowXWeight * highYWeight;
      const uint32 bottomRightWeight = highXWeight * highYWeight;

      const Pixel &topLeft = GetPixel(lowXIdx, lowYIdx, wrapMode);
      const Pixel &topRight = GetPixel(highXIdx, lowYIdx, wrapMode);
      const Pixel &bottomLeft = GetPixel(lowXIdx, highYIdx, wrapMode);
      const Pixel &bottomRight = GetPixel(highXIdx, highYIdx, wrapMode);

      // Make sure the bit depth matches the original...
      uint8 bitDepth[4];
      topLeft.GetBitDepth(bitDepth);
      p.ChangeBitDepth(bitDepth);

#ifndef NDEBUG
      uint8 debugDepth[4];

      topRight.GetBitDepth(debugDepth);
      assert(CompareBitDepths(bitDepth, debugDepth));

      bottomLeft.GetBitDepth(debugDepth);
      assert(CompareBitDepths(bitDepth, debugDepth));

      bottomRight.GetBitDepth(debugDepth);
      assert(CompareBitDepths(bitDepth, debugDepth));
#endif  // NDEBUG

      // bilerp each channel....
      const uint16 scaleMask = (scale * scale) - 1;
      uint8 fpDepths[4];
      for(uint32 c = 0; c < 4; c++) fpDepths[c] = times * times;
      fp.ChangeBitDepth(fpDepths);

      for(uint32 c = 0; c < 4; c++) {
        const uint32 tl = topLeft.Component(c) * topLeftWeight;
        const uint32 tr = topRight.Component(c) * topRightWeight;
        const uint32 bl = bottomLeft.Component(c) * bottomLeftWeight;
        const uint32 br = bottomRight.Component(c) * bottomRightWeight;
        const uint32 sum = tl + tr + bl + br;
        fp.Component(c) = sum & scaleMask;
        p.Component(c) = sum / (scale * scale);
      }
    }
  }

  delete m_Pixels;
  m_Pixels = upscaledPixels;
  m_Width = newWidth;
  m_Height = newHeight;
}

void Image::ChangeBitDepth(const uint8 (&depths)[4]) {
  for(uint32 j = 0; j < m_Height; j++) {
    for(uint32 i = 0; i < m_Width; i++) {
      uint32 pidx = j * m_Width + i;
      m_Pixels[pidx].ChangeBitDepth(depths);
    }
  }
}

void Image::ExpandTo8888() {
  uint8 currentDepth[4];
  m_Pixels[0].GetBitDepth(currentDepth);

  uint8 fractionDepth[4];
  const uint8 fullDepth[4] = { 8, 8, 8, 8 };

  for(uint32 j = 0; j < m_Height; j++) {
    for(uint32 i = 0; i < m_Width; i++) {

      uint32 pidx = j * m_Width + i;
      m_Pixels[pidx].ChangeBitDepth(fullDepth);
      m_FractionalPixels[pidx].GetBitDepth(fractionDepth);

      for(uint32 c = 0; c < 4; c++) {
        uint32 denominator = (1 << currentDepth[c]);
        uint32 numerator = denominator + 1;

        uint32 shift = fractionDepth[c] - (fullDepth[c] - currentDepth[c]);
        uint32 fractionBits = m_FractionalPixels[pidx].Component(c) >> shift;

        uint32 component = m_Pixels[pidx].Component(c);
        component += ((fractionBits * numerator) / denominator);

        m_Pixels[pidx].Component(c) = component;
      }
    }
  }
}

const Pixel &Image::GetPixel(int32 i, int32 j, EWrapMode wrapMode) {
  while(i < 0) {
    if(wrapMode == eWrapMode_Clamp) {
      i = 0;
    } else {
      i += m_Width;
    }
  }

  while(i >= static_cast<int32>(m_Width)) {
    if(wrapMode == eWrapMode_Clamp) {
      i = m_Width - 1;
    } else {
      i -= m_Width;
    }
  }

  while(j < 0) {
    if(wrapMode == eWrapMode_Clamp) {
      j = 0;
    } else {
      j += m_Height;
    }
  }

  while(j >= static_cast<int32>(m_Height)) {
    if(wrapMode == eWrapMode_Clamp) {
      j = m_Height - 1;
    } else {
      j -= m_Height;
    }
  }

  return m_Pixels[j * m_Width + i];
}

Pixel & Image::operator()(uint32 i, uint32 j) {
  assert(i < m_Width);
  assert(j < m_Height);
  return m_Pixels[j * m_Width + i];
}

const Pixel & Image::operator()(uint32 i, uint32 j) const {
  assert(i < m_Width);
  assert(j < m_Height);
  return m_Pixels[j * m_Width + i];
}

void Image::DebugOutput(const char *filename) const {
  uint32 *outPixels = new uint32[m_Width * m_Height];
  const uint8 fullDepth[4] = { 8, 8, 8, 8 };
  for(int j = 0; j < m_Height; j++) {
    for(int i = 0; i < m_Width; i++) {
      uint32 idx = j * m_Width + i;
      Pixel p = m_Pixels[idx];
      p.ChangeBitDepth(fullDepth);
      outPixels[idx] = p.PackRGBA();
    }
  }

  ::Image img(m_Height, m_Width, outPixels);

  char debugFilename[256];
  snprintf(debugFilename, sizeof(debugFilename), "%s.png", filename);

  ::ImageFile imgFile(debugFilename, eFileFormat_PNG, img);
  imgFile.Write();
}

}  // namespace PVRTCC
<commit_msg>Fix bug with debug images<commit_after>/* FasTC
 * Copyright (c) 2013 University of North Carolina at Chapel Hill.
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for educational, research, and non-profit purposes, without
 * fee, and without a written agreement is hereby granted, provided that the
 * above copyright notice, this paragraph, and the following four paragraphs
 * appear in all copies.
 *
 * Permission to incorporate this software into commercial products may be
 * obtained by contacting the authors or the Office of Technology Development
 * at the University of North Carolina at Chapel Hill <otd@unc.edu>.
 *
 * This software program and documentation are copyrighted by the University of
 * North Carolina at Chapel Hill. The software program and documentation are
 * supplied "as is," without any accompanying services from the University of
 * North Carolina at Chapel Hill or the authors. The University of North
 * Carolina at Chapel Hill and the authors do not warrant that the operation of
 * the program will be uninterrupted or error-free. The end-user understands
 * that the program was developed for research purposes and is advised not to
 * rely exclusively on the program for any reason.
 *
 * IN NO EVENT SHALL THE UNIVERSITY OF NORTH CAROLINA AT CHAPEL HILL OR THE
 * AUTHORS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL,
 * OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF
 * THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF NORTH CAROLINA
 * AT CHAPEL HILL OR THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 *
 * THE UNIVERSITY OF NORTH CAROLINA AT CHAPEL HILL AND THE AUTHORS SPECIFICALLY
 * DISCLAIM ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE AND ANY
 * STATUTORY WARRANTY OF NON-INFRINGEMENT. THE SOFTWARE PROVIDED HEREUNDER IS ON
 * AN "AS IS" BASIS, AND THE UNIVERSITY  OF NORTH CAROLINA AT CHAPEL HILL AND
 * THE AUTHORS HAVE NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
 * ENHANCEMENTS, OR MODIFICATIONS.
 *
 * Please send all BUG REPORTS to <pavel@cs.unc.edu>.
 *
 * The authors may be contacted via:
 *
 * Pavel Krajcevski
 * Dept of Computer Science
 * 201 S Columbia St
 * Frederick P. Brooks, Jr. Computer Science Bldg
 * Chapel Hill, NC 27599-3175
 * USA
 *
 * <http://gamma.cs.unc.edu/FasTC/>
 */

#include "Image.h"

#include <cassert>
#include <cstring>
#include <cstdio>

#include "Pixel.h"

#include "Core/include/Image.h"
#include "IO/include/ImageFile.h"

namespace PVRTCC {

Image::Image(uint32 height, uint32 width)
  : m_Width(width)
  , m_Height(height)
  , m_Pixels(new Pixel[width * height])
  , m_FractionalPixels(new Pixel[width * height]) {
  assert(width > 0);
  assert(height > 0);
}

Image::Image(uint32 height, uint32 width, const Pixel *pixels)
  : m_Width(width)
  , m_Height(height)
  , m_Pixels(new Pixel[width * height])
  , m_FractionalPixels(new Pixel[width * height]) {
  assert(width > 0);
  assert(height > 0);
  memcpy(m_Pixels, pixels, width * height * sizeof(Pixel));
}

Image::Image(const Image &other)
  : m_Width(other.m_Width)
  , m_Height(other.m_Height)
  , m_Pixels(new Pixel[other.m_Width * other.m_Height])
  , m_FractionalPixels(new Pixel[other.m_Width * other.m_Height]) {
  memcpy(m_Pixels, other.m_Pixels, m_Width * m_Height * sizeof(Pixel));
}

Image &Image::operator=(const Image &other) {
  m_Width = other.m_Width;
  m_Height = other.m_Height;

  assert(m_Pixels);
  delete m_Pixels;
  m_Pixels = new Pixel[other.m_Width * other.m_Height];
  memcpy(m_Pixels, other.m_Pixels, m_Width * m_Height * sizeof(Pixel));

  assert(m_FractionalPixels);
  delete m_FractionalPixels;
  m_FractionalPixels = new Pixel[other.m_Width * other.m_Height];
  memcpy(m_FractionalPixels, other.m_FractionalPixels,
         m_Width * m_Height * sizeof(Pixel));

  return *this;
}

Image::~Image() {
  assert(m_Pixels);
  delete [] m_Pixels;

  assert(m_FractionalPixels);
  delete [] m_FractionalPixels;
}

#ifndef NDEBUG
static bool CompareBitDepths(const uint8 (&depth1)[4],
                             const uint8 (&depth2)[4]) {
  bool ok = true;
  for(uint32 i = 0; i < 4; i++) {
    ok = ok && depth1[i] == depth2[i];
  }
  return ok;
}
#endif

void Image::BilinearUpscale(uint32 times, EWrapMode wrapMode) {
  const uint32 newWidth = m_Width << times;
  const uint32 newHeight = m_Height << times;

  const uint32 scale = 1 << times;
  const uint32 offset = scale >> 1;

  Pixel *upscaledPixels = new Pixel[newWidth * newHeight];

  assert(m_FractionalPixels);
  delete m_FractionalPixels;
  m_FractionalPixels = new Pixel[newWidth * newHeight];

  for(uint32 j = 0; j < newHeight; j++) {
    for(uint32 i = 0; i < newWidth; i++) {

      const uint32 pidx = j * newWidth + i;
      Pixel &p = upscaledPixels[pidx];
      Pixel &fp = m_FractionalPixels[pidx];

      const int32 highXIdx = (i + offset) / scale;
      const int32 lowXIdx = highXIdx - 1;
      const int32 highYIdx = (j + offset) / scale;
      const int32 lowYIdx = highYIdx - 1;

      const uint32 highXWeight = (i + offset) % scale;
      const uint32 lowXWeight = scale - highXWeight;
      const uint32 highYWeight = (j + offset) % scale;
      const uint32 lowYWeight = scale - highYWeight;

      const uint32 topLeftWeight = lowXWeight * lowYWeight;
      const uint32 topRightWeight = highXWeight * lowYWeight;
      const uint32 bottomLeftWeight = lowXWeight * highYWeight;
      const uint32 bottomRightWeight = highXWeight * highYWeight;

      const Pixel &topLeft = GetPixel(lowXIdx, lowYIdx, wrapMode);
      const Pixel &topRight = GetPixel(highXIdx, lowYIdx, wrapMode);
      const Pixel &bottomLeft = GetPixel(lowXIdx, highYIdx, wrapMode);
      const Pixel &bottomRight = GetPixel(highXIdx, highYIdx, wrapMode);

      // Make sure the bit depth matches the original...
      uint8 bitDepth[4];
      topLeft.GetBitDepth(bitDepth);
      p.ChangeBitDepth(bitDepth);

#ifndef NDEBUG
      uint8 debugDepth[4];

      topRight.GetBitDepth(debugDepth);
      assert(CompareBitDepths(bitDepth, debugDepth));

      bottomLeft.GetBitDepth(debugDepth);
      assert(CompareBitDepths(bitDepth, debugDepth));

      bottomRight.GetBitDepth(debugDepth);
      assert(CompareBitDepths(bitDepth, debugDepth));
#endif  // NDEBUG

      // bilerp each channel....
      const uint16 scaleMask = (scale * scale) - 1;
      uint8 fpDepths[4];
      for(uint32 c = 0; c < 4; c++) fpDepths[c] = times * times;
      fp.ChangeBitDepth(fpDepths);

      for(uint32 c = 0; c < 4; c++) {
        const uint32 tl = topLeft.Component(c) * topLeftWeight;
        const uint32 tr = topRight.Component(c) * topRightWeight;
        const uint32 bl = bottomLeft.Component(c) * bottomLeftWeight;
        const uint32 br = bottomRight.Component(c) * bottomRightWeight;
        const uint32 sum = tl + tr + bl + br;
        fp.Component(c) = sum & scaleMask;
        p.Component(c) = sum / (scale * scale);
      }
    }
  }

  delete m_Pixels;
  m_Pixels = upscaledPixels;
  m_Width = newWidth;
  m_Height = newHeight;
}

void Image::ChangeBitDepth(const uint8 (&depths)[4]) {
  for(uint32 j = 0; j < m_Height; j++) {
    for(uint32 i = 0; i < m_Width; i++) {
      uint32 pidx = j * m_Width + i;
      m_Pixels[pidx].ChangeBitDepth(depths);
    }
  }
}

void Image::ExpandTo8888() {
  uint8 currentDepth[4];
  m_Pixels[0].GetBitDepth(currentDepth);

  uint8 fractionDepth[4];
  const uint8 fullDepth[4] = { 8, 8, 8, 8 };

  for(uint32 j = 0; j < m_Height; j++) {
    for(uint32 i = 0; i < m_Width; i++) {

      uint32 pidx = j * m_Width + i;
      m_Pixels[pidx].ChangeBitDepth(fullDepth);
      m_FractionalPixels[pidx].GetBitDepth(fractionDepth);

      for(uint32 c = 0; c < 4; c++) {
        uint32 denominator = (1 << currentDepth[c]);
        uint32 numerator = denominator + 1;

        uint32 shift = fractionDepth[c] - (fullDepth[c] - currentDepth[c]);
        uint32 fractionBits = m_FractionalPixels[pidx].Component(c) >> shift;

        uint32 component = m_Pixels[pidx].Component(c);
        component += ((fractionBits * numerator) / denominator);

        m_Pixels[pidx].Component(c) = component;
      }
    }
  }
}

const Pixel &Image::GetPixel(int32 i, int32 j, EWrapMode wrapMode) {
  while(i < 0) {
    if(wrapMode == eWrapMode_Clamp) {
      i = 0;
    } else {
      i += m_Width;
    }
  }

  while(i >= static_cast<int32>(m_Width)) {
    if(wrapMode == eWrapMode_Clamp) {
      i = m_Width - 1;
    } else {
      i -= m_Width;
    }
  }

  while(j < 0) {
    if(wrapMode == eWrapMode_Clamp) {
      j = 0;
    } else {
      j += m_Height;
    }
  }

  while(j >= static_cast<int32>(m_Height)) {
    if(wrapMode == eWrapMode_Clamp) {
      j = m_Height - 1;
    } else {
      j -= m_Height;
    }
  }

  return m_Pixels[j * m_Width + i];
}

Pixel & Image::operator()(uint32 i, uint32 j) {
  assert(i < m_Width);
  assert(j < m_Height);
  return m_Pixels[j * m_Width + i];
}

const Pixel & Image::operator()(uint32 i, uint32 j) const {
  assert(i < m_Width);
  assert(j < m_Height);
  return m_Pixels[j * m_Width + i];
}

void Image::DebugOutput(const char *filename) const {
  uint32 *outPixels = new uint32[m_Width * m_Height];
  const uint8 fullDepth[4] = { 8, 8, 8, 8 };
  for(int j = 0; j < m_Height; j++) {
    for(int i = 0; i < m_Width; i++) {
      uint32 idx = j * m_Width + i;
      Pixel p = m_Pixels[idx];
      p.ChangeBitDepth(fullDepth);
      outPixels[idx] = p.PackRGBA();
    }
  }

  ::Image img(m_Width, m_Height, outPixels);

  char debugFilename[256];
  snprintf(debugFilename, sizeof(debugFilename), "%s.png", filename);

  ::ImageFile imgFile(debugFilename, eFileFormat_PNG, img);
  imgFile.Write();
}

}  // namespace PVRTCC
<|endoftext|>


<commit_before>/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

 Module:       FGPID.cpp
 Author:       Jon S. Berndt
 Date started: 6/17/2006

 ------------- Copyright (C) 2006 Jon S. Berndt (jsb@hal-pc.org) -------------

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

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

 You should have received a copy of the GNU Lesser General Public License along with
 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 Place - Suite 330, Boston, MA  02111-1307, USA.

 Further information about the GNU Lesser General Public License can also be found on
 the world wide web at http://www.gnu.org.

HISTORY
--------------------------------------------------------------------------------
Initial code 6/17/2006 JSB

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
COMMENTS, REFERENCES,  and NOTES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

#include "FGPID.h"

namespace JSBSim {

static const char *IdSrc = "$Id: FGPID.cpp,v 1.4 2007/11/13 12:35:55 jberndt Exp $";
static const char *IdHdr = ID_PID;

/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

FGPID::FGPID(FGFCS* fcs, Element* element) : FGFCSComponent(fcs, element)
{
  dt = fcs->GetState()->Getdt();

  Kp = Ki = Kd = 0.0;
  I_out_total = 0.0;
  Input_prev = Input_prev2 = 0.0;
  Trigger = 0;

  if (element->FindElement("kp")) Kp = element->FindElementValueAsNumber("kp");
  if (element->FindElement("ki")) Ki = element->FindElementValueAsNumber("ki");
  if (element->FindElement("kd")) Kd = element->FindElementValueAsNumber("kd");
  if (element->FindElement("trigger")) {
    Trigger =  PropertyManager->GetNode(element->FindElementValue("trigger"));
  }

  FGFCSComponent::bind();

  Debug(0);
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

FGPID::~FGPID()
{
  Debug(1);
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

bool FGPID::Run(void )
{
  double I_out_delta = 0.0;
  double P_out, D_out;

  Input = InputNodes[0]->getDoubleValue() * InputSigns[0];

  P_out = Kp * (Input - Input_prev);
  D_out = (Kd / dt) * (Input - 2*Input_prev + Input_prev2);

  // Do not continue to integrate the input to the integrator if a wind-up
  // condition is sensed - that is, if the property pointed to by the trigger
  // element is non-zero.

  if (Trigger != 0) {
    double test = Trigger->getDoubleValue();
    if (fabs(test) < 0.000001) I_out_delta = Ki * dt * Input;  // Normal
    if (test < -0.000001)      I_out_total = 0.0;  // Reset integrator to 0.0
  } else { // no anti-wind-up trigger defined
    I_out_delta = Ki * dt * Input;
  }

  I_out_total += I_out_delta;

  Output = P_out + I_out_total + D_out;

  Input_prev = Input;
  Input_prev2 = Input_prev;

  Clip();
  if (IsOutput) SetOutput();

  return true;
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//    The bitmasked value choices are as follows:
//    unset: In this case (the default) JSBSim would only print
//       out the normally expected messages, essentially echoing
//       the config files as they are read. If the environment
//       variable is not set, debug_lvl is set to 1 internally
//    0: This requests JSBSim not to output any messages
//       whatsoever.
//    1: This value explicity requests the normal JSBSim
//       startup messages
//    2: This value asks for a message to be printed out when
//       a class is instantiated
//    4: When this value is set, a message is displayed when a
//       FGModel object executes its Run() method
//    8: When this value is set, various runtime state variables
//       are printed out periodically
//    16: When set various parameters are sanity checked and
//       a message is printed out when they go out of bounds

void FGPID::Debug(int from)
{
  if (debug_lvl <= 0) return;

  if (debug_lvl & 1) { // Standard console startup message output
    if (from == 0) { // Constructor
      if (InputSigns[0] < 0)
        cout << "      INPUT: -" << InputNodes[0]->getName() << endl;
      else
        cout << "      INPUT: " << InputNodes[0]->getName() << endl;

      if (IsOutput) cout << "      OUTPUT: " << OutputNode->getName() << endl;
    }
  }
  if (debug_lvl & 2 ) { // Instantiation/Destruction notification
    if (from == 0) cout << "Instantiated: FGPID" << endl;
    if (from == 1) cout << "Destroyed:    FGPID" << endl;
  }
  if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
  }
  if (debug_lvl & 8 ) { // Runtime state variables
  }
  if (debug_lvl & 16) { // Sanity checking
  }
  if (debug_lvl & 64) {
    if (from == 0) { // Constructor
      cout << IdSrc << endl;
      cout << IdHdr << endl;
    }
  }
}
}
<commit_msg>improved comments, etc.<commit_after>/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

 Module:       FGPID.cpp
 Author:       Jon S. Berndt
 Date started: 6/17/2006

 ------------- Copyright (C) 2006 Jon S. Berndt (jsb@hal-pc.org) -------------

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

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

 You should have received a copy of the GNU Lesser General Public License along with
 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 Place - Suite 330, Boston, MA  02111-1307, USA.

 Further information about the GNU Lesser General Public License can also be found on
 the world wide web at http://www.gnu.org.

HISTORY
--------------------------------------------------------------------------------
Initial code 6/17/2006 JSB

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
COMMENTS, REFERENCES,  and NOTES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

#include "FGPID.h"

namespace JSBSim {

static const char *IdSrc = "$Id: FGPID.cpp,v 1.5 2007/11/13 12:57:48 jberndt Exp $";
static const char *IdHdr = ID_PID;

/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

FGPID::FGPID(FGFCS* fcs, Element* element) : FGFCSComponent(fcs, element)
{
  dt = fcs->GetState()->Getdt();

  Kp = Ki = Kd = 0.0;
  I_out_total = 0.0;
  Input_prev = Input_prev2 = 0.0;
  Trigger = 0;

  if (element->FindElement("kp")) Kp = element->FindElementValueAsNumber("kp");
  if (element->FindElement("ki")) Ki = element->FindElementValueAsNumber("ki");
  if (element->FindElement("kd")) Kd = element->FindElementValueAsNumber("kd");
  if (element->FindElement("trigger")) {
    Trigger =  PropertyManager->GetNode(element->FindElementValue("trigger"));
  }

  FGFCSComponent::bind();

  Debug(0);
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

FGPID::~FGPID()
{
  Debug(1);
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

bool FGPID::Run(void )
{
  double I_out_delta = 0.0;
  double P_out, D_out;

  Input = InputNodes[0]->getDoubleValue() * InputSigns[0];

  P_out = Kp * (Input - Input_prev);
  D_out = (Kd / dt) * (Input - 2*Input_prev + Input_prev2);

  // Do not continue to integrate the input to the integrator if a wind-up
  // condition is sensed - that is, if the property pointed to by the trigger
  // element is non-zero. Reset the integrator to 0.0 if the Trigger value
  // is negative.

  if (Trigger != 0) {
    double test = Trigger->getDoubleValue();
    if (fabs(test) < 0.000001) I_out_delta = Ki * dt * Input;  // Normal
    if (test < 0.0)            I_out_total = 0.0;  // Reset integrator to 0.0
  } else { // no anti-wind-up trigger defined
    I_out_delta = Ki * dt * Input;
  }

  I_out_total += I_out_delta;

  Output = P_out + I_out_total + D_out;

  Input_prev = Input;
  Input_prev2 = Input_prev;

  Clip();
  if (IsOutput) SetOutput();

  return true;
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//    The bitmasked value choices are as follows:
//    unset: In this case (the default) JSBSim would only print
//       out the normally expected messages, essentially echoing
//       the config files as they are read. If the environment
//       variable is not set, debug_lvl is set to 1 internally
//    0: This requests JSBSim not to output any messages
//       whatsoever.
//    1: This value explicity requests the normal JSBSim
//       startup messages
//    2: This value asks for a message to be printed out when
//       a class is instantiated
//    4: When this value is set, a message is displayed when a
//       FGModel object executes its Run() method
//    8: When this value is set, various runtime state variables
//       are printed out periodically
//    16: When set various parameters are sanity checked and
//       a message is printed out when they go out of bounds

void FGPID::Debug(int from)
{
  if (debug_lvl <= 0) return;

  if (debug_lvl & 1) { // Standard console startup message output
    if (from == 0) { // Constructor
      if (InputSigns[0] < 0)
        cout << "      INPUT: -" << InputNodes[0]->getName() << endl;
      else
        cout << "      INPUT: " << InputNodes[0]->getName() << endl;

      if (IsOutput) cout << "      OUTPUT: " << OutputNode->getName() << endl;
    }
  }
  if (debug_lvl & 2 ) { // Instantiation/Destruction notification
    if (from == 0) cout << "Instantiated: FGPID" << endl;
    if (from == 1) cout << "Destroyed:    FGPID" << endl;
  }
  if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
  }
  if (debug_lvl & 8 ) { // Runtime state variables
  }
  if (debug_lvl & 16) { // Sanity checking
  }
  if (debug_lvl & 64) {
    if (from == 0) { // Constructor
      cout << IdSrc << endl;
      cout << IdHdr << endl;
    }
  }
}
}
<|endoftext|>