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

#include "interactive.h"

namespace ledger {

void interactive_t::verify_arguments() const
{
  value_t::sequence_t::const_iterator i;

  const char *	  p	    = spec.c_str();
  const char *	  label	    = "unknown";
  bool		  wrong_arg = false;
  bool		  dont_skip = false;
  bool		  optional  = *p == '&';
  bool		  exit_loop = *p == '*';
  std::size_t	  offset    = 1;
  bool		  is_seq    = args.value().is_sequence();
  const value_t * next_arg  = NULL;
  string	  vlabel;

  if (is_seq) {
    i = args.begin();
    if (i != args.end())
      next_arg = &(*i);
  }
  else if (! args.value().is_null()) {
    next_arg = &args.value();
  }

  for (; ! wrong_arg && ! exit_loop && *p && next_arg; p++) {
    switch (*p) {
    case 'a':
      label = "an amount";
      wrong_arg = (! next_arg->is_long() &&
		   ! next_arg->is_amount() &&
		   ! next_arg->is_balance());
      break;
    case 'b':
      label = "a boolean";
      wrong_arg = false;	// booleans are converted
      break;
    case 'd':
      label = "a date";
      wrong_arg = ! next_arg->is_date();
      break;
    case 'i':
    case 'l':
      label = "an integer";
      if (next_arg->is_long() ||
	  (next_arg->is_amount() &&
	   ! next_arg->as_amount().has_commodity())) {
	wrong_arg = false;
      }
      else if (next_arg->is_string()) {
	wrong_arg = false;
	for (const char * q = next_arg->as_string().c_str(); *q; q++) {
	  if (! std::isdigit(*q) && *q != '-') {
	    wrong_arg = true;
	    break;
	  }
	}
      }
      else {
	wrong_arg = true;
      }
      break;
    case 'm':
      label = "a regex";
      wrong_arg = ! next_arg->is_mask();
      break;
    case 's':
      label = "a string";
      wrong_arg = ! next_arg->is_string();
      break;
    case 't':
      label = "a date or time";
      wrong_arg = (! next_arg->is_date() &&
		   ! next_arg->is_datetime());
      break;
    case 'v':
      label = "any value";
      wrong_arg = false;
      break;
    case 'P':
      label = "a pointer";
      wrong_arg = ! next_arg->is_pointer();
      break;
    case 'S':
      label = "a sequence";
      wrong_arg = ! next_arg->is_sequence();
      break;
    case '&':
      optional = true;
      dont_skip = true;
      break;
    case '*':
      optional  = true;
      exit_loop = true;
      dont_skip = true;
      break;
    }

    if (wrong_arg)
      vlabel = next_arg->label();

    if (! dont_skip) {
      if (is_seq) {
	if (++i != args.end()) {
	  next_arg = &(*i);
	  offset++;
	} else {
	  next_arg = NULL;
	}
      } else {
	next_arg = NULL;
      }
    }
    dont_skip = false;
  }

  if (*p == '&' || *p == '*')
    optional = true;

  if (wrong_arg) {
    throw_(std::logic_error,
	   "Expected " << label << " for argument " << offset
	   << ", but received " << vlabel);
  }
  else if (! optional && ! next_arg) {
    throw_(std::logic_error, "Too few arguments to function");
  }
  else if (! *p && next_arg) {
    throw_(std::logic_error, "Too many arguments to function");
  }
}

string join_args(call_scope_t& args)
{
  std::ostringstream buf;
  bool first = true;

  for (std::size_t i = 0; i < args.size(); i++) {
    if (first) {
      buf << args[i];
      first = false;
    } else {
      buf << ' ' << args[i];
    }
  }

  return buf.str();
}

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

#include "interactive.h"

namespace ledger {

void interactive_t::verify_arguments() const
{
  value_t::sequence_t::const_iterator i;

  const char *	  p	    = spec.c_str();
  const char *	  label	    = "unknown";
  bool		  wrong_arg = false;
  bool		  dont_skip = false;
  bool		  optional  = *p == '&';
  bool		  exit_loop = *p == '*';
  std::size_t	  offset    = 1;
  bool		  is_seq    = args.value().is_sequence();
  const value_t * next_arg  = NULL;
  string	  vlabel;

  if (is_seq) {
    i = args.begin();
    if (i != args.end())
      next_arg = &(*i);
  }
  else if (! args.value().is_null()) {
    next_arg = &args.value();
  }

  for (; ! wrong_arg && ! exit_loop && *p && next_arg; p++) {
    switch (*p) {
    case 'a':
      label = "an amount";
      wrong_arg = (! next_arg->is_long() &&
		   ! next_arg->is_amount() &&
		   ! next_arg->is_balance());
      break;
    case 'b':
      label = "a boolean";
      wrong_arg = false;	// booleans are converted
      break;
    case 'd':
      label = "a date";
      wrong_arg = ! next_arg->is_date();
      break;
    case 'i':
    case 'l':
      label = "an integer";
      if (next_arg->is_long() ||
	  (next_arg->is_amount() &&
	   ! next_arg->as_amount().has_commodity())) {
	wrong_arg = false;
      }
      else if (next_arg->is_string()) {
	wrong_arg = false;
	for (const char * q = next_arg->as_string().c_str(); *q; q++) {
	  if (! std::isdigit(*q) && *q != '-') {
	    wrong_arg = true;
	    break;
	  }
	}
      }
      else {
	wrong_arg = true;
      }
      break;
    case 'm':
      label = "a regex";
      wrong_arg = ! next_arg->is_mask();
      break;
    case 's':
      label = "a string";
      wrong_arg = ! next_arg->is_string();
      break;
    case 't':
      label = "a date or time";
      wrong_arg = (! next_arg->is_date() &&
		   ! next_arg->is_datetime());
      break;
    case 'v':
      label = "any value";
      wrong_arg = false;
      break;
    case 'P':
      label = "a pointer";
      wrong_arg = ! next_arg->is_pointer();
      break;
    case 'S':
      label = "a sequence";
      wrong_arg = ! next_arg->is_sequence();
      break;
    case '&':
      optional = true;
      dont_skip = true;
      break;
    case '*':
      optional  = true;
      exit_loop = true;
      dont_skip = true;
      break;
    }

    if (wrong_arg)
      vlabel = next_arg->label();

    if (! dont_skip) {
      if (is_seq) {
	if (++i != args.end()) {
	  next_arg = &(*i);
	  offset++;
	} else {
	  next_arg = NULL;
	}
      } else {
	next_arg = NULL;
      }
    }
    dont_skip = false;
  }

  if (*p == '&' || *p == '*')
    optional = true;

  if (wrong_arg) {
    throw_(std::logic_error,
	   "Expected " << label << " for argument " << offset
	   << ", but received " << vlabel);
  }
  else if (*p && ! optional && ! next_arg) {
    throw_(std::logic_error, "Too few arguments to function");
  }
  else if (! *p && next_arg) {
    throw_(std::logic_error, "Too many arguments to function");
  }
}

string join_args(call_scope_t& args)
{
  std::ostringstream buf;
  bool first = true;

  for (std::size_t i = 0; i < args.size(); i++) {
    if (first) {
      buf << args[i];
      first = false;
    } else {
      buf << ' ' << args[i];
    }
  }

  return buf.str();
}

} // namespace ledger
<|endoftext|>


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

 Infomap software package for multi-level network clustering

 Copyright (c) 2013 Daniel Edler, Martin Rosvall

 For more information, see <http://www.mapequation.org>

 This file is part of Infomap software package.

 Infomap software package is free software: you can redistribute it and/or modify
 it under the terms of the GNU Affero General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

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

