<commit_before>// Copyright (c) Sandeep Mistry. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

#include <LoRa.h>

// registers
#define REG_FIFO                 0x00
#define REG_OP_MODE              0x01
#define REG_FRF_MSB              0x06
#define REG_FRF_MID              0x07
#define REG_FRF_LSB              0x08
#define REG_PA_CONFIG            0x09
#define REG_LNA                  0x0c
#define REG_FIFO_ADDR_PTR        0x0d
#define REG_FIFO_TX_BASE_ADDR    0x0e
#define REG_FIFO_RX_BASE_ADDR    0x0f
#define REG_FIFO_RX_CURRENT_ADDR 0x10
#define REG_IRQ_FLAGS            0x12
#define REG_RX_NB_BYTES          0x13
#define REG_PKT_SNR_VALUE        0x19
#define REG_PKT_RSSI_VALUE       0x1a
#define REG_MODEM_CONFIG_1       0x1d
#define REG_MODEM_CONFIG_2       0x1e
#define REG_PREAMBLE_MSB         0x20
#define REG_PREAMBLE_LSB         0x21
#define REG_PAYLOAD_LENGTH       0x22
#define REG_MODEM_CONFIG_3       0x26
#define REG_RSSI_WIDEBAND        0x2c
#define REG_DETECTION_OPTIMIZE   0x31
#define REG_DETECTION_THRESHOLD  0x37
#define REG_SYNC_WORD            0x39
#define REG_DIO_MAPPING_1        0x40
#define REG_VERSION              0x42

// modes
#define MODE_LONG_RANGE_MODE     0x80
#define MODE_SLEEP               0x00
#define MODE_STDBY               0x01
#define MODE_TX                  0x03
#define MODE_RX_CONTINUOUS       0x05
#define MODE_RX_SINGLE           0x06

// PA config
#define PA_BOOST                 0x80

// IRQ masks
#define IRQ_TX_DONE_MASK           0x08
#define IRQ_PAYLOAD_CRC_ERROR_MASK 0x20
#define IRQ_RX_DONE_MASK           0x40

#define MAX_PKT_LENGTH           255

LoRaClass::LoRaClass() :
  _spiSettings(8E6, MSBFIRST, SPI_MODE0),
  _ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN),
  _frequency(0),
  _packetIndex(0),
  _implicitHeaderMode(0),
  _onReceive(NULL)
{
  // overide Stream timeout value
  setTimeout(0);
}

int LoRaClass::begin(long frequency)
{
  // setup pins
  pinMode(_ss, OUTPUT);
  pinMode(_reset, OUTPUT);

  // perform reset
  digitalWrite(_reset, LOW);
  delay(10);
  digitalWrite(_reset, HIGH);
  delay(10);

  // set SS high
  digitalWrite(_ss, HIGH);

  // start SPI
  SPI.begin();

  // check version
  uint8_t version = readRegister(REG_VERSION);
  if (version != 0x12) {
    return 0;
  }

  // put in sleep mode
  sleep();

  // set frequency
  setFrequency(frequency);

  // set base addresses
  writeRegister(REG_FIFO_TX_BASE_ADDR, 0);
  writeRegister(REG_FIFO_RX_BASE_ADDR, 0);

  // set LNA boost
  writeRegister(REG_LNA, readRegister(REG_LNA) | 0x03);

  // set auto AGC
  writeRegister(REG_MODEM_CONFIG_3, 0x04);

  // set output power to 17 dBm
  setTxPower(17);

  // put in standby mode
  idle();

  return 1;
}

void LoRaClass::end()
{
  // put in sleep mode
  sleep();

  // stop SPI
  SPI.end();
}

int LoRaClass::beginPacket(int implicitHeader)
{
  // put in standby mode
  idle();

  if (implicitHeader) {
    implicitHeaderMode();
  } else {
    explicitHeaderMode();
  }

  // reset FIFO address and paload length
  writeRegister(REG_FIFO_ADDR_PTR, 0);
  writeRegister(REG_PAYLOAD_LENGTH, 0);

  return 1;
}

int LoRaClass::endPacket()
{
  // put in TX mode
  writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_TX);

  // wait for TX done
  while((readRegister(REG_IRQ_FLAGS) & IRQ_TX_DONE_MASK) == 0);

  // clear IRQ's
  writeRegister(REG_IRQ_FLAGS, IRQ_TX_DONE_MASK);

  return 1;
}

int LoRaClass::parsePacket(int size)
{
  int packetLength = 0;
  int irqFlags = readRegister(REG_IRQ_FLAGS);

  if (size > 0) {
    implicitHeaderMode();

    writeRegister(REG_PAYLOAD_LENGTH, size & 0xff);
  } else {
    explicitHeaderMode();
  }

  // clear IRQ's
  writeRegister(REG_IRQ_FLAGS, irqFlags);

  if ((irqFlags & IRQ_RX_DONE_MASK) && (irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) {
    // received a packet
    _packetIndex = 0;

    // read packet length
    if (_implicitHeaderMode) {
      packetLength = readRegister(REG_PAYLOAD_LENGTH);
    } else {
      packetLength = readRegister(REG_RX_NB_BYTES);
    }

    // set FIFO address to current RX address
    writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_CURRENT_ADDR));

    // put in standby mode
    idle();
  } else if (readRegister(REG_OP_MODE) != (MODE_LONG_RANGE_MODE | MODE_RX_SINGLE)) {
    // not currently in RX mode

    // reset FIFO address
    writeRegister(REG_FIFO_ADDR_PTR, 0);

    // put in single RX mode
    writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_SINGLE);
  }

  return packetLength;
}

int LoRaClass::packetRssi()
{
  return (readRegister(REG_PKT_RSSI_VALUE) - (_frequency < 868E6 ? 164 : 157));
}

float LoRaClass::packetSnr()
{
  return ((int8_t)readRegister(REG_PKT_SNR_VALUE)) * 0.25;
}

size_t LoRaClass::write(uint8_t byte)
{
  return write(&byte, sizeof(byte));
}

size_t LoRaClass::write(const uint8_t *buffer, size_t size)
{
  int currentLength = readRegister(REG_PAYLOAD_LENGTH);

  // check size
  if ((currentLength + size) > MAX_PKT_LENGTH) {
    size = MAX_PKT_LENGTH - currentLength;
  }

  // write data
  for (size_t i = 0; i < size; i++) {
    writeRegister(REG_FIFO, buffer[i]);
  }

  // update length
  writeRegister(REG_PAYLOAD_LENGTH, currentLength + size);

  return size;
}

int LoRaClass::available()
{
  return (readRegister(REG_RX_NB_BYTES) - _packetIndex);
}

int LoRaClass::read()
{
  if (!available()) {
    return -1;
  }

  _packetIndex++;

  return readRegister(REG_FIFO);
}

int LoRaClass::peek()
{
  if (!available()) {
    return -1;
  }

  // store current FIFO address
  int currentAddress = readRegister(REG_FIFO_ADDR_PTR);

  // read
  uint8_t b = readRegister(REG_FIFO);

  // restore FIFO address
  writeRegister(REG_FIFO_ADDR_PTR, currentAddress);

  return b;
}

void LoRaClass::flush()
{
}

void LoRaClass::onReceive(void(*callback)(int))
{
  _onReceive = callback;

  if (callback) {
    writeRegister(REG_DIO_MAPPING_1, 0x00);

    attachInterrupt(digitalPinToInterrupt(_dio0), LoRaClass::onDio0Rise, RISING);
  } else {
    detachInterrupt(digitalPinToInterrupt(_dio0));
  }
}

void LoRaClass::receive(int size)
{
  if (size > 0) {
    implicitHeaderMode();

    writeRegister(REG_PAYLOAD_LENGTH, size & 0xff);
  } else {
    explicitHeaderMode();
  }

  writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_CONTINUOUS);
}

void LoRaClass::idle()
{
  writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_STDBY);
}

void LoRaClass::sleep()
{
  writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_SLEEP);
}

void LoRaClass::setTxPower(int level, int outputPin)
{
  if (PA_OUTPUT_RFO_PIN == outputPin) {
    // RFO
    if (level < 0) {
      level = 0;
    } else if (level > 14) {
      level = 14;
    }

    writeRegister(REG_PA_CONFIG, 0x70 | level);
  } else {
    // PA BOOST
    if (level < 2) {
      level = 2;
    } else if (level > 17) {
      level = 17;
    }

    writeRegister(REG_PA_CONFIG, PA_BOOST | (level - 2));
  }
}

void LoRaClass::setFrequency(long frequency)
{
  _frequency = frequency;

  uint64_t frf = ((uint64_t)frequency << 19) / 32000000;

  writeRegister(REG_FRF_MSB, (uint8_t)(frf >> 16));
  writeRegister(REG_FRF_MID, (uint8_t)(frf >> 8));
  writeRegister(REG_FRF_LSB, (uint8_t)(frf >> 0));
}

void LoRaClass::setSpreadingFactor(int sf)
{
  if (sf < 6) {
    sf = 6;
  } else if (sf > 12) {
    sf = 12;
  }

  if (sf == 6) {
    writeRegister(REG_DETECTION_OPTIMIZE, 0xc5);
    writeRegister(REG_DETECTION_THRESHOLD, 0x0c);
  } else {
    writeRegister(REG_DETECTION_OPTIMIZE, 0xc3);
    writeRegister(REG_DETECTION_THRESHOLD, 0x0a);
  }

  writeRegister(REG_MODEM_CONFIG_2, (readRegister(REG_MODEM_CONFIG_2) & 0x0f) | ((sf << 4) & 0xf0));
}

void LoRaClass::setSignalBandwidth(long sbw)
{
  int bw;

  if (sbw <= 7.8E3) {
    bw = 0;
  } else if (sbw <= 10.4E3) {
    bw = 1;
  } else if (sbw <= 15.6E3) {
    bw = 2;
  } else if (sbw <= 20.8E3) {
    bw = 3;
  } else if (sbw <= 31.25E3) {
    bw = 4;
  } else if (sbw <= 41.7E3) {
    bw = 5;
  } else if (sbw <= 62.5E3) {
    bw = 6;
  } else if (sbw <= 125E3) {
    bw = 7;
  } else if (sbw <= 250E3) {
    bw = 8;
  } else /*if (sbw <= 250E3)*/ {
    bw = 9;
  }

  writeRegister(REG_MODEM_CONFIG_1, (readRegister(REG_MODEM_CONFIG_1) & 0x0f) | (bw << 4));
}

void LoRaClass::setCodingRate4(int denominator)
{
  if (denominator < 5) {
    denominator = 5;
  } else if (denominator > 8) {
    denominator = 8;
  }

  int cr = denominator - 4;

  writeRegister(REG_MODEM_CONFIG_1, (readRegister(REG_MODEM_CONFIG_1) & 0xf1) | (cr << 1));
}

void LoRaClass::setPreambleLength(long length)
{
  writeRegister(REG_PREAMBLE_MSB, (uint8_t)(length >> 8));
  writeRegister(REG_PREAMBLE_LSB, (uint8_t)(length >> 0));
}

void LoRaClass::setSyncWord(int sw)
{
  writeRegister(REG_SYNC_WORD, sw);
}

void LoRaClass::enableCrc()
{
  writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) | 0x04);
}

void LoRaClass::disableCrc()
{
  writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) & 0xfb);
}

byte LoRaClass::random()
{
  return readRegister(REG_RSSI_WIDEBAND);
}

void LoRaClass::setPins(int ss, int reset, int dio0)
{
  _ss = ss;
  _reset = reset;
  _dio0 = dio0;
}

void LoRaClass::setSPIFrequency(uint32_t frequency)
{
  _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0);
}

void LoRaClass::dumpRegisters(Stream& out)
{
  for (int i = 0; i < 128; i++) {
    out.print("0x");
    out.print(i, HEX);
    out.print(": 0x");
    out.println(readRegister(i), HEX);
  }
}

void LoRaClass::explicitHeaderMode()
{
  _implicitHeaderMode = 0;

  writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) & 0xfe);
}

void LoRaClass::implicitHeaderMode()
{
  _implicitHeaderMode = 1;

  writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) | 0x01);
}

void LoRaClass::handleDio0Rise()
{
  int irqFlags = readRegister(REG_IRQ_FLAGS);

  // clear IRQ's
  writeRegister(REG_IRQ_FLAGS, irqFlags);

  if ((irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) {
    // received a packet
    _packetIndex = 0;

    // read packet length
    int packetLength = _implicitHeaderMode ? readRegister(REG_PAYLOAD_LENGTH) : readRegister(REG_RX_NB_BYTES);

    // set FIFO address to current RX address
    writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_CURRENT_ADDR));

    if (_onReceive) {
      _onReceive(packetLength);
    }

    // reset FIFO address
    writeRegister(REG_FIFO_ADDR_PTR, 0);
  }
}

uint8_t LoRaClass::readRegister(uint8_t address)
{
  return singleTransfer(address & 0x7f, 0x00);
}

void LoRaClass::writeRegister(uint8_t address, uint8_t value)
{
  singleTransfer(address | 0x80, value);
}

