<commit_before>/** \file
 * Implements generally useful functions.
 */

#include <type_traits> /* std::underlying_type */
#include <string>

#include <cstdio>      /* snprintf() */

#ifndef _POWERDXX_UTILITY_HPP_
#define _POWERDXX_UTILITY_HPP_

/**
 * A collection of generally useful functions.
 */
namespace utility {

/**
 * Like sizeof(), but it returns the number of elements an array consists
 * of instead of the number of bytes.
 *
 * @tparam T,Count
 *	The type and number of array elements
 * @return
 *	The number of array entries
 */
template <typename T, size_t Count>
constexpr size_t countof(T (&)[Count]) { return Count; }

/**
 * Contains literals.
 */
namespace literals {

/**
 * A string literal operator equivalent to the `operator "" s` literal
 * provided by C++14 in \<string\>.
 *
 * @param op
 *	The raw string to turn into an std::string object
 * @param size
 *	The size of the raw string
 * @return
 *	An std::string instance
 */
inline std::string operator "" _s(char const * const op, size_t const size) {
	return {op, size};
}

} /* namespace literals */

/**
 * This is a safeguard against accidentally using sprintf().
 *
 * Using it triggers a static_assert(), preventing compilation.
 *
 * @tparam Args
 *	Catch all arguments
 */
template <typename... Args>
inline void sprintf(Args...) {
	/* Assert depends on Args so it can only be determined if
	 * the function is actually instantiated. */
	static_assert(sizeof...(Args) && false,
	              "Use of sprintf() is unsafe, use sprintf_safe() instead");
}

/**
 * A wrapper around snprintf() that automatically pulls in the
 * destination buffer size.
 *
 * @tparam Size
 *	The destination buffer size
 * @tparam Args
 *	The types of the arguments
 * @param dst
 *	A reference to the destination buffer
 * @param format
 *	A printf style formatting string
 * @param args
 *	The printf arguments
 * @return
 *	The number of characters in the resulting string, regardless of the
 *	available space
 */
template <size_t Size, typename... Args>
inline int sprintf_safe(char (& dst)[Size], char const * const format,
                   Args const... args) {
	return snprintf(dst, Size, format, args...);
}

/**
 * Casts an enum to its underlying value.
 *
 * @tparam ET,VT
 *	The enum and value type
 * @param op
 *	The operand to convert
 * @return
 *	The integer representation of the operand
 */
template <class ET, typename VT = typename std::underlying_type<ET>::type>
constexpr VT to_value(ET const op) {
	return static_cast<VT>(op);
}

/**
 * A formatting wrapper around string literals.
 *
 * Overloads operator (), which treats the string as a printf formatting
 * string, the arguments represent the data to format.
 *
 * In combination with the literal _fmt, it can be used like this:
 *
 * ~~~ c++
 * std::cout << "%-15.15s %#018p\n"_fmt("Address:", this);
 * ~~~
 *
 * @tparam BufSize
 *	The buffer size for formatting, resulting strings cannot
 *	grow beyond `BufSize - 1`
 */
template <size_t BufSize>
class Formatter {
	private:
	/**
	 * Pointer to the string literal.
	 */
	char const * const fmt;

	public:
	/**
	 * Construct from string literal.
	 */
	constexpr Formatter(char const * const fmt) : fmt{fmt} {}

	/**
	 * Returns a formatted string.
	 *
	 * @tparam ArgTs
	 *	Variadic argument types
	 * @param args
	 *	Variadic arguments
	 * @return
	 *	An std::string formatted according to fmt
	 */
	template <typename... ArgTs>
	std::string operator ()(ArgTs const &... args) const {
		char buf[BufSize];
		auto count = sprintf_safe(buf, this->fmt, args...);
		if (count < 0) {
			/* encoding error */
			return {};
		} else if (static_cast<size_t>(count) >= BufSize) {
			/* does not fit into buffer */
			return {buf, BufSize - 1};
		}
		return {buf, static_cast<size_t>(count)};
	}
};

namespace literals {
/**
 * Literal to convert a string literal to a Formatter instance.
 *
 * @param fmt
 *	A printf style format string
 * @param const
 *	Unused
 * @return
 *	A Formatter instance
 */
constexpr Formatter<16384> operator "" _fmt(char const * const fmt, size_t const) {
	return {fmt};
}
} /* namespace literals */

} /* namespace utility */

#endif /* _POWERDXX_UTILITY_HPP_ */
<commit_msg>Add utility::Sum, an addition only value container<commit_after>/** \file
 * Implements generally useful functions.
 */

#include <type_traits> /* std::underlying_type */
#include <string>

#include <cstdio>      /* snprintf() */

#ifndef _POWERDXX_UTILITY_HPP_
#define _POWERDXX_UTILITY_HPP_

/**
 * A collection of generally useful functions.
 */
namespace utility {

/**
 * Like sizeof(), but it returns the number of elements an array consists
 * of instead of the number of bytes.
 *
 * @tparam T,Count
 *	The type and number of array elements
 * @return
 *	The number of array entries
 */
template <typename T, size_t Count>
constexpr size_t countof(T (&)[Count]) { return Count; }

/**
 * Contains literals.
 */
namespace literals {

/**
 * A string literal operator equivalent to the `operator "" s` literal
 * provided by C++14 in \<string\>.
 *
 * @param op
 *	The raw string to turn into an std::string object
 * @param size
 *	The size of the raw string
 * @return
 *	An std::string instance
 */
inline std::string operator "" _s(char const * const op, size_t const size) {
	return {op, size};
}

} /* namespace literals */

/**
 * This is a safeguard against accidentally using sprintf().
 *
 * Using it triggers a static_assert(), preventing compilation.
 *
 * @tparam Args
 *	Catch all arguments
 */
template <typename... Args>
inline void sprintf(Args...) {
	/* Assert depends on Args so it can only be determined if
	 * the function is actually instantiated. */
	static_assert(sizeof...(Args) && false,
	              "Use of sprintf() is unsafe, use sprintf_safe() instead");
}

/**
 * A wrapper around snprintf() that automatically pulls in the
 * destination buffer size.
 *
 * @tparam Size
 *	The destination buffer size
 * @tparam Args
 *	The types of the arguments
 * @param dst
 *	A reference to the destination buffer
 * @param format
 *	A printf style formatting string
 * @param args
 *	The printf arguments
 * @return
 *	The number of characters in the resulting string, regardless of the
 *	available space
 */
template <size_t Size, typename... Args>
inline int sprintf_safe(char (& dst)[Size], char const * const format,
                   Args const... args) {
	return snprintf(dst, Size, format, args...);
}

/**
 * Casts an enum to its underlying value.
 *
 * @tparam ET,VT
 *	The enum and value type
 * @param op
 *	The operand to convert
 * @return
 *	The integer representation of the operand
 */
template <class ET, typename VT = typename std::underlying_type<ET>::type>
constexpr VT to_value(ET const op) {
	return static_cast<VT>(op);
}

/**
 * A formatting wrapper around string literals.
 *
 * Overloads operator (), which treats the string as a printf formatting
 * string, the arguments represent the data to format.
 *
 * In combination with the literal _fmt, it can be used like this:
 *
 * ~~~ c++
 * std::cout << "%-15.15s %#018p\n"_fmt("Address:", this);
 * ~~~
 *
 * @tparam BufSize
 *	The buffer size for formatting, resulting strings cannot
 *	grow beyond `BufSize - 1`
 */
template <size_t BufSize>
class Formatter {
	private:
	/**
	 * Pointer to the string literal.
	 */
	char const * const fmt;

	public:
	/**
	 * Construct from string literal.
	 */
	constexpr Formatter(char const * const fmt) : fmt{fmt} {}

	/**
	 * Returns a formatted string.
	 *
	 * @tparam ArgTs
	 *	Variadic argument types
	 * @param args
	 *	Variadic arguments
	 * @return
	 *	An std::string formatted according to fmt
	 */
	template <typename... ArgTs>
	std::string operator ()(ArgTs const &... args) const {
		char buf[BufSize];
		auto count = sprintf_safe(buf, this->fmt, args...);
		if (count < 0) {
			/* encoding error */
			return {};
		} else if (static_cast<size_t>(count) >= BufSize) {
			/* does not fit into buffer */
			return {buf, BufSize - 1};
		}
		return {buf, static_cast<size_t>(count)};
	}
};

namespace literals {
/**
 * Literal to convert a string literal to a Formatter instance.
 *
 * @param fmt
 *	A printf style format string
 * @param const
 *	Unused
 * @return
 *	A Formatter instance
 */
constexpr Formatter<16384> operator "" _fmt(char const * const fmt, size_t const) {
	return {fmt};
}
} /* namespace literals */

/**
 * A simple value container only allowing += and copy assignment.
 *
 * @tparam T
 *	The value type
 */
template <typename T>
class Sum {
	private:
	/**
	 * The sum of values accumulated.
	 */
	T value;

	public:
	/**
	 * Construct from an initial value.
	 *
	 * @param value
	 *	The initial value
	 */
	explicit constexpr Sum(T const & value) : value{value} {}

	/**
	 * Default construct.
	 */
	constexpr Sum() : Sum{0} {}

	/**
	 * Returns the current sum of values.
	 *
	 * @return
	 *	The sum of values by const reference
	 */
	constexpr operator T const &() const {
		return this->value;
	}

	/**
	 * Add a value to the sum.
	 *
	 * @param value
	 *	The value to add to the current sum
	 * @return
	 *	A self reference
	 */
	constexpr Sum & operator +=(T const & value) {
		this->value += value;
		return *this;
	}
};

} /* namespace utility */

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


<commit_before>/* SevSeg Library

 Copyright 2014 Dean Reading

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.

 This library allows an arduino to easily display numbers in decimal format on
 a 4 digit 7-segment display without a separate 7-segment display controller.

 Direct any questions or suggestions to deanreading@hotmail.com

 See the included documentation file for instructions.

 CHANGELOG

 Version 3.0 - November 2014
 Library re-design. A display with any number of digits can be used.
 Floats are supported. Support for using transistors for switching.
 Much more user friendly. No backwards compatibility.
 Uploaded to GitHub to simplify any further development.
 Version 2.3; Allows for brightness control.
 Version 2.2; Allows 1, 2 or 3 digit displays to be used.
 Version 2.1; Includes a bug fix.
 Version 2.0; Now works for any digital pin arrangement.
 Supports both common anode and common cathode displays.
 */

#include "SevSeg.h"

#define BLANK 10 // Must match with 'digitCodeMap', defined in 'setDigitCodes'
#define DASH 11

const long SevSeg::powersOf10[] = {
  1, // 10^0
  10,
  100,
  1000,
  10000,
  100000,
  1000000,
  10000000,
  100000000,
  1000000000}; // 10^9

// SevSeg
/******************************************************************************/
SevSeg::SevSeg()
{
  // Initial value
  ledOnTime = 2000; // Corresponds to a brightness of 100
  numDigits = 0;
}

// begin
/******************************************************************************/
// Saves the input pin numbers to the class and sets up the pins to be used.