 You should have received a copy of the GNU Affero General Public License
 along with Infomap software package.  If not, see <http://www.gnu.org/licenses/>.

**********************************************************************************/

#include "version.h"

const char* INFOMAP_VERSION = "0.13.5";
<commit_msg>Updated internal version string.<commit_after>/**********************************************************************************

 Infomap software package for multi-level network clustering

 Copyright (c) 2013 Daniel Edler, Martin Rosvall

 For more information, see <http://www.mapequation.org>

 This file is part of Infomap software package.

 Infomap software package is free software: you can redistribute it and/or modify
 it under the terms of the GNU Affero General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

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

 You should have received a copy of the GNU Affero General Public License
 along with Infomap software package.  If not, see <http://www.gnu.org/licenses/>.

**********************************************************************************/

#include "version.h"

const char* INFOMAP_VERSION = "0.14.2";
<|endoftext|>


<commit_before>#include "iotsa.h"
#include "iotsaInput.h"
#include "iotsaConfigFile.h"

#define DEBOUNCE_DELAY 50 // 50 ms debouncing

#ifdef ESP32
static void dummyTouchCallback() {}
#endif // ESP32

static bool anyWakeOnTouch;
static uint64_t bitmaskButtonWakeHigh;
static int buttonWakeLow;

void IotsaInputMod::setup() {
  anyWakeOnTouch = false;
  bitmaskButtonWakeHigh = 0;
  buttonWakeLow = -1;
  for(int i=0; i<nInput; i++) {
    inputs[i]->setup();
  }
#ifdef ESP32
  esp_err_t err;
  if (bitmaskButtonWakeHigh && buttonWakeLow && anyWakeOnTouch) {
    IotsaSerial.println("IotsaInputMod: too many incompatible wakeup sources");
  }
  if (anyWakeOnTouch) {
    IFDEBUG IotsaSerial.println("IotsaInputMod: enable wake on touch");
    err = esp_sleep_enable_touchpad_wakeup();
    if (err != ESP_OK) IotsaSerial.println("Error in touchpad_wakeup");
  }
  if (bitmaskButtonWakeHigh) {
    IFDEBUG IotsaSerial.println("IotsaInputMod: enable wake on some high pins");
    err = esp_sleep_enable_ext1_wakeup(bitmaskButtonWakeHigh, ESP_EXT1_WAKEUP_ANY_HIGH);
    if (err != ESP_OK) IotsaSerial.println("Error in ext1_wakeup HIGH");
  }
  if (buttonWakeLow >= 0) {
    if (!anyWakeOnTouch) {
      IFDEBUG IotsaSerial.println("IotsaInputMod: enable wake on one low pins");
      err = esp_sleep_enable_ext0_wakeup((gpio_num_t)buttonWakeLow, 0);
      if (err != ESP_OK) IotsaSerial.println("Error in ext0_wakeup");
    } else {
      err = esp_sleep_enable_ext1_wakeup(1<<buttonWakeLow, ESP_EXT1_WAKEUP_ALL_LOW);
      if (err != ESP_OK) IotsaSerial.println("Error in ext1_wakeup LOW");
    }
  }
#else
  if (anyWakeOnTouch || buttonWakeLow >= 0 || bitmaskButtonWakeHigh) {
    IotsaSerial.println("Wake-from-sleep not implemented on esp8266");
  }
#endif
}

void IotsaInputMod::serverSetup() {
}

void IotsaInputMod::loop() {

  for (int i=0; i<nInput; i++) {
    inputs[i]->loop();
  }
}

Input::Input(bool _actOnPress, bool _actOnRelease, bool _wake)
: actOnPress(_actOnPress),
  actOnRelease(_actOnRelease),
  wake(_wake),
  activationCallback(NULL)
{
}

void Input::setCallback(ActivationCallbackType callback)
{
  activationCallback = callback;
}

Button::Button(int _pin, bool _actOnPress, bool _actOnRelease, bool _wake)
: Input(_actOnPress, _actOnRelease, _wake),
  pressed(false),
  duration(0),
  pin(_pin),
  debounceState(false),
  debounceTime(0),
  lastChangeMillis(0),
  firstRepeat(0),
  minRepeat(0),
  curRepeat(0),
  nextRepeat(0),
  boolVar(NULL),
  toggle(false)
{
}

void Button::setRepeat(uint32_t _firstRepeat, uint32_t _minRepeat) {
  firstRepeat = _firstRepeat;
  minRepeat = _minRepeat;
}

void Button::bindVar(bool& _var, bool _toggle) {
  boolVar = &_var;
  if (!_toggle) *boolVar = pressed;
  toggle = _toggle;
}

void Button::setup() {
  pinMode(pin, INPUT_PULLUP);
  if (wake) {
    // Buttons should be wired to GND. So press gives a low level.
    if (actOnPress) {
      if (buttonWakeLow > 0) IotsaSerial.println("Multiple low wake inputs");
      buttonWakeLow = pin;
    } else {
      bitmaskButtonWakeHigh |= 1LL << pin;
    }
  }

}

bool Button::_getState() {
  return digitalRead(pin) == LOW;
}

void Button::loop() {
  bool state = _getState();
  if (state != debounceState) {
    // The touchpad seems to have changed state. But we want
    // it to remain in the new state for some time (to cater for 50Hz/60Hz interference)
    debounceTime = millis();
    iotsaConfig.postponeSleep(DEBOUNCE_DELAY*2);
  }
  debounceState = state;
  if (millis() > debounceTime + DEBOUNCE_DELAY && state != pressed) {
    // The touchpad has been in the new state for long enough for us to trust it.
    pressed = state;
    if (boolVar) {
      if (toggle) {
        if (pressed) {
          *boolVar = !*boolVar;
        }
      } else {
        *boolVar = pressed;
      }
    }
    if (lastChangeMillis) {
      duration = millis() - lastChangeMillis;
    }
    lastChangeMillis = millis();
    if (pressed) {
      // Setup for repeat, if wanted
      if (firstRepeat) {
        curRepeat = firstRepeat;
        nextRepeat = millis() + curRepeat;
      } else {
        nextRepeat = 0;
      }
    } else {
      // Cancel any repeating
      nextRepeat = 0;
    }
    bool doSend = (pressed && actOnPress) || (!pressed && actOnRelease);
    IFDEBUG IotsaSerial.printf("Button callback for button pin %d state %d\n", pin, state);
    if (doSend && activationCallback) {
      activationCallback();
    }
  }
  // See if we need to do any repeating
  if (nextRepeat && millis() > nextRepeat) {
    if (curRepeat > minRepeat) {
      curRepeat = curRepeat - minRepeat;
      if (curRepeat < minRepeat) curRepeat = minRepeat;
    }
    nextRepeat = millis() + curRepeat;
    if (activationCallback) activationCallback();
  }
}

#ifdef ESP32
Touchpad::Touchpad(int _pin, bool _actOnPress, bool _actOnRelease, bool _wake)
: Button(_actOnPress, _actOnRelease, _wake),
  threshold(20)
{
}

void Touchpad::setup() {
  if (wake) {
    anyWakeOnTouch = true;
    touchAttachInterrupt(pin, dummyTouchCallback, threshold);
  }
}

bool Touchpad::_getState() {
  uint16_t value = touchRead(pin);
  if (value == 0) return false;
  return value < threshold;
}
#endif // ESP32

ValueInput::ValueInput()
: Input(true, true, false),
  value(0),
  intVar(NULL),
  intMin(0),
  intMax(0),
  intStep(0),
  floatVar(NULL),
  floatMin(0),
  floatMax(0),
  floatStep(0)
{
}

void ValueInput::bindVar(int& _var, int _min, int _max, int _stepSize) {
  intVar = &_var;
  intMin = _min;
  intMax = _max;
  intStep = _stepSize;
}

void ValueInput::bindVar(float& _var, float _min, float _max, float _stepSize) {
  floatVar = &_var;
  floatMin = _min;
  floatMax = _max;
  floatStep = _stepSize;
}

void ValueInput::_changeValue(int steps) {
  value += steps;
  if (intVar) {
    *intVar += steps*intStep;
    if (*intVar < intMin) *intVar = intMin;
    if (*intVar > intMax) *intVar = intMax;
  }
  if (floatVar) {
    *floatVar += steps*floatStep;
    if (*floatVar < floatMin) *floatVar = floatMin;
    if (*floatVar > floatMax) *floatVar = floatMax;
  }
  IFDEBUG IotsaSerial.printf("ValueInput callback increment %d value %d\n", steps, value);
  if (activationCallback) {
    activationCallback();
  }
}

RotaryEncoder::RotaryEncoder(int _pinA, int _pinB)
: ValueInput(),
  duration(0),
  pinA(_pinA),
  pinB(_pinB),
  pinAstate(false),
  lastChangeMillis(0),
  accelMillis(0)
{
}

void RotaryEncoder::setAcceleration(uint32_t _accelMillis) {
  accelMillis = _accelMillis;
}

void RotaryEncoder::setup() {
  pinMode(pinA, INPUT_PULLUP);
  pinMode(pinB, INPUT_PULLUP);
  pinAstate = digitalRead(pinA) == LOW;
  if (wake) {
    // xxxjack unsure about this: would "wake on any high" mean on positive flanks (as I hope) or
    // would this mean the cpu remain awake when any pin is level high? To be determined.
    bitmaskButtonWakeHigh |= 1LL << pinA;
    bitmaskButtonWakeHigh |= 1LL << pinB;
  }

}

void RotaryEncoder::loop() {
  bool pinAnewState = digitalRead(pinA) == LOW;

  if (pinAnewState != pinAstate) {
    if (lastChangeMillis) {
      duration = millis() - lastChangeMillis;
    }
    lastChangeMillis = millis();
    // PinA is in a new state
    pinAstate = pinAnewState;
    // If pinA changed state high read pinB to determine whether this is an increment or a decrement.
    bool pinBstate = digitalRead(pinB) == LOW;
    bool increment = pinAstate != pinBstate;
    int change = 1;
    if (accelMillis > 0 && duration > 0) {
      // Check if we want to do multiple steps, because the encoder was
      // rotated fast
      if (duration < accelMillis) {
        change += accelMillis / duration;
      }
    }
    if (increment) {
      _changeValue(change);
    } else {
      _changeValue(-change);
    }
  }
}

UpDownButtons::UpDownButtons(Button& _up, Button& _down)
: ValueInput(),
  up(_up),
  down(_down)
{
  up.setCallback(std::bind(&UpDownButtons::_upPressed, this));
  down.setCallback(std::bind(&UpDownButtons::_downPressed, this));
  up.setRepeat(1000, 100);
  down.setRepeat(1000, 100);
}

void UpDownButtons::setup() {
  up.setup();
  down.setup();
}

void UpDownButtons::loop() {
  up.loop();
  down.loop();
}

bool UpDownButtons::_upPressed() {
  _changeValue(1);
  return true;
}

bool UpDownButtons::_downPressed() {
  _changeValue(-1);
  return true;
}
<commit_msg>Fixed error in touchpad constructor<commit_after>#include "iotsa.h"
#include "iotsaInput.h"
#include "iotsaConfigFile.h"

#define DEBOUNCE_DELAY 50 // 50 ms debouncing

#ifdef ESP32
static void dummyTouchCallback() {}
#endif // ESP32

static bool anyWakeOnTouch;
static uint64_t bitmaskButtonWakeHigh;
static int buttonWakeLow;

void IotsaInputMod::setup() {
  anyWakeOnTouch = false;
  bitmaskButtonWakeHigh = 0;
  buttonWakeLow = -1;
  for(int i=0; i<nInput; i++) {
    inputs[i]->setup();
  }
#ifdef ESP32
  esp_err_t err;
  if (bitmaskButtonWakeHigh && buttonWakeLow && anyWakeOnTouch) {
    IotsaSerial.println("IotsaInputMod: too many incompatible wakeup sources");
  }
  if (anyWakeOnTouch) {
    IFDEBUG IotsaSerial.println("IotsaInputMod: enable wake on touch");
    err = esp_sleep_enable_touchpad_wakeup();
    if (err != ESP_OK) IotsaSerial.println("Error in touchpad_wakeup");
  }
  if (bitmaskButtonWakeHigh) {
    IFDEBUG IotsaSerial.println("IotsaInputMod: enable wake on some high pins");
    err = esp_sleep_enable_ext1_wakeup(bitmaskButtonWakeHigh, ESP_EXT1_WAKEUP_ANY_HIGH);
    if (err != ESP_OK) IotsaSerial.println("Error in ext1_wakeup HIGH");
  }
  if (buttonWakeLow >= 0) {
    if (!anyWakeOnTouch) {
      IFDEBUG IotsaSerial.println("IotsaInputMod: enable wake on one low pins");
      err = esp_sleep_enable_ext0_wakeup((gpio_num_t)buttonWakeLow, 0);
      if (err != ESP_OK) IotsaSerial.println("Error in ext0_wakeup");
    } else {
      err = esp_sleep_enable_ext1_wakeup(1<<buttonWakeLow, ESP_EXT1_WAKEUP_ALL_LOW);
      if (err != ESP_OK) IotsaSerial.println("Error in ext1_wakeup LOW");
    }
  }
#else
  if (anyWakeOnTouch || buttonWakeLow >= 0 || bitmaskButtonWakeHigh) {
    IotsaSerial.println("Wake-from-sleep not implemented on esp8266");
  }
#endif
}

void IotsaInputMod::serverSetup() {
}

void IotsaInputMod::loop() {

  for (int i=0; i<nInput; i++) {
    inputs[i]->loop();
  }
}

Input::Input(bool _actOnPress, bool _actOnRelease, bool _wake)
: actOnPress(_actOnPress),
  actOnRelease(_actOnRelease),
  wake(_wake),
  activationCallback(NULL)
{
}

void Input::setCallback(ActivationCallbackType callback)
{
  activationCallback = callback;
}

Button::Button(int _pin, bool _actOnPress, bool _actOnRelease, bool _wake)
: Input(_actOnPress, _actOnRelease, _wake),
  pressed(false),
  duration(0),
  pin(_pin),
  debounceState(false),
  debounceTime(0),
  lastChangeMillis(0),
  firstRepeat(0),
  minRepeat(0),
  curRepeat(0),
  nextRepeat(0),
  boolVar(NULL),
  toggle(false)
{
}

void Button::setRepeat(uint32_t _firstRepeat, uint32_t _minRepeat) {
  firstRepeat = _firstRepeat;
  minRepeat = _minRepeat;
}

void Button::bindVar(bool& _var, bool _toggle) {
  boolVar = &_var;
  if (!_toggle) *boolVar = pressed;
  toggle = _toggle;
}

void Button::setup() {
  pinMode(pin, INPUT_PULLUP);
  if (wake) {
    // Buttons should be wired to GND. So press gives a low level.
    if (actOnPress) {
      if (buttonWakeLow > 0) IotsaSerial.println("Multiple low wake inputs");
      buttonWakeLow = pin;
    } else {
      bitmaskButtonWakeHigh |= 1LL << pin;
    }
  }

}

bool Button::_getState() {
  return digitalRead(pin) == LOW;
}

void Button::loop() {
  bool state = _getState();
  if (state != debounceState) {
    // The touchpad seems to have changed state. But we want
    // it to remain in the new state for some time (to cater for 50Hz/60Hz interference)
    debounceTime = millis();
    iotsaConfig.postponeSleep(DEBOUNCE_DELAY*2);
  }
  debounceState = state;
  if (millis() > debounceTime + DEBOUNCE_DELAY && state != pressed) {
    // The touchpad has been in the new state for long enough for us to trust it.
    pressed = state;
    if (boolVar) {
      if (toggle) {
        if (pressed) {
          *boolVar = !*boolVar;
        }
      } else {
        *boolVar = pressed;
      }
    }
    if (lastChangeMillis) {
      duration = millis() - lastChangeMillis;
    }
    lastChangeMillis = millis();
    if (pressed) {
      // Setup for repeat, if wanted
      if (firstRepeat) {
        curRepeat = firstRepeat;
        nextRepeat = millis() + curRepeat;
      } else {
        nextRepeat = 0;
      }
    } else {
      // Cancel any repeating
      nextRepeat = 0;
    }
    bool doSend = (pressed && actOnPress) || (!pressed && actOnRelease);
    IFDEBUG IotsaSerial.printf("Button callback for button pin %d state %d\n", pin, state);
    if (doSend && activationCallback) {
      activationCallback();
    }
  }
  // See if we need to do any repeating
  if (nextRepeat && millis() > nextRepeat) {
    if (curRepeat > minRepeat) {
      curRepeat = curRepeat - minRepeat;
      if (curRepeat < minRepeat) curRepeat = minRepeat;
    }
    nextRepeat = millis() + curRepeat;
    if (activationCallback) activationCallback();
  }
}

#ifdef ESP32
Touchpad::Touchpad(int _pin, bool _actOnPress, bool _actOnRelease, bool _wake)
: Button(pin, _actOnPress, _actOnRelease, _wake),
  threshold(20)
{
}

void Touchpad::setup() {
  if (wake) {
    anyWakeOnTouch = true;
    touchAttachInterrupt(pin, dummyTouchCallback, threshold);
  }
}

bool Touchpad::_getState() {
  uint16_t value = touchRead(pin);
  if (value == 0) return false;
  return value < threshold;
}
#endif // ESP32

ValueInput::ValueInput()
: Input(true, true, false),
  value(0),
  intVar(NULL),
  intMin(0),
  intMax(0),
  intStep(0),
  floatVar(NULL),
  floatMin(0),
  floatMax(0),
  floatStep(0)
{
}

void ValueInput::bindVar(int& _var, int _min, int _max, int _stepSize) {
  intVar = &_var;
  intMin = _min;
  intMax = _max;
  intStep = _stepSize;
}

void ValueInput::bindVar(float& _var, float _min, float _max, float _stepSize) {
  floatVar = &_var;
  floatMin = _min;
  floatMax = _max;
  floatStep = _stepSize;
}

void ValueInput::_changeValue(int steps) {
  value += steps;
  if (intVar) {
    *intVar += steps*intStep;
    if (*intVar < intMin) *intVar = intMin;
    if (*intVar > intMax) *intVar = intMax;
  }
  if (floatVar) {
    *floatVar += steps*floatStep;
    if (*floatVar < floatMin) *floatVar = floatMin;
    if (*floatVar > floatMax) *floatVar = floatMax;
  }
  IFDEBUG IotsaSerial.printf("ValueInput callback increment %d value %d\n", steps, value);
  if (activationCallback) {
    activationCallback();
  }
}

RotaryEncoder::RotaryEncoder(int _pinA, int _pinB)
: ValueInput(),
  duration(0),
  pinA(_pinA),
  pinB(_pinB),
  pinAstate(false),
  lastChangeMillis(0),
  accelMillis(0)
{
}

void RotaryEncoder::setAcceleration(uint32_t _accelMillis) {
  accelMillis = _accelMillis;
}

void RotaryEncoder::setup() {
  pinMode(pinA, INPUT_PULLUP);
  pinMode(pinB, INPUT_PULLUP);
  pinAstate = digitalRead(pinA) == LOW;
  if (wake) {
    // xxxjack unsure about this: would "wake on any high" mean on positive flanks (as I hope) or
    // would this mean the cpu remain awake when any pin is level high? To be determined.
    bitmaskButtonWakeHigh |= 1LL << pinA;
    bitmaskButtonWakeHigh |= 1LL << pinB;
  }

}

void RotaryEncoder::loop() {
  bool pinAnewState = digitalRead(pinA) == LOW;

  if (pinAnewState != pinAstate) {
    if (lastChangeMillis) {
      duration = millis() - lastChangeMillis;
    }
    lastChangeMillis = millis();
    // PinA is in a new state
    pinAstate = pinAnewState;
    // If pinA changed state high read pinB to determine whether this is an increment or a decrement.
    bool pinBstate = digitalRead(pinB) == LOW;
    bool increment = pinAstate != pinBstate;
    int change = 1;
    if (accelMillis > 0 && duration > 0) {
      // Check if we want to do multiple steps, because the encoder was
      // rotated fast
      if (duration < accelMillis) {
        change += accelMillis / duration;
      }
    }
    if (increment) {
      _changeValue(change);
    } else {
      _changeValue(-change);
    }
  }
}

UpDownButtons::UpDownButtons(Button& _up, Button& _down)
: ValueInput(),
  up(_up),
  down(_down)
{
  up.setCallback(std::bind(&UpDownButtons::_upPressed, this));
  down.setCallback(std::bind(&UpDownButtons::_downPressed, this));
  up.setRepeat(1000, 100);
  down.setRepeat(1000, 100);
}

void UpDownButtons::setup() {
  up.setup();
  down.setup();
}

void UpDownButtons::loop() {
  up.loop();
  down.loop();
}

bool UpDownButtons::_upPressed() {
  _changeValue(1);
  return true;
}

bool UpDownButtons::_downPressed() {
  _changeValue(-1);
  return true;
}
<|endoftext|>


<commit_before>/*
* Copyright (C) 2008-2011 J-P Nurmi <jpnurmi@gmail.com>
* Copyright (C) 2010-2011 SmokeX <smokexjc@gmail.com>
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
* License for more details.
*/

#include "ircsession.h"
#include "ircsession_p.h"
#include "irccommand.h"
#include "ircmessage.h"
#include "ircutil.h"
#include "irc.h"
#include <QTcpSocket>
#include <QStringList>

/*!
    \class IrcSession ircsession.h
    \brief The IrcSession class provides an IRC session.

    IRC (Internet Relay Chat protocol) is a simple communication protocol.
    IrcSession provides means to establish a connection to an IRC server.
    IrcSession works asynchronously. None of the functions block the
    calling thread but they return immediately and the actual work is done
    behind the scenes in the event loop.

    Example usage:
    \code
    IrcSession* session = new IrcSession(this);
    session->setNick("jpnurmi");
    session->setIdent("jpn");
    session->setRealName("J-P Nurmi");
    session->connectToServer("irc.freenode.net", 6667);
    \endcode

    \note IrcSession supports SSL (Secure Sockets Layer) connections since version 0.3.0

    Example SSL usage:
    \code
    IrcSession* session = new IrcSession(this);
    // ...
    QSslSocket* socket = new QSslSocket(session);
    socket->ignoreSslErrors();
    socket->setPeerVerifyMode(QSslSocket::VerifyNone);
    session->setSocket(socket);
    session->connectToServer("irc.secure.ssl", 6669);
    \endcode

    \sa setSocket()
 */

/*!
    \fn void IrcSession::connecting()

    This signal is emitted when the connection is being established.
 */

/*!
    \fn void IrcSession::connected()

    This signal is emitted when the welcome message has been received.

    \sa Irc::RPL_WELCOME
 */

/*!
    \fn void IrcSession::disconnected()

    This signal is emitted when the session has been disconnected.
 */

IrcSessionPrivate::IrcSessionPrivate(IrcSession* session) :
    q_ptr(session),
    parser(),
    buffer(),
    socket(0),
    host(),
    port(6667),
    userName(),
    nickName(),
    realName()
{
}

void IrcSessionPrivate::_q_connected()
{
    Q_Q(IrcSession);
    emit q->connecting();

    QString password;
    emit q->password(&password);
    if (!password.isEmpty())
        q->sendCommand(IrcCommand::createPassword(password));

    q->sendCommand(IrcCommand::createNick(nickName));
    q->sendCommand(IrcCommand::createUser(userName, realName));
}

void IrcSessionPrivate::_q_disconnected()
{
    Q_Q(IrcSession);
    emit q->disconnected();
}

void IrcSessionPrivate::_q_reconnect()
{
    if (socket)
    {
        socket->connectToHost(host, port);
        if (socket->inherits("QSslSocket"))
            QMetaObject::invokeMethod(socket, "startClientEncryption");
    }
}

void IrcSessionPrivate::_q_error(QAbstractSocket::SocketError error)
{
    qCritical() << "IrcSessionPrivate::_q_error():" << error;
}

void IrcSessionPrivate::_q_state(QAbstractSocket::SocketState state)
{
    static bool dbg = qgetenv("COMMUNI_DEBUG").toInt();
    if (dbg) qDebug() << "IrcSessionPrivate::_q_state():" << state;
}

void IrcSessionPrivate::_q_readData()
{
    buffer += socket->readAll();
    // try reading RFC compliant message lines first
    readLines("\r\n");
    // fall back to RFC incompliant lines...
    readLines("\n");
}

void IrcSessionPrivate::readLines(const QByteArray& delimiter)
{
    int i = -1;
    while ((i = buffer.indexOf(delimiter)) != -1)
    {
        QByteArray line = buffer.left(i).trimmed();
        buffer = buffer.mid(i + delimiter.length());
        if (!line.isEmpty())
            processLine(line);
    }
}

void IrcSessionPrivate::processLine(const QByteArray& line)
{
    Q_Q(IrcSession);
    parser.parse(line);

    static bool dbg = qgetenv("COMMUNI_DEBUG").toInt();
    if (dbg) qDebug() << line;

    QString prefix = parser.prefix();
    QString command = parser.command();
    QStringList params = parser.params();

    IrcMessage* msg = IrcMessage::create(command);
    if (msg)
    {
        msg->initFrom(prefix, params);

        switch (msg->type())
        {
        case IrcMessage::Numeric:
            if (static_cast<IrcNumericMessage*>(msg)->code() == Irc::RPL_WELCOME)
                emit q->connected();
            break;
        case IrcMessage::Ping: {
            QString target = static_cast<IrcPingMessage*>(msg)->target();
            IrcCommand* pongCmd = IrcCommand::createPong(target);
            q->sendCommand(pongCmd);
            break;
            }
        default:
            break;
        }

        emit q->messageReceived(msg);
    }
}

bool IrcSessionPrivate::isConnected() const
{
    return socket &&
        (socket->state() == QAbstractSocket::ConnectingState
         || socket->state() == QAbstractSocket::ConnectedState);
}

/*!
    Constructs a new IRC session with \a parent.
 */
IrcSession::IrcSession(QObject* parent) : QObject(parent), d_ptr(new IrcSessionPrivate(this))
{
    setSocket(new QTcpSocket(this));
}

/*!
    Destructs the IRC session.
 */
IrcSession::~IrcSession()
{
    Q_D(IrcSession);
    if (d->socket)
        d->socket->close();
}

/*!
    Returns the encoding.

    The default value is a null QByteArray.
 */
QByteArray IrcSession::encoding() const
{
    Q_D(const IrcSession);
    return d->parser.encoding();
}

/*!
    Sets the \a encoding.

    See QTextCodec documentation for supported encodings.

    Encoding auto-detection can be turned on by passing a null QByteArray.

    The fallback locale is QTextCodec::codecForLocale().
 */
void IrcSession::setEncoding(const QByteArray& encoding)
{
    Q_D(IrcSession);
    d->parser.setEncoding(encoding);
}

/*!
    Returns the host.
 */
QString IrcSession::host() const
{
    Q_D(const IrcSession);
    return d->host;
}

/*!
    Sets the \a host.
 */
void IrcSession::setHost(const QString& host)
{
    Q_D(IrcSession);
    if (d->isConnected())
        qWarning("IrcSession::setHost() has no effect until re-connect");
    d->host = host;
}

/*!
    Returns the port.
 */
int IrcSession::port() const
{
    Q_D(const IrcSession);
    return d->port;
}

/*!
    Sets the \a port.
 */
void IrcSession::setPort(int port)
{
    Q_D(IrcSession);
    if (d->isConnected())
        qWarning("IrcSession::setPort() has no effect until re-connect");
    d->port = port;
}

/*!
    Returns the user name.
 */
QString IrcSession::userName() const
{
    Q_D(const IrcSession);
    return d->userName;
}

/*!
    Sets the user \a name.

    \note setUserName() has no effect on already established connection.
 */
void IrcSession::setUserName(const QString& name)
{
    Q_D(IrcSession);
    if (d->isConnected())
        qWarning("IrcSession::setUserName() has no effect until re-connect");
    d->userName = name.split(" ", QString::SkipEmptyParts).value(0).trimmed();
}

/*!
    Returns the nick name.
 */
QString IrcSession::nickName() const
{
    Q_D(const IrcSession);
    return d->nickName;
}

/*!
    Sets the nick \a name.
 */
void IrcSession::setNickName(const QString& name)
{
    Q_D(IrcSession);
    QString nick = name.split(" ", QString::SkipEmptyParts).value(0).trimmed();
    if (d->nickName != nick)
    {
        d->nickName = nick;
        if (d->isConnected())
            sendCommand(IrcCommand::createNick(nick));
    }
}

/*!
    Returns the real name.
 */
QString IrcSession::realName() const
{
    Q_D(const IrcSession);
    return d->realName;
}

/*!
    Sets the real \a name.

    \note setRealName() has no effect on already established connection.
 */
void IrcSession::setRealName(const QString& name)
{
    Q_D(IrcSession);
    if (d->isConnected())
        qWarning("IrcSession::setRealName() has no effect until re-connect");
    d->realName = name;
}

/*!
    Returns the socket.

    IrcSession creates an instance of QTcpSocket by default.

    This function was introduced in version 0.3.0.
 */
QAbstractSocket* IrcSession::socket() const
{
    Q_D(const IrcSession);
    return d->socket;
}

/*!
    Sets the \a socket. The previously set socket is deleted if its parent is \c this.

    IrcSession supports QSslSocket in the way that it automatically calls
    QSslSocket::startClientEncryption() while connecting.

    This function was introduced in version 0.3.0.
 */
void IrcSession::setSocket(QAbstractSocket* socket)
{
    Q_D(IrcSession);
    if (d->socket != socket)
    {
        if (d->socket)
        {
            d->socket->disconnect(this);
            if (d->socket->parent() == this)
                d->socket->deleteLater();
        }

        d->socket = socket;
        if (socket)
        {
            connect(socket, SIGNAL(connected()), this, SLOT(_q_connected()));
            connect(socket, SIGNAL(disconnected()), this, SLOT(_q_disconnected()));
            connect(socket, SIGNAL(readyRead()), this, SLOT(_q_readData()));
            connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(_q_error(QAbstractSocket::SocketError)));
            connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(_q_state(QAbstractSocket::SocketState)));
        }
    }
}