uint8_t LoRaClass::singleTransfer(uint8_t address, uint8_t value)
{
  uint8_t response;

  digitalWrite(_ss, LOW);

  SPI.beginTransaction(_spiSettings);
  SPI.transfer(address);
  response = SPI.transfer(value);
  SPI.endTransaction();

  digitalWrite(_ss, HIGH);

  return response;
}

void LoRaClass::onDio0Rise()
{
  LoRa.handleDio0Rise();
}

LoRaClass LoRa;
<commit_msg>stabilize ESP8266 watchdog (#61)<commit_after>// Copyright (c) Sandeep Mistry. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

#include <LoRa.h>

// registers
#define REG_FIFO                 0x00
#define REG_OP_MODE              0x01
#define REG_FRF_MSB              0x06
#define REG_FRF_MID              0x07
#define REG_FRF_LSB              0x08
#define REG_PA_CONFIG            0x09
#define REG_LNA                  0x0c
#define REG_FIFO_ADDR_PTR        0x0d
#define REG_FIFO_TX_BASE_ADDR    0x0e
#define REG_FIFO_RX_BASE_ADDR    0x0f
#define REG_FIFO_RX_CURRENT_ADDR 0x10
#define REG_IRQ_FLAGS            0x12
#define REG_RX_NB_BYTES          0x13
#define REG_PKT_SNR_VALUE        0x19
#define REG_PKT_RSSI_VALUE       0x1a
#define REG_MODEM_CONFIG_1       0x1d
#define REG_MODEM_CONFIG_2       0x1e
#define REG_PREAMBLE_MSB         0x20
#define REG_PREAMBLE_LSB         0x21
#define REG_PAYLOAD_LENGTH       0x22
#define REG_MODEM_CONFIG_3       0x26
#define REG_RSSI_WIDEBAND        0x2c
#define REG_DETECTION_OPTIMIZE   0x31
#define REG_DETECTION_THRESHOLD  0x37
#define REG_SYNC_WORD            0x39
#define REG_DIO_MAPPING_1        0x40
#define REG_VERSION              0x42

// modes
#define MODE_LONG_RANGE_MODE     0x80
#define MODE_SLEEP               0x00
#define MODE_STDBY               0x01
#define MODE_TX                  0x03
#define MODE_RX_CONTINUOUS       0x05
#define MODE_RX_SINGLE           0x06

// PA config
#define PA_BOOST                 0x80

// IRQ masks
#define IRQ_TX_DONE_MASK           0x08
#define IRQ_PAYLOAD_CRC_ERROR_MASK 0x20
#define IRQ_RX_DONE_MASK           0x40

#define MAX_PKT_LENGTH           255

LoRaClass::LoRaClass() :
  _spiSettings(8E6, MSBFIRST, SPI_MODE0),
  _ss(LORA_DEFAULT_SS_PIN), _reset(LORA_DEFAULT_RESET_PIN), _dio0(LORA_DEFAULT_DIO0_PIN),
  _frequency(0),
  _packetIndex(0),
  _implicitHeaderMode(0),
  _onReceive(NULL)
{
  // overide Stream timeout value
  setTimeout(0);
}

int LoRaClass::begin(long frequency)
{
  // setup pins
  pinMode(_ss, OUTPUT);
  pinMode(_reset, OUTPUT);

  // perform reset
  digitalWrite(_reset, LOW);
  delay(10);
  digitalWrite(_reset, HIGH);
  delay(10);

  // set SS high
  digitalWrite(_ss, HIGH);

  // start SPI
  SPI.begin();

  // check version
  uint8_t version = readRegister(REG_VERSION);
  if (version != 0x12) {
    return 0;
  }

  // put in sleep mode
  sleep();

  // set frequency
  setFrequency(frequency);

  // set base addresses
  writeRegister(REG_FIFO_TX_BASE_ADDR, 0);
  writeRegister(REG_FIFO_RX_BASE_ADDR, 0);

  // set LNA boost
  writeRegister(REG_LNA, readRegister(REG_LNA) | 0x03);

  // set auto AGC
  writeRegister(REG_MODEM_CONFIG_3, 0x04);

  // set output power to 17 dBm
  setTxPower(17);

  // put in standby mode
  idle();

  return 1;
}

void LoRaClass::end()
{
  // put in sleep mode
  sleep();

  // stop SPI
  SPI.end();
}

int LoRaClass::beginPacket(int implicitHeader)
{
  // put in standby mode
  idle();

  if (implicitHeader) {
    implicitHeaderMode();
  } else {
    explicitHeaderMode();
  }

  // reset FIFO address and paload length
  writeRegister(REG_FIFO_ADDR_PTR, 0);
  writeRegister(REG_PAYLOAD_LENGTH, 0);

  return 1;
}

int LoRaClass::endPacket()
{
  // put in TX mode
  writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_TX);

  // wait for TX done
  while ((readRegister(REG_IRQ_FLAGS) & IRQ_TX_DONE_MASK) == 0) {
    yield();
  }

  // clear IRQ's
  writeRegister(REG_IRQ_FLAGS, IRQ_TX_DONE_MASK);

  return 1;
}

int LoRaClass::parsePacket(int size)
{
  int packetLength = 0;
  int irqFlags = readRegister(REG_IRQ_FLAGS);

  if (size > 0) {
    implicitHeaderMode();

    writeRegister(REG_PAYLOAD_LENGTH, size & 0xff);
  } else {
    explicitHeaderMode();
  }

  // clear IRQ's
  writeRegister(REG_IRQ_FLAGS, irqFlags);

  if ((irqFlags & IRQ_RX_DONE_MASK) && (irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) {
    // received a packet
    _packetIndex = 0;

    // read packet length
    if (_implicitHeaderMode) {
      packetLength = readRegister(REG_PAYLOAD_LENGTH);
    } else {
      packetLength = readRegister(REG_RX_NB_BYTES);
    }

    // set FIFO address to current RX address
    writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_CURRENT_ADDR));

    // put in standby mode
    idle();
  } else if (readRegister(REG_OP_MODE) != (MODE_LONG_RANGE_MODE | MODE_RX_SINGLE)) {
    // not currently in RX mode

    // reset FIFO address
    writeRegister(REG_FIFO_ADDR_PTR, 0);

    // put in single RX mode
    writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_SINGLE);
  }

  return packetLength;
}

int LoRaClass::packetRssi()
{
  return (readRegister(REG_PKT_RSSI_VALUE) - (_frequency < 868E6 ? 164 : 157));
}

float LoRaClass::packetSnr()
{
  return ((int8_t)readRegister(REG_PKT_SNR_VALUE)) * 0.25;
}

size_t LoRaClass::write(uint8_t byte)
{
  return write(&byte, sizeof(byte));
}

size_t LoRaClass::write(const uint8_t *buffer, size_t size)
{
  int currentLength = readRegister(REG_PAYLOAD_LENGTH);

  // check size
  if ((currentLength + size) > MAX_PKT_LENGTH) {
    size = MAX_PKT_LENGTH - currentLength;
  }

  // write data
  for (size_t i = 0; i < size; i++) {
    writeRegister(REG_FIFO, buffer[i]);
  }

  // update length
  writeRegister(REG_PAYLOAD_LENGTH, currentLength + size);

  return size;
}

int LoRaClass::available()
{
  return (readRegister(REG_RX_NB_BYTES) - _packetIndex);
}

int LoRaClass::read()
{
  if (!available()) {
    return -1;
  }

  _packetIndex++;

  return readRegister(REG_FIFO);
}

int LoRaClass::peek()
{
  if (!available()) {
    return -1;
  }

  // store current FIFO address
  int currentAddress = readRegister(REG_FIFO_ADDR_PTR);

  // read
  uint8_t b = readRegister(REG_FIFO);

  // restore FIFO address
  writeRegister(REG_FIFO_ADDR_PTR, currentAddress);

  return b;
}

void LoRaClass::flush()
{
}

void LoRaClass::onReceive(void(*callback)(int))
{
  _onReceive = callback;

  if (callback) {
    writeRegister(REG_DIO_MAPPING_1, 0x00);

    attachInterrupt(digitalPinToInterrupt(_dio0), LoRaClass::onDio0Rise, RISING);
  } else {
    detachInterrupt(digitalPinToInterrupt(_dio0));
  }
}

void LoRaClass::receive(int size)
{
  if (size > 0) {
    implicitHeaderMode();

    writeRegister(REG_PAYLOAD_LENGTH, size & 0xff);
  } else {
    explicitHeaderMode();
  }

  writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_RX_CONTINUOUS);
}

void LoRaClass::idle()
{
  writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_STDBY);
}

void LoRaClass::sleep()
{
  writeRegister(REG_OP_MODE, MODE_LONG_RANGE_MODE | MODE_SLEEP);
}

void LoRaClass::setTxPower(int level, int outputPin)
{
  if (PA_OUTPUT_RFO_PIN == outputPin) {
    // RFO
    if (level < 0) {
      level = 0;
    } else if (level > 14) {
      level = 14;
    }

    writeRegister(REG_PA_CONFIG, 0x70 | level);
  } else {
    // PA BOOST
    if (level < 2) {
      level = 2;
    } else if (level > 17) {
      level = 17;
    }

    writeRegister(REG_PA_CONFIG, PA_BOOST | (level - 2));
  }
}

void LoRaClass::setFrequency(long frequency)
{
  _frequency = frequency;

  uint64_t frf = ((uint64_t)frequency << 19) / 32000000;

  writeRegister(REG_FRF_MSB, (uint8_t)(frf >> 16));
  writeRegister(REG_FRF_MID, (uint8_t)(frf >> 8));
  writeRegister(REG_FRF_LSB, (uint8_t)(frf >> 0));
}

void LoRaClass::setSpreadingFactor(int sf)
{
  if (sf < 6) {
    sf = 6;
  } else if (sf > 12) {
    sf = 12;
  }

  if (sf == 6) {
    writeRegister(REG_DETECTION_OPTIMIZE, 0xc5);
    writeRegister(REG_DETECTION_THRESHOLD, 0x0c);
  } else {
    writeRegister(REG_DETECTION_OPTIMIZE, 0xc3);
    writeRegister(REG_DETECTION_THRESHOLD, 0x0a);
  }

  writeRegister(REG_MODEM_CONFIG_2, (readRegister(REG_MODEM_CONFIG_2) & 0x0f) | ((sf << 4) & 0xf0));
}

void LoRaClass::setSignalBandwidth(long sbw)
{
  int bw;

  if (sbw <= 7.8E3) {
    bw = 0;
  } else if (sbw <= 10.4E3) {
    bw = 1;
  } else if (sbw <= 15.6E3) {
    bw = 2;
  } else if (sbw <= 20.8E3) {
    bw = 3;
  } else if (sbw <= 31.25E3) {
    bw = 4;
  } else if (sbw <= 41.7E3) {
    bw = 5;
  } else if (sbw <= 62.5E3) {
    bw = 6;
  } else if (sbw <= 125E3) {
    bw = 7;
  } else if (sbw <= 250E3) {
    bw = 8;
  } else /*if (sbw <= 250E3)*/ {
    bw = 9;
  }

  writeRegister(REG_MODEM_CONFIG_1, (readRegister(REG_MODEM_CONFIG_1) & 0x0f) | (bw << 4));
}

void LoRaClass::setCodingRate4(int denominator)
{
  if (denominator < 5) {
    denominator = 5;
  } else if (denominator > 8) {
    denominator = 8;
  }

  int cr = denominator - 4;

  writeRegister(REG_MODEM_CONFIG_1, (readRegister(REG_MODEM_CONFIG_1) & 0xf1) | (cr << 1));
}

void LoRaClass::setPreambleLength(long length)
{
  writeRegister(REG_PREAMBLE_MSB, (uint8_t)(length >> 8));
  writeRegister(REG_PREAMBLE_LSB, (uint8_t)(length >> 0));
}

void LoRaClass::setSyncWord(int sw)
{
  writeRegister(REG_SYNC_WORD, sw);
}

void LoRaClass::enableCrc()
{
  writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) | 0x04);
}

void LoRaClass::disableCrc()
{
  writeRegister(REG_MODEM_CONFIG_2, readRegister(REG_MODEM_CONFIG_2) & 0xfb);
}

byte LoRaClass::random()
{
  return readRegister(REG_RSSI_WIDEBAND);
}

void LoRaClass::setPins(int ss, int reset, int dio0)
{
  _ss = ss;
  _reset = reset;
  _dio0 = dio0;
}

void LoRaClass::setSPIFrequency(uint32_t frequency)
{
  _spiSettings = SPISettings(frequency, MSBFIRST, SPI_MODE0);
}

void LoRaClass::dumpRegisters(Stream& out)
{
  for (int i = 0; i < 128; i++) {
    out.print("0x");
    out.print(i, HEX);
    out.print(": 0x");
    out.println(readRegister(i), HEX);
  }
}

void LoRaClass::explicitHeaderMode()
{
  _implicitHeaderMode = 0;

  writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) & 0xfe);
}

