<commit_before>/*
 * C S O U N D
 *
 * L I C E N S E
 *
 * This software is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This software 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 software; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#include "VoiceleadingNode.hpp"
#include "Conversions.hpp"
#include "Voicelead.hpp"
#include "System.hpp"

#include <cmath>
#include <cfloat>
#include <sstream>

namespace csound
{
  extern void printChord(std::ostream &stream, std::string label, const std::vector<double> &chord);

  extern void printChord(std::string label, const std::vector<double> &chord);

  /**
   * Utility class for storing voice-leading operations.
   */
  VoiceleadingOperation::VoiceleadingOperation() :
    beginTime(0.0),
    rescaledBeginTime(0.0),
    rescaledEndTime(0.0),
    P(DBL_MAX),
    T(DBL_MAX),
    C(DBL_MAX),
    K(DBL_MAX),
    Q(DBL_MAX),
    V(DBL_MAX),
    L(false),
    begin(0),
    end(0),
    avoidParallels(false)
  {
  }

  VoiceleadingOperation::~VoiceleadingOperation()
  {
  }

  VoiceleadingNode::VoiceleadingNode() :
    base(36.0),
    range(60.0),
    rescaleTimes(true),
    avoidParallels(true),
    divisionsPerOctave(12)
  {
  }

  VoiceleadingNode::~VoiceleadingNode()
  {
  }

  std::ostream &operator << (std::ostream &stream, const VoiceleadingOperation &operation)
  {
    stream << "  beginTime:         " << operation.beginTime << std::endl;
    stream << "  rescaledBeginTime: " << operation.rescaledBeginTime << std::endl;
    stream << "  rescaledEndTime:   " << operation.rescaledEndTime << std::endl;
    stream << "  begin:             " << operation.begin << std::endl;
    stream << "  end:               " << operation.end << std::endl;
    if (!(operation.P == DBL_MAX)) {
      stream << "  P:                 " << operation.P << std::endl;
    }
    if (!(operation.T == DBL_MAX)) {
      stream << "  T:                 " << operation.T << std::endl;
    }
    if (!(operation.C == DBL_MAX)) {
      stream << "  C:                 " << operation.C << std::endl;
    }
    if (!(operation.K == DBL_MAX)) {
      stream << "  K:                 " << operation.K << std::endl;
    }
    if (!(operation.Q == DBL_MAX)) {
      stream << "  Q:                 " << operation.Q << std::endl;
    }
    if (!(operation.V == DBL_MAX)) {
      stream << "  V:                 " << operation.V << std::endl;
    }
    if (operation.L) {
      stream << "  L:                 " << int(operation.L) << std::endl;
    }
    return stream;
  }

  void VoiceleadingNode::apply(Score &score, const VoiceleadingOperation &priorOperation, const VoiceleadingOperation &operation)
  {
    if ( (System::getMessageLevel() & System::INFORMATION_LEVEL) == System::INFORMATION_LEVEL) {
      std::stringstream stream;
      stream << "BEGAN VoiceleadingNode::apply:..." << std::endl;
      stream << "Events in score:     " << score.size() << std::endl;
      stream << "Score duration:      " << score.getDuration() << std::endl;
      stream << "Events in operation: " << (operation.end - operation.begin) << std::endl;
      stream << "priorOperation:      " << std::endl << priorOperation;
      stream << "currrentOperation:   " << std::endl << operation;
      stream << "modality:            ";
      printChord(stream, "", modality);
      stream << std::endl;
      System::inform(stream.str().c_str());
    }
    if (operation.begin == operation.end) {
      return;
    }
    if (!(operation.K == DBL_MAX)) {
      if ((operation.V == DBL_MAX) && (!operation.L)) {
	System::inform("Operation: K\n");
	score.setK(priorOperation.begin,
		   operation.begin,
		   operation.end,
		   base,
		   range);
      } else if ((operation.V != DBL_MAX) && (!operation.L)) {
	System::inform("Operation: KV\n");
	score.setKV(priorOperation.begin,
		    operation.begin,
		    operation.end,
		    operation.V,
		    base,
		    range);
      } else if ((operation.V == DBL_MAX) && (operation.L)) {
	System::inform("Operation: KL\n");
	score.setKL(priorOperation.begin,
		    operation.begin,
		    operation.end,
		    base,
		    range,
		    operation.avoidParallels);
      }
    } else if (!(operation.Q == DBL_MAX)) {
      if ((operation.V == DBL_MAX) && (operation.L == DBL_MAX)) {
	System::inform("Operation: Q\n");
	score.setQ(priorOperation.begin,
		   operation.begin,
		   operation.end,
		   operation.Q,
		   modality,
		   base,
		   range);
      } else if ((operation.V != DBL_MAX) && (operation.L == DBL_MAX)) {
	System::inform("Operation: QV\n");
	score.setQV(priorOperation.begin,
		    operation.begin,
		    operation.end,
		    operation.Q,
		    modality,
		    operation.V,
		    base,
		    range);
      } else if ((operation.V == DBL_MAX) && (operation.L != DBL_MAX)) {
	System::inform("Operation: QL\n");
	score.setQL(priorOperation.begin,
		    operation.begin,
		    operation.end,
		    operation.Q,
		    modality,
		    base,
		    range,
		    operation.avoidParallels);
      }
    } else if (!(operation.P == DBL_MAX) && !(operation.T == DBL_MAX)) {
      if (!(operation.V == DBL_MAX)) {
	System::inform("Operation: PTV\n");
        score.setPTV(operation.begin,
                     operation.end,
                     operation.P,
                     operation.T,
                     operation.V,
                     base,
                     range);
      } else if (operation.L) {
	System::inform("Operation: PTL\n");
        score.setPT(operation.begin,
                    operation.end,
                    operation.P,
                    operation.T,
                    base,
                    range,
                    divisionsPerOctave);
        score.voicelead(priorOperation.begin,
                        priorOperation.end,
                        operation.begin,
                        operation.end,
                        base,
                        range,
                        avoidParallels,
                        divisionsPerOctave);
      } else {
	System::inform("Operation: PT\n");
        score.setPT(operation.begin,
                    operation.end,
                    operation.P,
                    operation.T,
                    base,
                    range,
                    divisionsPerOctave);
      }
    } else if (!(operation.C == DBL_MAX)) {
      if (!(operation.V == DBL_MAX)) {
	System::inform("Operation: CV\n");
        std::vector<double> pcs = Voicelead::mToPitchClassSet(Voicelead::cToM(operation.C, divisionsPerOctave), divisionsPerOctave);
        printChord("CV", pcs);
        std::vector<double> pt = Voicelead::pitchClassSetToPandT(pcs, divisionsPerOctave);
        double prime = pt[0];
        double transposition = pt[1];
        System::inform("prime: %f transposition %f: divisionsPerOctave %d\n", prime, transposition, divisionsPerOctave);
        score.setPTV(operation.begin,
                     operation.end,
                     prime,
                     transposition,
                     operation.V,
                     base,
                     range);
      } else if (operation.L) {
	System::inform("Operation: CL\n");
        std::vector<double> pcs = Voicelead::mToPitchClassSet(Voicelead::cToM(operation.C, divisionsPerOctave), divisionsPerOctave);
        printChord("CL", pcs);
        score.voicelead(priorOperation.begin,
                        priorOperation.end,
                        operation.begin,
                        operation.end,
                        pcs,
                        base,
                        range,
                        avoidParallels,
                        divisionsPerOctave);
      } else {
	System::inform("Operation: C\n");
        std::vector<double> pcs = Voicelead::mToPitchClassSet(Voicelead::cToM(operation.C, divisionsPerOctave), divisionsPerOctave);
        score.setPitchClassSet(operation.begin,
                               operation.end,
                               pcs,
                               divisionsPerOctave);
      }
    } else {
	System::inform("Operation: V\n");
        if (!(operation.V == DBL_MAX)) {
        std::vector<double> ptv = score.getPTV(operation.begin,
                                               operation.end,
                                               base,
                                               range,
                                               divisionsPerOctave);
        score.setPTV(operation.begin,
                     operation.end,
                     ptv[0],
                     ptv[1],
                     operation.V,
                     base,
                     range,
                     divisionsPerOctave);
      } else if (operation.L) {
	System::inform("Operation: L\n");
        score.voicelead(priorOperation.begin,
                        priorOperation.end,
                        operation.begin,
                        operation.end,
                        base,
                        range,
                        avoidParallels,
                        divisionsPerOctave);
      }
    }
    System::message("ENDED VoiceleadingNode::apply.\n");
  }

  void VoiceleadingNode::PT(double time, double P, double T)
  {
    operations[time].beginTime = time;
    operations[time].P = P;
    operations[time].T = T;
  }

  void VoiceleadingNode::PTV(double time, double P, double T, double V)
  {
    operations[time].beginTime = time;
    operations[time].P = P;
    operations[time].T = T;
    operations[time].V = V;
  }

  void VoiceleadingNode::PTL(double time, double P, double T, bool avoidParallels)
  {
    operations[time].beginTime = time;
    operations[time].P = P;
    operations[time].T = T;
    operations[time].L = true;
    operations[time].avoidParallels = avoidParallels;
  }

  void VoiceleadingNode::C(double time, double C_)
  {
    operations[time].beginTime = time;
    operations[time].C = C_;
  }

  void VoiceleadingNode::C(double time, std::string C_)
  {
    C(time, Voicelead::nameToC(C_, divisionsPerOctave));
  }

  void VoiceleadingNode::CV(double time, double C, double V)
  {
    operations[time].beginTime = time;
    operations[time].C = C;
    operations[time].V = V;
  }

  void VoiceleadingNode::CV(double time, std::string C, double V)
  {
    CV(time, Voicelead::nameToC(C, divisionsPerOctave), V);
  }

  void VoiceleadingNode::CL(double time, double C, bool avoidParallels)
  {
    operations[time].beginTime = time;
    operations[time].C = C;
    operations[time].L = true;
    operations[time].avoidParallels = avoidParallels;
  }

  void VoiceleadingNode::CL(double time, std::string C, bool avoidParallels)
  {
    CL(time, Voicelead::nameToC(C, divisionsPerOctave), avoidParallels);
  }

  void VoiceleadingNode::K(double time)
  {
    operations[time].beginTime = time;
    operations[time].K = 1.0;
  }

  void VoiceleadingNode::KV(double time, double V)
  {
    operations[time].beginTime = time;
    operations[time].K = 1.0;
    operations[time].V = V;
  }

  void VoiceleadingNode::KL(double time, bool avoidParallels)
  {
    operations[time].beginTime = time;
    operations[time].K = 1.0;
    operations[time].L = true;
    operations[time].avoidParallels = avoidParallels;
  }

  void VoiceleadingNode::Q(double time, double Q)
  {
    operations[time].beginTime = time;
    operations[time].Q = Q;
  }

  void VoiceleadingNode::QV(double time, double Q, double V)
  {
    operations[time].beginTime = time;
    operations[time].Q = Q;
    operations[time].V = V;
  }

  void VoiceleadingNode::QL(double time, double Q, bool avoidParallels)
  {
    operations[time].beginTime = time;
    operations[time].Q = Q;
    operations[time].L = true;
    operations[time].avoidParallels = avoidParallels;
  }

  void VoiceleadingNode::V(double time, double V_)
  {
    operations[time].beginTime = time;
    operations[time].V = V_;
  }

  void VoiceleadingNode::L(double time, bool avoidParallels)
  {
    operations[time].beginTime = time;
    operations[time].L = true;
    operations[time].avoidParallels = avoidParallels;
  }

  void VoiceleadingNode::produceOrTransform(Score &score, size_t beginAt, size_t endAt, const ublas::matrix<double> &coordinates)
  {
    transform(score, rescaleTimes);
  }

  void VoiceleadingNode::transform(Score &score, bool rescaleTimes_)
  {
    if (operations.empty()) {
      return;
    }
    // Find the time rescale factor.
    score.sort();
    score.findScale();
    double scoreMinTime = score.scaleActualMinima.getTime();
    double scoreDuration = score.getDuration();
    double scoreMaxTime = scoreMinTime + scoreDuration;
    double operationMaxTime = 0.0;
    double timeScale = 1.0;
    std::vector<VoiceleadingOperation *> ops;
    for (std::map<double, VoiceleadingOperation>::iterator it = operations.begin(); it != operations.end(); ++it) {
      if (it->second.beginTime > operationMaxTime) {
        operationMaxTime = it->second.beginTime;
      }
      ops.push_back(&it->second);
    }

    if (rescaleTimes_) {
      if (operationMaxTime > 0.0) {
        timeScale = scoreMaxTime / operationMaxTime;
      }
    }
    System::inform("BEGAN VoiceleadingNode::produceOrTransform  operationMaxTime: %f  origin: %f  duration: %f  scoreMaxTime: %f  timeScale: %f...\n",
                   operationMaxTime,
                   scoreMinTime,
                   scoreDuration,
                   scoreMaxTime,
                   timeScale);
    VoiceleadingOperation *priorOperation = 0;
    VoiceleadingOperation *currentOperation = 0;
    VoiceleadingOperation *nextOperation = 0;
    int priorIndex = 0;
    int currentIndex = 0;
    int nextIndex = 0;
    int backIndex = ops.size() - 1;
    int endIndex = ops.size();
    for (currentIndex = 0; currentIndex < endIndex; ++currentIndex) {
      if (currentIndex == 0) {
	priorIndex = currentIndex;
      } else {
	priorIndex = currentIndex - 1;
      }
      if (currentIndex == backIndex) {
	nextIndex = currentIndex;
      } else {
	nextIndex = currentIndex + 1;
      }
      priorOperation = ops[priorIndex];
      currentOperation = ops[currentIndex];
      nextOperation = ops[nextIndex];
      currentOperation->rescaledBeginTime = currentOperation->beginTime * timeScale;
      currentOperation->begin = score.indexAtTime(currentOperation->rescaledBeginTime);
      currentOperation->rescaledEndTime = nextOperation->beginTime * timeScale;
      currentOperation->end = score.indexAfterTime(currentOperation->rescaledEndTime);
      apply(score, *priorOperation, *currentOperation);
    }
    System::inform("ENDED VoiceleadingNode::produceOrTransform.\n");
  }

}
<commit_msg>*** empty log message ***<commit_after>/*
 * C S O U N D
 *
 * L I C E N S E
 *
 * This software is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This software 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 software; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#include "VoiceleadingNode.hpp"
#include "Conversions.hpp"
#include "Voicelead.hpp"
#include "System.hpp"

#include <cmath>
#include <cfloat>
#include <sstream>

namespace csound
{
  extern void printChord(std::ostream &stream, std::string label, const std::vector<double> &chord);

  extern void printChord(std::string label, const std::vector<double> &chord);

  /**
   * Utility class for storing voice-leading operations.
   */
  VoiceleadingOperation::VoiceleadingOperation() :
    beginTime(0.0),
    rescaledBeginTime(0.0),
    rescaledEndTime(0.0),
    P(DBL_MAX),
    T(DBL_MAX),
    C(DBL_MAX),
    K(DBL_MAX),
    Q(DBL_MAX),
    V(DBL_MAX),
    L(false),
    begin(0),
    end(0),
    avoidParallels(false)
  {
  }

  VoiceleadingOperation::~VoiceleadingOperation()
  {
  }

  VoiceleadingNode::VoiceleadingNode() :
    base(36.0),
    range(60.0),
    rescaleTimes(true),
    avoidParallels(true),
    divisionsPerOctave(12)
  {
  }

  VoiceleadingNode::~VoiceleadingNode()
  {
  }

  std::ostream &operator << (std::ostream &stream, const VoiceleadingOperation &operation)
  {
    stream << "  beginTime:         " << operation.beginTime << std::endl;
    stream << "  rescaledBeginTime: " << operation.rescaledBeginTime << std::endl;
    stream << "  rescaledEndTime:   " << operation.rescaledEndTime << std::endl;
    stream << "  begin:             " << operation.begin << std::endl;
    stream << "  end:               " << operation.end << std::endl;
    if (!(operation.P == DBL_MAX)) {
      stream << "  P:                 " << operation.P << std::endl;
    }
    if (!(operation.T == DBL_MAX)) {
      stream << "  T:                 " << operation.T << std::endl;
    }
    if (!(operation.C == DBL_MAX)) {
      stream << "  C:                 " << operation.C << std::endl;
    }
    if (!(operation.K == DBL_MAX)) {
      stream << "  K:                 " << operation.K << std::endl;
    }
    if (!(operation.Q == DBL_MAX)) {
      stream << "  Q:                 " << operation.Q << std::endl;
    }
    if (!(operation.V == DBL_MAX)) {
      stream << "  V:                 " << operation.V << std::endl;
    }
    if (operation.L) {
      stream << "  L:                 " << int(operation.L) << std::endl;
    }
    return stream;
  }

  void VoiceleadingNode::apply(Score &score, const VoiceleadingOperation &priorOperation, const VoiceleadingOperation &operation)
  {
    if ( (System::getMessageLevel() & System::INFORMATION_LEVEL) == System::INFORMATION_LEVEL) {
      std::stringstream stream;
      stream << "BEGAN VoiceleadingNode::apply:..." << std::endl;
      stream << "Events in score:     " << score.size() << std::endl;
      stream << "Score duration:      " << score.getDuration() << std::endl;
      stream << "Events in operation: " << (operation.end - operation.begin) << std::endl;
      stream << "priorOperation:      " << std::endl << priorOperation;
      stream << "currrentOperation:   " << std::endl << operation;
      stream << "modality:            ";
      printChord(stream, "", modality);
      stream << std::endl;
      System::inform(stream.str().c_str());
    }
    if (operation.begin == operation.end) {
      return;
    }
    if (!(operation.K == DBL_MAX)) {
      if ((operation.V == DBL_MAX) && (!operation.L)) {
	System::inform("Operation: K\n");
	score.setK(priorOperation.begin,
		   operation.begin,
		   operation.end,
		   base,
		   range);
      } else if ((operation.V != DBL_MAX) && (!operation.L)) {
	System::inform("Operation: KV\n");
	score.setKV(priorOperation.begin,
		    operation.begin,
		    operation.end,
		    operation.V,
		    base,
		    range);
      } else if ((operation.V == DBL_MAX) && (operation.L)) {
	System::inform("Operation: KL\n");
	score.setKL(priorOperation.begin,
		    operation.begin,
		    operation.end,
		    base,
		    range,
		    operation.avoidParallels);
      }
    } else if (!(operation.Q == DBL_MAX)) {
      if ((operation.V == DBL_MAX) && (!operation.L)) {
	System::inform("Operation: Q\n");
	score.setQ(priorOperation.begin,
		   operation.begin,
		   operation.end,
		   operation.Q,
		   modality,
		   base,
		   range);
      } else if ((operation.V != DBL_MAX) && (!operation.L)) {
	System::inform("Operation: QV\n");
	score.setQV(priorOperation.begin,
		    operation.begin,
		    operation.end,
		    operation.Q,
		    modality,
		    operation.V,
		    base,
		    range);
      } else if ((operation.V == DBL_MAX) && (operation.L)) {
	System::inform("Operation: QL\n");
	score.setQL(priorOperation.begin,
		    operation.begin,
		    operation.end,
		    operation.Q,
		    modality,
		    base,
		    range,
		    operation.avoidParallels);
      }
    } else if (!(operation.P == DBL_MAX) && !(operation.T == DBL_MAX)) {
      if (!(operation.V == DBL_MAX)) {
	System::inform("Operation: PTV\n");
        score.setPTV(operation.begin,
                     operation.end,
                     operation.P,
                     operation.T,
                     operation.V,
                     base,
                     range);
      } else if (operation.L) {
	System::inform("Operation: PTL\n");
        score.setPT(operation.begin,
                    operation.end,
                    operation.P,
                    operation.T,
                    base,
                    range,
                    divisionsPerOctave);
        score.voicelead(priorOperation.begin,
                        priorOperation.end,
                        operation.begin,
                        operation.end,
                        base,
                        range,
                        avoidParallels,
                        divisionsPerOctave);
      } else {
	System::inform("Operation: PT\n");
        score.setPT(operation.begin,
                    operation.end,
                    operation.P,
                    operation.T,
                    base,
                    range,
                    divisionsPerOctave);
      }
    } else if (!(operation.C == DBL_MAX)) {
      if (!(operation.V == DBL_MAX)) {
	System::inform("Operation: CV\n");
        std::vector<double> pcs = Voicelead::mToPitchClassSet(Voicelead::cToM(operation.C, divisionsPerOctave), divisionsPerOctave);
        printChord("CV", pcs);
        std::vector<double> pt = Voicelead::pitchClassSetToPandT(pcs, divisionsPerOctave);
        double prime = pt[0];
        double transposition = pt[1];
        System::inform("prime: %f transposition %f: divisionsPerOctave %d\n", prime, transposition, divisionsPerOctave);
        score.setPTV(operation.begin,
                     operation.end,
                     prime,
                     transposition,
                     operation.V,
                     base,
                     range);
      } else if (operation.L) {
	System::inform("Operation: CL\n");
        std::vector<double> pcs = Voicelead::mToPitchClassSet(Voicelead::cToM(operation.C, divisionsPerOctave), divisionsPerOctave);
        printChord("CL", pcs);
        score.voicelead(priorOperation.begin,
                        priorOperation.end,
                        operation.begin,
                        operation.end,
                        pcs,
                        base,
                        range,
                        avoidParallels,
                        divisionsPerOctave);
      } else {
	System::inform("Operation: C\n");
        std::vector<double> pcs = Voicelead::mToPitchClassSet(Voicelead::cToM(operation.C, divisionsPerOctave), divisionsPerOctave);
        score.setPitchClassSet(operation.begin,
                               operation.end,
                               pcs,
                               divisionsPerOctave);
      }
    } else {
	System::inform("Operation: V\n");
        if (!(operation.V == DBL_MAX)) {
        std::vector<double> ptv = score.getPTV(operation.begin,
                                               operation.end,
                                               base,
                                               range,
                                               divisionsPerOctave);
        score.setPTV(operation.begin,
                     operation.end,
                     ptv[0],
                     ptv[1],
                     operation.V,
                     base,
                     range,
                     divisionsPerOctave);
      } else if (operation.L) {
	System::inform("Operation: L\n");
        score.voicelead(priorOperation.begin,
                        priorOperation.end,
                        operation.begin,
                        operation.end,
                        base,
                        range,
                        avoidParallels,
                        divisionsPerOctave);
      }
    }
    System::message("ENDED VoiceleadingNode::apply.\n");
  }

  void VoiceleadingNode::PT(double time, double P, double T)
  {
    operations[time].beginTime = time;
    operations[time].P = P;
    operations[time].T = T;
  }

  void VoiceleadingNode::PTV(double time, double P, double T, double V)
  {
    operations[time].beginTime = time;
    operations[time].P = P;
    operations[time].T = T;
    operations[time].V = V;
  }

  void VoiceleadingNode::PTL(double time, double P, double T, bool avoidParallels)
  {
    operations[time].beginTime = time;
    operations[time].P = P;
    operations[time].T = T;
    operations[time].L = true;
    operations[time].avoidParallels = avoidParallels;
  }

  void VoiceleadingNode::C(double time, double C_)
  {
    operations[time].beginTime = time;
    operations[time].C = C_;
  }

  void VoiceleadingNode::C(double time, std::string C_)
  {
    C(time, Voicelead::nameToC(C_, divisionsPerOctave));
  }

  void VoiceleadingNode::CV(double time, double C, double V)
  {
    operations[time].beginTime = time;
    operations[time].C = C;
    operations[time].V = V;
  }

  void VoiceleadingNode::CV(double time, std::string C, double V)
  {
    CV(time, Voicelead::nameToC(C, divisionsPerOctave), V);
  }

  void VoiceleadingNode::CL(double time, double C, bool avoidParallels)
  {
    operations[time].beginTime = time;
    operations[time].C = C;
    operations[time].L = true;
    operations[time].avoidParallels = avoidParallels;
  }

  void VoiceleadingNode::CL(double time, std::string C, bool avoidParallels)
  {
    CL(time, Voicelead::nameToC(C, divisionsPerOctave), avoidParallels);
  }

  void VoiceleadingNode::K(double time)
  {
    operations[time].beginTime = time;
    operations[time].K = 1.0;
  }

  void VoiceleadingNode::KV(double time, double V)
  {
    operations[time].beginTime = time;
    operations[time].K = 1.0;
    operations[time].V = V;
  }

  void VoiceleadingNode::KL(double time, bool avoidParallels)
  {
    operations[time].beginTime = time;
    operations[time].K = 1.0;
    operations[time].L = true;
    operations[time].avoidParallels = avoidParallels;
  }

  void VoiceleadingNode::Q(double time, double Q)
  {
    operations[time].beginTime = time;
    operations[time].Q = Q;
  }

  void VoiceleadingNode::QV(double time, double Q, double V)
  {
    operations[time].beginTime = time;
    operations[time].Q = Q;
    operations[time].V = V;
  }

  void VoiceleadingNode::QL(double time, double Q, bool avoidParallels)
  {
    operations[time].beginTime = time;
    operations[time].Q = Q;
    operations[time].L = true;
    operations[time].avoidParallels = avoidParallels;
  }

  void VoiceleadingNode::V(double time, double V_)
  {
    operations[time].beginTime = time;
    operations[time].V = V_;
  }

  void VoiceleadingNode::L(double time, bool avoidParallels)
  {
    operations[time].beginTime = time;
    operations[time].L = true;
    operations[time].avoidParallels = avoidParallels;
  }

  void VoiceleadingNode::produceOrTransform(Score &score, size_t beginAt, size_t endAt, const ublas::matrix<double> &coordinates)
  {
    transform(score, rescaleTimes);
  }

  void VoiceleadingNode::transform(Score &score, bool rescaleTimes_)
  {
    if (operations.empty()) {
      return;
    }
    // Find the time rescale factor.
    score.sort();
    score.findScale();
    double scoreMinTime = score.scaleActualMinima.getTime();
    double scoreDuration = score.getDuration();
    double scoreMaxTime = scoreMinTime + scoreDuration;
    double operationMaxTime = 0.0;
    double timeScale = 1.0;
    std::vector<VoiceleadingOperation *> ops;
    for (std::map<double, VoiceleadingOperation>::iterator it = operations.begin(); it != operations.end(); ++it) {
      if (it->second.beginTime > operationMaxTime) {
        operationMaxTime = it->second.beginTime;
      }
      ops.push_back(&it->second);
    }

    if (rescaleTimes_) {
      if (operationMaxTime > 0.0) {
        timeScale = scoreMaxTime / operationMaxTime;
      }
    }
    System::inform("BEGAN VoiceleadingNode::produceOrTransform  operationMaxTime: %f  origin: %f  duration: %f  scoreMaxTime: %f  timeScale: %f...\n",
                   operationMaxTime,
                   scoreMinTime,
                   scoreDuration,
                   scoreMaxTime,
                   timeScale);
    VoiceleadingOperation *priorOperation = 0;
    VoiceleadingOperation *currentOperation = 0;
    VoiceleadingOperation *nextOperation = 0;
    int priorIndex = 0;
    int currentIndex = 0;
    int nextIndex = 0;
    int backIndex = ops.size() - 1;
    int endIndex = ops.size();
    for (currentIndex = 0; currentIndex < endIndex; ++currentIndex) {
      if (currentIndex == 0) {
	priorIndex = currentIndex;
      } else {
	priorIndex = currentIndex - 1;
      }
      if (currentIndex == backIndex) {
	nextIndex = currentIndex;
      } else {
	nextIndex = currentIndex + 1;
      }
      priorOperation = ops[priorIndex];
      currentOperation = ops[currentIndex];
      nextOperation = ops[nextIndex];
      currentOperation->rescaledBeginTime = currentOperation->beginTime * timeScale;
      currentOperation->begin = score.indexAtTime(currentOperation->rescaledBeginTime);
      currentOperation->rescaledEndTime = nextOperation->beginTime * timeScale;
      currentOperation->end = score.indexAfterTime(currentOperation->rescaledEndTime);
      apply(score, *priorOperation, *currentOperation);
    }
    System::inform("ENDED VoiceleadingNode::produceOrTransform.\n");
  }

}
<|endoftext|>