/*!
    Connects to the server.
 */
void IrcSession::open()
{
    Q_D(IrcSession);
    if (d->userName.isEmpty())
    {
        qCritical("IrcSession::open(): userName is empty!");
        return;
    }
    if (d->nickName.isEmpty())
    {
        qCritical("IrcSession::open(): nickName is empty!");
        return;
    }
    if (d->realName.isEmpty())
    {
        qCritical("IrcSession::open(): realName is empty!");
        return;
    }
    d->_q_reconnect();
}

/*!
    Disconnects from the server.
 */
void IrcSession::close()
{
    Q_D(IrcSession);
    if (d->socket)
        d->socket->disconnectFromHost();
}

/*!
    Sends a \a command to the server.

    The command must be allocated on the heap since the session will take
    ownership of the command and delete it once it has been sent. It is
    not safe to access the command after it has been sent.

    \sa sendRaw()
 */
bool IrcSession::sendCommand(IrcCommand* command)
{
    return command && sendRaw(command->toString());
}

/*!
    Sends a raw \a message to the server.

    \sa sendCommand()
 */
bool IrcSession::sendRaw(const QString& message)
{
    Q_D(IrcSession);
    qint64 bytes = -1;
    if (d->socket)
        bytes = d->socket->write(message.toUtf8() + QByteArray("\r\n"));
    return bytes != -1;
}

