<commit_before>#include "system/multirotor_vehicle_system.hpp"

#include <input/ppm_input_source.hpp>

constexpr double M_PI = 3.1415926535;

MultirotorVehicleSystem::MultirotorVehicleSystem(
    Gyroscope& gyr,
    Accelerometer& acc,
    optional<Barometer *> bar,
    optional<GPS *> gps,
    optional<Magnetometer *> mag,
    WorldEstimator& estimator,
    InputSource& inputSource,
    MotorMapper& motorMapper,
    Communicator& communicator,
    Logger& logger,
    Platform& platform)
  : VehicleSystem(communicator),
    MessageListener(communicator),
    gyr(gyr), acc(acc), bar(bar), gps(gps), mag(mag),
    estimator(estimator),
    inputSource(inputSource),
    motorMapper(motorMapper),
    platform(platform),
    stream(communicator, 10), logger(logger),
    mode(MultirotorControlMode::DISARMED),
    calibrated(false),
    altSp(0.0),
    yawPosSp(0.0) {
  // Disarm by default. A set_arm_state_message_t message is required to enable
  // the control pipeline.
  setArmed(false);
  gyr.setAxisConfig(unit_config::GYR_AXES);
  acc.setAxisConfig(unit_config::ACC_AXES);
  gyr.setOffsets(unit_config::GYR_OFFSETS);
  acc.setOffsets(unit_config::ACC_OFFSETS);
}

void MultirotorVehicleSystem::update() {
  // Poll sensors
  GyroscopeReading gyroReading = gyr.readGyro();
  AccelerometerReading accelReading = acc.readAccel();
  optional<BarometerReading> barReading;
  optional<GPSReading> gpsReading;
  optional<MagnetometerReading> magReading;

  if (bar) barReading = (*bar)->readBar();
  if (gps) gpsReading = (*gps)->readGPS();
  if (mag) magReading = (*mag)->readMag();

  // TODO: Currently copying all readings
  SensorMeasurements meas {
    .accel  = std::experimental::make_optional(accelReading),
    .accelH = std::experimental::nullopt,
    .bar    = barReading,
    .ggr    = std::experimental::nullopt,
    .gps    = gpsReading,
    .gyro   = std::experimental::make_optional(gyroReading),
    .mag    = magReading
  };

  // Update world estimate
  WorldEstimate estimate = estimator.update(meas);

  // Poll for controller input
  ControllerInput input = inputSource.read();

  // Set mode
  if (calibrated && input.valid) {
    setArmed(input.armed);

    if (isArmed()) {
      // AUTO
      if (input.mode == MODE_AUTO) {
        mode = MultirotorControlMode::ANGULAR_POS;   // TODO(yoos): AUTO disabled until we implement controller
      }
      // MANUAL or ALTCTL
      else {
        if (input.velocityMode) {
          mode = MultirotorControlMode::ANGULAR_VEL;
        }
        else {
          mode = MultirotorControlMode::ANGULAR_POS;
        }
      }
    }
    else {
      mode = MultirotorControlMode::DISARMED;
    }
  }

  // Run the controllers
  ActuatorSetpoint actuatorSp;
  switch (mode) {
    case MultirotorControlMode::DISARMED:
      DisarmedMode(meas, estimate, input, actuatorSp);
      break;
    case MultirotorControlMode::ANGULAR_VEL:
      AngularRateMode(meas, estimate, input, actuatorSp);
      break;
    case MultirotorControlMode::ANGULAR_POS:
      AngularPosMode(meas, estimate, input, actuatorSp);
      break;
    case MultirotorControlMode::POSITION:
      PosMode(meas, estimate, input, actuatorSp);
      break;
    default:
      break;
  }

  // Update motor outputs
  motorMapper.run(isArmed(), actuatorSp);
}

bool MultirotorVehicleSystem::healthy() {
  bool healthy = gyr.healthy() && acc.healthy();

  if(bar) {
    healthy &= (*bar)->healthy();
  }

  if(gps) {
    healthy &= (*gps)->healthy();
  }

  if(mag) {
    healthy &= (*mag)->healthy();
  }

  return healthy;
}

void MultirotorVehicleSystem::on(const protocol::message::set_arm_state_message_t& m) {
  setArmed(m.armed);
}

void MultirotorVehicleSystem::calibrate(SensorMeasurements meas) {
  PulseLED(0,1,0,4);
  static int calibCount = 0;
  static std::array<float, 3> gyrOffsets = unit_config::GYR_OFFSETS;

  // Calibrate ground altitude
  //groundAltitude = est.loc.alt;

  // Calibrate gyroscope
  for (int i=0; i<3; i++) {
    gyrOffsets[i] = (gyrOffsets[i]*calibCount + (*meas.gyro).axes[i]+unit_config::GYR_OFFSETS[i])/(calibCount+1);
  }
  calibCount++;

  // Reset calibration on excessive gyration
  if (fabs((*meas.gyro).axes[0] > 0.1) ||
      fabs((*meas.gyro).axes[1] > 0.1) ||
      fabs((*meas.gyro).axes[2] > 0.1)) {
    calibCount = 0;
  }

  // Run calibration for 5 seconds
  if (calibCount == 5000) {
    gyr.setOffsets(gyrOffsets);
    protocol::message::sensor_calibration_response_message_t m_gyrcal {
      .type = protocol::message::sensor_calibration_response_message_t::SensorType::GYRO,
      .offsets = {gyrOffsets[0], gyrOffsets[1], gyrOffsets[2]}
    };
    logger.write(m_gyrcal);
    if (stream.ready()) {
      stream.publish(m_gyrcal);
    }

    calibrated = true;
  }
}

void MultirotorVehicleSystem::DisarmedMode(SensorMeasurements meas, WorldEstimate est, ControllerInput input, ActuatorSetpoint& sp) {
  PulseLED(0,1,0,1);
  if (!calibrated) {
    calibrate(meas);
  }

  // Run the zero controller
  ActuatorSetpoint zSp = {0, 0, 0, 0};
  sp = zeroController.run(est, zSp);
}

void MultirotorVehicleSystem::AngularRateMode(SensorMeasurements meas, WorldEstimate est, ControllerInput input, ActuatorSetpoint& sp) {
  PulseLED(0,0,1,8);
  altSp = est.loc.alt;   // Reset altitude setpoint
  yawPosSp = est.att.yaw;   // Reset yaw position setpoint to current yaw position while in velocity mode

  AngularVelocitySetpoint avSp {
    .rollVel  = input.roll,
    .pitchVel = input.pitch,
    .yawVel   = input.yaw,
    .throttle = input.throttle
  };
  sp = pipeline.run(est, avSp, attVelController, attAccController);
}

void MultirotorVehicleSystem::AngularPosMode(SensorMeasurements meas, WorldEstimate est, ControllerInput input, ActuatorSetpoint& sp) {
  SetLED(0,0,1);
  yawPosSp += input.yaw * unit_config::DT;
  if (yawPosSp > M_PI)  {yawPosSp -= 2*M_PI;}
  if (yawPosSp < -M_PI) {yawPosSp += 2*M_PI;}

  AngularPositionSetpoint apSp {
    .rollPos  = input.roll,
    .pitchPos = input.pitch,
    .yawPos   = yawPosSp,
    .throttle = input.throttle
  };

  // Reset altitude setpoint only if not in ALTCTL mode
  if (input.mode != MODE_ALTCTL) {
    altSp = est.loc.alt;
  }
  // Somewhat hacky way to achieve altitude hold. Pilot should first find
  // a good hover throttle, then switch to ALTCTL.
  else {
    float throttleShift = std::min(std::max(altSp - est.loc.alt, -0.2), 0.2);
    apSp.throttle += throttleShift;
  }

  // TODO(yoos): Adjust roll based on yaw. Servo angle is coupled with
  // roll. That is, we need the output of the acceleration controller to
  // change the input to the position controller. How can we do this?
  sp = pipeline.run(est, apSp, attPosController, attVelController, attAccController);
}

void MultirotorVehicleSystem::PosMode(SensorMeasurements meas, WorldEstimate est, ControllerInput input, ActuatorSetpoint& sp) {
  SetLED(0,1,1);
  yawPosSp += input.yaw * unit_config::DT;
  if (yawPosSp > M_PI)  {yawPosSp -= 2*M_PI;}
  if (yawPosSp < -M_PI) {yawPosSp += 2*M_PI;}

  PositionSetpoint pSp {
    .lat    = est.loc.lat,
    .lon    = est.loc.lon,
    .yawPos = yawPosSp,
    .alt    = est.loc.alt
  };
  sp = pipeline.run(est, pSp, posController, attPosController, attVelController, attAccController);
}

void MultirotorVehicleSystem::SetLED(float r, float g, float b) {
  platform.get<PWMPlatform>().set(9,  r);
  platform.get<PWMPlatform>().set(10, g);
  platform.get<PWMPlatform>().set(11, b);
}

void MultirotorVehicleSystem::BlinkLED(float r, float g, float b, float freq) {
  static int count = 0;
  int period = 1000 / freq;
  if (count % period < period/2) {
    SetLED(r,g,b);
  }
  else {
    SetLED(0,0,0);
  }
  count = (count+1) % period;
}

void MultirotorVehicleSystem::PulseLED(float r, float g, float b, float freq) {
  int period = 1000 / freq;
  static int count = 0;
  float dc = ((float) abs(period/2 - count)) / (period/2);
  SetLED(dc*r, dc*g, dc*b);
  count = (count+1) % period;
}

void MultirotorVehicleSystem::RGBLED(float freq) {
  static float dc = 0.0;
  static int dir = 1;
  if (dc >= 1.0) {
    dir = -1;
  }
  else if (dc <= 0.0) {
    dir = 1;
  }
  dc += dir * (2*freq * unit_config::DT);

  float dc_ = dc;
  float dir_ = dir;
  for (int i=0; i<3; i++) {
    dc_ += dir_ * 0.666;
    if (dc_ > 1.0) {
      dc_ = 2.0 - dc_;
      dir_ = -1;
    }
    else if (dc_ < 0.0) {
      dc_ = 0.0 - dc_;
      dir_ = 1;
    }
    platform.get<PWMPlatform>().set(9+i, dc_);
  }
}
<commit_msg>Cast controlmode enum to int.<commit_after>#include "system/multirotor_vehicle_system.hpp"

#include <input/ppm_input_source.hpp>

constexpr double M_PI = 3.1415926535;

MultirotorVehicleSystem::MultirotorVehicleSystem(
    Gyroscope& gyr,
    Accelerometer& acc,
    optional<Barometer *> bar,
    optional<GPS *> gps,
    optional<Magnetometer *> mag,
    WorldEstimator& estimator,
    InputSource& inputSource,
    MotorMapper& motorMapper,
    Communicator& communicator,
    Logger& logger,
    Platform& platform)
  : VehicleSystem(communicator),
    MessageListener(communicator),
    gyr(gyr), acc(acc), bar(bar), gps(gps), mag(mag),
    estimator(estimator),
    inputSource(inputSource),
    motorMapper(motorMapper),
    platform(platform),
    stream(communicator, 10), logger(logger),
    mode(MultirotorControlMode::DISARMED),
    calibrated(false),
    altSp(0.0),
    yawPosSp(0.0) {
  // Disarm by default. A set_arm_state_message_t message is required to enable
  // the control pipeline.
  setArmed(false);
  gyr.setAxisConfig(unit_config::GYR_AXES);
  acc.setAxisConfig(unit_config::ACC_AXES);
  gyr.setOffsets(unit_config::GYR_OFFSETS);
  acc.setOffsets(unit_config::ACC_OFFSETS);
}