void LoRaClass::implicitHeaderMode()
{
  _implicitHeaderMode = 1;

  writeRegister(REG_MODEM_CONFIG_1, readRegister(REG_MODEM_CONFIG_1) | 0x01);
}

void LoRaClass::handleDio0Rise()
{
  int irqFlags = readRegister(REG_IRQ_FLAGS);

  // clear IRQ's
  writeRegister(REG_IRQ_FLAGS, irqFlags);

  if ((irqFlags & IRQ_PAYLOAD_CRC_ERROR_MASK) == 0) {
    // received a packet
    _packetIndex = 0;

    // read packet length
    int packetLength = _implicitHeaderMode ? readRegister(REG_PAYLOAD_LENGTH) : readRegister(REG_RX_NB_BYTES);

    // set FIFO address to current RX address
    writeRegister(REG_FIFO_ADDR_PTR, readRegister(REG_FIFO_RX_CURRENT_ADDR));

    if (_onReceive) {
      _onReceive(packetLength);
    }

    // reset FIFO address
    writeRegister(REG_FIFO_ADDR_PTR, 0);
  }
}

uint8_t LoRaClass::readRegister(uint8_t address)
{
  return singleTransfer(address & 0x7f, 0x00);
}

void LoRaClass::writeRegister(uint8_t address, uint8_t value)
{
  singleTransfer(address | 0x80, value);
}

uint8_t LoRaClass::singleTransfer(uint8_t address, uint8_t value)
{
  uint8_t response;

  digitalWrite(_ss, LOW);

  SPI.beginTransaction(_spiSettings);
  SPI.transfer(address);
  response = SPI.transfer(value);
  SPI.endTransaction();

  digitalWrite(_ss, HIGH);

  return response;
}

void LoRaClass::onDio0Rise()
{
  LoRa.handleDio0Rise();
}

LoRaClass LoRa;
<|endoftext|>


<commit_before>// -*- C++ -*-

/**
 * @file   gnuwrapper.cpp
 * @brief  Replaces malloc family on GNU/Linux with custom versions.
 * @author Emery Berger <http://www.cs.umass.edu/~emery>
 * @note   Copyright (C) 2010 by Emery Berger, University of Massachusetts Amherst.
 */

#ifndef __GNUC__
#error "This file requires the GNU compiler."
#endif

#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include <new>
#include <pthread.h>
#include <sys/cdefs.h>

#include "heaplayers.h"

/*
  To use this library,
  you only need to define the following allocation functions:

  - xxmalloc
  - xxfree
  - xxmalloc_usable_size
  - xxmalloc_lock
  - xxmalloc_unlock

  See the extern "C" block below for function prototypes and more
  details. YOU SHOULD NOT NEED TO MODIFY ANY OF THE CODE HERE TO
  SUPPORT ANY ALLOCATOR.

  LIMITATIONS:

  - This wrapper assumes that the underlying allocator will do "the
    right thing" when xxfree() is called with a pointer internal to an
    allocated object. Header-based allocators, for example, need not
    apply.

*/

#define WEAK(x) __attribute__ ((weak, alias(#x)))
#ifndef __THROW
#define __THROW
#endif

#define CUSTOM_PREFIX(x) custom##x

#define WEAK_REDEF1(type,fname,arg1) type fname(arg1) __THROW WEAK(custom##fname)
#define WEAK_REDEF2(type,fname,arg1,arg2) type fname(arg1,arg2) __THROW WEAK(custom##fname)
#define WEAK_REDEF3(type,fname,arg1,arg2,arg3) type fname(arg1,arg2,arg3) __THROW WEAK(custom##fname)

extern "C" {
  WEAK_REDEF1(void *, malloc, size_t);
  WEAK_REDEF1(void, free, void *);
  WEAK_REDEF1(void, cfree, void *);
  WEAK_REDEF2(void *, calloc, size_t, size_t);
  WEAK_REDEF2(void *, realloc, void *, size_t);
  WEAK_REDEF2(void *, memalign, size_t, size_t);
  WEAK_REDEF3(int, posix_memalign, void **, size_t, size_t);
  WEAK_REDEF2(void *, aligned_alloc, size_t, size_t);
  WEAK_REDEF1(size_t, malloc_usable_size, void *);
}

#include "wrapper.cpp"
#include "gnuwrapper-hooks.cpp"
<commit_msg>Force export visibility.<commit_after>// -*- C++ -*-

/**
 * @file   gnuwrapper.cpp
 * @brief  Replaces malloc family on GNU/Linux with custom versions.
 * @author Emery Berger <http://www.cs.umass.edu/~emery>
 * @note   Copyright (C) 2010 by Emery Berger, University of Massachusetts Amherst.
 */

#ifndef __GNUC__
#error "This file requires the GNU compiler."
#endif

#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include <new>
#include <pthread.h>
#include <sys/cdefs.h>

#include "heaplayers.h"

/*
  To use this library,
  you only need to define the following allocation functions:

  - xxmalloc
  - xxfree
  - xxmalloc_usable_size
  - xxmalloc_lock
  - xxmalloc_unlock

  See the extern "C" block below for function prototypes and more
  details. YOU SHOULD NOT NEED TO MODIFY ANY OF THE CODE HERE TO
  SUPPORT ANY ALLOCATOR.

  LIMITATIONS:

  - This wrapper assumes that the underlying allocator will do "the
    right thing" when xxfree() is called with a pointer internal to an
    allocated object. Header-based allocators, for example, need not
    apply.

*/

#define WEAK(x) __attribute__ ((weak, alias(#x)))
#ifndef __THROW
#define __THROW
#endif

#define CUSTOM_PREFIX(x) custom##x
#define ATTRIBUTE_EXPORT __attribute__((visibility("default")))

#define WEAK_REDEF1(type,fname,arg1) ATTRIBUTE_EXPORT type fname(arg1) __THROW WEAK(custom##fname)
#define WEAK_REDEF2(type,fname,arg1,arg2) ATTRIBUTE_EXPORT type fname(arg1,arg2) __THROW WEAK(custom##fname)
#define WEAK_REDEF3(type,fname,arg1,arg2,arg3) ATTRIBUTE_EXPORT type fname(arg1,arg2,arg3) __THROW WEAK(custom##fname)

extern "C" {
  WEAK_REDEF1(void *, malloc, size_t);
  WEAK_REDEF1(void, free, void *);
  WEAK_REDEF1(void, cfree, void *);
  WEAK_REDEF2(void *, calloc, size_t, size_t);
  WEAK_REDEF2(void *, realloc, void *, size_t);
  WEAK_REDEF2(void *, memalign, size_t, size_t);
  WEAK_REDEF3(int, posix_memalign, void **, size_t, size_t);
  WEAK_REDEF2(void *, aligned_alloc, size_t, size_t);
  WEAK_REDEF1(size_t, malloc_usable_size, void *);
}

#include "wrapper.cpp"
#include "gnuwrapper-hooks.cpp"
<|endoftext|>


<commit_before>#include <nan.h>
#include <native-json.h>
#include "dvbtee-parser.h"

TableData::TableData(const uint8_t &tableId,
                     const std::string &decoderName,
                     const std::string &json)
: tableId(tableId)
, decoderName(decoderName)
, json(json)
{}

////////////////////////////////////////

TableReceiver::TableReceiver(dvbteeParser *dvbteeParser)
: m_dvbteeParser(dvbteeParser) {
  uv_mutex_init(&m_ev_mutex);
  uv_async_init(uv_default_loop(), &m_async, completeCb);
  m_async.data = this;
}

TableReceiver::~TableReceiver() {
  unregisterInterface();

  m_cb.Reset();

  uv_mutex_lock(&m_ev_mutex);
  while (!m_eq.empty()) {
    TableData *data = m_eq.front();
    delete data;
    m_eq.pop();
  }
  uv_mutex_unlock(&m_ev_mutex);

  uv_mutex_destroy(&m_ev_mutex);
}

void TableReceiver::subscribe(const v8::Local<v8::Function> &fn) {
  m_cb.SetFunction(fn);
  registerInterface();
}

void TableReceiver::registerInterface() {
  m_dvbteeParser->m_parser.subscribeTables(this);
}

void TableReceiver::unregisterInterface() {
  m_dvbteeParser->m_parser.subscribeTables(NULL);
}

void TableReceiver::updateTable(uint8_t tId, dvbtee::decode::Table *table) {
  uv_mutex_lock(&m_ev_mutex);
  m_eq.push(
    new TableData(table->getTableid(),
    table->getDecoderName(),
    table->toJson())
  );
  uv_mutex_unlock(&m_ev_mutex);

  uv_async_send(&m_async);
}

#if defined(NODE_MAJOR_VERSION) && (NODE_MAJOR_VERSION == 0 && \
    defined(NODE_MINOR_VERSION) && (NODE_MINOR_VERSION < 12))
#define v8_JSON Native::JSON
#else
#define v8_JSON v8::JSON
#endif

void TableReceiver::notify() {
  Nan::HandleScope scope;
  uv_mutex_lock(&m_ev_mutex);
  while (!m_eq.empty()) {
    TableData *data = m_eq.front();
    uv_mutex_unlock(&m_ev_mutex);

    if (!m_cb.IsEmpty()) {
      v8::Local<v8::String> jsonStr = Nan::New(data->json).ToLocalChecked();
      v8::Local<v8::Value> jsonObj = v8_JSON::Parse(jsonStr);

      v8::Local<v8::Value> argv[] = {
        Nan::New(data->tableId),
        Nan::New(data->decoderName).ToLocalChecked(),
        jsonObj
      };

      m_cb.Call(3, argv);
    }

    delete data;

    uv_mutex_lock(&m_ev_mutex);
    m_eq.pop();
  }
  uv_mutex_unlock(&m_ev_mutex);
}

void TableReceiver::completeCb(uv_async_t *handle
#if NODE_MODULE_VERSION<=11
                              , int status /*UNUSED*/
#endif
                              ) {
  TableReceiver* rcvr = static_cast<TableReceiver*>(handle->data);
  rcvr->notify();
}

////////////////////////////////////////

Nan::Persistent<v8::Function> dvbteeParser::constructor;

dvbteeParser::dvbteeParser()
: m_tableReceiver(this) {
}

dvbteeParser::~dvbteeParser() {
}

void dvbteeParser::Init(v8::Handle<v8::Object> exports) {
  Nan::HandleScope scope;

  // Prepare constructor template
  v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New);
  tpl->SetClassName(Nan::New("Parser").ToLocalChecked());
  tpl->InstanceTemplate()->SetInternalFieldCount(1);

  // Prototype
  Nan::SetPrototypeMethod(tpl, "reset",        reset);
  Nan::SetPrototypeMethod(tpl, "feed",         feed);
  Nan::SetPrototypeMethod(tpl, "push",         feed); // deprecated
  Nan::SetPrototypeMethod(tpl, "parse",        feed); // deprecated
  Nan::SetPrototypeMethod(tpl, "listenTables", listenTables);

  constructor.Reset(tpl->GetFunction());
  exports->Set(Nan::New("Parser").ToLocalChecked(), tpl->GetFunction());
}

void dvbteeParser::New(const Nan::FunctionCallbackInfo<v8::Value>& info) {
  if (info.IsConstructCall()) {
    // Invoked as constructor: `new dvbteeParser(...)`
    dvbteeParser* obj = new dvbteeParser();
    obj->Wrap(info.This());
    info.GetReturnValue().Set(info.This());
  } else {
    // Invoked as plain function `dvbteeParser(...)`, turn into construct call.
    const int argc = 0;
    v8::Local<v8::Value> argv[argc] = { };
    v8::Local<v8::Function> cons = Nan::New<v8::Function>(constructor);
    info.GetReturnValue().Set(cons->NewInstance(argc, argv));
  }
}

void dvbteeParser::listenTables(const Nan::FunctionCallbackInfo<v8::Value>& info) {
  dvbteeParser* obj = ObjectWrap::Unwrap<dvbteeParser>(info.Holder());

  int lastArg = info.Length() - 1;

  if ((lastArg >= 0) && (info[lastArg]->IsFunction())) {
    obj->m_tableReceiver.subscribe(info[lastArg].As<v8::Function>());
  }

  info.GetReturnValue().Set(info.Holder());
}

////////////////////////////////////////

class ResetWorker : public Nan::AsyncWorker {
public:
  ResetWorker(Nan::Callback *callback,
              const Nan::FunctionCallbackInfo<v8::Value>& info)
    : Nan::AsyncWorker(callback) {
      m_obj = Nan::ObjectWrap::Unwrap<dvbteeParser>(info.Holder());
    }
  ~ResetWorker() {
    }

  // Executed inside the worker-thread.
  // It is not safe to access V8, or V8 data structures
  // here, so everything we need for input and output
  // should go on `this`.
  void Execute () {
    m_obj->m_parser.cleanup();
    m_obj->m_parser.reset();
  }

  // Executed when the async work is complete
  // this function will be run inside the main event loop
  // so it is safe to use V8 again
  void HandleOKCallback () {
    Nan::HandleScope scope;

    v8::Local<v8::Value> argv[] = {
        Nan::Null()
      , Nan::New<v8::Number>(0)
    };

    callback->Call(2, argv);
  }

private:
  dvbteeParser* m_obj;
};

