<commit_before>
#ifndef __BUFFER_CACHE_WRITEBACK_TCC__
#define __BUFFER_CACHE_WRITEBACK_TCC__

template <class config_t>
writeback_tmpl_t<config_t>::writeback_tmpl_t(
        cache_t *cache,
        bool wait_for_flush,
        unsigned int flush_timer_ms,
        unsigned int flush_threshold)
    : flush_timer(NULL),
      wait_for_flush(wait_for_flush),
      flush_timer_ms(flush_timer_ms),
      flush_threshold(flush_threshold),
      cache(cache),
      num_txns(0),
      start_next_sync_immediately(false),
      shutdown_callback(NULL),
      in_shutdown_sync(false),
      transaction_backdoor(false),
      state(state_none),
      transaction(NULL) {
}

template <class config_t>
writeback_tmpl_t<config_t>::~writeback_tmpl_t() {
    gdelete(flush_lock);
}

template <class config_t>
void writeback_tmpl_t<config_t>::start() {
    flush_lock =
        gnew<rwi_lock_t>(&get_cpu_context()->event_queue->message_hub,
                         get_cpu_context()->event_queue->queue_id);
}

template <class config_t>
void writeback_tmpl_t<config_t>::shutdown(sync_callback_t *callback) {
    assert(shutdown_callback == NULL);
    shutdown_callback = callback;
    if (!num_txns) // If num_txns, commit() will do this
        sync(callback);
}

template <class config_t>
void writeback_tmpl_t<config_t>::sync(sync_callback_t *callback) {

    if (callback) sync_callbacks.push_back(callback);

    // TODO: If state == state_locking, we could probably still join the current writeback rather
    // than waiting for the next one.

    if (state == state_none) {
        /* Start the writeback process immediately */
        writeback(NULL);
    } else {
        /* There is a writeback currently in progress, but sync() has been called, so there is more
        data that needs to be flushed that didn't become part of the current sync. So we start
        another sync right after this one. */
        start_next_sync_immediately = true;
    }
}

template <class config_t>
bool writeback_tmpl_t<config_t>::begin_transaction(transaction_t *txn) {

    assert(txn->get_access() == rwi_read || txn->get_access() == rwi_write);

    // TODO(NNW): If there's ever any asynchrony between socket reads and
    // begin_transaction, we'll need a better check here.
    assert(shutdown_callback == NULL || transaction_backdoor);

    num_txns++;

    if (txn->get_access() == rwi_read) return true;
    bool locked = flush_lock->lock(rwi_read, txn);
    return locked;
}

template <class config_t>
bool writeback_tmpl_t<config_t>::commit(transaction_t *txn) {

    num_txns --;

    if (txn->get_access() == rwi_write) {
        flush_lock -> unlock();
    }

    if (num_txns == 0 && shutdown_callback != NULL && !in_shutdown_sync) {
        // All txns shut down, start final sync.

        // So we don't do this again when the final sync's transaction commits
        in_shutdown_sync = true;

        sync(shutdown_callback);
    }

    if (txn->get_access() == rwi_write) {
        /* At the end of every write transaction, check if the number of dirty blocks exceeds the
        threshold to force writeback to start. */
        if (num_dirty_blocks() > flush_threshold) {
            sync(NULL);
        }
        /* Otherwise, start the flush timer so that the modified data doesn't sit in memory for too
        long without being written to disk. */
        else if (!flush_timer && flush_timer_ms != NEVER_FLUSH) {
            flush_timer = get_cpu_context()->event_queue->
                fire_timer_once(flush_timer_ms, flush_timer_callback, this);
        }
    }

    if (txn->get_access() == rwi_write && wait_for_flush) {
        /* Push the callback in manually rather than calling sync(); we will patiently wait for the
        next sync to come naturally. */
        sync_callbacks.push_back(txn);
        return false;
    } else {
        return true;
    }
}

template <class config_t>
void writeback_tmpl_t<config_t>::aio_complete(buf_t *buf, bool written) {
    if (written)
        writeback(buf);
}

template <class config_t>
unsigned int writeback_tmpl_t<config_t>::num_dirty_blocks() {
    return dirty_bufs.size();
}

#ifndef NDEBUG
template <class config_t>
void writeback_tmpl_t<config_t>::deadlock_debug() {
    printf("\n----- Writeback -----\n");
    const char *st_name;
    switch(state) {
        case state_none: st_name = "state_none"; break;
        case state_locking: st_name = "state_locking"; break;
        case state_locked: st_name = "state_locked"; break;
        case state_write_bufs: st_name = "state_write_bufs"; break;
        default: st_name = "<invalid state>"; break;
    }
    printf("state = %s\n", st_name);
}
#endif

template <class config_t>
void writeback_tmpl_t<config_t>::local_buf_t::set_dirty(buf_t *super) {
    // 'super' is actually 'this', but as a buf_t* instead of a local_buf_t*
    if(!dirty) {
        // Mark block as dirty if it hasn't been already
        dirty = true;
        writeback->dirty_bufs.push_back(super);
    }
}

template <class config_t>
void writeback_tmpl_t<config_t>::flush_timer_callback(void *ctx) {
    writeback_tmpl_t *self = static_cast<writeback_tmpl_t *>(ctx);
    self->flush_timer = NULL;

    self->sync(NULL);
}

template <class config_t>
void writeback_tmpl_t<config_t>::on_lock_available() {
    assert(state == state_locking);
    if (state == state_locking) {
        state = state_locked;
        writeback(NULL);
    }
}

template <class config_t>
void writeback_tmpl_t<config_t>::writeback(buf_t *buf) {
    //printf("Writeback being called, state %d\n", state);

    if (state == state_none) {

        assert(buf == NULL);

        /* Start a read transaction so we can request bufs. */
        assert(transaction == NULL);
        if (shutdown_callback) // Backdoor around "no new transactions" assert.
            transaction_backdoor = true;
        transaction = cache->begin_transaction(rwi_read, NULL);
        transaction_backdoor = false;
        assert(transaction != NULL); // Read txns always start immediately.

        /* Request exclusive flush_lock, forcing all write txns to complete. */
        state = state_locking;
        bool locked = flush_lock->lock(rwi_write, this);
        if (locked) {
            state = state_locked;
        }
    }
    if (state == state_locked) {
        assert(buf == NULL);
        assert(flush_bufs.empty());
        assert(current_sync_callbacks.empty());

        current_sync_callbacks.append_and_clear(&sync_callbacks);

        /* Request read locks on all of the blocks we need to flush. */
        // TODO: optimize away dynamic allocation
        typename serializer_t::write *writes =
            (typename serializer_t::write*)calloc(dirty_bufs.size(), sizeof *writes);
        int i;
        typename intrusive_list_t<buf_t>::iterator it;
        for (it = dirty_bufs.begin(), i = 0; it != dirty_bufs.end(); it++, i++) {
            buf_t *_buf = &*it;

            // Acquire the blocks
            buf_t *buf = transaction->acquire(_buf->get_block_id(), rwi_read, NULL);
            assert(buf);         // Acquire must succeed since we hold the flush_lock.
            assert(buf == _buf); // Acquire should return the same buf we stored earlier.

            // Fill the serializer structure
            writes[i].block_id = buf->get_block_id();
            writes[i].buf = buf->ptr();
            writes[i].callback = buf;

#ifndef NDEBUG
            buf->active_callback_count ++;
#endif
        }
        flush_bufs.append_and_clear(&dirty_bufs);

        flush_lock->unlock(); // Write transactions can now proceed again.

        /* Start writing all the dirty bufs down, as a transaction. */
        // TODO(NNW): Now that the serializer/aio-system breaks writes up into
        // chunks, we may want to worry about submitting more heavily contended
        // bufs earlier in the process so more write FSMs can proceed sooner.
        if (flush_bufs.size())
            cache->do_write(get_cpu_context()->event_queue, writes,
                            flush_bufs.size());
        free(writes);
        state = state_write_bufs;
    }
    if (state == state_write_bufs) {
        if (buf) {
            flush_bufs.remove(buf);
            buf->set_clean();
            buf->release();
        }
        if (flush_bufs.empty()) {
            /* We are done writing all of the buffers */

            bool committed __attribute__((unused)) = transaction->commit(NULL);
            assert(committed); // Read-only transactions commit immediately.
            transaction = NULL;

            while (!current_sync_callbacks.empty()) {
                sync_callback_t *cb = current_sync_callbacks.head();
                current_sync_callbacks.remove(cb);
                cb->on_sync();
            }

            state = state_none;

            if (start_next_sync_immediately) {
                start_next_sync_immediately = false;
                writeback(NULL);
            }
        }
    }
}