<commit_before>// Utility functions used for parsing and evaluating equations
// William Immendorf - 2016
// Compile with --std=c++11

#include <stdexcept>
#include <unordered_map>

#include "utility.hpp"

struct operator_detail
{
	int precendence;
	bool right_associative;
};

const std::unordered_map<char, operator_detail> operator_map
{
	{'+', {2, false}},
	{'-', {2, false}},
	{'*', {3, false}},
	{'/', {3, false}},
	{'^', {4, true}}
};

namespace EquParser
{
	bool precendence_less_than(char op1, char op2)
	{
		auto op_iterator1 = operator_map.find(op1);
		auto op_iterator2 = operator_map.find(op2);

		if (op_iterator1 != operator_map.end() && op_iterator2 != operator_map.end())
		{
			if (op_iterator1->second.right_associative)
			{
				if (op_iterator1->second.precendence < op_iterator2->second.precendence)
					return true;
			}
			else
			{
				if (op_iterator1->second.precendence <= op_iterator2->second.precendence)
					return true;
			}
		}

		return false;
	}

	bool enough_on_stack(std::stack<int> stack, int size)
  {
    if (stack.size() < size)
    {
      return false;
    }
	  return true;
  }

  std::stack<int> process_operator(std::stack<int> num_stack, std::function<int (int val1, int val2)> expression)
  {
    if (enough_on_stack(num_stack, 2))
    {
      int val1 = num_stack.top();
      num_stack.pop();
      int val2 = num_stack.top();
      num_stack.pop();
      num_stack.push(expression(val1, val2));
    }
    else
      throw std::runtime_error("Not enough arguments for an operator");
    return num_stack;
	}
}
<commit_msg>Inline 'enough on stack' function<commit_after>// Utility functions used for parsing and evaluating equations
// William Immendorf - 2016
// Compile with --std=c++11