void MultirotorVehicleSystem::update() {
  // Poll sensors
  GyroscopeReading gyroReading = gyr.readGyro();
  AccelerometerReading accelReading = acc.readAccel();
  optional<BarometerReading> barReading;
  optional<GPSReading> gpsReading;
  optional<MagnetometerReading> magReading;

  if (bar) barReading = (*bar)->readBar();
  if (gps) gpsReading = (*gps)->readGPS();
  if (mag) magReading = (*mag)->readMag();

  // TODO: Currently copying all readings
  SensorMeasurements meas {
    .accel  = std::experimental::make_optional(accelReading),
    .accelH = std::experimental::nullopt,
    .bar    = barReading,
    .ggr    = std::experimental::nullopt,
    .gps    = gpsReading,
    .gyro   = std::experimental::make_optional(gyroReading),
    .mag    = magReading
  };

  // Update world estimate
  WorldEstimate estimate = estimator.update(meas);

  // Poll for controller input
  ControllerInput input = inputSource.read();

  // Set mode
  if (calibrated && input.valid) {
    setArmed(input.armed);

    if (isArmed()) {
      // AUTO
      if (input.mode == (int)InputControlMode::AUTO) {
        mode = MultirotorControlMode::ANGULAR_POS;   // TODO(yoos): AUTO disabled until we implement controller
      }
      // MANUAL or ALTCTL
      else {
        if (input.velocityMode) {
          mode = MultirotorControlMode::ANGULAR_VEL;
        }
        else {
          mode = MultirotorControlMode::ANGULAR_POS;
        }
      }
    }
    else {
      mode = MultirotorControlMode::DISARMED;
    }
  }

  // Run the controllers
  ActuatorSetpoint actuatorSp;
  switch (mode) {
    case MultirotorControlMode::DISARMED:
      DisarmedMode(meas, estimate, input, actuatorSp);
      break;
    case MultirotorControlMode::ANGULAR_VEL:
      AngularRateMode(meas, estimate, input, actuatorSp);
      break;
    case MultirotorControlMode::ANGULAR_POS:
      AngularPosMode(meas, estimate, input, actuatorSp);
      break;
    case MultirotorControlMode::POSITION:
      PosMode(meas, estimate, input, actuatorSp);
      break;
    default:
      break;
  }

  // Update motor outputs
  motorMapper.run(isArmed(), actuatorSp);
}

bool MultirotorVehicleSystem::healthy() {
  bool healthy = gyr.healthy() && acc.healthy();

  if(bar) {
    healthy &= (*bar)->healthy();
  }

  if(gps) {
    healthy &= (*gps)->healthy();
  }

  if(mag) {
    healthy &= (*mag)->healthy();
  }

  return healthy;
}

void MultirotorVehicleSystem::on(const protocol::message::set_arm_state_message_t& m) {
  setArmed(m.armed);
}

void MultirotorVehicleSystem::calibrate(SensorMeasurements meas) {
  PulseLED(0,1,0,4);
  static int calibCount = 0;
  static std::array<float, 3> gyrOffsets = unit_config::GYR_OFFSETS;

  // Calibrate ground altitude
  //groundAltitude = est.loc.alt;

  // Calibrate gyroscope
  for (int i=0; i<3; i++) {
    gyrOffsets[i] = (gyrOffsets[i]*calibCount + (*meas.gyro).axes[i]+unit_config::GYR_OFFSETS[i])/(calibCount+1);
  }
  calibCount++;

  // Reset calibration on excessive gyration
  if (fabs((*meas.gyro).axes[0] > 0.1) ||
      fabs((*meas.gyro).axes[1] > 0.1) ||
      fabs((*meas.gyro).axes[2] > 0.1)) {
    calibCount = 0;
  }

  // Run calibration for 5 seconds
  if (calibCount == 5000) {
    gyr.setOffsets(gyrOffsets);
    protocol::message::sensor_calibration_response_message_t m_gyrcal {
      .type = protocol::message::sensor_calibration_response_message_t::SensorType::GYRO,
      .offsets = {gyrOffsets[0], gyrOffsets[1], gyrOffsets[2]}
    };
    logger.write(m_gyrcal);
    if (stream.ready()) {
      stream.publish(m_gyrcal);
    }

    calibrated = true;
  }
}

void MultirotorVehicleSystem::DisarmedMode(SensorMeasurements meas, WorldEstimate est, ControllerInput input, ActuatorSetpoint& sp) {
  PulseLED(0,1,0,1);
  if (!calibrated) {
    calibrate(meas);
  }

  // Run the zero controller
  ActuatorSetpoint zSp = {0, 0, 0, 0};
  sp = zeroController.run(est, zSp);
}

void MultirotorVehicleSystem::AngularRateMode(SensorMeasurements meas, WorldEstimate est, ControllerInput input, ActuatorSetpoint& sp) {
  PulseLED(0,0,1,8);
  altSp = est.loc.alt;   // Reset altitude setpoint
  yawPosSp = est.att.yaw;   // Reset yaw position setpoint to current yaw position while in velocity mode

  AngularVelocitySetpoint avSp {
    .rollVel  = input.roll,
    .pitchVel = input.pitch,
    .yawVel   = input.yaw,
    .throttle = input.throttle
  };
  sp = pipeline.run(est, avSp, attVelController, attAccController);
}

void MultirotorVehicleSystem::AngularPosMode(SensorMeasurements meas, WorldEstimate est, ControllerInput input, ActuatorSetpoint& sp) {
  SetLED(0,0,1);
  yawPosSp += input.yaw * unit_config::DT;
  if (yawPosSp > M_PI)  {yawPosSp -= 2*M_PI;}
  if (yawPosSp < -M_PI) {yawPosSp += 2*M_PI;}

  AngularPositionSetpoint apSp {
    .rollPos  = input.roll,
    .pitchPos = input.pitch,
    .yawPos   = yawPosSp,
    .throttle = input.throttle
  };

  // Reset altitude setpoint only if not in ALTCTL mode
  if (input.mode != (int)InputControlMode::ALTCTL) {
    altSp = est.loc.alt;
  }
  // Somewhat hacky way to achieve altitude hold. Pilot should first find
  // a good hover throttle, then switch to ALTCTL.
  else {
    float throttleShift = std::min(std::max(altSp - est.loc.alt, -0.2), 0.2);
    apSp.throttle += throttleShift;
  }

  // TODO(yoos): Adjust roll based on yaw. Servo angle is coupled with
  // roll. That is, we need the output of the acceleration controller to
  // change the input to the position controller. How can we do this?
  sp = pipeline.run(est, apSp, attPosController, attVelController, attAccController);
}

void MultirotorVehicleSystem::PosMode(SensorMeasurements meas, WorldEstimate est, ControllerInput input, ActuatorSetpoint& sp) {
  SetLED(0,1,1);
  yawPosSp += input.yaw * unit_config::DT;
  if (yawPosSp > M_PI)  {yawPosSp -= 2*M_PI;}
  if (yawPosSp < -M_PI) {yawPosSp += 2*M_PI;}

  PositionSetpoint pSp {
    .lat    = est.loc.lat,
    .lon    = est.loc.lon,
    .yawPos = yawPosSp,
    .alt    = est.loc.alt
  };
  sp = pipeline.run(est, pSp, posController, attPosController, attVelController, attAccController);
}

void MultirotorVehicleSystem::SetLED(float r, float g, float b) {
  platform.get<PWMPlatform>().set(9,  r);
  platform.get<PWMPlatform>().set(10, g);
  platform.get<PWMPlatform>().set(11, b);
}

void MultirotorVehicleSystem::BlinkLED(float r, float g, float b, float freq) {
  static int count = 0;
  int period = 1000 / freq;
  if (count % period < period/2) {
    SetLED(r,g,b);
  }
  else {
    SetLED(0,0,0);
  }
  count = (count+1) % period;
}

void MultirotorVehicleSystem::PulseLED(float r, float g, float b, float freq) {
  int period = 1000 / freq;
  static int count = 0;
  float dc = ((float) abs(period/2 - count)) / (period/2);
  SetLED(dc*r, dc*g, dc*b);
  count = (count+1) % period;
}

void MultirotorVehicleSystem::RGBLED(float freq) {
  static float dc = 0.0;
  static int dir = 1;
  if (dc >= 1.0) {
    dir = -1;
  }
  else if (dc <= 0.0) {
    dir = 1;
  }
  dc += dir * (2*freq * unit_config::DT);

  float dc_ = dc;
  float dir_ = dir;
  for (int i=0; i<3; i++) {
    dc_ += dir_ * 0.666;
    if (dc_ > 1.0) {
      dc_ = 2.0 - dc_;
      dir_ = -1;
    }
    else if (dc_ < 0.0) {
      dc_ = 0.0 - dc_;
      dir_ = 1;
    }
    platform.get<PWMPlatform>().set(9+i, dc_);
  }
}
<|endoftext|>


<commit_before>/* RTcmix  - Copyright (C) 2000  The RTcmix Development Team
   See ``AUTHORS'' for a list of contributors. See ``LICENSE'' for
   the license to this software and for a DISCLAIMER OF ALL WARRANTIES.
*/
/* rev'd for v2.3, JGG, 20-Feb-00 */
/* major revision for 3.7 (converted to C++ source), DS, April-04
   (see deleted .c file for earlier history)
*/

#include <stdio.h>
#include <unistd.h>
#include <math.h>       /* for fabs */
#include <assert.h>
#include <globals.h>
#include <prototypes.h>
#include <buffers.h>
#include <ugens.h>
#include <sndlibsupport.h>
#include "../rtstuff/rtdefs.h"
#include "AudioDevice.h"
#include "AudioFileDevice.h"

/* #define DUMP_AUDIO_TO_RAW_FILE */
/* #define USE_REAL2INT */

extern AudioDevice *globalOutputFileDevice;	// audio_devices.cpp

static int printing_dots = 0;

/* local prototypes */
static int write_to_audio_device(AudioDevice *);
static void limiter(void);

#ifdef DUMP_AUDIO_TO_RAW_FILE
/* ----------------------------------------------- dump_audio_to_raw_file --- */
/* For debugging audio device writes.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
static void
dump_audio_to_raw_file(OBufPtr dest, int nbytes)
{
   int         result;
   static int  fd = -1;

   if (fd == -1) {
      fd = open("dumpaudio.raw", O_RDWR | O_CREAT | O_TRUNC, 0666);
      assert(fd > 0);
      fprintf(stderr, "Dumping audio to \"dumpaudio.raw\".\n");
   }
   result = write(fd, dest, nbytes);
   assert(result != -1);
}
#endif /* DUMP_AUDIO_TO_RAW_FILE */

#ifdef USE_REAL2INT
/* ------------------------------------------------------------- real2int --- */
/* This is supposed to be more efficient than casting from float to int,
   but I didn't notice much difference.  This came from info off of the
   music-dsp list.  -JGG
*/
typedef long int32;
static double _double2fixmagic = 68719476736.0 * 1.5;
static int32 _shiftamt = 16;
#if MUS_LITTLE_ENDIAN    /* processor is little-endian */
   #define iman_   0
#else
   #define iman_   1
#endif

static INLINE int
real2int(float val)
{
   double x = (double) val;
   x = x + _double2fixmagic;
   return ((int32 *) &x)[iman_] >> _shiftamt;
}
#endif /* USE_REAL2INT */

/* ------------------------------------------------ write_to_audio_device --- */
static int
write_to_audio_device(AudioDevice *device)
{
	return device->sendFrames(out_buffer, RTBUFSAMPS) == RTBUFSAMPS ? 0 : -1;
}

/* -------------------------------------------------------------- limiter --- */
/* If clipping occurs within the out_buffer, pin the clipped samples to
   extrema and then print some information about them: how many samples
   clipped, the absolute value of the sample farthest out of range, the
   time range of this buffer in the output, etc. Also, maintain peak
   amplitude stats for later reporting and writing to the comment of the
   output file (if any).

   Note: This modifies the floating-point output buffers *in place*.
*/
static void
limiter()
{
   int      i, j, numclipped;
   BUFTYPE  clipmax, orig_samp;
   BufPtr   buf;

   numclipped = 0;
   clipmax = (BUFTYPE) 0;

   for (i = 0; i < NCHANS; i++) {
      buf = out_buffer[i];
      for (j = 0; j < RTBUFSAMPS; j++) {
         orig_samp = buf[j];
         if (orig_samp < -32768.0) {
            if (orig_samp < -clipmax)
               clipmax = -orig_samp;
            buf[j] = -32768.0;
            numclipped++;
         }
         else if (orig_samp > 32767.0) {
            if (orig_samp > clipmax)
               clipmax = orig_samp;
            buf[j] = 32767.0;
            numclipped++;
         }
         if (check_peaks) {
            BUFTYPE abs_samp = (BUFTYPE) fabs((double) buf[j]);
            if (abs_samp > peaks[i]) {
               peaks[i] = abs_samp;
               peaklocs[i] = bufStartSamp + (j / NCHANS);  /* frame count */
            }
         }
      }
   }

   if (numclipped && report_clipping) {
      float loc1 = (float) bufStartSamp / SR;
      float loc2 = loc1 + ((float) RTBUFSAMPS / SR);

      /* We start with a newline if we're also printing the buffer dots. */
      fprintf(stderr,
              "%s  CLIPPING: %4d samps, max: %g, time range: %f - %f\n",
              (printing_dots? "\n" : ""), numclipped, clipmax, loc1, loc2);
   }

//printf("limiter: bufStartSamp = %ld\n", bufStartSamp);
}