void SevSeg::begin(byte hardwareConfig, byte numDigitsIn,
                  byte digitPinsIn[], byte segmentPinsIn[]) {
  numDigits = numDigitsIn;

  digitCodes = new byte[numDigits];
  digitPins = new byte[numDigits];

  switch (hardwareConfig){

  case 0: // Common cathode
    digitOn = LOW;
    segmentOn = HIGH;
    break;

  case 1: // Common anode
    digitOn = HIGH;
    segmentOn = LOW;
    break;

  case 2: // With active-high, low-side switches (most commonly N-type FETs)
    digitOn = HIGH;
    segmentOn = HIGH;
    break;

  case 3: // With active low, high side switches (most commonly P-type FETs)
    digitOn = LOW;
    segmentOn = LOW;
    break;
  }

  digitOff = !digitOn;
  segmentOff = !segmentOff;

  // Save the input pin numbers to library variables
  for (byte segmentNum = 0 ; segmentNum < 8 ; segmentNum++) {
    segmentPins[segmentNum] = segmentPinsIn[segmentNum];
  }

  for (byte digitNum = 0 ; digitNum < numDigits ; digitNum++) {
    digitPins[digitNum] = digitPinsIn[digitNum];
  }

  // Set the pins as outputs, and turn them off
  for (byte digit=0 ; digit < numDigits ; digit++) {
    pinMode(digitPins[digit], OUTPUT);
    digitalWrite(digitPins[digit], digitOff);
  }

  for (byte segmentNum=0 ; segmentNum < 8 ; segmentNum++) {
    pinMode(segmentPins[segmentNum], OUTPUT);
    digitalWrite(segmentPins[segmentNum], segmentOff);
  }

  setNewNum(0,0); // Initialise the number displayed to 0
}

// refreshDisplay
/******************************************************************************/
// Flashes the output on the seven segment display.
// This is achieved by cycling through all segments and digits, turning the
// required segments on as specified by the array 'digitCodes'.

void SevSeg::refreshDisplay(){
  for (byte segmentNum=0 ; segmentNum < 8 ; segmentNum++) {

    // Illuminate the required digits for this segment
    digitalWrite(segmentPins[segmentNum], segmentOn);
    for (byte digitNum=0 ; digitNum < numDigits ; digitNum++){
      if (digitCodes[digitNum] & (1 << segmentNum)) { // Check a single bit
        digitalWrite(digitPins[digitNum], digitOn);
      }
    }

    //Wait with lights on (to increase brightness)
    delayMicroseconds(ledOnTime);

    //Turn all segments off
    for (byte digitNum=0 ; digitNum < numDigits ; digitNum++){
      digitalWrite(digitPins[digitNum], digitOff);
    }
    digitalWrite(segmentPins[segmentNum], segmentOff);
  }
}

// setBrightness
/******************************************************************************/

void SevSeg::setBrightness(int brightness){
  brightness = constrain(brightness, 0, 100);
  ledOnTime = map(brightness, 0, 100, 1, 2000);
}

// setNumber
/******************************************************************************/
// This function only receives the input and passes it to 'setNewNum'.
// It is overloaded for all number data types, so that floats can be handled
// correctly.

void SevSeg::setNumber(long numToShow, byte decPlaces) //long
{
  setNewNum(numToShow, decPlaces);
}

void SevSeg::setNumber(unsigned long numToShow, byte decPlaces) //unsigned long
{
  setNewNum(numToShow, decPlaces);
}

void SevSeg::setNumber(int numToShow, byte decPlaces) //int
{
  setNewNum(numToShow, decPlaces);
}

void SevSeg::setNumber(unsigned int numToShow, byte decPlaces) //unsigned int
{
  setNewNum(numToShow, decPlaces);
}

void SevSeg::setNumber(char numToShow, byte decPlaces) //char
{
  setNewNum(numToShow, decPlaces);
}

void SevSeg::setNumber(byte numToShow, byte decPlaces) //byte
{
  setNewNum(numToShow, decPlaces);
}

void SevSeg::setNumber(float numToShow, byte decPlaces) //float
{
  numToShow = numToShow * powersOf10[decPlaces];
  // Modify the number so that it is rounded to an integer correctly
  numToShow += (numToShow >= 0) ? 0.5f : -0.5f;
  setNewNum(numToShow, decPlaces);
}

// setNewNum
/******************************************************************************/
// Changes the number that will be displayed.

void SevSeg::setNewNum(long numToShow, byte decPlaces){
  byte digits[numDigits];
  findDigits(numToShow, decPlaces, digits);
  setDigitCodes(digits, decPlaces);
}

// findDigits
/******************************************************************************/
// Decides what each digit will display.
// Enforces the upper and lower limits on the number to be displayed.

void SevSeg::findDigits(long numToShow, byte decPlaces, byte digits[]) {
  static const int maxNum = powersOf10[numDigits] - 1;
  static const int minNum = -(powersOf10[numDigits - 1] - 1);

  // If the number is out of range, just display dashes
  if (numToShow > maxNum || numToShow < minNum) {
    for (byte digitNum = 0 ; digitNum < numDigits ; digitNum++){
      digits[digitNum] = DASH;
    }
  }
  else{
    byte digitNum = 0;

    // Convert all number to positive values
    if (numToShow < 0) {
      digits[0] = DASH;
      digitNum = 1; // Skip the first iteration
      numToShow = -numToShow;
    }

    // Find all digits for the base 10 representation, starting with the most
    // significant digit
    for ( ; digitNum < numDigits ; digitNum++){
      long factor = powersOf10[numDigits - 1 - digitNum];
      digits[digitNum] = numToShow / factor;
      numToShow -= digits[digitNum] * factor;
    }

    // Find unnnecessary leading zeros and set them to BLANK
    for (digitNum = 0 ; digitNum < (numDigits - 1 - decPlaces) ; digitNum++){
      if (digits[digitNum] == 0) {
        digits[digitNum] = BLANK;
      }
      // Exit once the first non-zero number is encountered
      else if (digits[digitNum] <= 9) {
        break;
      }
    }

  }
}

// setDigitCodes
/******************************************************************************/
// Sets the 'digitCodes' that are required to display the input numbers

void SevSeg::setDigitCodes(byte digits[], byte decPlaces) {

  // The codes below indicate which segments must be illuminated to display
  // each number.
  static const byte digitCodeMap[] = {
    B00111111, // 0
    B00000110, // 1
    B01011011, // 2
    B01001111, // 3
    B01100110, // 4
    B01101101, // 5
    B01111101, // 6
    B00000111, // 7
    B01111111, // 8
    B01101111, // 9
    B00000000, // BLANK
    B01000000    }; // DASH

  // Set the digitCode for each digit in the display
  for (byte digitNum = 0 ; digitNum < numDigits ; digitNum++) {
    digitCodes[digitNum] = digitCodeMap[digits[digitNum]];
  }

  // Set the decimal place segment
  digitCodes[numDigits - 1 - decPlaces] |= B10000000;
}

/// END ///
<commit_msg>Minor change to deal with bad input<commit_after>/* SevSeg Library

 Copyright 2014 Dean Reading

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.

 This library allows an arduino to easily display numbers in decimal format on
 a 4 digit 7-segment display without a separate 7-segment display controller.

 Direct any questions or suggestions to deanreading@hotmail.com

 See the included documentation file for instructions.

 CHANGELOG

 Version 3.0 - November 2014
 Library re-design. A display with any number of digits can be used.
 Floats are supported. Support for using transistors for switching.
 Much more user friendly. No backwards compatibility.
 Uploaded to GitHub to simplify any further development.
 Version 2.3; Allows for brightness control.
 Version 2.2; Allows 1, 2 or 3 digit displays to be used.
 Version 2.1; Includes a bug fix.
 Version 2.0; Now works for any digital pin arrangement.
 Supports both common anode and common cathode displays.
 */

#include "SevSeg.h"

#define BLANK 10 // Must match with 'digitCodeMap', defined in 'setDigitCodes'
#define DASH 11

const long SevSeg::powersOf10[] = {
  1, // 10^0
  10,
  100,
  1000,
  10000,
  100000,
  1000000,
  10000000,
  100000000,
  1000000000}; // 10^9

// SevSeg
/******************************************************************************/
SevSeg::SevSeg()
{
  // Initial value
  ledOnTime = 2000; // Corresponds to a brightness of 100
  numDigits = 0;
}

// begin
/******************************************************************************/
// Saves the input pin numbers to the class and sets up the pins to be used.

void SevSeg::begin(byte hardwareConfig, byte numDigitsIn,
                  byte digitPinsIn[], byte segmentPinsIn[]) {
  numDigits = numDigitsIn;

  digitCodes = new byte[numDigits];
  digitPins = new byte[numDigits];

  switch (hardwareConfig){

  case 0: // Common cathode
    digitOn = LOW;
    segmentOn = HIGH;
    break;

  case 1: // Common anode
    digitOn = HIGH;
    segmentOn = LOW;
    break;

  case 2: // With active-high, low-side switches (most commonly N-type FETs)
    digitOn = HIGH;
    segmentOn = HIGH;
    break;

  case 3: // With active low, high side switches (most commonly P-type FETs)
    digitOn = LOW;
    segmentOn = LOW;
    break;
  }

  digitOff = !digitOn;
  segmentOff = !segmentOff;

  // Save the input pin numbers to library variables
  for (byte segmentNum = 0 ; segmentNum < 8 ; segmentNum++) {
    segmentPins[segmentNum] = segmentPinsIn[segmentNum];
  }

  for (byte digitNum = 0 ; digitNum < numDigits ; digitNum++) {
    digitPins[digitNum] = digitPinsIn[digitNum];
  }

  // Set the pins as outputs, and turn them off
  for (byte digit=0 ; digit < numDigits ; digit++) {
    pinMode(digitPins[digit], OUTPUT);
    digitalWrite(digitPins[digit], digitOff);
  }

  for (byte segmentNum=0 ; segmentNum < 8 ; segmentNum++) {
    pinMode(segmentPins[segmentNum], OUTPUT);
    digitalWrite(segmentPins[segmentNum], segmentOff);
  }

  setNewNum(0,0); // Initialise the number displayed to 0
}

// refreshDisplay
/******************************************************************************/
// Flashes the output on the seven segment display.
// This is achieved by cycling through all segments and digits, turning the
// required segments on as specified by the array 'digitCodes'.

void SevSeg::refreshDisplay(){
  for (byte segmentNum=0 ; segmentNum < 8 ; segmentNum++) {

    // Illuminate the required digits for this segment
    digitalWrite(segmentPins[segmentNum], segmentOn);
    for (byte digitNum=0 ; digitNum < numDigits ; digitNum++){
      if (digitCodes[digitNum] & (1 << segmentNum)) { // Check a single bit
        digitalWrite(digitPins[digitNum], digitOn);
      }
    }

    //Wait with lights on (to increase brightness)
    delayMicroseconds(ledOnTime);

    //Turn all segments off
    for (byte digitNum=0 ; digitNum < numDigits ; digitNum++){
      digitalWrite(digitPins[digitNum], digitOff);
    }
    digitalWrite(segmentPins[segmentNum], segmentOff);
  }
}

// setBrightness
/******************************************************************************/

void SevSeg::setBrightness(int brightness){
  brightness = constrain(brightness, 0, 100);
  ledOnTime = map(brightness, 0, 100, 1, 2000);
}

// setNumber
/******************************************************************************/
// This function only receives the input and passes it to 'setNewNum'.
// It is overloaded for all number data types, so that floats can be handled
// correctly.

void SevSeg::setNumber(long numToShow, byte decPlaces) //long
{
  setNewNum(numToShow, decPlaces);
}

void SevSeg::setNumber(unsigned long numToShow, byte decPlaces) //unsigned long
{
  setNewNum(numToShow, decPlaces);
}

void SevSeg::setNumber(int numToShow, byte decPlaces) //int
{
  setNewNum(numToShow, decPlaces);
}