void dvbteeParser::reset(const Nan::FunctionCallbackInfo<v8::Value>& info) {
  // If a callback function is supplied, this method will run async
  // otherwise, it will run synchronous

  int lastArg = info.Length() - 1;

  if ((lastArg >= 0) && (info[lastArg]->IsFunction())) {
    Nan::Callback *callback = new Nan::Callback(
      info[lastArg].As<v8::Function>()
    );
    Nan::AsyncQueueWorker(new ResetWorker(callback, info));

  } else {
    dvbteeParser* obj = ObjectWrap::Unwrap<dvbteeParser>(info.Holder());

    obj->m_parser.cleanup();
    obj->m_parser.reset();

    info.GetReturnValue().Set(info.Holder());
  }
}

////////////////////////////////////////

class FeedWorker : public Nan::AsyncWorker {
public:
  FeedWorker(Nan::Callback *callback,
             const Nan::FunctionCallbackInfo<v8::Value>& info)
    : Nan::AsyncWorker(callback)
    , m_buf(NULL)
    , m_buf_len(0)
    , m_ret(-1) {
      m_obj = Nan::ObjectWrap::Unwrap<dvbteeParser>(info.Holder());

      if ((info[0]->IsObject()) && (info[1]->IsNumber())) {
        const char *key = "buf";
        SaveToPersistent(key, info[0]);
        m_buf = node::Buffer::Data(GetFromPersistent(key)->ToObject());
        m_buf_len = info[1]->Uint32Value();
      }
    }
  ~FeedWorker() {
    }

  // Executed inside the worker-thread.
  // It is not safe to access V8, or V8 data structures
  // here, so everything we need for input and output
  // should go on `this`.
  void Execute () {
    if ((m_buf) && (m_buf_len)) {
      m_ret = m_obj->m_parser.feed(m_buf_len, (uint8_t*)m_buf);
    }
    if (m_ret < 0) {
      SetErrorMessage("invalid buffer / length");
    }
  }

  // Executed when the async work is complete
  // this function will be run inside the main event loop
  // so it is safe to use V8 again
  void HandleOKCallback () {
    Nan::HandleScope scope;

    v8::Local<v8::Value> argv[] = {
        Nan::Null()
      , Nan::New<v8::Number>(m_ret)
    };

    callback->Call(2, argv);
  }

private:
  dvbteeParser* m_obj;
  char* m_buf;
  unsigned int m_buf_len;
  int m_ret;
};

void dvbteeParser::feed(const Nan::FunctionCallbackInfo<v8::Value>& info) {
  // If a callback function is supplied, this method will run async
  // otherwise, it will run synchronous
  //
  // Note: when packets are pushed to the parser, the parser will start
  // generating async events containing PSIP table data regardless of
  // whether this function was called synchronously or not

  int lastArg = info.Length() - 1;

  if ((lastArg >= 0) && (info[lastArg]->IsFunction())) {
    Nan::Callback *callback = new Nan::Callback(
      info[lastArg].As<v8::Function>()
    );
    Nan::AsyncQueueWorker(new FeedWorker(callback, info));

  } else {
    dvbteeParser* obj = ObjectWrap::Unwrap<dvbteeParser>(info.Holder());
    int ret = -1;

    if ((info[0]->IsObject()) && (info[1]->IsNumber())) {
      v8::Local<v8::Object> bufferObj = info[0]->ToObject();
      unsigned int len = info[1]->Uint32Value();
      const char* buf = node::Buffer::Data(bufferObj);

      ret = obj->m_parser.feed(len, (uint8_t*)buf);
    }

    info.GetReturnValue().Set(Nan::New(ret));
  }
}
<commit_msg>At least two spaces is best between code and comments  [whitespace/comments] [2]<commit_after>#include <nan.h>
#include <native-json.h>
#include "dvbtee-parser.h"

TableData::TableData(const uint8_t &tableId,
                     const std::string &decoderName,
                     const std::string &json)
: tableId(tableId)
, decoderName(decoderName)
, json(json)
{}

////////////////////////////////////////

TableReceiver::TableReceiver(dvbteeParser *dvbteeParser)
: m_dvbteeParser(dvbteeParser) {
  uv_mutex_init(&m_ev_mutex);
  uv_async_init(uv_default_loop(), &m_async, completeCb);
  m_async.data = this;
}

TableReceiver::~TableReceiver() {
  unregisterInterface();

  m_cb.Reset();

  uv_mutex_lock(&m_ev_mutex);
  while (!m_eq.empty()) {
    TableData *data = m_eq.front();
    delete data;
    m_eq.pop();
  }
  uv_mutex_unlock(&m_ev_mutex);

  uv_mutex_destroy(&m_ev_mutex);
}

void TableReceiver::subscribe(const v8::Local<v8::Function> &fn) {
  m_cb.SetFunction(fn);
  registerInterface();
}

void TableReceiver::registerInterface() {
  m_dvbteeParser->m_parser.subscribeTables(this);
}

void TableReceiver::unregisterInterface() {
  m_dvbteeParser->m_parser.subscribeTables(NULL);
}

void TableReceiver::updateTable(uint8_t tId, dvbtee::decode::Table *table) {
  uv_mutex_lock(&m_ev_mutex);
  m_eq.push(
    new TableData(table->getTableid(),
    table->getDecoderName(),
    table->toJson())
  );
  uv_mutex_unlock(&m_ev_mutex);

  uv_async_send(&m_async);
}

#if defined(NODE_MAJOR_VERSION) && (NODE_MAJOR_VERSION == 0 && \
    defined(NODE_MINOR_VERSION) && (NODE_MINOR_VERSION < 12))
#define v8_JSON Native::JSON
#else
#define v8_JSON v8::JSON
#endif

void TableReceiver::notify() {
  Nan::HandleScope scope;
  uv_mutex_lock(&m_ev_mutex);
  while (!m_eq.empty()) {
    TableData *data = m_eq.front();
    uv_mutex_unlock(&m_ev_mutex);

    if (!m_cb.IsEmpty()) {
      v8::Local<v8::String> jsonStr = Nan::New(data->json).ToLocalChecked();
      v8::Local<v8::Value> jsonObj = v8_JSON::Parse(jsonStr);

      v8::Local<v8::Value> argv[] = {
        Nan::New(data->tableId),
        Nan::New(data->decoderName).ToLocalChecked(),
        jsonObj
      };

      m_cb.Call(3, argv);
    }

    delete data;

    uv_mutex_lock(&m_ev_mutex);
    m_eq.pop();
  }
  uv_mutex_unlock(&m_ev_mutex);
}

void TableReceiver::completeCb(uv_async_t *handle
#if NODE_MODULE_VERSION<=11
                              , int status /*UNUSED*/
#endif
                              ) {
  TableReceiver* rcvr = static_cast<TableReceiver*>(handle->data);
  rcvr->notify();
}

////////////////////////////////////////

Nan::Persistent<v8::Function> dvbteeParser::constructor;

dvbteeParser::dvbteeParser()
: m_tableReceiver(this) {
}

dvbteeParser::~dvbteeParser() {
}

void dvbteeParser::Init(v8::Handle<v8::Object> exports) {
  Nan::HandleScope scope;

  // Prepare constructor template
  v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New);
  tpl->SetClassName(Nan::New("Parser").ToLocalChecked());
  tpl->InstanceTemplate()->SetInternalFieldCount(1);

  // Prototype
  Nan::SetPrototypeMethod(tpl, "reset",        reset);
  Nan::SetPrototypeMethod(tpl, "feed",         feed);
  Nan::SetPrototypeMethod(tpl, "push",         feed);  // deprecated
  Nan::SetPrototypeMethod(tpl, "parse",        feed);  // deprecated
  Nan::SetPrototypeMethod(tpl, "listenTables", listenTables);

  constructor.Reset(tpl->GetFunction());
  exports->Set(Nan::New("Parser").ToLocalChecked(), tpl->GetFunction());
}

void dvbteeParser::New(const Nan::FunctionCallbackInfo<v8::Value>& info) {
  if (info.IsConstructCall()) {
    // Invoked as constructor: `new dvbteeParser(...)`
    dvbteeParser* obj = new dvbteeParser();
    obj->Wrap(info.This());
    info.GetReturnValue().Set(info.This());
  } else {
    // Invoked as plain function `dvbteeParser(...)`, turn into construct call.
    const int argc = 0;
    v8::Local<v8::Value> argv[argc] = { };
    v8::Local<v8::Function> cons = Nan::New<v8::Function>(constructor);
    info.GetReturnValue().Set(cons->NewInstance(argc, argv));
  }
}

void dvbteeParser::listenTables(const Nan::FunctionCallbackInfo<v8::Value>& info) {
  dvbteeParser* obj = ObjectWrap::Unwrap<dvbteeParser>(info.Holder());

  int lastArg = info.Length() - 1;

  if ((lastArg >= 0) && (info[lastArg]->IsFunction())) {
    obj->m_tableReceiver.subscribe(info[lastArg].As<v8::Function>());
  }

  info.GetReturnValue().Set(info.Holder());
}

////////////////////////////////////////

class ResetWorker : public Nan::AsyncWorker {
public:
  ResetWorker(Nan::Callback *callback,
              const Nan::FunctionCallbackInfo<v8::Value>& info)
    : Nan::AsyncWorker(callback) {
      m_obj = Nan::ObjectWrap::Unwrap<dvbteeParser>(info.Holder());
    }
  ~ResetWorker() {
    }

  // Executed inside the worker-thread.
  // It is not safe to access V8, or V8 data structures
  // here, so everything we need for input and output
  // should go on `this`.
  void Execute () {
    m_obj->m_parser.cleanup();
    m_obj->m_parser.reset();
  }

  // Executed when the async work is complete
  // this function will be run inside the main event loop
  // so it is safe to use V8 again
  void HandleOKCallback () {
    Nan::HandleScope scope;

    v8::Local<v8::Value> argv[] = {
        Nan::Null()
      , Nan::New<v8::Number>(0)
    };

    callback->Call(2, argv);
  }

private:
  dvbteeParser* m_obj;
};

void dvbteeParser::reset(const Nan::FunctionCallbackInfo<v8::Value>& info) {
  // If a callback function is supplied, this method will run async
  // otherwise, it will run synchronous

  int lastArg = info.Length() - 1;

  if ((lastArg >= 0) && (info[lastArg]->IsFunction())) {
    Nan::Callback *callback = new Nan::Callback(
      info[lastArg].As<v8::Function>()
    );
    Nan::AsyncQueueWorker(new ResetWorker(callback, info));

  } else {
    dvbteeParser* obj = ObjectWrap::Unwrap<dvbteeParser>(info.Holder());

    obj->m_parser.cleanup();
    obj->m_parser.reset();

    info.GetReturnValue().Set(info.Holder());
  }
}

////////////////////////////////////////

class FeedWorker : public Nan::AsyncWorker {
public:
  FeedWorker(Nan::Callback *callback,
             const Nan::FunctionCallbackInfo<v8::Value>& info)
    : Nan::AsyncWorker(callback)
    , m_buf(NULL)
    , m_buf_len(0)
    , m_ret(-1) {
      m_obj = Nan::ObjectWrap::Unwrap<dvbteeParser>(info.Holder());

      if ((info[0]->IsObject()) && (info[1]->IsNumber())) {
        const char *key = "buf";
        SaveToPersistent(key, info[0]);
        m_buf = node::Buffer::Data(GetFromPersistent(key)->ToObject());
        m_buf_len = info[1]->Uint32Value();
      }
    }
  ~FeedWorker() {
    }

  // Executed inside the worker-thread.
  // It is not safe to access V8, or V8 data structures
  // here, so everything we need for input and output
  // should go on `this`.
  void Execute () {
    if ((m_buf) && (m_buf_len)) {
      m_ret = m_obj->m_parser.feed(m_buf_len, (uint8_t*)m_buf);
    }
    if (m_ret < 0) {
      SetErrorMessage("invalid buffer / length");
    }
  }

  // Executed when the async work is complete
  // this function will be run inside the main event loop
  // so it is safe to use V8 again
  void HandleOKCallback () {
    Nan::HandleScope scope;

    v8::Local<v8::Value> argv[] = {
        Nan::Null()
      , Nan::New<v8::Number>(m_ret)
    };

    callback->Call(2, argv);
  }

private:
  dvbteeParser* m_obj;
  char* m_buf;
  unsigned int m_buf_len;
  int m_ret;
};