#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug debug, const IrcSession* session)
{
    if (!session)
        return debug << "IrcSession(0x0) ";
    debug.nospace() << session->metaObject()->className() << '(' << (void*) session;
    if (!session->objectName().isEmpty())
        debug << ", name = " << session->objectName();
    if (!session->host().isEmpty())
        debug << ", host = " << session->host()
              << ", port = " << session->port();
    debug << ')';
    return debug.space();
}
#endif // QT_NO_DEBUG_STREAM

#include "moc_ircsession.cpp"
<commit_msg>Updated IrcSession docs<commit_after>/*
* Copyright (C) 2008-2011 J-P Nurmi <jpnurmi@gmail.com>
* Copyright (C) 2010-2011 SmokeX <smokexjc@gmail.com>
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
* License for more details.
*/

#include "ircsession.h"
#include "ircsession_p.h"
#include "irccommand.h"
#include "ircmessage.h"
#include "ircutil.h"
#include "irc.h"
#include <QTcpSocket>
#include <QStringList>

/*!
    \class IrcSession ircsession.h IrcSession
    \brief The IrcSession class provides an IRC session.

    IRC (Internet Relay Chat protocol) is a simple communication protocol.
    IrcSession provides means to establish a connection to an IRC server.
    IrcSession works asynchronously. None of the functions block the
    calling thread but they return immediately and the actual work is done
    behind the scenes in the event loop.

    Example usage:
    \code
    IrcSession* session = new IrcSession(this);
    session->setHost("irc.freenode.net");
    session->setPort(6667);
    session->setUserName("jpn");
    session->setNickName("jpnurmi");
    session->setRealName("J-P Nurmi");
    session->open();
    \endcode

    \section ssl Secure connections

    IrcSession supports SSL (Secure Sockets Layer) connections.

    Example SSL usage:
    \code
    IrcSession* session = new IrcSession(this);
    session->setHost("irc.secure.ssl");
    session->setPort(6669);
    // ...
    QSslSocket* socket = new QSslSocket(session);
    socket->ignoreSslErrors();
    socket->setPeerVerifyMode(QSslSocket::VerifyNone);
    session->setSocket(socket);
    session->open();
    \endcode

    \sa setSocket()
 */

/*!
    \fn void IrcSession::connecting()

    This signal is emitted when the connection is being established.
 */

/*!
    \fn void IrcSession::connected()

    This signal is emitted when the welcome message has been received.

    \sa Irc::RPL_WELCOME
 */

/*!
    \fn void IrcSession::disconnected()

    This signal is emitted when the session has been disconnected.
 */

IrcSessionPrivate::IrcSessionPrivate(IrcSession* session) :
    q_ptr(session),
    parser(),
    buffer(),
    socket(0),
    host(),
    port(6667),
    userName(),
    nickName(),
    realName()
{
}

void IrcSessionPrivate::_q_connected()
{
    Q_Q(IrcSession);
    emit q->connecting();

    QString password;
    emit q->password(&password);
    if (!password.isEmpty())
        q->sendCommand(IrcCommand::createPassword(password));

    q->sendCommand(IrcCommand::createNick(nickName));
    q->sendCommand(IrcCommand::createUser(userName, realName));
}

void IrcSessionPrivate::_q_disconnected()
{
    Q_Q(IrcSession);
    emit q->disconnected();
}

void IrcSessionPrivate::_q_reconnect()
{
    if (socket)
    {
        socket->connectToHost(host, port);
        if (socket->inherits("QSslSocket"))
            QMetaObject::invokeMethod(socket, "startClientEncryption");
    }
}

void IrcSessionPrivate::_q_error(QAbstractSocket::SocketError error)
{
    qCritical() << "IrcSessionPrivate::_q_error():" << error;
}

void IrcSessionPrivate::_q_state(QAbstractSocket::SocketState state)
{
    static bool dbg = qgetenv("COMMUNI_DEBUG").toInt();
    if (dbg) qDebug() << "IrcSessionPrivate::_q_state():" << state;
}

void IrcSessionPrivate::_q_readData()
{
    buffer += socket->readAll();
    // try reading RFC compliant message lines first
    readLines("\r\n");
    // fall back to RFC incompliant lines...
    readLines("\n");
}

void IrcSessionPrivate::readLines(const QByteArray& delimiter)
{
    int i = -1;
    while ((i = buffer.indexOf(delimiter)) != -1)
    {
        QByteArray line = buffer.left(i).trimmed();
        buffer = buffer.mid(i + delimiter.length());
        if (!line.isEmpty())
            processLine(line);
    }
}

