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

#include <string>
#include <unistd.h>

#include "hd44780_bits.hpp"
#include "ssd1306.hpp"

using namespace upm;

SSD1306::SSD1306(int bus_in, int addr_in) : m_i2c_lcd_control(bus_in)
{
    int vccstate = SSD1306_SWITCHCAPVCC;
    _vccstate = vccstate;

    int LCD_CMD = 0x00;

    m_lcd_control_address = addr_in;
    m_name = "SSD1306";

    mraa::Result error = m_i2c_lcd_control.address(m_lcd_control_address);

    if (error != mraa::SUCCESS) {
        throw std::runtime_error(std::string(__FUNCTION__) +
                                ": mraa_i2c_address() failed");
        return;
    }

    error = m_i2c_lcd_control.frequency(mraa::I2C_FAST);

    if (error != mraa::SUCCESS) {
        throw std::invalid_argument(std::string(__FUNCTION__) +
                                ": mraa_i2c_frequency(MRAA_I2C_FAST) failed");
        return;
    }

    m_i2c_lcd_control.writeReg(LCD_CMD, DISPLAY_CMD_OFF); // display off
    usleep(4500);
    //ADD 1306 stuff
    // Init sequence for 128x64 OLED module                 // 0xAE
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SETDISPLAYCLOCKDIV);            // 0xD5
    m_i2c_lcd_control.writeReg(LCD_CMD, 0x80);                                  // the suggested ratio 0x80
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SETMULTIPLEX);                  // 0xA8
    m_i2c_lcd_control.writeReg(LCD_CMD, 0x3F);
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SETDISPLAYOFFSET);              // 0xD3
    m_i2c_lcd_control.writeReg(LCD_CMD, 0x0);                                   // no offset
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SETSTARTLINE | 0x0);            // line #0
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_CHARGEPUMP);                    // 0x8D
    if (vccstate == SSD1306_EXTERNALVCC) {
        m_i2c_lcd_control.writeReg(LCD_CMD, 0x10);
    } else {
        m_i2c_lcd_control.writeReg(LCD_CMD,0x14);
    }
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_MEMORYMODE);                    // 0x20
    m_i2c_lcd_control.writeReg(LCD_CMD, 0x00);                                  // 0x0 act like ks0108
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SEGREMAP | 0x1);
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_COMSCANDEC);
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SETCOMPINS);                    // 0xDA
    m_i2c_lcd_control.writeReg(LCD_CMD, 0x12);
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SETCONTRAST);                   // 0x81
    if (vccstate == SSD1306_EXTERNALVCC) {
        m_i2c_lcd_control.writeReg(LCD_CMD, 0x9F);
    } else {
        m_i2c_lcd_control.writeReg(LCD_CMD, 0xCF);
    }
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SETPRECHARGE);                  // 0xd9
    if (vccstate == SSD1306_EXTERNALVCC) {
      m_i2c_lcd_control.writeReg(LCD_CMD, 0x22);
    } else {
      m_i2c_lcd_control.writeReg(LCD_CMD,0xF1);
    }
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SETVCOMDETECT);                 // 0xDB
    m_i2c_lcd_control.writeReg(LCD_CMD, 0x40);
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_DISPLAYALLON_RESUME);           // 0xA4
    m_i2c_lcd_control.writeReg(LCD_CMD, DISPLAY_CMD_SET_NORMAL_1306);                 // 0xA6

    //END 1306 Stuff
    m_i2c_lcd_control.writeReg(LCD_CMD, DISPLAY_CMD_ON); // display on
    usleep(4500);
    setNormalDisplay(); // set to normal display '1' is ON

    clear();
    setAddressingMode(PAGE);
}

SSD1306::~SSD1306()
{
}

mraa::Result
SSD1306::draw(uint8_t* data, int bytes)
{
    mraa::Result error = mraa::SUCCESS;

    setAddressingMode(HORIZONTAL);
    for (int idx = 0; idx < bytes; idx++) {
        m_i2c_lcd_control.writeReg(LCD_DATA, data[idx]);
    }

    return error;
}

/*
 * **************
 *  virtual area
 * **************
 */
mraa::Result
SSD1306::write(std::string msg)
{
    mraa::Result error = mraa::SUCCESS;

    setAddressingMode(PAGE);
    for (std::string::size_type i = 0; i < msg.size(); ++i) {
        writeChar(msg[i]);
    }

    return error;
}

mraa::Result
SSD1306::setCursor(int row, int column)
{
    mraa::Result error = mraa::SUCCESS;

    error = m_i2c_lcd_control.writeReg(LCD_CMD, BASE_PAGE_START_ADDR + row); // set page address
    error = m_i2c_lcd_control.writeReg(LCD_CMD,
                                       BASE_LOW_COLUMN_ADDR + (8 * column & 0x0F)); // set column
                                                                                // lower address
    error = m_i2c_lcd_control.writeReg(LCD_CMD,
                                       BASE_HIGH_COLUMN_ADDR +
                                       ((8 * column >> 4) & 0x0F)); // set column higher address

    return error;
}

mraa::Result
SSD1306::clear()
{
    mraa::Result error = mraa::SUCCESS;
    uint8_t columnIdx, rowIdx;

    m_i2c_lcd_control.writeReg(LCD_CMD, DISPLAY_CMD_OFF); // display off
    for (rowIdx = 0; rowIdx < 8; rowIdx++) {
        setCursor(rowIdx, 0);

        // clear all columns
        for (columnIdx = 0; columnIdx < 16; columnIdx++) {
            writeChar(' ');
        }
    }
    m_i2c_lcd_control.writeReg(LCD_CMD, DISPLAY_CMD_ON); // display on
    home();

    return error;
}

mraa::Result
SSD1306::home()
{
    return setCursor(0, 0);
}

/*
 * **************
 *  private area
 * **************
 */
mraa::Result
SSD1306::writeChar(uint8_t value)
{
    mraa::Result rv;
    if (value < 0x20 || value > 0x7F) {
        value = 0x20; // space
    }

    for (uint8_t idx = 0; idx < 8; idx++) {
        rv = m_i2c_lcd_control.writeReg(LCD_DATA, BasicFont[value - 32][idx]);
    }

    return rv;
}

mraa::Result
SSD1306::setNormalDisplay()
{
    return m_i2c_lcd_control.writeReg(LCD_CMD,
                                      DISPLAY_CMD_SET_NORMAL_1306); // set to normal display '1' is
                                                                    // ON
}

mraa::Result
SSD1306::setAddressingMode(displayAddressingMode mode)
{
    mraa::Result rv;
    rv =m_i2c_lcd_control.writeReg(LCD_CMD, DISPLAY_CMD_MEM_ADDR_MODE); // set addressing mode
    rv =m_i2c_lcd_control.writeReg(LCD_CMD, mode);                      // set page addressing mode
    return rv;
}

mraa::Result
SSD1306::invert(bool i)
{
    mraa::Result rv;
    if(i){
        rv = m_i2c_lcd_control.writeReg(LCD_CMD,  DISPLAY_CMD_SET_INVERT_1306);
    } else {
        rv = m_i2c_lcd_control.writeReg(LCD_CMD,  DISPLAY_CMD_SET_NORMAL_1306);
    }
    return rv;
}

void SSD1306::startscrollright(uint8_t start, uint8_t stop){
    m_i2c_lcd_control.writeReg(LCD_CMD,SSD1306_RIGHT_HORIZONTAL_SCROLL);
    m_i2c_lcd_control.writeReg(LCD_CMD,0X00);
    m_i2c_lcd_control.writeReg(LCD_CMD,start);
    m_i2c_lcd_control.writeReg(LCD_CMD,0X00);
    m_i2c_lcd_control.writeReg(LCD_CMD,stop);
    m_i2c_lcd_control.writeReg(LCD_CMD,0X00);
    m_i2c_lcd_control.writeReg(LCD_CMD,0XFF);
    m_i2c_lcd_control.writeReg(LCD_CMD,SSD1306_ACTIVATE_SCROLL);
}

void SSD1306::startscrollleft(uint8_t start, uint8_t stop){
    m_i2c_lcd_control.writeReg(LCD_CMD,SSD1306_LEFT_HORIZONTAL_SCROLL);
    m_i2c_lcd_control.writeReg(LCD_CMD,0X00);
    m_i2c_lcd_control.writeReg(LCD_CMD,start);
    m_i2c_lcd_control.writeReg(LCD_CMD,0X00);
    m_i2c_lcd_control.writeReg(LCD_CMD,stop);
    m_i2c_lcd_control.writeReg(LCD_CMD,0X00);
    m_i2c_lcd_control.writeReg(LCD_CMD,0XFF);
    m_i2c_lcd_control.writeReg(LCD_CMD,SSD1306_ACTIVATE_SCROLL);
}

void SSD1306::startscrolldiagright(uint8_t start, uint8_t stop){
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SET_VERTICAL_SCROLL_AREA);
    m_i2c_lcd_control.writeReg(LCD_CMD, 0X00);
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_LCDHEIGHT);
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL);
    m_i2c_lcd_control.writeReg(LCD_CMD, 0X00);
    m_i2c_lcd_control.writeReg(LCD_CMD, start);
    m_i2c_lcd_control.writeReg(LCD_CMD, 0X00);
    m_i2c_lcd_control.writeReg(LCD_CMD, stop);
    m_i2c_lcd_control.writeReg(LCD_CMD, 0X01);
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_ACTIVATE_SCROLL);
}

void SSD1306::startscrolldiagleft(uint8_t start, uint8_t stop){
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SET_VERTICAL_SCROLL_AREA);
    m_i2c_lcd_control.writeReg(LCD_CMD, 0X00);
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_LCDHEIGHT);
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL);
    m_i2c_lcd_control.writeReg(LCD_CMD, 0X00);
    m_i2c_lcd_control.writeReg(LCD_CMD, start);
    m_i2c_lcd_control.writeReg(LCD_CMD, 0X00);
    m_i2c_lcd_control.writeReg(LCD_CMD, stop);
    m_i2c_lcd_control.writeReg(LCD_CMD, 0X01);
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_ACTIVATE_SCROLL);
}

void SSD1306::stopscroll(void){
    m_i2c_lcd_control.writeReg(LCD_CMD,SSD1306_DEACTIVATE_SCROLL);
}

// Dim the display
// dim = true: display is dimmed
// dim = false: display is normal
void SSD1306::dim(bool dim) {
    uint8_t contrast;

    if (dim) {
    contrast = 0; // Dimmed display
    } else {
    if (_vccstate == SSD1306_EXTERNALVCC) {
      contrast = 0x9F;
    } else {
      contrast = 0xCF;
    }
    }
    // the range of contrast to too small to be really useful
    // it is useful to dim the display
    m_i2c_lcd_control.writeReg(LCD_CMD,SSD1306_SETCONTRAST);
    m_i2c_lcd_control.writeReg(LCD_CMD,contrast);
}
<commit_msg>ssd1306: failed i2c frequency change should be non-fatal for this device<commit_after>/*
 * Author: Marc Graham <marc@m2ag.net>
 * Copyright (c) 2015 Intel Corporation
 *
 * Adapted from ssd1308 library.
 * Author: Yevgeniy Kiveisha <yevgeniy.kiveisha@intel.com>
 * Copyright (c) 2014 Intel Corporation.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include <string>
#include <unistd.h>
#include <syslog.h>

#include "hd44780_bits.hpp"
#include "ssd1306.hpp"

using namespace upm;

SSD1306::SSD1306(int bus_in, int addr_in) : m_i2c_lcd_control(bus_in)
{
    int vccstate = SSD1306_SWITCHCAPVCC;
    _vccstate = vccstate;

    int LCD_CMD = 0x00;

    m_lcd_control_address = addr_in;
    m_name = "SSD1306";

    mraa::Result error = m_i2c_lcd_control.address(m_lcd_control_address);

    if (error != mraa::SUCCESS) {
        throw std::runtime_error(std::string(__FUNCTION__) +
                                ": mraa_i2c_address() failed");
        return;
    }

    error = m_i2c_lcd_control.frequency(mraa::I2C_FAST);

    if (error != mraa::SUCCESS) {
        syslog(LOG_WARNING, "%s: mraa_i2c_frequency(MRAA_I2C_FAST) failed, using default speed", std::string(__FUNCTION__).c_str());
    }

    m_i2c_lcd_control.writeReg(LCD_CMD, DISPLAY_CMD_OFF); // display off
    usleep(4500);
    //ADD 1306 stuff
    // Init sequence for 128x64 OLED module                 // 0xAE
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SETDISPLAYCLOCKDIV);            // 0xD5
    m_i2c_lcd_control.writeReg(LCD_CMD, 0x80);                                  // the suggested ratio 0x80
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SETMULTIPLEX);                  // 0xA8
    m_i2c_lcd_control.writeReg(LCD_CMD, 0x3F);
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SETDISPLAYOFFSET);              // 0xD3
    m_i2c_lcd_control.writeReg(LCD_CMD, 0x0);                                   // no offset
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SETSTARTLINE | 0x0);            // line #0
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_CHARGEPUMP);                    // 0x8D
    if (vccstate == SSD1306_EXTERNALVCC) {
        m_i2c_lcd_control.writeReg(LCD_CMD, 0x10);
    } else {
        m_i2c_lcd_control.writeReg(LCD_CMD,0x14);
    }
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_MEMORYMODE);                    // 0x20
    m_i2c_lcd_control.writeReg(LCD_CMD, 0x00);                                  // 0x0 act like ks0108
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SEGREMAP | 0x1);
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_COMSCANDEC);
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SETCOMPINS);                    // 0xDA
    m_i2c_lcd_control.writeReg(LCD_CMD, 0x12);
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SETCONTRAST);                   // 0x81
    if (vccstate == SSD1306_EXTERNALVCC) {
        m_i2c_lcd_control.writeReg(LCD_CMD, 0x9F);
    } else {
        m_i2c_lcd_control.writeReg(LCD_CMD, 0xCF);
    }
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SETPRECHARGE);                  // 0xd9
    if (vccstate == SSD1306_EXTERNALVCC) {
      m_i2c_lcd_control.writeReg(LCD_CMD, 0x22);
    } else {
      m_i2c_lcd_control.writeReg(LCD_CMD,0xF1);
    }
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SETVCOMDETECT);                 // 0xDB
    m_i2c_lcd_control.writeReg(LCD_CMD, 0x40);
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_DISPLAYALLON_RESUME);           // 0xA4
    m_i2c_lcd_control.writeReg(LCD_CMD, DISPLAY_CMD_SET_NORMAL_1306);                 // 0xA6

    //END 1306 Stuff
    m_i2c_lcd_control.writeReg(LCD_CMD, DISPLAY_CMD_ON); // display on
    usleep(4500);
    setNormalDisplay(); // set to normal display '1' is ON

    clear();
    setAddressingMode(PAGE);
}

SSD1306::~SSD1306()
{
}

mraa::Result
SSD1306::draw(uint8_t* data, int bytes)
{
    mraa::Result error = mraa::SUCCESS;

    setAddressingMode(HORIZONTAL);
    for (int idx = 0; idx < bytes; idx++) {
        m_i2c_lcd_control.writeReg(LCD_DATA, data[idx]);
    }

    return error;
}

/*
 * **************
 *  virtual area
 * **************
 */