#endif  // __BUFFER_CACHE_WRITEBACK_TCC__
<commit_msg>Put back the cancellation of the flush timer and added a comment to explain why it is necessary.<commit_after>
#ifndef __BUFFER_CACHE_WRITEBACK_TCC__
#define __BUFFER_CACHE_WRITEBACK_TCC__

template <class config_t>
writeback_tmpl_t<config_t>::writeback_tmpl_t(
        cache_t *cache,
        bool wait_for_flush,
        unsigned int flush_timer_ms,
        unsigned int flush_threshold)
    : flush_timer(NULL),
      wait_for_flush(wait_for_flush),
      flush_timer_ms(flush_timer_ms),
      flush_threshold(flush_threshold),
      cache(cache),
      num_txns(0),
      start_next_sync_immediately(false),
      shutdown_callback(NULL),
      in_shutdown_sync(false),
      transaction_backdoor(false),
      state(state_none),
      transaction(NULL) {
}

template <class config_t>
writeback_tmpl_t<config_t>::~writeback_tmpl_t() {
    gdelete(flush_lock);
}

template <class config_t>
void writeback_tmpl_t<config_t>::start() {
    flush_lock =
        gnew<rwi_lock_t>(&get_cpu_context()->event_queue->message_hub,
                         get_cpu_context()->event_queue->queue_id);
}

template <class config_t>
void writeback_tmpl_t<config_t>::shutdown(sync_callback_t *callback) {
    assert(shutdown_callback == NULL);
    shutdown_callback = callback;
    if (!num_txns) // If num_txns, commit() will do this
        sync(callback);
}

template <class config_t>
void writeback_tmpl_t<config_t>::sync(sync_callback_t *callback) {

    if (callback) sync_callbacks.push_back(callback);

    // TODO: If state == state_locking, we could probably still join the current writeback rather
    // than waiting for the next one.

    if (state == state_none) {
        /* Start the writeback process immediately */
        writeback(NULL);
    } else {
        /* There is a writeback currently in progress, but sync() has been called, so there is more
        data that needs to be flushed that didn't become part of the current sync. So we start
        another sync right after this one. */
        start_next_sync_immediately = true;
    }
}

template <class config_t>
bool writeback_tmpl_t<config_t>::begin_transaction(transaction_t *txn) {

    assert(txn->get_access() == rwi_read || txn->get_access() == rwi_write);

    // TODO(NNW): If there's ever any asynchrony between socket reads and
    // begin_transaction, we'll need a better check here.
    assert(shutdown_callback == NULL || transaction_backdoor);

    num_txns++;

    if (txn->get_access() == rwi_read) return true;
    bool locked = flush_lock->lock(rwi_read, txn);
    return locked;
}

template <class config_t>
bool writeback_tmpl_t<config_t>::commit(transaction_t *txn) {

    num_txns --;

    if (txn->get_access() == rwi_write) {
        flush_lock -> unlock();
    }

    if (num_txns == 0 && shutdown_callback != NULL && !in_shutdown_sync) {
        // All txns shut down, start final sync.

        // So we don't do this again when the final sync's transaction commits
        in_shutdown_sync = true;

        sync(shutdown_callback);
    }

    if (txn->get_access() == rwi_write) {
        /* At the end of every write transaction, check if the number of dirty blocks exceeds the
        threshold to force writeback to start. */
        if (num_dirty_blocks() > flush_threshold) {
            sync(NULL);
        }
        /* Otherwise, start the flush timer so that the modified data doesn't sit in memory for too
        long without being written to disk. */
        else if (!flush_timer && flush_timer_ms != NEVER_FLUSH) {
            flush_timer = get_cpu_context()->event_queue->
                fire_timer_once(flush_timer_ms, flush_timer_callback, this);
        }
    }

    if (txn->get_access() == rwi_write && wait_for_flush) {
        /* Push the callback in manually rather than calling sync(); we will patiently wait for the
        next sync to come naturally. */
        sync_callbacks.push_back(txn);
        return false;
    } else {
        return true;
    }
}

template <class config_t>
void writeback_tmpl_t<config_t>::aio_complete(buf_t *buf, bool written) {
    if (written)
        writeback(buf);
}

template <class config_t>
unsigned int writeback_tmpl_t<config_t>::num_dirty_blocks() {
    return dirty_bufs.size();
}

#ifndef NDEBUG
template <class config_t>
void writeback_tmpl_t<config_t>::deadlock_debug() {
    printf("\n----- Writeback -----\n");
    const char *st_name;
    switch(state) {
        case state_none: st_name = "state_none"; break;
        case state_locking: st_name = "state_locking"; break;
        case state_locked: st_name = "state_locked"; break;
        case state_write_bufs: st_name = "state_write_bufs"; break;
        default: st_name = "<invalid state>"; break;
    }
    printf("state = %s\n", st_name);
}
#endif

template <class config_t>
void writeback_tmpl_t<config_t>::local_buf_t::set_dirty(buf_t *super) {
    // 'super' is actually 'this', but as a buf_t* instead of a local_buf_t*
    if(!dirty) {
        // Mark block as dirty if it hasn't been already
        dirty = true;
        writeback->dirty_bufs.push_back(super);
    }
}

template <class config_t>
void writeback_tmpl_t<config_t>::flush_timer_callback(void *ctx) {
    writeback_tmpl_t *self = static_cast<writeback_tmpl_t *>(ctx);
    self->flush_timer = NULL;

    self->sync(NULL);
}

template <class config_t>
void writeback_tmpl_t<config_t>::on_lock_available() {
    assert(state == state_locking);
    if (state == state_locking) {
        state = state_locked;
        writeback(NULL);
    }
}

template <class config_t>
void writeback_tmpl_t<config_t>::writeback(buf_t *buf) {
    //printf("Writeback being called, state %d\n", state);

    if (state == state_none) {

        assert(buf == NULL);

        // Cancel the flush timer because we're doing writeback now, so we don't need it to remind
        // us later. This happens only if the flush timer is running, and writeback starts for some
        // other reason before the flush timer goes off; if this writeback had been started by the
        // flush timer, then flush_timer would be NULL here, because flush_timer_callback sets it
        // to NULL.
        if (flush_timer) {
            get_cpu_context()->event_queue->cancel_timer(flush_timer);
            flush_timer = NULL;
        }

        /* Start a read transaction so we can request bufs. */
        assert(transaction == NULL);
        if (shutdown_callback) // Backdoor around "no new transactions" assert.
            transaction_backdoor = true;
        transaction = cache->begin_transaction(rwi_read, NULL);
        transaction_backdoor = false;
        assert(transaction != NULL); // Read txns always start immediately.

        /* Request exclusive flush_lock, forcing all write txns to complete. */
        state = state_locking;
        bool locked = flush_lock->lock(rwi_write, this);
        if (locked) {
            state = state_locked;
        }
    }
    if (state == state_locked) {
        assert(buf == NULL);
        assert(flush_bufs.empty());
        assert(current_sync_callbacks.empty());

        current_sync_callbacks.append_and_clear(&sync_callbacks);

        /* Request read locks on all of the blocks we need to flush. */
        // TODO: optimize away dynamic allocation
        typename serializer_t::write *writes =
            (typename serializer_t::write*)calloc(dirty_bufs.size(), sizeof *writes);
        int i;
        typename intrusive_list_t<buf_t>::iterator it;
        for (it = dirty_bufs.begin(), i = 0; it != dirty_bufs.end(); it++, i++) {
            buf_t *_buf = &*it;

            // Acquire the blocks
            buf_t *buf = transaction->acquire(_buf->get_block_id(), rwi_read, NULL);
            assert(buf);         // Acquire must succeed since we hold the flush_lock.
            assert(buf == _buf); // Acquire should return the same buf we stored earlier.

            // Fill the serializer structure
            writes[i].block_id = buf->get_block_id();
            writes[i].buf = buf->ptr();
            writes[i].callback = buf;

#ifndef NDEBUG
            buf->active_callback_count ++;
#endif
        }
        flush_bufs.append_and_clear(&dirty_bufs);

        flush_lock->unlock(); // Write transactions can now proceed again.

        /* Start writing all the dirty bufs down, as a transaction. */
        // TODO(NNW): Now that the serializer/aio-system breaks writes up into
        // chunks, we may want to worry about submitting more heavily contended
        // bufs earlier in the process so more write FSMs can proceed sooner.
        if (flush_bufs.size())
            cache->do_write(get_cpu_context()->event_queue, writes,
                            flush_bufs.size());
        free(writes);
        state = state_write_bufs;
    }
    if (state == state_write_bufs) {
        if (buf) {
            flush_bufs.remove(buf);
            buf->set_clean();
            buf->release();
        }
        if (flush_bufs.empty()) {
            /* We are done writing all of the buffers */

            bool committed __attribute__((unused)) = transaction->commit(NULL);
            assert(committed); // Read-only transactions commit immediately.
            transaction = NULL;

            while (!current_sync_callbacks.empty()) {
                sync_callback_t *cb = current_sync_callbacks.head();
                current_sync_callbacks.remove(cb);
                cb->on_sync();
            }

            state = state_none;

            if (start_next_sync_immediately) {
                start_next_sync_immediately = false;
                writeback(NULL);
            }
        }
    }
}