/* ---------------------------------------------------------- rtsendzeros --- */
/* Send a buffer of zeros to the audio output device, and to the output sound
   file if <also_write_to_file> is true.

   NOTE NOTE NOTE: This clears all the global out_buffers!
*/
void
rtsendzeros(AudioDevice *device, int also_write_to_file)
{
   int   err;

   clear_output_buffers();

   if (play_audio) {
      int i, j, nsamps, nbufs;
      err = write_to_audio_device(device);
      if (err)
         fprintf(stderr, "rtsendzeros: Error: %s\n", device->getLastError());
   }

   if (also_write_to_file && rtfileit) {
      err = rtwritesamps(device);
      if (err)
         fprintf(stderr, "rtsendzeros: bad write to output sound file\n");
   }
}

/* ---------------------------------------------------------- rtsendsamps --- */
/* Called by the scheduler to write the output buffer to the audio device
   and/or a sound file.   All format conversion happens inside the AudioDevice.
   For all supported cases, we limit the floating point buffer to +-32768 to
   avoid overflow during conversion to other formats.
*/

#define NPLAY_BUFSIZE 8192

void
rtsendsamps(AudioDevice *device)
{
   int   err;

   /* If we're writing to a file, and not playing, print a dot to show
      we've output one buffer.
   */
   if (!play_audio && rtfileit) {
      printing_dots = 1;
      printf(".");    /* no '\n' */
   }

   /* Write float file *before* doing any limiting. */
   if (is_float_format && rtfileit) {
	  // FOR NOW, IF WE ARE BOTH PLAYING AND WRITING, DO IT WITH SEPARATE
	  // AudioDevice INSTANCES.
	  if (play_audio)
      	err = rtwritesamps(globalOutputFileDevice);
	  else
	  	err = rtwritesamps(device);
      if (err)
         fprintf(stderr, "rtsendsamps: bad write to output sound file\n");
      if (!play_audio)
         return;        /* without limiting */
   }

#ifdef LIMIT_OBUF_FOR_AUDIO_DEV
   limiter();    /* Limit output buffer data to +-32767.0 */
#endif

   if (play_audio) {
      err = write_to_audio_device(device);
      if (err)
         fprintf(stderr, "rtsendsamps: Error: %s\n", device->getLastError());
   }

   if (!is_float_format && rtfileit) {
#ifndef LIMIT_OBUF_FOR_AUDIO_DEV
      limiter();                        /* Do limiting just for output file. */
#endif
	  // FOR NOW, IF WE ARE BOTH PLAYING AND WRITING, DO IT WITH SEPARATE
	  // AudioDevice INSTANCES.
	  if (play_audio)
      	err = rtwritesamps(globalOutputFileDevice);
	  else
	  	err = rtwritesamps(device);
      if (err)
         fprintf(stderr, "rtsendsamps: bad write to output sound file\n");
   }

#ifdef NETPLAYER
   if (netplay == 1) {        /* send to the socket connection */
      int   i, j, result;
      short outbufshort[NPLAY_BUFSIZE];

      for (i = 0; i < NPLAY_BUFSIZE / 2; i++) {
         for (j = 0; j < 2; j++)
            outbufshort[i * 2 + j] = out_buffer[j][i];
      }
      result = write(netplaysock, &outbufshort, RTBUFSAMPS * NCHANS
                                                            * sizeof(short));
      if (result < 0) {
         fprintf(stderr, "bad write to network sound socket\n");
         return;
      }
   }
#endif
}

/* -------------------------------------------------------- rtreportstats --- */
void
rtreportstats(AudioDevice *device)
{
	int    n;
	double dbref = dbamp(32768.0);
	AudioFileDevice *fileDevice = dynamic_cast<AudioFileDevice *>(device);

   if (report_clipping && check_peaks) {
      printf("\nPeak amplitudes of output:\n");
      for (n = 0; n < NCHANS; n++) {
	  	if (is_float_format && rtfileit)
			peaks[n] = fileDevice->getPeak(n, &peaklocs[n]);
         double peak_dbfs = dbamp(peaks[n]) - dbref;
         printf("  channel %d: %12.6f (%6.2f dBFS) at frame %ld (%g seconds)\n",
                n, peaks[n], peak_dbfs, peaklocs[n], (float) peaklocs[n] / SR);
      }
   }
}

<commit_msg>Removed old netplayer code.<commit_after>/* RTcmix  - Copyright (C) 2000  The RTcmix Development Team
   See ``AUTHORS'' for a list of contributors. See ``LICENSE'' for
   the license to this software and for a DISCLAIMER OF ALL WARRANTIES.
*/
/* rev'd for v2.3, JGG, 20-Feb-00 */
/* major revision for 3.7 (converted to C++ source), DS, April-04
   (see deleted .c file for earlier history)
*/

#include <stdio.h>
#include <unistd.h>
#include <math.h>       /* for fabs */
#include <assert.h>
#include <globals.h>
#include <prototypes.h>
#include <buffers.h>
#include <ugens.h>
#include <sndlibsupport.h>
#include "../rtstuff/rtdefs.h"
#include "AudioDevice.h"
#include "AudioFileDevice.h"

/* #define DUMP_AUDIO_TO_RAW_FILE */
/* #define USE_REAL2INT */

extern AudioDevice *globalOutputFileDevice;	// audio_devices.cpp

static int printing_dots = 0;

/* local prototypes */
static int write_to_audio_device(AudioDevice *);
static void limiter(void);

#ifdef DUMP_AUDIO_TO_RAW_FILE
/* ----------------------------------------------- dump_audio_to_raw_file --- */
/* For debugging audio device writes.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
static void
dump_audio_to_raw_file(OBufPtr dest, int nbytes)
{
   int         result;
   static int  fd = -1;

   if (fd == -1) {
      fd = open("dumpaudio.raw", O_RDWR | O_CREAT | O_TRUNC, 0666);
      assert(fd > 0);
      fprintf(stderr, "Dumping audio to \"dumpaudio.raw\".\n");
   }
   result = write(fd, dest, nbytes);
   assert(result != -1);
}
#endif /* DUMP_AUDIO_TO_RAW_FILE */

#ifdef USE_REAL2INT
/* ------------------------------------------------------------- real2int --- */
/* This is supposed to be more efficient than casting from float to int,
   but I didn't notice much difference.  This came from info off of the
   music-dsp list.  -JGG
*/
typedef long int32;
static double _double2fixmagic = 68719476736.0 * 1.5;
static int32 _shiftamt = 16;
#if MUS_LITTLE_ENDIAN    /* processor is little-endian */
   #define iman_   0
#else
   #define iman_   1
#endif

static INLINE int
real2int(float val)
{
   double x = (double) val;
   x = x + _double2fixmagic;
   return ((int32 *) &x)[iman_] >> _shiftamt;
}
#endif /* USE_REAL2INT */

/* ------------------------------------------------ write_to_audio_device --- */
static int
write_to_audio_device(AudioDevice *device)
{
	return device->sendFrames(out_buffer, RTBUFSAMPS) == RTBUFSAMPS ? 0 : -1;
}

/* -------------------------------------------------------------- limiter --- */
/* If clipping occurs within the out_buffer, pin the clipped samples to
   extrema and then print some information about them: how many samples
   clipped, the absolute value of the sample farthest out of range, the
   time range of this buffer in the output, etc. Also, maintain peak
   amplitude stats for later reporting and writing to the comment of the
   output file (if any).

   Note: This modifies the floating-point output buffers *in place*.
*/
static void
limiter()
{
   int      i, j, numclipped;
   BUFTYPE  clipmax, orig_samp;
   BufPtr   buf;

   numclipped = 0;
   clipmax = (BUFTYPE) 0;

   for (i = 0; i < NCHANS; i++) {
      buf = out_buffer[i];
      for (j = 0; j < RTBUFSAMPS; j++) {
         orig_samp = buf[j];
         if (orig_samp < -32768.0) {
            if (orig_samp < -clipmax)
               clipmax = -orig_samp;
            buf[j] = -32768.0;
            numclipped++;
         }
         else if (orig_samp > 32767.0) {
            if (orig_samp > clipmax)
               clipmax = orig_samp;
            buf[j] = 32767.0;
            numclipped++;
         }
         if (check_peaks) {
            BUFTYPE abs_samp = (BUFTYPE) fabs((double) buf[j]);
            if (abs_samp > peaks[i]) {
               peaks[i] = abs_samp;
               peaklocs[i] = bufStartSamp + (j / NCHANS);  /* frame count */
            }
         }
      }
   }

   if (numclipped && report_clipping) {
      float loc1 = (float) bufStartSamp / SR;
      float loc2 = loc1 + ((float) RTBUFSAMPS / SR);

      /* We start with a newline if we're also printing the buffer dots. */
      fprintf(stderr,
              "%s  CLIPPING: %4d samps, max: %g, time range: %f - %f\n",
              (printing_dots? "\n" : ""), numclipped, clipmax, loc1, loc2);
   }

//printf("limiter: bufStartSamp = %ld\n", bufStartSamp);
}

/* ---------------------------------------------------------- rtsendzeros --- */
/* Send a buffer of zeros to the audio output device, and to the output sound
   file if <also_write_to_file> is true.

   NOTE NOTE NOTE: This clears all the global out_buffers!
*/
void
rtsendzeros(AudioDevice *device, int also_write_to_file)
{
   int   err;

   clear_output_buffers();

   if (play_audio) {
      int i, j, nsamps, nbufs;
      err = write_to_audio_device(device);
      if (err)
         fprintf(stderr, "rtsendzeros: Error: %s\n", device->getLastError());
   }

   if (also_write_to_file && rtfileit) {
      err = rtwritesamps(device);
      if (err)
         fprintf(stderr, "rtsendzeros: bad write to output sound file\n");
   }
}

/* ---------------------------------------------------------- rtsendsamps --- */
/* Called by the scheduler to write the output buffer to the audio device
   and/or a sound file.   All format conversion happens inside the AudioDevice.
   For all supported cases, we limit the floating point buffer to +-32768 to
   avoid overflow during conversion to other formats.
*/

void
rtsendsamps(AudioDevice *device)
{
   int   err;

   /* If we're writing to a file, and not playing, print a dot to show
      we've output one buffer.
   */
   if (!play_audio && rtfileit) {
      printing_dots = 1;
      printf(".");    /* no '\n' */
   }

   /* Write float file *before* doing any limiting. */
   if (is_float_format && rtfileit) {
	  // FOR NOW, IF WE ARE BOTH PLAYING AND WRITING, DO IT WITH SEPARATE
	  // AudioDevice INSTANCES.
	  if (play_audio)
      	err = rtwritesamps(globalOutputFileDevice);
	  else
	  	err = rtwritesamps(device);
      if (err)
         fprintf(stderr, "rtsendsamps: bad write to output sound file\n");
      if (!play_audio)
         return;        /* without limiting */
   }

#ifdef LIMIT_OBUF_FOR_AUDIO_DEV
   limiter();    /* Limit output buffer data to +-32767.0 */
#endif

   if (play_audio) {
      err = write_to_audio_device(device);
      if (err)
         fprintf(stderr, "rtsendsamps: Error: %s\n", device->getLastError());
   }

   if (!is_float_format && rtfileit) {
#ifndef LIMIT_OBUF_FOR_AUDIO_DEV
      limiter();                        /* Do limiting just for output file. */
#endif
	  // FOR NOW, IF WE ARE BOTH PLAYING AND WRITING, DO IT WITH SEPARATE
	  // AudioDevice INSTANCES.
	  if (play_audio)
      	err = rtwritesamps(globalOutputFileDevice);
	  else
	  	err = rtwritesamps(device);
      if (err)
         fprintf(stderr, "rtsendsamps: bad write to output sound file\n");
   }
}

/* -------------------------------------------------------- rtreportstats --- */
void
rtreportstats(AudioDevice *device)
{
	int    n;
	double dbref = dbamp(32768.0);
	AudioFileDevice *fileDevice = dynamic_cast<AudioFileDevice *>(device);

   if (report_clipping && check_peaks) {
      printf("\nPeak amplitudes of output:\n");
      for (n = 0; n < NCHANS; n++) {
	  	if (is_float_format && rtfileit)
			peaks[n] = fileDevice->getPeak(n, &peaklocs[n]);
         double peak_dbfs = dbamp(peaks[n]) - dbref;
         printf("  channel %d: %12.6f (%6.2f dBFS) at frame %ld (%g seconds)\n",
                n, peaks[n], peak_dbfs, peaklocs[n], (float) peaklocs[n] / SR);
      }
   }
}

<|endoftext|>


<commit_before>// This file is part of MorphoDiTa <http://github.com/ufal/morphodita/>.
//
// Copyright 2015 Institute of Formal and Applied Linguistics, Faculty of
// Mathematics and Physics, Charles University in Prague, Czech Republic.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include "derivator/derivation_formatter.h"
#include "morpho/morpho.h"
#include "tagger/tagger.h"
#include "tagset_converter/tagset_converter.h"
#include "utils/getpara.h"
#include "utils/iostreams.h"
#include "utils/options.h"
#include "utils/parse_int.h"
#include "utils/process_args.h"
#include "utils/xml_encoded.h"
#include "version/version.h"