void IrcSessionPrivate::processLine(const QByteArray& line)
{
    Q_Q(IrcSession);
    parser.parse(line);

    static bool dbg = qgetenv("COMMUNI_DEBUG").toInt();
    if (dbg) qDebug() << line;

    QString prefix = parser.prefix();
    QString command = parser.command();
    QStringList params = parser.params();

    IrcMessage* msg = IrcMessage::create(command);
    if (msg)
    {
        msg->initFrom(prefix, params);

        switch (msg->type())
        {
        case IrcMessage::Numeric:
            if (static_cast<IrcNumericMessage*>(msg)->code() == Irc::RPL_WELCOME)
                emit q->connected();
            break;
        case IrcMessage::Ping: {
            QString target = static_cast<IrcPingMessage*>(msg)->target();
            IrcCommand* pongCmd = IrcCommand::createPong(target);
            q->sendCommand(pongCmd);
            break;
            }
        default:
            break;
        }

        emit q->messageReceived(msg);
    }
}

bool IrcSessionPrivate::isConnected() const
{
    return socket &&
        (socket->state() == QAbstractSocket::ConnectingState
         || socket->state() == QAbstractSocket::ConnectedState);
}

/*!
    Constructs a new IRC session with \a parent.
 */
IrcSession::IrcSession(QObject* parent) : QObject(parent), d_ptr(new IrcSessionPrivate(this))
{
    setSocket(new QTcpSocket(this));
}

/*!
    Destructs the IRC session.
 */
IrcSession::~IrcSession()
{
    Q_D(IrcSession);
    if (d->socket)
        d->socket->close();
}

/*!
    This property holds the message encoding. See QTextCodec documentation for supported encodings.

    Encoding auto-detection can be turned on by passing a null QByteArray. The fallback codec is QTextCodec::codecForLocale().

    The default value is a null QByteArray.

    \par Access functions:
    \li QByteArray encoding() const
    \li void setEncoding(const QByteArray& encoding)
 */
QByteArray IrcSession::encoding() const
{
    Q_D(const IrcSession);
    return d->parser.encoding();
}

void IrcSession::setEncoding(const QByteArray& encoding)
{
    Q_D(IrcSession);
    d->parser.setEncoding(encoding);
}

/*!
    This property holds the server host.

    \par Access functions:
    \li QString host() const
    \li void setHost(const QString& host)
 */
QString IrcSession::host() const
{
    Q_D(const IrcSession);
    return d->host;
}

void IrcSession::setHost(const QString& host)
{
    Q_D(IrcSession);
    if (d->isConnected())
        qWarning("IrcSession::setHost() has no effect until re-connect");
    d->host = host;
}

/*!
    This property holds the server port.

    The default value is \c 6667.

    \par Access functions:
    \li int port() const
    \li void setPort(int port)
 */
int IrcSession::port() const
{
    Q_D(const IrcSession);
    return d->port;
}

void IrcSession::setPort(int port)
{
    Q_D(IrcSession);
    if (d->isConnected())
        qWarning("IrcSession::setPort() has no effect until re-connect");
    d->port = port;
}

/*!
    This property holds the user name.

    \note Changing the user name has no effect until the connection is re-established.

    \par Access functions:
    \li QString userName() const
    \li void setUserName(const QString& name)
 */
QString IrcSession::userName() const
{
    Q_D(const IrcSession);
    return d->userName;
}

void IrcSession::setUserName(const QString& name)
{
    Q_D(IrcSession);
    if (d->isConnected())
        qWarning("IrcSession::setUserName() has no effect until re-connect");
    d->userName = name.split(" ", QString::SkipEmptyParts).value(0).trimmed();
}

/*!
    This property holds the nick name.

    \par Access functions:
    \li QString nickName() const
    \li void setNickName(const QString& name)
 */
QString IrcSession::nickName() const
{
    Q_D(const IrcSession);
    return d->nickName;
}

void IrcSession::setNickName(const QString& name)
{
    Q_D(IrcSession);
    QString nick = name.split(" ", QString::SkipEmptyParts).value(0).trimmed();
    if (d->nickName != nick)
    {
        d->nickName = nick;
        if (d->isConnected())
            sendCommand(IrcCommand::createNick(nick));
    }
}

/*!
    This property holds the real name.

    \note Changing the real name has no effect until the connection is re-established.

    \par Access functions:
    \li QString realName() const
    \li void setRealName(const QString& name)
 */
QString IrcSession::realName() const
{
    Q_D(const IrcSession);
    return d->realName;
}

void IrcSession::setRealName(const QString& name)
{
    Q_D(IrcSession);
    if (d->isConnected())
        qWarning("IrcSession::setRealName() has no effect until re-connect");
    d->realName = name;
}

/*!
    This property holds the socket. IrcSession creates an instance of QTcpSocket by default.

    The previously set socket is deleted if its parent is \c this.

    \note IrcSession supports QSslSocket in the way that it automatically
    calls QSslSocket::startClientEncryption() while connecting.

    \par Access functions:
    \li QAbstractSocket* socket() const
    \li void setSocket(QAbstractSocket* socket)
 */
QAbstractSocket* IrcSession::socket() const
{
    Q_D(const IrcSession);
    return d->socket;
}

void IrcSession::setSocket(QAbstractSocket* socket)
{
    Q_D(IrcSession);
    if (d->socket != socket)
    {
        if (d->socket)
        {
            d->socket->disconnect(this);
            if (d->socket->parent() == this)
                d->socket->deleteLater();
        }

        d->socket = socket;
        if (socket)
        {
            connect(socket, SIGNAL(connected()), this, SLOT(_q_connected()));
            connect(socket, SIGNAL(disconnected()), this, SLOT(_q_disconnected()));
            connect(socket, SIGNAL(readyRead()), this, SLOT(_q_readData()));
            connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(_q_error(QAbstractSocket::SocketError)));
            connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(_q_state(QAbstractSocket::SocketState)));
        }
    }
}

/*!
    Opens a connection to the server.
 */
void IrcSession::open()
{
    Q_D(IrcSession);
    if (d->userName.isEmpty())
    {
        qCritical("IrcSession::open(): userName is empty!");
        return;
    }
    if (d->nickName.isEmpty())
    {
        qCritical("IrcSession::open(): nickName is empty!");
        return;
    }
    if (d->realName.isEmpty())
    {
        qCritical("IrcSession::open(): realName is empty!");
        return;
    }
    d->_q_reconnect();
}

/*!
    Closes the connection to the server.
 */
void IrcSession::close()
{
    Q_D(IrcSession);
    if (d->socket)
        d->socket->disconnectFromHost();
}

/*!
    Sends a \a command to the server.

    \warning The command must be allocated on the heap since the session
    will take ownership of the command and delete it once it has been sent.
    It is not safe to access the command after it has been sent.

    \sa sendRaw()
 */
bool IrcSession::sendCommand(IrcCommand* command)
{
    bool res = false;
    if (command)
    {
        res = sendRaw(command->toString());
        command->deleteLater();
    }
    return res;
}

/*!
    Sends a raw \a message to the server.

    \sa sendCommand()
 */
bool IrcSession::sendRaw(const QString& message)
{
    Q_D(IrcSession);
    qint64 bytes = -1;
    if (d->socket)
        bytes = d->socket->write(message.toUtf8() + QByteArray("\r\n"));
    return bytes != -1;
}

#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug debug, const IrcSession* session)
{
    if (!session)
        return debug << "IrcSession(0x0) ";
    debug.nospace() << session->metaObject()->className() << '(' << (void*) session;
    if (!session->objectName().isEmpty())
        debug << ", name = " << session->objectName();
    if (!session->host().isEmpty())
        debug << ", host = " << session->host()
              << ", port = " << session->port();
    debug << ')';
    return debug.space();
}
#endif // QT_NO_DEBUG_STREAM

#include "moc_ircsession.cpp"
<|endoftext|>


<commit_before>/*
 * author: Max Kellermann <mk@cm4all.com>
 */

#ifndef ISTREAM_OO_HXX
#define ISTREAM_OO_HXX

#include "pool.hxx"
#include "istream.hxx"
#include "istream_invoke.hxx"
#include "istream_new.hxx"
#include "util/Cast.hxx"

class Istream {
    struct istream output;

    static const struct istream_class cls;

protected:
    explicit Istream(struct pool &pool)
        :output(pool, cls) {}

    Istream(const Istream &) = delete;
    Istream &operator=(const Istream &) = delete;

    virtual ~Istream() {
        istream_deinit(&output);
    }

    istream_direct_t GetHandlerDirect() const {
        return output.handler_direct;
    }

    size_t InvokeData(const void *data, size_t length) {
        return istream_invoke_data(&output, data, length);
    }

    ssize_t InvokeDirect(enum istream_direct type, int fd, size_t max_length) {
        return istream_invoke_direct(&output, type, fd, max_length);
    }

    void InvokeEof() {
        istream_invoke_eof(&output);
    }

    void InvokeError(GError *error) {
        istream_invoke_abort(&output, error);
    }

    void Destroy() {
        this->~Istream();
        /* no need to free memory from the pool */
    }

    void DestroyEof() {
        InvokeEof();
        Destroy();
    }

    void DestroyError(GError *error) {
        InvokeError(error);
        Destroy();
    }

public:
    struct istream *Cast() {
        return &output;
    }

    static constexpr Istream &Cast(struct istream &i) {
        return ContainerCast2(i, &Istream::output);
    }

    /* istream */

    virtual off_t GetAvailable(gcc_unused bool partial) {
        return -1;
    }

    virtual off_t Skip(gcc_unused off_t length) {
        return -1;
    }

    virtual void Read() = 0;

    virtual int AsFd() {
        return -1;
    }

    virtual void Close() {
        Destroy();
    }

private:
    static off_t GetAvailable(struct istream *istream, bool partial) {
        return Cast(*istream).GetAvailable(partial);
    }

    static off_t Skip(struct istream *istream, off_t length) {
        return Cast(*istream).Skip(length);
    }

    static void Read(struct istream *istream) {
        Cast(*istream).Read();
    }

    static int AsFd(struct istream *istream) {
        return Cast(*istream).AsFd();
    }

    static void Close(struct istream *istream) {
        Cast(*istream).Close();
    }
};

template<typename T, typename... Args>
static inline struct istream *
NewIstream(struct pool &pool, Args&&... args)
{
    return NewFromPool<T>(pool, pool,
                          std::forward<Args>(args)...)->Cast();
}

template<typename T>
class MakeIstreamHandler {
    static constexpr T &Cast(void *ctx) {
        return *(T *)ctx;
    }

    static size_t Data(const void *data, size_t length, void *ctx) {
        return Cast(ctx).OnData(data, length);
    }

    static ssize_t Direct(enum istream_direct type, int fd, size_t max_length,
                          void *ctx) {
        return Cast(ctx).OnDirect(type, fd, max_length);
    }

    static void Eof(void *ctx) {
        return Cast(ctx).OnEof();
    }

    static void Error(GError *error, void *ctx) {
        return Cast(ctx).OnError(error);
    }

public:
    static const struct istream_handler handler;
};

template<typename T>
constexpr struct istream_handler MakeIstreamHandler<T>::handler = {
    Data,
    Direct,
    Eof,
    Error,
};

#endif
<commit_msg>istream_oo: add method GetPool()<commit_after>/*
 * author: Max Kellermann <mk@cm4all.com>
 */