#include <stdexcept>
#include <unordered_map>

#include "utility.hpp"

struct operator_detail
{
	int precendence;
	bool right_associative;
};

const std::unordered_map<char, operator_detail> operator_map
{
	{'+', {2, false}},
	{'-', {2, false}},
	{'*', {3, false}},
	{'/', {3, false}},
	{'^', {4, true}}
};

namespace EquParser
{
	bool precendence_less_than(char op1, char op2)
	{
		auto op_iterator1 = operator_map.find(op1);
		auto op_iterator2 = operator_map.find(op2);

		if (op_iterator1 != operator_map.end() && op_iterator2 != operator_map.end())
		{
			if (op_iterator1->second.right_associative)
			{
				if (op_iterator1->second.precendence < op_iterator2->second.precendence)
					return true;
			}
			else
			{
				if (op_iterator1->second.precendence <= op_iterator2->second.precendence)
					return true;
			}
		}

		return false;
	}

	inline bool enough_on_stack(std::stack<int> stack, int size)
  {
    if (stack.size() < size)
    {
      return false;
    }
	  return true;
  }

  std::stack<int> process_operator(std::stack<int> num_stack, std::function<int (int val1, int val2)> expression)
  {
    if (enough_on_stack(num_stack, 2))
    {
      int val1 = num_stack.top();
      num_stack.pop();
      int val2 = num_stack.top();
      num_stack.pop();
      num_stack.push(expression(val1, val2));
    }
    else
      throw std::runtime_error("Not enough arguments for an operator");
    return num_stack;
	}
}
<|endoftext|>