void dvbteeParser::feed(const Nan::FunctionCallbackInfo<v8::Value>& info) {
  // If a callback function is supplied, this method will run async
  // otherwise, it will run synchronous
  //
  // Note: when packets are pushed to the parser, the parser will start
  // generating async events containing PSIP table data regardless of
  // whether this function was called synchronously or not

  int lastArg = info.Length() - 1;

  if ((lastArg >= 0) && (info[lastArg]->IsFunction())) {
    Nan::Callback *callback = new Nan::Callback(
      info[lastArg].As<v8::Function>()
    );
    Nan::AsyncQueueWorker(new FeedWorker(callback, info));

  } else {
    dvbteeParser* obj = ObjectWrap::Unwrap<dvbteeParser>(info.Holder());
    int ret = -1;

    if ((info[0]->IsObject()) && (info[1]->IsNumber())) {
      v8::Local<v8::Object> bufferObj = info[0]->ToObject();
      unsigned int len = info[1]->Uint32Value();
      const char* buf = node::Buffer::Data(bufferObj);

      ret = obj->m_parser.feed(len, (uint8_t*)buf);
    }

    info.GetReturnValue().Set(Nan::New(ret));
  }
}
<|endoftext|>


<commit_before>/*

Copyright (c) 2003, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#ifndef TORRENT_ALERT_TYPES_HPP_INCLUDED
#define TORRENT_ALERT_TYPES_HPP_INCLUDED

#include <string>

#include "libtorrent/alert.hpp"
#include "libtorrent/torrent_handle.hpp"

namespace libtorrent
{
	struct tracker_alert: alert
	{
		tracker_alert(const torrent_handle& h
			, const std::string& msg)
			: alert(alert::warning, msg)
			, handle(h)
			{}

		virtual std::auto_ptr<alert> clone() const
		{ return std::auto_ptr<alert>(new tracker_alert(*this)); }

		torrent_handle handle;
	};

	struct hash_failed_alert: alert
	{
		hash_failed_alert(
			const torrent_handle& h
			, int index
			, const std::string& msg)
			: alert(alert::info, msg)
			, handle(h)
			, piece_index(index)
			{ assert(index >= 0);}

		virtual std::auto_ptr<alert> clone() const
		{ return std::auto_ptr<alert>(new hash_failed_alert(*this)); }

		torrent_handle handle;
		int piece_index;
	};

	struct peer_error_alert: alert
	{
		peer_error_alert(const address& pip, const std::string& msg)
			: alert(alert::debug, msg)
			, ip(pip)
		{}

		virtual std::auto_ptr<alert> clone() const
		{ return std::auto_ptr<alert>(new peer_error_alert(*this)); }

		address ip;
	};

	struct chat_message_alert: alert
	{
		chat_message_alert(
			const torrent_handle& h
			, const address& sender
			, const std::string& msg)
			: alert(alert::critical, msg)
			, handle(h)
			, ip(sender)
		{}

		virtual std::auto_ptr<alert> clone() const
		{ return std::auto_ptr<alert>(new chat_message_alert(*this)); }

		torrent_handle handle;
		address ip;
	};

	struct invalid_request_alert: alert
	{
		invalid_request_alert(
			const peer_request& r
			, const torrent_handle& h
			, const address& sender
			, const std::string& msg)
			: alert(alert::debug, msg)
			, handle(h)
			, ip(sender)
			, request(r)
		{}

		virtual std::auto_ptr<alert> clone() const
		{ return std::auto_ptr<alert>(new invalid_request_alert(*this)); }

		torrent_handle handle;
		address ip;
		peer_request request;
	};

	struct torrent_finished_alert: alert
	{
		torrent_finished_alert(
			const torrent_handle& h
			, const std::string& msg)
			: alert(alert::warning, msg)
			, handle(h)
		{}

		virtual std::auto_ptr<alert> clone() const
		{ return std::auto_ptr<alert>(new torrent_finished_alert(*this)); }

		torrent_handle handle;
	};

	struct file_error_alert: alert
	{
		file_error_alert(
			const torrent_handle& h
			, const std::string& msg)
			: alert(alert::fatal, msg)
			, handle(h)
		{}

		virtual std::auto_ptr<alert> clone() const
		{ return std::auto_ptr<alert>(new file_error_alert(*this)); }

		torrent_handle handle;
	};

	struct listen_failed_alert: alert
	{
		listen_failed_alert(
			const std::string& msg)
			: alert(alert::fatal, msg)
		{}

		virtual std::auto_ptr<alert> clone() const
		{ return std::auto_ptr<alert>(new listen_failed_alert(*this)); }
	};
}

#endif
<commit_msg>*** empty log message ***<commit_after>/*

Copyright (c) 2003, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#ifndef TORRENT_ALERT_TYPES_HPP_INCLUDED
#define TORRENT_ALERT_TYPES_HPP_INCLUDED

#include "libtorrent/alert.hpp"
#include "libtorrent/torrent_handle.hpp"
#include "libtorrent/socket.hpp"
#include "libtorrent/peer_connection.hpp"

namespace libtorrent
{
	struct tracker_alert: alert
	{
		tracker_alert(const torrent_handle& h
			, const std::string& msg)
			: alert(alert::warning, msg)
			, handle(h)
			{}

		virtual std::auto_ptr<alert> clone() const
		{ return std::auto_ptr<alert>(new tracker_alert(*this)); }

		torrent_handle handle;
	};

	struct hash_failed_alert: alert
	{
		hash_failed_alert(
			const torrent_handle& h
			, int index
			, const std::string& msg)
			: alert(alert::info, msg)
			, handle(h)
			, piece_index(index)
			{ assert(index >= 0);}

		virtual std::auto_ptr<alert> clone() const
		{ return std::auto_ptr<alert>(new hash_failed_alert(*this)); }

		torrent_handle handle;
		int piece_index;
	};

	struct peer_error_alert: alert
	{
		peer_error_alert(const address& pip, const std::string& msg)
			: alert(alert::debug, msg)
			, ip(pip)
		{}

		virtual std::auto_ptr<alert> clone() const
		{ return std::auto_ptr<alert>(new peer_error_alert(*this)); }

		address ip;
	};

	struct chat_message_alert: alert
	{
		chat_message_alert(
			const torrent_handle& h
			, const address& sender
			, const std::string& msg)
			: alert(alert::critical, msg)
			, handle(h)
			, ip(sender)
		{}

		virtual std::auto_ptr<alert> clone() const
		{ return std::auto_ptr<alert>(new chat_message_alert(*this)); }

		torrent_handle handle;
		address ip;
	};

	struct invalid_request_alert: alert
	{
		invalid_request_alert(
			const peer_request& r
			, const torrent_handle& h
			, const address& sender
			, const std::string& msg)
			: alert(alert::debug, msg)
			, handle(h)
			, ip(sender)
			, request(r)
		{}

		virtual std::auto_ptr<alert> clone() const
		{ return std::auto_ptr<alert>(new invalid_request_alert(*this)); }

		torrent_handle handle;
		address ip;
		peer_request request;
	};

	struct torrent_finished_alert: alert
	{
		torrent_finished_alert(
			const torrent_handle& h
			, const std::string& msg)
			: alert(alert::warning, msg)
			, handle(h)
		{}

		virtual std::auto_ptr<alert> clone() const
		{ return std::auto_ptr<alert>(new torrent_finished_alert(*this)); }

		torrent_handle handle;
	};

	struct file_error_alert: alert
	{
		file_error_alert(
			const torrent_handle& h
			, const std::string& msg)
			: alert(alert::fatal, msg)
			, handle(h)
		{}

		virtual std::auto_ptr<alert> clone() const
		{ return std::auto_ptr<alert>(new file_error_alert(*this)); }

		torrent_handle handle;
	};

	struct listen_failed_alert: alert
	{
		listen_failed_alert(
			const std::string& msg)
			: alert(alert::fatal, msg)
		{}

		virtual std::auto_ptr<alert> clone() const
		{ return std::auto_ptr<alert>(new listen_failed_alert(*this)); }
	};
}

#endif
<|endoftext|>


<commit_before>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2011 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#ifndef MAPNIK_TEXT_SYMBOLIZER_HPP
#define MAPNIK_TEXT_SYMBOLIZER_HPP

// mapnik
#include <mapnik/color.hpp>
#include <mapnik/font_set.hpp>
#include <mapnik/graphics.hpp>
#include <mapnik/filter_factory.hpp>
#include <mapnik/symbolizer.hpp>
#include <mapnik/text_placements.hpp>

// boost
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>

// stl
#include <string>

namespace mapnik
{

struct MAPNIK_DECL text_symbolizer : public symbolizer_base
{
    text_symbolizer(expression_ptr name, std::string const& face_name,
                    float size, color const& fill,
                    text_placements_ptr placements = boost::make_shared<text_placements_dummy>()
                    );
    text_symbolizer(expression_ptr name, float size, color const& fill,
                    text_placements_ptr placements = boost::make_shared<text_placements_dummy>()
                    );
    text_symbolizer(text_symbolizer const& rhs);
    text_symbolizer& operator=(text_symbolizer const& rhs);
    expression_ptr get_name() const;
    void set_name(expression_ptr expr);

    expression_ptr get_orientation() const; // orienation (rotation angle atm)
    void set_orientation(expression_ptr expr);

    unsigned get_text_ratio() const; // target ratio for text bounding box in pixels
    void set_text_ratio(unsigned ratio);
    unsigned get_wrap_width() const; // width to wrap text at, or trigger ratio
    void set_wrap_width(unsigned ratio);
    unsigned char get_wrap_char() const; // character used to wrap lines
    std::string get_wrap_char_string() const; // character used to wrap lines as std::string
    void set_wrap_char(unsigned char character);
    void set_wrap_char_from_string(std::string const& character);
    text_transform_e get_text_transform() const; // text conversion on strings before display
    void set_text_transform(text_transform_e convert);
    unsigned get_line_spacing() const; // spacing between lines of text
    void set_line_spacing(unsigned spacing);
    unsigned get_character_spacing() const; // spacing between characters in text
    void set_character_spacing(unsigned spacing);
    unsigned get_label_spacing() const; // spacing between repeated labels on lines
    void set_label_spacing(unsigned spacing);
    unsigned get_label_position_tolerance() const; //distance the label can be moved on the line to fit, if 0 the default is used
    void set_label_position_tolerance(unsigned tolerance);
    bool get_force_odd_labels() const; // try render an odd amount of labels
    void set_force_odd_labels(bool force);
    double get_max_char_angle_delta() const; // maximum change in angle between adjacent characters
    void set_max_char_angle_delta(double angle);
    float get_text_size() const;
    void set_text_size(float size);
    std::string const& get_face_name() const;
    void set_face_name(std::string face_name);
    font_set const& get_fontset() const;
    void set_fontset(font_set const& fset);
    color const& get_fill() const;
    void set_fill(color const& fill);
    void set_halo_fill(color const& fill);
    color const& get_halo_fill() const;
    void set_halo_radius(double radius);
    double get_halo_radius() const;
    void set_label_placement(label_placement_e label_p);
    label_placement_e get_label_placement() const;
    void set_vertical_alignment(vertical_alignment_e valign);
    vertical_alignment_e get_vertical_alignment() const;
    void set_anchor(double x, double y);
    position const& get_anchor() const;
    void set_displacement(double x, double y);
    void set_displacement(position const& p);
    position const& get_displacement() const;
    void set_avoid_edges(bool avoid);
    bool get_avoid_edges() const;
    void set_minimum_distance(double distance);
    double get_minimum_distance() const;
    void set_minimum_padding(double distance);
    double get_minimum_padding() const;
    void set_minimum_path_length(double size);
    double get_minimum_path_length() const;
    void set_allow_overlap(bool overlap);
    bool get_allow_overlap() const;
    void set_text_opacity(double opacity);
    double get_text_opacity() const;
    bool get_wrap_before() const; // wrap text at wrap_char immediately before current work
    void set_wrap_before(bool wrap_before);
    void set_horizontal_alignment(horizontal_alignment_e valign);
    horizontal_alignment_e get_horizontal_alignment() const;
    void set_justify_alignment(justify_alignment_e valign);
    justify_alignment_e get_justify_alignment() const;
    text_placements_ptr get_placement_options() const;
    void set_placement_options(text_placements_ptr placement_options);

private:
    expression_ptr name_;
    expression_ptr orientation_;
    std::string face_name_;
    font_set fontset_;
    unsigned text_ratio_;
    unsigned wrap_width_;
    unsigned char wrap_char_;
    text_transform_e text_transform_;
    unsigned line_spacing_;
    unsigned character_spacing_;
    unsigned label_spacing_;
    unsigned label_position_tolerance_;
    bool force_odd_labels_;
    double max_char_angle_delta_;
    color fill_;
    color halo_fill_;
    double halo_radius_;
    label_placement_e label_p_;
    position anchor_;
    bool avoid_edges_;
    double minimum_distance_;
    double minimum_padding_;
    double minimum_path_length_;
    bool overlap_;
    double text_opacity_;
    bool wrap_before_;
    text_placements_ptr placement_options_;
};
}

#endif // MAPNIK_TEXT_SYMBOLIZER_HPP
<commit_msg>allow compile with msvs - make_shared is preferable but vs compiler cannot handle it<commit_after>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2011 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#ifndef MAPNIK_TEXT_SYMBOLIZER_HPP
#define MAPNIK_TEXT_SYMBOLIZER_HPP

// mapnik
#include <mapnik/color.hpp>
#include <mapnik/font_set.hpp>
#include <mapnik/graphics.hpp>
#include <mapnik/filter_factory.hpp>
#include <mapnik/symbolizer.hpp>
#include <mapnik/text_placements.hpp>

// boost
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>

// stl
#include <string>

namespace mapnik
{

struct MAPNIK_DECL text_symbolizer : public symbolizer_base
{
    // Note - we do not use boost::make_shared below as VC2010 is
	// not able to compile make_shared used within a constructor
	text_symbolizer(expression_ptr name, std::string const& face_name,
                    float size, color const& fill,
                    text_placements_ptr placements = text_placements_ptr(new text_placements_dummy)
                    );
    text_symbolizer(expression_ptr name, float size, color const& fill,
                    text_placements_ptr placements = text_placements_ptr(new text_placements_dummy)
                    );
    text_symbolizer(text_symbolizer const& rhs);
    text_symbolizer& operator=(text_symbolizer const& rhs);
    expression_ptr get_name() const;
    void set_name(expression_ptr expr);

    expression_ptr get_orientation() const; // orienation (rotation angle atm)
    void set_orientation(expression_ptr expr);

    unsigned get_text_ratio() const; // target ratio for text bounding box in pixels
    void set_text_ratio(unsigned ratio);
    unsigned get_wrap_width() const; // width to wrap text at, or trigger ratio
    void set_wrap_width(unsigned ratio);
    unsigned char get_wrap_char() const; // character used to wrap lines
    std::string get_wrap_char_string() const; // character used to wrap lines as std::string
    void set_wrap_char(unsigned char character);
    void set_wrap_char_from_string(std::string const& character);
    text_transform_e get_text_transform() const; // text conversion on strings before display
    void set_text_transform(text_transform_e convert);
    unsigned get_line_spacing() const; // spacing between lines of text
    void set_line_spacing(unsigned spacing);
    unsigned get_character_spacing() const; // spacing between characters in text
    void set_character_spacing(unsigned spacing);
    unsigned get_label_spacing() const; // spacing between repeated labels on lines
    void set_label_spacing(unsigned spacing);
    unsigned get_label_position_tolerance() const; //distance the label can be moved on the line to fit, if 0 the default is used
    void set_label_position_tolerance(unsigned tolerance);
    bool get_force_odd_labels() const; // try render an odd amount of labels
    void set_force_odd_labels(bool force);
    double get_max_char_angle_delta() const; // maximum change in angle between adjacent characters
    void set_max_char_angle_delta(double angle);
    float get_text_size() const;
    void set_text_size(float size);
    std::string const& get_face_name() const;
    void set_face_name(std::string face_name);
    font_set const& get_fontset() const;
    void set_fontset(font_set const& fset);
    color const& get_fill() const;
    void set_fill(color const& fill);
    void set_halo_fill(color const& fill);
    color const& get_halo_fill() const;
    void set_halo_radius(double radius);
    double get_halo_radius() const;
    void set_label_placement(label_placement_e label_p);
    label_placement_e get_label_placement() const;
    void set_vertical_alignment(vertical_alignment_e valign);
    vertical_alignment_e get_vertical_alignment() const;
    void set_anchor(double x, double y);
    position const& get_anchor() const;
    void set_displacement(double x, double y);
    void set_displacement(position const& p);
    position const& get_displacement() const;
    void set_avoid_edges(bool avoid);
    bool get_avoid_edges() const;
    void set_minimum_distance(double distance);
    double get_minimum_distance() const;
    void set_minimum_padding(double distance);
    double get_minimum_padding() const;
    void set_minimum_path_length(double size);
    double get_minimum_path_length() const;
    void set_allow_overlap(bool overlap);
    bool get_allow_overlap() const;
    void set_text_opacity(double opacity);
    double get_text_opacity() const;
    bool get_wrap_before() const; // wrap text at wrap_char immediately before current work
    void set_wrap_before(bool wrap_before);
    void set_horizontal_alignment(horizontal_alignment_e valign);
    horizontal_alignment_e get_horizontal_alignment() const;
    void set_justify_alignment(justify_alignment_e valign);
    justify_alignment_e get_justify_alignment() const;
    text_placements_ptr get_placement_options() const;
    void set_placement_options(text_placements_ptr placement_options);

private:
    expression_ptr name_;
    expression_ptr orientation_;
    std::string face_name_;
    font_set fontset_;
    unsigned text_ratio_;
    unsigned wrap_width_;
    unsigned char wrap_char_;
    text_transform_e text_transform_;
    unsigned line_spacing_;
    unsigned character_spacing_;
    unsigned label_spacing_;
    unsigned label_position_tolerance_;
    bool force_odd_labels_;
    double max_char_angle_delta_;
    color fill_;
    color halo_fill_;
    double halo_radius_;
    label_placement_e label_p_;
    position anchor_;
    bool avoid_edges_;
    double minimum_distance_;
    double minimum_padding_;
    double minimum_path_length_;
    bool overlap_;
    double text_opacity_;
    bool wrap_before_;
    text_placements_ptr placement_options_;
};
}

#endif // MAPNIK_TEXT_SYMBOLIZER_HPP
<|endoftext|>


<commit_before>// Copyright (c) 2013, German Neuroinformatics Node (G-Node)
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted under the terms of the BSD License. See
// LICENSE file in the root of the Project.

#ifndef IMPL_CONTAINER_H
#define IMPL_CONTAINER_H

#include <memory>
#include <vector>
#include <list>
#include <functional>

#include <nix/Platform.hpp> //for pragma warnings on windows
#include <nix/None.hpp>
#include <nix/Exception.hpp>

namespace nix {
namespace base {

template<typename T>
class ImplContainer {
protected:

    std::shared_ptr<T> impl_ptr;

    /**
     * Low level helper to get multiple entities via a getter function
     * that has to be provided.
     *
     * Get multiple entities of given type and return them as vector.
     * The template param specifies which type.
     * The parameter "getEnt" is the function used to get the entities
     * of given type T_ENT and has to be of return type T_ENT with
     * parameter "size_t" specifying the index of the entity to get.
     * The parameter "nT" should give the number of entities to get and
     * should be =>0 & <= the total number of existing entities.
     * The parameter "filter" is defaulted to giving back all entities
     * of given type. To use your own filter pass a lambda that accepts
     * an entity of given type as parameter and returns a bool telling
     * whether to get it or not.
     * NOTE: there is no need to specify 2nd template param TFUNC as it
     * should be automatically deduced.
     *
     * @param class "get function": std::function of return type T_ENT and
     *              param type "int" to get entities
     * @param size_t number of entities to get
     * @param class "filter function": std::function of return type bool
     *              and param type T_ENT to filter which entities to get
     * @return class entities of the given type as a vector
     */
    template<typename TENT, typename TFUNC>
    std::vector<TENT> getEntities(
        TFUNC const &getEntity,
        size_t nT,
        std::function<bool(TENT)> filter) const
    {
        std::vector<TENT> e;
        size_t i = 0;

        if(nT < 1) { return e; }
        e.resize(nT);

        for (typename std::vector<TENT>::iterator it = e.begin(); it!=e.end(); ++it) {
            if(filter(*it)) {
                *it = getEntity( i++ );
            }
        }

        return e;
    }