void SevSeg::setNumber(unsigned int numToShow, byte decPlaces) //unsigned int
{
  setNewNum(numToShow, decPlaces);
}

void SevSeg::setNumber(char numToShow, byte decPlaces) //char
{
  setNewNum(numToShow, decPlaces);
}

void SevSeg::setNumber(byte numToShow, byte decPlaces) //byte
{
  setNewNum(numToShow, decPlaces);
}

void SevSeg::setNumber(float numToShow, byte decPlaces) //float
{
  numToShow = numToShow * powersOf10[decPlaces];
  // Modify the number so that it is rounded to an integer correctly
  numToShow += (numToShow >= 0) ? 0.5f : -0.5f;
  setNewNum(numToShow, decPlaces);
}

// setNewNum
/******************************************************************************/
// Changes the number that will be displayed.

void SevSeg::setNewNum(long numToShow, byte decPlaces){
  byte digits[numDigits];
  findDigits(numToShow, decPlaces, digits);
  setDigitCodes(digits, decPlaces);
}

// findDigits
/******************************************************************************/
// Decides what each digit will display.
// Enforces the upper and lower limits on the number to be displayed.

void SevSeg::findDigits(long numToShow, byte decPlaces, byte digits[]) {
  static const int maxNum = powersOf10[numDigits] - 1;
  static const int minNum = -(powersOf10[numDigits - 1] - 1);

  // If the number is out of range, just display dashes
  if (numToShow > maxNum || numToShow < minNum) {
    for (byte digitNum = 0 ; digitNum < numDigits ; digitNum++){
      digits[digitNum] = DASH;
    }
  }
  else{
    byte digitNum = 0;

    // Convert all number to positive values
    if (numToShow < 0) {
      digits[0] = DASH;
      digitNum = 1; // Skip the first iteration
      numToShow = -numToShow;
    }

    // Find all digits for the base 10 representation, starting with the most
    // significant digit
    for ( ; digitNum < numDigits ; digitNum++){
      long factor = powersOf10[numDigits - 1 - digitNum];
      digits[digitNum] = numToShow / factor;
      numToShow -= digits[digitNum] * factor;
    }

    // Find unnnecessary leading zeros and set them to BLANK
    for (digitNum = 0 ; digitNum < (numDigits - 1 - decPlaces) ; digitNum++){
      if (digits[digitNum] == 0) {
        digits[digitNum] = BLANK;
      }
      // Exit once the first non-zero number is encountered
      else if (digits[digitNum] <= 9) {
        break;
      }
    }

  }
}

// setDigitCodes
/******************************************************************************/
// Sets the 'digitCodes' that are required to display the input numbers

void SevSeg::setDigitCodes(byte digits[], byte decPlaces) {

  // The codes below indicate which segments must be illuminated to display
  // each number.
  static const byte digitCodeMap[] = {
    B00111111, // 0
    B00000110, // 1
    B01011011, // 2
    B01001111, // 3
    B01100110, // 4
    B01101101, // 5
    B01111101, // 6
    B00000111, // 7
    B01111111, // 8
    B01101111, // 9
    B00000000, // BLANK
    B01000000    }; // DASH

  // Set the digitCode for each digit in the display
  for (byte digitNum = 0 ; digitNum < numDigits ; digitNum++) {
    digitCodes[digitNum] = digitCodeMap[digits[digitNum]];
    // Set the decimal place segment
    if (digitNum == numDigits - 1 - decPlaces) {
     digitCodes[digitNum] |= B10000000;
    }
  }
}

/// END ///
<|endoftext|>


<commit_before>/** \file
 * Implements generally useful functions.
 */

#include <type_traits> /* std::underlying_type */
#include <string>

#include <cstdio>      /* snprintf() */

#ifndef _POWERDXX_UTILITY_HPP_
#define _POWERDXX_UTILITY_HPP_

/**
 * A collection of generally useful functions.
 */
namespace utility {

/**
 * Like sizeof(), but it returns the number of elements an array consists
 * of instead of the number of bytes.
 *
 * @tparam T,Count
 *	The type and number of array elements
 * @return
 *	The number of array entries
 */
template <typename T, size_t Count>
constexpr size_t countof(T (&)[Count]) { return Count; }

/**
 * Contains literals.
 */
namespace literals {

/**
 * A string literal operator equivalent to the `operator "" s` literal
 * provided by C++14 in \<string\>.
 *
 * @param op
 *	The raw string to turn into an std::string object
 * @param size
 *	The size of the raw string
 * @return
 *	An std::string instance
 */
inline std::string operator "" _s(char const * const op, size_t const size) {
	return {op, size};
}

} /* namespace literals */

/**
 * This is a safeguard against accidentally using sprintf().
 *
 * Using it triggers a static_assert(), preventing compilation.
 *
 * @tparam Args
 *	Catch all arguments
 */
template <typename... Args>
inline void sprintf(Args...) {
	/* Assert depends on Args so it can only be determined if
	 * the function is actually instantiated. */
	static_assert(sizeof...(Args) && false,
	              "Use of sprintf() is unsafe, use sprintf_safe() instead");
}

/**
 * A wrapper around snprintf() that automatically pulls in the
 * destination buffer size.
 *
 * @tparam Size
 *	The destination buffer size
 * @tparam Args
 *	The types of the arguments
 * @param dst
 *	A reference to the destination buffer
 * @param format
 *	A printf style formatting string
 * @param args
 *	The printf arguments
 * @return
 *	The number of characters in the resulting string, regardless of the
 *	available space
 */
template <size_t Size, typename... Args>
inline int sprintf_safe(char (& dst)[Size], char const * const format,
                   Args const... args) {
	return snprintf(dst, Size, format, args...);
}

/**
 * Casts an enum to its underlying value.
 *
 * @tparam ET,VT
 *	The enum and value type
 * @param op
 *	The operand to convert
 * @return
 *	The integer representation of the operand
 */
template <class ET, typename VT = typename std::underlying_type<ET>::type>
constexpr VT to_value(ET const op) {
	return static_cast<VT>(op);
}

/**
 * A formatting wrapper around string literals.
 *
 * Overloads operator (), which treats the string as a printf formatting
 * string, the arguments represent the data to format.
 *
 * In combination with the literal _fmt, it can be used like this:
 *
 * ~~~ c++
 * std::cout << "%-15.15s %#018p\n"_fmt("Address:", this);
 * ~~~
 *
 * @tparam BufSize
 *	The buffer size for formatting, resulting strings cannot
 *	grow beyond `BufSize - 1`
 */
template <size_t BufSize>
class Formatter {
	private:
	/**
	 * Pointer to the string literal.
	 */
	char const * const fmt;

	public:
	/**
	 * Construct from string literal.
	 */
	constexpr Formatter(char const * const fmt) : fmt{fmt} {}

	/**
	 * Returns a formatted string.
	 *
	 * @tparam ArgTs
	 *	Variadic argument types
	 * @param args
	 *	Variadic arguments
	 * @return
	 *	An std::string formatted according to fmt
	 */
	template <typename... ArgTs>
	std::string operator ()(ArgTs const &... args) const {
		char buf[BufSize];
		auto count = sprintf_safe(buf, this->fmt, args...);
		if (count < 0) {
			/* encoding error */
			return {};
		} else if (static_cast<size_t>(count) >= BufSize) {
			/* does not fit into buffer */
			return {buf, BufSize - 1};
		}
		return {buf, static_cast<size_t>(count)};
	}
};

namespace literals {
/**
 * Literal to convert a string literal to a Formatter instance.
 *
 * @param fmt
 *	A printf style format string
 * @param const
 *	Unused
 * @return
 *	A Formatter instance
 */
constexpr Formatter<16384> operator "" _fmt(char const * const fmt, size_t const) {
	return {fmt};
}
} /* namespace literals */

/**
 * A simple value container only allowing += and copy assignment.
 *
 * @tparam T
 *	The value type
 */
template <typename T>
class Sum {
	private:
	/**
	 * The sum of values accumulated.
	 */
	T value;

	public:
	/**
	 * Construct from an initial value.
	 *
	 * @param value
	 *	The initial value
	 */
	explicit constexpr Sum(T const & value) : value{value} {}

	/**
	 * Default construct.
	 */
	constexpr Sum() : Sum{0} {}

	/**
	 * Returns the current sum of values.
	 *
	 * @return
	 *	The sum of values by const reference
	 */
	constexpr operator T const &() const {
		return this->value;
	}

	/**
	 * Add a value to the sum.
	 *
	 * @param value
	 *	The value to add to the current sum
	 * @return
	 *	A self reference
	 */
	constexpr Sum & operator +=(T const & value) {
		this->value += value;
		return *this;
	}
};

} /* namespace utility */

#endif /* _POWERDXX_UTILITY_HPP_ */
<commit_msg>Remove dummy parameter documentation<commit_after>/** \file
 * Implements generally useful functions.
 */

#include <type_traits> /* std::underlying_type */
#include <string>

#include <cstdio>      /* snprintf() */

#ifndef _POWERDXX_UTILITY_HPP_
#define _POWERDXX_UTILITY_HPP_

/**
 * A collection of generally useful functions.
 */
namespace utility {

/**
 * Like sizeof(), but it returns the number of elements an array consists
 * of instead of the number of bytes.
 *
 * @tparam T,Count
 *	The type and number of array elements
 * @return
 *	The number of array entries
 */
template <typename T, size_t Count>
constexpr size_t countof(T (&)[Count]) { return Count; }

/**
 * Contains literals.
 */
namespace literals {

/**
 * A string literal operator equivalent to the `operator "" s` literal
 * provided by C++14 in \<string\>.
 *
 * @param op
 *	The raw string to turn into an std::string object
 * @param size
 *	The size of the raw string
 * @return
 *	An std::string instance
 */
inline std::string operator "" _s(char const * const op, size_t const size) {
	return {op, size};
}

} /* namespace literals */

/**
 * This is a safeguard against accidentally using sprintf().
 *
 * Using it triggers a static_assert(), preventing compilation.
 *
 * @tparam Args
 *	Catch all arguments
 */
template <typename... Args>
inline void sprintf(Args...) {
	/* Assert depends on Args so it can only be determined if
	 * the function is actually instantiated. */
	static_assert(sizeof...(Args) && false,
	              "Use of sprintf() is unsafe, use sprintf_safe() instead");
}

/**
 * A wrapper around snprintf() that automatically pulls in the
 * destination buffer size.
 *
 * @tparam Size
 *	The destination buffer size
 * @tparam Args
 *	The types of the arguments
 * @param dst
 *	A reference to the destination buffer
 * @param format
 *	A printf style formatting string
 * @param args
 *	The printf arguments
 * @return
 *	The number of characters in the resulting string, regardless of the
 *	available space
 */
template <size_t Size, typename... Args>
inline int sprintf_safe(char (& dst)[Size], char const * const format,
                   Args const... args) {
	return snprintf(dst, Size, format, args...);
}

/**
 * Casts an enum to its underlying value.
 *
 * @tparam ET,VT
 *	The enum and value type
 * @param op
 *	The operand to convert
 * @return
 *	The integer representation of the operand
 */
template <class ET, typename VT = typename std::underlying_type<ET>::type>
constexpr VT to_value(ET const op) {
	return static_cast<VT>(op);
}

/**
 * A formatting wrapper around string literals.
 *
 * Overloads operator (), which treats the string as a printf formatting
 * string, the arguments represent the data to format.
 *
 * In combination with the literal _fmt, it can be used like this:
 *
 * ~~~ c++
 * std::cout << "%-15.15s %#018p\n"_fmt("Address:", this);
 * ~~~
 *
 * @tparam BufSize
 *	The buffer size for formatting, resulting strings cannot
 *	grow beyond `BufSize - 1`
 */
template <size_t BufSize>
class Formatter {
	private:
	/**
	 * Pointer to the string literal.
	 */
	char const * const fmt;