<commit_before>/*
    Copyright (C) 2000 by Jorrit Tyberghein

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

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

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include <stdarg.h>

#include "sysdef.h"
#include "qint.h"
#include "csgeom/math2d.h"
#include "csgeom/math3d.h"
#include "cs3d/line/line_g3d.h"
#include "cs3d/line/line_txt.h"
#include "cs3d/common/memheap.h"
#include "csutil/inifile.h"
#include "ipolygon.h"
#include "isystem.h"
#include "igraph2d.h"
#include "ilghtmap.h"

///---------------------------------------------------------------------------

IMPLEMENT_FACTORY (csGraphics3DLine)

EXPORT_CLASS_TABLE (line3d)
  EXPORT_CLASS (csGraphics3DLine, "crystalspace.graphics3d.line",
    "Line 3D graphics driver for Crystal Space")
EXPORT_CLASS_TABLE_END

IMPLEMENT_IBASE (csGraphics3DLine)
  IMPLEMENTS_INTERFACE (iPlugIn)
  IMPLEMENTS_INTERFACE (iGraphics3D)
  IMPLEMENTS_EMBEDDED_INTERFACE (iConfig)
IMPLEMENT_IBASE_END

#if defined (OS_LINUX)
char* get_software_2d_driver ()
{
  if (getenv ("GGI_DISPLAY"))
    return SOFTWARE_2D_DRIVER_GGI;
  else if (getenv ("DISPLAY"))
    return "crystalspace.graphics2d.linexlib";
  else
    return SOFTWARE_2D_DRIVER_SVGA;
}
#elif defined (OS_UNIX) && !defined (OS_BE)
// by the way, other unices has SVGALib support too... through GGI ;-)
char* get_software_2d_driver ()
{
  if (getenv ("GGI_DISPLAY"))
    return SOFTWARE_2D_DRIVER_GGI;
  else
    return "crystalspace.graphics2d.linexlib";
}
#endif

csGraphics3DLine::csGraphics3DLine (iBase *iParent) : G2D (NULL)
{
  CONSTRUCT_IBASE (iParent);
  CONSTRUCT_EMBEDDED_IBASE (scfiConfig);

  config = new csIniFile ("line3d.cfg");

  txtmgr = NULL;
}

csGraphics3DLine::~csGraphics3DLine ()
{
  Close ();
  CHK (delete txtmgr);
  if (G2D)
    G2D->DecRef ();
  CHK (delete config;)
}

bool csGraphics3DLine::Initialize (iSystem *iSys)
{
  System = iSys;

  width = height = -1;

  if (!System->RegisterDriver ("iGraphics3D", this))
    return false;

  const char *driver = config->GetStr ("Hardware", "Driver2D", SOFTWARE_2D_DRIVER);
  G2D = LOAD_PLUGIN (System, driver, iGraphics2D);
  if (!G2D)
    return false;

  CHK (txtmgr = new csTextureManagerLine (System, G2D));
  txtmgr->SetConfig (config);
  txtmgr->Initialize ();

  return true;
}

bool csGraphics3DLine::Open (const char *Title)
{
  DrawMode = 0;

  if (!G2D->Open (Title))
  {
    SysPrintf (MSG_FATAL_ERROR, "Error opening Graphics2D context.");
    // set "not opened" flag
    width = height = -1;

    return false;
  }

  int nWidth = G2D->GetWidth ();
  int nHeight = G2D->GetHeight ();
  bool bFullScreen = G2D->GetFullScreen ();

  SetDimensions (nWidth, nHeight);

  SysPrintf (MSG_INITIALIZATION, "Using %s mode %dx%d.\n",
            bFullScreen ? "full screen" : "windowed", width, height);

  z_buf_mode = CS_ZBUF_NONE;

  return true;
}

void csGraphics3DLine::Close()
{
  if ((width == height) && (width == -1))
    return;

  G2D->Close ();
  width = height = -1;
}

void csGraphics3DLine::SetDimensions (int nwidth, int nheight)
{
  width = nwidth;
  height = nheight;
  width2 = width/2;
  height2 = height/2;
}

void csGraphics3DLine::SetPerspectiveCenter (int x, int y)
{
  width2 = x;
  height2 = y;
}

bool csGraphics3DLine::BeginDraw (int DrawFlags)
{
  // if 2D graphics is not locked, lock it
  if ((DrawFlags & (CSDRAW_2DGRAPHICS | CSDRAW_3DGRAPHICS))
   && (!(DrawMode & (CSDRAW_2DGRAPHICS | CSDRAW_3DGRAPHICS))))
  {
    if (!G2D->BeginDraw())
      return false;
  }

  if (DrawFlags & CSDRAW_CLEARSCREEN)
    G2D->Clear (0);

  if (DrawFlags & CSDRAW_3DGRAPHICS)
  {
    G2D->Clear (0);
  }
  else if (DrawMode & CSDRAW_3DGRAPHICS)
  {
  }

  DrawMode = DrawFlags;

  return true;
}

void csGraphics3DLine::FinishDraw ()
{
  if (DrawMode & (CSDRAW_2DGRAPHICS | CSDRAW_3DGRAPHICS))
    G2D->FinishDraw ();
  DrawMode = 0;
}

void csGraphics3DLine::Print (csRect *area)
{
  G2D->Print (area);
}

void csGraphics3DLine::SetZBufMode (G3DZBufMode mode)
{
  z_buf_mode = mode;
}

void csGraphics3DLine::DrawPolygon (G3DPolygonDP& poly)
{
  if (poly.num < 3)
    return;
  int i;
  int color = txtmgr->FindRGB (255, 255, 255);
  for (i = 0 ; i < poly.num ; i++)
  {
    G2D->DrawLine (poly.vertices[i].sx, height-poly.vertices[i].sy,
    	poly.vertices[(i+1)%poly.num].sx, height-poly.vertices[(i+1)%poly.num].sy,
	color);
  }
}

void csGraphics3DLine::DrawPolygonFX (G3DPolygonDPFX& poly)
{
  if (poly.num < 3)
    return;
  int i;
  int color = txtmgr->FindRGB (255, 0, 0);
  for (i = 0 ; i < poly.num ; i++)
  {
    G2D->DrawLine (poly.vertices[i].sx, height-poly.vertices[i].sy,
    	poly.vertices[(i+1)%poly.num].sx, height-poly.vertices[(i+1)%poly.num].sy,
	color);
  }
}

bool csGraphics3DLine::SetRenderState (G3D_RENDERSTATEOPTION op,
  long value)
{
  switch (op)
  {
    case G3DRENDERSTATE_NOTHING:
      return true;
    case G3DRENDERSTATE_ZBUFFERTESTENABLE:
      if (value)
      {
         if (z_buf_mode == CS_ZBUF_TEST)
           return true;
         if (z_buf_mode == CS_ZBUF_NONE)
           z_buf_mode = CS_ZBUF_TEST;
         else if (z_buf_mode == CS_ZBUF_FILL)
           z_buf_mode = CS_ZBUF_USE;
      }
      else
      {
         if (z_buf_mode == CS_ZBUF_FILL)
           return true;
         if (z_buf_mode == CS_ZBUF_USE)
           z_buf_mode = CS_ZBUF_FILL;
         else if (z_buf_mode == CS_ZBUF_TEST)
           z_buf_mode = CS_ZBUF_NONE;
      }
      break;
    case G3DRENDERSTATE_ZBUFFERFILLENABLE:
      if (value)
      {
        if (z_buf_mode == CS_ZBUF_FILL)
          return true;
        if (z_buf_mode == CS_ZBUF_NONE)
          z_buf_mode = CS_ZBUF_FILL;
        else if (z_buf_mode == CS_ZBUF_TEST)
          z_buf_mode = CS_ZBUF_USE;
      }
      else
      {
        if (z_buf_mode == CS_ZBUF_TEST)
          return true;
        if (z_buf_mode == CS_ZBUF_USE)
          z_buf_mode = CS_ZBUF_TEST;
        else if (z_buf_mode == CS_ZBUF_FILL)
          z_buf_mode = CS_ZBUF_NONE;
      }
      break;
    default:
      return false;
  }

  return true;
}

long csGraphics3DLine::GetRenderState (G3D_RENDERSTATEOPTION op)
{
  switch (op)
  {
    case G3DRENDERSTATE_NOTHING:
      return 0;
    case G3DRENDERSTATE_ZBUFFERTESTENABLE:
      return (bool)(z_buf_mode & CS_ZBUF_TEST);
    case G3DRENDERSTATE_ZBUFFERFILLENABLE:
      return (bool)(z_buf_mode & CS_ZBUF_FILL);
    default:
      return 0;
  }
}

void csGraphics3DLine::GetCaps (G3D_CAPS *caps)
{
  if (!caps)
    return;

  caps->ColorModel = G3DCOLORMODEL_RGB;
  caps->CanClip = false;
  caps->SupportsArbitraryMipMapping = true;
  caps->BitDepth = 8;
  caps->ZBufBitDepth = 32;
  caps->minTexHeight = 2;
  caps->minTexWidth = 2;
  caps->maxTexHeight = 1024;
  caps->maxTexWidth = 1024;
  caps->PrimaryCaps.RasterCaps = G3DRASTERCAPS_SUBPIXEL;
  caps->PrimaryCaps.canBlend = true;
  caps->PrimaryCaps.ShadeCaps = G3DRASTERCAPS_LIGHTMAP;
  caps->PrimaryCaps.PerspectiveCorrects = true;
  caps->PrimaryCaps.FilterCaps = G3D_FILTERCAPS((int)G3DFILTERCAPS_NEAREST | (int)G3DFILTERCAPS_MIPNEAREST);
  caps->fog = G3D_FOGMETHOD((int)G3DFOGMETHOD_NONE);
}

void csGraphics3DLine::DrawLine (csVector3& v1, csVector3& v2, float fov, int color)
{
  if (v1.z < SMALL_Z && v2.z < SMALL_Z)
    return;

  float x1 = v1.x, y1 = v1.y, z1 = v1.z;
  float x2 = v2.x, y2 = v2.y, z2 = v2.z;

  if (z1 < SMALL_Z)
  {
    // x = t*(x2-x1)+x1;
    // y = t*(y2-y1)+y1;
    // z = t*(z2-z1)+z1;
    float t = (SMALL_Z-z1) / (z2-z1);
    x1 = t*(x2-x1)+x1;
    y1 = t*(y2-y1)+y1;
    z1 = SMALL_Z;
  }
  else if (z2 < SMALL_Z)
  {
    // x = t*(x2-x1)+x1;
    // y = t*(y2-y1)+y1;
    // z = t*(z2-z1)+z1;
    float t = (SMALL_Z-z1) / (z2-z1);
    x2 = t*(x2-x1)+x1;
    y2 = t*(y2-y1)+y1;
    z2 = SMALL_Z;
  }

  float iz1 = fov/z1;
  int px1 = QInt (x1 * iz1 + (width/2));
  int py1 = height - 1 - QInt (y1 * iz1 + (height/2));
  float iz2 = fov/z2;
  int px2 = QInt (x2 * iz2 + (width/2));
  int py2 = height - 1 - QInt (y2 * iz2 + (height/2));

  G2D->DrawLine (px1, py1, px2, py2, color);
}

void csGraphics3DLine::SysPrintf (int mode, char* szMsg, ...)
{
  char buf[1024];
  va_list arg;

  va_start (arg, szMsg);
  vsprintf (buf, szMsg, arg);
  va_end (arg);

  System->Print (mode, buf);
}

//---------------------------------------------------------------------------

IMPLEMENT_EMBEDDED_IBASE (csGraphics3DLine::csLineConfig)
  IMPLEMENTS_INTERFACE (iConfig)
IMPLEMENT_EMBEDDED_IBASE_END

#define NUM_OPTIONS 1

static const csOptionDescription config_options [NUM_OPTIONS] =
{
  { 0, "dummy", "Dummy", CSVAR_BOOL },
};

bool csGraphics3DLine::csLineConfig::SetOption (int id, csVariant* value)
{
  if (value->type != config_options[id].type)
    return false;
  switch (id)
  {
    case 0: break;
    default: return false;
  }
  return true;
}

bool csGraphics3DLine::csLineConfig::GetOption (int id, csVariant* value)
{
  value->type = config_options[id].type;
  switch (id)
  {
    case 0: value->v.b = false; break;
    default: return false;
  }
  return true;
}

bool csGraphics3DLine::csLineConfig::GetOptionDescription
  (int idx, csOptionDescription* option)
{
  if (idx < 0 || idx >= NUM_OPTIONS)
    return false;
  *option = config_options[idx];
  return true;
}

<commit_msg>Draw lines based on texture color.<commit_after>/*
    Copyright (C) 2000 by Jorrit Tyberghein

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

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

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include <stdarg.h>

#include "sysdef.h"
#include "qint.h"
#include "csgeom/math2d.h"
#include "csgeom/math3d.h"
#include "cs3d/line/line_g3d.h"
#include "cs3d/line/line_txt.h"
#include "cs3d/common/memheap.h"
#include "csutil/inifile.h"
#include "ipolygon.h"
#include "isystem.h"
#include "igraph2d.h"
#include "ilghtmap.h"

///---------------------------------------------------------------------------

IMPLEMENT_FACTORY (csGraphics3DLine)

EXPORT_CLASS_TABLE (line3d)
  EXPORT_CLASS (csGraphics3DLine, "crystalspace.graphics3d.line",
    "Line 3D graphics driver for Crystal Space")
EXPORT_CLASS_TABLE_END

IMPLEMENT_IBASE (csGraphics3DLine)
  IMPLEMENTS_INTERFACE (iPlugIn)
  IMPLEMENTS_INTERFACE (iGraphics3D)
  IMPLEMENTS_EMBEDDED_INTERFACE (iConfig)
IMPLEMENT_IBASE_END

#if defined (OS_LINUX)
char* get_software_2d_driver ()
{
  if (getenv ("GGI_DISPLAY"))
    return SOFTWARE_2D_DRIVER_GGI;
  else if (getenv ("DISPLAY"))
    return "crystalspace.graphics2d.linexlib";
  else
    return SOFTWARE_2D_DRIVER_SVGA;
}
#elif defined (OS_UNIX) && !defined (OS_BE)
// by the way, other unices has SVGALib support too... through GGI ;-)
char* get_software_2d_driver ()
{
  if (getenv ("GGI_DISPLAY"))
    return SOFTWARE_2D_DRIVER_GGI;
  else
    return "crystalspace.graphics2d.linexlib";
}
#endif

csGraphics3DLine::csGraphics3DLine (iBase *iParent) : G2D (NULL)
{
  CONSTRUCT_IBASE (iParent);
  CONSTRUCT_EMBEDDED_IBASE (scfiConfig);

  config = new csIniFile ("line3d.cfg");

  txtmgr = NULL;
}

csGraphics3DLine::~csGraphics3DLine ()
{
  Close ();
  CHK (delete txtmgr);
  if (G2D)
    G2D->DecRef ();
  CHK (delete config;)
}

bool csGraphics3DLine::Initialize (iSystem *iSys)
{
  System = iSys;

  width = height = -1;

  if (!System->RegisterDriver ("iGraphics3D", this))
    return false;

  const char *driver = config->GetStr ("Hardware", "Driver2D", SOFTWARE_2D_DRIVER);
  G2D = LOAD_PLUGIN (System, driver, iGraphics2D);
  if (!G2D)
    return false;

  CHK (txtmgr = new csTextureManagerLine (System, G2D));
  txtmgr->SetConfig (config);
  txtmgr->Initialize ();

  return true;
}

bool csGraphics3DLine::Open (const char *Title)
{
  DrawMode = 0;

  if (!G2D->Open (Title))
  {
    SysPrintf (MSG_FATAL_ERROR, "Error opening Graphics2D context.");
    // set "not opened" flag
    width = height = -1;

    return false;
  }

  int nWidth = G2D->GetWidth ();
  int nHeight = G2D->GetHeight ();
  bool bFullScreen = G2D->GetFullScreen ();

  SetDimensions (nWidth, nHeight);

  SysPrintf (MSG_INITIALIZATION, "Using %s mode %dx%d.\n",
            bFullScreen ? "full screen" : "windowed", width, height);

  z_buf_mode = CS_ZBUF_NONE;

  return true;
}

void csGraphics3DLine::Close()
{
  if ((width == height) && (width == -1))
    return;

  G2D->Close ();
  width = height = -1;
}

void csGraphics3DLine::SetDimensions (int nwidth, int nheight)
{
  width = nwidth;
  height = nheight;
  width2 = width/2;
  height2 = height/2;
}

void csGraphics3DLine::SetPerspectiveCenter (int x, int y)
{
  width2 = x;
  height2 = y;
}

bool csGraphics3DLine::BeginDraw (int DrawFlags)
{
  // if 2D graphics is not locked, lock it
  if ((DrawFlags & (CSDRAW_2DGRAPHICS | CSDRAW_3DGRAPHICS))
   && (!(DrawMode & (CSDRAW_2DGRAPHICS | CSDRAW_3DGRAPHICS))))
  {
    if (!G2D->BeginDraw())
      return false;
  }

  if (DrawFlags & CSDRAW_CLEARSCREEN)
    G2D->Clear (0);

  if (DrawFlags & CSDRAW_3DGRAPHICS)
  {
    G2D->Clear (0);
  }
  else if (DrawMode & CSDRAW_3DGRAPHICS)
  {
  }

  DrawMode = DrawFlags;

  return true;
}

void csGraphics3DLine::FinishDraw ()
{
  if (DrawMode & (CSDRAW_2DGRAPHICS | CSDRAW_3DGRAPHICS))
    G2D->FinishDraw ();
  DrawMode = 0;
}

void csGraphics3DLine::Print (csRect *area)
{
  G2D->Print (area);
}

void csGraphics3DLine::SetZBufMode (G3DZBufMode mode)
{
  z_buf_mode = mode;
}

void csGraphics3DLine::DrawPolygon (G3DPolygonDP& poly)
{
  if (poly.num < 3)
    return;
  int i;
  int color = txtmgr->FindRGB (128+QInt (poly.flat_color_r*128),
  	128+QInt (poly.flat_color_g*128), 128+QInt (poly.flat_color_b*128));
  for (i = 0 ; i < poly.num ; i++)
  {
    G2D->DrawLine (poly.vertices[i].sx, height-poly.vertices[i].sy,
    	poly.vertices[(i+1)%poly.num].sx, height-poly.vertices[(i+1)%poly.num].sy,
	color);
  }
}

void csGraphics3DLine::DrawPolygonFX (G3DPolygonDPFX& poly)
{
  if (poly.num < 3)
    return;
  int i;
  int color = txtmgr->FindRGB (QInt (poly.flat_color_r*255),
  	QInt (poly.flat_color_g*255), QInt (poly.flat_color_b*255));
  for (i = 0 ; i < poly.num ; i++)
  {
    G2D->DrawLine (poly.vertices[i].sx, height-poly.vertices[i].sy,
    	poly.vertices[(i+1)%poly.num].sx, height-poly.vertices[(i+1)%poly.num].sy,
	color);
  }
}

bool csGraphics3DLine::SetRenderState (G3D_RENDERSTATEOPTION op,
  long value)
{
  switch (op)
  {
    case G3DRENDERSTATE_NOTHING:
      return true;
    case G3DRENDERSTATE_ZBUFFERTESTENABLE:
      if (value)
      {
         if (z_buf_mode == CS_ZBUF_TEST)
           return true;
         if (z_buf_mode == CS_ZBUF_NONE)
           z_buf_mode = CS_ZBUF_TEST;
         else if (z_buf_mode == CS_ZBUF_FILL)
           z_buf_mode = CS_ZBUF_USE;
      }
      else
      {
         if (z_buf_mode == CS_ZBUF_FILL)
           return true;
         if (z_buf_mode == CS_ZBUF_USE)
           z_buf_mode = CS_ZBUF_FILL;
         else if (z_buf_mode == CS_ZBUF_TEST)
           z_buf_mode = CS_ZBUF_NONE;
      }
      break;
    case G3DRENDERSTATE_ZBUFFERFILLENABLE:
      if (value)
      {
        if (z_buf_mode == CS_ZBUF_FILL)
          return true;
        if (z_buf_mode == CS_ZBUF_NONE)
          z_buf_mode = CS_ZBUF_FILL;
        else if (z_buf_mode == CS_ZBUF_TEST)
          z_buf_mode = CS_ZBUF_USE;
      }
      else
      {
        if (z_buf_mode == CS_ZBUF_TEST)
          return true;
        if (z_buf_mode == CS_ZBUF_USE)
          z_buf_mode = CS_ZBUF_TEST;
        else if (z_buf_mode == CS_ZBUF_FILL)
          z_buf_mode = CS_ZBUF_NONE;
      }
      break;
    default:
      return false;
  }

  return true;
}

long csGraphics3DLine::GetRenderState (G3D_RENDERSTATEOPTION op)
{
  switch (op)
  {
    case G3DRENDERSTATE_NOTHING:
      return 0;
    case G3DRENDERSTATE_ZBUFFERTESTENABLE:
      return (bool)(z_buf_mode & CS_ZBUF_TEST);
    case G3DRENDERSTATE_ZBUFFERFILLENABLE:
      return (bool)(z_buf_mode & CS_ZBUF_FILL);
    default:
      return 0;
  }
}

void csGraphics3DLine::GetCaps (G3D_CAPS *caps)
{
  if (!caps)
    return;

  caps->ColorModel = G3DCOLORMODEL_RGB;
  caps->CanClip = false;
  caps->SupportsArbitraryMipMapping = true;
  caps->BitDepth = 8;
  caps->ZBufBitDepth = 32;
  caps->minTexHeight = 2;
  caps->minTexWidth = 2;
  caps->maxTexHeight = 1024;
  caps->maxTexWidth = 1024;
  caps->PrimaryCaps.RasterCaps = G3DRASTERCAPS_SUBPIXEL;
  caps->PrimaryCaps.canBlend = true;
  caps->PrimaryCaps.ShadeCaps = G3DRASTERCAPS_LIGHTMAP;
  caps->PrimaryCaps.PerspectiveCorrects = true;
  caps->PrimaryCaps.FilterCaps = G3D_FILTERCAPS((int)G3DFILTERCAPS_NEAREST | (int)G3DFILTERCAPS_MIPNEAREST);
  caps->fog = G3D_FOGMETHOD((int)G3DFOGMETHOD_NONE);
}

void csGraphics3DLine::DrawLine (csVector3& v1, csVector3& v2, float fov, int color)
{
  if (v1.z < SMALL_Z && v2.z < SMALL_Z)
    return;

  float x1 = v1.x, y1 = v1.y, z1 = v1.z;
  float x2 = v2.x, y2 = v2.y, z2 = v2.z;

  if (z1 < SMALL_Z)
  {
    // x = t*(x2-x1)+x1;
    // y = t*(y2-y1)+y1;
    // z = t*(z2-z1)+z1;
    float t = (SMALL_Z-z1) / (z2-z1);
    x1 = t*(x2-x1)+x1;
    y1 = t*(y2-y1)+y1;
    z1 = SMALL_Z;
  }
  else if (z2 < SMALL_Z)
  {
    // x = t*(x2-x1)+x1;
    // y = t*(y2-y1)+y1;
    // z = t*(z2-z1)+z1;
    float t = (SMALL_Z-z1) / (z2-z1);
    x2 = t*(x2-x1)+x1;
    y2 = t*(y2-y1)+y1;
    z2 = SMALL_Z;
  }

  float iz1 = fov/z1;
  int px1 = QInt (x1 * iz1 + (width/2));
  int py1 = height - 1 - QInt (y1 * iz1 + (height/2));
  float iz2 = fov/z2;
  int px2 = QInt (x2 * iz2 + (width/2));
  int py2 = height - 1 - QInt (y2 * iz2 + (height/2));

  G2D->DrawLine (px1, py1, px2, py2, color);
}

void csGraphics3DLine::SysPrintf (int mode, char* szMsg, ...)
{
  char buf[1024];
  va_list arg;

  va_start (arg, szMsg);
  vsprintf (buf, szMsg, arg);
  va_end (arg);

  System->Print (mode, buf);
}

//---------------------------------------------------------------------------

IMPLEMENT_EMBEDDED_IBASE (csGraphics3DLine::csLineConfig)
  IMPLEMENTS_INTERFACE (iConfig)
IMPLEMENT_EMBEDDED_IBASE_END

#define NUM_OPTIONS 1

static const csOptionDescription config_options [NUM_OPTIONS] =
{
  { 0, "dummy", "Dummy", CSVAR_BOOL },
};

bool csGraphics3DLine::csLineConfig::SetOption (int id, csVariant* value)
{
  if (value->type != config_options[id].type)
    return false;
  switch (id)
  {
    case 0: break;
    default: return false;
  }
  return true;
}

bool csGraphics3DLine::csLineConfig::GetOption (int id, csVariant* value)
{
  value->type = config_options[id].type;
  switch (id)
  {
    case 0: value->v.b = false; break;
    default: return false;
  }
  return true;
}

bool csGraphics3DLine::csLineConfig::GetOptionDescription
  (int idx, csOptionDescription* option)
{
  if (idx < 0 || idx >= NUM_OPTIONS)
    return false;
  *option = config_options[idx];
  return true;
}

<|endoftext|>


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

#include "net/base/tcp_client_socket.h"

#include "base/memory_debug.h"
#include "base/string_util.h"
#include "base/trace_event.h"
#include "net/base/net_errors.h"
#include "net/base/winsock_init.h"

namespace net {

//-----------------------------------------------------------------------------

static int MapWinsockError(DWORD err) {
  // There are numerous Winsock error codes, but these are the ones we thus far
  // find interesting.
  switch (err) {
    case WSAENETDOWN:
      return ERR_INTERNET_DISCONNECTED;
    case WSAETIMEDOUT:
      return ERR_TIMED_OUT;
    case WSAECONNRESET:
    case WSAENETRESET:  // Related to keep-alive
      return ERR_CONNECTION_RESET;
    case WSAECONNABORTED:
      return ERR_CONNECTION_ABORTED;
    case WSAECONNREFUSED:
      return ERR_CONNECTION_REFUSED;
    case WSAEDISCON:
      // Returned by WSARecv or WSARecvFrom for message-oriented sockets (where
      // a return value of zero means a zero-byte message) to indicate graceful
      // connection shutdown.  We should not ever see this error code for TCP
      // sockets, which are byte stream oriented.
      NOTREACHED();
      return ERR_CONNECTION_CLOSED;
    case WSAEHOSTUNREACH:
    case WSAENETUNREACH:
      return ERR_ADDRESS_UNREACHABLE;
    case WSAEADDRNOTAVAIL:
      return ERR_ADDRESS_INVALID;
    case WSA_IO_INCOMPLETE:
      return ERR_UNEXPECTED;
    case ERROR_SUCCESS:
      return OK;
    default:
      LOG(WARNING) << "Unknown error " << err << " mapped to net::ERR_FAILED";
      return ERR_FAILED;
  }
}

//-----------------------------------------------------------------------------

TCPClientSocket::TCPClientSocket(const AddressList& addresses)
    : socket_(INVALID_SOCKET),
      addresses_(addresses),
      current_ai_(addresses_.head()),
      wait_state_(NOT_WAITING),
      callback_(NULL) {
  memset(&overlapped_, 0, sizeof(overlapped_));
  EnsureWinsockInit();
}

TCPClientSocket::~TCPClientSocket() {
  Disconnect();
}

int TCPClientSocket::Connect(CompletionCallback* callback) {
  // If already connected, then just return OK.
  if (socket_ != INVALID_SOCKET)
    return OK;

  TRACE_EVENT_BEGIN("socket.connect", this, "");
  const struct addrinfo* ai = current_ai_;
  DCHECK(ai);

  int rv = CreateSocket(ai);
  if (rv != OK)
    return rv;

  // WSACreateEvent creates a manual-reset event object.
  overlapped_.hEvent = WSACreateEvent();
  // WSAEventSelect sets the socket to non-blocking mode as a side effect.
  // Our connect() and recv() calls require that the socket be non-blocking.
  WSAEventSelect(socket_, overlapped_.hEvent, FD_CONNECT);

  if (!connect(socket_, ai->ai_addr, static_cast<int>(ai->ai_addrlen))) {
    // Connected without waiting!
    CHECK(WaitForSingleObject(overlapped_.hEvent, 0) == WAIT_OBJECT_0);
    BOOL ok = WSAResetEvent(overlapped_.hEvent);
    CHECK(ok);
    TRACE_EVENT_END("socket.connect", this, "");
    return OK;
  }

  DWORD err = WSAGetLastError();
  if (err != WSAEWOULDBLOCK) {
    LOG(ERROR) << "connect failed: " << err;
    return MapWinsockError(err);
  }

  watcher_.StartWatching(overlapped_.hEvent, this);
  wait_state_ = WAITING_CONNECT;
  callback_ = callback;
  return ERR_IO_PENDING;
}

int TCPClientSocket::ReconnectIgnoringLastError(CompletionCallback* callback) {
  // No ignorable errors!
  return ERR_UNEXPECTED;
}

void TCPClientSocket::Disconnect() {
  if (socket_ == INVALID_SOCKET)
    return;

  TRACE_EVENT_INSTANT("socket.disconnect", this, "");

  // Make sure the message loop is not watching this object anymore.
  watcher_.StopWatching();

  // Cancel any pending IO and wait for it to be aborted.
  if (wait_state_ == WAITING_READ || wait_state_ == WAITING_WRITE) {
    CancelIo(reinterpret_cast<HANDLE>(socket_));
    WaitForSingleObject(overlapped_.hEvent, INFINITE);
    wait_state_ = NOT_WAITING;
  }

  // In most socket implementations, closing a socket results in a graceful
  // connection shutdown, but in Winsock we have to call shutdown explicitly.
  // See the MSDN page "Graceful Shutdown, Linger Options, and Socket Closure"
  // at http://msdn.microsoft.com/en-us/library/ms738547.aspx
  shutdown(socket_, SD_SEND);

  closesocket(socket_);
  socket_ = INVALID_SOCKET;

  WSACloseEvent(overlapped_.hEvent);
  memset(&overlapped_, 0, sizeof(overlapped_));

  // Reset for next time.
  current_ai_ = addresses_.head();
}

bool TCPClientSocket::IsConnected() const {
  if (socket_ == INVALID_SOCKET || wait_state_ == WAITING_CONNECT)
    return false;

  // Check if connection is alive.
  char c;
  int rv = recv(socket_, &c, 1, MSG_PEEK);
  if (rv == 0)
    return false;
  if (rv == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK)
    return false;

  return true;
}

int TCPClientSocket::Read(char* buf,
                          int buf_len,
                          CompletionCallback* callback) {
  DCHECK(socket_ != INVALID_SOCKET);
  DCHECK(wait_state_ == NOT_WAITING);
  DCHECK(!callback_);

  buffer_.len = buf_len;
  buffer_.buf = buf;

  TRACE_EVENT_BEGIN("socket.read", this, "");
  // TODO(wtc): Remove the CHECKs after enough testing.
  CHECK(WaitForSingleObject(overlapped_.hEvent, 0) == WAIT_TIMEOUT);
  DWORD num, flags = 0;
  int rv = WSARecv(socket_, &buffer_, 1, &num, &flags, &overlapped_, NULL);
  if (rv == 0) {
    CHECK(WaitForSingleObject(overlapped_.hEvent, 0) == WAIT_OBJECT_0);
    BOOL ok = WSAResetEvent(overlapped_.hEvent);
    CHECK(ok);
    TRACE_EVENT_END("socket.read", this, StringPrintf("%d bytes", num));

    // Because of how WSARecv fills memory when used asynchronously, Purify
    // isn't able to detect that it's been initialized, so it scans for 0xcd
    // in the buffer and reports UMRs (uninitialized memory reads) for those
    // individual bytes. We override that in PURIFY builds to avoid the false
    // error reports.
    // See bug 5297.
    base::MemoryDebug::MarkAsInitialized(buffer_.buf, num);
    return static_cast<int>(num);
  }
  int err = WSAGetLastError();
  if (err == WSA_IO_PENDING) {
    watcher_.StartWatching(overlapped_.hEvent, this);
    wait_state_ = WAITING_READ;
    callback_ = callback;
    return ERR_IO_PENDING;
  }
  return MapWinsockError(err);
}

int TCPClientSocket::Write(const char* buf,
                           int buf_len,
                           CompletionCallback* callback) {
  DCHECK(socket_ != INVALID_SOCKET);
  DCHECK(wait_state_ == NOT_WAITING);
  DCHECK(!callback_);

  buffer_.len = buf_len;
  buffer_.buf = const_cast<char*>(buf);

  TRACE_EVENT_BEGIN("socket.write", this, "");
  // TODO(wtc): Remove the CHECKs after enough testing.
  CHECK(WaitForSingleObject(overlapped_.hEvent, 0) == WAIT_TIMEOUT);
  DWORD num;
  int rv = WSASend(socket_, &buffer_, 1, &num, 0, &overlapped_, NULL);
  if (rv == 0) {
    // TODO(wtc): These temporary CHECKs are intended to determine the return
    // value and error code of the WaitForSingleObject call if it doesn't
    // return the expected WAIT_OBJECT_0.  See http://crbug.com/6500.
    DWORD wait_rv = WaitForSingleObject(overlapped_.hEvent, 0);
    if (wait_rv != WAIT_OBJECT_0) {
      if (wait_rv == WAIT_ABANDONED) {
        CHECK(false);
      } else if (wait_rv == WAIT_TIMEOUT) {
        CHECK(false);
      } else if (wait_rv == WAIT_FAILED) {
        DWORD wait_error = GetLastError();
        if (wait_error == ERROR_INVALID_HANDLE) {
          CHECK(false);
        } else {
          CHECK(false);
        }
      } else {
        CHECK(false);
      }
    }
    BOOL ok = WSAResetEvent(overlapped_.hEvent);
    CHECK(ok);
    TRACE_EVENT_END("socket.write", this, StringPrintf("%d bytes", num));
    return static_cast<int>(num);
  }
  int err = WSAGetLastError();
  if (err == WSA_IO_PENDING) {
    watcher_.StartWatching(overlapped_.hEvent, this);
    wait_state_ = WAITING_WRITE;
    callback_ = callback;
    return ERR_IO_PENDING;
  }
  return MapWinsockError(err);
}

int TCPClientSocket::CreateSocket(const struct addrinfo* ai) {
  socket_ = WSASocket(ai->ai_family, ai->ai_socktype, ai->ai_protocol, NULL, 0,
                      WSA_FLAG_OVERLAPPED);
  if (socket_ == INVALID_SOCKET) {
    DWORD err = WSAGetLastError();
    LOG(ERROR) << "WSASocket failed: " << err;
    return MapWinsockError(err);
  }

  // Increase the socket buffer sizes from the default sizes.
  // In performance testing, there is substantial benefit by increasing
  // from 8KB to 32KB.  I tested 64, 128, and 256KB as well, but did not
  // see additional performance benefit (will be network dependent).
  // See also:
  //    http://support.microsoft.com/kb/823764/EN-US
  // On XP, the default buffer sizes are 8KB.
  const int kSocketBufferSize = 32 * 1024;
  int rv = setsockopt(socket_, SOL_SOCKET, SO_SNDBUF,
      reinterpret_cast<const char*>(&kSocketBufferSize),
      sizeof(kSocketBufferSize));
  DCHECK(!rv) << "Could not set socket send buffer size";
  rv = setsockopt(socket_, SOL_SOCKET, SO_RCVBUF,
      reinterpret_cast<const char*>(&kSocketBufferSize),
      sizeof(kSocketBufferSize));
  DCHECK(!rv) << "Could not set socket receive buffer size";

  // Disable Nagle.
  // The Nagle implementation on windows is governed by RFC 896.  The idea
  // behind Nagle is to reduce small packets on the network.  When Nagle is
  // enabled, if a partial packet has been sent, the TCP stack will disallow
  // further *partial* packets until an ACK has been received from the other
  // side.  Good applications should always strive to send as much data as
  // possible and avoid partial-packet sends.  However, in most real world
  // applications, there are edge cases where this does not happen, and two
  // partil packets may be sent back to back.  For a browser, it is NEVER
  // a benefit to delay for an RTT before the second packet is sent.
  //
  // As a practical example in Chromium today, consider the case of a small
  // POST.  I have verified this:
  //     Client writes 649 bytes of header  (partial packet #1)
  //     Client writes 50 bytes of POST data (partial packet #2)
  // In the above example, with Nagle, a RTT delay is inserted between these
  // two sends due to nagle.  RTTs can easily be 100ms or more.  The best
  // fix is to make sure that for POSTing data, we write as much data as
  // possible and minimize partial packets.  We will fix that.  But disabling
  // Nagle also ensure we don't run into this delay in other edge cases.
  // See also:
  //    http://technet.microsoft.com/en-us/library/bb726981.aspx
  const BOOL kDisableNagle = TRUE;
  rv = setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY,
      reinterpret_cast<const char*>(&kDisableNagle), sizeof(kDisableNagle));
  DCHECK(!rv) << "Could not disable nagle";

  return OK;
}

void TCPClientSocket::DoCallback(int rv) {
  DCHECK(rv != ERR_IO_PENDING);
  DCHECK(callback_);

  // since Run may result in Read being called, clear callback_ up front.
  CompletionCallback* c = callback_;
  callback_ = NULL;
  c->Run(rv);
}

void TCPClientSocket::DidCompleteConnect() {
  int result;

  TRACE_EVENT_END("socket.connect", this, "");
  wait_state_ = NOT_WAITING;

  WSANETWORKEVENTS events;
  int rv = WSAEnumNetworkEvents(socket_, overlapped_.hEvent, &events);
  if (rv == SOCKET_ERROR) {
    NOTREACHED();
    result = MapWinsockError(WSAGetLastError());
  } else if (events.lNetworkEvents & FD_CONNECT) {
    wait_state_ = NOT_WAITING;
    DWORD error_code = static_cast<DWORD>(events.iErrorCode[FD_CONNECT_BIT]);
    if (current_ai_->ai_next && (
        error_code == WSAEADDRNOTAVAIL ||
        error_code == WSAEAFNOSUPPORT ||
        error_code == WSAECONNREFUSED ||
        error_code == WSAENETUNREACH ||
        error_code == WSAEHOSTUNREACH ||
        error_code == WSAETIMEDOUT)) {
      // Try using the next address.
      const struct addrinfo* next = current_ai_->ai_next;
      Disconnect();
      current_ai_ = next;
      result = Connect(callback_);
    } else {
      result = MapWinsockError(error_code);
    }
  } else {
    NOTREACHED();
    result = ERR_UNEXPECTED;
  }

  if (result != ERR_IO_PENDING)
    DoCallback(result);
}

void TCPClientSocket::DidCompleteIO() {
  DWORD num_bytes, flags;
  BOOL ok = WSAGetOverlappedResult(
      socket_, &overlapped_, &num_bytes, FALSE, &flags);
  WSAResetEvent(overlapped_.hEvent);
  if (wait_state_ == WAITING_READ) {
    TRACE_EVENT_END("socket.read", this, StringPrintf("%d bytes", num_bytes));
  } else {
    TRACE_EVENT_END("socket.write", this, StringPrintf("%d bytes", num_bytes));
  }
  wait_state_ = NOT_WAITING;
  DoCallback(ok ? num_bytes : MapWinsockError(WSAGetLastError()));
}

void TCPClientSocket::OnObjectSignaled(HANDLE object) {
  DCHECK(object == overlapped_.hEvent);

  switch (wait_state_) {
    case WAITING_CONNECT:
      DidCompleteConnect();
      break;
    case WAITING_READ:
    case WAITING_WRITE:
      DidCompleteIO();
      break;
    default:
      NOTREACHED();
      break;
  }
}

}  // namespace net

<commit_msg>Add a dummy function CrashBug6500 so that the return value and error code of the WaitForSingleObject call are captured as function arguments in the crash dumps.<commit_after>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/base/tcp_client_socket.h"

#include "base/memory_debug.h"
#include "base/string_util.h"
#include "base/trace_event.h"
#include "net/base/net_errors.h"
#include "net/base/winsock_init.h"

namespace net {

//-----------------------------------------------------------------------------

static int MapWinsockError(DWORD err) {
  // There are numerous Winsock error codes, but these are the ones we thus far
  // find interesting.
  switch (err) {
    case WSAENETDOWN:
      return ERR_INTERNET_DISCONNECTED;
    case WSAETIMEDOUT:
      return ERR_TIMED_OUT;
    case WSAECONNRESET:
    case WSAENETRESET:  // Related to keep-alive
      return ERR_CONNECTION_RESET;
    case WSAECONNABORTED:
      return ERR_CONNECTION_ABORTED;
    case WSAECONNREFUSED:
      return ERR_CONNECTION_REFUSED;
    case WSAEDISCON:
      // Returned by WSARecv or WSARecvFrom for message-oriented sockets (where
      // a return value of zero means a zero-byte message) to indicate graceful
      // connection shutdown.  We should not ever see this error code for TCP
      // sockets, which are byte stream oriented.
      NOTREACHED();
      return ERR_CONNECTION_CLOSED;
    case WSAEHOSTUNREACH:
    case WSAENETUNREACH:
      return ERR_ADDRESS_UNREACHABLE;
    case WSAEADDRNOTAVAIL:
      return ERR_ADDRESS_INVALID;
    case WSA_IO_INCOMPLETE:
      return ERR_UNEXPECTED;
    case ERROR_SUCCESS:
      return OK;
    default:
      LOG(WARNING) << "Unknown error " << err << " mapped to net::ERR_FAILED";
      return ERR_FAILED;
  }
}

//-----------------------------------------------------------------------------

TCPClientSocket::TCPClientSocket(const AddressList& addresses)
    : socket_(INVALID_SOCKET),
      addresses_(addresses),
      current_ai_(addresses_.head()),
      wait_state_(NOT_WAITING),
      callback_(NULL) {
  memset(&overlapped_, 0, sizeof(overlapped_));
  EnsureWinsockInit();
}

TCPClientSocket::~TCPClientSocket() {
  Disconnect();
}

int TCPClientSocket::Connect(CompletionCallback* callback) {
  // If already connected, then just return OK.
  if (socket_ != INVALID_SOCKET)
    return OK;

  TRACE_EVENT_BEGIN("socket.connect", this, "");
  const struct addrinfo* ai = current_ai_;
  DCHECK(ai);

  int rv = CreateSocket(ai);
  if (rv != OK)
    return rv;

  // WSACreateEvent creates a manual-reset event object.
  overlapped_.hEvent = WSACreateEvent();
  // WSAEventSelect sets the socket to non-blocking mode as a side effect.
  // Our connect() and recv() calls require that the socket be non-blocking.
  WSAEventSelect(socket_, overlapped_.hEvent, FD_CONNECT);

  if (!connect(socket_, ai->ai_addr, static_cast<int>(ai->ai_addrlen))) {
    // Connected without waiting!
    CHECK(WaitForSingleObject(overlapped_.hEvent, 0) == WAIT_OBJECT_0);
    BOOL ok = WSAResetEvent(overlapped_.hEvent);
    CHECK(ok);
    TRACE_EVENT_END("socket.connect", this, "");
    return OK;
  }

  DWORD err = WSAGetLastError();
  if (err != WSAEWOULDBLOCK) {
    LOG(ERROR) << "connect failed: " << err;
    return MapWinsockError(err);
  }

  watcher_.StartWatching(overlapped_.hEvent, this);
  wait_state_ = WAITING_CONNECT;
  callback_ = callback;
  return ERR_IO_PENDING;
}

int TCPClientSocket::ReconnectIgnoringLastError(CompletionCallback* callback) {
  // No ignorable errors!
  return ERR_UNEXPECTED;
}

void TCPClientSocket::Disconnect() {
  if (socket_ == INVALID_SOCKET)
    return;

  TRACE_EVENT_INSTANT("socket.disconnect", this, "");

  // Make sure the message loop is not watching this object anymore.
  watcher_.StopWatching();

  // Cancel any pending IO and wait for it to be aborted.
  if (wait_state_ == WAITING_READ || wait_state_ == WAITING_WRITE) {
    CancelIo(reinterpret_cast<HANDLE>(socket_));
    WaitForSingleObject(overlapped_.hEvent, INFINITE);
    wait_state_ = NOT_WAITING;
  }

  // In most socket implementations, closing a socket results in a graceful
  // connection shutdown, but in Winsock we have to call shutdown explicitly.
  // See the MSDN page "Graceful Shutdown, Linger Options, and Socket Closure"
  // at http://msdn.microsoft.com/en-us/library/ms738547.aspx
  shutdown(socket_, SD_SEND);

  closesocket(socket_);
  socket_ = INVALID_SOCKET;

  WSACloseEvent(overlapped_.hEvent);
  memset(&overlapped_, 0, sizeof(overlapped_));

  // Reset for next time.
  current_ai_ = addresses_.head();
}

bool TCPClientSocket::IsConnected() const {
  if (socket_ == INVALID_SOCKET || wait_state_ == WAITING_CONNECT)
    return false;

  // Check if connection is alive.
  char c;
  int rv = recv(socket_, &c, 1, MSG_PEEK);
  if (rv == 0)
    return false;
  if (rv == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK)
    return false;

  return true;
}

int TCPClientSocket::Read(char* buf,
                          int buf_len,
                          CompletionCallback* callback) {
  DCHECK(socket_ != INVALID_SOCKET);
  DCHECK(wait_state_ == NOT_WAITING);
  DCHECK(!callback_);

  buffer_.len = buf_len;
  buffer_.buf = buf;

  TRACE_EVENT_BEGIN("socket.read", this, "");
  // TODO(wtc): Remove the CHECKs after enough testing.
  CHECK(WaitForSingleObject(overlapped_.hEvent, 0) == WAIT_TIMEOUT);
  DWORD num, flags = 0;
  int rv = WSARecv(socket_, &buffer_, 1, &num, &flags, &overlapped_, NULL);
  if (rv == 0) {
    CHECK(WaitForSingleObject(overlapped_.hEvent, 0) == WAIT_OBJECT_0);
    BOOL ok = WSAResetEvent(overlapped_.hEvent);
    CHECK(ok);
    TRACE_EVENT_END("socket.read", this, StringPrintf("%d bytes", num));

    // Because of how WSARecv fills memory when used asynchronously, Purify
    // isn't able to detect that it's been initialized, so it scans for 0xcd
    // in the buffer and reports UMRs (uninitialized memory reads) for those
    // individual bytes. We override that in PURIFY builds to avoid the false
    // error reports.
    // See bug 5297.
    base::MemoryDebug::MarkAsInitialized(buffer_.buf, num);
    return static_cast<int>(num);
  }
  int err = WSAGetLastError();
  if (err == WSA_IO_PENDING) {
    watcher_.StartWatching(overlapped_.hEvent, this);
    wait_state_ = WAITING_READ;
    callback_ = callback;
    return ERR_IO_PENDING;
  }
  return MapWinsockError(err);
}

// TODO(wtc): This temporary function is intended to determine the return
// value and error code of the WaitForSingleObject call in
// TCPClientSocket::Write if it doesn't return the expected WAIT_OBJECT_0.
// See http://crbug.com/6500.
static void CrashBug6500(DWORD wait_rv, DWORD wait_error) {
  // wait_error is meaningful only if wait_rv is WAIT_FAILED.
  CHECK(false) << wait_rv << wait_error;
}

int TCPClientSocket::Write(const char* buf,
                           int buf_len,
                           CompletionCallback* callback) {
  DCHECK(socket_ != INVALID_SOCKET);
  DCHECK(wait_state_ == NOT_WAITING);
  DCHECK(!callback_);

  buffer_.len = buf_len;
  buffer_.buf = const_cast<char*>(buf);

  TRACE_EVENT_BEGIN("socket.write", this, "");
  // TODO(wtc): Remove the CHECKs after enough testing.
  CHECK(WaitForSingleObject(overlapped_.hEvent, 0) == WAIT_TIMEOUT);
  DWORD num;
  int rv = WSASend(socket_, &buffer_, 1, &num, 0, &overlapped_, NULL);
  if (rv == 0) {
    DWORD wait_rv = WaitForSingleObject(overlapped_.hEvent, 0);
    if (wait_rv != WAIT_OBJECT_0) {
      DWORD wait_error = GetLastError();
      CrashBug6500(wait_rv, wait_error);
    }
    BOOL ok = WSAResetEvent(overlapped_.hEvent);
    CHECK(ok);
    TRACE_EVENT_END("socket.write", this, StringPrintf("%d bytes", num));
    return static_cast<int>(num);
  }
  int err = WSAGetLastError();
  if (err == WSA_IO_PENDING) {
    watcher_.StartWatching(overlapped_.hEvent, this);
    wait_state_ = WAITING_WRITE;
    callback_ = callback;
    return ERR_IO_PENDING;
  }
  return MapWinsockError(err);
}

int TCPClientSocket::CreateSocket(const struct addrinfo* ai) {
  socket_ = WSASocket(ai->ai_family, ai->ai_socktype, ai->ai_protocol, NULL, 0,
                      WSA_FLAG_OVERLAPPED);
  if (socket_ == INVALID_SOCKET) {
    DWORD err = WSAGetLastError();
    LOG(ERROR) << "WSASocket failed: " << err;
    return MapWinsockError(err);
  }

  // Increase the socket buffer sizes from the default sizes.
  // In performance testing, there is substantial benefit by increasing
  // from 8KB to 32KB.  I tested 64, 128, and 256KB as well, but did not
  // see additional performance benefit (will be network dependent).
  // See also:
  //    http://support.microsoft.com/kb/823764/EN-US
  // On XP, the default buffer sizes are 8KB.
  const int kSocketBufferSize = 32 * 1024;
  int rv = setsockopt(socket_, SOL_SOCKET, SO_SNDBUF,
      reinterpret_cast<const char*>(&kSocketBufferSize),
      sizeof(kSocketBufferSize));
  DCHECK(!rv) << "Could not set socket send buffer size";
  rv = setsockopt(socket_, SOL_SOCKET, SO_RCVBUF,
      reinterpret_cast<const char*>(&kSocketBufferSize),
      sizeof(kSocketBufferSize));
  DCHECK(!rv) << "Could not set socket receive buffer size";

  // Disable Nagle.
  // The Nagle implementation on windows is governed by RFC 896.  The idea
  // behind Nagle is to reduce small packets on the network.  When Nagle is
  // enabled, if a partial packet has been sent, the TCP stack will disallow
  // further *partial* packets until an ACK has been received from the other
  // side.  Good applications should always strive to send as much data as
  // possible and avoid partial-packet sends.  However, in most real world
  // applications, there are edge cases where this does not happen, and two
  // partil packets may be sent back to back.  For a browser, it is NEVER
  // a benefit to delay for an RTT before the second packet is sent.
  //
  // As a practical example in Chromium today, consider the case of a small
  // POST.  I have verified this:
  //     Client writes 649 bytes of header  (partial packet #1)
  //     Client writes 50 bytes of POST data (partial packet #2)
  // In the above example, with Nagle, a RTT delay is inserted between these
  // two sends due to nagle.  RTTs can easily be 100ms or more.  The best
  // fix is to make sure that for POSTing data, we write as much data as
  // possible and minimize partial packets.  We will fix that.  But disabling
  // Nagle also ensure we don't run into this delay in other edge cases.
  // See also:
  //    http://technet.microsoft.com/en-us/library/bb726981.aspx
  const BOOL kDisableNagle = TRUE;
  rv = setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY,
      reinterpret_cast<const char*>(&kDisableNagle), sizeof(kDisableNagle));
  DCHECK(!rv) << "Could not disable nagle";

  return OK;
}

void TCPClientSocket::DoCallback(int rv) {
  DCHECK(rv != ERR_IO_PENDING);
  DCHECK(callback_);

  // since Run may result in Read being called, clear callback_ up front.
  CompletionCallback* c = callback_;
  callback_ = NULL;
  c->Run(rv);
}

void TCPClientSocket::DidCompleteConnect() {
  int result;

  TRACE_EVENT_END("socket.connect", this, "");
  wait_state_ = NOT_WAITING;

  WSANETWORKEVENTS events;
  int rv = WSAEnumNetworkEvents(socket_, overlapped_.hEvent, &events);
  if (rv == SOCKET_ERROR) {
    NOTREACHED();
    result = MapWinsockError(WSAGetLastError());
  } else if (events.lNetworkEvents & FD_CONNECT) {
    wait_state_ = NOT_WAITING;
    DWORD error_code = static_cast<DWORD>(events.iErrorCode[FD_CONNECT_BIT]);
    if (current_ai_->ai_next && (
        error_code == WSAEADDRNOTAVAIL ||
        error_code == WSAEAFNOSUPPORT ||
        error_code == WSAECONNREFUSED ||
        error_code == WSAENETUNREACH ||
        error_code == WSAEHOSTUNREACH ||
        error_code == WSAETIMEDOUT)) {
      // Try using the next address.
      const struct addrinfo* next = current_ai_->ai_next;
      Disconnect();
      current_ai_ = next;
      result = Connect(callback_);
    } else {
      result = MapWinsockError(error_code);
    }
  } else {
    NOTREACHED();
    result = ERR_UNEXPECTED;
  }

  if (result != ERR_IO_PENDING)
    DoCallback(result);
}

void TCPClientSocket::DidCompleteIO() {
  DWORD num_bytes, flags;
  BOOL ok = WSAGetOverlappedResult(
      socket_, &overlapped_, &num_bytes, FALSE, &flags);
  WSAResetEvent(overlapped_.hEvent);
  if (wait_state_ == WAITING_READ) {
    TRACE_EVENT_END("socket.read", this, StringPrintf("%d bytes", num_bytes));
  } else {
    TRACE_EVENT_END("socket.write", this, StringPrintf("%d bytes", num_bytes));
  }
  wait_state_ = NOT_WAITING;
  DoCallback(ok ? num_bytes : MapWinsockError(WSAGetLastError()));
}

void TCPClientSocket::OnObjectSignaled(HANDLE object) {
  DCHECK(object == overlapped_.hEvent);

  switch (wait_state_) {
    case WAITING_CONNECT:
      DidCompleteConnect();
      break;
    case WAITING_READ:
    case WAITING_WRITE:
      DidCompleteIO();
      break;
    default:
      NOTREACHED();
      break;
  }
}

}  // namespace net

<|endoftext|>


<commit_before><commit_msg>INTEGRATION: CWS layoutmanager (1.1.2); FILE ADDED 2004/01/19 13:12:05 cd 1.1.2.1: #111899# Moved xml load/store classes to own sub folder<commit_after><|endoftext|>