mraa::Result
SSD1306::write(std::string msg)
{
    mraa::Result error = mraa::SUCCESS;

    setAddressingMode(PAGE);
    for (std::string::size_type i = 0; i < msg.size(); ++i) {
        writeChar(msg[i]);
    }

    return error;
}

mraa::Result
SSD1306::setCursor(int row, int column)
{
    mraa::Result error = mraa::SUCCESS;

    error = m_i2c_lcd_control.writeReg(LCD_CMD, BASE_PAGE_START_ADDR + row); // set page address
    error = m_i2c_lcd_control.writeReg(LCD_CMD,
                                       BASE_LOW_COLUMN_ADDR + (8 * column & 0x0F)); // set column
                                                                                // lower address
    error = m_i2c_lcd_control.writeReg(LCD_CMD,
                                       BASE_HIGH_COLUMN_ADDR +
                                       ((8 * column >> 4) & 0x0F)); // set column higher address

    return error;
}

mraa::Result
SSD1306::clear()
{
    mraa::Result error = mraa::SUCCESS;
    uint8_t columnIdx, rowIdx;

    m_i2c_lcd_control.writeReg(LCD_CMD, DISPLAY_CMD_OFF); // display off
    for (rowIdx = 0; rowIdx < 8; rowIdx++) {
        setCursor(rowIdx, 0);

        // clear all columns
        for (columnIdx = 0; columnIdx < 16; columnIdx++) {
            writeChar(' ');
        }
    }
    m_i2c_lcd_control.writeReg(LCD_CMD, DISPLAY_CMD_ON); // display on
    home();

    return error;
}

mraa::Result
SSD1306::home()
{
    return setCursor(0, 0);
}

/*
 * **************
 *  private area
 * **************
 */
mraa::Result
SSD1306::writeChar(uint8_t value)
{
    mraa::Result rv;
    if (value < 0x20 || value > 0x7F) {
        value = 0x20; // space
    }

    for (uint8_t idx = 0; idx < 8; idx++) {
        rv = m_i2c_lcd_control.writeReg(LCD_DATA, BasicFont[value - 32][idx]);
    }

    return rv;
}

mraa::Result
SSD1306::setNormalDisplay()
{
    return m_i2c_lcd_control.writeReg(LCD_CMD,
                                      DISPLAY_CMD_SET_NORMAL_1306); // set to normal display '1' is
                                                                    // ON
}

mraa::Result
SSD1306::setAddressingMode(displayAddressingMode mode)
{
    mraa::Result rv;
    rv =m_i2c_lcd_control.writeReg(LCD_CMD, DISPLAY_CMD_MEM_ADDR_MODE); // set addressing mode
    rv =m_i2c_lcd_control.writeReg(LCD_CMD, mode);                      // set page addressing mode
    return rv;
}

mraa::Result
SSD1306::invert(bool i)
{
    mraa::Result rv;
    if(i){
        rv = m_i2c_lcd_control.writeReg(LCD_CMD,  DISPLAY_CMD_SET_INVERT_1306);
    } else {
        rv = m_i2c_lcd_control.writeReg(LCD_CMD,  DISPLAY_CMD_SET_NORMAL_1306);
    }
    return rv;
}

void SSD1306::startscrollright(uint8_t start, uint8_t stop){
    m_i2c_lcd_control.writeReg(LCD_CMD,SSD1306_RIGHT_HORIZONTAL_SCROLL);
    m_i2c_lcd_control.writeReg(LCD_CMD,0X00);
    m_i2c_lcd_control.writeReg(LCD_CMD,start);
    m_i2c_lcd_control.writeReg(LCD_CMD,0X00);
    m_i2c_lcd_control.writeReg(LCD_CMD,stop);
    m_i2c_lcd_control.writeReg(LCD_CMD,0X00);
    m_i2c_lcd_control.writeReg(LCD_CMD,0XFF);
    m_i2c_lcd_control.writeReg(LCD_CMD,SSD1306_ACTIVATE_SCROLL);
}

void SSD1306::startscrollleft(uint8_t start, uint8_t stop){
    m_i2c_lcd_control.writeReg(LCD_CMD,SSD1306_LEFT_HORIZONTAL_SCROLL);
    m_i2c_lcd_control.writeReg(LCD_CMD,0X00);
    m_i2c_lcd_control.writeReg(LCD_CMD,start);
    m_i2c_lcd_control.writeReg(LCD_CMD,0X00);
    m_i2c_lcd_control.writeReg(LCD_CMD,stop);
    m_i2c_lcd_control.writeReg(LCD_CMD,0X00);
    m_i2c_lcd_control.writeReg(LCD_CMD,0XFF);
    m_i2c_lcd_control.writeReg(LCD_CMD,SSD1306_ACTIVATE_SCROLL);
}

void SSD1306::startscrolldiagright(uint8_t start, uint8_t stop){
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SET_VERTICAL_SCROLL_AREA);
    m_i2c_lcd_control.writeReg(LCD_CMD, 0X00);
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_LCDHEIGHT);
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL);
    m_i2c_lcd_control.writeReg(LCD_CMD, 0X00);
    m_i2c_lcd_control.writeReg(LCD_CMD, start);
    m_i2c_lcd_control.writeReg(LCD_CMD, 0X00);
    m_i2c_lcd_control.writeReg(LCD_CMD, stop);
    m_i2c_lcd_control.writeReg(LCD_CMD, 0X01);
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_ACTIVATE_SCROLL);
}

void SSD1306::startscrolldiagleft(uint8_t start, uint8_t stop){
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_SET_VERTICAL_SCROLL_AREA);
    m_i2c_lcd_control.writeReg(LCD_CMD, 0X00);
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_LCDHEIGHT);
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL);
    m_i2c_lcd_control.writeReg(LCD_CMD, 0X00);
    m_i2c_lcd_control.writeReg(LCD_CMD, start);
    m_i2c_lcd_control.writeReg(LCD_CMD, 0X00);
    m_i2c_lcd_control.writeReg(LCD_CMD, stop);
    m_i2c_lcd_control.writeReg(LCD_CMD, 0X01);
    m_i2c_lcd_control.writeReg(LCD_CMD, SSD1306_ACTIVATE_SCROLL);
}

void SSD1306::stopscroll(void){
    m_i2c_lcd_control.writeReg(LCD_CMD,SSD1306_DEACTIVATE_SCROLL);
}

// Dim the display
// dim = true: display is dimmed
// dim = false: display is normal
void SSD1306::dim(bool dim) {
    uint8_t contrast;

    if (dim) {
    contrast = 0; // Dimmed display
    } else {
    if (_vccstate == SSD1306_EXTERNALVCC) {
      contrast = 0x9F;
    } else {
      contrast = 0xCF;
    }
    }
    // the range of contrast to too small to be really useful
    // it is useful to dim the display
    m_i2c_lcd_control.writeReg(LCD_CMD,SSD1306_SETCONTRAST);
    m_i2c_lcd_control.writeReg(LCD_CMD,contrast);
}
<|endoftext|>


<commit_before>/* ******************************************************************************
 *
 *
 * This program and the accompanying materials are made available under the
 * terms of the Apache License, Version 2.0 which is available at
 * https://www.apache.org/licenses/LICENSE-2.0.
 *
 *  See the NOTICE file distributed with this work for additional
 *  information regarding copyright ownership.
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 * SPDX-License-Identifier: Apache-2.0
 ******************************************************************************/

//
// Created by raver119 on 07.10.2017.
//

#include <ops/declarable/OpRegistrator.h>

#include <sstream>