using namespace ufal::morphodita;

static void analyze_vertical(istream& is, ostream& os, const morpho& dictionary, bool use_guesser, tokenizer& tokenizer, const tagset_converter& tagset_converter, const derivation_formatter& derivation);
static void analyze_xml(istream& is, ostream& os, const morpho& dictionary, bool use_guesser, tokenizer& tokenizer, const tagset_converter& tagset_converter, const derivation_formatter& derivation);

int main(int argc, char* argv[]) {
  iostreams_init();

  options::map options;
  if (!options::parse({{"input",options::value{"untokenized", "vertical"}},
                       {"convert_tagset",options::value::any},
                       {"derivation",options::value{"none", "root", "path", "tree"}},
                       {"output",options::value{"vertical","xml"}},
                       {"from_tagger",options::value::none},
                       {"version", options::value::none},
                       {"help", options::value::none}}, argc, argv, options) ||
      options.count("help") ||
      (argc < 3 && !options.count("version")))
    runtime_failure("Usage: " << argv[0] << "[options] dict_file use_guesser [file[:output_file]]...\n"
                    "Options: --input=untokenized|vertical\n"
                    "         --convert_tagset=pdt_to_conll2009|strip_lemma_comment|strip_lemma_id\n"
                    "         --derivation=none|root|path|tree\n"
                    "         --output=vertical|xml\n"
                    "         --from_tagger\n"
                    "         --version\n"
                    "         --help");
  if (options.count("version"))
    return cout << version::version_and_copyright() << endl, 0;

  unique_ptr<morpho> morpho;
  unique_ptr<tagger> tagger;
  if (options.count("from_tagger")) {
    cerr << "Loading dictionary from tagger: ";
    tagger.reset(tagger::load(argv[1]));
    if (!tagger) runtime_failure("Cannot load tagger from file '" << argv[1] << "'!");
  } else {
    cerr << "Loading dictionary: ";
    morpho.reset(morpho::load(argv[1]));
    if (!morpho) runtime_failure("Cannot load dictionary from file '" << argv[1] << "'!");
  }
  cerr << "done" << endl;
  auto& dictionary = options.count("from_tagger") ? *tagger->get_morpho() : *morpho;

  bool use_guesser = parse_int(argv[2], "use_guesser");

  unique_ptr<tokenizer> tokenizer(options.count("input") && options["input"] == "vertical" ? tokenizer::new_vertical_tokenizer() : dictionary.new_tokenizer());
  if (!tokenizer) runtime_failure("No tokenizer is defined for the supplied model!");

  unique_ptr<tagset_converter> tagset_converter;
  if (options.count("convert_tagset")) {
    tagset_converter.reset(new_tagset_converter(options["convert_tagset"], dictionary));
    if (!tagset_converter) runtime_failure("Unknown tag set converter '" << options["convert_tagset"] << "'!");
  } else {
    tagset_converter.reset(tagset_converter::new_identity_converter());
    if (!tagset_converter) runtime_failure("Cannot create identity tag set converter!");
  }

  unique_ptr<derivation_formatter> derivation;
  if (options.count("derivation")) {
    if (!options["derivation"].empty() && options["derivation"] != "none" && !morpho->get_derivator())
      runtime_failure("No derivator is defined for the supplied model!");
    derivation.reset(derivation_formatter::new_derivation_formatter(options["derivation"], morpho->get_derivator()));
    if (!derivation) runtime_failure("Cannot create derivation formatter '" << options["derivation"] << "' for the supplied model!");
  } else {
    derivation.reset(derivation_formatter::new_none_derivation_formatter());
    if (!derivation) runtime_failure("Cannot create trivial derivation formatter for the supplied model!");
  }

  if (options.count("output") && options["output"] == "vertical")
    process_args(3, argc, argv, analyze_vertical, dictionary, use_guesser, *tokenizer, *tagset_converter, *derivation);
  else
    process_args(3, argc, argv, analyze_xml, dictionary, use_guesser, *tokenizer, *tagset_converter, *derivation);

  return 0;
}

void analyze_vertical(istream& is, ostream& os, const morpho& dictionary, bool use_guesser, tokenizer& tokenizer, const tagset_converter& tagset_converter, const derivation_formatter& derivation) {
  string para;
  vector<string_piece> forms;
  vector<tagged_lemma> lemmas;

  while (getpara(is, para)) {
    tokenizer.set_text(para);
    while (tokenizer.next_sentence(&forms, nullptr)) {
      for (auto&& form : forms) {
        dictionary.analyze(form, use_guesser ? morpho::GUESSER : morpho::NO_GUESSER, lemmas);
        derivation.format_tagged_lemmas(lemmas, &tagset_converter);

        os << form;
        for (auto&& lemma : lemmas) {
          os << '\t' << lemma.lemma << '\t' << lemma.tag;
        }
        os << '\n';
      }
      os << endl;
    }
  }
}

void analyze_xml(istream& is, ostream& os, const morpho& dictionary, bool use_guesser, tokenizer& tokenizer, const tagset_converter& tagset_converter, const derivation_formatter& derivation) {
  string para;
  vector<string_piece> forms;
  vector<tagged_lemma> lemmas;

  while (getpara(is, para)) {
    // Tokenize and analyze
    tokenizer.set_text(para);
    const char* unprinted = para.c_str();
    while (tokenizer.next_sentence(&forms, nullptr))
      for (unsigned i = 0; i < forms.size(); i++) {
        dictionary.analyze(forms[i], use_guesser ? morpho::GUESSER : morpho::NO_GUESSER, lemmas);
        derivation.format_tagged_lemmas(lemmas, &tagset_converter);

        os << xml_encoded(string_piece(unprinted, forms[i].str - unprinted));
        if (!i) os << "<sentence>";
        os << "<token>";
        for (auto&& lemma : lemmas) {
          os << "<analysis lemma=\"" << xml_encoded(lemma.lemma, true) << "\" tag=\"" << xml_encoded(lemma.tag, true) << "\"/>";
        }
        os << xml_encoded(forms[i]) << "</token>";
        if (i + 1 == forms.size()) os << "</sentence>";
        unprinted = forms[i].str + forms[i].len;
      }

    os << xml_encoded(string_piece(unprinted, para.c_str() + para.size() - unprinted)) << flush;
  }
}
<commit_msg>Do not segfault on derivation plus from_tagger combination.<commit_after>// This file is part of MorphoDiTa <http://github.com/ufal/morphodita/>.
//
// Copyright 2015 Institute of Formal and Applied Linguistics, Faculty of
// Mathematics and Physics, Charles University in Prague, Czech Republic.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include "derivator/derivation_formatter.h"
#include "morpho/morpho.h"
#include "tagger/tagger.h"
#include "tagset_converter/tagset_converter.h"
#include "utils/getpara.h"
#include "utils/iostreams.h"
#include "utils/options.h"
#include "utils/parse_int.h"
#include "utils/process_args.h"
#include "utils/xml_encoded.h"
#include "version/version.h"

using namespace ufal::morphodita;

static void analyze_vertical(istream& is, ostream& os, const morpho& dictionary, bool use_guesser, tokenizer& tokenizer, const tagset_converter& tagset_converter, const derivation_formatter& derivation);
static void analyze_xml(istream& is, ostream& os, const morpho& dictionary, bool use_guesser, tokenizer& tokenizer, const tagset_converter& tagset_converter, const derivation_formatter& derivation);

int main(int argc, char* argv[]) {
  iostreams_init();

  options::map options;
  if (!options::parse({{"input",options::value{"untokenized", "vertical"}},
                       {"convert_tagset",options::value::any},
                       {"derivation",options::value{"none", "root", "path", "tree"}},
                       {"output",options::value{"vertical","xml"}},
                       {"from_tagger",options::value::none},
                       {"version", options::value::none},
                       {"help", options::value::none}}, argc, argv, options) ||
      options.count("help") ||
      (argc < 3 && !options.count("version")))
    runtime_failure("Usage: " << argv[0] << "[options] dict_file use_guesser [file[:output_file]]...\n"
                    "Options: --input=untokenized|vertical\n"
                    "         --convert_tagset=pdt_to_conll2009|strip_lemma_comment|strip_lemma_id\n"
                    "         --derivation=none|root|path|tree\n"
                    "         --output=vertical|xml\n"
                    "         --from_tagger\n"
                    "         --version\n"
                    "         --help");
  if (options.count("version"))
    return cout << version::version_and_copyright() << endl, 0;

  unique_ptr<morpho> morpho;
  unique_ptr<tagger> tagger;
  if (options.count("from_tagger")) {
    cerr << "Loading dictionary from tagger: ";
    tagger.reset(tagger::load(argv[1]));
    if (!tagger) runtime_failure("Cannot load tagger from file '" << argv[1] << "'!");
  } else {
    cerr << "Loading dictionary: ";
    morpho.reset(morpho::load(argv[1]));
    if (!morpho) runtime_failure("Cannot load dictionary from file '" << argv[1] << "'!");
  }
  cerr << "done" << endl;
  auto& dictionary = options.count("from_tagger") ? *tagger->get_morpho() : *morpho;

  bool use_guesser = parse_int(argv[2], "use_guesser");

  unique_ptr<tokenizer> tokenizer(options.count("input") && options["input"] == "vertical" ? tokenizer::new_vertical_tokenizer() : dictionary.new_tokenizer());
  if (!tokenizer) runtime_failure("No tokenizer is defined for the supplied model!");

  unique_ptr<tagset_converter> tagset_converter;
  if (options.count("convert_tagset")) {
    tagset_converter.reset(new_tagset_converter(options["convert_tagset"], dictionary));
    if (!tagset_converter) runtime_failure("Unknown tag set converter '" << options["convert_tagset"] << "'!");
  } else {
    tagset_converter.reset(tagset_converter::new_identity_converter());
    if (!tagset_converter) runtime_failure("Cannot create identity tag set converter!");
  }

  unique_ptr<derivation_formatter> derivation;
  if (options.count("derivation")) {
    if (!options["derivation"].empty() && options["derivation"] != "none" && !dictionary.get_derivator())
      runtime_failure("No derivator is defined for the supplied model!");
    derivation.reset(derivation_formatter::new_derivation_formatter(options["derivation"], dictionary.get_derivator()));
    if (!derivation) runtime_failure("Cannot create derivation formatter '" << options["derivation"] << "' for the supplied model!");
  } else {
    derivation.reset(derivation_formatter::new_none_derivation_formatter());
    if (!derivation) runtime_failure("Cannot create trivial derivation formatter for the supplied model!");
  }

  if (options.count("output") && options["output"] == "vertical")
    process_args(3, argc, argv, analyze_vertical, dictionary, use_guesser, *tokenizer, *tagset_converter, *derivation);
  else
    process_args(3, argc, argv, analyze_xml, dictionary, use_guesser, *tokenizer, *tagset_converter, *derivation);

  return 0;
}

void analyze_vertical(istream& is, ostream& os, const morpho& dictionary, bool use_guesser, tokenizer& tokenizer, const tagset_converter& tagset_converter, const derivation_formatter& derivation) {
  string para;
  vector<string_piece> forms;
  vector<tagged_lemma> lemmas;

  while (getpara(is, para)) {
    tokenizer.set_text(para);
    while (tokenizer.next_sentence(&forms, nullptr)) {
      for (auto&& form : forms) {
        dictionary.analyze(form, use_guesser ? morpho::GUESSER : morpho::NO_GUESSER, lemmas);
        derivation.format_tagged_lemmas(lemmas, &tagset_converter);

        os << form;
        for (auto&& lemma : lemmas) {
          os << '\t' << lemma.lemma << '\t' << lemma.tag;
        }
        os << '\n';
      }
      os << endl;
    }
  }
}