#ifndef ISTREAM_OO_HXX
#define ISTREAM_OO_HXX

#include "pool.hxx"
#include "istream.hxx"
#include "istream_invoke.hxx"
#include "istream_new.hxx"
#include "util/Cast.hxx"

class Istream {
    struct istream output;

    static const struct istream_class cls;

protected:
    explicit Istream(struct pool &pool)
        :output(pool, cls) {}

    Istream(const Istream &) = delete;
    Istream &operator=(const Istream &) = delete;

    virtual ~Istream() {
        istream_deinit(&output);
    }

    struct pool &GetPool() {
        return *output.pool;
    }

    istream_direct_t GetHandlerDirect() const {
        return output.handler_direct;
    }

    size_t InvokeData(const void *data, size_t length) {
        return istream_invoke_data(&output, data, length);
    }

    ssize_t InvokeDirect(enum istream_direct type, int fd, size_t max_length) {
        return istream_invoke_direct(&output, type, fd, max_length);
    }

    void InvokeEof() {
        istream_invoke_eof(&output);
    }

    void InvokeError(GError *error) {
        istream_invoke_abort(&output, error);
    }

    void Destroy() {
        this->~Istream();
        /* no need to free memory from the pool */
    }

    void DestroyEof() {
        InvokeEof();
        Destroy();
    }

    void DestroyError(GError *error) {
        InvokeError(error);
        Destroy();
    }

public:
    struct istream *Cast() {
        return &output;
    }

    static constexpr Istream &Cast(struct istream &i) {
        return ContainerCast2(i, &Istream::output);
    }

    /* istream */

    virtual off_t GetAvailable(gcc_unused bool partial) {
        return -1;
    }

    virtual off_t Skip(gcc_unused off_t length) {
        return -1;
    }

    virtual void Read() = 0;

    virtual int AsFd() {
        return -1;
    }

    virtual void Close() {
        Destroy();
    }

private:
    static off_t GetAvailable(struct istream *istream, bool partial) {
        return Cast(*istream).GetAvailable(partial);
    }

    static off_t Skip(struct istream *istream, off_t length) {
        return Cast(*istream).Skip(length);
    }

    static void Read(struct istream *istream) {
        Cast(*istream).Read();
    }

    static int AsFd(struct istream *istream) {
        return Cast(*istream).AsFd();
    }

    static void Close(struct istream *istream) {
        Cast(*istream).Close();
    }
};

template<typename T, typename... Args>
static inline struct istream *
NewIstream(struct pool &pool, Args&&... args)
{
    return NewFromPool<T>(pool, pool,
                          std::forward<Args>(args)...)->Cast();
}

template<typename T>
class MakeIstreamHandler {
    static constexpr T &Cast(void *ctx) {
        return *(T *)ctx;
    }

    static size_t Data(const void *data, size_t length, void *ctx) {
        return Cast(ctx).OnData(data, length);
    }

    static ssize_t Direct(enum istream_direct type, int fd, size_t max_length,
                          void *ctx) {
        return Cast(ctx).OnDirect(type, fd, max_length);
    }

    static void Eof(void *ctx) {
        return Cast(ctx).OnEof();
    }

    static void Error(GError *error, void *ctx) {
        return Cast(ctx).OnError(error);
    }

public:
    static const struct istream_handler handler;
};

template<typename T>
constexpr struct istream_handler MakeIstreamHandler<T>::handler = {
    Data,
    Direct,
    Eof,
    Error,
};

#endif
<|endoftext|>


<commit_before>#include "mini_stdint.h"
#include "HalideRuntime.h"

extern "C" {

extern char *getenv(const char *);
extern void *fopen(const char *path, const char *mode);
extern size_t fwrite(const void *ptr, size_t size, size_t n, void *file);
extern int snprintf(char *str, size_t size, const char *format, ...);
extern int fclose(void *f);

typedef void (*trace_fn)(const char *, halide_trace_event_t,
                         int32_t, int32_t, int32_t, int32_t,
                         const void *, int32_t, const int32_t *);
WEAK trace_fn halide_custom_trace = NULL;

WEAK void halide_set_custom_trace(trace_fn t) {
    halide_custom_trace = t;
}

WEAK void *halide_trace_file = NULL;

WEAK void halide_trace(const char *func, halide_trace_event_t event,
                       int32_t type_code, int32_t bits, int32_t width, int32_t value_idx, void *value,
                       int32_t num_int_args, const int32_t *int_args) {
    if (halide_custom_trace) {
        (*halide_custom_trace)(func, event, type_code, bits, width, value_idx, value, num_int_args, int_args);
    } else {
        static bool initialized = false;

        if (!initialized) {
            const char *trace_file_name = getenv("HL_TRACE_FILE");
            initialized = true;
            if (trace_file_name) {
                halide_trace_file = fopen(trace_file_name, "wb");
                halide_assert(halide_trace_file && "Failed to open trace file\n");
            }
        }

        // If we're dumping to a file, use a binary format
        if (halide_trace_file) {
            // A 32-byte header. The first 6 bytes are metadata, then the rest is a zero-terminated string.
            uint8_t clamped_width = width < 256 ? width : 255;
            uint8_t clamped_num_int_args = num_int_args < 256 ? num_int_args : 255;

            // Upgrade the bit count to a power of two, because that's
            // how it will be stored on the stack.
            int bytes = 1;
            while (bytes < (bits*8)) bytes <<= 1;

            // Compute the size of each portion of the tracing packet
            size_t header_bytes = 32;
            size_t value_bytes = clamped_width * bytes;
            size_t int_arg_bytes = clamped_num_int_args * sizeof(int32_t);
            size_t total_bytes = header_bytes + value_bytes + int_arg_bytes;
            uint8_t buffer[4096];
            halide_assert(total_bytes <= 4096 && "Tracing packet too large");

            buffer[0] = event;
            buffer[1] = type_code;
            buffer[2] = bits;
            buffer[3] = clamped_width;
            buffer[4] = value_idx;
            buffer[5] = clamped_num_int_args;

            // Use up to 25 bytes for the function name
            int i = 6;
            for (; i < header_bytes-1; i++) {
                buffer[i] = func[i-6];
                if (buffer[i] == 0) break;
            }
            // Fill the rest with zeros
            for (; i < header_bytes; i++) {
                buffer[i] = 0;
            }

            // Next comes the value
            for (size_t i = 0; i < value_bytes; i++) {
                buffer[header_bytes + i] = ((uint8_t *)value)[i];
            }

            // Then the int args
            for (size_t i = 0; i < int_arg_bytes; i++) {
                buffer[header_bytes + value_bytes + i] = ((uint8_t *)int_args)[i];
            }

            size_t written = fwrite(&buffer[0], 1, total_bytes, halide_trace_file);
            halide_assert(written == total_bytes && "Can't write to trace file");

        } else {
            char buf[256];
            char *buf_ptr = &buf[0];
            char *buf_end = &buf[255];

            // Round up bits to 8, 16, 32, or 64
            int print_bits = 8;
            while (print_bits < bits) print_bits <<= 1;
            halide_assert(print_bits <= 64 && "Tracing bad type");

            // Otherwise, use halide_printf and a plain-text format
            const char *event_types[] = {"Load",
                                         "Store",
                                         "Begin realization",
                                         "End realization",
                                         "Produce",
                                         "Update",
                                         "Consume",
                                         "End consume"};

            if (buf_ptr < buf_end) {
                buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, "%s %s.%d[", event_types[event], func, value_idx);
            }
            for (int i = 0; i < num_int_args && buf_ptr < buf_end; i++) {
                if (i > 0) {
                    buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, ", %d", int_args[i]);
                } else {
                    buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, "%d", int_args[i]);
                }
            }
            if (buf_ptr < buf_end) {
                buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, "] = (");
            }

            for (int i = 0; i < width && buf_ptr < buf_end; i++) {
                if (i > 0) {
                    buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, ", ");
                }
                if (type_code == 0) {
                    if (print_bits == 8) {
                        buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, "%d", ((int8_t *)(value))[i]);
                    } else if (print_bits == 16) {
                        buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, "%d", ((int16_t *)(value))[i]);
                    } else if (print_bits == 32) {
                        buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, "%d", ((int32_t *)(value))[i]);
                    } else {
                        buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, "%d", (int32_t)((int64_t *)(value))[i]);
                    }
                } else if (type_code == 1) {
                    if (print_bits == 8) {
                        buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, "%u", ((uint8_t *)(value))[i]);
                        if (buf_ptr > buf_end) buf_ptr = buf_end;
                    } else if (print_bits == 16) {
                        buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, "%u", ((uint16_t *)(value))[i]);
                    } else if (print_bits == 32) {
                        buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, "%u", ((uint32_t *)(value))[i]);
                    } else {
                        buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, "%u", (uint32_t)((uint64_t *)(value))[i]);
                    }
                } else if (type_code == 2) {
                    halide_assert(print_bits >= 32 && "Tracing a bad type");
                    if (print_bits == 32) {
                        buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, "%f", ((float *)(value))[i]);
                    } else {
                        buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, "%f", ((double *)(value))[i]);
                    }
                } else if (type_code == 3) {
                    buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, "%p", ((void **)(value))[i]);
                }
            }

            if (buf_ptr < buf_end) {
                buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, ")");
            }

            halide_printf("%s\n", buf);
        }
    }
}

WEAK void halide_shutdown_trace() {
    if (halide_trace_file) {
        fclose(halide_trace_file);
        halide_trace_file = NULL;
    }
}

}
<commit_msg>Better default prints for tracing.<commit_after>#include "mini_stdint.h"
#include "HalideRuntime.h"