#endif  // __BUFFER_CACHE_WRITEBACK_TCC__
<|endoftext|>


<commit_before>// Implementation for Touch_Buttons
//
// attention, to be fast, this needs the -O3 compiler option to be set!
//
// author: ulno
// created: 2016-03-01

#include <Arduino.h>
#include "Touch_Buttons.h"

#define ulno_do_2x(exp) {exp;exp;}
#define ulno_do_5x(exp) {exp;exp;exp;exp;exp;}
#define ulno_do_10x(exp) {ulno_do_2x(ulno_do_5x(exp))}

void Touch_Buttons::init(int threshold, int debounce, int discharge_delay_ms, bool internal_pullup, bool chargedelay ) {
  // threshold = 108; // 1/2mOhm resistor + graphite + scotch tape
  // threshold = 400; // > 50000 with 1MOhm
  //threshold = 4;// internal resistor (set to 20 to see speed)
  this->default_threshold = threshold;
  this->debounce_value = debounce*2; // reasonable is 5
  this->use_internal_pullup = internal_pullup; // we use it in the simplest version to not have external resistors
  this->measure_chargedelay = chargedelay; // true should be used when using internal pullup

  _size = 0;
  initial_wait = discharge_delay_ms;
  for(int i=0; i < MAX_BUTTONS * 2; i++) {
    button_array[i] = 0;
  }
  for(int i=0; i < MAX_BUTTONS; i++) {
    debouncer[i] = 0;
  }
  debug(0,0); // no debugging by default
}

void Touch_Buttons::debug( int level, int count ) { // debug level: 0=off, 1=info, 2=all; count: <0: never, n: every n calls
  this->debug_level = level;
  this->debug_count = count;
  debug_frame = 0;
}

Touch_Buttons::Touch_Buttons(int threshold, int debounce, int discharge_delay_ms, bool internal_pullup, bool chargedelay ) {
  init( threshold, debounce, discharge_delay_ms, internal_pullup, chargedelay);
}

Touch_Buttons::Touch_Buttons() {
  init( 9, 5, 1, true, true);
}

static void pull_down( int gpio ) {
  pinMode(gpio, OUTPUT);
  digitalWrite(gpio, LOW);
}

void Touch_Buttons::pull_down_all() {
  // pull all buttons down
  for(int b=0; b<_size; b++) {
    int gpio = button_gpio[b];
    pull_down(gpio);
  }
}

/*void Touch_Buttons::set_input_all() {
  // pull all buttons down
  for(int b=0; b<_size; b++) {
    int gpio =  button_gpio[b];
    if( use_internal_pullup )
      pinMode(gpio, INPUT_PULLUP);
    else
      pinMode(gpio, INPUT);
  }
} too slow */

void Touch_Buttons::add_button(int id, int gpio_pin, int _threshold) {
  if(_size < MAX_BUTTONS ) {
    set_button_id(_size, id);
    set_button_state(_size, -1);
    button_gpio[_size] = gpio_pin;
    threshold[_size] = _threshold;
    _size ++;
    // needs to be pulled down by default to be discharged
    pull_down(gpio_pin);
  } else {
    Serial.println("Maximum numbers of buttons defined, not adding new one.\n");
  }
}

void Touch_Buttons::add_button(int id, int gpio_pin) {
  add_button(id,gpio_pin,default_threshold);
}

bool Touch_Buttons::check() {
  const int MAX_DIRECT_READS = 100;  // this is a fixed constant reflecting the up to 100 single reads in this function
  uint32_t regcache[MAX_DIRECT_READS];
  int_fast16_t timer = 0;
  bool one_pressed = false;

/*    //PIN_PULLUP_DIS(PERIPHS_IO_MUX_GPIO5_U);
  //GPIO_OUTPUT_SET(gpio, 0); // pull down
  pinMode(gpio, OUTPUT);
  digitalWrite(gpio, LOW);
  //os_delay_us(500);
  //os_delay_us(100);
  delay(1);*/
  pull_down_all();
  delay(initial_wait);

  for(int b=_size-1; b>=0; b--) {
    int gpio = button_gpio[b];

    // (GPIO_REG_READ(GPIO_IN_ADDRESS) = READ_PERI_REG(PERIPHS_GPIO_BASEADDR + GPIO_IN_ADDRESS)
    //volatile uint32_t *gpio_ports = (volatile uint32_t *)(PERIPHS_GPIO_BASEADDR + GPIO_IN_ADDRESS); slower than fixed address
    uint32_t bitmask = 1 << gpio;
    uint32_t *regcache_writer = regcache;

    //GPIO_DIS_OUTPUT(gpio);
    //PIN_PULLUP_EN(PERIPHS_IO_MUX_GPIO5_U);
    int pullup_mode =  use_internal_pullup?INPUT_PULLUP:INPUT;
    int_fast16_t threshold_left = threshold[b];
    int_fast16_t threshold_10steps = threshold_left / 10;
    if(threshold_10steps>9) threshold_10steps = 9;
    threshold_10steps ++;
    uint16_t direct_reads = threshold_10steps * 10;
    threshold_left -= direct_reads;
    // the following is extremely time critical as the recharging is pretty fast
    // read directly to be maximum fast
    switch(threshold_10steps) {
      case 1:
        pinMode(gpio, pullup_mode);
        ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS));
        break;
      case 2:
        pinMode(gpio, pullup_mode);
        ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS));
        ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS));
        break;
      case 3:
        pinMode(gpio, pullup_mode);
        ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS));
        ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS));
        ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS));
        break;
      case 4:
        pinMode(gpio, pullup_mode);
        ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS));
        ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS));
        ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS));
        ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS));
        break;
      case 5:
        pinMode(gpio, pullup_mode);
        ulno_do_5x(ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS)));
        break;
      case 6:
        pinMode(gpio, pullup_mode);
        ulno_do_5x(ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS)));
        ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS));
        break;
      case 7:
        pinMode(gpio, pullup_mode);
        ulno_do_5x(ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS)));
        ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS));
        ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS));
        break;
      case 8:
        pinMode(gpio, pullup_mode);
        ulno_do_2x(ulno_do_2x(ulno_do_2x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS))));
        break;
      case 9:
        pinMode(gpio, pullup_mode);
        ulno_do_2x(ulno_do_2x(ulno_do_2x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS))));
        ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS));
        break;
      case 10:
        pinMode(gpio, pullup_mode);
        ulno_do_10x(ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS)));
        break;
    }
    // read the potential rest a little slower
    //while (!(gpio_input_get()&(1<<gpio)) && (riseTime < threshold)) { // slow?
    //while (!(gpio_input_get()&32) && (riseTime < threshold)) { // slow?
    //while (!(*gpio_ports&32) && (timer > 0)) { // slower than fixed address
    while ((threshold_left > 0) && !(GPIO_REG_READ(GPIO_IN_ADDRESS)&bitmask)) {
        --threshold_left;
    }

    pull_down(gpio); // needs to be pulled down as early as possible to not acumulate too much charge

    threshold_left = threshold[b] - (threshold_10steps*10) - threshold_left;
    // adjust by the fast read direct accesses
    int timer2 = 0;
    for(int i=0; i<direct_reads; i++) {
      if(regcache[i]&bitmask) {
        break;
      }
      timer2++;
    }
    if( timer2 < direct_reads) timer = timer2;
    else timer += direct_reads;
    button_time[b] = timer; // save time for this button
    if (timer < threshold[b]) {
      if(measure_chargedelay) { // in this case being under the time means, no human touched the wire -> it is untouched
        decrease_debouncer(b);
      } else {
        increase_debouncer(b);
      }
    } else {
      if(measure_chargedelay) { // in this case being under the time means, a human touched the wire -> it is touched
        increase_debouncer(b);
      } else {
        decrease_debouncer(b);
      }
    }
    if(update_state(b)) one_pressed = true; // if only on epressed change to true
    /* debug */
    if(debug_level>=1 && debug_frame >= debug_count) {
      Serial.print("I");
      Serial.print(get_button_id(b));
      Serial.print(" P");
      Serial.print(gpio);
      Serial.print(" T");
      Serial.print(timer);
      Serial.print(" TT");
      Serial.print(timer2);
      Serial.print(" D");
      Serial.print(debouncer[b]);
      Serial.print("  ");
    }
  } // end of loop through all buttons
  if(debug_level>=1) {
    if(debug_frame >= debug_count) {
      debug_frame = 0;
      Serial.println();
    }
    debug_frame ++;
  }
  return one_pressed;
}