	public:
	/**
	 * Construct from string literal.
	 */
	constexpr Formatter(char const * const fmt) : fmt{fmt} {}

	/**
	 * Returns a formatted string.
	 *
	 * @tparam ArgTs
	 *	Variadic argument types
	 * @param args
	 *	Variadic arguments
	 * @return
	 *	An std::string formatted according to fmt
	 */
	template <typename... ArgTs>
	std::string operator ()(ArgTs const &... args) const {
		char buf[BufSize];
		auto count = sprintf_safe(buf, this->fmt, args...);
		if (count < 0) {
			/* encoding error */
			return {};
		} else if (static_cast<size_t>(count) >= BufSize) {
			/* does not fit into buffer */
			return {buf, BufSize - 1};
		}
		return {buf, static_cast<size_t>(count)};
	}
};

namespace literals {
/**
 * Literal to convert a string literal to a Formatter instance.
 *
 * @param fmt
 *	A printf style format string
 * @return
 *	A Formatter instance
 */
constexpr Formatter<16384> operator "" _fmt(char const * const fmt, size_t const) {
	return {fmt};
}
} /* namespace literals */

/**
 * A simple value container only allowing += and copy assignment.
 *
 * @tparam T
 *	The value type
 */
template <typename T>
class Sum {
	private:
	/**
	 * The sum of values accumulated.
	 */
	T value;

	public:
	/**
	 * Construct from an initial value.
	 *
	 * @param value
	 *	The initial value
	 */
	explicit constexpr Sum(T const & value) : value{value} {}

	/**
	 * Default construct.
	 */
	constexpr Sum() : Sum{0} {}

	/**
	 * Returns the current sum of values.
	 *
	 * @return
	 *	The sum of values by const reference
	 */
	constexpr operator T const &() const {
		return this->value;
	}

	/**
	 * Add a value to the sum.
	 *
	 * @param value
	 *	The value to add to the current sum
	 * @return
	 *	A self reference
	 */
	constexpr Sum & operator +=(T const & value) {
		this->value += value;
		return *this;
	}
};

} /* namespace utility */

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


<commit_before>/*
 * Funambol is a mobile platform developed by Funambol, Inc.
 * Copyright (C) 2003 - 2007 Funambol, Inc.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by
 * the Free Software Foundation with the addition of the following permission
 * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
 * WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE
 * WARRANTY OF NON INFRINGEMENT  OF THIRD PARTY RIGHTS.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA.
 *
 * You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite
 * 305, Redwood City, CA 94063, USA, or at email address info@funambol.com.
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License version 3.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License
 * version 3, these Appropriate Legal Notices must retain the display of the
 * "Powered by Funambol" logo. If the display of the logo is not reasonably
 * feasible for technical reasons, the Appropriate Legal Notices must display
 * the words "Powered by Funambol".
 */

#include "base/util/PropertyFile.h"
#include "base/globalsdef.h"

#define REMOVED "__#REM#O#VED#__"

USE_NAMESPACE

StringBuffer escapeString(const char* val) {
    StringBuffer s(val);
    s.replaceAll(":", "\\:");
    return s;
}

StringBuffer unescapeString(const char* val) {
    StringBuffer s(val);
    s.replaceAll("\\:", ":");
    return s;
}

static bool existsFile(const char* fullname) {
    bool found = false;
    FILE* f = fopen(fullname, "r");
    if (f) {
        found = true;
        fclose(f);
    }
    return found;
}

static bool removeFile(const char* fullname) {
    char* p;
	int len;
    bool ret = false;

    p = strrchr((char*)fullname, '/');

    if (!p) {
        // the file is in the current directory
        ret = removeFileInDir(".", fullname);
    } else {
	    len = p-fullname;
        StringBuffer dir(fullname, len);
	    p++; len=strlen(fullname)-len;
        StringBuffer filename(p, len);
        ret = removeFileInDir(dir, filename);
    }
    return ret;
}

int PropertyFile::read() {

    char line[512];
    size_t found = 0;
    FILE* f;
    f = fopen(node, "r");
    if (!f) {
        LOG.debug("PropertyFile: the file '%s' doesn't exist. Try the journal file. '%s'", node.c_str());
    } else {
        while(fgets(line, 511, f) != NULL) {
            StringBuffer s(line);
            StringBuffer key;
            StringBuffer value;

            if (separateKeyValue(s, key, value)) {
                KeyValuePair toInsert(key, value);
                data.add(toInsert);
            }
        }
        fclose(f);
    }
    // check if there is the journal file and if any, set every value in memory. After that
    // empty the journal
    f = fopen(nodeJour, "r");
    if (!f) {
        // LOG.debug("PropertyFile: there is no journal file: '%s'", nodeJour.c_str());
    } else {
        while(fgets(line, 511, f) != NULL) {
            StringBuffer s(line);
            StringBuffer key;
            StringBuffer value;
            if (separateKeyValue(s, key, value)) {
                if (value == REMOVED) {
                    ArrayListKeyValueStore::removeProperty(key);
                } else {
                    ArrayListKeyValueStore::setPropertyValue(key, value);
                }
            }
        }
        fclose(f);
    }
    return 0;
}

int PropertyFile::close() {

    FILE* file;
    file = fopen(node, "w");
    int ret = 0;
    if (file) {
        KeyValuePair* curr = NULL;
        for (curr = (KeyValuePair*)data.front(); curr;
             curr = (KeyValuePair*)data.next() ) {

             fprintf(file, "%s:%s\n", escapeString(curr->getKey()).c_str(), escapeString(curr->getValue()).c_str());
        }
        fclose(file);

        // reset the content of the journal
        if (existsFile(nodeJour) && !removeFile(nodeJour)) {
            LOG.error("There are problem in removing journal file");
        }

        ret = 0;

    } else {
        LOG.error("PropertyFile: it is not possible to save the file: '%s'", node.c_str());
        ret = -1;
    }
    return ret;
}

int PropertyFile::setPropertyValue(const char *prop, const char *value) {

    int ret = ArrayListKeyValueStore::setPropertyValue(prop, value);
    if (ret) {
        return ret;
    }

    FILE* file = fopen(nodeJour, "a+");
    if (file) {
        fprintf(file, "%s:%s\n", escapeString(prop).c_str(), escapeString(value).c_str());
        fclose(file);
    } else {
        LOG.error("PropertyFile setProperty: it is not possible to save the journal file: '%s'", node.c_str());
        ret = -1;
    }

    return ret;

}

int PropertyFile::removeProperty(const char *prop) {

    int ret = 0;

    FILE* file = fopen(nodeJour, "a+");
    if (file) {
        fprintf(file, "%s:%s\n", escapeString(prop).c_str(), escapeString(REMOVED).c_str());
        fclose(file);
    } else {
        LOG.error("PropertyFile removeProperty: it is not possible to save the journal file: '%s'", node.c_str());
    }

    ret = ArrayListKeyValueStore::removeProperty(prop);
    if (ret) {
        LOG.debug("PropertyFile: it is not possible to remove from the ArrayList");
    }

    return ret;

}

int PropertyFile::removeAllProperties() {

    int ret = ArrayListKeyValueStore::removeAllProperties();
    if (ret) {
        return ret;
    }
    // reset the content
    if (existsFile(node) && !removeFile(node)) {
        LOG.error("There are problem in removing the file %s", node);
    }
    return ret;
}

bool PropertyFile::separateKeyValue(StringBuffer& s, StringBuffer& key, StringBuffer& value) {
    bool ret = false;
    size_t found = 0;

    for (unsigned int i = 0; i < s.length(); i++) {
        if  (((found = s.find(":", found + 1)) != StringBuffer::npos) && (found > 1 && s.c_str()[found-1] != '\\')) {

            key = unescapeString(s.substr(0, found));
            value = unescapeString(s.substr(found + 1, (s.length() - (found + 2)))); // it remove the \n at the end

            ret = true;
            break;
        }
    }

    return ret;
}<commit_msg>Warning removed<commit_after>/*
 * Funambol is a mobile platform developed by Funambol, Inc.
 * Copyright (C) 2003 - 2007 Funambol, Inc.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by
 * the Free Software Foundation with the addition of the following permission
 * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
 * WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE
 * WARRANTY OF NON INFRINGEMENT  OF THIRD PARTY RIGHTS.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA.
 *
 * You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite
 * 305, Redwood City, CA 94063, USA, or at email address info@funambol.com.
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License version 3.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License
 * version 3, these Appropriate Legal Notices must retain the display of the
 * "Powered by Funambol" logo. If the display of the logo is not reasonably
 * feasible for technical reasons, the Appropriate Legal Notices must display
 * the words "Powered by Funambol".
 */

#include "base/util/PropertyFile.h"
#include "base/globalsdef.h"

#define REMOVED "__#REM#O#VED#__"

USE_NAMESPACE

StringBuffer escapeString(const char* val) {
    StringBuffer s(val);
    s.replaceAll(":", "\\:");
    return s;
}

StringBuffer unescapeString(const char* val) {
    StringBuffer s(val);
    s.replaceAll("\\:", ":");
    return s;
}

static bool existsFile(const char* fullname) {
    bool found = false;
    FILE* f = fopen(fullname, "r");
    if (f) {
        found = true;
        fclose(f);
    }
    return found;
}

static bool removeFile(const char* fullname) {
    char* p;
	int len;
    bool ret = false;

    p = strrchr((char*)fullname, '/');

    if (!p) {
        // the file is in the current directory
        ret = removeFileInDir(".", fullname);
    } else {
	    len = p-fullname;
        StringBuffer dir(fullname, len);
	    p++; len=strlen(fullname)-len;
        StringBuffer filename(p, len);
        ret = removeFileInDir(dir, filename);
    }
    return ret;
}

int PropertyFile::read() {

    char line[512];
    size_t found = 0;
    FILE* f;
    f = fopen(node, "r");
    if (!f) {
        LOG.debug("PropertyFile: the file '%s' doesn't exist. Try the journal file. '%s'", node.c_str());
    } else {
        while(fgets(line, 511, f) != NULL) {
            StringBuffer s(line);
            StringBuffer key;
            StringBuffer value;

            if (separateKeyValue(s, key, value)) {
                KeyValuePair toInsert(key, value);
                data.add(toInsert);
            }
        }
        fclose(f);
    }
    // check if there is the journal file and if any, set every value in memory. After that
    // empty the journal
    f = fopen(nodeJour, "r");
    if (!f) {
        // LOG.debug("PropertyFile: there is no journal file: '%s'", nodeJour.c_str());
    } else {
        while(fgets(line, 511, f) != NULL) {
            StringBuffer s(line);
            StringBuffer key;
            StringBuffer value;
            if (separateKeyValue(s, key, value)) {
                if (value == REMOVED) {
                    ArrayListKeyValueStore::removeProperty(key);
                } else {
                    ArrayListKeyValueStore::setPropertyValue(key, value);
                }
            }
        }
        fclose(f);
    }
    return 0;
}