void analyze_xml(istream& is, ostream& os, const morpho& dictionary, bool use_guesser, tokenizer& tokenizer, const tagset_converter& tagset_converter, const derivation_formatter& derivation) {
  string para;
  vector<string_piece> forms;
  vector<tagged_lemma> lemmas;

  while (getpara(is, para)) {
    // Tokenize and analyze
    tokenizer.set_text(para);
    const char* unprinted = para.c_str();
    while (tokenizer.next_sentence(&forms, nullptr))
      for (unsigned i = 0; i < forms.size(); i++) {
        dictionary.analyze(forms[i], use_guesser ? morpho::GUESSER : morpho::NO_GUESSER, lemmas);
        derivation.format_tagged_lemmas(lemmas, &tagset_converter);

        os << xml_encoded(string_piece(unprinted, forms[i].str - unprinted));
        if (!i) os << "<sentence>";
        os << "<token>";
        for (auto&& lemma : lemmas) {
          os << "<analysis lemma=\"" << xml_encoded(lemma.lemma, true) << "\" tag=\"" << xml_encoded(lemma.tag, true) << "\"/>";
        }
        os << xml_encoded(forms[i]) << "</token>";
        if (i + 1 == forms.size()) os << "</sentence>";
        unprinted = forms[i].str + forms[i].len;
      }

    os << xml_encoded(string_piece(unprinted, para.c_str() + para.size() - unprinted)) << flush;
  }
}
<|endoftext|>


<commit_before>/**
 ** \file scheduler/scheduler.cc
 ** \brief Implementation of scheduler::Scheduler.
 */

//#define ENABLE_DEBUG_TRACES

#include <cassert>
#include <cstdlib>

#include <libport/compiler.hh>
#include <libport/containers.hh>
#include <libport/foreach.hh>

#include "kernel/userver.hh"

#include "object/urbi-exception.hh"

#include "scheduler/scheduler.hh"
#include "scheduler/job.hh"

namespace scheduler
{

  // This function is required to start a new job using the libcoroutine.
  // Its only purpose is to create the context and start the execution
  // of the new job.
  static void
  run_job (void* job)
  {
    static_cast<Job*>(job)->run();
  }

  void
  Scheduler::add_job (Job* job)
  {
    assert (job);
    assert (!libport::has (jobs_, job));
    jobs_.push_back (job);
    jobs_to_start_ = true;
  }

  libport::utime_t
  Scheduler::work ()
  {
#ifdef ENABLE_DEBUG_TRACES
    static int cycle = 0;
#endif
    ECHO ("======================================================== cycle "
	  << ++cycle);

    libport::utime_t deadline = execute_round (false);

    // If some jobs need to be stopped, do it as soon as possible.
    deadline = std::min (deadline, check_for_stopped_tags (deadline));

#ifdef ENABLE_DEBUG_TRACES
    if (deadline)
      ECHO ("Scheduler asking to be woken up in "
	    << (deadline - ::urbiserver->getTime ()) / 1000000L << " seconds");
    else
      ECHO ("Scheduler asking to be woken up ASAP");
#endif

    return deadline;
  }

  libport::utime_t
  Scheduler::execute_round (bool blocked_only)
  {
    // Run all the jobs in the run queue once. If any job declares upon entry or
    // return that it is not side-effect free, we remember that for the next
    // cycle.
    pending_.clear ();
    std::swap (pending_, jobs_);

    // By default, wake us up after one hour and consider that we have no
    // new job to start. Also, run waiting jobs only if the previous round
    // may have add a side effect and reset this indication for the current
    // job.
    libport::utime_t deadline = ::urbiserver->getTime () +  3600000000LL;
    jobs_to_start_ = false;
    bool start_waiting = possible_side_effect_;
    possible_side_effect_ = false;

    ECHO (pending_.size() << " jobs in the queue for this round");
    foreach (Job* job, pending_)
    {
      // Kill a job if needed. See explanation in job.hh.
      to_kill_ = 0;

      assert (job);
      assert (!job->terminated ());

      // Should the job be started?
      bool start = false;

      ECHO ("Considering " << *job << " in state " << state_name (job->state_get ()));

      switch (job->state_get ())
      {
      case to_start:
	// New job. Start its coroutine but do not start the job as it would be queued
	// twice otherwise. It will start doing real work at the next cycle, so set
	// deadline to 0. Note that we use "continue" here to avoid having the job
	// requeued because it hasn't been started by setting "start".
	ECHO ("Starting job " << *job);
	current_job_ = job;
	Coro_startCoro_ (self_, job->coro_get(), job, run_job);
	current_job_ = 0;
	ECHO ("Job " << *job << " has been started");
	assert (job->state_get () != to_start);
	deadline = 0;
	continue;
      case zombie:
	assert (false);
	break;
      case running:
	start = !blocked_only || job->blocked ();
	break;
      case sleeping:
	{
	  libport::utime_t job_deadline = job->deadline_get ();
	  if (job_deadline <= ::urbiserver->getTime ())
	    start = true;
	  else
	    deadline = std::min (deadline, job_deadline);
	}
	break;
      case waiting:
	// Since jobs keep their orders in the queue, start waiting jobs if
	// previous jobs in the run have had a possible side effect or if
	// the previous run may have had some. Without it, we may miss some
	// changes if the watching job is after the modifying job in the queue
	// and the watched condition gets true for only one cycle.
	start = start_waiting | possible_side_effect_;
	break;
      case joining:
	break;
      }

      if (start)
      {
	ECHO ("will resume job " << *job
	      << (job->side_effect_free_get() ? " (side-effect free)" : ""));
	possible_side_effect_ |= !job->side_effect_free_get ();
	assert (!current_job_);
	Coro_switchTo_ (self_, job->coro_get ());
	assert (!current_job_);
	possible_side_effect_ |= !job->side_effect_free_get ();
	ECHO ("back from job " << *job
	      << (job->side_effect_free_get() ? " (side-effect free)" : ""));
	switch (job->state_get ())
	{
	case running:
	  deadline = 0;
	  break;
	case sleeping:
	  deadline = std::min (deadline, job->deadline_get ());
	  break;
	default:
	  break;
	}
      }
      else
	jobs_.push_back (job);   // Job not started, keep it in queue
    }

    // Kill a job if needed. See explanation in job.hh.
    to_kill_ = 0;

    /// If during this cycle a new job has been created by an existing job,
    /// start it.
    if (jobs_to_start_)
      deadline = 0;

    return deadline;
  }

  libport::utime_t
  Scheduler::check_for_stopped_tags (libport::utime_t old_deadline)
  {
    bool blocked_job = false;

    // If we have had no stopped tag, return immediately.
    if (stopped_tags_.empty ())
      return old_deadline;

    // If some jobs have been blocked, mark them as running so that they will
    // handle the condition when they are resumed.
    foreach (Job* job, jobs_)
      if (job->blocked ())
      {
	job->state_set (running);
	blocked_job = true;
      }

    // Wake up blocked jobs.
    libport::utime_t deadline = execute_round (true);

    // Reset tags to their real blocked value and reset the list.
    foreach (tag_state_type t, stopped_tags_)
      t.first->set_blocked (t.second);
    stopped_tags_.clear ();

    return deadline;
  }

  void
  Scheduler::switch_back (Job* job)
  {
    // Switch back to the scheduler.
    assert (current_job_ == job);
    current_job_ = 0;
    Coro_switchTo_ (job->coro_get (), self_);
    // We regained control, we are again in the context of the job.
    assert (!current_job_);
    current_job_ = job;
    ECHO ("job " << *job << " resumed");
    // Check that we are not near exhausting the stack space.
    job->check_stack_space ();
    // Execute a deferred exception if any
    job->check_for_pending_exception ();
    // If we are in frozen state, let's requeue ourselves waiting
    // for something to change. And let's mark us side-effect
    // free during this time so that we won't cause other jobs
    // to be scheduled. Of course, we have to check for pending
    // exceptions each time we are woken up.
    if (job->frozen ())
    {
      bool side_effect_free_save = job->side_effect_free_get ();
      do {
	job->side_effect_free_set (true);
	job->yield_until_things_changed ();
	job->side_effect_free_set (side_effect_free_save);
	// Execute a deferred exception if any
	job->check_for_pending_exception ();
      } while (job->frozen ());
    }
  }

  void
  Scheduler::resume_scheduler (Job* job)
  {
    // If the job has not terminated and is side-effect free, then we
    // assume it will not take a long time as we are probably evaluating
    // a condition. In order to reduce the number of cycles spent to evaluate
    // the condition, continue until it asks to be suspended in another
    // way or until it is no longer side-effect free.

    if (job->state_get () == running && job->side_effect_free_get ())
      return;

    if (!job->terminated ())
      jobs_.push_back (job);

    ECHO (*job << " has " << (job->terminated () ? "" : "not ") << "terminated\n\t"
	  << "state: " << state_name (job->state_get ()));
    switch_back (job);
  }

  void
  Scheduler::killall_jobs ()
  {
    ECHO ("killing all jobs!");

    foreach (Job* job, jobs_)
      kill_job (job);

    foreach (Job* job, pending_)
      kill_job (job);
  }

  void
  Scheduler::unschedule_job (Job* job)
  {
    assert (job);
    assert (job != current_job_);

    ECHO ("unscheduling job " << *job);

    // Remove the job from the queue.
    jobs_.remove (job);

    // Remove it from live queues as well if the job is destroyed.
    pending_.remove (job);
  }

  void
  Scheduler::kill_job (Job* job)
  {
    KillException ke;
    job->async_throw (ke);
  }

  void Scheduler::signal_stop (rTag t)
  {
    bool previous_state = t->own_blocked ();
    t->set_blocked (true);
    stopped_tags_.push_back (std::make_pair(t, previous_state));
  }

} // namespace scheduler
<commit_msg>Do not resume a terminated job in any case<commit_after>/**
 ** \file scheduler/scheduler.cc
 ** \brief Implementation of scheduler::Scheduler.
 */

//#define ENABLE_DEBUG_TRACES

#include <cassert>
#include <cstdlib>

#include <libport/compiler.hh>
#include <libport/containers.hh>
#include <libport/foreach.hh>

#include "kernel/userver.hh"

#include "object/urbi-exception.hh"

#include "scheduler/scheduler.hh"
#include "scheduler/job.hh"

namespace scheduler
{

  // This function is required to start a new job using the libcoroutine.
  // Its only purpose is to create the context and start the execution
  // of the new job.
  static void
  run_job (void* job)
  {
    static_cast<Job*>(job)->run();
  }

  void
  Scheduler::add_job (Job* job)
  {
    assert (job);
    assert (!libport::has (jobs_, job));
    jobs_.push_back (job);
    jobs_to_start_ = true;
  }

  libport::utime_t
  Scheduler::work ()
  {
#ifdef ENABLE_DEBUG_TRACES
    static int cycle = 0;
#endif
    ECHO ("======================================================== cycle "
	  << ++cycle);

    libport::utime_t deadline = execute_round (false);

    // If some jobs need to be stopped, do it as soon as possible.
    deadline = std::min (deadline, check_for_stopped_tags (deadline));

#ifdef ENABLE_DEBUG_TRACES
    if (deadline)
      ECHO ("Scheduler asking to be woken up in "
	    << (deadline - ::urbiserver->getTime ()) / 1000000L << " seconds");
    else
      ECHO ("Scheduler asking to be woken up ASAP");
#endif

    return deadline;
  }

  libport::utime_t
  Scheduler::execute_round (bool blocked_only)
  {
    // Run all the jobs in the run queue once. If any job declares upon entry or
    // return that it is not side-effect free, we remember that for the next
    // cycle.
    pending_.clear ();
    std::swap (pending_, jobs_);

    // By default, wake us up after one hour and consider that we have no
    // new job to start. Also, run waiting jobs only if the previous round
    // may have add a side effect and reset this indication for the current
    // job.
    libport::utime_t deadline = ::urbiserver->getTime () +  3600000000LL;
    jobs_to_start_ = false;
    bool start_waiting = possible_side_effect_;
    possible_side_effect_ = false;

    ECHO (pending_.size() << " jobs in the queue for this round");
    foreach (Job* job, pending_)
    {
      // Kill a job if needed. See explanation in job.hh.
      to_kill_ = 0;

      assert (job);
      assert (!job->terminated ());

      // Should the job be started?
      bool start = false;

      ECHO ("Considering " << *job << " in state " << state_name (job->state_get ()));

      switch (job->state_get ())
      {
      case to_start:
	// New job. Start its coroutine but do not start the job as it would be queued
	// twice otherwise. It will start doing real work at the next cycle, so set
	// deadline to 0. Note that we use "continue" here to avoid having the job
	// requeued because it hasn't been started by setting "start".
	ECHO ("Starting job " << *job);
	current_job_ = job;
	Coro_startCoro_ (self_, job->coro_get(), job, run_job);
	current_job_ = 0;
	ECHO ("Job " << *job << " has been started");
	assert (job->state_get () != to_start);
	deadline = 0;
	continue;
      case zombie:
	assert (false);
	break;
      case running:
	start = !blocked_only || job->blocked ();
	break;
      case sleeping:
	{
	  libport::utime_t job_deadline = job->deadline_get ();
	  if (job_deadline <= ::urbiserver->getTime ())
	    start = true;
	  else
	    deadline = std::min (deadline, job_deadline);
	}
	break;
      case waiting:
	// Since jobs keep their orders in the queue, start waiting jobs if
	// previous jobs in the run have had a possible side effect or if
	// the previous run may have had some. Without it, we may miss some
	// changes if the watching job is after the modifying job in the queue
	// and the watched condition gets true for only one cycle.
	start = start_waiting | possible_side_effect_;
	break;
      case joining:
	break;
      }

      if (start)
      {
	ECHO ("will resume job " << *job
	      << (job->side_effect_free_get() ? " (side-effect free)" : ""));
	possible_side_effect_ |= !job->side_effect_free_get ();
	assert (!current_job_);
	Coro_switchTo_ (self_, job->coro_get ());
	assert (!current_job_);
	possible_side_effect_ |= !job->side_effect_free_get ();
	ECHO ("back from job " << *job
	      << (job->side_effect_free_get() ? " (side-effect free)" : ""));
	switch (job->state_get ())
	{
	case running:
	  deadline = 0;
	  break;
	case sleeping:
	  deadline = std::min (deadline, job->deadline_get ());
	  break;
	default:
	  break;
	}
      }
      else
	jobs_.push_back (job);   // Job not started, keep it in queue
    }

    // Kill a job if needed. See explanation in job.hh.
    to_kill_ = 0;

    /// If during this cycle a new job has been created by an existing job,
    /// start it.
    if (jobs_to_start_)
      deadline = 0;

    return deadline;
  }