int Touch_Buttons::get_button(int id) {
  // find button id
  for(int b=_size-1; b>=0; b--) {
    if(get_button_id(b) == id)
      return get_button_state(b);
  }
  return -1;
}
<commit_msg>small bugfix with first timer<commit_after>// Implementation for Touch_Buttons
//
// attention, to be fast, this needs the -O3 compiler option to be set!
//
// author: ulno
// created: 2016-03-01

#include <Arduino.h>
#include "Touch_Buttons.h"

#define ulno_do_2x(exp) {exp;exp;}
#define ulno_do_5x(exp) {exp;exp;exp;exp;exp;}
#define ulno_do_10x(exp) {ulno_do_2x(ulno_do_5x(exp))}

void Touch_Buttons::init(int threshold, int debounce, int discharge_delay_ms, bool internal_pullup, bool chargedelay ) {
  // threshold = 108; // 1/2mOhm resistor + graphite + scotch tape
  // threshold = 400; // > 50000 with 1MOhm
  //threshold = 4;// internal resistor (set to 20 to see speed)
  this->default_threshold = threshold;
  this->debounce_value = debounce*2; // reasonable is 5
  this->use_internal_pullup = internal_pullup; // we use it in the simplest version to not have external resistors
  this->measure_chargedelay = chargedelay; // true should be used when using internal pullup

  _size = 0;
  initial_wait = discharge_delay_ms;
  for(int i=0; i < MAX_BUTTONS * 2; i++) {
    button_array[i] = 0;
  }
  for(int i=0; i < MAX_BUTTONS; i++) {
    debouncer[i] = 0;
  }
  debug(0,0); // no debugging by default
}

void Touch_Buttons::debug( int level, int count ) { // debug level: 0=off, 1=info, 2=all; count: <0: never, n: every n calls
  this->debug_level = level;
  this->debug_count = count;
  debug_frame = 0;
}

Touch_Buttons::Touch_Buttons(int threshold, int debounce, int discharge_delay_ms, bool internal_pullup, bool chargedelay ) {
  init( threshold, debounce, discharge_delay_ms, internal_pullup, chargedelay);
}

Touch_Buttons::Touch_Buttons() {
  init( 9, 5, 1, true, true);
}

static void pull_down( int gpio ) {
  pinMode(gpio, OUTPUT);
  digitalWrite(gpio, LOW);
}

void Touch_Buttons::pull_down_all() {
  // pull all buttons down
  for(int b=0; b<_size; b++) {
    int gpio = button_gpio[b];
    pull_down(gpio);
  }
}

/*void Touch_Buttons::set_input_all() {
  // pull all buttons down
  for(int b=0; b<_size; b++) {
    int gpio =  button_gpio[b];
    if( use_internal_pullup )
      pinMode(gpio, INPUT_PULLUP);
    else
      pinMode(gpio, INPUT);
  }
} too slow */

void Touch_Buttons::add_button(int id, int gpio_pin, int _threshold) {
  if(_size < MAX_BUTTONS ) {
    set_button_id(_size, id);
    set_button_state(_size, -1);
    button_gpio[_size] = gpio_pin;
    threshold[_size] = _threshold;
    _size ++;
    // needs to be pulled down by default to be discharged
    pull_down(gpio_pin);
  } else {
    Serial.println("Maximum numbers of buttons defined, not adding new one.\n");
  }
}

void Touch_Buttons::add_button(int id, int gpio_pin) {
  add_button(id,gpio_pin,default_threshold);
}

bool Touch_Buttons::check() {
  const int MAX_DIRECT_READS = 100;  // this is a fixed constant reflecting the up to 100 single reads in this function
  uint32_t regcache[MAX_DIRECT_READS];
  int_fast16_t threshold_left;
  bool one_pressed = false;

/*    //PIN_PULLUP_DIS(PERIPHS_IO_MUX_GPIO5_U);
  //GPIO_OUTPUT_SET(gpio, 0); // pull down
  pinMode(gpio, OUTPUT);
  digitalWrite(gpio, LOW);
  //os_delay_us(500);
  //os_delay_us(100);
  delay(1);*/
  pull_down_all();
  delay(initial_wait);

  for(int b=_size-1; b>=0; b--) {
    int gpio = button_gpio[b];

    // (GPIO_REG_READ(GPIO_IN_ADDRESS) = READ_PERI_REG(PERIPHS_GPIO_BASEADDR + GPIO_IN_ADDRESS)
    //volatile uint32_t *gpio_ports = (volatile uint32_t *)(PERIPHS_GPIO_BASEADDR + GPIO_IN_ADDRESS); slower than fixed address
    uint32_t bitmask = 1 << gpio;
    uint32_t *regcache_writer = regcache;

    //GPIO_DIS_OUTPUT(gpio);
    //PIN_PULLUP_EN(PERIPHS_IO_MUX_GPIO5_U);
    int pullup_mode =  use_internal_pullup?INPUT_PULLUP:INPUT;
    threshold_left = threshold[b];
    int_fast16_t threshold_10steps = (threshold_left - 1)/ 10;
    if(threshold_10steps>9) threshold_10steps = 9;
    else if(threshold_10steps<0) threshold_10steps = 0;
    threshold_10steps ++; // so 0-10: 1 | 11-20: 2 | 21-30: 3 | ... 91-100: 10
    uint16_t direct_reads = threshold_10steps * 10;
    threshold_left -= direct_reads;
    // the following is extremely time critical as the recharging is pretty fast
    // read directly to be maximum fast
    switch(threshold_10steps) {
      case 1: // 0-10 -> 10 steps
        pinMode(gpio, pullup_mode);
        ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS));
        break;
      case 2: // 11-20 -> 20 steps
        pinMode(gpio, pullup_mode);
        ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS));
        ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS));
        break;
      case 3: // 21-30 -> 30 steps
        pinMode(gpio, pullup_mode);
        ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS));
        ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS));
        ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS));
        break;
      case 4:
        pinMode(gpio, pullup_mode);
        ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS));
        ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS));
        ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS));
        ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS));
        break;
      case 5:
        pinMode(gpio, pullup_mode);
        ulno_do_5x(ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS)));
        break;
      case 6:
        pinMode(gpio, pullup_mode);
        ulno_do_5x(ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS)));
        ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS));
        break;
      case 7:
        pinMode(gpio, pullup_mode);
        ulno_do_5x(ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS)));
        ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS));
        ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS));
        break;
      case 8:
        pinMode(gpio, pullup_mode);
        ulno_do_2x(ulno_do_2x(ulno_do_2x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS))));
        break;
      case 9:
        pinMode(gpio, pullup_mode);
        ulno_do_2x(ulno_do_2x(ulno_do_2x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS))));
        ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS));
        break;
      case 10:
        pinMode(gpio, pullup_mode);
        ulno_do_10x(ulno_do_10x(*((regcache_writer) ++) = GPIO_REG_READ(GPIO_IN_ADDRESS)));
        break;
    }
    // read the potential rest a little slower
    //while (!(gpio_input_get()&(1<<gpio)) && (riseTime < threshold)) { // slow?
    //while (!(gpio_input_get()&32) && (riseTime < threshold)) { // slow?
    //while (!(*gpio_ports&32) && (timer > 0)) { // slower than fixed address
    while ((threshold_left > 0) && !(GPIO_REG_READ(GPIO_IN_ADDRESS)&bitmask)) {
        --threshold_left;
    }

    pull_down(gpio); // needs to be pulled down as early as possible to not acumulate too much charge

    threshold_left = threshold[b] - (threshold_10steps*10) - threshold_left;
    // adjust by the fast read direct accesses
    int timer2 = 0;
    for(int i=0; i<direct_reads; i++) {
      if(regcache[i]&bitmask) {
        break;
      }
      timer2++;
    }
    if( timer2 < direct_reads) threshold_left = timer2;
    else threshold_left += direct_reads;
    button_time[b] = threshold_left; // save time for this button
    if (threshold_left < threshold[b]) {
      if(measure_chargedelay) { // in this case being under the time means, no human touched the wire -> it is untouched
        decrease_debouncer(b);
      } else {
        increase_debouncer(b);
      }
    } else {
      if(measure_chargedelay) { // in this case being under the time means, a human touched the wire -> it is touched
        increase_debouncer(b);
      } else {
        decrease_debouncer(b);
      }
    }
    if(update_state(b)) one_pressed = true; // if only on epressed change to true
    /* debug */
    if(debug_level>=1 && debug_frame >= debug_count) {
      Serial.print("I");
      Serial.print(get_button_id(b));
      Serial.print(" P");
      Serial.print(gpio);
      Serial.print(" T");
      Serial.print(threshold_left);
      Serial.print(" TT");
      Serial.print(timer2);
      Serial.print(" D");
      Serial.print(debouncer[b]);
      Serial.print("  ");
    }
  } // end of loop through all buttons
  if(debug_level>=1) {
    if(debug_frame >= debug_count) {
      debug_frame = 0;
      Serial.println();
    }
    debug_frame ++;
  }
  return one_pressed;
}