int PropertyFile::close() {

    FILE* file;
    file = fopen(node, "w");
    int ret = 0;
    if (file) {
        KeyValuePair* curr = NULL;
        for (curr = (KeyValuePair*)data.front(); curr;
             curr = (KeyValuePair*)data.next() ) {

             fprintf(file, "%s:%s\n", escapeString(curr->getKey()).c_str(), escapeString(curr->getValue()).c_str());
        }
        fclose(file);

        // reset the content of the journal
        if (existsFile(nodeJour) && !removeFile(nodeJour)) {
            LOG.error("There are problem in removing journal file");
        }

        ret = 0;

    } else {
        LOG.error("PropertyFile: it is not possible to save the file: '%s'", node.c_str());
        ret = -1;
    }
    return ret;
}

int PropertyFile::setPropertyValue(const char *prop, const char *value) {

    int ret = ArrayListKeyValueStore::setPropertyValue(prop, value);
    if (ret) {
        return ret;
    }

    FILE* file = fopen(nodeJour, "a+");
    if (file) {
        fprintf(file, "%s:%s\n", escapeString(prop).c_str(), escapeString(value).c_str());
        fclose(file);
    } else {
        LOG.error("PropertyFile setProperty: it is not possible to save the journal file: '%s'", node.c_str());
        ret = -1;
    }

    return ret;

}

int PropertyFile::removeProperty(const char *prop) {

    int ret = 0;

    FILE* file = fopen(nodeJour, "a+");
    if (file) {
        fprintf(file, "%s:%s\n", escapeString(prop).c_str(), escapeString(REMOVED).c_str());
        fclose(file);
    } else {
        LOG.error("PropertyFile removeProperty: it is not possible to save the journal file: '%s'", node.c_str());
    }

    ret = ArrayListKeyValueStore::removeProperty(prop);
    if (ret) {
        LOG.debug("PropertyFile: it is not possible to remove from the ArrayList");
    }

    return ret;

}

int PropertyFile::removeAllProperties() {

    int ret = ArrayListKeyValueStore::removeAllProperties();
    if (ret) {
        return ret;
    }
    // reset the content
    if (existsFile(node) && !removeFile(node)) {
        LOG.error("There are problem in removing the file %s", node.c_str());
    }
    return ret;
}

bool PropertyFile::separateKeyValue(StringBuffer& s, StringBuffer& key, StringBuffer& value) {
    bool ret = false;
    size_t found = 0;

    for (unsigned int i = 0; i < s.length(); i++) {
        if  (((found = s.find(":", found + 1)) != StringBuffer::npos) && (found > 1 && s.c_str()[found-1] != '\\')) {

            key = unescapeString(s.substr(0, found));
            value = unescapeString(s.substr(found + 1, (s.length() - (found + 2)))); // it remove the \n at the end

            ret = true;
            break;
        }
    }

    return ret;
}

<|endoftext|>


<commit_before>/*  This file is part of the KDE project
    Copyright (C) 2008 Matthias Kretz <kretz@kde.org>

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

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

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.

*/

#include <QtCore/QCoreApplication>
#include <QtCore/QtDebug>
#include <QtCore/QMutableListIterator>
#include <QtCore/QList>
#include "../qsettingsgroup_p.h"
#include <kcomponentdata.h>
#include <kconfig.h>
#include <kconfiggroup.h>

using Phonon::QSettingsGroup;

Q_DECLARE_METATYPE(QList<int>)

int main(int argc, char **argv)
{
    QCoreApplication app(argc, argv);
    KComponentData cData("phonon device preference update");

    KConfig kconfig("phonondevicesrc", KConfig::NoGlobals);
    KConfigGroup globalGroup(&kconfig, "Globals");
    int newIndexForZero = 0;

    foreach (const QString &group, kconfig.groupList()) {
        KConfigGroup configGroup(&kconfig, group);
        int index = configGroup.readEntry("index", -1);
        if (index == 0) {
            newIndexForZero = globalGroup.readEntry("nextIndex", 0);
            configGroup.writeEntry("index", newIndexForZero);
            globalGroup.writeEntry("nextIndex", newIndexForZero + 1);
            break;
        }
    }

    qDebug() << newIndexForZero;
    qRegisterMetaTypeStreamOperators<QList<int> >("QList<int>");

    QSettings qconfig(QLatin1String("kde.org"), QLatin1String("libphonon"));
    QSettingsGroup outputGroup(&qconfig, QLatin1String("AudioOutputDevice"));
    for (int i = -1; i < 10; ++i) {
        const QString oldKey = QLatin1String("Category") + QString::number(i);
        const QString newKey = QLatin1String("Category_") + QString::number(i);
        qDebug() << oldKey << newKey;
        if (outputGroup.hasKey(oldKey) && !outputGroup.hasKey(newKey)) {
            QList<int> deviceIndexes = outputGroup.value(oldKey, QList<int>());
            QMutableListIterator<int> index(deviceIndexes);
            while (index.hasNext()) {
                index.next();
                if (index.value() < 10000 && index.value() >= 0) {
                    qDebug() << "changing index" << index.value();
                    if (index.value() == 0) {
                        Q_ASSERT(newIndexForZero);
                        index.setValue(-newIndexForZero);
                    } else {
                        index.setValue(-index.value());
                    }
                }
            }
            outputGroup.setValue(newKey, deviceIndexes);
            outputGroup.removeEntry(oldKey);
        }
    }

    return 0;
}
<commit_msg>remove debug output<commit_after>/*  This file is part of the KDE project
    Copyright (C) 2008 Matthias Kretz <kretz@kde.org>

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

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

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.

*/

#include <QtCore/QCoreApplication>
#include <QtCore/QMutableListIterator>
#include <QtCore/QList>
#include "../qsettingsgroup_p.h"
#include <kcomponentdata.h>
#include <kconfig.h>
#include <kconfiggroup.h>

using Phonon::QSettingsGroup;

Q_DECLARE_METATYPE(QList<int>)

int main(int argc, char **argv)
{
    QCoreApplication app(argc, argv);
    KComponentData cData("phonon device preference update");

    KConfig kconfig("phonondevicesrc", KConfig::NoGlobals);
    KConfigGroup globalGroup(&kconfig, "Globals");
    int newIndexForZero = 0;

    foreach (const QString &group, kconfig.groupList()) {
        KConfigGroup configGroup(&kconfig, group);
        int index = configGroup.readEntry("index", -1);
        if (index == 0) {
            newIndexForZero = globalGroup.readEntry("nextIndex", 0);
            configGroup.writeEntry("index", newIndexForZero);
            globalGroup.writeEntry("nextIndex", newIndexForZero + 1);
            break;
        }
    }

    qRegisterMetaTypeStreamOperators<QList<int> >("QList<int>");

    QSettings qconfig(QLatin1String("kde.org"), QLatin1String("libphonon"));
    QSettingsGroup outputGroup(&qconfig, QLatin1String("AudioOutputDevice"));
    for (int i = -1; i < 10; ++i) {
        const QString oldKey = QLatin1String("Category") + QString::number(i);
        const QString newKey = QLatin1String("Category_") + QString::number(i);
        if (outputGroup.hasKey(oldKey) && !outputGroup.hasKey(newKey)) {
            QList<int> deviceIndexes = outputGroup.value(oldKey, QList<int>());
            QMutableListIterator<int> index(deviceIndexes);
            while (index.hasNext()) {
                index.next();
                if (index.value() < 10000 && index.value() >= 0) {
                    if (index.value() == 0) {
                        Q_ASSERT(newIndexForZero);
                        index.setValue(-newIndexForZero);
                    } else {
                        index.setValue(-index.value());
                    }
                }
            }
            outputGroup.setValue(newKey, deviceIndexes);
            outputGroup.removeEntry(oldKey);
        }
    }

    return 0;
}
<|endoftext|>


<commit_before>/*
 * Funambol is a mobile platform developed by Funambol, Inc.
 * Copyright (C) 2003 - 2007 Funambol, Inc.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by
 * the Free Software Foundation with the addition of the following permission
 * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
 * WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE
 * WARRANTY OF NON INFRINGEMENT  OF THIRD PARTY RIGHTS.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA.
 *
 * You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite
 * 305, Redwood City, CA 94063, USA, or at email address info@funambol.com.
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License version 3.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License
 * version 3, these Appropriate Legal Notices must retain the display of the
 * "Powered by Funambol" logo. If the display of the logo is not reasonably
 * feasible for technical reasons, the Appropriate Legal Notices must display
 * the words "Powered by Funambol".
 */

#include "spds/SyncSource.h"
#include "base/Log.h"
#include "client/SyncClient.h"
#include "spds/spdsutils.h"
#include "spds/SyncSourceConfig.h"
#include "client/CacheSyncSource.h"
#include "base/util/KeyValuePair.h"
#include "base/util/PropertyFile.h"
#include "base/util/ArrayListEnumeration.h"

BEGIN_NAMESPACE

CacheSyncSource::CacheSyncSource(const WCHAR* sourceName, AbstractSyncSourceConfig *sc, KeyValueStore* cache) :
                    SyncSource(sourceName, sc) {

    allKeys = NULL;
    newKeys = NULL;
    updatedKeys = NULL;
    deletedKeys = NULL;

    if (cache) {
        this->cache = cache;
    } else {
        // get the default directory of the
        StringBuffer completeName = getCacheDirectory();
        completeName += "/";
        completeName += CACHE_FILE_NAME;
        LOG.debug("PropertyFile: path to the PropertyFile %s", completeName.c_str());

        this->cache = new PropertyFile(completeName);
    }
}

/**
 * Release dynamically allocated resources
 */
CacheSyncSource::~CacheSyncSource() {

    if (newKeys)     { delete newKeys;     }
    if (updatedKeys) { delete updatedKeys; }
    if (deletedKeys) { delete deletedKeys; }
    if (allKeys)     { delete allKeys;     }
    if (cache)       { delete cache;       }
}

/**
* Fill a key value pair object and set into the partialModification array list.
* This is used to set the changes in the cache or in the listener based sync source.
* If there is an error this error is reported in the saving procedure and there
* it is decided how procede
*/
void CacheSyncSource::setItemStatus(const WCHAR* key, int status, const char* command) {

    KeyValuePair vp;
    switch (status) {

        case 200:
        case 201:
        case 418:
            {
             LOG.info("[%s], Received success status code from server for %s on item with key %s - code: %d", getName(), command, key, status);
             char* k = toMultibyte(key);
             vp.setKey(k);
             StringBuffer v(k);
             vp.setValue(getItemSignature(v));
             delete [] k;
            }
            break;
        case 500:
        default:
            LOG.info("[%s], Received failed status code from server for %s on item with key %s - code: %d", getName(), command, key, status);
            // error. it doesn't update the cache
            break;
    }
    if (vp.getKey()) {
        updateInCache(vp, command);
    }

}

/**
* Get the signature of an item given the value and the size.
* The signature could be a crc computation or a timestamp or whatever can identify uniquely the
* content of an item. It is used by the implementation of the ItemHandler
* and it need the getItemContent too. The default implementation uses a
* crc computation of the value
*
* @param key        the value on which signature must be calculated
* @param size       the size of the value
*/

StringBuffer CacheSyncSource::getItemSignature(StringBuffer& key) {

    void* content       = NULL;
    size_t size         = 0;

    if (key.length() <= 0) {
        return NULL;
    }

    LOG.debug("[%s] Getting signature for item with key %s", getName(), key.c_str());

    content = getItemContent(key, &size);
    StringBuffer s;
    s.sprintf("%ld", calculateCRC(content, size));
    if (content) { delete [] (char*)content; content = NULL; }
    return s;
}