  libport::utime_t
  Scheduler::check_for_stopped_tags (libport::utime_t old_deadline)
  {
    bool blocked_job = false;

    // If we have had no stopped tag, return immediately.
    if (stopped_tags_.empty ())
      return old_deadline;

    // If some jobs have been blocked, mark them as running so that they will
    // handle the condition when they are resumed.
    foreach (Job* job, jobs_)
      if (job->blocked ())
      {
	job->state_set (running);
	blocked_job = true;
      }

    // Wake up blocked jobs.
    libport::utime_t deadline = execute_round (true);

    // Reset tags to their real blocked value and reset the list.
    foreach (tag_state_type t, stopped_tags_)
      t.first->set_blocked (t.second);
    stopped_tags_.clear ();

    return deadline;
  }

  void
  Scheduler::switch_back (Job* job)
  {
    // Switch back to the scheduler.
    assert (current_job_ == job);
    current_job_ = 0;
    Coro_switchTo_ (job->coro_get (), self_);
    // We regained control, we are again in the context of the job.
    assert (!current_job_);
    current_job_ = job;
    ECHO ("job " << *job << " resumed");
    // Check that we are not near exhausting the stack space.
    job->check_stack_space ();
    // Execute a deferred exception if any
    job->check_for_pending_exception ();
    // If we are in frozen state, let's requeue ourselves waiting
    // for something to change. And let's mark us side-effect
    // free during this time so that we won't cause other jobs
    // to be scheduled. Of course, we have to check for pending
    // exceptions each time we are woken up.
    if (job->frozen ())
    {
      bool side_effect_free_save = job->side_effect_free_get ();
      do {
	job->side_effect_free_set (true);
	job->yield_until_things_changed ();
	job->side_effect_free_set (side_effect_free_save);
	// Execute a deferred exception if any
	job->check_for_pending_exception ();
      } while (job->frozen ());
    }
  }

  void
  Scheduler::resume_scheduler (Job* job)
  {
    // If the job has not terminated and is side-effect free, then we
    // assume it will not take a long time as we are probably evaluating
    // a condition. In order to reduce the number of cycles spent to evaluate
    // the condition, continue until it asks to be suspended in another
    // way or until it is no longer side-effect free.

    if (!job->terminated ())
      {
	if (job->state_get () == running && job->side_effect_free_get ())
	  return;
	else
	  jobs_.push_back (job);
      }

    ECHO (*job << " has " << (job->terminated () ? "" : "not ") << "terminated\n\t"
	  << "state: " << state_name (job->state_get ()));
    switch_back (job);
  }

  void
  Scheduler::killall_jobs ()
  {
    ECHO ("killing all jobs!");

    foreach (Job* job, jobs_)
      kill_job (job);

    foreach (Job* job, pending_)
      kill_job (job);
  }

  void
  Scheduler::unschedule_job (Job* job)
  {
    assert (job);
    assert (job != current_job_);

    ECHO ("unscheduling job " << *job);

    // Remove the job from the queue.
    jobs_.remove (job);

    // Remove it from live queues as well if the job is destroyed.
    pending_.remove (job);
  }

  void
  Scheduler::kill_job (Job* job)
  {
    KillException ke;
    job->async_throw (ke);
  }

  void Scheduler::signal_stop (rTag t)
  {
    bool previous_state = t->own_blocked ();
    t->set_blocked (true);
    stopped_tags_.push_back (std::make_pair(t, previous_state));
  }

} // namespace scheduler
<|endoftext|>


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

#include "server_base.h"

#include "server.h"

BaseServer::BaseServer(std::shared_ptr<XapiandServer>&& server_, ev::loop_ref *loop_, int sock_)
	: Worker(std::move(server_), loop_),
	  io(*loop)
{
	io.set<BaseServer, &BaseServer::io_accept>(this);
	io.start(sock_, ev::READ);
}

BaseServer::~BaseServer()
{
	destroy();
}

void
BaseServer::destroy()
{
	io.stop();
}
<commit_msg>Print when Base Server object is destroyed<commit_after>/*
 * Copyright (C) 2015 deipi.com LLC and contributors. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include "server_base.h"

#include "server.h"

BaseServer::BaseServer(std::shared_ptr<XapiandServer>&& server_, ev::loop_ref *loop_, int sock_)
	: Worker(std::move(server_), loop_),
	  io(*loop)
{
	io.set<BaseServer, &BaseServer::io_accept>(this);
	io.start(sock_, ev::READ);
}

BaseServer::~BaseServer()
{
	destroy();
}

void
BaseServer::destroy()
{
	io.stop();

	LOG_OBJ(this, "DESTROYED BASE SERVER!\n");
}
<|endoftext|>


<commit_before>#include "luaruntime/lualibs/Curl.hpp"
#include "luaruntime/LuaRuntime.hpp"
#include "GameHooks.hpp"

LuaCurl g_LuaCurl;

size_t curl_read_cb(char *ptr, size_t size, size_t nmemb, void *userdata) {
  CurlReq *req = (CurlReq *)userdata;
  size_t newDataSize = size * nmemb;
  if(req->dataSize + newDataSize > req->bufferSize) {
    size_t newSize = req->dataSize + newDataSize + 512;
    char *buffer = (char *)malloc(newSize);
    memcpy(buffer, req->data, req->dataSize);

    free(req->data);
    req->data = buffer;
    req->bufferSize = newSize;
  }

  memcpy(&req->data[req->dataSize], ptr, newDataSize);
  req->dataSize = req->dataSize + newDataSize;
  return newDataSize;
}

void LuaCurl::OnPluginStart() {
  curl_global_init(CURL_GLOBAL_DEFAULT);
  g_GameHooks.AddFrameHook([this](bool simulating) -> void {
    this->OnGameFrame(simulating);
  });
}

void LuaCurl::OnGameFrame(bool simulating) {
  if(mCurlHandle) {
    curl_multi_perform(mCurlHandle, &this->running_handles);

    int msgs = 0;
    CURLMsg *m = curl_multi_info_read(mCurlHandle, &msgs);
    if(m && (m->msg == CURLMSG_DONE)) {
      CURL *transfer = m->easy_handle;
      curl_multi_remove_handle(mCurlHandle, transfer);
      CurlReq *req;

      curl_easy_getinfo(transfer, CURLINFO_PRIVATE, (char **)&req);
      req->data[req->dataSize] = 0;
      lua_rawgeti(g_LuaRuntime.L, LUA_REGISTRYINDEX, req->callbackRef);
      lua_pushstring(g_LuaRuntime.L, req->data);
      lua_docall(g_LuaRuntime.L, 1, 0);

      free(req->data);
      free(req);

      curl_easy_cleanup(transfer);
    }

    if(running_handles == 0) {
      curl_multi_cleanup(mCurlHandle);
    }
  }
}

void LuaCurl::Get(const char *url, int callbackRef) {
  if(!mCurlHandle) {
    mCurlHandle = curl_multi_init();
  }

  CurlReq *req = (CurlReq *)malloc(sizeof(CurlReq));
  req->callbackRef = callbackRef;
  req->dataSize = 0;
  req->bufferSize = 512;
  req->data = (char *)malloc(512);

  CURL *transfer = curl_easy_init();
  curl_easy_setopt(transfer, CURLOPT_URL, url);
  curl_easy_setopt(transfer, CURLOPT_WRITEFUNCTION, curl_read_cb);
  curl_easy_setopt(transfer, CURLOPT_WRITEDATA, req);
  curl_easy_setopt(transfer, CURLOPT_PRIVATE, req);
  curl_multi_add_handle(mCurlHandle, transfer);
}

int lua_Curl_Get(lua_State *L) {
  lua_pushvalue(L, 2);
  int ref = luaL_ref(L, LUA_REGISTRYINDEX);
  g_LuaCurl.Get(lua_tostring(L, 1), ref);

  return 0;
}

void lua_register_Curl(lua_State *L) {
  lua_getglobal(L, "SourceLua");

  lua_newtable(L);
  lua_pushcfunction(L, lua_Curl_Get);
  lua_setfield(L, -2, "Get");

  lua_setfield(L, -2, "Curl");
  lua_remove(L, -1);
}
<commit_msg>Support getting binary files<commit_after>#include "luaruntime/lualibs/Curl.hpp"
#include "luaruntime/LuaRuntime.hpp"
#include "GameHooks.hpp"

LuaCurl g_LuaCurl;

size_t curl_read_cb(char *ptr, size_t size, size_t nmemb, void *userdata) {
  CurlReq *req = (CurlReq *)userdata;
  size_t newDataSize = size * nmemb;
  if(req->dataSize + newDataSize > req->bufferSize) {
    size_t newSize = req->dataSize + newDataSize + (CURL_MAX_WRITE_SIZE / 2);
    char *buffer = (char *)malloc(newSize);
    memcpy(buffer, req->data, req->dataSize);

    free(req->data);
    req->data = buffer;
    req->bufferSize = newSize;
  }

  memcpy(&req->data[req->dataSize], ptr, newDataSize);
  req->dataSize = req->dataSize + newDataSize;
  return newDataSize;
}

void LuaCurl::OnPluginStart() {
  curl_global_init(CURL_GLOBAL_DEFAULT);
  g_GameHooks.AddFrameHook([this](bool simulating) -> void {
    this->OnGameFrame(simulating);
  });
}

void LuaCurl::OnGameFrame(bool simulating) {
  if(mCurlHandle) {
    curl_multi_perform(mCurlHandle, &this->running_handles);

    int msgs = 0;
    CURLMsg *m = curl_multi_info_read(mCurlHandle, &msgs);
    if(m && (m->msg == CURLMSG_DONE)) {
      CURL *transfer = m->easy_handle;
      curl_multi_remove_handle(mCurlHandle, transfer);
      CurlReq *req;

      curl_easy_getinfo(transfer, CURLINFO_PRIVATE, (char **)&req);
      lua_rawgeti(g_LuaRuntime.L, LUA_REGISTRYINDEX, req->callbackRef);
      lua_pushlstring(g_LuaRuntime.L, req->data, req->dataSize);
      lua_docall(g_LuaRuntime.L, 1, 0);

      free(req->data);
      free(req);

      curl_easy_cleanup(transfer);
    }

    if(running_handles == 0) {
      curl_multi_cleanup(mCurlHandle);
    }
  }
}

void LuaCurl::Get(const char *url, int callbackRef) {
  if(!mCurlHandle) {
    mCurlHandle = curl_multi_init();
  }

  CurlReq *req = (CurlReq *)malloc(sizeof(CurlReq));
  req->callbackRef = callbackRef;
  req->dataSize = 0;
  req->bufferSize = CURL_MAX_WRITE_SIZE;
  req->data = (char *)malloc(CURL_MAX_WRITE_SIZE);

  CURL *transfer = curl_easy_init();
  curl_easy_setopt(transfer, CURLOPT_URL, url);
  curl_easy_setopt(transfer, CURLOPT_WRITEFUNCTION, curl_read_cb);
  curl_easy_setopt(transfer, CURLOPT_WRITEDATA, req);
  curl_easy_setopt(transfer, CURLOPT_PRIVATE, req);
  curl_multi_add_handle(mCurlHandle, transfer);
}

int lua_Curl_Get(lua_State *L) {
  lua_pushvalue(L, 2);
  int ref = luaL_ref(L, LUA_REGISTRYINDEX);
  g_LuaCurl.Get(lua_tostring(L, 1), ref);

  return 0;
}

void lua_register_Curl(lua_State *L) {
  lua_getglobal(L, "SourceLua");

  lua_newtable(L);
  lua_pushcfunction(L, lua_Curl_Get);
  lua_setfield(L, -2, "Get");

  lua_setfield(L, -2, "Curl");
  lua_remove(L, -1);
}
<|endoftext|>


<commit_before>#ifndef STR_IS_SUBSTRING_HPP_
#define STR_IS_SUBSTRING_HPP_

#include <string>

namespace StrSubstring {

// https://leetcode.com/problems/implement-strstr/description/

// Implement strStr(). Return the index of the first occurrence of needle in
// haystack, or -1 if needle is not part of haystack.

// Example 1:
// Input: haystack = "hello", needle = "ll"
// Output: 2

// Example 2:
// Input: haystack = "aaaaa", needle = "bba"
// Output: -1

class Solution {
public:
    int strStr(std::string haystack, std::string needle) {
        // needle should be less or equal in size with haystack to be
        // a substring
        if (haystack.size() < needle.size())
        {
            return -1;
        }
        else if (haystack.size() == needle.size())
        {
            // quick check if size of strings are equal
            return (haystack == needle) ? 0 : -1;
        }
        else if (needle.empty()) {
            // empty string is substring for every string
            return 0;
        }

        size_t needlePos = 0;
        int ssStartPos = -1;
        for(size_t i = 0; i < haystack.size(); ++i)
        {
            if (haystack[i] == needle[needlePos])
            {
                // remember position where substring start
                if (needlePos == 0) {
                    ssStartPos = static_cast<int>(i);
                }

                ++needlePos;

                // check if we compare all symbols in needle string
                if (needlePos == needle.size())
                {
                    return ssStartPos;
                }
            }
            else
            {
                // if previous susbstring match was false, return back to
                // position where we thought it would start
                if (needlePos > 0) {
                    i = static_cast<size_t>(ssStartPos);
                    needlePos = 0;
                }
            }
        }

        return -1;
    }

    // Assume you have a method isSubstring which checks if one word is a
    // substring of another. Given two strings, s1 and s2, write code to check
    // if s2 is a rotation of s1 using only one call to isSubstring (e.g.,
    // "waterbottLe" is a rotation of "erbottLewat").
    bool isStrRotationOfOtherStr(const std::string& original,
            const std::string& str)
    {
        if (original.empty() || original.size() != str.size())
        {
            return false;
        }

        std::string sourceStr = original + original;
        return strStr(sourceStr, str) > 0;
    }
};

}

#endif /* STR_IS_SUBSTRING_HPP_ */
<commit_msg>Simplifu strstr() algorithm<commit_after>#ifndef STR_IS_SUBSTRING_HPP_
#define STR_IS_SUBSTRING_HPP_