extern "C" {

extern char *getenv(const char *);
extern void *fopen(const char *path, const char *mode);
extern size_t fwrite(const void *ptr, size_t size, size_t n, void *file);
extern int snprintf(char *str, size_t size, const char *format, ...);
extern int fclose(void *f);

typedef void (*trace_fn)(const char *, halide_trace_event_t,
                         int32_t, int32_t, int32_t, int32_t,
                         const void *, int32_t, const int32_t *);
WEAK trace_fn halide_custom_trace = NULL;

WEAK void halide_set_custom_trace(trace_fn t) {
    halide_custom_trace = t;
}

WEAK void *halide_trace_file = NULL;

WEAK void halide_trace(const char *func, halide_trace_event_t event,
                       int32_t type_code, int32_t bits, int32_t width, int32_t value_idx, void *value,
                       int32_t num_int_args, const int32_t *int_args) {
    if (halide_custom_trace) {
        (*halide_custom_trace)(func, event, type_code, bits, width, value_idx, value, num_int_args, int_args);
    } else {
        static bool initialized = false;

        if (!initialized) {
            const char *trace_file_name = getenv("HL_TRACE_FILE");
            initialized = true;
            if (trace_file_name) {
                halide_trace_file = fopen(trace_file_name, "wb");
                halide_assert(halide_trace_file && "Failed to open trace file\n");
            }
        }

        // If we're dumping to a file, use a binary format
        if (halide_trace_file) {
            // A 32-byte header. The first 6 bytes are metadata, then the rest is a zero-terminated string.
            uint8_t clamped_width = width < 256 ? width : 255;
            uint8_t clamped_num_int_args = num_int_args < 256 ? num_int_args : 255;

            // Upgrade the bit count to a power of two, because that's
            // how it will be stored on the stack.
            int bytes = 1;
            while (bytes < (bits*8)) bytes <<= 1;

            // Compute the size of each portion of the tracing packet
            size_t header_bytes = 32;
            size_t value_bytes = clamped_width * bytes;
            size_t int_arg_bytes = clamped_num_int_args * sizeof(int32_t);
            size_t total_bytes = header_bytes + value_bytes + int_arg_bytes;
            uint8_t buffer[4096];
            halide_assert(total_bytes <= 4096 && "Tracing packet too large");

            buffer[0] = event;
            buffer[1] = type_code;
            buffer[2] = bits;
            buffer[3] = clamped_width;
            buffer[4] = value_idx;
            buffer[5] = clamped_num_int_args;

            // Use up to 25 bytes for the function name
            int i = 6;
            for (; i < header_bytes-1; i++) {
                buffer[i] = func[i-6];
                if (buffer[i] == 0) break;
            }
            // Fill the rest with zeros
            for (; i < header_bytes; i++) {
                buffer[i] = 0;
            }

            // Next comes the value
            for (size_t i = 0; i < value_bytes; i++) {
                buffer[header_bytes + i] = ((uint8_t *)value)[i];
            }

            // Then the int args
            for (size_t i = 0; i < int_arg_bytes; i++) {
                buffer[header_bytes + value_bytes + i] = ((uint8_t *)int_args)[i];
            }

            size_t written = fwrite(&buffer[0], 1, total_bytes, halide_trace_file);
            halide_assert(written == total_bytes && "Can't write to trace file");

        } else {
            char buf[256];
            char *buf_ptr = &buf[0];
            char *buf_end = &buf[255];

            // Round up bits to 8, 16, 32, or 64
            int print_bits = 8;
            while (print_bits < bits) print_bits <<= 1;
            halide_assert(print_bits <= 64 && "Tracing bad type");

            // Otherwise, use halide_printf and a plain-text format
            const char *event_types[] = {"Load",
                                         "Store",
                                         "Begin realization",
                                         "End realization",
                                         "Produce",
                                         "Update",
                                         "Consume",
                                         "End consume"};

            // Only print out the value on stores and loads.
            bool print_value = (event < 2);

            if (buf_ptr < buf_end) {
                buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, "%s %s.%d[", event_types[event], func, value_idx);
            }
            if (width > 1) {
                buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, "<");
            }
            for (int i = 0; i < num_int_args && buf_ptr < buf_end; i++) {
                if (i > 0) {
                    if ((width > 1) && (i % width) == 0) {
                        buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, ">, <");
                    } else {
                        buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, ", ");
                    }
                }
                buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, "%d", int_args[i]);
            }
            if (buf_ptr < buf_end) {
                if (width > 1) {
                    buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, ">]");
                } else {
                    buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, "]");
                }
            }

            if (print_value) {
                if (buf_ptr < buf_end) {
                    if (width > 1) {
                        buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, " = <");
                    } else {
                        buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, " = ");
                    }
                }
                for (int i = 0; i < width && buf_ptr < buf_end; i++) {
                    if (i > 0) {
                        buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, ", ");
                    }
                    if (type_code == 0) {
                        if (print_bits == 8) {
                            buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, "%d", ((int8_t *)(value))[i]);
                        } else if (print_bits == 16) {
                            buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, "%d", ((int16_t *)(value))[i]);
                        } else if (print_bits == 32) {
                            buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, "%d", ((int32_t *)(value))[i]);
                        } else {
                            buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, "%d", (int32_t)((int64_t *)(value))[i]);
                        }
                    } else if (type_code == 1) {
                        if (print_bits == 8) {
                            buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, "%u", ((uint8_t *)(value))[i]);
                            if (buf_ptr > buf_end) buf_ptr = buf_end;
                        } else if (print_bits == 16) {
                            buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, "%u", ((uint16_t *)(value))[i]);
                        } else if (print_bits == 32) {
                            buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, "%u", ((uint32_t *)(value))[i]);
                        } else {
                            buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, "%u", (uint32_t)((uint64_t *)(value))[i]);
                        }
                    } else if (type_code == 2) {
                        halide_assert(print_bits >= 32 && "Tracing a bad type");
                        if (print_bits == 32) {
                            buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, "%f", ((float *)(value))[i]);
                        } else {
                            buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, "%f", ((double *)(value))[i]);
                        }
                    } else if (type_code == 3) {
                        buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, "%p", ((void **)(value))[i]);
                    }
                }
                if (width > 1 && buf_ptr < buf_end) {
                    buf_ptr += snprintf(buf_ptr, buf_end - buf_ptr, ">");
                }
            }

            halide_printf("%s\n", buf);
        }
    }
}

WEAK void halide_shutdown_trace() {
    if (halide_trace_file) {
        fclose(halide_trace_file);
        halide_trace_file = NULL;
    }
}

}
<|endoftext|>


<commit_before>// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <script/sigcache.h>

#include <pubkey.h>
#include <random.h>
#include <uint256.h>
#include <util/system.h>

#include <cuckoocache.h>
#include <boost/thread/shared_mutex.hpp>

namespace {
/**
 * Valid signature cache, to avoid doing expensive ECDSA signature checking
 * twice for every transaction (once when accepted into memory pool, and
 * again when accepted into the block chain)
 */
class CSignatureCache
{
private:
     //! Entries are SHA256(nonce || 'E' or 'S' || 31 zero bytes || signature hash || public key || signature):
    CSHA256 m_salted_hasher_ecdsa;
    CSHA256 m_salted_hasher_schnorr;
    typedef CuckooCache::cache<uint256, SignatureCacheHasher> map_type;
    map_type setValid;
    boost::shared_mutex cs_sigcache;

public:
    CSignatureCache()
    {
        uint256 nonce = GetRandHash();
        // We want the nonce to be 64 bytes long to force the hasher to process
        // this chunk, which makes later hash computations more efficient. We
        // just write our 32-byte entropy, and then pad with 'E' for ECDSA and
        // 'S' for Schnorr (followed by 0 bytes).
        static constexpr unsigned char PADDING_ECDSA[32] = {'E'};
        static constexpr unsigned char PADDING_SCHNORR[32] = {'S'};
        m_salted_hasher_ecdsa.Write(nonce.begin(), 32);
        m_salted_hasher_ecdsa.Write(PADDING_ECDSA, 32);
        m_salted_hasher_schnorr.Write(nonce.begin(), 32);
        m_salted_hasher_schnorr.Write(PADDING_SCHNORR, 32);
    }

    void
    ComputeEntryECDSA(uint256& entry, const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubkey)
    {
        CSHA256 hasher = m_salted_hasher_ecdsa;
        hasher.Write(hash.begin(), 32).Write(&pubkey[0], pubkey.size()).Write(&vchSig[0], vchSig.size()).Finalize(entry.begin());
    }

    void
    ComputeEntrySchnorr(uint256& entry, const uint256 &hash, Span<const unsigned char> sig, const XOnlyPubKey& pubkey)
    {
        CSHA256 hasher = m_salted_hasher_schnorr;
        hasher.Write(hash.begin(), 32).Write(&pubkey[0], pubkey.size()).Write(sig.data(), sig.size()).Finalize(entry.begin());
    }

    bool
    Get(const uint256& entry, const bool erase)
    {
        boost::shared_lock<boost::shared_mutex> lock(cs_sigcache);
        return setValid.contains(entry, erase);
    }

    void Set(uint256& entry)
    {
        boost::unique_lock<boost::shared_mutex> lock(cs_sigcache);
        setValid.insert(entry);
    }
    uint32_t setup_bytes(size_t n)
    {
        return setValid.setup_bytes(n);
    }
};

/* In previous versions of this code, signatureCache was a local static variable
 * in CachingTransactionSignatureChecker::VerifySignature.  We initialize
 * signatureCache outside of VerifySignature to avoid the atomic operation per
 * call overhead associated with local static variables even though
 * signatureCache could be made local to VerifySignature.
*/
static CSignatureCache signatureCache;
} // namespace

// To be called once in AppInitMain/BasicTestingSetup to initialize the
// signatureCache.
void InitSignatureCache()
{
    // nMaxCacheSize is unsigned. If -maxsigcachesize is set to zero,
    // setup_bytes creates the minimum possible cache (2 elements).
    size_t nMaxCacheSize = std::min(std::max((int64_t)0, gArgs.GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) / 2), MAX_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20);
    size_t nElems = signatureCache.setup_bytes(nMaxCacheSize);
    LogPrintf("Using %zu MiB out of %zu/2 requested for signature cache, able to store %zu elements\n",
            (nElems*sizeof(uint256)) >>20, (nMaxCacheSize*2)>>20, nElems);
}

bool CachingTransactionSignatureChecker::VerifyECDSASignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const
{
    uint256 entry;
    signatureCache.ComputeEntryECDSA(entry, sighash, vchSig, pubkey);
    if (signatureCache.Get(entry, !store))
        return true;
    if (!TransactionSignatureChecker::VerifyECDSASignature(vchSig, pubkey, sighash))
        return false;
    if (store)
        signatureCache.Set(entry);
    return true;
}

bool CachingTransactionSignatureChecker::VerifySchnorrSignature(Span<const unsigned char> sig, const XOnlyPubKey& pubkey, const uint256& sighash) const
{
    uint256 entry;
    signatureCache.ComputeEntrySchnorr(entry, sighash, sig, pubkey);
    if (signatureCache.Get(entry, !store)) return true;
    if (!TransactionSignatureChecker::VerifySchnorrSignature(sig, pubkey, sighash)) return false;
    if (store) signatureCache.Set(entry);
    return true;
}
<commit_msg>script: Make ComputeEntrySchnorr and ComputeEntryECDSA const to clarify contract<commit_after>// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <script/sigcache.h>

#include <pubkey.h>
#include <random.h>
#include <uint256.h>
#include <util/system.h>

#include <cuckoocache.h>
#include <boost/thread/shared_mutex.hpp>

namespace {
/**
 * Valid signature cache, to avoid doing expensive ECDSA signature checking
 * twice for every transaction (once when accepted into memory pool, and
 * again when accepted into the block chain)
 */
class CSignatureCache
{
private:
     //! Entries are SHA256(nonce || 'E' or 'S' || 31 zero bytes || signature hash || public key || signature):
    CSHA256 m_salted_hasher_ecdsa;
    CSHA256 m_salted_hasher_schnorr;
    typedef CuckooCache::cache<uint256, SignatureCacheHasher> map_type;
    map_type setValid;
    boost::shared_mutex cs_sigcache;

public:
    CSignatureCache()
    {
        uint256 nonce = GetRandHash();
        // We want the nonce to be 64 bytes long to force the hasher to process
        // this chunk, which makes later hash computations more efficient. We
        // just write our 32-byte entropy, and then pad with 'E' for ECDSA and
        // 'S' for Schnorr (followed by 0 bytes).
        static constexpr unsigned char PADDING_ECDSA[32] = {'E'};
        static constexpr unsigned char PADDING_SCHNORR[32] = {'S'};
        m_salted_hasher_ecdsa.Write(nonce.begin(), 32);
        m_salted_hasher_ecdsa.Write(PADDING_ECDSA, 32);
        m_salted_hasher_schnorr.Write(nonce.begin(), 32);
        m_salted_hasher_schnorr.Write(PADDING_SCHNORR, 32);
    }