public:

    ImplContainer()
        : impl_ptr(nullptr)
    {
    }

    ImplContainer(T *p_impl)
        : impl_ptr(p_impl)
    {
    }

    ImplContainer(const std::shared_ptr<T> &p_impl)
        : impl_ptr(p_impl)
    {
    }

    ImplContainer(const ImplContainer<T> &other)
        : impl_ptr(other.impl_ptr)
    {
    }

    bool isNone() const {
        return !impl_ptr;
    }

    explicit operator bool() const {
        return !isNone();
    }

    virtual ImplContainer<T> &operator=(const ImplContainer<T> &other) {
        if (*this != other) {
            ImplContainer tmp(other);
            swap(tmp);
        }
        return *this;
    }

    virtual ImplContainer<T> &operator=(std::nullptr_t nullp) {
        impl_ptr = nullp;
        return *this;
    }

    virtual bool operator==(const ImplContainer<T> &other) const {
        return this->impl_ptr == other.impl_ptr;
    }

    virtual bool operator!=(const ImplContainer<T> &other) const {
        return !(*this == other);
    }

    // bool "==" operator "boost::none_t" overload: when an object
    // is compared to "none_t" (e.g. boost::none) internally we compare
    // the "impl_ptr" to the null pointer.
    virtual bool operator==(none_t t) const {
        return impl_ptr == nullptr;
    }

    // bool "=!" operator "boost::none_t" overload: same as "==" operator.
    virtual bool operator!=(none_t t) const {
        return impl_ptr != nullptr;
    }

    virtual void swap(ImplContainer<T> &second) {
        using std::swap;

        swap(impl_ptr, second.impl_ptr);
    }

    virtual ~ImplContainer() {}

    std::shared_ptr<T> impl() const {
        return impl_ptr;
    }

protected:
    T* backend() {
        if (isNone()) {
            throw std::runtime_error("Trying to access object with invalid object");
        }

        return impl_ptr.get();
    }

    const T* backend() const {
        return const_cast<ImplContainer *>(this)->backend();
    }
};

} // namespace base
} // namespace nix

#endif
<commit_msg>ImplContainer::nullify() new function to nullify the impl_ptr<commit_after>// Copyright (c) 2013, German Neuroinformatics Node (G-Node)
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted under the terms of the BSD License. See
// LICENSE file in the root of the Project.

#ifndef IMPL_CONTAINER_H
#define IMPL_CONTAINER_H

#include <memory>
#include <vector>
#include <list>
#include <functional>

#include <nix/Platform.hpp> //for pragma warnings on windows
#include <nix/None.hpp>
#include <nix/Exception.hpp>

namespace nix {
namespace base {

template<typename T>
class ImplContainer {
protected:

    std::shared_ptr<T> impl_ptr;

    /**
     * Low level helper to get multiple entities via a getter function
     * that has to be provided.
     *
     * Get multiple entities of given type and return them as vector.
     * The template param specifies which type.
     * The parameter "getEnt" is the function used to get the entities
     * of given type T_ENT and has to be of return type T_ENT with
     * parameter "size_t" specifying the index of the entity to get.
     * The parameter "nT" should give the number of entities to get and
     * should be =>0 & <= the total number of existing entities.
     * The parameter "filter" is defaulted to giving back all entities
     * of given type. To use your own filter pass a lambda that accepts
     * an entity of given type as parameter and returns a bool telling
     * whether to get it or not.
     * NOTE: there is no need to specify 2nd template param TFUNC as it
     * should be automatically deduced.
     *
     * @param class "get function": std::function of return type T_ENT and
     *              param type "int" to get entities
     * @param size_t number of entities to get
     * @param class "filter function": std::function of return type bool
     *              and param type T_ENT to filter which entities to get
     * @return class entities of the given type as a vector
     */
    template<typename TENT, typename TFUNC>
    std::vector<TENT> getEntities(
        TFUNC const &getEntity,
        size_t nT,
        std::function<bool(TENT)> filter) const
    {
        std::vector<TENT> e;
        size_t i = 0;

        if(nT < 1) { return e; }
        e.resize(nT);

        for (typename std::vector<TENT>::iterator it = e.begin(); it!=e.end(); ++it) {
            if(filter(*it)) {
                *it = getEntity( i++ );
            }
        }

        return e;
    }

public:

    ImplContainer()
        : impl_ptr(nullptr)
    {
    }

    ImplContainer(T *p_impl)
        : impl_ptr(p_impl)
    {
    }

    ImplContainer(const std::shared_ptr<T> &p_impl)
        : impl_ptr(p_impl)
    {
    }

    ImplContainer(const ImplContainer<T> &other)
        : impl_ptr(other.impl_ptr)
    {
    }

    bool isNone() const {
        return !impl_ptr;
    }

    explicit operator bool() const {
        return !isNone();
    }

    virtual ImplContainer<T> &operator=(const ImplContainer<T> &other) {
        if (*this != other) {
            ImplContainer tmp(other);
            swap(tmp);
        }
        return *this;
    }

    virtual ImplContainer<T> &operator=(std::nullptr_t nullp) {
        impl_ptr = nullp;
        return *this;
    }

    virtual bool operator==(const ImplContainer<T> &other) const {
        return this->impl_ptr == other.impl_ptr;
    }

    virtual bool operator!=(const ImplContainer<T> &other) const {
        return !(*this == other);
    }

    // bool "==" operator "boost::none_t" overload: when an object
    // is compared to "none_t" (e.g. boost::none) internally we compare
    // the "impl_ptr" to the null pointer.
    virtual bool operator==(none_t t) const {
        return impl_ptr == nullptr;
    }

    // bool "=!" operator "boost::none_t" overload: same as "==" operator.
    virtual bool operator!=(none_t t) const {
        return impl_ptr != nullptr;
    }

    virtual void swap(ImplContainer<T> &second) {
        using std::swap;

        swap(impl_ptr, second.impl_ptr);
    }

    virtual ~ImplContainer() {}

    std::shared_ptr<T> impl() const {
        return impl_ptr;
    }

protected:
    T* backend() {
        if (isNone()) {
            throw std::runtime_error("Trying to access object with invalid object");
        }

        return impl_ptr.get();
    }