SyncItem* CacheSyncSource::fillSyncItem(StringBuffer* key) {

    SyncItem* syncItem  = NULL;
    size_t size         = 0;
    void* content       = NULL;

    if (!key) {
        return NULL;
    }

    LOG.debug("[%s] Filling item with key %s", getName(), key->c_str());

    content = getItemContent((*key), &size);

    WCHAR* wkey = toWideChar(key->c_str());
    syncItem = new SyncItem(wkey);
    syncItem->setData(content, size);

    if (wkey) { delete [] wkey; wkey = NULL; }
    if (content) { delete [] (char*)content; content = NULL; }

    return syncItem;

}

/**
 * Return the first SyncItem of all.
 * It is used in case of slow sync and retrieve the entire
 * data source content.
 */
SyncItem* CacheSyncSource::getFirstItem() {

    allKeys = getAllItemList();
    return getNextItem();

};

 /**
 * Return the next SyncItem of all.
 * It is used in case of slow sync
 * and retrieve the entire data source content.
 */
SyncItem* CacheSyncSource::getNextItem() {

    SyncItem* syncItem = NULL;
    if (allKeys && allKeys->hasMoreElement()) {
        StringBuffer* s     = (StringBuffer*)allKeys->getNextElement();
        syncItem = fillSyncItem(s);
    }
    if (!syncItem) {
        LOG.info("There are no more items to be exchanged. Return NULL");
    }
    return syncItem;

}
/**
 * Return the first new SyncItem. It is used in case of fast sync
 * and retrieve the new data source content.
 */
SyncItem* CacheSyncSource::getFirstNewItem() {
    fillItemModifications();
    return getNextNewItem();

}

/**
 * Return the next SyncItem of new one. It is used in case of fast sync
 * and retrieve the new data source content.
 */
SyncItem* CacheSyncSource::getNextNewItem() {

    SyncItem* syncItem = NULL;
    if (newKeys && newKeys->hasMoreElement()) {
        StringBuffer* s     = (StringBuffer*)newKeys->getNextElement();
        syncItem = fillSyncItem(s);
    }
    if (!syncItem) {
        LOG.info("There are no more new items to be exchanged. Return NULL");
    }
    return syncItem;
}

/**
 * Return the first SyncItem of updated one. It is used in case of fast sync
 * and retrieve the new data source content.
 */
SyncItem* CacheSyncSource::getFirstUpdatedItem() {

    return getNextUpdatedItem();
}

SyncItem* CacheSyncSource::getNextUpdatedItem() {

    SyncItem* syncItem = NULL;
    if (updatedKeys && updatedKeys->hasMoreElement()) {
        StringBuffer* s     = (StringBuffer*)updatedKeys->getNextElement();
        syncItem = fillSyncItem(s);
    }
    if (!syncItem) {
        LOG.info("There are no more updated items to be exchanged. Return NULL");
    }
    return syncItem;
}

SyncItem* CacheSyncSource::getFirstDeletedItem() {

    return getNextDeletedItem();
}

/**
 * Return the next SyncItem of updated one. It is used in case of fast sync
 * and retrieve the new data source content.
 */
SyncItem* CacheSyncSource::getNextDeletedItem() {

    SyncItem* syncItem = NULL;
    if (deletedKeys && deletedKeys->hasMoreElement()) {
        StringBuffer* s     = (StringBuffer*)deletedKeys->getNextElement();
        if (!s) {
            return NULL;
        }
        WCHAR* wkey = toWideChar(s->c_str());
        syncItem = new SyncItem(wkey);
        if (wkey) { delete [] wkey; wkey = NULL; }
    }
    if (!syncItem) {
        LOG.info("There are no more deleted items to be exchanged. Return NULL");
    }

    return syncItem;
}

int CacheSyncSource::endSync() {
    saveCache();
    return 0;
}

/**
* The way to calculate the cache is the follow:
* loop on the current element against an array list
* that has the cache. It is the copy of the original cache.
* When an current element is found in the cache, it is removed
* from the cache copy. At the end the remained element
* in the cache are the deleted ones.
*/
bool CacheSyncSource::fillItemModifications() {

    // all the current items keys list
    Enumeration* items = (Enumeration*)getAllItemList();

    if (!items) {
        LOG.error("Error in fillItemModification");
        return false;
    }

    // all the action are done on the copy so we can delete
    // the element found. The remained are the deleted by the user.
    Enumeration& e = cache->getProperties();
    ArrayList cacheCopy;
    while(e.hasMoreElement()) {
        cacheCopy.add(*e.getNextElement());
    }

    StringBuffer* key;
    KeyValuePair* kvp;

    ArrayListEnumeration *newitems = new ArrayListEnumeration(),
                         *moditems = new ArrayListEnumeration(),
                         *delitems = new ArrayListEnumeration();

    if (items) {
        while(items->hasMoreElement()) {
            key = (StringBuffer*)items->getNextElement();
            int size = cacheCopy.size();
            bool foundnew = true;

            for (int i = 0; i < size; i++) {
                kvp = (KeyValuePair*)(cacheCopy[i]);
                if (strcmp(kvp->getKey(), key->c_str()) == 0) {
                    foundnew = false;
                    // see if it is updated.
                    StringBuffer sign = getItemSignature(*key);
                    if (sign != kvp->getValue()) {
                        // there is an update. if equal nothing to do...
                        moditems->add(*key);
                    }
                    cacheCopy.removeElementAt(i);
                    break;
                }
            }
            if (foundnew) {
                newitems->add(*key);
            }
        }
    }

    for(kvp=(KeyValuePair*)cacheCopy.front();kvp;kvp=(KeyValuePair*)cacheCopy.next()){
        delitems->add((StringBuffer&)kvp->getKey());
    }

    newKeys = newitems;
    updatedKeys = moditems;
    deletedKeys = delitems;

    delete items;
    return true;
}

/**
* Save the current status of the cache arrayList
*
*/
int CacheSyncSource::saveCache() {

    LOG.debug("[%s] Saving cache", getName());

    int ret = cache->close();
    return ret;

}

int CacheSyncSource::updateInCache(KeyValuePair& k, const char* action) {

    if (strcmp(action, ADD    ) == 0 ||
        strcmp(action, REPLACE) == 0) {
        cache->setPropertyValue(k.getKey(), k.getValue());
    } else if (strcmp(action, DEL) == 0) {
        cache->removeProperty(k.getKey());
    }
    return 0;
}

void CacheSyncSource::getKeyAndSignature(SyncItem& item, KeyValuePair& kvp) {

    char* t = toMultibyte(item.getKey());
    StringBuffer s(t);
    StringBuffer sign = getItemSignature(s);
    kvp.setKey(t);
    kvp.setValue(sign);
    delete [] t;

}

int CacheSyncSource::addItem(SyncItem& item) {
    int ret = insertItem(item);
    switch (ret) {
        case 200:
        case 201:
        case 418: {
            LOG.info("[%s] Successful add of item with key %s - code %d", getName(), item.getKey(), ret);
            KeyValuePair k;
            getKeyAndSignature(item, k);
            insertInCache(k);
        }
        break;
        default:
            LOG.error("[%s] Failed add of item with key %s - code %d", getName(), item.getKey(), ret);
        break;
    }
    return ret;
}

int CacheSyncSource::updateItem(SyncItem& item) {
    int ret = modifyItem(item);
    switch (ret) {
        case 200:
        case 201:
        case 418: {
            LOG.info("[%s] Successful update of item with key %s - code %d", getName(), item.getKey(), ret);
            KeyValuePair k;
            getKeyAndSignature(item, k);
            updateInCache(k);
        }
        break;
        default:
            LOG.error("[%s] Failed update of item with key %s - code %d", getName(), item.getKey(), ret);
        break;
    }
    return ret;
}

int CacheSyncSource::deleteItem(SyncItem& item) {
    int ret = removeItem(item);
    switch (ret) {
        case 200:
        case 201:
        case 418: {
            LOG.info("[%s] Successful delete of item with key %s - code %d", getName(), item.getKey(), ret);
            char* t = toMultibyte(item.getKey());
            KeyValuePair k (t, "");
            removeFromCache(k);
            delete [] t;
        }
        break;
        default:
            LOG.error("[%s] Failed delete of item with key %s - code %d", getName(), item.getKey(), ret);
        break;
    }

    return ret;
}

END_NAMESPACE
<commit_msg>clear the cache when a slow sync starts<commit_after>/*
 * Funambol is a mobile platform developed by Funambol, Inc.
 * Copyright (C) 2003 - 2007 Funambol, Inc.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by
 * the Free Software Foundation with the addition of the following permission
 * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
 * WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE
 * WARRANTY OF NON INFRINGEMENT  OF THIRD PARTY RIGHTS.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA.
 *
 * You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite
 * 305, Redwood City, CA 94063, USA, or at email address info@funambol.com.
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License version 3.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License
 * version 3, these Appropriate Legal Notices must retain the display of the
 * "Powered by Funambol" logo. If the display of the logo is not reasonably
 * feasible for technical reasons, the Appropriate Legal Notices must display
 * the words "Powered by Funambol".
 */

#include "spds/SyncSource.h"
#include "base/Log.h"
#include "client/SyncClient.h"
#include "spds/spdsutils.h"
#include "spds/SyncSourceConfig.h"
#include "client/CacheSyncSource.h"
#include "base/util/KeyValuePair.h"
#include "base/util/PropertyFile.h"
#include "base/util/ArrayListEnumeration.h"

BEGIN_NAMESPACE

CacheSyncSource::CacheSyncSource(const WCHAR* sourceName, AbstractSyncSourceConfig *sc, KeyValueStore* cache) :
                    SyncSource(sourceName, sc) {

    allKeys = NULL;
    newKeys = NULL;
    updatedKeys = NULL;
    deletedKeys = NULL;

    if (cache) {
        this->cache = cache;
    } else {
        // get the default directory of the
        StringBuffer completeName = getCacheDirectory();
        completeName += "/";
        completeName += CACHE_FILE_NAME;
        LOG.debug("PropertyFile: path to the PropertyFile %s", completeName.c_str());

        this->cache = new PropertyFile(completeName);
    }
}

/**
 * Release dynamically allocated resources
 */
CacheSyncSource::~CacheSyncSource() {

    if (newKeys)     { delete newKeys;     }
    if (updatedKeys) { delete updatedKeys; }
    if (deletedKeys) { delete deletedKeys; }
    if (allKeys)     { delete allKeys;     }
    if (cache)       { delete cache;       }
}

/**
* Fill a key value pair object and set into the partialModification array list.
* This is used to set the changes in the cache or in the listener based sync source.
* If there is an error this error is reported in the saving procedure and there
* it is decided how procede
*/
void CacheSyncSource::setItemStatus(const WCHAR* key, int status, const char* command) {

    KeyValuePair vp;
    switch (status) {

        case 200:
        case 201:
        case 418:
            {
             LOG.info("[%s], Received success status code from server for %s on item with key %s - code: %d", getName(), command, key, status);
             char* k = toMultibyte(key);
             vp.setKey(k);
             StringBuffer v(k);
             vp.setValue(getItemSignature(v));
             delete [] k;
            }
            break;
        case 500:
        default:
            LOG.info("[%s], Received failed status code from server for %s on item with key %s - code: %d", getName(), command, key, status);
            // error. it doesn't update the cache
            break;
    }
    if (vp.getKey()) {
        updateInCache(vp, command);
    }

}