int Touch_Buttons::get_button(int id) {
  // find button id
  for(int b=_size-1; b>=0; b--) {
    if(get_button_id(b) == id)
      return get_button_state(b);
  }
  return -1;
}
<|endoftext|>


<commit_before>/*******************************************************************************
 * This file is part of "Patrick's Programming Library", Version 7 (PPL7).
 * Web: http://www.pfp.de/ppl/
 *
 *******************************************************************************
 * Copyright (c) 2022, Patrick Fedick <patrick@pfp.de>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *    1. Redistributions of source code must retain the above copyright notice, this
 *       list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright notice,
 *       this list of conditions and the following disclaimer in the documentation
 *       and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER AND CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 *******************************************************************************/

#include "prolog_ppl7.h"

#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_STDARG_H
#include <stdarg.h>
#endif

#include "ppl7.h"
#include "ppl7-grafix.h"
#include "ppl7-tk.h"

namespace ppl7::tk {

RadioButton::RadioButton()
	: ppl7::tk::Label()
{
	ischecked=false;
}

RadioButton::RadioButton(int x, int y, int width, int height, const ppl7::String& text, bool checked) // @suppress("Class members should be properly initialized")
	: ppl7::tk::Label(x, y, width, height, text)
{
	ischecked=checked;
}

RadioButton::~RadioButton()
{

}

ppl7::String RadioButton::widgetType() const
{
	return ppl7::String("RadioButton");
}

bool RadioButton::checked() const
{
	return ischecked;
}

void RadioButton::setChecked(bool checked)
{
	bool laststate=ischecked;
	ischecked=checked;
	needsRedraw();
	// uncheck all other RadioButtons in Parent-Widget
	if (checked == true && this->getParent()) {
		Widget* parent=this->getParent();
		std::list<Widget*>::iterator it;
		for (it=parent->childsBegin(); it != parent->childsEnd();++it) {
			if (typeid(*it) == typeid(RadioButton) && *it != this) {
				((RadioButton*)(*it))->setChecked(false);
			}
		}
	}
	ppl7::tk::Event ev(ppl7::tk::Event::Toggled);
	ev.setWidget(this);
	if (checked != laststate) {
		toggledEvent(&ev, checked);
	}
}

void RadioButton::paint(ppl7::grafix::Drawable& draw)
{
	const ppl7::tk::WidgetStyle& style=ppl7::tk::GetWidgetStyle();
	ppl7::grafix::Drawable d=draw.getDrawable(16, 0, draw.width() - 16, draw.height());
	Label::paint(d);
	int y1=draw.height() / 2;
	draw.circle(9, y1, 7, style.frameBorderColorLight);
	draw.circle(9, y1, 6, style.frameBorderColorLight);
	if (ischecked) draw.floodFill(9, y1, this->color(), style.frameBorderColorLight);
}

void RadioButton::mouseDownEvent(ppl7::tk::MouseEvent* event)
{
	setChecked(true);
}

} //EOF namespace
<commit_msg>fixed radio button<commit_after>/*******************************************************************************
 * This file is part of "Patrick's Programming Library", Version 7 (PPL7).
 * Web: http://www.pfp.de/ppl/
 *
 *******************************************************************************
 * Copyright (c) 2022, Patrick Fedick <patrick@pfp.de>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *    1. Redistributions of source code must retain the above copyright notice, this
 *       list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright notice,
 *       this list of conditions and the following disclaimer in the documentation
 *       and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER AND CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 *******************************************************************************/

#include "prolog_ppl7.h"

#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_STDARG_H
#include <stdarg.h>
#endif

#include "ppl7.h"
#include "ppl7-grafix.h"
#include "ppl7-tk.h"

namespace ppl7::tk {

RadioButton::RadioButton()
	: ppl7::tk::Label()
{
	ischecked=false;
}

RadioButton::RadioButton(int x, int y, int width, int height, const ppl7::String& text, bool checked) // @suppress("Class members should be properly initialized")
	: ppl7::tk::Label(x, y, width, height, text)
{
	ischecked=checked;
}

RadioButton::~RadioButton()
{

}

ppl7::String RadioButton::widgetType() const
{
	return ppl7::String("RadioButton");
}

bool RadioButton::checked() const
{
	return ischecked;
}

void RadioButton::setChecked(bool checked)
{
	bool laststate=ischecked;
	ischecked=checked;
	needsRedraw();
	// uncheck all other RadioButtons in Parent-Widget
	if (checked == true && this->getParent()) {
		Widget* parent=this->getParent();
		std::list<Widget*>::iterator it;
		for (it=parent->childsBegin(); it != parent->childsEnd();++it) {
			if (typeid(**it) == typeid(RadioButton) && *it != this) {
				((RadioButton*)(*it))->setChecked(false);
			}
		}
	}
	ppl7::tk::Event ev(ppl7::tk::Event::Toggled);
	ev.setWidget(this);
	if (checked != laststate) {
		toggledEvent(&ev, checked);
	}
}

void RadioButton::paint(ppl7::grafix::Drawable& draw)
{
	const ppl7::tk::WidgetStyle& style=ppl7::tk::GetWidgetStyle();
	ppl7::grafix::Drawable d=draw.getDrawable(16, 0, draw.width() - 16, draw.height());
	Label::paint(d);
	int y1=draw.height() / 2;
	draw.circle(9, y1, 7, style.frameBorderColorLight);
	draw.circle(9, y1, 6, style.frameBorderColorLight);
	if (ischecked) draw.floodFill(9, y1, this->color(), style.frameBorderColorLight);
}

void RadioButton::mouseDownEvent(ppl7::tk::MouseEvent* event)
{
	setChecked(true);
}

} //EOF namespace
<|endoftext|>


<commit_before>#include "../core_include/api.h"
#include "../core_include/rect.h"
#include "../core_include/resource.h"
#include "../core_include/theme.h"

static const FONT_INFO* s_font_map[FONT_MAX];
static const BITMAP_INFO* s_bmp_map[BITMAP_MAX];
static unsigned int s_color_map[COLOR_MAX];

int c_theme::add_font(FONT_TYPE index, const FONT_INFO* font)
{
	if (index >= FONT_MAX)
	{
		ASSERT(false);
		return -1;
	}
	s_font_map[index] = font;
	return 0;
}

const FONT_INFO* c_theme::get_font(FONT_TYPE index)
{
	if (index >= FONT_MAX)
	{
		ASSERT(false);
		return 0;
	}
	return s_font_map[index];
}

int c_theme::add_bitmap(BITMAP_TYPE index, const BITMAP_INFO* bmp)
{
	if (index >= BITMAP_MAX)
	{
		ASSERT(false);
		return -1;
	}
	s_bmp_map[index] = bmp;
	return 0;
}

const BITMAP_INFO* c_theme::get_bmp(BITMAP_TYPE index)
{
	if (index >= BITMAP_MAX)
	{
		ASSERT(false);
		return 0;
	}
	return s_bmp_map[index];
}

int c_theme::add_color(COLOR_TYPE index, const unsigned int color)
{
	if (index >= COLOR_MAX)
	{
		ASSERT(false);
		return -1;
	}
	s_color_map[index] = color;
	return 0;
}

const unsigned int c_theme::get_color(COLOR_TYPE index)
{
	if (index >= COLOR_MAX)
	{
		ASSERT(false);
		return 0;
	}
	return s_color_map[index];
}<commit_msg>add end line for theme.cpp<commit_after>#include "../core_include/api.h"
#include "../core_include/rect.h"
#include "../core_include/resource.h"
#include "../core_include/theme.h"

static const FONT_INFO* s_font_map[FONT_MAX];
static const BITMAP_INFO* s_bmp_map[BITMAP_MAX];
static unsigned int s_color_map[COLOR_MAX];

int c_theme::add_font(FONT_TYPE index, const FONT_INFO* font)
{
	if (index >= FONT_MAX)
	{
		ASSERT(false);
		return -1;
	}
	s_font_map[index] = font;
	return 0;
}