    void
    ComputeEntryECDSA(uint256& entry, const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubkey) const
    {
        CSHA256 hasher = m_salted_hasher_ecdsa;
        hasher.Write(hash.begin(), 32).Write(&pubkey[0], pubkey.size()).Write(&vchSig[0], vchSig.size()).Finalize(entry.begin());
    }

    void
    ComputeEntrySchnorr(uint256& entry, const uint256 &hash, Span<const unsigned char> sig, const XOnlyPubKey& pubkey) const
    {
        CSHA256 hasher = m_salted_hasher_schnorr;
        hasher.Write(hash.begin(), 32).Write(&pubkey[0], pubkey.size()).Write(sig.data(), sig.size()).Finalize(entry.begin());
    }

    bool
    Get(const uint256& entry, const bool erase)
    {
        boost::shared_lock<boost::shared_mutex> lock(cs_sigcache);
        return setValid.contains(entry, erase);
    }

    void Set(uint256& entry)
    {
        boost::unique_lock<boost::shared_mutex> lock(cs_sigcache);
        setValid.insert(entry);
    }
    uint32_t setup_bytes(size_t n)
    {
        return setValid.setup_bytes(n);
    }
};

/* In previous versions of this code, signatureCache was a local static variable
 * in CachingTransactionSignatureChecker::VerifySignature.  We initialize
 * signatureCache outside of VerifySignature to avoid the atomic operation per
 * call overhead associated with local static variables even though
 * signatureCache could be made local to VerifySignature.
*/
static CSignatureCache signatureCache;
} // namespace

// To be called once in AppInitMain/BasicTestingSetup to initialize the
// signatureCache.
void InitSignatureCache()
{
    // nMaxCacheSize is unsigned. If -maxsigcachesize is set to zero,
    // setup_bytes creates the minimum possible cache (2 elements).
    size_t nMaxCacheSize = std::min(std::max((int64_t)0, gArgs.GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) / 2), MAX_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20);
    size_t nElems = signatureCache.setup_bytes(nMaxCacheSize);
    LogPrintf("Using %zu MiB out of %zu/2 requested for signature cache, able to store %zu elements\n",
            (nElems*sizeof(uint256)) >>20, (nMaxCacheSize*2)>>20, nElems);
}

bool CachingTransactionSignatureChecker::VerifyECDSASignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const
{
    uint256 entry;
    signatureCache.ComputeEntryECDSA(entry, sighash, vchSig, pubkey);
    if (signatureCache.Get(entry, !store))
        return true;
    if (!TransactionSignatureChecker::VerifyECDSASignature(vchSig, pubkey, sighash))
        return false;
    if (store)
        signatureCache.Set(entry);
    return true;
}

bool CachingTransactionSignatureChecker::VerifySchnorrSignature(Span<const unsigned char> sig, const XOnlyPubKey& pubkey, const uint256& sighash) const
{
    uint256 entry;
    signatureCache.ComputeEntrySchnorr(entry, sighash, sig, pubkey);
    if (signatureCache.Get(entry, !store)) return true;
    if (!TransactionSignatureChecker::VerifySchnorrSignature(sig, pubkey, sighash)) return false;
    if (store) signatureCache.Set(entry);
    return true;
}
<|endoftext|>


<commit_before>/* Searcher.hpp
 *
 * Kubo Ryosuke
 */

#ifndef SUNFISH_SEARCH_SEARCHER_HPP__
#define SUNFISH_SEARCH_SEARCHER_HPP__

#include "search/eval/Score.hpp"
#include "search/SearchConfig.hpp"
#include "search/SearchInfo.hpp"
#include "search/SearchResult.hpp"
#include "search/SearchHandler.hpp"
#include "search/time/TimeManager.hpp"
#include "search/tree/Tree.hpp"
#include "search/tree/NodeStat.hpp"
#include "search/tree/Worker.hpp"
#include "search/tt/TT.hpp"
#include "search/history/History.hpp"
#include "common/math/Random.hpp"
#include "common/time/Timer.hpp"
#include <memory>
#include <atomic>
#include <array>
#include <climits>

namespace sunfish {

class Position;
class Move;
struct Record;
class Evaluator;

class Searcher {
public:

  static CONSTEXPR_CONST int Depth1Ply = 8;
  static CONSTEXPR_CONST int DepthInfinity = INT_MAX;

  static void initialize();

  Searcher();

  Searcher(std::shared_ptr<Evaluator> evaluator);

  void clean();

  void search(const Position& pos,
              int depth,
              Record* record = nullptr) {
    search(pos,
           depth,
           -Score::infinity(),
           Score::infinity(),
           record);
  }

  void search(const Position& pos,
              int depth,
              Score alpha,
              Score beta,
              Record* record = nullptr);

  /**
   * iterative deepening search.
   */
  void idsearch(const Position& pos,
                int depth,
                Record* record = nullptr);

  const SearchConfig& getConfig() const {
    return config_;
  }

  void setConfig(const SearchConfig& config) {
    config_ = config;
  }

  const SearchResult& getResult() const {
    return result_;
  }

  const SearchInfo& getInfo() const {
    return info_;
  }

  void interrupt() {
    interrupted_ = true;
  }

  void setHandler(SearchHandler* handler) {
    handler_ = handler;
  }

  float ttUsageRates() const {
    return tt_.usageRates();
  }

  void ttResizeMB(unsigned mebiBytes) {
    tt_.resizeMB(mebiBytes);
  }

private:

  void onSearchStarted();

  void updateInfo();

  bool aspsearch(Tree& tree,
                 int depth);

  Score search(Tree& tree,
               int depth,
               Score alpha,
               Score beta,
               NodeStat nodeStat);

  Score quies(Tree& tree,
              int qply,
              Score alpha,
              Score beta);

  template <bool isRootNode>
  void generateMoves(Tree& tree);

  Move nextMove(Tree& tree);

  void generateMovesOnQuies(Tree& tree,
                            int qply,
                            Score alpha);

  Move nextMoveOnQuies(Node& node);

  void sortMovesOnHistory(Tree& tree);

  void sortRootMoves(Tree& tree);

  void storePV(Tree& tree,
               const PV& pv,
               unsigned ply,
               Score score);

  bool isInterrupted() const {
    if (interrupted_) {
      return true;
    }

    if (timer_.elapsedMs() >= config_.maximumTimeMs) {
      return true;
    }

    return false;
  }

  SearchConfig config_;
  SearchResult result_;
  SearchInfo info_;

  std::atomic<bool> interrupted_;
  Timer timer_;

  std::shared_ptr<Evaluator> evaluator_;

  TT tt_;

  History history_;

  Tree mainThreadTree_;
  Worker mainThreadWorker_;

  Random random_;

  TimeManager timeManager_;

  SearchHandler* handler_;

};

} // namespace sunfish

#endif // SUNFISH_SEARCH_SEARCHER_HPP__
<commit_msg>Modify consistency level<commit_after>/* Searcher.hpp
 *
 * Kubo Ryosuke
 */

#ifndef SUNFISH_SEARCH_SEARCHER_HPP__
#define SUNFISH_SEARCH_SEARCHER_HPP__

#include "search/eval/Score.hpp"
#include "search/SearchConfig.hpp"
#include "search/SearchInfo.hpp"
#include "search/SearchResult.hpp"
#include "search/SearchHandler.hpp"
#include "search/time/TimeManager.hpp"
#include "search/tree/Tree.hpp"
#include "search/tree/NodeStat.hpp"
#include "search/tree/Worker.hpp"
#include "search/tt/TT.hpp"
#include "search/history/History.hpp"
#include "common/math/Random.hpp"
#include "common/time/Timer.hpp"
#include <memory>
#include <atomic>
#include <array>
#include <climits>

namespace sunfish {

class Position;
class Move;
struct Record;
class Evaluator;

class Searcher {
public:

  static CONSTEXPR_CONST int Depth1Ply = 8;
  static CONSTEXPR_CONST int DepthInfinity = INT_MAX;

  static void initialize();

  Searcher();

  Searcher(std::shared_ptr<Evaluator> evaluator);

  void clean();

  void search(const Position& pos,
              int depth,
              Record* record = nullptr) {
    search(pos,
           depth,
           -Score::infinity(),
           Score::infinity(),
           record);
  }

  void search(const Position& pos,
              int depth,
              Score alpha,
              Score beta,
              Record* record = nullptr);

  /**
   * iterative deepening search.
   */
  void idsearch(const Position& pos,
                int depth,
                Record* record = nullptr);

  const SearchConfig& getConfig() const {
    return config_;
  }

  void setConfig(const SearchConfig& config) {
    config_ = config;
  }

  const SearchResult& getResult() const {
    return result_;
  }

  const SearchInfo& getInfo() const {
    return info_;
  }

  void interrupt() {
    interrupted_ = true;
  }

  void setHandler(SearchHandler* handler) {
    handler_ = handler;
  }

  float ttUsageRates() const {
    return tt_.usageRates();
  }

  void ttResizeMB(unsigned mebiBytes) {
    tt_.resizeMB(mebiBytes);
  }

private:

  void onSearchStarted();

  void updateInfo();

  bool aspsearch(Tree& tree,
                 int depth);

  Score search(Tree& tree,
               int depth,
               Score alpha,
               Score beta,
               NodeStat nodeStat);

  Score quies(Tree& tree,
              int qply,
              Score alpha,
              Score beta);

  template <bool isRootNode>
  void generateMoves(Tree& tree);

  Move nextMove(Tree& tree);

  void generateMovesOnQuies(Tree& tree,
                            int qply,
                            Score alpha);

  Move nextMoveOnQuies(Node& node);

  void sortMovesOnHistory(Tree& tree);

  void sortRootMoves(Tree& tree);

  void storePV(Tree& tree,
               const PV& pv,
               unsigned ply,
               Score score);

  bool isInterrupted() const {
    if (interrupted_.load(std::memory_order_relaxed)) {
      return true;
    }

    if (timer_.elapsedMs() >= config_.maximumTimeMs) {
      return true;
    }

    return false;
  }

  SearchConfig config_;
  SearchResult result_;
  SearchInfo info_;

  std::atomic_bool interrupted_;
  Timer timer_;

  std::shared_ptr<Evaluator> evaluator_;

  TT tt_;

  History history_;

  Tree mainThreadTree_;
  Worker mainThreadWorker_;

  Random random_;

  TimeManager timeManager_;

  SearchHandler* handler_;

};

} // namespace sunfish

#endif // SUNFISH_SEARCH_SEARCHER_HPP__
<|endoftext|>