/**
* Get the signature of an item given the value and the size.
* The signature could be a crc computation or a timestamp or whatever can identify uniquely the
* content of an item. It is used by the implementation of the ItemHandler
* and it need the getItemContent too. The default implementation uses a
* crc computation of the value
*
* @param key        the value on which signature must be calculated
* @param size       the size of the value
*/

StringBuffer CacheSyncSource::getItemSignature(StringBuffer& key) {

    void* content       = NULL;
    size_t size         = 0;

    if (key.length() <= 0) {
        return NULL;
    }

    LOG.debug("[%s] Getting signature for item with key %s", getName(), key.c_str());

    content = getItemContent(key, &size);
    StringBuffer s;
    s.sprintf("%ld", calculateCRC(content, size));
    if (content) { delete [] (char*)content; content = NULL; }
    return s;
}

SyncItem* CacheSyncSource::fillSyncItem(StringBuffer* key) {

    SyncItem* syncItem  = NULL;
    size_t size         = 0;
    void* content       = NULL;

    if (!key) {
        return NULL;
    }

    LOG.debug("[%s] Filling item with key %s", getName(), key->c_str());

    content = getItemContent((*key), &size);

    WCHAR* wkey = toWideChar(key->c_str());
    syncItem = new SyncItem(wkey);
    syncItem->setData(content, size);

    if (wkey) { delete [] wkey; wkey = NULL; }
    if (content) { delete [] (char*)content; content = NULL; }

    return syncItem;

}

/**
 * Return the first SyncItem of all.
 * It is used in case of slow sync and retrieve the entire
 * data source content.
 */
SyncItem* CacheSyncSource::getFirstItem() {

    // A slow sync is started -> clear the cache
    clearCache();

    allKeys = getAllItemList();
    return getNextItem();

};

 /**
 * Return the next SyncItem of all.
 * It is used in case of slow sync
 * and retrieve the entire data source content.
 */
SyncItem* CacheSyncSource::getNextItem() {

    SyncItem* syncItem = NULL;
    if (allKeys && allKeys->hasMoreElement()) {
        StringBuffer* s     = (StringBuffer*)allKeys->getNextElement();
        syncItem = fillSyncItem(s);
    }
    if (!syncItem) {
        LOG.info("There are no more items to be exchanged. Return NULL");
    }
    return syncItem;

}
/**
 * Return the first new SyncItem. It is used in case of fast sync
 * and retrieve the new data source content.
 */
SyncItem* CacheSyncSource::getFirstNewItem() {
    fillItemModifications();
    return getNextNewItem();

}

/**
 * Return the next SyncItem of new one. It is used in case of fast sync
 * and retrieve the new data source content.
 */
SyncItem* CacheSyncSource::getNextNewItem() {

    SyncItem* syncItem = NULL;
    if (newKeys && newKeys->hasMoreElement()) {
        StringBuffer* s     = (StringBuffer*)newKeys->getNextElement();
        syncItem = fillSyncItem(s);
    }
    if (!syncItem) {
        LOG.info("There are no more new items to be exchanged. Return NULL");
    }
    return syncItem;
}

/**
 * Return the first SyncItem of updated one. It is used in case of fast sync
 * and retrieve the new data source content.
 */
SyncItem* CacheSyncSource::getFirstUpdatedItem() {

    return getNextUpdatedItem();
}

SyncItem* CacheSyncSource::getNextUpdatedItem() {

    SyncItem* syncItem = NULL;
    if (updatedKeys && updatedKeys->hasMoreElement()) {
        StringBuffer* s     = (StringBuffer*)updatedKeys->getNextElement();
        syncItem = fillSyncItem(s);
    }
    if (!syncItem) {
        LOG.info("There are no more updated items to be exchanged. Return NULL");
    }
    return syncItem;
}

SyncItem* CacheSyncSource::getFirstDeletedItem() {

    return getNextDeletedItem();
}

/**
 * Return the next SyncItem of updated one. It is used in case of fast sync
 * and retrieve the new data source content.
 */
SyncItem* CacheSyncSource::getNextDeletedItem() {

    SyncItem* syncItem = NULL;
    if (deletedKeys && deletedKeys->hasMoreElement()) {
        StringBuffer* s     = (StringBuffer*)deletedKeys->getNextElement();
        if (!s) {
            return NULL;
        }
        WCHAR* wkey = toWideChar(s->c_str());
        syncItem = new SyncItem(wkey);
        if (wkey) { delete [] wkey; wkey = NULL; }
    }
    if (!syncItem) {
        LOG.info("There are no more deleted items to be exchanged. Return NULL");
    }

    return syncItem;
}

int CacheSyncSource::endSync() {
    saveCache();
    return 0;
}

/**
* The way to calculate the cache is the follow:
* loop on the current element against an array list
* that has the cache. It is the copy of the original cache.
* When an current element is found in the cache, it is removed
* from the cache copy. At the end the remained element
* in the cache are the deleted ones.
*/
bool CacheSyncSource::fillItemModifications() {

    // all the current items keys list
    Enumeration* items = (Enumeration*)getAllItemList();

    if (!items) {
        LOG.error("Error in fillItemModification");
        return false;
    }

    // all the action are done on the copy so we can delete
    // the element found. The remained are the deleted by the user.
    Enumeration& e = cache->getProperties();
    ArrayList cacheCopy;
    while(e.hasMoreElement()) {
        cacheCopy.add(*e.getNextElement());
    }

    StringBuffer* key;
    KeyValuePair* kvp;

    ArrayListEnumeration *newitems = new ArrayListEnumeration(),
                         *moditems = new ArrayListEnumeration(),
                         *delitems = new ArrayListEnumeration();

    if (items) {
        while(items->hasMoreElement()) {
            key = (StringBuffer*)items->getNextElement();
            int size = cacheCopy.size();
            bool foundnew = true;

            for (int i = 0; i < size; i++) {
                kvp = (KeyValuePair*)(cacheCopy[i]);
                if (strcmp(kvp->getKey(), key->c_str()) == 0) {
                    foundnew = false;
                    // see if it is updated.
                    StringBuffer sign = getItemSignature(*key);
                    if (sign != kvp->getValue()) {
                        // there is an update. if equal nothing to do...
                        moditems->add(*key);
                    }
                    cacheCopy.removeElementAt(i);
                    break;
                }
            }
            if (foundnew) {
                newitems->add(*key);
            }
        }
    }

    for(kvp=(KeyValuePair*)cacheCopy.front();kvp;kvp=(KeyValuePair*)cacheCopy.next()){
        delitems->add((StringBuffer&)kvp->getKey());
    }

    newKeys = newitems;
    updatedKeys = moditems;
    deletedKeys = delitems;

    delete items;
    return true;
}

/**
* Save the current status of the cache arrayList
*
*/
int CacheSyncSource::saveCache() {

    LOG.debug("[%s] Saving cache", getName());

    int ret = cache->close();
    return ret;

}

int CacheSyncSource::updateInCache(KeyValuePair& k, const char* action) {

    if (strcmp(action, ADD    ) == 0 ||
        strcmp(action, REPLACE) == 0) {
        cache->setPropertyValue(k.getKey(), k.getValue());
    } else if (strcmp(action, DEL) == 0) {
        cache->removeProperty(k.getKey());
    }
    return 0;
}

void CacheSyncSource::getKeyAndSignature(SyncItem& item, KeyValuePair& kvp) {

    char* t = toMultibyte(item.getKey());
    StringBuffer s(t);
    StringBuffer sign = getItemSignature(s);
    kvp.setKey(t);
    kvp.setValue(sign);
    delete [] t;

}

int CacheSyncSource::addItem(SyncItem& item) {
    int ret = insertItem(item);
    switch (ret) {
        case 200:
        case 201:
        case 418: {
            LOG.info("[%s] Successful add of item with key %s - code %d", getName(), item.getKey(), ret);
            KeyValuePair k;
            getKeyAndSignature(item, k);
            insertInCache(k);
        }
        break;
        default:
            LOG.error("[%s] Failed add of item with key %s - code %d", getName(), item.getKey(), ret);
        break;
    }
    return ret;
}

int CacheSyncSource::updateItem(SyncItem& item) {
    int ret = modifyItem(item);
    switch (ret) {
        case 200:
        case 201:
        case 418: {
            LOG.info("[%s] Successful update of item with key %s - code %d", getName(), item.getKey(), ret);
            KeyValuePair k;
            getKeyAndSignature(item, k);
            updateInCache(k);
        }
        break;
        default:
            LOG.error("[%s] Failed update of item with key %s - code %d", getName(), item.getKey(), ret);
        break;
    }
    return ret;
}

int CacheSyncSource::deleteItem(SyncItem& item) {
    int ret = removeItem(item);
    switch (ret) {
        case 200:
        case 201:
        case 418: {
            LOG.info("[%s] Successful delete of item with key %s - code %d", getName(), item.getKey(), ret);
            char* t = toMultibyte(item.getKey());
            KeyValuePair k (t, "");
            removeFromCache(k);
            delete [] t;
        }
        break;
        default:
            LOG.error("[%s] Failed delete of item with key %s - code %d", getName(), item.getKey(), ret);
        break;
    }

    return ret;
}

END_NAMESPACE
<|endoftext|>


<commit_before>/*************************************************************************
 * Copyright (c) 2014 eProsima. All rights reserved.
 *
 * This copy of eProsima Fast RTPS is licensed to you under the terms described in the
 * FASTRTPS_LIBRARY_LICENSE file included in this distribution.
 *
 *************************************************************************/

/**
 * @file TimedEventImpl.cpp
 *
 */

#include "TimedEventImpl.h"
#include <fastrtps/rtps/resources/TimedEvent.h>
#include <fastrtps/utils/TimeConversion.h>

#include <boost/atomic.hpp>
#include <cassert>

namespace eprosima
{
    namespace fastrtps
    {
        namespace rtps
        {
            class TimerState
            {
                public:

                    typedef enum
                    {
                        INACTIVE = 0,
                        WAITING,
                        CANCELLED,
                        RUNNING,
                        DESTROYED
                    } StateCode;

                    TimerState(TimedEvent::AUTODESTRUCTION_MODE autodestruction) : code_(INACTIVE),
                autodestruction_(autodestruction) {}

                    boost::atomic<StateCode> code_;

                    TimedEvent::AUTODESTRUCTION_MODE autodestruction_;
            };
        }
    }
}

using namespace eprosima::fastrtps::rtps;

TimedEventImpl::TimedEventImpl(TimedEvent* event, boost::asio::io_service &service, const boost::thread& event_thread, boost::posix_time::microseconds interval, TimedEvent::AUTODESTRUCTION_MODE autodestruction) :
timer_(service, interval), m_interval_microsec(interval), mp_event(event),
autodestruction_(autodestruction), state_(std::make_shared<TimerState>(autodestruction)), event_thread_id_(event_thread.get_id())
{
	//TIME_INFINITE(m_timeInfinite);
}

TimedEventImpl::~TimedEventImpl()
{
    boost::unique_lock<boost::mutex> lock(mutex_);
    // Exchange state to avoid race conditions. Any state will go to state TimerState::DESTROYED.
    TimerState::StateCode code = state_.get()->code_.exchange(TimerState::DESTROYED, boost::memory_order_relaxed);

    // code's value cannot be TimerState::DESTROYED. In this case other destructor was called.
    assert(code != TimerState::DESTROYED);

    // If the event is waiting, cancel it.
    if(code == TimerState::WAITING)
        timer_.cancel();

    // If the event is waiting or running, wait it finishes.
    // Don't wait if it is the event thread.
    if(code == TimerState::WAITING || (code == TimerState::RUNNING && event_thread_id_ != boost::this_thread::get_id()))
        cond_.wait(lock);
}