namespace sd {
namespace ops {

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

template <typename OpName>
__registrator<OpName>::__registrator() {
  auto ptr = new OpName();
  OpRegistrator::getInstance().registerOperation(ptr);
}

template <typename OpName>
__registratorSynonym<OpName>::__registratorSynonym(const char* name, const char* oname) {
  auto ptr = reinterpret_cast<OpName*>(OpRegistrator::getInstance().getOperation(oname));
  if (ptr == nullptr) {
    std::string newName(name);
    std::string oldName(oname);

    OpRegistrator::getInstance().updateMSVC(sd::ops::HashHelper::getInstance().getLongHash(newName), oldName);
    return;
  }
  OpRegistrator::getInstance().registerOperation(name, ptr);
}

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

OpRegistrator& OpRegistrator::getInstance() {
  static OpRegistrator instance;
  return instance;
}

void OpRegistrator::updateMSVC(sd::LongType newHash, std::string& oldName) {
  std::pair<sd::LongType, std::string> pair(newHash, oldName);
  _msvc.insert(pair);
}

template <typename T>
std::string OpRegistrator::local_to_string(T value) {
  // create an output string stream
  std::ostringstream os;

  // throw the value into the string stream
  os << value;

  // convert the string stream into a string and return
  return os.str();
}

template <>
std::string OpRegistrator::local_to_string(int value) {
  // create an output string stream
  std::ostringstream os;

  // throw the value into the string stream
  os << value;

  // convert the string stream into a string and return
  return os.str();
}

void OpRegistrator::sigIntHandler(int sig) {}

void OpRegistrator::exitHandler() {}

void OpRegistrator::sigSegVHandler(int sig) {}

OpRegistrator::~OpRegistrator() {
#ifndef _RELEASE
  _msvc.clear();

  for (auto x : _uniqueD) delete x;

  for (auto x : _uniqueH) delete x;

  _uniqueD.clear();

  _uniqueH.clear();

  _declarablesD.clear();

  _declarablesLD.clear();
#if defined(HAVE_VEDA)
  for (auto x : _uniqueHLegacy) delete x;
  _helpersHLegacy.clear();
#endif
#endif
}

const char* OpRegistrator::getAllCustomOperations() {
  _locker.lock();

  if (!isInit) {
    for (SD_MAP_IMPL<std::string, sd::ops::DeclarableOp*>::iterator it = _declarablesD.begin();
         it != _declarablesD.end(); ++it) {
      std::string op = it->first + ":" + local_to_string(it->second->getOpDescriptor()->getHash()) + ":" +
                       local_to_string(it->second->getOpDescriptor()->getNumberOfInputs()) + ":" +
                       local_to_string(it->second->getOpDescriptor()->getNumberOfOutputs()) + ":" +
                       local_to_string(it->second->getOpDescriptor()->allowsInplace()) + ":" +
                       local_to_string(it->second->getOpDescriptor()->getNumberOfTArgs()) + ":" +
                       local_to_string(it->second->getOpDescriptor()->getNumberOfIArgs()) + ":" + ";";
      _opsList += op;
    }

    isInit = true;
  }

  _locker.unlock();

  return _opsList.c_str();
}

bool OpRegistrator::registerOperation(const char* name, sd::ops::DeclarableOp* op) {
  std::string str(name);
  std::pair<std::string, sd::ops::DeclarableOp*> pair(str, op);
  _declarablesD.insert(pair);

  auto hash = sd::ops::HashHelper::getInstance().getLongHash(str);
  std::pair<sd::LongType, sd::ops::DeclarableOp*> pair2(hash, op);
  _declarablesLD.insert(pair2);
  return true;
}

/**
 * This method registers operation
 *
 * @param op
 */
bool OpRegistrator::registerOperation(sd::ops::DeclarableOp* op) {
  _uniqueD.emplace_back(op);
  return registerOperation(op->getOpName()->c_str(), op);
}

void OpRegistrator::registerHelper(sd::ops::platforms::PlatformHelper* op) {
  std::pair<sd::LongType, samediff::Engine> p = {op->hash(), op->engine()};
  if (_helpersLH.count(p) > 0) throw std::runtime_error("Tried to double register PlatformHelper");

  _uniqueH.emplace_back(op);

  sd_debug("Adding helper for op \"%s\": [%lld - %i]\n", op->name().c_str(), op->hash(), (int)op->engine());

  std::pair<std::pair<std::string, samediff::Engine>, sd::ops::platforms::PlatformHelper*> pair(
      {op->name(), op->engine()}, op);
  _helpersH.insert(pair);

  std::pair<std::pair<sd::LongType, samediff::Engine>, sd::ops::platforms::PlatformHelper*> pair2(p, op);
  _helpersLH.insert(pair2);
}

void OpRegistrator::registerHelperLegacy(sd::ops::platforms::PlatformHelperLegacy* op) {
  auto entry = op->getEntry();
  if (_helpersHLegacy.count(entry) > 0) throw std::runtime_error("Tried to double register PlatformHelper Legacy");

  _uniqueHLegacy.emplace_back(op);

  sd_debug("Adding legacy helper  for op prefix\"%s\" opNum: %d engine: [%i]\n", entry.prefix, entry.opNum,
           entry.engine);

  _helpersHLegacy.emplace(entry, op);
}

sd::ops::DeclarableOp* OpRegistrator::getOperation(const char* name) {
  std::string str(name);
  return getOperation(str);
}

/**
 * This method returns registered Op by name
 *
 * @param name
 * @return
 */
sd::ops::DeclarableOp* OpRegistrator::getOperation(sd::LongType hash) {
  if (!_declarablesLD.count(hash)) {
    if (!_msvc.count(hash)) {
      sd_printf("Unknown D operation requested by hash: [%lld]\n", hash);
      return nullptr;
    } else {
      _locker.lock();

      auto str = _msvc.at(hash);
      auto op = _declarablesD.at(str);
      auto oHash = op->getOpDescriptor()->getHash();

      std::pair<sd::LongType, sd::ops::DeclarableOp*> pair(oHash, op);
      _declarablesLD.insert(pair);

      _locker.unlock();
    }
  }

  return _declarablesLD.at(hash);
}

sd::ops::DeclarableOp* OpRegistrator::getOperation(std::string& name) {
  if (!_declarablesD.count(name)) {
    sd_debug("Unknown operation requested: [%s]\n", name.c_str());
    return nullptr;
  }

  return _declarablesD.at(name);
}

sd::ops::platforms::PlatformHelper* OpRegistrator::getPlatformHelper(sd::LongType hash, samediff::Engine engine) {
  std::pair<sd::LongType, samediff::Engine> p = {hash, engine};
  if (_helpersLH.count(p) == 0) throw std::runtime_error("Requested helper can't be found");

  return _helpersLH[p];
}

#if defined(HAVE_VEDA)
sd::ops::platforms::PlatformHelperLegacy* OpRegistrator::getPlatformHelperLegacy(
    const platforms::PlatformHelperLegacyEntry& entry) {
  // do not throw , just return nullptr
  if (_helpersHLegacy.count(entry) < 1) return nullptr;
  return _helpersHLegacy[entry];
}
#endif

bool OpRegistrator::hasHelper(sd::LongType hash, samediff::Engine engine) {
  std::pair<sd::LongType, samediff::Engine> p = {hash, engine};
  return _helpersLH.count(p) > 0;
}

int OpRegistrator::numberOfOperations() { return (int)_declarablesLD.size(); }

std::vector<sd::LongType> OpRegistrator::getAllHashes() {
  std::vector<sd::LongType> result;

  for (auto& v : _declarablesLD) {
    result.emplace_back(v.first);
  }

  return result;
}
}  // namespace ops
}  // namespace sd