const FONT_INFO* c_theme::get_font(FONT_TYPE index)
{
	if (index >= FONT_MAX)
	{
		ASSERT(false);
		return 0;
	}
	return s_font_map[index];
}

int c_theme::add_bitmap(BITMAP_TYPE index, const BITMAP_INFO* bmp)
{
	if (index >= BITMAP_MAX)
	{
		ASSERT(false);
		return -1;
	}
	s_bmp_map[index] = bmp;
	return 0;
}

const BITMAP_INFO* c_theme::get_bmp(BITMAP_TYPE index)
{
	if (index >= BITMAP_MAX)
	{
		ASSERT(false);
		return 0;
	}
	return s_bmp_map[index];
}

int c_theme::add_color(COLOR_TYPE index, const unsigned int color)
{
	if (index >= COLOR_MAX)
	{
		ASSERT(false);
		return -1;
	}
	s_color_map[index] = color;
	return 0;
}

const unsigned int c_theme::get_color(COLOR_TYPE index)
{
	if (index >= COLOR_MAX)
	{
		ASSERT(false);
		return 0;
	}
	return s_color_map[index];
}
<|endoftext|>


<commit_before>#include <iostream>
#include <iomanip> // << fixed << setprecision(xxx)
#include <algorithm> // do { } while ( next_permutation(A, A+xxx) ) ;
#include <vector>
#include <string> // to_string(nnn) // substr(m, n) // stoi(nnn)
#include <complex>
#include <tuple> // get<n>(xxx)
#include <queue>
#include <stack>
#include <map> // if (M.find(key) != M.end()) { }
#include <set> // S.insert(M);
// if (S.find(key) != S.end()) { }
// for (auto it=S.begin(); it != S.end(); it++) { }
// auto it = S.lower_bound(M);
#include <random> // random_device rd; mt19937 mt(rd());
#include <cctype>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstdlib> // atoi(xxx)
using namespace std;

#define DEBUG 0 // change 0 -> 1 if we need debug.
// insert #if<tab> by my emacs. #if DEBUG == 1 ... #end

typedef long long ll;

// const int dx[4] = {1, 0, -1, 0};
// const int dy[4] = {0, 1, 0, -1};

// const int C = 1e6+10;
// const ll M = 1000000007;

string S, T;

int main () {
  cin >> S >> T;
  int N = S.size();
  int M = T.size();
  vector<string> V;
  for (auto i = 0; i < N - M; ++i) {
    string X = S;
    for (auto j = 0; j < M; ++j) {
      X[i + j] = T[j];
    }
    for (auto i = 0; i < N; ++i) {
      if (X[i] == '?') X[i] = 'a';
    }
    // cerr << X << endl;
    V.push_back(X);
  }
  sort(V.begin(), V.end());
  for (auto X : V) {
    bool ok = true;
    for (auto i = 0; i < N; ++i) {
      if (S[i] != '?' && S[i] != X[i]) ok = false;
    }
    if (ok) {
      cout << X << endl;
      return 0;
    }
  }
  cout << "UNRESTORABLE" << endl;
}
<commit_msg>tried C.cpp to 'C'<commit_after>#include <iostream>
#include <iomanip> // << fixed << setprecision(xxx)
#include <algorithm> // do { } while ( next_permutation(A, A+xxx) ) ;
#include <vector>
#include <string> // to_string(nnn) // substr(m, n) // stoi(nnn)
#include <complex>
#include <tuple> // get<n>(xxx)
#include <queue>
#include <stack>
#include <map> // if (M.find(key) != M.end()) { }
#include <set> // S.insert(M);
// if (S.find(key) != S.end()) { }
// for (auto it=S.begin(); it != S.end(); it++) { }
// auto it = S.lower_bound(M);
#include <random> // random_device rd; mt19937 mt(rd());
#include <cctype>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstdlib> // atoi(xxx)
using namespace std;

#define DEBUG 0 // change 0 -> 1 if we need debug.
// insert #if<tab> by my emacs. #if DEBUG == 1 ... #end

typedef long long ll;

// const int dx[4] = {1, 0, -1, 0};
// const int dy[4] = {0, 1, 0, -1};

// const int C = 1e6+10;
// const ll M = 1000000007;

string S, T;

int main () {
  cin >> S >> T;
  int N = S.size();
  int M = T.size();
  vector<string> V;
  for (auto i = 0; i < N - M; ++i) {
    string X = S;
    for (auto j = 0; j < M; ++j) {
      X[i + j] = T[j];
    }
    for (auto i = 0; i < N; ++i) {
      if (X[i] == '?') X[i] = 'a';
    }
    cerr << X << endl;
    V.push_back(X);
  }
  sort(V.begin(), V.end());
  for (auto X : V) {
    bool ok = true;
    for (auto i = 0; i < N; ++i) {
      if (S[i] != '?' && S[i] != X[i]) ok = false;
    }
    if (ok) {
      cout << X << endl;
      return 0;
    }
  }
  cout << "UNRESTORABLE" << endl;
}
<|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/dom_ui/clear_browser_data_handler.h"

#include "app/l10n_util.h"
#include "base/basictypes.h"
#include "base/values.h"
#include "chrome/browser/pref_service.h"
#include "chrome/browser/profile.h"
#include "chrome/common/pref_names.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "grit/locale_settings.h"

ClearBrowserDataHandler::ClearBrowserDataHandler() {
}

ClearBrowserDataHandler::~ClearBrowserDataHandler() {
}

void ClearBrowserDataHandler::GetLocalizedValues(
    DictionaryValue* localized_strings) {
  DCHECK(localized_strings);
  localized_strings->SetString(L"clearBrowsingDataTitle",
      l10n_util::GetString(IDS_CLEAR_BROWSING_DATA_TITLE));
  localized_strings->SetString(L"clearBrowsingDataLabel",
      l10n_util::GetString(IDS_CLEAR_BROWSING_DATA_LABEL));
  localized_strings->SetString(L"clearBrowsingDataTimeLabel",
      l10n_util::GetString(IDS_CLEAR_BROWSING_DATA_TIME_LABEL));
  localized_strings->SetString(L"deleteBrowsingHistoryCheckbox",
      l10n_util::GetString(IDS_DEL_BROWSING_HISTORY_CHKBOX));
  localized_strings->SetString(L"deleteDownloadHistoryCheckbox",
      l10n_util::GetString(IDS_DEL_DOWNLOAD_HISTORY_CHKBOX));
  localized_strings->SetString(L"deleteCacheCheckbox",
      l10n_util::GetString(IDS_DEL_CACHE_CHKBOX));
  localized_strings->SetString(L"deleteCookiesCheckbox",
      l10n_util::GetString(IDS_DEL_COOKIES_CHKBOX));
  localized_strings->SetString(L"deletePasswordsCheckbox",
      l10n_util::GetString(IDS_DEL_PASSWORDS_CHKBOX));
  localized_strings->SetString(L"deleteFormDataCheckbox",
      l10n_util::GetString(IDS_DEL_FORM_DATA_CHKBOX));
  localized_strings->SetString(L"clearBrowsingDataCommit",
      l10n_util::GetString(IDS_CLEAR_BROWSING_DATA_COMMIT));
  localized_strings->SetString(L"flashStorageSettings",
      l10n_util::GetString(IDS_FLASH_STORAGE_SETTINGS));
  localized_strings->SetString(L"flash_storage_url",
      l10n_util::GetString(IDS_FLASH_STORAGE_URL));
  localized_strings->SetString(L"clearDataDeleting",
      l10n_util::GetString(IDS_CLEAR_DATA_DELETING));

  ListValue* time_list = new ListValue;
  for (int i = 0; i < 5; i++) {
    std::wstring label_string;
    switch (i) {
      case 0:
        label_string = l10n_util::GetString(IDS_CLEAR_DATA_HOUR);
        break;
      case 1:
        label_string = l10n_util::GetString(IDS_CLEAR_DATA_DAY);
        break;
      case 2:
        label_string = l10n_util::GetString(IDS_CLEAR_DATA_WEEK);
        break;
      case 3:
        label_string = l10n_util::GetString(IDS_CLEAR_DATA_4WEEKS);
        break;
      case 4:
        label_string = l10n_util::GetString(IDS_CLEAR_DATA_EVERYTHING);
        break;
    }
    ListValue* option = new ListValue();
    option->Append(Value::CreateIntegerValue(i));
    option->Append(Value::CreateStringValue(label_string));
    time_list->Append(option);
  }
  localized_strings->Set(L"clearBrowsingDataTimeList", time_list);
}