void TimedEventImpl::destroy()
{
}

/* In this function we don't need to exchange the state,
 * because this function try to cancel, but if the event is running
 * in the middle of the operation, it doesn't bother.
 */
void TimedEventImpl::cancel_timer()
{
    TimerState::StateCode code = TimerState::WAITING;

    // Lock timer to protect state_ and timer_ objects.
    boost::unique_lock<boost::mutex> lock(mutex_);

    // Exchange state to avoid race conditions. Only TimerState::WAITING state can be set to TimerState::CANCELLED.
    bool ret = state_.get()->code_.compare_exchange_strong(code, TimerState::CANCELLED, boost::memory_order_relaxed);

    if(ret)
    {
        // Unattach the event state from future event execution.
        state_.reset(new TimerState(autodestruction_));
        // Cancel the event.
        timer_.cancel();
        // Alert to user.
        mp_event->event(TimedEvent::EVENT_ABORT, nullptr);
    }
}

void TimedEventImpl::restart_timer()
{
    // Lock timer to protect state_ and timer_ objects.
    boost::unique_lock<boost::mutex> lock(mutex_);

    // Get current state.
    TimerState::StateCode code = state_.get()->code_.load(boost::memory_order_relaxed);

    // if the code is executed in the event thread, and the event is being destroyed, don't start other event.
    // if the code indicate an event is already waiting, don't start other event.
    if(code != TimerState::DESTROYED && code != TimerState::WAITING)
    {
        state_.get()->code_.store(TimerState::WAITING, boost::memory_order_relaxed);

        timer_.expires_from_now(m_interval_microsec);
        timer_.async_wait(boost::bind(&TimedEventImpl::event,this,boost::asio::placeholders::error, state_));
    }
}

bool TimedEventImpl::update_interval(const Duration_t& inter)
{
    boost::unique_lock<boost::mutex> lock(mutex_);
	m_interval_microsec = boost::posix_time::microseconds(TimeConv::Time_t2MicroSecondsInt64(inter));
	return true;
}

bool TimedEventImpl::update_interval_millisec(double time_millisec)
{
    boost::unique_lock<boost::mutex> lock(mutex_);
	m_interval_microsec = boost::posix_time::microseconds((int64_t)(time_millisec*1000));
	return true;
}

void TimedEventImpl::event(const boost::system::error_code& ec, const std::shared_ptr<TimerState>& state)
{
    TimerState::StateCode scode = TimerState::WAITING;

    // First step is exchange the state, to prevent race condition from the destruction case.
    bool ret =  state.get()->code_.compare_exchange_strong(scode, TimerState::RUNNING, boost::memory_order_relaxed);

    // Check bad preconditions
    assert(!(ret && scode == TimerState::DESTROYED));

    if(scode != TimerState::WAITING || !ret || ec == boost::asio::error::operation_aborted)
    {
        // If autodestruction is TimedEvent::ALLWAYS, delete the event.
        if(scode != TimerState::DESTROYED && state.get()->autodestruction_ == TimedEvent::ALLWAYS)
        {
            delete this->mp_event;
        }
        // If code is TimerState::DESTROYED, then the destructor is waiting because this event were in state TimerState::WAITING.
        else if(scode == TimerState::DESTROYED)
        {
            boost::unique_lock<boost::mutex> lock(mutex_);
            cond_.notify_one();
            lock.unlock();
        }

        return;
    }

    TimedEvent::EventCode code = TimedEvent::EVENT_MSG;
    const char *message = nullptr;

    if(ec == boost::system::errc::success)
        code = TimedEvent::EVENT_SUCCESS;
    else
        message = ec.message().c_str();

    this->mp_event->event(code, message);

    // If the destructor is waiting, signal it.
    boost::unique_lock<boost::mutex> lock(mutex_);

    scode = TimerState::RUNNING;
    ret =  state.get()->code_.compare_exchange_strong(scode, TimerState::INACTIVE, boost::memory_order_relaxed);

    if(scode == TimerState::DESTROYED)
        cond_.notify_one();

    //Unlock mutex
    lock.unlock();

    if(state.get()->autodestruction_ == TimedEvent::ALLWAYS ||
            (code == TimedEvent::EVENT_SUCCESS && state.get()->autodestruction_ == TimedEvent::ON_SUCCESS))
    {
        delete this->mp_event;
    }
}
<commit_msg>Refs #1538. Revert code test of destroy in event.<commit_after>/*************************************************************************
 * Copyright (c) 2014 eProsima. All rights reserved.
 *
 * This copy of eProsima Fast RTPS is licensed to you under the terms described in the
 * FASTRTPS_LIBRARY_LICENSE file included in this distribution.
 *
 *************************************************************************/

/**
 * @file TimedEventImpl.cpp
 *
 */

#include "TimedEventImpl.h"
#include <fastrtps/rtps/resources/TimedEvent.h>
#include <fastrtps/utils/TimeConversion.h>

#include <boost/atomic.hpp>
#include <cassert>

namespace eprosima
{
    namespace fastrtps
    {
        namespace rtps
        {
            class TimerState
            {
                public:

                    typedef enum
                    {
                        INACTIVE = 0,
                        WAITING,
                        CANCELLED,
                        RUNNING,
                        DESTROYED
                    } StateCode;

                    TimerState(TimedEvent::AUTODESTRUCTION_MODE autodestruction) : code_(INACTIVE),
                autodestruction_(autodestruction) {}

                    boost::atomic<StateCode> code_;

                    TimedEvent::AUTODESTRUCTION_MODE autodestruction_;
            };
        }
    }
}

using namespace eprosima::fastrtps::rtps;

TimedEventImpl::TimedEventImpl(TimedEvent* event, boost::asio::io_service &service, const boost::thread& event_thread, boost::posix_time::microseconds interval, TimedEvent::AUTODESTRUCTION_MODE autodestruction) :
timer_(service, interval), m_interval_microsec(interval), mp_event(event),
autodestruction_(autodestruction), state_(std::make_shared<TimerState>(autodestruction)), event_thread_id_(event_thread.get_id())
{
	//TIME_INFINITE(m_timeInfinite);
}

TimedEventImpl::~TimedEventImpl()
{
}

void TimedEventImpl::destroy()
{
    boost::unique_lock<boost::mutex> lock(mutex_);
    // Exchange state to avoid race conditions. Any state will go to state TimerState::DESTROYED.
    TimerState::StateCode code = state_.get()->code_.exchange(TimerState::DESTROYED, boost::memory_order_relaxed);

    // code's value cannot be TimerState::DESTROYED. In this case other destructor was called.
    assert(code != TimerState::DESTROYED);

    // If the event is waiting, cancel it.
    if(code == TimerState::WAITING)
        timer_.cancel();

    // If the event is waiting or running, wait it finishes.
    // Don't wait if it is the event thread.
    if(code == TimerState::WAITING || (code == TimerState::RUNNING && event_thread_id_ != boost::this_thread::get_id()))
        cond_.wait(lock);
}

/* In this function we don't need to exchange the state,
 * because this function try to cancel, but if the event is running
 * in the middle of the operation, it doesn't bother.
 */
void TimedEventImpl::cancel_timer()
{
    TimerState::StateCode code = TimerState::WAITING;

    // Lock timer to protect state_ and timer_ objects.
    boost::unique_lock<boost::mutex> lock(mutex_);

    // Exchange state to avoid race conditions. Only TimerState::WAITING state can be set to TimerState::CANCELLED.
    bool ret = state_.get()->code_.compare_exchange_strong(code, TimerState::CANCELLED, boost::memory_order_relaxed);

    if(ret)
    {
        // Unattach the event state from future event execution.
        state_.reset(new TimerState(autodestruction_));
        // Cancel the event.
        timer_.cancel();
        // Alert to user.
        mp_event->event(TimedEvent::EVENT_ABORT, nullptr);
    }
}

void TimedEventImpl::restart_timer()
{
    // Lock timer to protect state_ and timer_ objects.
    boost::unique_lock<boost::mutex> lock(mutex_);

    // Get current state.
    TimerState::StateCode code = state_.get()->code_.load(boost::memory_order_relaxed);

    // if the code is executed in the event thread, and the event is being destroyed, don't start other event.
    // if the code indicate an event is already waiting, don't start other event.
    if(code != TimerState::DESTROYED && code != TimerState::WAITING)
    {
        state_.get()->code_.store(TimerState::WAITING, boost::memory_order_relaxed);

        timer_.expires_from_now(m_interval_microsec);
        timer_.async_wait(boost::bind(&TimedEventImpl::event,this,boost::asio::placeholders::error, state_));
    }
}

bool TimedEventImpl::update_interval(const Duration_t& inter)
{
    boost::unique_lock<boost::mutex> lock(mutex_);
	m_interval_microsec = boost::posix_time::microseconds(TimeConv::Time_t2MicroSecondsInt64(inter));
	return true;
}

bool TimedEventImpl::update_interval_millisec(double time_millisec)
{
    boost::unique_lock<boost::mutex> lock(mutex_);
	m_interval_microsec = boost::posix_time::microseconds((int64_t)(time_millisec*1000));
	return true;
}

void TimedEventImpl::event(const boost::system::error_code& ec, const std::shared_ptr<TimerState>& state)
{
    TimerState::StateCode scode = TimerState::WAITING;

    // First step is exchange the state, to prevent race condition from the destruction case.
    bool ret =  state.get()->code_.compare_exchange_strong(scode, TimerState::RUNNING, boost::memory_order_relaxed);

    // Check bad preconditions
    assert(!(ret && scode == TimerState::DESTROYED));

    if(scode != TimerState::WAITING || !ret || ec == boost::asio::error::operation_aborted)
    {
        // If autodestruction is TimedEvent::ALLWAYS, delete the event.
        if(scode != TimerState::DESTROYED && state.get()->autodestruction_ == TimedEvent::ALLWAYS)
        {
            delete this->mp_event;
        }
        // If code is TimerState::DESTROYED, then the destructor is waiting because this event were in state TimerState::WAITING.
        else if(scode == TimerState::DESTROYED)
        {
            boost::unique_lock<boost::mutex> lock(mutex_);
            cond_.notify_one();
            lock.unlock();
        }

        return;
    }

    TimedEvent::EventCode code = TimedEvent::EVENT_MSG;
    const char *message = nullptr;

    if(ec == boost::system::errc::success)
        code = TimedEvent::EVENT_SUCCESS;
    else
        message = ec.message().c_str();

    this->mp_event->event(code, message);

    // If the destructor is waiting, signal it.
    boost::unique_lock<boost::mutex> lock(mutex_);

    scode = TimerState::RUNNING;
    ret =  state.get()->code_.compare_exchange_strong(scode, TimerState::INACTIVE, boost::memory_order_relaxed);

    if(scode == TimerState::DESTROYED)
        cond_.notify_one();

    //Unlock mutex
    lock.unlock();

    if(state.get()->autodestruction_ == TimedEvent::ALLWAYS ||
            (code == TimedEvent::EVENT_SUCCESS && state.get()->autodestruction_ == TimedEvent::ON_SUCCESS))
    {
        delete this->mp_event;
    }
}
<|endoftext|>