namespace std {
size_t hash<std::pair<sd::LongType, samediff::Engine>>::operator()(
    const std::pair<sd::LongType, samediff::Engine>& k) const {
  using std::hash;
  auto res = std::hash<sd::LongType>()(k.first);
  res ^= std::hash<int>()((int)k.second) + 0x9e3779b9 + (res << 6) + (res >> 2);
  return res;
}

size_t hash<std::pair<std::string, samediff::Engine>>::operator()(
    const std::pair<std::string, samediff::Engine>& k) const {
  using std::hash;
  auto res = std::hash<std::string>()(k.first);
  res ^= std::hash<int>()((int)k.second) + 0x9e3779b9 + (res << 6) + (res >> 2);
  return res;
}
}  // namespace std
<commit_msg>exclude veda for normal builds (#9707)<commit_after>/* ******************************************************************************
 *
 *
 * This program and the accompanying materials are made available under the
 * terms of the Apache License, Version 2.0 which is available at
 * https://www.apache.org/licenses/LICENSE-2.0.
 *
 *  See the NOTICE file distributed with this work for additional
 *  information regarding copyright ownership.
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 * SPDX-License-Identifier: Apache-2.0
 ******************************************************************************/

//
// Created by raver119 on 07.10.2017.
//

#include <ops/declarable/OpRegistrator.h>

#include <sstream>

namespace sd {
namespace ops {

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

template <typename OpName>
__registrator<OpName>::__registrator() {
  auto ptr = new OpName();
  OpRegistrator::getInstance().registerOperation(ptr);
}

template <typename OpName>
__registratorSynonym<OpName>::__registratorSynonym(const char* name, const char* oname) {
  auto ptr = reinterpret_cast<OpName*>(OpRegistrator::getInstance().getOperation(oname));
  if (ptr == nullptr) {
    std::string newName(name);
    std::string oldName(oname);

    OpRegistrator::getInstance().updateMSVC(sd::ops::HashHelper::getInstance().getLongHash(newName), oldName);
    return;
  }
  OpRegistrator::getInstance().registerOperation(name, ptr);
}

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

OpRegistrator& OpRegistrator::getInstance() {
  static OpRegistrator instance;
  return instance;
}

void OpRegistrator::updateMSVC(sd::LongType newHash, std::string& oldName) {
  std::pair<sd::LongType, std::string> pair(newHash, oldName);
  _msvc.insert(pair);
}

template <typename T>
std::string OpRegistrator::local_to_string(T value) {
  // create an output string stream
  std::ostringstream os;

  // throw the value into the string stream
  os << value;

  // convert the string stream into a string and return
  return os.str();
}

template <>
std::string OpRegistrator::local_to_string(int value) {
  // create an output string stream
  std::ostringstream os;

  // throw the value into the string stream
  os << value;

  // convert the string stream into a string and return
  return os.str();
}

void OpRegistrator::sigIntHandler(int sig) {}

void OpRegistrator::exitHandler() {}

void OpRegistrator::sigSegVHandler(int sig) {}

OpRegistrator::~OpRegistrator() {
#ifndef _RELEASE
  _msvc.clear();

  for (auto x : _uniqueD) delete x;

  for (auto x : _uniqueH) delete x;

  _uniqueD.clear();

  _uniqueH.clear();

  _declarablesD.clear();

  _declarablesLD.clear();
#if defined(HAVE_VEDA)
  for (auto x : _uniqueHLegacy) delete x;
  _helpersHLegacy.clear();
#endif
#endif
}

const char* OpRegistrator::getAllCustomOperations() {
  _locker.lock();

  if (!isInit) {
    for (SD_MAP_IMPL<std::string, sd::ops::DeclarableOp*>::iterator it = _declarablesD.begin();
         it != _declarablesD.end(); ++it) {
      std::string op = it->first + ":" + local_to_string(it->second->getOpDescriptor()->getHash()) + ":" +
                       local_to_string(it->second->getOpDescriptor()->getNumberOfInputs()) + ":" +
                       local_to_string(it->second->getOpDescriptor()->getNumberOfOutputs()) + ":" +
                       local_to_string(it->second->getOpDescriptor()->allowsInplace()) + ":" +
                       local_to_string(it->second->getOpDescriptor()->getNumberOfTArgs()) + ":" +
                       local_to_string(it->second->getOpDescriptor()->getNumberOfIArgs()) + ":" + ";";
      _opsList += op;
    }

    isInit = true;
  }

  _locker.unlock();

  return _opsList.c_str();
}

bool OpRegistrator::registerOperation(const char* name, sd::ops::DeclarableOp* op) {
  std::string str(name);
  std::pair<std::string, sd::ops::DeclarableOp*> pair(str, op);
  _declarablesD.insert(pair);

  auto hash = sd::ops::HashHelper::getInstance().getLongHash(str);
  std::pair<sd::LongType, sd::ops::DeclarableOp*> pair2(hash, op);
  _declarablesLD.insert(pair2);
  return true;
}

/**
 * This method registers operation
 *
 * @param op
 */
bool OpRegistrator::registerOperation(sd::ops::DeclarableOp* op) {
  _uniqueD.emplace_back(op);
  return registerOperation(op->getOpName()->c_str(), op);
}

void OpRegistrator::registerHelper(sd::ops::platforms::PlatformHelper* op) {
  std::pair<sd::LongType, samediff::Engine> p = {op->hash(), op->engine()};
  if (_helpersLH.count(p) > 0) throw std::runtime_error("Tried to double register PlatformHelper");

  _uniqueH.emplace_back(op);

  sd_debug("Adding helper for op \"%s\": [%lld - %i]\n", op->name().c_str(), op->hash(), (int)op->engine());

  std::pair<std::pair<std::string, samediff::Engine>, sd::ops::platforms::PlatformHelper*> pair(
      {op->name(), op->engine()}, op);
  _helpersH.insert(pair);

  std::pair<std::pair<sd::LongType, samediff::Engine>, sd::ops::platforms::PlatformHelper*> pair2(p, op);
  _helpersLH.insert(pair2);
}
#if defined(HAVE_VEDA)
void OpRegistrator::registerHelperLegacy(sd::ops::platforms::PlatformHelperLegacy* op) {
  auto entry = op->getEntry();
  if (_helpersHLegacy.count(entry) > 0) throw std::runtime_error("Tried to double register PlatformHelper Legacy");

  _uniqueHLegacy.emplace_back(op);

  sd_debug("Adding legacy helper  for op prefix\"%s\" opNum: %d engine: [%i]\n", entry.prefix, entry.opNum,
           entry.engine);

  _helpersHLegacy.emplace(entry, op);
}
#endif
sd::ops::DeclarableOp* OpRegistrator::getOperation(const char* name) {
  std::string str(name);
  return getOperation(str);
}

/**
 * This method returns registered Op by name
 *
 * @param name
 * @return
 */
sd::ops::DeclarableOp* OpRegistrator::getOperation(sd::LongType hash) {
  if (!_declarablesLD.count(hash)) {
    if (!_msvc.count(hash)) {
      sd_printf("Unknown D operation requested by hash: [%lld]\n", hash);
      return nullptr;
    } else {
      _locker.lock();

      auto str = _msvc.at(hash);
      auto op = _declarablesD.at(str);
      auto oHash = op->getOpDescriptor()->getHash();

      std::pair<sd::LongType, sd::ops::DeclarableOp*> pair(oHash, op);
      _declarablesLD.insert(pair);

      _locker.unlock();
    }
  }

  return _declarablesLD.at(hash);
}

sd::ops::DeclarableOp* OpRegistrator::getOperation(std::string& name) {
  if (!_declarablesD.count(name)) {
    sd_debug("Unknown operation requested: [%s]\n", name.c_str());
    return nullptr;
  }

  return _declarablesD.at(name);
}

sd::ops::platforms::PlatformHelper* OpRegistrator::getPlatformHelper(sd::LongType hash, samediff::Engine engine) {
  std::pair<sd::LongType, samediff::Engine> p = {hash, engine};
  if (_helpersLH.count(p) == 0) throw std::runtime_error("Requested helper can't be found");

  return _helpersLH[p];
}

#if defined(HAVE_VEDA)
sd::ops::platforms::PlatformHelperLegacy* OpRegistrator::getPlatformHelperLegacy(
    const platforms::PlatformHelperLegacyEntry& entry) {
  // do not throw , just return nullptr
  if (_helpersHLegacy.count(entry) < 1) return nullptr;
  return _helpersHLegacy[entry];
}
#endif

bool OpRegistrator::hasHelper(sd::LongType hash, samediff::Engine engine) {
  std::pair<sd::LongType, samediff::Engine> p = {hash, engine};
  return _helpersLH.count(p) > 0;
}

int OpRegistrator::numberOfOperations() { return (int)_declarablesLD.size(); }

std::vector<sd::LongType> OpRegistrator::getAllHashes() {
  std::vector<sd::LongType> result;

  for (auto& v : _declarablesLD) {
    result.emplace_back(v.first);
  }

  return result;
}
}  // namespace ops
}  // namespace sd

namespace std {
size_t hash<std::pair<sd::LongType, samediff::Engine>>::operator()(
    const std::pair<sd::LongType, samediff::Engine>& k) const {
  using std::hash;
  auto res = std::hash<sd::LongType>()(k.first);
  res ^= std::hash<int>()((int)k.second) + 0x9e3779b9 + (res << 6) + (res >> 2);
  return res;
}

size_t hash<std::pair<std::string, samediff::Engine>>::operator()(
    const std::pair<std::string, samediff::Engine>& k) const {
  using std::hash;
  auto res = std::hash<std::string>()(k.first);
  res ^= std::hash<int>()((int)k.second) + 0x9e3779b9 + (res << 6) + (res >> 2);
  return res;
}
}  // namespace std
<|endoftext|>


<commit_before>// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "ghost.h"

#include <limits.h>
#include <mntent.h>
#include <string.h>
#include <sys/auxv.h>
#include <sys/mman.h>
#include <sys/mount.h>

#include <csignal>
#include <filesystem>
#include <fstream>
#include <limits>
#include <regex>  // NOLINT: no RE2; ghost limits itself to absl
#include <thread>
#include <unordered_map>
#include <vector>

#include "absl/container/flat_hash_map.h"

// The open source Google benchmarks library includes its own verbose flag (`v`)
// and it does not use absl to parse this flag. Thus, to avoid a symbol
// conflict, we need to name our verbose flag differently outside of Google.
//
// All internal Google binaries have a `v` flag by default. To be consistent
// with our open source project, we instead use the `verbose` flag internally,
// too.
ABSL_FLAG(int32_t, verbose, 0, "Verbosity level");

namespace ghost {

LocalStatusWordTable::LocalStatusWordTable(int enclave_fd, int id,
                                           int numa_node) {
  int ctl = openat(enclave_fd, "ctl", O_RDWR);
  CHECK_GE(ctl, 0);
  std::string cmd = absl::StrCat("create sw_region ", id, " ", numa_node);
  ssize_t ret = write(ctl, cmd.c_str(), cmd.length());
  CHECK(ret == cmd.length() || errno == EEXIST);
  close(ctl);

  fd_ =
      openat(enclave_fd, absl::StrCat("sw_regions/sw_", id).c_str(), O_RDONLY);
  CHECK_GE(fd_, 0);
  map_size_ = GetFileSize(fd_);
  header_ = static_cast<ghost_sw_region_header*>(
      mmap(nullptr, map_size_, PROT_READ, MAP_SHARED, fd_, 0));
  CHECK_NE(header_, MAP_FAILED);
  CHECK_LT(0, header_->capacity);
  CHECK_EQ(header_->id, id);
  CHECK_EQ(header_->numa_node, numa_node);

  table_ = reinterpret_cast<ghost_status_word*>(
      reinterpret_cast<intptr_t>(header_) + header_->start);
  CHECK_NE(table_, nullptr);
}

LocalStatusWordTable::~LocalStatusWordTable() {
  CHECK_EQ(munmap(header_, map_size_), 0);
  CHECK_EQ(close(fd_), 0);
}

static ghost_status_word* status_word_from_info(ghost_sw_info* sw_info) {
  StatusWordTable* table = Ghost::GetGlobalStatusWordTable();
  CHECK_EQ(sw_info->id, table->id());
  return table->get(sw_info->index);
}

StatusWord::StatusWord(Gtid gtid, ghost_sw_info sw_info) {
  sw_info_ = sw_info;
  sw_ = status_word_from_info(&sw_info_);
  owner_ = gtid;
}

StatusWord::~StatusWord() {
  // TODO: Consider just making this automatic?
  if (!empty()) {
    GHOST_ERROR("%s leaked status word", owner_.describe());
  }
}

LocalStatusWord::LocalStatusWord(StatusWord::AgentSW) {
  CHECK_ZERO(Ghost::GetStatusWordInfo(GHOST_AGENT, sched_getcpu(), &sw_info_));
  sw_ = status_word_from_info(&sw_info_);
  owner_ = Gtid::Current();
}

LocalStatusWord& LocalStatusWord::operator=(LocalStatusWord&& move_from) {
  if (&move_from == this) return *this;

  sw_info_ = move_from.sw_info_;
  sw_ = move_from.sw_;
  owner_ = move_from.owner_;
  move_from.sw_ = nullptr;  // reset move_from to empty

  return *this;
}

LocalStatusWord::LocalStatusWord(LocalStatusWord&& move_from) {
  *this = std::move(move_from);
}

void LocalStatusWord::Free() {
  CHECK(!empty());
  CHECK(can_free());

  CHECK_EQ(Ghost::FreeStatusWordInfo(&sw_info_), 0);
  sw_ = nullptr;
  owner_ = Gtid(0);
}

// static
bool Ghost::GhostIsMountedAt(const char* path) {
  bool ret = false;
  FILE* mounts = setmntent("/proc/self/mounts", "r");
  CHECK_NE(mounts, nullptr);

  mntent* ent;
  while ((ent = getmntent(mounts))) {
    if (!strcmp(path, ent->mnt_dir) && !strcmp("ghost", ent->mnt_type)) {
      ret = true;
      break;
    }
  }
  endmntent(mounts);
  return ret;
}

// static
void Ghost::MountGhostfs() {
  if (mount("ghost", Ghost::kGhostfsMount, "ghost", 0, nullptr)) {
    // EBUSY means it is already mounted. Anything else is failure. This CHECK
    // is generally triggered when you forget to compile ghOSt into the kernel,
    // such as by neglecting to set the Linux config option
    // `CONFIG_SCHED_CLASS_GHOST` to `y` in the Linux `.config` file.
    CHECK_EQ(errno, EBUSY);
  }
}

// Returns the ghOSt abi versions supported by the kernel.
// static
int Ghost::GetSupportedVersions(std::vector<uint32_t>& versions) {
  if (!GhostIsMountedAt(Ghost::kGhostfsMount)) {
    MountGhostfs();
  }
  std::ifstream ver(absl::StrCat(Ghost::kGhostfsMount, "/version"));
  if (!ver.is_open()) {
    return -1;
  }

  std::string line;
  while (std::getline(ver, line)) {
    uint32_t v;
    if (absl::SimpleAtoi(line, &v)) {
      versions.push_back(v);
    } else {
      return -1;
    }
  }
  return 0;
}

// static
int Ghost::gbl_ctl_fd_ = -1;
int Ghost::gbl_dir_fd_ = -1;
// static
StatusWordTable* Ghost::gbl_sw_table_;

// static
void Ghost::InitCore() {
  Gtid::Current().assign_name("main");
  GhostSignals::Init();

  // Some of the tests don't have agents, but they call InitCore()
  CheckVersion();

  // We make assumptions around MAX_CPUS being a power of 2 in Topology.
  static_assert((MAX_CPUS & (MAX_CPUS - 1)) == 0);
}

// static
void GhostSignals::Init() {
  std::signal(SIGABRT, SigHand);
  std::signal(SIGFPE, SigHand);
  std::signal(SIGILL, SigHand);
  std::signal(SIGINT, SigHand);
  std::signal(SIGTERM, SigHand);
  std::signal(SIGUSR1, SigHand);
  // Don't handle `SIGCHLD`; it's used by `ForkedProcess`.

  struct sigaction sigsegv_act = {{0}};
  sigsegv_act.sa_sigaction = SigSegvAction;
  sigsegv_act.sa_flags = SA_SIGINFO;

  sigaction(SIGSEGV, &sigsegv_act, NULL);
}

void GhostSignals::IgnoreCommon() {
  std::signal(SIGINT, SigIgnore);
  std::signal(SIGTERM, SigIgnore);
  std::signal(SIGUSR1, SigIgnore);
  // Don't handle `SIGCHLD`; it's used by `ForkedProcess`.
}

// static
absl::flat_hash_map<int, std::vector<std::function<bool(int)>>>
    GhostSignals::handlers_;
// static
void GhostSignals::SigHand(int signum) {
  bool fatal = true;

  for (auto& handler : handlers_[signum]) fatal = handler(signum) && fatal;

  if (fatal) {
    std::cerr << "Fatal signal " << strsignal(signum) << ": " << std::endl;
    Exit(1);
  }
}

void GhostSignals::SigSegvAction(int signum, siginfo_t* info, void* uctx) {
  std::cerr << "PID " << Gtid::Current().tid() << " Fatal segfault at addr "
            << info->si_addr << ": " << std::endl;
  PrintBacktrace(stderr, uctx);
  std::exit(1);
}

void GhostSignals::AddHandler(int signal, std::function<bool(int)> handler) {
  handlers_[signal].push_back(std::move(handler));
}

int SchedTaskEnterGhost(pid_t pid, int dir_fd) {
  if (dir_fd == -1) {
    dir_fd = Ghost::GetGlobalEnclaveDirFd();
  }
  // If the open/close of tasks is a performance problem, we can have the caller
  // open it for us.
  int tasks_fd = openat(dir_fd, "tasks", O_WRONLY);
  if (tasks_fd < 0) {
    return -1;
  }
  std::string pid_s = std::to_string(pid);
  int ret = 0;
  if (write(tasks_fd, pid_s.c_str(), pid_s.length()) != pid_s.length()) {
    ret = -1;
  }
  int old_errno = errno;
  close(tasks_fd);
  errno = old_errno;
  // We used to "Trust but verify" that pid was in ghost.  However, it's
  // possible that the syscall succeeded, but the enclave was immediately
  // destroyed, and our task is back in CFS already.
  return ret;
}

int SchedAgentEnterGhost(int ctl_fd, int queue_fd) {
  std::string cmd = absl::StrCat("become agent ", queue_fd);
  ssize_t ret = write(ctl_fd, cmd.c_str(), cmd.length());
  if (ret == cmd.length()) {
    CHECK_EQ(sched_getscheduler(0), SCHED_GHOST | SCHED_RESET_ON_FORK);
    return 0;
  }
  return ret;
}

// Returns the ctlfd and dirfd for some enclave that is accepting tasks.
struct ctl_dir {
  int ctl;
  int dir;
};
static ctl_dir FindActiveEnclave() {
  std::error_code ec;
  auto f = std::filesystem::directory_iterator(Ghost::kGhostfsMount, ec);
  auto end = std::filesystem::directory_iterator();
  for (/* f */; !ec && f != end; f.increment(ec)) {
    if (std::regex_match(f->path().filename().string(),
                         std::regex("^enclave_.*"))) {
      std::ifstream status((f->path() / "status").string());
      std::string line;
      while (std::getline(status, line)) {
        if (line == "active yes") {
          int ctl = open((f->path() / "ctl").string().c_str(), O_RDONLY);
          if (ctl >= 0) {
            int dir = open(f->path().string().c_str(), O_PATH);
            CHECK_GE(dir, 0);
            return {ctl, dir};
          }
        }
      }
    }
  }
  return {-1, -1};
}

GhostThread::GhostThread(KernelScheduler ksched, std::function<void()> work)
    : ksched_(ksched) {
  GhostThread::SetGlobalEnclaveFdsOnce();

  thread_ = std::thread([this, w = std::move(work)] {
    tid_ = GetTID();
    gtid_ = Gtid::Current();

    // TODO: Consider moving after SchedEnterGhost.
    started_.Notify();

    if (ksched_ == KernelScheduler::kGhost) {
      const int ret = SchedTaskEnterGhost(/*pid=*/0);
      CHECK_EQ(ret, 0);
    }
    std::move(w)();
  });
  started_.WaitForNotification();
}

GhostThread::~GhostThread() { CHECK(!thread_.joinable()); }

// Agents should have already set the global enclave fd before creating agent
// tasks, so this helper is used by clients to find an enclave to join.
//
// This is the only place where there are concurrent writers that set the
// process-wide GlobalEnclaveCtlFd, and there are numerous places with unlocked
// readers.  All readers (agents or client tasks) will go through this helper
// before reading.  Once someone finds an enclave, all tasks will use that FD
// until someone comes along and resets it.
//
// The only resetters are weird test cases, such as agent_test and
// transaction_test, where the same global value gets reused because we run our
// tests from within the same process.
// static
void GhostThread::SetGlobalEnclaveFdsOnce() {
  static absl::Mutex mtx(absl::kConstInit);
  absl::MutexLock lock(&mtx);
  if (Ghost::GetGlobalEnclaveCtlFd() == -1) {
    CHECK_EQ(Ghost::GetGlobalEnclaveDirFd(), -1);
    ctl_dir cd = FindActiveEnclave();
    if (cd.ctl >= 0) {
      Ghost::SetGlobalEnclaveFds(cd.ctl, cd.dir);
    }
  }
}

}  // namespace ghost
<commit_msg>ghost: do not move Notify() after SchedEnterGhost()<commit_after>// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "ghost.h"

#include <limits.h>
#include <mntent.h>
#include <string.h>
#include <sys/auxv.h>
#include <sys/mman.h>
#include <sys/mount.h>

#include <csignal>
#include <filesystem>
#include <fstream>
#include <limits>
#include <regex>  // NOLINT: no RE2; ghost limits itself to absl
#include <thread>
#include <unordered_map>
#include <vector>

#include "absl/container/flat_hash_map.h"

// The open source Google benchmarks library includes its own verbose flag (`v`)
// and it does not use absl to parse this flag. Thus, to avoid a symbol
// conflict, we need to name our verbose flag differently outside of Google.
//
// All internal Google binaries have a `v` flag by default. To be consistent
// with our open source project, we instead use the `verbose` flag internally,
// too.
ABSL_FLAG(int32_t, verbose, 0, "Verbosity level");

namespace ghost {

LocalStatusWordTable::LocalStatusWordTable(int enclave_fd, int id,
                                           int numa_node) {
  int ctl = openat(enclave_fd, "ctl", O_RDWR);
  CHECK_GE(ctl, 0);
  std::string cmd = absl::StrCat("create sw_region ", id, " ", numa_node);
  ssize_t ret = write(ctl, cmd.c_str(), cmd.length());
  CHECK(ret == cmd.length() || errno == EEXIST);
  close(ctl);

  fd_ =
      openat(enclave_fd, absl::StrCat("sw_regions/sw_", id).c_str(), O_RDONLY);
  CHECK_GE(fd_, 0);
  map_size_ = GetFileSize(fd_);
  header_ = static_cast<ghost_sw_region_header*>(
      mmap(nullptr, map_size_, PROT_READ, MAP_SHARED, fd_, 0));
  CHECK_NE(header_, MAP_FAILED);
  CHECK_LT(0, header_->capacity);
  CHECK_EQ(header_->id, id);
  CHECK_EQ(header_->numa_node, numa_node);

  table_ = reinterpret_cast<ghost_status_word*>(
      reinterpret_cast<intptr_t>(header_) + header_->start);
  CHECK_NE(table_, nullptr);
}

LocalStatusWordTable::~LocalStatusWordTable() {
  CHECK_EQ(munmap(header_, map_size_), 0);
  CHECK_EQ(close(fd_), 0);
}

static ghost_status_word* status_word_from_info(ghost_sw_info* sw_info) {
  StatusWordTable* table = Ghost::GetGlobalStatusWordTable();
  CHECK_EQ(sw_info->id, table->id());
  return table->get(sw_info->index);
}

StatusWord::StatusWord(Gtid gtid, ghost_sw_info sw_info) {
  sw_info_ = sw_info;
  sw_ = status_word_from_info(&sw_info_);
  owner_ = gtid;
}

StatusWord::~StatusWord() {
  // TODO: Consider just making this automatic?
  if (!empty()) {
    GHOST_ERROR("%s leaked status word", owner_.describe());
  }
}

LocalStatusWord::LocalStatusWord(StatusWord::AgentSW) {
  CHECK_ZERO(Ghost::GetStatusWordInfo(GHOST_AGENT, sched_getcpu(), &sw_info_));
  sw_ = status_word_from_info(&sw_info_);
  owner_ = Gtid::Current();
}

LocalStatusWord& LocalStatusWord::operator=(LocalStatusWord&& move_from) {
  if (&move_from == this) return *this;

  sw_info_ = move_from.sw_info_;
  sw_ = move_from.sw_;
  owner_ = move_from.owner_;
  move_from.sw_ = nullptr;  // reset move_from to empty

  return *this;
}

LocalStatusWord::LocalStatusWord(LocalStatusWord&& move_from) {
  *this = std::move(move_from);
}

void LocalStatusWord::Free() {
  CHECK(!empty());
  CHECK(can_free());

  CHECK_EQ(Ghost::FreeStatusWordInfo(&sw_info_), 0);
  sw_ = nullptr;
  owner_ = Gtid(0);
}

// static
bool Ghost::GhostIsMountedAt(const char* path) {
  bool ret = false;
  FILE* mounts = setmntent("/proc/self/mounts", "r");
  CHECK_NE(mounts, nullptr);

  mntent* ent;
  while ((ent = getmntent(mounts))) {
    if (!strcmp(path, ent->mnt_dir) && !strcmp("ghost", ent->mnt_type)) {
      ret = true;
      break;
    }
  }
  endmntent(mounts);
  return ret;
}

// static
void Ghost::MountGhostfs() {
  if (mount("ghost", Ghost::kGhostfsMount, "ghost", 0, nullptr)) {
    // EBUSY means it is already mounted. Anything else is failure. This CHECK
    // is generally triggered when you forget to compile ghOSt into the kernel,
    // such as by neglecting to set the Linux config option
    // `CONFIG_SCHED_CLASS_GHOST` to `y` in the Linux `.config` file.
    CHECK_EQ(errno, EBUSY);
  }
}

// Returns the ghOSt abi versions supported by the kernel.
// static
int Ghost::GetSupportedVersions(std::vector<uint32_t>& versions) {
  if (!GhostIsMountedAt(Ghost::kGhostfsMount)) {
    MountGhostfs();
  }
  std::ifstream ver(absl::StrCat(Ghost::kGhostfsMount, "/version"));
  if (!ver.is_open()) {
    return -1;
  }

  std::string line;
  while (std::getline(ver, line)) {
    uint32_t v;
    if (absl::SimpleAtoi(line, &v)) {
      versions.push_back(v);
    } else {
      return -1;
    }
  }
  return 0;
}

// static
int Ghost::gbl_ctl_fd_ = -1;
int Ghost::gbl_dir_fd_ = -1;
// static
StatusWordTable* Ghost::gbl_sw_table_;

// static
void Ghost::InitCore() {
  Gtid::Current().assign_name("main");
  GhostSignals::Init();

  // Some of the tests don't have agents, but they call InitCore()
  CheckVersion();

  // We make assumptions around MAX_CPUS being a power of 2 in Topology.
  static_assert((MAX_CPUS & (MAX_CPUS - 1)) == 0);
}

// static
void GhostSignals::Init() {
  std::signal(SIGABRT, SigHand);
  std::signal(SIGFPE, SigHand);
  std::signal(SIGILL, SigHand);
  std::signal(SIGINT, SigHand);
  std::signal(SIGTERM, SigHand);
  std::signal(SIGUSR1, SigHand);
  // Don't handle `SIGCHLD`; it's used by `ForkedProcess`.

  struct sigaction sigsegv_act = {{0}};
  sigsegv_act.sa_sigaction = SigSegvAction;
  sigsegv_act.sa_flags = SA_SIGINFO;

  sigaction(SIGSEGV, &sigsegv_act, NULL);
}

void GhostSignals::IgnoreCommon() {
  std::signal(SIGINT, SigIgnore);
  std::signal(SIGTERM, SigIgnore);
  std::signal(SIGUSR1, SigIgnore);
  // Don't handle `SIGCHLD`; it's used by `ForkedProcess`.
}

// static
absl::flat_hash_map<int, std::vector<std::function<bool(int)>>>
    GhostSignals::handlers_;
// static
void GhostSignals::SigHand(int signum) {
  bool fatal = true;

  for (auto& handler : handlers_[signum]) fatal = handler(signum) && fatal;

  if (fatal) {
    std::cerr << "Fatal signal " << strsignal(signum) << ": " << std::endl;
    Exit(1);
  }
}

void GhostSignals::SigSegvAction(int signum, siginfo_t* info, void* uctx) {
  std::cerr << "PID " << Gtid::Current().tid() << " Fatal segfault at addr "
            << info->si_addr << ": " << std::endl;
  PrintBacktrace(stderr, uctx);
  std::exit(1);
}

void GhostSignals::AddHandler(int signal, std::function<bool(int)> handler) {
  handlers_[signal].push_back(std::move(handler));
}

int SchedTaskEnterGhost(pid_t pid, int dir_fd) {
  if (dir_fd == -1) {
    dir_fd = Ghost::GetGlobalEnclaveDirFd();
  }
  // If the open/close of tasks is a performance problem, we can have the caller
  // open it for us.
  int tasks_fd = openat(dir_fd, "tasks", O_WRONLY);
  if (tasks_fd < 0) {
    return -1;
  }
  std::string pid_s = std::to_string(pid);
  int ret = 0;
  if (write(tasks_fd, pid_s.c_str(), pid_s.length()) != pid_s.length()) {
    ret = -1;
  }
  int old_errno = errno;
  close(tasks_fd);
  errno = old_errno;
  // We used to "Trust but verify" that pid was in ghost.  However, it's
  // possible that the syscall succeeded, but the enclave was immediately
  // destroyed, and our task is back in CFS already.
  return ret;
}

int SchedAgentEnterGhost(int ctl_fd, int queue_fd) {
  std::string cmd = absl::StrCat("become agent ", queue_fd);
  ssize_t ret = write(ctl_fd, cmd.c_str(), cmd.length());
  if (ret == cmd.length()) {
    CHECK_EQ(sched_getscheduler(0), SCHED_GHOST | SCHED_RESET_ON_FORK);
    return 0;
  }
  return ret;
}

// Returns the ctlfd and dirfd for some enclave that is accepting tasks.
struct ctl_dir {
  int ctl;
  int dir;
};
static ctl_dir FindActiveEnclave() {
  std::error_code ec;
  auto f = std::filesystem::directory_iterator(Ghost::kGhostfsMount, ec);
  auto end = std::filesystem::directory_iterator();
  for (/* f */; !ec && f != end; f.increment(ec)) {
    if (std::regex_match(f->path().filename().string(),
                         std::regex("^enclave_.*"))) {
      std::ifstream status((f->path() / "status").string());
      std::string line;
      while (std::getline(status, line)) {
        if (line == "active yes") {
          int ctl = open((f->path() / "ctl").string().c_str(), O_RDONLY);
          if (ctl >= 0) {
            int dir = open(f->path().string().c_str(), O_PATH);
            CHECK_GE(dir, 0);
            return {ctl, dir};
          }
        }
      }
    }
  }
  return {-1, -1};
}

GhostThread::GhostThread(KernelScheduler ksched, std::function<void()> work)
    : ksched_(ksched) {
  GhostThread::SetGlobalEnclaveFdsOnce();

  thread_ = std::thread([this, w = std::move(work)] {
    tid_ = GetTID();
    gtid_ = Gtid::Current();

    started_.Notify();

    if (ksched_ == KernelScheduler::kGhost) {
      const int ret = SchedTaskEnterGhost(/*pid=*/0);
      CHECK_EQ(ret, 0);
    }
    std::move(w)();
  });
  started_.WaitForNotification();
}

GhostThread::~GhostThread() { CHECK(!thread_.joinable()); }

// Agents should have already set the global enclave fd before creating agent
// tasks, so this helper is used by clients to find an enclave to join.
//
// This is the only place where there are concurrent writers that set the
// process-wide GlobalEnclaveCtlFd, and there are numerous places with unlocked
// readers.  All readers (agents or client tasks) will go through this helper
// before reading.  Once someone finds an enclave, all tasks will use that FD
// until someone comes along and resets it.
//
// The only resetters are weird test cases, such as agent_test and
// transaction_test, where the same global value gets reused because we run our
// tests from within the same process.
// static
void GhostThread::SetGlobalEnclaveFdsOnce() {
  static absl::Mutex mtx(absl::kConstInit);
  absl::MutexLock lock(&mtx);
  if (Ghost::GetGlobalEnclaveCtlFd() == -1) {
    CHECK_EQ(Ghost::GetGlobalEnclaveDirFd(), -1);
    ctl_dir cd = FindActiveEnclave();
    if (cd.ctl >= 0) {
      Ghost::SetGlobalEnclaveFds(cd.ctl, cd.dir);
    }
  }
}

}  // namespace ghost
<|endoftext|>


<commit_before>#ifndef SRC_NODES_BASE_NODE_HPP_
#define SRC_NODES_BASE_NODE_HPP_

#include <flexcore/extended/graph/graph.hpp>

#include <flexcore/scheduler/parallelregion.hpp>
#include <adobe/forest.hpp>

#include <cassert>
#include <string>
#include <memory>

#include <flexcore/ports.hpp>

namespace fc
{

/**
 * \brief base class for nodes contained in forest
 *
 *  Nodes are neither copy_constructyble nor copy_assignable.
 *
 *
 * \invariant region_ != null_ptr
 * \invariant self_ is always valid
 */
class tree_base_node
{
public:
	typedef adobe::forest<std::unique_ptr<tree_base_node>> forest_t;

	template<class data_t> using event_source = ::fc::event_source<data_t>;
	template<class data_t> using event_sink = ::fc::event_sink<data_t>;
	template<class data_t> using state_source = ::fc::state_source<data_t>;
	template<class data_t> using state_sink = ::fc::state_sink<data_t>;

	tree_base_node(forest_t* f, std::shared_ptr<parallel_region> r, std::string name);
	virtual ~tree_base_node() = default;

	const std::shared_ptr<parallel_region>& region() const { return region_; }

	std::string name() const { return graph_info_.name(); }

	graph::graph_node_properties graph_info() const { return graph_info_; }
	graph::connection_graph& get_graph() const;

protected:
	forest_t* forest_;
private:
	/* Information about which region the node belongs to */
	std::shared_ptr<parallel_region> region_;
	/* Information for abstract graph */
	//stores the metainformation of the node used by the abstract graph
	graph::graph_node_properties graph_info_;

};

/**
 * \brief base class for nodes which own other nodes, aka nested nodes.
 *
 * \invariant forest_ != nullptr
 *
 * Nodes of this type may have children nodes.
 *
 * Use make_child/make_child_named to create a node already inserted into
 * the ownership tree.
 *
 * Node creation examples:
 * \code{cpp}
 * owning_base_node root;
 * // simple creation
 * root.make_child<node>();
 * root.make_child<node_tmpl<int>>();
 * root.make_child_named<node_tmpl<int>>("name");
 * \endcode
 */
class owning_base_node : public tree_base_node
{
public:
	owning_base_node(forest_t* f, std::shared_ptr<parallel_region> r, std::string name)
		: tree_base_node(f, r, name)
	{
	}

	/**
	 * \brief creates child node of type node_t with constructor arguments args.
	 *
	 * Inserts new child into tree.
	 * Child node is attached to region of parent node.
	 *
	 * \tparam node_t type of node to be created
	 * \param args constructor arguments passed to node_t
	 * \return pointer to child node
	 * \post nr of children > 0
	 */
	template<class node_t, class ... args_t>
	node_t* make_child(args_t&&... args)
	{
		return static_cast<node_t*>(add_child(std::make_unique<node_t>(
				std::forward<args_t>(args)...,
				forest_,
				region())));
	}

	/**
	 * \brief  Overload of make_child with region.
	 *
	 * @param r Child is attached to this region
	 * @param args onstructor arguments passed to node_t
	 * @return pointer to child node
	 */
	template<class node_t, class ... args_t>
	node_t* make_child(std::shared_ptr<parallel_region> r, args_t&&... args)
	{
		return static_cast<node_t*>(add_child(std::make_unique<node_t>(
				std::forward<args_t>(args)...,
				forest_,
				r)));
	}

	/**
	 * \brief Creates a new child node of type node_t from args
	 * if node_t inherits from owning_base_node
	 *
	 * Sets name of child to n and inserts child into tree.
	 * Sets forest of of child to forest of parent.
	 * \return pointer to child node
	 * \post nr of children > 0
	 */
	template<class node_t, class ... args_t>
	node_t* make_child_named(std::string name, args_t&&... args)
	{
		return static_cast<node_t*>(add_child(std::make_unique<node_t>(
				std::forward<args_t>(args)...,
				forest_,
				region(),
				name)));
	}

	template<class node_t, class ... args_t>
	node_t* make_child_named(std::shared_ptr<parallel_region> r, std::string name, args_t&&... args)
	{
		return static_cast<node_t*>(add_child(std::make_unique<node_t>(
				std::forward<args_t>(args)...,
				forest_,
				std::move(r),
				std::move(name))));
	}

protected:
	forest_t::iterator self() const;
	// stores the access to the forest this node is contained in.
private:
	/**
	 * Takes ownership of child node and inserts into tree.
	 * \return pointer to child node
	 * \pre child != nullptr
	 */
	tree_base_node* add_child(std::unique_ptr<tree_base_node> child);

};

class root_node : public owning_base_node
{
public:
	root_node(forest_t* f, std::shared_ptr<parallel_region> r, std::string n);
	graph::connection_graph& graph() { return graph_; }
private:
	graph::connection_graph graph_;
};

/**
 * \brief Root node for building node trees.
 *
 * Has ownership of the forest and thus serves
 * as the root node for all other nodes in the forest.
 */
class forest_owner
{
public:
	typedef adobe::forest<std::unique_ptr<tree_base_node>> forest_t;

	forest_owner(std::string n, std::shared_ptr<parallel_region> r);

	owning_base_node& nodes() { return *tree_root; }
	graph::connection_graph& get_graph() { return tree_root->graph(); }

private:
	std::unique_ptr<forest_t> forest_;
	/// non_owning access to first node in tree, ownership is in forest.
	root_node* tree_root;
};

/**
 * \brief Erases node and recursively erases all children.
 *
 * \param forest, forest to delete node from.
 * \position iterator of forest pointing to node.
 * \pre position must be in forest.
 * \returns todo
 *
 * invalidates iterators pointing to deleted node.
 */
inline tree_base_node::forest_t::iterator
erase_with_subtree(
		tree_base_node::forest_t& forest,
		tree_base_node::forest_t::iterator position)
{
	return forest.erase(
			adobe::child_begin(position).base(),
			adobe::child_end(position).base());
}

/**
 * \brief Returns the full name of a node.
 *
 * The full name consists of the chained name of the nodes parent, grandparent etc.
 * and the name of the node itself.
 * The names are separated by a separation token.
 */
std::string full_name(tree_base_node::forest_t& forest,
                      const tree_base_node* node);

} // namespace fc

#endif /* SRC_NODES_BASE_NODE_HPP_ */
<commit_msg>nodes: Allow for non-inheritance based forest membership<commit_after>#ifndef SRC_NODES_BASE_NODE_HPP_
#define SRC_NODES_BASE_NODE_HPP_

#include <flexcore/extended/graph/graph.hpp>

#include <flexcore/scheduler/parallelregion.hpp>
#include <adobe/forest.hpp>

#include <cassert>
#include <string>
#include <memory>

#include <flexcore/ports.hpp>

namespace fc
{

/**
 * \brief base class for nodes contained in forest
 *
 *  Nodes are neither copy_constructyble nor copy_assignable.
 *
 *
 * \invariant region_ != null_ptr
 * \invariant self_ is always valid
 */
class tree_base_node
{
public:
	typedef adobe::forest<std::unique_ptr<tree_base_node>> forest_t;

	template<class data_t> using event_source = ::fc::event_source<data_t>;
	template<class data_t> using event_sink = ::fc::event_sink<data_t>;
	template<class data_t> using state_source = ::fc::state_source<data_t>;
	template<class data_t> using state_sink = ::fc::state_sink<data_t>;

	tree_base_node(forest_t* f, std::shared_ptr<parallel_region> r, std::string name);
	virtual ~tree_base_node() = default;

	const std::shared_ptr<parallel_region>& region() const { return region_; }

	std::string name() const { return graph_info_.name(); }

	graph::graph_node_properties graph_info() const { return graph_info_; }
	graph::connection_graph& get_graph() const;

protected:
	forest_t* forest_;
private:
	/* Information about which region the node belongs to */
	std::shared_ptr<parallel_region> region_;
	/* Information for abstract graph */
	//stores the metainformation of the node used by the abstract graph
	graph::graph_node_properties graph_info_;

};

/**
 * \brief base class for nodes which own other nodes, aka nested nodes.
 *
 * \invariant forest_ != nullptr
 *
 * Nodes of this type may have children nodes.
 *
 * Use make_child/make_child_named to create a node already inserted into
 * the ownership tree.
 *
 * Node creation examples:
 * \code{cpp}
 * owning_base_node root;
 * // simple creation
 * root.make_child<node>();
 * root.make_child<node_tmpl<int>>();
 * root.make_child_named<node_tmpl<int>>("name");
 * \endcode
 */
class owning_base_node : public tree_base_node
{
public:
	owning_base_node(forest_t* f, std::shared_ptr<parallel_region> r, std::string name)
		: tree_base_node(f, r, name)
	{
	}

	tree_base_node* new_node(std::string name)
	{
		return add_child(std::make_unique<tree_base_node>(forest_, region(), name));
	}
	tree_base_node* new_node(std::shared_ptr<parallel_region> r, std::string name)
	{
		return add_child(std::make_unique<tree_base_node>(forest_, r, name));
	}
	/**
	 * \brief creates child node of type node_t with constructor arguments args.
	 *
	 * Inserts new child into tree.
	 * Child node is attached to region of parent node.
	 *
	 * \tparam node_t type of node to be created
	 * \param args constructor arguments passed to node_t
	 * \return pointer to child node
	 * \post nr of children > 0
	 */
	template<class node_t, class ... args_t>
	node_t* make_child(args_t&&... args)
	{
		return static_cast<node_t*>(add_child(std::make_unique<node_t>(
				std::forward<args_t>(args)...,
				forest_,
				region())));
	}

	/**
	 * \brief  Overload of make_child with region.
	 *
	 * @param r Child is attached to this region
	 * @param args onstructor arguments passed to node_t
	 * @return pointer to child node
	 */
	template<class node_t, class ... args_t>
	node_t* make_child(std::shared_ptr<parallel_region> r, args_t&&... args)
	{
		return static_cast<node_t*>(add_child(std::make_unique<node_t>(
				std::forward<args_t>(args)...,
				forest_,
				r)));
	}

	/**
	 * \brief Creates a new child node of type node_t from args
	 * if node_t inherits from owning_base_node
	 *
	 * Sets name of child to n and inserts child into tree.
	 * Sets forest of of child to forest of parent.
	 * \return pointer to child node
	 * \post nr of children > 0
	 */
	template<class node_t, class ... args_t>
	node_t* make_child_named(std::string name, args_t&&... args)
	{
		return static_cast<node_t*>(add_child(std::make_unique<node_t>(
				std::forward<args_t>(args)...,
				forest_,
				region(),
				name)));
	}

	template<class node_t, class ... args_t>
	node_t* make_child_named(std::shared_ptr<parallel_region> r, std::string name, args_t&&... args)
	{
		return static_cast<node_t*>(add_child(std::make_unique<node_t>(
				std::forward<args_t>(args)...,
				forest_,
				std::move(r),
				std::move(name))));
	}

protected:
	forest_t::iterator self() const;
	// stores the access to the forest this node is contained in.
private:
	/**
	 * Takes ownership of child node and inserts into tree.
	 * \return pointer to child node
	 * \pre child != nullptr
	 */
	tree_base_node* add_child(std::unique_ptr<tree_base_node> child);

};

class root_node : public owning_base_node
{
public:
	root_node(forest_t* f, std::shared_ptr<parallel_region> r, std::string n);
	graph::connection_graph& graph() { return graph_; }
private:
	graph::connection_graph graph_;
};

/**
 * \brief Root node for building node trees.
 *
 * Has ownership of the forest and thus serves
 * as the root node for all other nodes in the forest.
 */
class forest_owner
{
public:
	typedef adobe::forest<std::unique_ptr<tree_base_node>> forest_t;

	forest_owner(std::string n, std::shared_ptr<parallel_region> r);

	owning_base_node& nodes() { return *tree_root; }
	graph::connection_graph& get_graph() { return tree_root->graph(); }

private:
	std::unique_ptr<forest_t> forest_;
	/// non_owning access to first node in tree, ownership is in forest.
	root_node* tree_root;
};

/**
 * \brief Erases node and recursively erases all children.
 *
 * \param forest, forest to delete node from.
 * \position iterator of forest pointing to node.
 * \pre position must be in forest.
 * \returns todo
 *
 * invalidates iterators pointing to deleted node.
 */
inline tree_base_node::forest_t::iterator
erase_with_subtree(
		tree_base_node::forest_t& forest,
		tree_base_node::forest_t::iterator position)
{
	return forest.erase(
			adobe::child_begin(position).base(),
			adobe::child_end(position).base());
}

/**
 * \brief Returns the full name of a node.
 *
 * The full name consists of the chained name of the nodes parent, grandparent etc.
 * and the name of the node itself.
 * The names are separated by a separation token.
 */
std::string full_name(tree_base_node::forest_t& forest,
                      const tree_base_node* node);

} // namespace fc

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


<commit_before>// RUN: %clang_cc1 -std=c++1y -triple i686-pc-windows-msvc -emit-llvm %s -o - | tee /tmp/__.bak | FileCheck %s
//CHECK: %[[A_LAMBDA:.*]] = type { %struct.A }
//CHECK: %[[B_LAMBDA:.*]] = type { %struct.B* }
struct A {
  double a = 111;
  auto foo() { return [*this] { return a; }; }
};

namespace ns1 {
int X = A{}.foo()();
} //end ns1

//CHECK: @"\01?foo@A@@QAE?A?<auto>@@XZ"(%struct.A* %this, %class.anon* noalias sret %[[A_LAMBDA_RETVAL:.*]])
// get the first object with the closure type, which is of type 'struct.A'
//CHECK: %[[I0:.+]] = getelementptr inbounds %[[A_LAMBDA]], %[[A_LAMBDA]]* %[[A_LAMBDA_RETVAL]], i32 0, i32 0
//CHECK: %[[I1:.+]] = bitcast %struct.A* %[[I0]] to i8*
//CHECK: %[[I2:.+]] = bitcast %struct.A* %this1 to i8*
// copy the contents ...
//CHECK: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %[[I1]], i8* %[[I2]], i32 8, i32 8, i1 false)

struct B {
  double b = 222;
  auto bar() { return [this] { return b; }; };
};

namespace ns2 {
int X = B{}.bar()();
}
//CHECK: @"\01?bar@B@@QAE?A?<auto>@@XZ"(%struct.B* %this, %class.anon.0* noalias sret %agg.result)
//CHECK: %[[I20:.+]] = getelementptr inbounds %class.anon.0, %class.anon.0* %agg.result, i32 0, i32 0
//CHECK: store %struct.B* %this1, %struct.B** %[[I20]], align 4
<commit_msg>Remove debug output. Sorry for the noise.<commit_after>// RUN: %clang_cc1 -std=c++1y -triple i686-pc-windows-msvc -emit-llvm %s -o - | FileCheck %s
//CHECK: %[[A_LAMBDA:.*]] = type { %struct.A }
//CHECK: %[[B_LAMBDA:.*]] = type { %struct.B* }
struct A {
  double a = 111;
  auto foo() { return [*this] { return a; }; }
};

namespace ns1 {
int X = A{}.foo()();
} //end ns1

//CHECK: @"\01?foo@A@@QAE?A?<auto>@@XZ"(%struct.A* %this, %class.anon* noalias sret %[[A_LAMBDA_RETVAL:.*]])
// get the first object with the closure type, which is of type 'struct.A'
//CHECK: %[[I0:.+]] = getelementptr inbounds %[[A_LAMBDA]], %[[A_LAMBDA]]* %[[A_LAMBDA_RETVAL]], i32 0, i32 0
//CHECK: %[[I1:.+]] = bitcast %struct.A* %[[I0]] to i8*
//CHECK: %[[I2:.+]] = bitcast %struct.A* %this1 to i8*
// copy the contents ...
//CHECK: call void @llvm.memcpy.p0i8.p0i8.i32(i8* %[[I1]], i8* %[[I2]], i32 8, i32 8, i1 false)

struct B {
  double b = 222;
  auto bar() { return [this] { return b; }; };
};

namespace ns2 {
int X = B{}.bar()();
}
//CHECK: @"\01?bar@B@@QAE?A?<auto>@@XZ"(%struct.B* %this, %class.anon.0* noalias sret %agg.result)
//CHECK: %[[I20:.+]] = getelementptr inbounds %class.anon.0, %class.anon.0* %agg.result, i32 0, i32 0
//CHECK: store %struct.B* %this1, %struct.B** %[[I20]], align 4
<|endoftext|>


<commit_before>#include "StarControl.h"
#include "ObjectParticles.h"
#include "Engine.h"
#include "Game.h"
#include "Object.h"
#include "Player.h"
#include "Common.h"
#include "App.h"

CStarControl::CStarControl(void)
{
	m_pStarNormal = NULL;
	m_pStarClick = NULL;
	m_fViewDistance = 0.5f;
	m_nClickState = 0;
	m_fClickScale = 1.0f;
	Init();
}<commit_msg>~CstartControl()<commit_after>#include "StarControl.h"
#include "ObjectParticles.h"
#include "Engine.h"
#include "Game.h"
#include "Object.h"
#include "Player.h"
#include "Common.h"
#include "App.h"

CStarControl::CStarControl(void)
{
	m_pStarNormal = NULL;
	m_pStarClick = NULL;
	m_fViewDistance = 0.5f;
	m_nClickState = 0;
	m_fClickScale = 1.0f;
	Init();
}

CStarControl::~CStarControl(void)
{
	g_Engine.pGame->RemoveNode(m_pStarNormal);
	g_Engine.pGame->RemoveNode(m_pStarClick);
}<|endoftext|>


<commit_before>#include "stdafx.h"
#include <locale>
#include <map>
#include "../../data-processing.h"

namespace {     // anonymous namespace

using cdmh::data_processing::string_view;

// http://armandbrahaj.blog.al/2009/04/14/list-of-english-stop-words/
char const * const english_stopwords[] = {
    "a", "about", "above", "above", "across", "after", "afterwards", "again", "against",
    "all", "almost", "alone", "along", "already", "also","although","always","am","among",
    "amongst", "amoungst", "amount",  "an", "and", "another", "any","anyhow","anyone",
    "anything","anyway", "anywhere", "are", "around", "as",  "at", "back","be","became",
    "because","become","becomes", "becoming", "been", "before", "beforehand", "behind",
    "being", "below", "beside", "besides", "between", "beyond", "bill", "both", "bottom",
    "but", "by", "call", "can", "cannot", "cant", "co", "con", "could", "couldnt", "cry",
    "de", "describe", "detail", "do", "done", "down", "due", "during", "each", "eg", "eight",
    "either", "eleven","else", "elsewhere", "empty", "enough", "etc", "even", "ever", "every",
    "everyone", "everything", "everywhere", "except", "few", "fifteen", "fify", "fill",
    "find", "fire", "first", "five", "for", "former", "formerly", "forty", "found", "four",
    "from", "front", "full", "further", "get", "give", "go", "had", "has", "hasnt", "have",
    "he", "hence", "her", "here", "hereafter", "hereby", "herein", "hereupon", "hers", "herself",
    "him", "himself", "his", "how", "however", "hundred", "i", "ie", "if", "in", "inc", "indeed",
    "interest", "into", "is", "it", "its", "itself", "keep", "last", "latter", "latterly",
    "least", "less", "ltd", "made", "many", "may", "me", "meanwhile", "might", "mill", "mine",
    "more", "moreover", "most", "mostly", "move", "much", "must", "my", "myself", "name",
    "namely", "neither", "never", "nevertheless", "next", "nine", "no", "nobody", "none",
    "noone", "nor", "not", "nothing", "now", "nowhere", "of", "off", "often", "on", "once",
    "one", "only", "onto", "or", "other", "others", "otherwise", "our", "ours", "ourselves",
    "out", "over", "own","part", "per", "perhaps", "please", "put", "rather", "re", "same",
    "see", "seem", "seemed", "seeming", "seems", "serious", "several", "she", "should",
    "show", "side", "since", "sincere", "six", "sixty", "so", "some", "somehow", "someone",
    "something", "sometime", "sometimes", "somewhere", "still", "such", "system", "take",
    "ten", "than", "that", "the", "their", "them", "themselves", "then", "thence", "there",
    "thereafter", "thereby", "therefore", "therein", "thereupon", "these", "they", "thickv",
    "thin", "third", "this", "those", "though", "three", "through", "throughout", "thru",
    "thus", "to", "together", "too", "top", "toward", "towards", "twelve", "twenty", "two",
    "un", "under", "until", "up", "upon", "us", "very", "via", "was", "we", "well", "were",
    "what", "whatever", "when", "whence", "whenever", "where", "whereafter", "whereas",
    "whereby", "wherein", "whereupon", "wherever", "whether", "which", "while", "whither",
    "who", "whoever", "whole", "whom", "whose", "why", "will", "with", "within", "without",
    "would", "yet", "you", "your", "yours", "yourself", "yourselves" };

bool const is_stop_word(string_view const &word)
{
    auto const stopwords_end = english_stopwords + (sizeof(english_stopwords) / sizeof(english_stopwords[0]));
    // stop word must be in ascending order
    assert(
        std::is_sorted(
            english_stopwords,
            stopwords_end,
            [](char const *first, char const *second) {
                return strcmp(first, second) < 0;
            }));

    return std::binary_search(english_stopwords, stopwords_end, word);
}

template<typename It>
inline
It find_word_begin(It &it,It ite)
{
    while (it != ite  &&  (*it<'a'  ||  *it>'z')  &&  (*it<'A'  ||  *it>'Z')  &&  (*it != '_'))
        ++it;

    return it;
}

string_view next_word(char const *&it, char const *ite, bool ignore_stopwords=true)
{
    auto begin = find_word_begin(it,ite);
    it = std::find_if(it, ite, [](char ch) { return (ch < '0'  ||  ch > '9')  &&  (ch < 'a'  ||  ch > 'z')  &&  (ch < 'A'  ||  ch > 'Z')  && ch != '-'; });
    auto word = string_view(begin, it);
    return (ignore_stopwords  &&  is_stop_word(word))?
        next_word(it, ite)
      : word;
}

template<typename Out>
void calculate_hashes(string_view const &string, Out ito, bool ignore_stopwords=true)
{
    std::locale loc;
    std::collate<char> const &col = std::use_facet<std::collate<char>>(loc);
    auto it  = string.begin();
    auto ite = string.end();
    while (it != ite)
    {
        string_view const word = next_word(it, ite, ignore_stopwords);
        if (word.length() > 0)
            *ito++ = col.hash(word.begin(), word.end());
    }
}

template<typename It>
inline
typename std::iterator_traits<It>::value_type const
sum(It begin, It end)
{
    using type = typename std::iterator_traits<It>::value_type;
    return std::accumulate(
        begin, end, type(),
        [](type sum, type const &value) {
            return sum + value;
        });
}

template<typename It>
double const mean(It begin, It end)
{
    using type = typename std::iterator_traits<It>::value_type;

    auto const count = std::distance(begin, end);
    // assert that the cast is safe
    assert(count <= std::numeric_limits<type>::max());
    return sum(begin, end) / type(count);
}

template<typename Words>
void count_words(string_view const &string, Words &words, bool ignore_stopwords)
{
    auto it  = string.begin();
    auto ite = string.end();
    while (it != ite)
    {
        string_view const word = next_word(it, ite, ignore_stopwords);
        if (word.length() > 0)
            words[word]++;
    }
}

void create_word_list_and_map(
    cdmh::data_processing::dataset const &ds,
    int                                   column,
    std::vector<string_view>             &list,
    std::map<string_view, int>           &indices,
    bool                                  ignore_stopwords=true)
{
    typedef std::map<string_view, unsigned> words_t;
    words_t word_map;

    // count up the words and
    for (size_t loop=0; loop<ds.rows(); ++loop)
        count_words(ds[loop][column].get<string_view>(), word_map, ignore_stopwords);

    // add the words to the list
    list.reserve(word_map.size());
    std::transform(
        word_map.begin(),
        word_map.end(),
        std::back_inserter(list),
        [](words_t::value_type const &word_and_count) {
            return word_and_count.first;
        });

    // map the words to the index into the list
    for (int loop=0; loop<list.size(); ++loop)
        indices[list[loop]] = loop;
}

}               // anonymous namespace

#include "naive-bayes-classifier/src/BayesianClassifier.h"
#include "naive-bayes-classifier/src/ActionClassifier.h"
#include "naive-bayes-classifier/src/Domain.h"

void classify(BayesianClassifier &classifier, string_view const &title)
{
    classifier.calculateOutput({});
}

int main(int argc, char const *argv[])
{
#if defined(_MSC_VER)  &&  defined(_DEBUG)
    _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
#endif

    char const *filename = "\\test-data\\keyword-extraction\\train.csv";
    cdmh::memory_mapped_file<char> const mmf(filename);
    if (!mmf.is_open())
    {
        std::cerr << "Failed to open file: " << filename << std::endl;
        return 1;
    }

    std::cout << "Loading file ...";
    cdmh::data_processing::dataset ds;
#ifdef NDEBUG
    size_t num_rows = 0;
#else
    size_t num_rows = 5;
#endif
    ds.attach(mmf.get(), mmf.get() + mmf.size(), num_rows);

    std::cout << "\n";
    ds.write_column_info(std::cout);
    std::cout << "\n";

    // id, title, body, tags

    std::vector<string_view>   title_words;
    std::map<string_view, int> title_word_indices;
    create_word_list_and_map(ds, 1, title_words, title_word_indices);

    std::vector<string_view>   tag_words;
    std::map<string_view, int> tag_word_indices;
    create_word_list_and_map(ds, 3, tag_words, tag_word_indices);

    /*
     inspired by http://www.inf.ed.ac.uk/teaching/courses/inf2b/learnnotes/inf2b-learn-note07-2up.pdf
    */

    // create domains:
    //     title_words.size() input domains
    //     tag_words.size() output domains
	std::vector<Domain> domains;
    for (size_t d=0; d<title_words.size() + tag_words.size(); ++d)
	    domains.emplace_back(0.0f, 1.0f, 2);  // min, max, number of values

    // train a bayesian classifier
    BayesianClassifier classifier(domains);
    std::vector<float> training;
    training.resize(title_words.size() + tag_words.size(), 0.0);
    for (size_t loop=0; loop<ds.rows(); ++loop)
    {
        // reset the vector to contain zeros
        for (float &value : training)
            value = 0.0;

        auto string = ds[loop][1].get<string_view>();
        auto it  = string.begin();
        auto ite = string.end();
        while (it != ite)
        {
            string_view const word = next_word(it, ite);
            if (word.length() > 0)
            {
                auto index = title_word_indices[word];
                training[index] = 1.0;                                          // !!!binary title does/doesn't contain tag. Count might work better?
            }
        }
        std::cout << "\n\n" << string;

        string = ds[loop][3].get<string_view>();
        it  = string.begin();
        ite = string.end();
        while (it != ite)
        {
            string_view const word = next_word(it, ite);
            if (word.length() > 0)
                training[title_words.size() + tag_word_indices[word]] = 1.0;    // !!!binary title does/doesn't contain tag. Count might work better?
        }

        std::cout << "\n";
        for (float &value : training)
            std::cout << (int)value;

        std::cout << "\n";
        for (size_t loop=0; loop<training.size(); ++loop)
        {
            if (training[loop])
            {
                if (loop < title_words.size())
                    std::cout << title_words[loop] << " ";
                else
                    std::cout << "\n*** " << tag_words[loop - title_words.size()] << " ";
            }
        }
        std::cout << "\n\n";
        classifier.addRawTrainingData(training);
    }

    classify(classifier, "Getting rid of site-specific hotkeys");
    classify(classifier, "Remove old vCenter servers from VMWare vSphere Client login window");
	return 0;
}
<commit_msg>fixed infinite loop in splitting words<commit_after>#include "stdafx.h"
#include <locale>
#include <map>
#include "../../data-processing.h"

namespace {     // anonymous namespace

using cdmh::data_processing::string_view;

// http://armandbrahaj.blog.al/2009/04/14/list-of-english-stop-words/
char const * const english_stopwords[] = {
    "a", "about", "above", "above", "across", "after", "afterwards", "again", "against",
    "all", "almost", "alone", "along", "already", "also","although","always","am","among",
    "amongst", "amoungst", "amount",  "an", "and", "another", "any","anyhow","anyone",
    "anything","anyway", "anywhere", "are", "around", "as",  "at", "back","be","became",
    "because","become","becomes", "becoming", "been", "before", "beforehand", "behind",
    "being", "below", "beside", "besides", "between", "beyond", "bill", "both", "bottom",
    "but", "by", "call", "can", "cannot", "cant", "co", "con", "could", "couldnt", "cry",
    "de", "describe", "detail", "do", "done", "down", "due", "during", "each", "eg", "eight",
    "either", "eleven","else", "elsewhere", "empty", "enough", "etc", "even", "ever", "every",
    "everyone", "everything", "everywhere", "except", "few", "fifteen", "fify", "fill",
    "find", "fire", "first", "five", "for", "former", "formerly", "forty", "found", "four",
    "from", "front", "full", "further", "get", "give", "go", "had", "has", "hasnt", "have",
    "he", "hence", "her", "here", "hereafter", "hereby", "herein", "hereupon", "hers", "herself",
    "him", "himself", "his", "how", "however", "hundred", "i", "ie", "if", "in", "inc", "indeed",
    "interest", "into", "is", "it", "its", "itself", "keep", "last", "latter", "latterly",
    "least", "less", "ltd", "made", "many", "may", "me", "meanwhile", "might", "mill", "mine",
    "more", "moreover", "most", "mostly", "move", "much", "must", "my", "myself", "name",
    "namely", "neither", "never", "nevertheless", "next", "nine", "no", "nobody", "none",
    "noone", "nor", "not", "nothing", "now", "nowhere", "of", "off", "often", "on", "once",
    "one", "only", "onto", "or", "other", "others", "otherwise", "our", "ours", "ourselves",
    "out", "over", "own","part", "per", "perhaps", "please", "put", "rather", "re", "same",
    "see", "seem", "seemed", "seeming", "seems", "serious", "several", "she", "should",
    "show", "side", "since", "sincere", "six", "sixty", "so", "some", "somehow", "someone",
    "something", "sometime", "sometimes", "somewhere", "still", "such", "system", "take",
    "ten", "than", "that", "the", "their", "them", "themselves", "then", "thence", "there",
    "thereafter", "thereby", "therefore", "therein", "thereupon", "these", "they", "thickv",
    "thin", "third", "this", "those", "though", "three", "through", "throughout", "thru",
    "thus", "to", "together", "too", "top", "toward", "towards", "twelve", "twenty", "two",
    "un", "under", "until", "up", "upon", "us", "very", "via", "was", "we", "well", "were",
    "what", "whatever", "when", "whence", "whenever", "where", "whereafter", "whereas",
    "whereby", "wherein", "whereupon", "wherever", "whether", "which", "while", "whither",
    "who", "whoever", "whole", "whom", "whose", "why", "will", "with", "within", "without",
    "would", "yet", "you", "your", "yours", "yourself", "yourselves" };

bool const is_stop_word(string_view const &word)
{
    auto const stopwords_end = english_stopwords + (sizeof(english_stopwords) / sizeof(english_stopwords[0]));
    // stop word must be in ascending order
    assert(
        std::is_sorted(
            english_stopwords,
            stopwords_end,
            [](char const *first, char const *second) {
                return strcmp(first, second) < 0;
            }));

    return std::binary_search(english_stopwords, stopwords_end, word);
}

bool const is_word_char(char ch)
{
    return (ch >= '0'  &&  ch <= '9')
       ||  (ch >= 'a'  &&  ch <= 'z')
       ||  (ch >= 'A'  &&  ch <= 'Z')
       ||   ch == '-'  ||  ch == '_';
}

template<typename It>
inline
It find_word_begin(It &it,It ite)
{
    while (it != ite  &&  !is_word_char(*it))
        ++it;
    return it;
}

string_view next_word(char const *&it, char const *ite, bool ignore_stopwords=true)
{
    auto begin = find_word_begin(it,ite);
    it = std::find_if(it, ite, [](char ch) { return !is_word_char(ch); });
    auto word = string_view(begin, it);
    return (ignore_stopwords  &&  is_stop_word(word))?
        next_word(it, ite)
      : word;
}

template<typename Out>
void calculate_hashes(string_view const &string, Out ito, bool ignore_stopwords=true)
{
    std::locale loc;
    std::collate<char> const &col = std::use_facet<std::collate<char>>(loc);
    auto it  = string.begin();
    auto ite = string.end();
    while (it != ite)
    {
        string_view const word = next_word(it, ite, ignore_stopwords);
        if (word.length() > 0)
            *ito++ = col.hash(word.begin(), word.end());
    }
}

template<typename It>
inline
typename std::iterator_traits<It>::value_type const
sum(It begin, It end)
{
    using type = typename std::iterator_traits<It>::value_type;
    return std::accumulate(
        begin, end, type(),
        [](type sum, type const &value) {
            return sum + value;
        });
}

template<typename It>
double const mean(It begin, It end)
{
    using type = typename std::iterator_traits<It>::value_type;

    auto const count = std::distance(begin, end);
    // assert that the cast is safe
    assert(count <= std::numeric_limits<type>::max());
    return sum(begin, end) / type(count);
}

template<typename Words>
void count_words(string_view const &string, Words &words, bool ignore_stopwords)
{
    auto it  = string.begin();
    auto ite = string.end();
    while (it != ite)
    {
        string_view const word = next_word(it, ite, ignore_stopwords);
        if (word.length() > 0)
            words[word]++;
    }
}

void create_word_list_and_map(
    cdmh::data_processing::dataset const &ds,
    int                                   column,
    std::vector<string_view>             &list,
    std::map<string_view, int>           &indices,
    bool                                  ignore_stopwords=true)
{
    typedef std::map<string_view, unsigned> words_t;
    words_t word_map;

    // count up the words and
    for (size_t loop=0; loop<ds.rows(); ++loop)
        count_words(ds[loop][column].get<string_view>(), word_map, ignore_stopwords);

    // add the words to the list
    list.reserve(word_map.size());
    std::transform(
        word_map.begin(),
        word_map.end(),
        std::back_inserter(list),
        [](words_t::value_type const &word_and_count) {
            return word_and_count.first;
        });

    // map the words to the index into the list
    for (int loop=0; loop<list.size(); ++loop)
        indices[list[loop]] = loop;
}

}               // anonymous namespace

#include "naive-bayes-classifier/src/BayesianClassifier.h"
#include "naive-bayes-classifier/src/ActionClassifier.h"
#include "naive-bayes-classifier/src/Domain.h"

void classify(BayesianClassifier &classifier, string_view const &title)
{
    classifier.calculateOutput({});
}

int main(int argc, char const *argv[])
{
#if defined(_MSC_VER)  &&  defined(_DEBUG)
    _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
#endif

    char const *filename = "\\test-data\\keyword-extraction\\train.csv";
    cdmh::memory_mapped_file<char> const mmf(filename);
    if (!mmf.is_open())
    {
        std::cerr << "Failed to open file: " << filename << std::endl;
        return 1;
    }

    std::cout << "Loading file ...";
    cdmh::data_processing::dataset ds;
#ifdef NDEBUG
    size_t num_rows = 0;
#else
    size_t num_rows = 5;
#endif
    ds.attach(mmf.get(), mmf.get() + mmf.size(), num_rows);

    std::cout << "\n";
    ds.write_column_info(std::cout);
    std::cout << "\n";

    // id, title, body, tags

    std::vector<string_view>   title_words;
    std::map<string_view, int> title_word_indices;
    create_word_list_and_map(ds, 1, title_words, title_word_indices);

    std::vector<string_view>   tag_words;
    std::map<string_view, int> tag_word_indices;
    create_word_list_and_map(ds, 3, tag_words, tag_word_indices);

    /*
     inspired by http://www.inf.ed.ac.uk/teaching/courses/inf2b/learnnotes/inf2b-learn-note07-2up.pdf
    */

    // create domains:
    //     title_words.size() input domains
    //     tag_words.size() output domains
	std::vector<Domain> domains;
    for (size_t d=0; d<title_words.size() + tag_words.size(); ++d)
	    domains.emplace_back(0.0f, 1.0f, 2);  // min, max, number of values

    // train a bayesian classifier
    BayesianClassifier classifier(domains);
    std::vector<float> training;
    training.resize(title_words.size() + tag_words.size(), 0.0);
    for (size_t loop=0; loop<ds.rows(); ++loop)
    {
        // reset the vector to contain zeros
        for (float &value : training)
            value = 0.0;

        auto string = ds[loop][1].get<string_view>();
        auto it  = string.begin();
        auto ite = string.end();
        while (it != ite)
        {
            string_view const word = next_word(it, ite);
            if (word.length() > 0)
            {
                auto index = title_word_indices[word];
                training[index] = 1.0;                                          // !!!binary title does/doesn't contain tag. Count might work better?
            }
        }
        std::cout << "\n\n" << string;

        string = ds[loop][3].get<string_view>();
        it  = string.begin();
        ite = string.end();
        while (it != ite)
        {
            string_view const word = next_word(it, ite);
            if (word.length() > 0)
                training[title_words.size() + tag_word_indices[word]] = 1.0;    // !!!binary title does/doesn't contain tag. Count might work better?
        }

#ifndef NDEBUG
        std::cout << "\n";
        for (float &value : training)
            std::cout << (int)value;
#endif

        std::cout << "\n";
        for (size_t loop=0; loop<training.size(); ++loop)
        {
            if (training[loop])
            {
                if (loop < title_words.size())
                    std::cout << title_words[loop] << " ";
                else
                    std::cout << "\n*** " << tag_words[loop - title_words.size()] << " ";
            }
        }
        std::cout << "\n\n";
        classifier.addRawTrainingData(training);
    }

    classify(classifier, "Getting rid of site-specific hotkeys");
    classify(classifier, "Remove old vCenter servers from VMWare vSphere Client login window");
	return 0;
}
<|endoftext|>