    const T* backend() const {
        return const_cast<ImplContainer *>(this)->backend();
    }

    void nullify() {
        impl_ptr = nullptr;
    }
};

} // namespace base
} // namespace nix

#endif
<|endoftext|>


<commit_before>
#include <sstream>
using namespace std;

#include <PLCM.hpp>

#include <internals/Counters.hpp>
#include <internals/Dataset.hpp>
#include <internals/ExplorationStep.hpp>
#include <io/MultiThreadedFileCollector.hpp>
#include <io/NullCollector.hpp>
#include <io/PatternSortCollector.hpp>
#include <io/StdOutCollector.hpp>
#include <util/MemoryPeakWatcherThread.hpp>
#include <util/ProgressWatcherThread.hpp>
#include <util/Helpers.h>

using namespace util;
using namespace io;

unique_ptr<PLCM::thread_map> PLCM::threads = nullptr;

PLCM::PLCM(struct Options *options) {
	uint32_t nbThreads = options->num_threads;
	if (nbThreads < 1) {
		cerr << "nbThreads has to be > 0, given " << nbThreads <<
				". Aborting.";
		exit(EXIT_FAILURE);
	}
	collector = instanciateCollector(options);
	threads = unique_ptr<thread_map>(new thread_map());
	createThreads(nbThreads);
	progressWatch = unique_ptr<ProgressWatcherThread>(
			new ProgressWatcherThread());
	for (uint i = 0; i < PLCMCounters::Number_of_PLCMCounters; ++i) {
				globalCounters[i] = 0;
	}
}

PLCM::~PLCM() {
}

void PLCM::collect(int32_t support, vector<int32_t>* pattern) {
	collector->collect(support, pattern);
}

void PLCM::lcm(shared_ptr<ExplorationStep> initState) {
	if (initState->pattern->size() > 0) {
		collector->collect(
				initState->counters->transactionsCount,
				initState->pattern.get());
	}

	progressWatch->setInitState(initState);

	initializeAndStartThreads(initState);

	progressWatch->start();

	for (auto it = threads->begin(); it != threads->end(); ++it) {
		auto t = it->second.get();
		t->join();
		for (uint i = 0; i < PLCMCounters::Number_of_PLCMCounters; ++i) {
			globalCounters[i] += t->counters[i];
		}
	}

	progressWatch->stop();
}

void PLCM::display(ostream& stream,
		map<string, uint64_t>* additionalcounters) {
	stream << "{\"name\":\"PLCM\", \"threads\":" << threads->size();

	for (uint i = 0; i < PLCMCounters::Number_of_PLCMCounters; ++i) {
		stream << ", \"" << PLCMCountersNames[(PLCMCounters)i] <<
				"\":" << globalCounters[i];
	}

	if (additionalcounters != nullptr) {
		for (auto entry : (*additionalcounters)) {
			stream << ", \"" << entry.first << "\":" << entry.second;
		}
	}

	stream << "}" << endl;
}

int64_t PLCM::closeCollector() {
	return collector->close();
}

int32_t PLCM::getAveragePatternLength() {
	return collector->getAveragePatternLength();
}

uint32_t PLCM::getDefaultNumThreads() {
	unsigned concurentThreadsSupported = thread::hardware_concurrency();
	if (concurentThreadsSupported == 0)
	{
		cerr << "Warning: could not get the number of concurrent threads supported"
				" by your system." << endl;
		concurentThreadsSupported = 4;
	}
	else
	{
	    cout << "Info: your system supports " << concurentThreadsSupported <<
	    		" concurrent threads." << endl;
	}

	return concurentThreadsSupported;
}

int PLCM::main(int argc, char** argv) {

	uint32_t default_num_threads = getDefaultNumThreads();

	TCLAP::CmdLine cmd("Parallel LCM, C++ implementation.");

	TCLAP::SwitchArg benchmark_mode("b","benchmark",
			"Benchmark mode: patterns are not outputted at "
			"all (in which case OUTPUT_PATH is ignored)", cmd);
	TCLAP::SwitchArg memory_usage("m","mem-usage",
			"Give peak memory usage after mining "
			"(instanciates a periodic watcher thread)", cmd);
	TCLAP::SwitchArg sort_items("s", "sort-items",
			"Sort items in outputted patterns, in ascending order", cmd);
	ostringstream oss_explain;
	oss_explain << "How many threads will be launched (defaults to " <<
		default_num_threads << ")";
	TCLAP::ValueArg<uint32_t> num_threads("t", "num-threads",
			oss_explain.str(), false, default_num_threads,
			"num_threads", cmd);
	TCLAP::SwitchArg verbose("v", "verbose",
			"Enable verbose mode, which logs every extension "
			"of the empty pattern", cmd);
	TCLAP::SwitchArg ultra_verbose("V", "ultra-verbose",
			"Enable ultra-verbose mode, which logs every pattern extension"
			" (use with care: it may produce a LOT of output)", cmd);
	TCLAP::UnlabeledValueArg<string> input_path("INPUT_PATH",
			"The path of the file to mine into.", true, "None", "in_file", cmd);
	TCLAP::UnlabeledValueArg<uint32_t> minsup("MINSUP",
			"The minimum support of items.", true, 10, "min_support", cmd);
	TCLAP::UnlabeledValueArg<string> output_path("OUTPUT_PATH",
			"The path of the file where results should be dumped "
			"(defaults to standard output).", false, "-", "out_file", cmd);

	// Parse the argv array	(will exit if invalid)
	cmd.parse( argc, argv );

	unique_ptr<PLCM::Options> options(new PLCM::Options {
			benchmark_mode.getValue(),
			memory_usage.getValue(),
			sort_items.getValue(),
			num_threads.getValue(),
			verbose.getValue(),
			ultra_verbose.getValue(),
			input_path.getValue(),
			minsup.getValue(),
			output_path.getValue()
	});

	// run the main procedure
	standalone(move(options) /* transfer ownership */);
	return 0;
}

void PLCM::standalone(unique_ptr<PLCM::Options> options) {
	unique_ptr<MemoryPeakWatcherThread> memoryWatch = nullptr;

	if (options->memory_usage) {
		memoryWatch = unique_ptr<MemoryPeakWatcherThread>(
				new MemoryPeakWatcherThread());
		memoryWatch->start();
	}

	if (options->ultra_verbose)
	{
		ExplorationStep::verbose = true;
		ExplorationStep::ultraVerbose = true;
	}
	else
	{
		ExplorationStep::verbose = options->verbose;
	}

	double chrono = Helpers::precise_time();
	shared_ptr<ExplorationStep> initState = make_shared<ExplorationStep>(
			options->minsup, options->input_path);
	double loadingTime = Helpers::precise_time() - chrono;
	cerr << "Dataset loaded in " << loadingTime << "s" << endl;

	unique_ptr<PLCM> miner(new PLCM(options.get()));

	chrono = Helpers::precise_time();
	miner->lcm(initState);
	chrono = Helpers::precise_time() - chrono;

	unique_ptr<map<string, uint64_t> > additionalcounters(
			new map<string, uint64_t>());
	(*additionalcounters)["miningTime"] = chrono*1000 /* milliseconds */;
	(*additionalcounters)["outputtedPatterns"] = miner->closeCollector();
	(*additionalcounters)["loadingTime"] = loadingTime*1000 /* milliseconds */;
	(*additionalcounters)["avgPatternLength"] =
			(uint64_t) miner->getAveragePatternLength();

	if (memoryWatch != nullptr) {
		memoryWatch->stop();
		(*additionalcounters)["maxUsedMemory"] = memoryWatch->getMaxUsedMemory();
	}

	miner->display(cerr, additionalcounters.get());
}

PLCMThread* PLCM::getCurrentThread() {
	return (*threads)[this_thread::get_id()].get();
}

void PLCM::createThreads(int32_t nbThreads) {
	cpu_set_t cpu_set;
	sched_getaffinity(0, sizeof(cpu_set_t), &cpu_set);
	int num_cpus = CPU_COUNT(&cpu_set);
	int index_cpu;

	for (int i = 0; i < nbThreads; i++) {
		index_cpu = i%num_cpus;
		auto t = new PLCMThread(this, index_cpu);
		(*threads)[t->getId()] = unique_ptr<PLCMThread>(t);
	}
}

void PLCM::initializeAndStartThreads(shared_ptr<ExplorationStep> initState) {
	for (auto it = threads->begin(); it != threads->end(); ++it) {
		it->second->init(initState);
		it->second->start();
	}
}

shared_ptr<ExplorationStep> PLCM::stealJob(PLCMThread* thief) {
	for (auto it = threads->begin(); it != threads->end(); ++it) {
		auto victim = it->second.get();
		if (victim != thief) {
			auto e = victim->giveJob(thief);
			if (e != nullptr) {
				return e;
			}
		}
	}
	return nullptr;
}

unique_ptr<PatternsCollector> PLCM::instanciateCollector(PLCM::Options *options) {
	PatternsCollector* collector = nullptr;

	if (options->benchmark_mode) {
		collector = new NullCollector();
	} else {
		if (options->output_path != "-") {
			collector = new MultiThreadedFileCollector(
						options->output_path);
		} else {
			collector = new StdOutCollector();
		}

		if (options->sort_items) {
			collector = new PatternSortCollector(collector);
		}
	}

	return unique_ptr<PatternsCollector>(collector);
}

PLCMThread::PLCMThread(PLCM* PLCM_instance, int index_cpu) {
	stackedJobs = new vector<shared_ptr<ExplorationStep> >();
	stackedJobs_storage = unique_ptr<vector<shared_ptr<ExplorationStep> > >(
			stackedJobs); // auto delete

	_PLCM_instance = PLCM_instance;
	_index_cpu = index_cpu;
	should_start = false; // wait for the signal
	_thread = new thread(&PLCMThread::run, this);
	_thread_storage = unique_ptr<thread>(_thread); // auto delete
	for (uint i = 0; i < PLCM::PLCMCounters::Number_of_PLCMCounters; ++i) {
		counters[i] = 0;
	}
}

thread::id PLCMThread::getId() {
	return _thread->get_id();
}

void PLCMThread::run() {
	// 1st, set the thread affinity
	cpu_set_t cpu_set;
	CPU_ZERO(&cpu_set);
	CPU_SET(_index_cpu, &cpu_set);
	sched_setaffinity(0, sizeof(cpu_set_t), &cpu_set);

	// then, wait for the start condition
	{
		unique_lock<mutex> ul(starting_mutex);
		while (!should_start)	// handle spurious wake-ups
		{
			cond_should_start.wait(ul);
		}
	}

	// no need to readlock, this thread is the only one that can do
	// writes
	bool exit = false;
	while (!exit) {
		ExplorationStep *sj = nullptr;
		if (!stackedJobs->empty()) {
			sj = stackedJobs->back().get();

			unique_ptr<ExplorationStep> extended = sj->next();
			// iterator is finished, remove it from the stack
			if (extended == nullptr) {
				ReadWriteLock::WriteGuard lg(_lock);
				stackedJobs->pop_back();
				counters[PLCM::PLCMCounters::ExplorationStepInstances]++;
				counters[PLCM::PLCMCounters::ExplorationStepCatchedWrongFirstParents] +=
						sj->getCatchedWrongFirstParentCount();
			} else {
				lcm(move(extended) /* transfer ownership */);
			}
		} else { // our list was empty, we should steal from another
					// thread
			shared_ptr<ExplorationStep> stolj = _PLCM_instance->stealJob(this);
			if (stolj == nullptr) {
				exit = true;
			} else {
				lcm(stolj);
			}
		}
	}
}

void PLCMThread::init(shared_ptr<ExplorationStep> initState) {
	ReadWriteLock::WriteGuard lg(_lock);
	stackedJobs->push_back(initState);
}

void PLCMThread::join() {
	_thread->join();
}

void PLCMThread::start() {
	{
		lock_guard<mutex> lg(starting_mutex);
		should_start = true;
	}
	cond_should_start.notify_one();
}

void PLCMThread::lcm(shared_ptr<ExplorationStep> state) {
	_PLCM_instance->collect(
			state->counters->transactionsCount,
			state->pattern.get());
	{
		ReadWriteLock::WriteGuard lg(_lock);
		stackedJobs->push_back(state);
	}
}

shared_ptr<ExplorationStep> PLCMThread::giveJob(PLCMThread* thief) {
	// here we need to readlock because the owner thread can write
	ReadWriteLock::ReadGuard lg(_lock);
	for (uint32_t stealPos = 0; stealPos < stackedJobs->size(); stealPos++) {
		shared_ptr<ExplorationStep> sj = stackedJobs->at(stealPos);
		shared_ptr<ExplorationStep> next = Helpers::unique_to_shared(sj->next());

		if (next != nullptr) {
			thief->init(sj);
			return next;
		}
	}
	return nullptr;
}

<commit_msg>Reduce concurrency when stealing jobs.<commit_after>
#include <sstream>
using namespace std;

#include <PLCM.hpp>