void ClearBrowserDataHandler::RegisterMessages() {
  // Setup handlers specific to this panel.
  DCHECK(dom_ui_);
  dom_ui_->RegisterMessageCallback("performClearBrowserData",
      NewCallback(this, &ClearBrowserDataHandler::HandleClearBrowserData));
}

void ClearBrowserDataHandler::HandleClearBrowserData(const Value* value) {
  Profile *profile = dom_ui_->GetProfile();
  PrefService *prefs = profile->GetPrefs();

  int remove_mask = 0;
  if (prefs->GetBoolean(prefs::kDeleteBrowsingHistory))
    remove_mask |= BrowsingDataRemover::REMOVE_HISTORY;
  if (prefs->GetBoolean(prefs::kDeleteDownloadHistory))
    remove_mask |= BrowsingDataRemover::REMOVE_DOWNLOADS;
  if (prefs->GetBoolean(prefs::kDeleteCache))
    remove_mask |= BrowsingDataRemover::REMOVE_CACHE;
  if (prefs->GetBoolean(prefs::kDeleteCookies))
    remove_mask |= BrowsingDataRemover::REMOVE_COOKIES;
  if (prefs->GetBoolean(prefs::kDeletePasswords))
    remove_mask |= BrowsingDataRemover::REMOVE_PASSWORDS;
  if (prefs->GetBoolean(prefs::kDeleteFormData))
    remove_mask |= BrowsingDataRemover::REMOVE_FORM_DATA;

  int period_selected = prefs->GetInteger(prefs::kDeleteTimePeriod);

  FundamentalValue state(true);
  dom_ui_->CallJavascriptFunction(L"clearBrowserDataSetClearingState", state);

  // BrowsingDataRemover deletes itself when done.
  remover_ = new BrowsingDataRemover(profile,
      static_cast<BrowsingDataRemover::TimePeriod>(period_selected),
      base::Time());
  remover_->AddObserver(this);
  remover_->Remove(remove_mask);
}

void ClearBrowserDataHandler::OnBrowsingDataRemoverDone() {
  // No need to remove ourselves as an observer as BrowsingDataRemover deletes
  // itself after we return.
  remover_ = NULL;
  DCHECK(dom_ui_);
  dom_ui_->CallJavascriptFunction(L"clearBrowserDataDismiss");
}

<commit_msg>Remove observer from BrowsingDataRemover on destruct.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/dom_ui/clear_browser_data_handler.h"

#include "app/l10n_util.h"
#include "base/basictypes.h"
#include "base/values.h"
#include "chrome/browser/pref_service.h"
#include "chrome/browser/profile.h"
#include "chrome/common/pref_names.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "grit/locale_settings.h"

ClearBrowserDataHandler::ClearBrowserDataHandler() : remover_(NULL) {
}

ClearBrowserDataHandler::~ClearBrowserDataHandler() {
  if (remover_) {
    remover_->RemoveObserver(this);
  }
}

void ClearBrowserDataHandler::GetLocalizedValues(
    DictionaryValue* localized_strings) {
  DCHECK(localized_strings);
  localized_strings->SetString(L"clearBrowsingDataTitle",
      l10n_util::GetString(IDS_CLEAR_BROWSING_DATA_TITLE));
  localized_strings->SetString(L"clearBrowsingDataLabel",
      l10n_util::GetString(IDS_CLEAR_BROWSING_DATA_LABEL));
  localized_strings->SetString(L"clearBrowsingDataTimeLabel",
      l10n_util::GetString(IDS_CLEAR_BROWSING_DATA_TIME_LABEL));
  localized_strings->SetString(L"deleteBrowsingHistoryCheckbox",
      l10n_util::GetString(IDS_DEL_BROWSING_HISTORY_CHKBOX));
  localized_strings->SetString(L"deleteDownloadHistoryCheckbox",
      l10n_util::GetString(IDS_DEL_DOWNLOAD_HISTORY_CHKBOX));
  localized_strings->SetString(L"deleteCacheCheckbox",
      l10n_util::GetString(IDS_DEL_CACHE_CHKBOX));
  localized_strings->SetString(L"deleteCookiesCheckbox",
      l10n_util::GetString(IDS_DEL_COOKIES_CHKBOX));
  localized_strings->SetString(L"deletePasswordsCheckbox",
      l10n_util::GetString(IDS_DEL_PASSWORDS_CHKBOX));
  localized_strings->SetString(L"deleteFormDataCheckbox",
      l10n_util::GetString(IDS_DEL_FORM_DATA_CHKBOX));
  localized_strings->SetString(L"clearBrowsingDataCommit",
      l10n_util::GetString(IDS_CLEAR_BROWSING_DATA_COMMIT));
  localized_strings->SetString(L"flashStorageSettings",
      l10n_util::GetString(IDS_FLASH_STORAGE_SETTINGS));
  localized_strings->SetString(L"flash_storage_url",
      l10n_util::GetString(IDS_FLASH_STORAGE_URL));
  localized_strings->SetString(L"clearDataDeleting",
      l10n_util::GetString(IDS_CLEAR_DATA_DELETING));

  ListValue* time_list = new ListValue;
  for (int i = 0; i < 5; i++) {
    std::wstring label_string;
    switch (i) {
      case 0:
        label_string = l10n_util::GetString(IDS_CLEAR_DATA_HOUR);
        break;
      case 1:
        label_string = l10n_util::GetString(IDS_CLEAR_DATA_DAY);
        break;
      case 2:
        label_string = l10n_util::GetString(IDS_CLEAR_DATA_WEEK);
        break;
      case 3:
        label_string = l10n_util::GetString(IDS_CLEAR_DATA_4WEEKS);
        break;
      case 4:
        label_string = l10n_util::GetString(IDS_CLEAR_DATA_EVERYTHING);
        break;
    }
    ListValue* option = new ListValue();
    option->Append(Value::CreateIntegerValue(i));
    option->Append(Value::CreateStringValue(label_string));
    time_list->Append(option);
  }
  localized_strings->Set(L"clearBrowsingDataTimeList", time_list);
}

void ClearBrowserDataHandler::RegisterMessages() {
  // Setup handlers specific to this panel.
  DCHECK(dom_ui_);
  dom_ui_->RegisterMessageCallback("performClearBrowserData",
      NewCallback(this, &ClearBrowserDataHandler::HandleClearBrowserData));
}

void ClearBrowserDataHandler::HandleClearBrowserData(const Value* value) {
  Profile *profile = dom_ui_->GetProfile();
  PrefService *prefs = profile->GetPrefs();

  int remove_mask = 0;
  if (prefs->GetBoolean(prefs::kDeleteBrowsingHistory))
    remove_mask |= BrowsingDataRemover::REMOVE_HISTORY;
  if (prefs->GetBoolean(prefs::kDeleteDownloadHistory))
    remove_mask |= BrowsingDataRemover::REMOVE_DOWNLOADS;
  if (prefs->GetBoolean(prefs::kDeleteCache))
    remove_mask |= BrowsingDataRemover::REMOVE_CACHE;
  if (prefs->GetBoolean(prefs::kDeleteCookies))
    remove_mask |= BrowsingDataRemover::REMOVE_COOKIES;
  if (prefs->GetBoolean(prefs::kDeletePasswords))
    remove_mask |= BrowsingDataRemover::REMOVE_PASSWORDS;
  if (prefs->GetBoolean(prefs::kDeleteFormData))
    remove_mask |= BrowsingDataRemover::REMOVE_FORM_DATA;

  int period_selected = prefs->GetInteger(prefs::kDeleteTimePeriod);

  FundamentalValue state(true);
  dom_ui_->CallJavascriptFunction(L"clearBrowserDataSetClearingState", state);

  // BrowsingDataRemover deletes itself when done.
  remover_ = new BrowsingDataRemover(profile,
      static_cast<BrowsingDataRemover::TimePeriod>(period_selected),
      base::Time());
  remover_->AddObserver(this);
  remover_->Remove(remove_mask);
}

void ClearBrowserDataHandler::OnBrowsingDataRemoverDone() {
  // No need to remove ourselves as an observer as BrowsingDataRemover deletes
  // itself after we return.
  remover_ = NULL;
  DCHECK(dom_ui_);
  dom_ui_->CallJavascriptFunction(L"clearBrowserDataDismiss");
}

<|endoftext|>


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

#ifdef _WIN32
    #define NOMINMAX
#endif // _WIN32

#include <iostream>
#include <mutex>
#include <chrono>
#include <map>
#include <thread>
#include <algorithm>
#include <tins/tins.h>

using namespace Tins;

// Holds the DNS response time statistics. The response time is
// represented using the Duration template parameter.
template<typename Duration>
class statistics {
public:
    using duration_type = Duration;
    using locker_type = std::lock_guard<std::mutex>;