#include <string>

namespace StrSubstring {

// https://leetcode.com/problems/implement-strstr/description/

// Implement strStr(). Return the index of the first occurrence of needle in
// haystack, or -1 if needle is not part of haystack.

// Example 1:
// Input: haystack = "hello", needle = "ll"
// Output: 2

// Example 2:
// Input: haystack = "aaaaa", needle = "bba"
// Output: -1

class Solution {
public:
    int strStr(std::string haystack, std::string needle) {
        // needle should be less or equal in size with haystack to be
        // a substring
        if (haystack.size() < needle.size())
        {
            return -1;
        }
        else if (haystack.size() == needle.size())
        {
            // quick check if size of strings are equal
            return (haystack == needle) ? 0 : -1;
        }
        else if (needle.empty()) {
            // empty string is substring for every string
            return 0;
        }

        for(size_t i = 0; i < haystack.size(); ++i) {
            if (haystack[i] == needle[0]) {
                size_t startPos = i;
                size_t hi = i + 1, ni = 1;
                while ( hi < haystack.size() &&
                        ni < needle.size() &&
                        haystack[hi] == needle[ni]) {
                    ++hi;
                    ++ni;
                }

                if (ni == needle.size()) {
                    return static_cast<int>(startPos);
                }
            }
        }

        return -1;
    }

    // Assume you have a method isSubstring which checks if one word is a
    // substring of another. Given two strings, s1 and s2, write code to check
    // if s2 is a rotation of s1 using only one call to isSubstring (e.g.,
    // "waterbottLe" is a rotation of "erbottLewat").
    bool isStrRotationOfOtherStr(const std::string& original,
            const std::string& str)
    {
        if (original.empty() || original.size() != str.size())
        {
            return false;
        }

        std::string sourceStr = original + original;
        return strStr(sourceStr, str) > 0;
    }
};

}

#endif /* STR_IS_SUBSTRING_HPP_ */
<|endoftext|>


<commit_before>
#include "filesystem.h"
#include "utils.h"

#include <iostream>

#include <stdio.h>
#include <string.h>

FileSystem::FileSystem(const std::string &partfname):part_filename(partfname){
}

FileSystem::~FileSystem(){
}

void FileSystem::debug(){

  std::cout << "Debugging the filesystem class..." << std::endl;
  //this->init();
  this->load();
  this->makedir("/home");

}

int FileSystem::init(){

  FILE* fd = fopen(part_filename.c_str(), "wb+");

  // step 1: writeout 1024 0xbb's
  unsigned char bootblock[1024];
  memset(bootblock, 0xbb, sizeof(bootblock));
  fwrite(bootblock, 1, sizeof(bootblock), fd);

  // step 2: writeout the fat "header"
  unsigned char fatheader[20] = {0xff, 0xfd,
                                 0xff, 0xfe,
                                 0xff, 0xfe,
                                 0xff, 0xfe,
                                 0xff, 0xfe,
                                 0xff, 0xfe,
                                 0xff, 0xfe,
                                 0xff, 0xfe,
                                 0xff, 0xfe,
                                 0xff, 0xff};

  fwrite(fatheader, 1, sizeof(fatheader), fd);

  // step 3: writeout blank rest
  unsigned char blankrest[4193260];
  memset(blankrest, 0x00, sizeof(blankrest));
  fwrite(blankrest, 1, sizeof(blankrest), fd);

  fclose(fd);

  return RET_OK;

}

int FileSystem::load(){

  for (unsigned int i=0; i<8; i++){

    if (!readblock(&(fat[i*1024]), (i+1) * 1024)){
      // TODO: Handle errors when file manipulation goes wrong
    }

  }

  return RET_OK;

}

int FileSystem::makedir(const std::string &path){

  std::vector<std::string> ret = tokenize_path(path);

  for (unsigned int i=0; i<ret.size(); i++){

    int const rid = find_free_rootdir();
    if (rid == -1){
      return RET_ROOTDIR_FULL;
    }

    if (i==0){ // root dir
    } else {
    }

  }

  return RET_OK;
}

int FileSystem::listdir(const std::string &path, std::vector<std::string> &result){
  (void)path;
  (void)result;
  return -1;
}

int FileSystem::createfile(const std::string &path){
  (void)path;
  return -1;
}

int FileSystem::unlink(const std::string &path){
  (void)path;
  return -1;
}

int FileSystem::write(const std::string &path, const std::string &content){
  (void)path;
  (void)content;
  return -1;
}

int FileSystem::append(const std::string &path, const std::string &content){
  (void)path;
  (void)content;
  return -1;
}

int FileSystem::read(const std::string &path, std::string &content){
  (void)path;
  (void)content;
  return -1;
}

int FileSystem::find_free_rootdir() const {

  for (unsigned int i=0; i<sizeof(rootdir); i++){
    if (rootdir[i].filename[0] == 0x00){
      return i;
    }
  }
  return -1;
}

bool FileSystem::readblock(void *into, const unsigned int offset) const {

  FILE* fd = fopen(part_filename.c_str(), "rb+");
  fseek(fd, offset, SEEK_SET);
  fread(into, 1, 1024, fd);
  fclose(fd);

  return true;
}

bool FileSystem::writeblock(void *buf, const unsigned int offset){

  FILE* fd = fopen(part_filename.c_str(), "wb+");
  fseek(fd, offset, SEEK_SET);
  fwrite(buf, 1, 1024, fd);
  fclose(fd);

  return true;

}

<commit_msg>started using the writeblock too<commit_after>
#include "filesystem.h"
#include "utils.h"

#include <iostream>

#include <stdio.h>
#include <string.h>

FileSystem::FileSystem(const std::string &partfname):part_filename(partfname){
}

FileSystem::~FileSystem(){
}

void FileSystem::debug(){

  std::cout << "Debugging the filesystem class..." << std::endl;
  this->init();
  //this->load();
  this->makedir("/home");

}

int FileSystem::init(){

  // mvtodo: remove fat.part before "formatting"

  // step 1: writeout 1024 0xbb's
  unsigned char bootblock[1024];
  memset(bootblock, 0xbb, sizeof(bootblock));
  if (!writeblock(bootblock, 0)){
    // mvtodo: tratar erro aqui
  }

  // step 2: writeout the fat "header"
  unsigned char fatheader[8192] = {0xff, 0xfd,
                                   0xff, 0xfe,
                                   0xff, 0xfe,
                                   0xff, 0xfe,
                                   0xff, 0xfe,
                                   0xff, 0xfe,
                                   0xff, 0xfe,
                                   0xff, 0xfe,
                                   0xff, 0xfe,
                                   0xff, 0xff};

  memset(fatheader+20, 0x00, sizeof(fatheader)-20);
  for (unsigned int i=0; i<8; i++){
    if (!writeblock(&(fatheader[i*1024]), (i+1)*1024)){
      // mvtodo: tratar erro aqui
    }
  }

  // step 3: writeout blank rest
  unsigned char blankrest[4185088];
  memset(blankrest, 0x00, sizeof(blankrest));
  for (unsigned int i=0; i<4087; i++){
    // mvtodo: howcome (i+1)*1024 passed the python script test??
    if (!writeblock(blankrest, (i+9)*1024)){
      // mvtodo: tratar erro aqui
    }
  }

  return RET_OK;

}

int FileSystem::load(){

  for (unsigned int i=0; i<8; i++){

    if (!readblock(&(fat[i*1024]), (i+1) * 1024)){
      // TODO: Handle errors when file manipulation goes wrong
    }

  }

  return RET_OK;

}

int FileSystem::makedir(const std::string &path){

  std::vector<std::string> ret = tokenize_path(path);

  for (unsigned int i=0; i<ret.size(); i++){

    int const rid = find_free_rootdir();
    if (rid == -1){
      return RET_ROOTDIR_FULL;
    }

    if (i==0){ // root dir
    } else {
    }

  }

  return RET_OK;
}

int FileSystem::listdir(const std::string &path, std::vector<std::string> &result){
  (void)path;
  (void)result;
  return -1;
}

int FileSystem::createfile(const std::string &path){
  (void)path;
  return -1;
}

int FileSystem::unlink(const std::string &path){
  (void)path;
  return -1;
}

int FileSystem::write(const std::string &path, const std::string &content){
  (void)path;
  (void)content;
  return -1;
}

int FileSystem::append(const std::string &path, const std::string &content){
  (void)path;
  (void)content;
  return -1;
}

int FileSystem::read(const std::string &path, std::string &content){
  (void)path;
  (void)content;
  return -1;
}

int FileSystem::find_free_rootdir() const {

  for (unsigned int i=0; i<sizeof(rootdir); i++){
    if (rootdir[i].filename[0] == 0x00){
      return i;
    }
  }
  return -1;
}

bool FileSystem::readblock(void *into, const unsigned int offset) const {

  FILE* fd = fopen(part_filename.c_str(), "rb");
  // mvtodo: treat errors
  fseek(fd, offset, SEEK_SET);
  fread(into, 1, 1024, fd);
  fclose(fd);

  return true;
}

bool FileSystem::writeblock(void *buf, const unsigned int offset){

  FILE* fd = fopen(part_filename.c_str(), "ab");
  // mvtodo: treat errors
  fseek(fd, offset, SEEK_SET);
  fwrite(buf, 1, 1024, fd);
  fclose(fd);

  return true;

}

<|endoftext|>


<commit_before>#ifndef MJOLNIR_OMP_SYSTEM_HPP
#define MJOLNIR_OMP_SYSTEM_HPP
#include <mjolnir/util/aligned_allocator.hpp>
#include <mjolnir/core/System.hpp>
#include <mjolnir/omp/OpenMPSimulatorTraits.hpp>

namespace mjolnir
{

template<typename realT, template<typename, typename> class boundaryT>
class System<OpenMPSimulatorTraits<realT, boundaryT>>
{
  public:
    using traits_type     = OpenMPSimulatorTraits<realT, boundaryT>;
    using real_type       = typename traits_type::real_type;
    using coordinate_type = typename traits_type::coordinate_type;
    using boundary_type   = typename traits_type::boundary_type;
    using topology_type   = Topology;
    using attribute_type  = std::map<std::string, real_type>;