#include <internals/Counters.hpp>
#include <internals/Dataset.hpp>
#include <internals/ExplorationStep.hpp>
#include <io/MultiThreadedFileCollector.hpp>
#include <io/NullCollector.hpp>
#include <io/PatternSortCollector.hpp>
#include <io/StdOutCollector.hpp>
#include <util/MemoryPeakWatcherThread.hpp>
#include <util/ProgressWatcherThread.hpp>
#include <util/Helpers.h>

using namespace util;
using namespace io;

unique_ptr<PLCM::thread_map> PLCM::threads = nullptr;

PLCM::PLCM(struct Options *options) {
	uint32_t nbThreads = options->num_threads;
	if (nbThreads < 1) {
		cerr << "nbThreads has to be > 0, given " << nbThreads <<
				". Aborting.";
		exit(EXIT_FAILURE);
	}
	collector = instanciateCollector(options);
	threads = unique_ptr<thread_map>(new thread_map());
	createThreads(nbThreads);
	progressWatch = unique_ptr<ProgressWatcherThread>(
			new ProgressWatcherThread());
	for (uint i = 0; i < PLCMCounters::Number_of_PLCMCounters; ++i) {
				globalCounters[i] = 0;
	}
}

PLCM::~PLCM() {
}

void PLCM::collect(int32_t support, vector<int32_t>* pattern) {
	collector->collect(support, pattern);
}

void PLCM::lcm(shared_ptr<ExplorationStep> initState) {
	if (initState->pattern->size() > 0) {
		collector->collect(
				initState->counters->transactionsCount,
				initState->pattern.get());
	}

	progressWatch->setInitState(initState);

	initializeAndStartThreads(initState);

	progressWatch->start();

	for (auto it = threads->begin(); it != threads->end(); ++it) {
		auto t = it->second.get();
		t->join();
		for (uint i = 0; i < PLCMCounters::Number_of_PLCMCounters; ++i) {
			globalCounters[i] += t->counters[i];
		}
	}

	progressWatch->stop();
}

void PLCM::display(ostream& stream,
		map<string, uint64_t>* additionalcounters) {
	stream << "{\"name\":\"PLCM\", \"threads\":" << threads->size();

	for (uint i = 0; i < PLCMCounters::Number_of_PLCMCounters; ++i) {
		stream << ", \"" << PLCMCountersNames[(PLCMCounters)i] <<
				"\":" << globalCounters[i];
	}

	if (additionalcounters != nullptr) {
		for (auto entry : (*additionalcounters)) {
			stream << ", \"" << entry.first << "\":" << entry.second;
		}
	}

	stream << "}" << endl;
}

int64_t PLCM::closeCollector() {
	return collector->close();
}

int32_t PLCM::getAveragePatternLength() {
	return collector->getAveragePatternLength();
}

uint32_t PLCM::getDefaultNumThreads() {
	unsigned concurentThreadsSupported = thread::hardware_concurrency();
	if (concurentThreadsSupported == 0)
	{
		cerr << "Warning: could not get the number of concurrent threads supported"
				" by your system." << endl;
		concurentThreadsSupported = 4;
	}
	else
	{
	    cout << "Info: your system supports " << concurentThreadsSupported <<
	    		" concurrent threads." << endl;
	}

	return concurentThreadsSupported;
}

int PLCM::main(int argc, char** argv) {

	uint32_t default_num_threads = getDefaultNumThreads();

	TCLAP::CmdLine cmd("Parallel LCM, C++ implementation.");

	TCLAP::SwitchArg benchmark_mode("b","benchmark",
			"Benchmark mode: patterns are not outputted at "
			"all (in which case OUTPUT_PATH is ignored)", cmd);
	TCLAP::SwitchArg memory_usage("m","mem-usage",
			"Give peak memory usage after mining "
			"(instanciates a periodic watcher thread)", cmd);
	TCLAP::SwitchArg sort_items("s", "sort-items",
			"Sort items in outputted patterns, in ascending order", cmd);
	ostringstream oss_explain;
	oss_explain << "How many threads will be launched (defaults to " <<
		default_num_threads << ")";
	TCLAP::ValueArg<uint32_t> num_threads("t", "num-threads",
			oss_explain.str(), false, default_num_threads,
			"num_threads", cmd);
	TCLAP::SwitchArg verbose("v", "verbose",
			"Enable verbose mode, which logs every extension "
			"of the empty pattern", cmd);
	TCLAP::SwitchArg ultra_verbose("V", "ultra-verbose",
			"Enable ultra-verbose mode, which logs every pattern extension"
			" (use with care: it may produce a LOT of output)", cmd);
	TCLAP::UnlabeledValueArg<string> input_path("INPUT_PATH",
			"The path of the file to mine into.", true, "None", "in_file", cmd);
	TCLAP::UnlabeledValueArg<uint32_t> minsup("MINSUP",
			"The minimum support of items.", true, 10, "min_support", cmd);
	TCLAP::UnlabeledValueArg<string> output_path("OUTPUT_PATH",
			"The path of the file where results should be dumped "
			"(defaults to standard output).", false, "-", "out_file", cmd);

	// Parse the argv array	(will exit if invalid)
	cmd.parse( argc, argv );

	unique_ptr<PLCM::Options> options(new PLCM::Options {
			benchmark_mode.getValue(),
			memory_usage.getValue(),
			sort_items.getValue(),
			num_threads.getValue(),
			verbose.getValue(),
			ultra_verbose.getValue(),
			input_path.getValue(),
			minsup.getValue(),
			output_path.getValue()
	});

	// run the main procedure
	standalone(move(options) /* transfer ownership */);
	return 0;
}

void PLCM::standalone(unique_ptr<PLCM::Options> options) {
	unique_ptr<MemoryPeakWatcherThread> memoryWatch = nullptr;

	if (options->memory_usage) {
		memoryWatch = unique_ptr<MemoryPeakWatcherThread>(
				new MemoryPeakWatcherThread());
		memoryWatch->start();
	}

	if (options->ultra_verbose)
	{
		ExplorationStep::verbose = true;
		ExplorationStep::ultraVerbose = true;
	}
	else
	{
		ExplorationStep::verbose = options->verbose;
	}

	double chrono = Helpers::precise_time();
	shared_ptr<ExplorationStep> initState = make_shared<ExplorationStep>(
			options->minsup, options->input_path);
	double loadingTime = Helpers::precise_time() - chrono;
	cerr << "Dataset loaded in " << loadingTime << "s" << endl;

	unique_ptr<PLCM> miner(new PLCM(options.get()));

	chrono = Helpers::precise_time();
	miner->lcm(initState);
	chrono = Helpers::precise_time() - chrono;

	unique_ptr<map<string, uint64_t> > additionalcounters(
			new map<string, uint64_t>());
	(*additionalcounters)["miningTime"] = chrono*1000 /* milliseconds */;
	(*additionalcounters)["outputtedPatterns"] = miner->closeCollector();
	(*additionalcounters)["loadingTime"] = loadingTime*1000 /* milliseconds */;
	(*additionalcounters)["avgPatternLength"] =
			(uint64_t) miner->getAveragePatternLength();

	if (memoryWatch != nullptr) {
		memoryWatch->stop();
		(*additionalcounters)["maxUsedMemory"] = memoryWatch->getMaxUsedMemory();
	}

	miner->display(cerr, additionalcounters.get());
}

PLCMThread* PLCM::getCurrentThread() {
	return (*threads)[this_thread::get_id()].get();
}

void PLCM::createThreads(int32_t nbThreads) {
	cpu_set_t cpu_set;
	sched_getaffinity(0, sizeof(cpu_set_t), &cpu_set);
	int num_cpus = CPU_COUNT(&cpu_set);
	int index_cpu;

	for (int i = 0; i < nbThreads; i++) {
		index_cpu = i%num_cpus;
		auto t = new PLCMThread(this, index_cpu);
		(*threads)[t->getId()] = unique_ptr<PLCMThread>(t);
	}
}

void PLCM::initializeAndStartThreads(shared_ptr<ExplorationStep> initState) {
	for (auto it = threads->begin(); it != threads->end(); ++it) {
		it->second->init(initState);
		it->second->start();
	}
}

shared_ptr<ExplorationStep> PLCM::stealJob(PLCMThread* thief) {

	/* we try to avoid too much concurrency, with all thread trying
	 * to steal jobs to the same victim.
	 *
	 * For example, thread 3 will preferably steal a job to thread 4,
	 * or 5, or 6, ... or N, or 0, or 1, or 2.
	 */

	const thread::id thief_id = thief->getId();

	// point to the thread following the thief
	auto it = threads->begin();
	for (; it->first != thief_id; ++it) { }
	++it;

	// the thief can steal a job to any thread except itself
	size_t num_stealable_threads = threads->size() -1;

	// try each thread pointed by it, in turn
	for (size_t index = 0; index < num_stealable_threads; ++index) {
		if (it == threads->end())
			it = threads->begin();
		auto job = it->second->giveJob(thief);
		if (job != nullptr) {
			//cout << "Job stolen: thread " << it->second->getId()
			//		<< " to " << thief_id << endl;
			return job;
		}

		++it;
	}
	return nullptr;
}

unique_ptr<PatternsCollector> PLCM::instanciateCollector(PLCM::Options *options) {
	PatternsCollector* collector = nullptr;

	if (options->benchmark_mode) {
		collector = new NullCollector();
	} else {
		if (options->output_path != "-") {
			collector = new MultiThreadedFileCollector(
						options->output_path);
		} else {
			collector = new StdOutCollector();
		}

		if (options->sort_items) {
			collector = new PatternSortCollector(collector);
		}
	}

	return unique_ptr<PatternsCollector>(collector);
}

PLCMThread::PLCMThread(PLCM* PLCM_instance, int index_cpu) {
	stackedJobs = new vector<shared_ptr<ExplorationStep> >();
	stackedJobs_storage = unique_ptr<vector<shared_ptr<ExplorationStep> > >(
			stackedJobs); // auto delete

	_PLCM_instance = PLCM_instance;
	_index_cpu = index_cpu;
	should_start = false; // wait for the signal
	_thread = new thread(&PLCMThread::run, this);
	_thread_storage = unique_ptr<thread>(_thread); // auto delete
	for (uint i = 0; i < PLCM::PLCMCounters::Number_of_PLCMCounters; ++i) {
		counters[i] = 0;
	}
}

thread::id PLCMThread::getId() {
	return _thread->get_id();
}

void PLCMThread::run() {
	// 1st, set the thread affinity
	cpu_set_t cpu_set;
	CPU_ZERO(&cpu_set);
	CPU_SET(_index_cpu, &cpu_set);
	sched_setaffinity(0, sizeof(cpu_set_t), &cpu_set);

	// then, wait for the start condition
	{
		unique_lock<mutex> ul(starting_mutex);
		while (!should_start)	// handle spurious wake-ups
		{
			cond_should_start.wait(ul);
		}
	}

	// no need to readlock, this thread is the only one that can do
	// writes
	bool exit = false;
	while (!exit) {
		ExplorationStep *sj = nullptr;
		if (!stackedJobs->empty()) {
			sj = stackedJobs->back().get();

			unique_ptr<ExplorationStep> extended = sj->next();
			// iterator is finished, remove it from the stack
			if (extended == nullptr) {
				ReadWriteLock::WriteGuard lg(_lock);
				stackedJobs->pop_back();
				counters[PLCM::PLCMCounters::ExplorationStepInstances]++;
				counters[PLCM::PLCMCounters::ExplorationStepCatchedWrongFirstParents] +=
						sj->getCatchedWrongFirstParentCount();
			} else {
				lcm(move(extended) /* transfer ownership */);
			}
		} else { // our list was empty, we should steal from another
					// thread
			shared_ptr<ExplorationStep> stolj = _PLCM_instance->stealJob(this);
			if (stolj == nullptr) {
				exit = true;
			} else {
				lcm(stolj);
			}
		}
	}
}

void PLCMThread::init(shared_ptr<ExplorationStep> initState) {
	ReadWriteLock::WriteGuard lg(_lock);
	stackedJobs->push_back(initState);
}

void PLCMThread::join() {
	_thread->join();
}

void PLCMThread::start() {
	{
		lock_guard<mutex> lg(starting_mutex);
		should_start = true;
	}
	cond_should_start.notify_one();
}

void PLCMThread::lcm(shared_ptr<ExplorationStep> state) {
	_PLCM_instance->collect(
			state->counters->transactionsCount,
			state->pattern.get());
	{
		ReadWriteLock::WriteGuard lg(_lock);
		stackedJobs->push_back(state);
	}
}

shared_ptr<ExplorationStep> PLCMThread::giveJob(PLCMThread* thief) {
	// here we need to readlock because the owner thread can write
	ReadWriteLock::ReadGuard lg(_lock);
	for (uint32_t stealPos = 0; stealPos < stackedJobs->size(); stealPos++) {
		shared_ptr<ExplorationStep> sj = stackedJobs->at(stealPos);
		shared_ptr<ExplorationStep> next = Helpers::unique_to_shared(sj->next());

		if (next != nullptr) {
			thief->init(sj);
			return next;
		}
	}
	return nullptr;
}

<|endoftext|>