    struct information {
        duration_type average, worst;
        size_t count;
    };

    statistics()
    : m_duration(), m_worst(duration_type::min()), m_count()
    {

    }

    void add_response_time(const duration_type& duration)
    {
        locker_type _(m_lock);
        m_duration += duration;
        m_count++;
        m_worst = std::max(m_worst, duration);
    }

    information get_information() const
    {
        locker_type _(m_lock);
        if(m_count == 0)
            return { };
        else
            return { m_duration / m_count, m_worst, m_count };
    };
private:
    duration_type m_duration, m_worst;
    size_t m_count;
    mutable std::mutex m_lock;
};

// Sniffs and tracks DNS queries. When a matching DNS response is found,
// the response time is added to a statistics object.
//
// This class performs *no cleanup* on data associated with queries that
// weren't answered.
class dns_monitor {
public:
    // The response times are measured in milliseconds
    using duration_type = std::chrono::milliseconds;
    // The statistics type used.
    using statistics_type = statistics<duration_type>;

    void run(BaseSniffer& sniffer);
    const statistics_type& stats() const {
        return m_stats;
    }
private:
    using packet_info = std::tuple<IPv4Address, IPv4Address, uint16_t>;
    using clock_type = std::chrono::system_clock;
    using time_point_type = clock_type::time_point;

    bool callback(const PDU& pdu);
    static packet_info make_packet_info(const PDU& pdu, const DNS& dns);

    statistics_type m_stats;
    std::map<packet_info, time_point_type> m_packet_info;
};

void dns_monitor::run(BaseSniffer& sniffer)
{
    sniffer.sniff_loop(
        std::bind(
            &dns_monitor::callback,
            this,
            std::placeholders::_1
        )
    );
}

bool dns_monitor::callback(const PDU& pdu)
{
    auto now = clock_type::now();
    auto dns = pdu.rfind_pdu<RawPDU>().to<DNS>();
    auto info = make_packet_info(pdu, dns);
    // If it's a query, add the sniff time to our map.
    if(dns.type() == DNS::QUERY) {
        m_packet_info.insert(
            std::make_pair(info, now)
        );
    }
    else {
        // It's a response, we need to find the query in our map.
        auto iter = m_packet_info.find(info);
        if(iter != m_packet_info.end()) {
            // We found the query, let's add the response time to the
            // statistics object.
            m_stats.add_response_time(
                std::chrono::duration_cast<duration_type>(now - iter->second)
            );
            // Forget about the query.
            m_packet_info.erase(iter);
        }
    }
    return true;
}

// It is required that we can identify packets sent and received that
// hold the same DNS id as belonging to the same query.
//
// This function retrieves a tuple (addr, addr, id) that will achieve it.
auto dns_monitor::make_packet_info(const PDU& pdu, const DNS& dns) -> packet_info
{
    const auto& ip = pdu.rfind_pdu<IP>();
    return std::make_tuple(
        // smallest address first
        std::min(ip.src_addr(), ip.dst_addr()),
        // largest address second
        std::max(ip.src_addr(), ip.dst_addr()),
        dns.id()
    );
}

int main(int argc, char *argv[]) {
    std::string iface;
    if (argc == 2) {
        // Use the provided interface
        iface = argv[1];
    }
    else {
        // Use the default interface
        iface = NetworkInterface::default_interface().name();
    }
    try {
        SnifferConfiguration config;
        config.set_promisc_mode(true);
        config.set_filter("udp and port 53");
        Sniffer sniffer(iface, config);
        dns_monitor monitor;
        std::thread thread(
            [&]() {
                monitor.run(sniffer);
            }
        );
        while(true) {
            auto info = monitor.stats().get_information();
            std::cout << "\rAverage " << info.average.count()
                        << "ms. Worst: " << info.worst.count() << "ms. Count: "
                        << info.count;
            std::cout.flush();
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }
    }
    catch(std::exception& ex) {
        std::cout << "[-] Error: " << ex.what() << std::endl;
    }
}
<commit_msg>Add padding at the end of the line on dns_stats<commit_after>/*
 * Copyright (c) 2014, Matias Fontanini
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above
 *   copyright notice, this list of conditions and the following disclaimer
 *   in the documentation and/or other materials provided with the
 *   distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#ifdef _WIN32
    #define NOMINMAX
#endif // _WIN32

#include <iostream>
#include <mutex>
#include <chrono>
#include <map>
#include <thread>
#include <algorithm>
#include <tins/tins.h>

using namespace Tins;

// Holds the DNS response time statistics. The response time is
// represented using the Duration template parameter.
template<typename Duration>
class statistics {
public:
    using duration_type = Duration;
    using locker_type = std::lock_guard<std::mutex>;

    struct information {
        duration_type average, worst;
        size_t count;
    };

    statistics()
    : m_duration(), m_worst(duration_type::min()), m_count()
    {

    }

    void add_response_time(const duration_type& duration)
    {
        locker_type _(m_lock);
        m_duration += duration;
        m_count++;
        m_worst = std::max(m_worst, duration);
    }

    information get_information() const
    {
        locker_type _(m_lock);
        if(m_count == 0)
            return { };
        else
            return { m_duration / m_count, m_worst, m_count };
    };
private:
    duration_type m_duration, m_worst;
    size_t m_count;
    mutable std::mutex m_lock;
};

// Sniffs and tracks DNS queries. When a matching DNS response is found,
// the response time is added to a statistics object.
//
// This class performs *no cleanup* on data associated with queries that
// weren't answered.
class dns_monitor {
public:
    // The response times are measured in milliseconds
    using duration_type = std::chrono::milliseconds;
    // The statistics type used.
    using statistics_type = statistics<duration_type>;

    void run(BaseSniffer& sniffer);
    const statistics_type& stats() const {
        return m_stats;
    }
private:
    using packet_info = std::tuple<IPv4Address, IPv4Address, uint16_t>;
    using clock_type = std::chrono::system_clock;
    using time_point_type = clock_type::time_point;

    bool callback(const PDU& pdu);
    static packet_info make_packet_info(const PDU& pdu, const DNS& dns);

    statistics_type m_stats;
    std::map<packet_info, time_point_type> m_packet_info;
};

void dns_monitor::run(BaseSniffer& sniffer)
{
    sniffer.sniff_loop(
        std::bind(
            &dns_monitor::callback,
            this,
            std::placeholders::_1
        )
    );
}

bool dns_monitor::callback(const PDU& pdu)
{
    auto now = clock_type::now();
    auto dns = pdu.rfind_pdu<RawPDU>().to<DNS>();
    auto info = make_packet_info(pdu, dns);
    // If it's a query, add the sniff time to our map.
    if(dns.type() == DNS::QUERY) {
        m_packet_info.insert(
            std::make_pair(info, now)
        );
    }
    else {
        // It's a response, we need to find the query in our map.
        auto iter = m_packet_info.find(info);
        if(iter != m_packet_info.end()) {
            // We found the query, let's add the response time to the
            // statistics object.
            m_stats.add_response_time(
                std::chrono::duration_cast<duration_type>(now - iter->second)
            );
            // Forget about the query.
            m_packet_info.erase(iter);
        }
    }
    return true;
}

// It is required that we can identify packets sent and received that
// hold the same DNS id as belonging to the same query.
//
// This function retrieves a tuple (addr, addr, id) that will achieve it.
auto dns_monitor::make_packet_info(const PDU& pdu, const DNS& dns) -> packet_info
{
    const auto& ip = pdu.rfind_pdu<IP>();
    return std::make_tuple(
        // smallest address first
        std::min(ip.src_addr(), ip.dst_addr()),
        // largest address second
        std::max(ip.src_addr(), ip.dst_addr()),
        dns.id()
    );
}

int main(int argc, char *argv[]) {
    std::string iface;
    if (argc == 2) {
        // Use the provided interface
        iface = argv[1];
    }
    else {
        // Use the default interface
        iface = NetworkInterface::default_interface().name();
    }
    try {
        SnifferConfiguration config;
        config.set_promisc_mode(true);
        config.set_filter("udp and port 53");
        Sniffer sniffer(iface, config);
        dns_monitor monitor;
        std::thread thread(
            [&]() {
                monitor.run(sniffer);
            }
        );
        while(true) {
            auto info = monitor.stats().get_information();
            std::cout << "\rAverage " << info.average.count()
                        << "ms. Worst: " << info.worst.count() << "ms. Count: "
                        << info.count << "   ";
            std::cout.flush();
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }
    }
    catch(std::exception& ex) {
        std::cout << "[-] Error: " << ex.what() << std::endl;
    }
}
<|endoftext|>