    using particle_type            = Particle<real_type, coordinate_type>;
    using static_string_type       = typename particle_type::static_string_type;
    using particle_view_type       = ParticleView<real_type, coordinate_type>;
    using particle_const_view_type = ParticleConstView<real_type, coordinate_type>;

    using real_container_type          = std::vector<real_type>;
    using coordinate_container_type    = std::vector<coordinate_type>;
    using static_string_container_type = std::vector<static_string_type>;

    static constexpr std::size_t cache_alignment = 64;

    template<typename T>
    using cache_aligned_allocator = aligned_allocator<T, cache_alignment>;

  public:

    System(const std::size_t num_particles, const boundary_type& bound)
        : boundary_(bound), topology_(num_particles),
          attributes_(), num_particles_(num_particles),
          masses_   (num_particles), rmasses_   (num_particles),
          positions_(num_particles), velocities_(num_particles),
          forces_master_(num_particles),
          forces_threads_(omp_get_max_threads(),
              coordinate_container_type(num_particles),
              cache_aligned_allocator<coordinate_container_type>{}),
          names_    (num_particles), groups_    (num_particles)
    {}
    ~System() = default;

    coordinate_type adjust_direction(coordinate_type dr) const noexcept
    {return boundary_.adjust_direction(dr);}
    coordinate_type  adjust_position(coordinate_type dr) const noexcept
    {return boundary_.adjust_position(dr);}

    std::size_t size() const noexcept {return num_particles_;}

    particle_view_type operator[](std::size_t i) noexcept
    {
        return particle_view_type{
            masses_[i],    rmasses_[i],
            positions_[i], velocities_[i], forces_master_[i],
            names_[i],     groups_[i]
        };
    }
    particle_const_view_type operator[](std::size_t i) const noexcept
    {
        return particle_const_view_type{
            masses_[i],    rmasses_[i],
            positions_[i], velocities_[i], forces_master_[i],
            names_[i],     groups_[i]
        };
    }
    particle_view_type at(std::size_t i)
    {
        return particle_view_type{
            masses_.at(i),    rmasses_.at(i),
            positions_.at(i), velocities_.at(i), forces_master_.at(i),
            names_.at(i),     groups_.at(i)
        };
    }
    particle_const_view_type at(std::size_t i) const
    {
        return particle_const_view_type{
            masses_.at(i),    rmasses_.at(i),
            positions_.at(i), velocities_.at(i), forces_master_.at(i),
            names_.at(i),     groups_.at(i)
        };
    }

    real_type  mass (std::size_t i) const noexcept {return masses_[i];}
    real_type& mass (std::size_t i)       noexcept {return masses_[i];}
    real_type  rmass(std::size_t i) const noexcept {return rmasses_[i];}
    real_type& rmass(std::size_t i)       noexcept {return rmasses_[i];}

    coordinate_type const& position(std::size_t i) const noexcept {return positions_[i];}
    coordinate_type&       position(std::size_t i)       noexcept {return positions_[i];}
    coordinate_type const& velocity(std::size_t i) const noexcept {return velocities_[i];}
    coordinate_type&       velocity(std::size_t i)       noexcept {return velocities_[i];}
    coordinate_type const& force   (std::size_t i) const noexcept {return forces_master_[i];}
    coordinate_type&       force   (std::size_t i)       noexcept {return forces_master_[i];}

    coordinate_type const&
    force_thread(std::size_t thread_num, std::size_t particle_id) const noexcept
    {
        return forces_threads_[thread_num][particle_id];
    }
    coordinate_type&
    force_thread(std::size_t thread_num, std::size_t particle_id)       noexcept
    {
        return forces_threads_[thread_num][particle_id];
    }

    void merge_forces() noexcept
    {
#pragma omp parallel for
        for(std::size_t i=0; i<this->size(); ++i)
        {
            // sumup all the thread-local forces into the main force.
            // XXX: keep the values in the main force! Some pre-omp interactions
            //      may write its force in it. Clearing main force is a task
            //      for Integrators.
            for(std::size_t thread_id=0, max_threads=omp_get_max_threads();
                    thread_id < max_threads; ++thread_id)
            {
                this->force(i) += this->force_thread(thread_id, i);
                this->force_thread(thread_id, i) =
                    math::make_coordinate<coordinate_type>(0, 0, 0);
            }
        }
        return;
    }

    static_string_type const& name(std::size_t i) const noexcept {return names_[i];}
    static_string_type&       name(std::size_t i)       noexcept {return names_[i];}
    static_string_type const& group(std::size_t i) const noexcept {return groups_[i];}
    static_string_type&       group(std::size_t i)       noexcept {return groups_[i];}

    boundary_type&       boundary()       noexcept {return boundary_;}
    boundary_type const& boundary() const noexcept {return boundary_;}
    topology_type&       topology()       noexcept {return topology_;}
    topology_type const& topology() const noexcept {return topology_;}

    // system attributes like `reference temperature`, `ionic strength`, ...
    // assuming it will not be called so often.
    real_type  attribute(const std::string& key) const {return attributes_.at(key);}
    real_type& attribute(const std::string& key)       {return attributes_[key];}
    bool   has_attribute(const std::string& key) const {return attributes_.count(key) == 1;}

  private:

    boundary_type  boundary_;
    topology_type  topology_;
    attribute_type attributes_;

    std::size_t                  num_particles_;
    real_container_type          masses_;
    real_container_type          rmasses_; // r for reciprocal
    coordinate_container_type    positions_;
    coordinate_container_type    velocities_;
    coordinate_container_type    forces_master_;
    std::vector<coordinate_container_type,
                cache_aligned_allocator<coordinate_container_type>
        > forces_threads_;
    static_string_container_type names_;
    static_string_container_type groups_;
};

} // mjolnir
#endif// MJOLNIR_SYSTEM_HPP
<commit_msg>fix: update OpenMP implementaion of System<commit_after>#ifndef MJOLNIR_OMP_SYSTEM_HPP
#define MJOLNIR_OMP_SYSTEM_HPP
#include <mjolnir/util/aligned_allocator.hpp>
#include <mjolnir/core/System.hpp>
#include <mjolnir/omp/OpenMPSimulatorTraits.hpp>

namespace mjolnir
{

template<typename realT, template<typename, typename> class boundaryT>
class System<OpenMPSimulatorTraits<realT, boundaryT>>
{
  public:
    using traits_type     = OpenMPSimulatorTraits<realT, boundaryT>;
    using real_type       = typename traits_type::real_type;
    using coordinate_type = typename traits_type::coordinate_type;
    using boundary_type   = typename traits_type::boundary_type;
    using topology_type   = Topology;
    using attribute_type  = std::map<std::string, real_type>;

    using string_type              = std::string;
    using particle_type            = Particle<real_type, coordinate_type>;
    using particle_view_type       = ParticleView<real_type, coordinate_type>;
    using particle_const_view_type = ParticleConstView<real_type, coordinate_type>;

    using real_container_type          = std::vector<real_type>;
    using coordinate_container_type    = std::vector<coordinate_type>;

    static constexpr std::size_t cache_alignment = 64;

    template<typename T>
    using cache_aligned_allocator = aligned_allocator<T, cache_alignment>;

  public:

    System(const std::size_t num_particles, const boundary_type& bound)
        : boundary_(bound), topology_(num_particles),
          attributes_(), num_particles_(num_particles),
          masses_   (num_particles), rmasses_   (num_particles),
          positions_(num_particles), velocities_(num_particles),
          forces_master_(num_particles),
          forces_threads_(omp_get_max_threads(),
              coordinate_container_type(num_particles),
              cache_aligned_allocator<coordinate_container_type>{})
    {}
    ~System() = default;

    coordinate_type adjust_direction(coordinate_type dr) const noexcept
    {return boundary_.adjust_direction(dr);}
    coordinate_type  adjust_position(coordinate_type dr) const noexcept
    {return boundary_.adjust_position(dr);}

    std::size_t size() const noexcept {return num_particles_;}

    particle_view_type operator[](std::size_t i) noexcept
    {
        return particle_view_type{
            masses_[i],    rmasses_[i],
            positions_[i], velocities_[i], forces_master_[i],
            this->topology_.name_of (i, std::nothrow),
            this->topology_.group_of(i, std::nothrow)
        };
    }
    particle_const_view_type operator[](std::size_t i) const noexcept
    {
        return particle_const_view_type{
            masses_[i],    rmasses_[i],
            positions_[i], velocities_[i], forces_master_[i],
            this->topology_.name_of (i, std::nothrow),
            this->topology_.group_of(i, std::nothrow)
        };
    }
    particle_view_type at(std::size_t i)
    {
        return particle_view_type{
            masses_.at(i),    rmasses_.at(i),
            positions_.at(i), velocities_.at(i), forces_master_.at(i),
            topology_.name_of(i), topology_.group_of(i)
        };
    }
    particle_const_view_type at(std::size_t i) const
    {
        return particle_const_view_type{
            masses_.at(i),    rmasses_.at(i),
            positions_.at(i), velocities_.at(i), forces_master_.at(i),
            topology_.name_of(i), topology_.group_of(i)
        };
    }

    real_type  mass (std::size_t i) const noexcept {return masses_[i];}
    real_type& mass (std::size_t i)       noexcept {return masses_[i];}
    real_type  rmass(std::size_t i) const noexcept {return rmasses_[i];}
    real_type& rmass(std::size_t i)       noexcept {return rmasses_[i];}

    coordinate_type const& position(std::size_t i) const noexcept {return positions_[i];}
    coordinate_type&       position(std::size_t i)       noexcept {return positions_[i];}
    coordinate_type const& velocity(std::size_t i) const noexcept {return velocities_[i];}
    coordinate_type&       velocity(std::size_t i)       noexcept {return velocities_[i];}
    coordinate_type const& force   (std::size_t i) const noexcept {return forces_master_[i];}
    coordinate_type&       force   (std::size_t i)       noexcept {return forces_master_[i];}

    coordinate_type const&
    force_thread(std::size_t thread_num, std::size_t particle_id) const noexcept
    {
        return forces_threads_[thread_num][particle_id];
    }
    coordinate_type&
    force_thread(std::size_t thread_num, std::size_t particle_id)       noexcept
    {
        return forces_threads_[thread_num][particle_id];
    }

    void merge_forces() noexcept
    {
#pragma omp parallel for
        for(std::size_t i=0; i<this->size(); ++i)
        {
            // sumup all the thread-local forces into the main force.
            // XXX: keep the values in the main force! Some pre-omp interactions
            //      may write its force in it. Clearing main force is a task
            //      for Integrators.
            for(std::size_t thread_id=0, max_threads=omp_get_max_threads();
                    thread_id < max_threads; ++thread_id)
            {
                this->force(i) += this->force_thread(thread_id, i);
                this->force_thread(thread_id, i) =
                    math::make_coordinate<coordinate_type>(0, 0, 0);
            }
        }
        return;
    }

    string_type const& name (std::size_t i) const noexcept {return topology_.name_of(i, std::nothrow);}
    string_type&       name (std::size_t i)       noexcept {return topology_.name_of(i, std::nothrow);}
    string_type const& group(std::size_t i) const noexcept {return topology_.group_of(i, std::nothrow);}
    string_type&       group(std::size_t i)       noexcept {return topology_.group_of(i, std::nothrow);}

    boundary_type&       boundary()       noexcept {return boundary_;}
    boundary_type const& boundary() const noexcept {return boundary_;}
    topology_type&       topology()       noexcept {return topology_;}
    topology_type const& topology() const noexcept {return topology_;}

    // system attributes like `reference temperature`, `ionic strength`, ...
    // assuming it will not be called so often.
    real_type  attribute(const std::string& key) const {return attributes_.at(key);}
    real_type& attribute(const std::string& key)       {return attributes_[key];}
    bool   has_attribute(const std::string& key) const {return attributes_.count(key) == 1;}

  private:

    boundary_type  boundary_;
    topology_type  topology_;
    attribute_type attributes_;

    std::size_t                  num_particles_;
    real_container_type          masses_;
    real_container_type          rmasses_; // r for reciprocal
    coordinate_container_type    positions_;
    coordinate_container_type    velocities_;
    coordinate_container_type    forces_master_;
    // thread-local forces
    std::vector<coordinate_container_type,
                cache_aligned_allocator<coordinate_container_type>
        > forces_threads_;

    // names and groups are in Topology class
};

} // mjolnir
#endif// MJOLNIR_SYSTEM_HPP
<|endoftext|>