<commit_before>#include <wayland-client.h>
#include <cstring>
#include <config.h>
#include <cstdio>
#include <cassert>
#include <fstream>
#include <sys/time.h>
#include <sys/stat.h>
#include <unistd.h>
#include "window.hpp"
#include "../proto/wayfire-shell-client.h"

#include <asoundlib.h>

struct audio_data
{
    snd_mixer_selem_id_t *sid;
    snd_mixer_t *handle;
    snd_mixer_elem_t *elem;
    long min, max;
} audata;

void setup_audio()
{
    const char *card = "default";
    const char *selem_name = "Master";

    snd_mixer_open(&audata.handle, 0);
    snd_mixer_attach(audata.handle, card);
    snd_mixer_selem_register(audata.handle, NULL, NULL);
    snd_mixer_load(audata.handle);

    snd_mixer_selem_id_alloca(&audata.sid);
    snd_mixer_selem_id_set_index(audata.sid, 0);
    snd_mixer_selem_id_set_name(audata.sid, selem_name);

    audata.elem = snd_mixer_find_selem(audata.handle, audata.sid);
    snd_mixer_selem_get_playback_volume_range(audata.elem, &audata.min, &audata.max);
}

void cleanup_audio()
{
    snd_mixer_close(audata.handle);
}

int tar_level = -1;

int get_audio_level()
{
    setup_audio();

    float dta = -1;

    if (tar_level == -1)
    {

        long volume;
        snd_mixer_selem_get_playback_volume(audata.elem, (snd_mixer_selem_channel_id_t)0, &volume);
        volume -= audata.min;

        dta =  1. * volume / (audata.max - audata.min) * 100.f;
    } else
    {
        dta = tar_level;
        long volume = audata.min + (audata.max - audata.min) * tar_level / 100;
        snd_mixer_selem_set_playback_volume_all(audata.elem, volume);

        tar_level = -1;
    }

    cleanup_audio();
    return dta + 0.5;
}

timeval get_current_time()
{
    timeval time;
    gettimeofday(&time, 0);
    return time;
}

const char *lock_file = "/tmp/.wayfire-sound-lock";
bool check_has_lock_file()
{
    struct stat buffer;
    if (stat(lock_file, &buffer))
        return false;
    return true;
}

void create_lock_file()
{
    std::ofstream out(lock_file);
    out << 1 << std::endl;
}

void cleanup()
{
    finish_wayland_connection();
    remove(lock_file);
}

wayfire_window *window = nullptr;
cairo_t *cr = nullptr;

int input_count = 0;
timeval start_time;

struct
{
    int x = 100, y = 100, w = 450, h = 70;
} geometry, bar_geometry;

int alpha = 0;
int step = 1;
int cur_level = -1;

long inactive_time_mms = 1e6;

bool fade_in = true;

void render_frame();
void redraw_handler(void *data, wl_callback*, uint32_t)
{ render_frame(); }

static const struct wl_callback_listener frame_listener = { redraw_handler };

static wl_callback *repaint_callback = nullptr;
void add_callback()
{
    if (repaint_callback)
        wl_callback_destroy(repaint_callback);

    repaint_callback = wl_surface_frame(window->surface);
    wl_callback_add_listener(repaint_callback, &frame_listener, window);
}

void render_frame()
{
    if (fade_in)
    {
        alpha += 1;
        if (alpha >= 8)
        {
            alpha = 8;
            fade_in = 0;
        }
    }

    auto ctime = get_current_time();
    auto level = get_audio_level();

    if (cur_level != level || input_count)
    {
        start_time = get_current_time();

        fade_in = true;
    }

    long long elapsed = (ctime.tv_sec - start_time.tv_sec) * 1e6 + (ctime.tv_usec - start_time.tv_usec);
    if (elapsed > inactive_time_mms)
    {
        fade_in = false;
        alpha -= 1;
        if (alpha == 0)
        {
            cleanup();
            std::exit(0);
        }
    } else if (!fade_in)
    {
        usleep(1e6/15);
    }

    cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
    render_rounded_rectangle(cr, 0, 0, geometry.w, geometry.h,
                             5.0, 0.033, 0.041, 0.047, alpha / 10.f);

    std::string label = " " + std::to_string(level) + "%  ";
    if (level < 10) label = " " + label;
    if (level < 100) label = " " + label;

    cairo_set_source_rgba(cr, 1, 1, 1, 1.25 * alpha / 10.f);
    float font_size = geometry.h * 0.4;
    cairo_set_font_size(cr, font_size);

    cairo_text_extents_t te;
    cairo_text_extents(cr, " 100% ", &te);

    float y = (geometry.h + te.height) / 2.0;

    cairo_move_to(cr, 0, y);
    cairo_show_text(cr, label.c_str());

    cairo_set_source_rgba(cr, 0.3, 0.5, 1.0, 1.25 * alpha / 10.f);
    float x = te.x_advance;

    cairo_new_path(cr);
    cairo_move_to(cr, 0, 0);

    float bar_height = font_size * 0.8;
    y = (geometry.h - bar_height) / 2.0;

    if (std::abs(cur_level - level) < 3)
        cur_level = level;
    else
    {
        cur_level = (2 * cur_level + 3 * level) / 5;
    }

    float bar_width = geometry.w * 0.96 - te.x_advance;
    float vol_width = bar_width * cur_level / 100.f;

    bar_geometry.x = x;
    bar_geometry.y = y;
    bar_geometry.w = bar_width;
    bar_geometry.h = bar_height;

    cairo_rectangle(cr, x, y, vol_width, bar_height);
    cairo_fill(cr);
    cairo_new_path(cr);
    cairo_set_source_rgba(cr, 0.2, 0.2, 0.2, 1.25 * alpha / 10.f);
    cairo_rectangle(cr, x + vol_width, y, bar_width - vol_width, bar_height);
    cairo_fill(cr);
    cairo_new_path(cr);

    add_callback();
    damage_commit_window(window);
}

bool should_handle_input(int x, int y)
{
    return bar_geometry.x <= x && bar_geometry.y <= y &&
        bar_geometry.x + bar_geometry.w >= x && bar_geometry.y + bar_geometry.h >= y;
}

void handle_input(int x, int y)
{
    double d = x - bar_geometry.x;
    d /= bar_geometry.w;

    tar_level = 100 * d;

    tar_level = std::min(tar_level, 100);
    tar_level = std::max(tar_level, 0);
}

void setup_window()
{
    window = create_window(geometry.w, geometry.h);

    window->touch_down = [] (uint32_t, int id, int x, int y)
    {
        if (should_handle_input(x, y))
        {
            input_count |= (1 << id);
            handle_input(x, y);
        }
    };

    window->touch_up = [] (int id)
    {
        input_count &= ~(1 << id);
    };

    window->touch_motion = [] (int id, int x, int y)
    {
        if (should_handle_input(x, y))
            handle_input(x, y);
    };

    window->pointer_enter = [] (wl_pointer*, uint32_t time, int, int)
    {
        show_default_cursor(time);
    };

    window->pointer_button = [] (uint32_t button, uint32_t type, int x, int y)
    {
        if (type == WL_POINTER_BUTTON_STATE_PRESSED)
        {
            if (should_handle_input(x, y))
            {
                input_count |= (1 << 31);
                handle_input(x, y);
            }
        } else
        {
            input_count &= ~(1 << 31);
        }
    };

    window->pointer_move = [] (int x, int y)
    {
        if (input_count)
            handle_input(x, y);
    };

    wayfire_shell_add_panel(display.wfshell, -1, window->surface);
    wayfire_shell_configure_panel(display.wfshell, -1, window->surface, geometry.x, geometry.y);

    cr = cairo_create(window->cairo_surface);
}

int main(int argc, char **argv)
{
    if (check_has_lock_file())
        return 0;

    create_lock_file();

    if (!setup_wayland_connection())
        return -1;

    if (argc >= 5)
    {
        geometry.x = std::atoi(argv[1]);
        geometry.y = std::atoi(argv[2]);
        geometry.w = std::atoi(argv[3]);
        geometry.h = std::atoi(argv[4]);
    }

    if (argc >= 6)
        inactive_time_mms = std::atoi(argv[5]) * 1000;

    setup_audio();
    setup_window();
    render_frame();

    while(true) {
        if (wl_display_dispatch(display.wl_disp) < 0)
            break;
    }

    cleanup();
}
<commit_msg>sound-popup: don't close if pointer/touch is pressed<commit_after>#include <wayland-client.h>
#include <cstring>
#include <config.h>
#include <cstdio>
#include <cassert>
#include <fstream>
#include <sys/time.h>
#include <sys/stat.h>
#include <unistd.h>
#include "window.hpp"
#include "../proto/wayfire-shell-client.h"

#include <asoundlib.h>

struct audio_data
{
    snd_mixer_selem_id_t *sid;
    snd_mixer_t *handle;
    snd_mixer_elem_t *elem;
    long min, max;
} audata;

void setup_audio()
{
    const char *card = "default";
    const char *selem_name = "Master";

    snd_mixer_open(&audata.handle, 0);
    snd_mixer_attach(audata.handle, card);
    snd_mixer_selem_register(audata.handle, NULL, NULL);
    snd_mixer_load(audata.handle);

    snd_mixer_selem_id_alloca(&audata.sid);
    snd_mixer_selem_id_set_index(audata.sid, 0);
    snd_mixer_selem_id_set_name(audata.sid, selem_name);

    audata.elem = snd_mixer_find_selem(audata.handle, audata.sid);
    snd_mixer_selem_get_playback_volume_range(audata.elem, &audata.min, &audata.max);
}

void cleanup_audio()
{
    snd_mixer_close(audata.handle);
}

int tar_level = -1;

int get_audio_level()
{
    setup_audio();

    float dta = -1;

    if (tar_level == -1)
    {

        long volume;
        snd_mixer_selem_get_playback_volume(audata.elem, (snd_mixer_selem_channel_id_t)0, &volume);
        volume -= audata.min;

        dta =  1. * volume / (audata.max - audata.min) * 100.f;
    } else
    {
        dta = tar_level;
        long volume = audata.min + (audata.max - audata.min) * tar_level / 100;
        snd_mixer_selem_set_playback_volume_all(audata.elem, volume);

        tar_level = -1;
    }

    cleanup_audio();
    return dta + 0.5;
}

timeval get_current_time()
{
    timeval time;
    gettimeofday(&time, 0);
    return time;
}

const char *lock_file = "/tmp/.wayfire-sound-lock";
bool check_has_lock_file()
{
    struct stat buffer;
    if (stat(lock_file, &buffer))
        return false;
    return true;
}

void create_lock_file()
{
    std::ofstream out(lock_file);
    out << 1 << std::endl;
}

void cleanup()
{
    finish_wayland_connection();
    remove(lock_file);
}

wayfire_window *window = nullptr;
cairo_t *cr = nullptr;

int input_count = 0;
const int input_pointer_button = (1 << 31);
const int input_input_focus = (1 << 30);
timeval start_time;

struct
{
    int x = 100, y = 100, w = 450, h = 70;
} geometry, bar_geometry;

int alpha = 0;
int step = 1;
int cur_level = -1;

long inactive_time_mms = 1e6;

bool fade_in = true;

void render_frame();
void redraw_handler(void *data, wl_callback*, uint32_t)
{ render_frame(); }

static const struct wl_callback_listener frame_listener = { redraw_handler };

static wl_callback *repaint_callback = nullptr;
void add_callback()
{
    if (repaint_callback)
        wl_callback_destroy(repaint_callback);

    repaint_callback = wl_surface_frame(window->surface);
    wl_callback_add_listener(repaint_callback, &frame_listener, window);
}

void render_frame()
{
    if (fade_in)
    {
        alpha += 1;
        if (alpha >= 8)
        {
            alpha = 8;
            fade_in = 0;
        }
    }

    auto ctime = get_current_time();
    auto level = get_audio_level();

    if (cur_level != level || input_count)
    {
        start_time = get_current_time();

        fade_in = true;
    }

    long long elapsed = (ctime.tv_sec - start_time.tv_sec) * 1e6 + (ctime.tv_usec - start_time.tv_usec);
    if (elapsed > inactive_time_mms)
    {
        fade_in = false;
        alpha -= 1;
        if (alpha == 0)
        {
            cleanup();
            std::exit(0);
        }
    } else if (!fade_in)
    {
        usleep(1e6/15);
    }

    cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
    render_rounded_rectangle(cr, 0, 0, geometry.w, geometry.h,
                             5.0, 0.033, 0.041, 0.047, alpha / 10.f);

    std::string label = " " + std::to_string(level) + "%  ";
    if (level < 10) label = " " + label;
    if (level < 100) label = " " + label;

    cairo_set_source_rgba(cr, 1, 1, 1, 1.25 * alpha / 10.f);
    float font_size = geometry.h * 0.4;
    cairo_set_font_size(cr, font_size);

    cairo_text_extents_t te;
    cairo_text_extents(cr, " 100% ", &te);

    float y = (geometry.h + te.height) / 2.0;

    cairo_move_to(cr, 0, y);
    cairo_show_text(cr, label.c_str());

    cairo_set_source_rgba(cr, 0.3, 0.5, 1.0, 1.25 * alpha / 10.f);
    float x = te.x_advance;

    cairo_new_path(cr);
    cairo_move_to(cr, 0, 0);

    float bar_height = font_size * 0.8;
    y = (geometry.h - bar_height) / 2.0;

    if (std::abs(cur_level - level) < 3)
        cur_level = level;
    else
    {
        cur_level = (2 * cur_level + 3 * level) / 5;
    }

    float bar_width = geometry.w * 0.96 - te.x_advance;
    float vol_width = bar_width * cur_level / 100.f;

    bar_geometry.x = x;
    bar_geometry.y = y;
    bar_geometry.w = bar_width;
    bar_geometry.h = bar_height;

    cairo_rectangle(cr, x, y, vol_width, bar_height);
    cairo_fill(cr);
    cairo_new_path(cr);
    cairo_set_source_rgba(cr, 0.2, 0.2, 0.2, 1.25 * alpha / 10.f);
    cairo_rectangle(cr, x + vol_width, y, bar_width - vol_width, bar_height);
    cairo_fill(cr);
    cairo_new_path(cr);

    add_callback();
    damage_commit_window(window);
}

bool should_handle_input(int x, int y)
{
    return bar_geometry.x <= x && bar_geometry.y <= y &&
        bar_geometry.x + bar_geometry.w >= x && bar_geometry.y + bar_geometry.h >= y;
}

void handle_input(int x, int y)
{
    double d = x - bar_geometry.x;
    d /= bar_geometry.w;

    tar_level = 100 * d;

    tar_level = std::min(tar_level, 100);
    tar_level = std::max(tar_level, 0);
}

void setup_window()
{
    window = create_window(geometry.w, geometry.h);

    window->touch_down = [] (uint32_t, int id, int x, int y)
    {
        input_count |= input_input_focus;
        if (should_handle_input(x, y))
        {
            input_count |= (1 << id);
            handle_input(x, y);
        }
    };

    window->touch_up = [] (int id)
    {
        input_count &= ~(1 << id);
        input_count &= ~input_input_focus;
    };

    window->touch_motion = [] (int id, int x, int y)
    {
        if (should_handle_input(x, y))
            handle_input(x, y);
    };

    window->pointer_enter = [] (wl_pointer*, uint32_t time, int, int)
    {
        show_default_cursor(time);
        input_count |= input_input_focus;
    };

    window->pointer_leave = [] ()
    {
        input_count &= ~input_input_focus;
    };

    window->pointer_button = [] (uint32_t button, uint32_t type, int x, int y)
    {
        if (type == WL_POINTER_BUTTON_STATE_PRESSED)
        {
            if (should_handle_input(x, y))
            {
                input_count |= input_pointer_button;
                handle_input(x, y);
            }
        } else
        {
            input_count &= ~input_pointer_button;
        }
    };

    window->pointer_move = [] (int x, int y)
    {
        if (input_count & ~input_input_focus)
            handle_input(x, y);
    };

    wayfire_shell_add_panel(display.wfshell, -1, window->surface);
    wayfire_shell_configure_panel(display.wfshell, -1, window->surface, geometry.x, geometry.y);

    cr = cairo_create(window->cairo_surface);
}

int main(int argc, char **argv)
{
    if (check_has_lock_file())
        return 0;

    create_lock_file();

    if (!setup_wayland_connection())
        return -1;

    if (argc >= 5)
    {
        geometry.x = std::atoi(argv[1]);
        geometry.y = std::atoi(argv[2]);
        geometry.w = std::atoi(argv[3]);
        geometry.h = std::atoi(argv[4]);
    }

    if (argc >= 6)
        inactive_time_mms = std::atoi(argv[5]) * 1000;

    setup_audio();
    setup_window();
    render_frame();

    while(true) {
        if (wl_display_dispatch(display.wl_disp) < 0)
            break;
    }

    cleanup();
}
<|endoftext|>


<commit_before>// Copyright 2014 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 "components/metrics/serialization/serialization_utils.h"

#include <sys/file.h>

#include <string>
#include <vector>

#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "components/metrics/serialization/metric_sample.h"

#define READ_WRITE_ALL_FILE_FLAGS \
  (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)

namespace metrics {
namespace {

// Reads the next message from |file_descriptor| into |message|.
//
// |message| will be set to the empty string if no message could be read (EOF)
// or the message was badly constructed.
//
// Returns false if no message can be read from this file anymore (EOF or
// unrecoverable error).
bool ReadMessage(int fd, std::string* message) {
  CHECK(message);

  int result;
  int32 message_size;
  // The file containing the metrics do not leave the device so the writer and
  // the reader will always have the same endianness.
  result = HANDLE_EINTR(read(fd, &message_size, sizeof(message_size)));
  if (result < 0) {
    DPLOG(ERROR) << "reading metrics message header";
    return false;
  }
  if (result == 0) {
    // This indicates a normal EOF.
    return false;
  }
  if (result < static_cast<int>(sizeof(message_size))) {
    DLOG(ERROR) << "bad read size " << result << ", expecting "
                << sizeof(message_size);
    return false;
  }

  // kMessageMaxLength applies to the entire message: the 4-byte
  // length field and the content.
  if (message_size > SerializationUtils::kMessageMaxLength) {
    DLOG(ERROR) << "message too long : " << message_size;
    if (HANDLE_EINTR(lseek(fd, message_size - 4, SEEK_CUR)) == -1) {
      DLOG(ERROR) << "error while skipping message. abort";
      return false;
    }
    // Badly formatted message was skipped. Treat the badly formatted sample as
    // an empty sample.
    message->clear();
    return true;
  }

  message_size -= sizeof(message_size);  // The message size includes itself.
  char buffer[SerializationUtils::kMessageMaxLength];
  if (!base::ReadFromFD(fd, buffer, message_size)) {
    DPLOG(ERROR) << "reading metrics message body";
    return false;
  }
  *message = std::string(buffer, message_size);
  return true;
}

}  // namespace

scoped_ptr<MetricSample> SerializationUtils::ParseSample(
    const std::string& sample) {
  if (sample.empty())
    return scoped_ptr<MetricSample>();

  std::vector<std::string> parts;
  base::SplitString(sample, '\0', &parts);
  // We should have two null terminated strings so split should produce
  // three chunks.
  if (parts.size() != 3) {
    DLOG(ERROR) << "splitting message on \\0 produced " << parts.size()
                << " parts (expected 3)";
    return scoped_ptr<MetricSample>();
  }
  const std::string& name = parts[0];
  const std::string& value = parts[1];

  if (base::LowerCaseEqualsASCII(name, "crash")) {
    return MetricSample::CrashSample(value);
  } else if (base::LowerCaseEqualsASCII(name, "histogram")) {
    return MetricSample::ParseHistogram(value);
  } else if (base::LowerCaseEqualsASCII(name, "linearhistogram")) {
    return MetricSample::ParseLinearHistogram(value);
  } else if (base::LowerCaseEqualsASCII(name, "sparsehistogram")) {
    return MetricSample::ParseSparseHistogram(value);
  } else if (base::LowerCaseEqualsASCII(name, "useraction")) {
    return MetricSample::UserActionSample(value);
  } else {
    DLOG(ERROR) << "invalid event type: " << name << ", value: " << value;
  }
  return scoped_ptr<MetricSample>();
}

void SerializationUtils::ReadAndTruncateMetricsFromFile(
    const std::string& filename,
    ScopedVector<MetricSample>* metrics) {
  struct stat stat_buf;
  int result;

  result = stat(filename.c_str(), &stat_buf);
  if (result < 0) {
    if (errno != ENOENT)
      DPLOG(ERROR) << "bad metrics file stat: " << filename;

    // Nothing to collect---try later.
    return;
  }
  if (stat_buf.st_size == 0) {
    // Also nothing to collect.
    return;
  }
  base::ScopedFD fd(open(filename.c_str(), O_RDWR));
  if (fd.get() < 0) {
    DPLOG(ERROR) << "cannot open: " << filename;
    return;
  }
  result = flock(fd.get(), LOCK_EX);
  if (result < 0) {
    DPLOG(ERROR) << "cannot lock: " << filename;
    return;
  }

  // This processes all messages in the log. When all messages are
  // read and processed, or an error occurs, truncate the file to zero size.
  for (;;) {
    std::string message;

    if (!ReadMessage(fd.get(), &message))
      break;

    scoped_ptr<MetricSample> sample = ParseSample(message);
    if (sample)
      metrics->push_back(sample.Pass());
  }

  result = ftruncate(fd.get(), 0);
  if (result < 0)
    DPLOG(ERROR) << "truncate metrics log: " << filename;

  result = flock(fd.get(), LOCK_UN);
  if (result < 0)
    DPLOG(ERROR) << "unlock metrics log: " << filename;
}

bool SerializationUtils::WriteMetricToFile(const MetricSample& sample,
                                           const std::string& filename) {
  if (!sample.IsValid())
    return false;

  base::ScopedFD file_descriptor(open(filename.c_str(),
                                      O_WRONLY | O_APPEND | O_CREAT,
                                      READ_WRITE_ALL_FILE_FLAGS));

  if (file_descriptor.get() < 0) {
    DPLOG(ERROR) << "error openning the file: " << filename;
    return false;
  }

  fchmod(file_descriptor.get(), READ_WRITE_ALL_FILE_FLAGS);
  // Grab a lock to avoid chrome truncating the file
  // underneath us. Keep the file locked as briefly as possible.
  // Freeing file_descriptor will close the file and and remove the lock.
  if (HANDLE_EINTR(flock(file_descriptor.get(), LOCK_EX)) < 0) {
    DPLOG(ERROR) << "error locking: " << filename;
    return false;
  }

  std::string msg = sample.ToString();
  int32 size = msg.length() + sizeof(int32);
  if (size > kMessageMaxLength) {
    DPLOG(ERROR) << "cannot write message: too long: " << filename;
    return false;
  }

  // The file containing the metrics samples will only be read by programs on
  // the same device so we do not check endianness.
  if (!base::WriteFileDescriptor(file_descriptor.get(),
                                 reinterpret_cast<char*>(&size),
                                 sizeof(size))) {
    DPLOG(ERROR) << "error writing message length: " << filename;
    return false;
  }

  if (!base::WriteFileDescriptor(
          file_descriptor.get(), msg.c_str(), msg.size())) {
    DPLOG(ERROR) << "error writing message: " << filename;
    return false;
  }

  return true;
}

}  // namespace metrics
<commit_msg>metrics: Add a check for abnormally small messages to prevent crashes<commit_after>// Copyright 2014 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 "components/metrics/serialization/serialization_utils.h"

#include <sys/file.h>

#include <string>
#include <vector>

#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "components/metrics/serialization/metric_sample.h"

#define READ_WRITE_ALL_FILE_FLAGS \
  (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)

namespace metrics {
namespace {

// Reads the next message from |file_descriptor| into |message|.
//
// |message| will be set to the empty string if no message could be read (EOF)
// or the message was badly constructed.
//
// Returns false if no message can be read from this file anymore (EOF or
// unrecoverable error).
bool ReadMessage(int fd, std::string* message) {
  CHECK(message);

  int result;
  int32_t message_size;
  const int32_t message_header_size = sizeof(message_size);
  // The file containing the metrics does not leave the device so the writer and
  // the reader will always have the same endianness.
  result = HANDLE_EINTR(read(fd, &message_size, message_header_size));
  if (result < 0) {
    DPLOG(ERROR) << "reading metrics message header";
    return false;
  }
  if (result == 0) {
    // This indicates a normal EOF.
    return false;
  }
  if (result < message_header_size) {
    DLOG(ERROR) << "bad read size " << result << ", expecting "
                << sizeof(message_size);
    return false;
  }

  // kMessageMaxLength applies to the entire message: the 4-byte
  // length field and the content.
  if (message_size > SerializationUtils::kMessageMaxLength) {
    DLOG(ERROR) << "message too long : " << message_size;
    if (HANDLE_EINTR(lseek(fd, message_size - 4, SEEK_CUR)) == -1) {
      DLOG(ERROR) << "error while skipping message. abort";
      return false;
    }
    // Badly formatted message was skipped. Treat the badly formatted sample as
    // an empty sample.
    message->clear();
    return true;
  }

  if (message_size < message_header_size) {
    DLOG(ERROR) << "message too short : " << message_size;
    return false;
  }

  message_size -= message_header_size;  // The message size includes itself.
  char buffer[SerializationUtils::kMessageMaxLength];
  if (!base::ReadFromFD(fd, buffer, message_size)) {
    DPLOG(ERROR) << "reading metrics message body";
    return false;
  }
  *message = std::string(buffer, message_size);
  return true;
}

}  // namespace

scoped_ptr<MetricSample> SerializationUtils::ParseSample(
    const std::string& sample) {
  if (sample.empty())
    return scoped_ptr<MetricSample>();

  std::vector<std::string> parts;
  base::SplitString(sample, '\0', &parts);
  // We should have two null terminated strings so split should produce
  // three chunks.
  if (parts.size() != 3) {
    DLOG(ERROR) << "splitting message on \\0 produced " << parts.size()
                << " parts (expected 3)";
    return scoped_ptr<MetricSample>();
  }
  const std::string& name = parts[0];
  const std::string& value = parts[1];

  if (base::LowerCaseEqualsASCII(name, "crash")) {
    return MetricSample::CrashSample(value);
  } else if (base::LowerCaseEqualsASCII(name, "histogram")) {
    return MetricSample::ParseHistogram(value);
  } else if (base::LowerCaseEqualsASCII(name, "linearhistogram")) {
    return MetricSample::ParseLinearHistogram(value);
  } else if (base::LowerCaseEqualsASCII(name, "sparsehistogram")) {
    return MetricSample::ParseSparseHistogram(value);
  } else if (base::LowerCaseEqualsASCII(name, "useraction")) {
    return MetricSample::UserActionSample(value);
  } else {
    DLOG(ERROR) << "invalid event type: " << name << ", value: " << value;
  }
  return scoped_ptr<MetricSample>();
}

void SerializationUtils::ReadAndTruncateMetricsFromFile(
    const std::string& filename,
    ScopedVector<MetricSample>* metrics) {
  struct stat stat_buf;
  int result;

  result = stat(filename.c_str(), &stat_buf);
  if (result < 0) {
    if (errno != ENOENT)
      DPLOG(ERROR) << "bad metrics file stat: " << filename;

    // Nothing to collect---try later.
    return;
  }
  if (stat_buf.st_size == 0) {
    // Also nothing to collect.
    return;
  }
  base::ScopedFD fd(open(filename.c_str(), O_RDWR));
  if (fd.get() < 0) {
    DPLOG(ERROR) << "cannot open: " << filename;
    return;
  }
  result = flock(fd.get(), LOCK_EX);
  if (result < 0) {
    DPLOG(ERROR) << "cannot lock: " << filename;
    return;
  }

  // This processes all messages in the log. When all messages are
  // read and processed, or an error occurs, truncate the file to zero size.
  for (;;) {
    std::string message;

    if (!ReadMessage(fd.get(), &message))
      break;

    scoped_ptr<MetricSample> sample = ParseSample(message);
    if (sample)
      metrics->push_back(sample.Pass());
  }

  result = ftruncate(fd.get(), 0);
  if (result < 0)
    DPLOG(ERROR) << "truncate metrics log: " << filename;

  result = flock(fd.get(), LOCK_UN);
  if (result < 0)
    DPLOG(ERROR) << "unlock metrics log: " << filename;
}

bool SerializationUtils::WriteMetricToFile(const MetricSample& sample,
                                           const std::string& filename) {
  if (!sample.IsValid())
    return false;

  base::ScopedFD file_descriptor(open(filename.c_str(),
                                      O_WRONLY | O_APPEND | O_CREAT,
                                      READ_WRITE_ALL_FILE_FLAGS));

  if (file_descriptor.get() < 0) {
    DPLOG(ERROR) << "error openning the file: " << filename;
    return false;
  }

  fchmod(file_descriptor.get(), READ_WRITE_ALL_FILE_FLAGS);
  // Grab a lock to avoid chrome truncating the file
  // underneath us. Keep the file locked as briefly as possible.
  // Freeing file_descriptor will close the file and and remove the lock.
  if (HANDLE_EINTR(flock(file_descriptor.get(), LOCK_EX)) < 0) {
    DPLOG(ERROR) << "error locking: " << filename;
    return false;
  }

  std::string msg = sample.ToString();
  int32 size = msg.length() + sizeof(int32);
  if (size > kMessageMaxLength) {
    DPLOG(ERROR) << "cannot write message: too long: " << filename;
    return false;
  }

  // The file containing the metrics samples will only be read by programs on
  // the same device so we do not check endianness.
  if (!base::WriteFileDescriptor(file_descriptor.get(),
                                 reinterpret_cast<char*>(&size),
                                 sizeof(size))) {
    DPLOG(ERROR) << "error writing message length: " << filename;
    return false;
  }

  if (!base::WriteFileDescriptor(
          file_descriptor.get(), msg.c_str(), msg.size())) {
    DPLOG(ERROR) << "error writing message: " << filename;
    return false;
  }

  return true;
}

}  // namespace metrics
<|endoftext|>


<commit_before>/** \file    rewrite_keywords_and_authors_from_authority_data
 *  \brief   Update fields with references to authority data with potentially
             more current authority data
 *  \author  Johannes Riedl
 */

/*
    Copyright (C) 2018, Library of the University of Tübingen

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

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

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

#include <fstream>
#include <iostream>
#include <map>
#include <vector>
#include <cstdlib>
#include "Compiler.h"
#include "MARC.h"
#include "RegexMatcher.h"
#include "StringUtil.h"
#include "util.h"

namespace {

[[noreturn]] void Usage() {
    std::cerr << "Usage: " << ::progname << " master_marc_input authority_data_marc_input.mrc marc_output\n"
                           << "The Authority data must be in the MARC-21 binary format.\n";
    std::exit(EXIT_FAILURE);
}

// Return the first matching primary field (Vorzugsbenennung) from authority data
// This implicitly assumes that the correct tag can be uniquely identified from the PPN
MARC::Record::const_iterator GetFirstPrimaryField(const MARC::Record& authority_record) {
     static const std::vector<std::string> tags_to_check {"100", "151", "150", "110", "111", "130", "153"} ;
     for (const auto &tag_to_check : tags_to_check) {
         MARC::Record::const_iterator primary_field(authority_record.findTag(tag_to_check));
         if (primary_field != authority_record.end())
             return primary_field;
     }
     return authority_record.end();
}

bool GetAuthorityRecordFromPPN(const std::string &bsz_authority_ppn, MARC::Record * const authority_record,
                               MARC::Reader * const authority_reader, const std::unordered_map<std::string, off_t> &authority_offsets)
{
    auto authority_offset(authority_offsets.find(bsz_authority_ppn));
    if (authority_offset != authority_offsets.end()) {
        off_t authority_record_offset(authority_offset->second);
        if (authority_reader->seek(authority_record_offset)) {
            *authority_record = authority_reader->read();
            if (authority_record->getControlNumber() != bsz_authority_ppn)
                LOG_ERROR("We got a wrong PPN " + authority_record->getControlNumber() +
                          " instead of " + bsz_authority_ppn);
            else
                return true;
        } else
            LOG_ERROR("Unable to seek to record for authority PPN " + bsz_authority_ppn);
    } else {
        LOG_WARNING("Unable to find offset for authority PPN " + bsz_authority_ppn);
        return false;
    }
    std::runtime_error("Logical flaw in GetAuthorityRecordFromPPN");
}

bool IsWorkTitleField(const MARC::Subfields &subfields) {
    return subfields.hasSubfieldWithValue('D', "u");
}

void UpdateTitleDataField(MARC::Record::Field * const field, const MARC::Record authority_record) {
    auto authority_primary_field(GetFirstPrimaryField(authority_record));
    if (authority_primary_field == authority_record.end())
        LOG_ERROR("Could not find appropriate Tag for authority PPN " + authority_record.getControlNumber());
    MARC::Subfields subfields(field->getSubfields());
    // We have to make sure that the order of the subfields is inherited from the authority data
    // so delete the subfields to be replaced first
    // Moreover there is a special case with "Werktitel". These are in $a
    // in the authority data but must be mapped to $t in the title data
    for (const auto &authority_subfield : authority_primary_field->getSubfields()) {
        if (IsWorkTitleField(subfields) and authority_subfield.code_ == 'a')
            subfields.deleteAllSubfieldsWithCode('t');
        else
            subfields.deleteAllSubfieldsWithCode(authority_subfield.code_);
    }
    for (const auto &authority_subfield : authority_primary_field->getSubfields()){
        if (IsWorkTitleField(subfields) and authority_subfield.code_ == 'a')
            subfields.appendSubfield('t', authority_subfield.value_);
        else
            subfields.appendSubfield(authority_subfield.code_, authority_subfield.value_);
    }
    field->setContents(subfields, field->getIndicator1(), field->getIndicator2());
}

void AugmentAuthors(MARC::Record * const record, MARC::Reader * const authority_reader,
                    const std::unordered_map<std::string, off_t> &authority_offsets,
                    RegexMatcher * const matcher, bool * const modified_record)
{
    static std::vector<std::string> tags_to_check({"100", "110", "111", "700", "710", "711"});
    for (auto tag_to_check : tags_to_check) {
        for (auto &field : record->getTagRange(tag_to_check)) {
            std::string _author_content(field.getContents());
            if (matcher->matched(_author_content)) {
                MARC::Record authority_record(std::string(MARC::Record::LEADER_LENGTH, ' '));
                if (GetAuthorityRecordFromPPN((*matcher)[1], &authority_record, authority_reader, authority_offsets)) {
                    UpdateTitleDataField(&field, authority_record);
                    *modified_record = true;
                }
            }
        }
    }
}

void AugmentKeywords(MARC::Record * const record, MARC::Reader * const authority_reader,
                     const std::unordered_map<std::string, off_t> &authority_offsets,
                     RegexMatcher * const matcher, bool * const modified_record)
{
    for (auto &field : record->getTagRange("689")) {
        std::string _689_content(field.getContents());
        if (matcher->matched(_689_content)) {
             MARC::Record authority_record(std::string(MARC::Record::LEADER_LENGTH, ' '));
             if (GetAuthorityRecordFromPPN((*matcher)[1], &authority_record, authority_reader, authority_offsets)) {
                 UpdateTitleDataField(&field, authority_record);
                 *modified_record = true;
             }
        }
    }
}

void AugmentKeywordsAndAuthors(MARC::Reader * const marc_reader, MARC::Reader * const authority_reader, MARC::Writer * const marc_writer,
                               const std::unordered_map<std::string, off_t>& authority_offsets)
{
    std::string err_msg;
    RegexMatcher * const matcher(RegexMatcher::RegexMatcherFactory("\x1F""0\\(DE-576\\)([^\x1F]+).*\x1F?", &err_msg));

    if (matcher == nullptr)
        LOG_ERROR("Failed to compile standardized keywords regex matcher: " + err_msg);

    unsigned record_count(0), modified_count(0);
    while (MARC::Record record = marc_reader->read()) {
       ++record_count;
       bool modified_record(false);
       AugmentAuthors(&record, authority_reader, authority_offsets, matcher, &modified_record);
       AugmentKeywords(&record, authority_reader, authority_offsets, matcher, &modified_record);
       if (modified_record)
           ++modified_count;
       marc_writer->write(record);
    }

    LOG_INFO("Modified " + std::to_string(modified_count) + " of " + std::to_string(record_count) + " records.");
}

} // unnamed namespace

int Main(int argc, char **argv) {
    if (argc != 4)
        Usage();

    const std::string marc_input_filename(argv[1]);
    const std::string authority_data_marc_input_filename(argv[2]);
    const std::string marc_output_filename(argv[3]);
    if (unlikely(marc_input_filename == marc_output_filename))
        LOG_ERROR("Title data input file name equals output file name!");
    if (unlikely(authority_data_marc_input_filename == marc_output_filename))
        LOG_ERROR("Authority data input file name equals output file name!");

    std::unique_ptr<MARC::Reader> marc_reader(MARC::Reader::Factory(marc_input_filename));
    std::unique_ptr<MARC::Reader> authority_reader(MARC::Reader::Factory(authority_data_marc_input_filename,
                                                                         MARC::FileType::BINARY));
    std::unique_ptr<MARC::Writer> marc_writer(MARC::Writer::Factory(marc_output_filename));
    std::unordered_map<std::string, off_t> authority_offsets;

    MARC::CollectRecordOffsets(authority_reader.get(), &authority_offsets);
    AugmentKeywordsAndAuthors(marc_reader.get(), authority_reader.get(), marc_writer.get(), authority_offsets);

    return EXIT_SUCCESS;
}
<commit_msg>changed include<commit_after>/** \file    rewrite_keywords_and_authors_from_authority_data
 *  \brief   Update fields with references to authority data with potentially
             more current authority data
 *  \author  Johannes Riedl
 */

/*
    Copyright (C) 2018, Library of the University of Tübingen

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

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

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

#include <fstream>
#include <iostream>
#include <unordered_map>
#include <vector>
#include <cstdlib>
#include "Compiler.h"
#include "MARC.h"
#include "RegexMatcher.h"
#include "StringUtil.h"
#include "util.h"

namespace {

[[noreturn]] void Usage() {
    std::cerr << "Usage: " << ::progname << " master_marc_input authority_data_marc_input.mrc marc_output\n"
                           << "The Authority data must be in the MARC-21 binary format.\n";
    std::exit(EXIT_FAILURE);
}

// Return the first matching primary field (Vorzugsbenennung) from authority data
// This implicitly assumes that the correct tag can be uniquely identified from the PPN
MARC::Record::const_iterator GetFirstPrimaryField(const MARC::Record& authority_record) {
     static const std::vector<std::string> tags_to_check {"100", "151", "150", "110", "111", "130", "153"} ;
     for (const auto &tag_to_check : tags_to_check) {
         MARC::Record::const_iterator primary_field(authority_record.findTag(tag_to_check));
         if (primary_field != authority_record.end())
             return primary_field;
     }
     return authority_record.end();
}

bool GetAuthorityRecordFromPPN(const std::string &bsz_authority_ppn, MARC::Record * const authority_record,
                               MARC::Reader * const authority_reader, const std::unordered_map<std::string, off_t> &authority_offsets)
{
    auto authority_offset(authority_offsets.find(bsz_authority_ppn));
    if (authority_offset != authority_offsets.end()) {
        off_t authority_record_offset(authority_offset->second);
        if (authority_reader->seek(authority_record_offset)) {
            *authority_record = authority_reader->read();
            if (authority_record->getControlNumber() != bsz_authority_ppn)
                LOG_ERROR("We got a wrong PPN " + authority_record->getControlNumber() +
                          " instead of " + bsz_authority_ppn);
            else
                return true;
        } else
            LOG_ERROR("Unable to seek to record for authority PPN " + bsz_authority_ppn);
    } else {
        LOG_WARNING("Unable to find offset for authority PPN " + bsz_authority_ppn);
        return false;
    }
    std::runtime_error("Logical flaw in GetAuthorityRecordFromPPN");
}

bool IsWorkTitleField(const MARC::Subfields &subfields) {
    return subfields.hasSubfieldWithValue('D', "u");
}

void UpdateTitleDataField(MARC::Record::Field * const field, const MARC::Record authority_record) {
    auto authority_primary_field(GetFirstPrimaryField(authority_record));
    if (authority_primary_field == authority_record.end())
        LOG_ERROR("Could not find appropriate Tag for authority PPN " + authority_record.getControlNumber());
    MARC::Subfields subfields(field->getSubfields());
    // We have to make sure that the order of the subfields is inherited from the authority data
    // so delete the subfields to be replaced first
    // Moreover there is a special case with "Werktitel". These are in $a
    // in the authority data but must be mapped to $t in the title data
    for (const auto &authority_subfield : authority_primary_field->getSubfields()) {
        if (IsWorkTitleField(subfields) and authority_subfield.code_ == 'a')
            subfields.deleteAllSubfieldsWithCode('t');
        else
            subfields.deleteAllSubfieldsWithCode(authority_subfield.code_);
    }
    for (const auto &authority_subfield : authority_primary_field->getSubfields()){
        if (IsWorkTitleField(subfields) and authority_subfield.code_ == 'a')
            subfields.appendSubfield('t', authority_subfield.value_);
        else
            subfields.appendSubfield(authority_subfield.code_, authority_subfield.value_);
    }
    field->setContents(subfields, field->getIndicator1(), field->getIndicator2());
}

void AugmentAuthors(MARC::Record * const record, MARC::Reader * const authority_reader,
                    const std::unordered_map<std::string, off_t> &authority_offsets,
                    RegexMatcher * const matcher, bool * const modified_record)
{
    static std::vector<std::string> tags_to_check({"100", "110", "111", "700", "710", "711"});
    for (auto tag_to_check : tags_to_check) {
        for (auto &field : record->getTagRange(tag_to_check)) {
            std::string _author_content(field.getContents());
            if (matcher->matched(_author_content)) {
                MARC::Record authority_record(std::string(MARC::Record::LEADER_LENGTH, ' '));
                if (GetAuthorityRecordFromPPN((*matcher)[1], &authority_record, authority_reader, authority_offsets)) {
                    UpdateTitleDataField(&field, authority_record);
                    *modified_record = true;
                }
            }
        }
    }
}

void AugmentKeywords(MARC::Record * const record, MARC::Reader * const authority_reader,
                     const std::unordered_map<std::string, off_t> &authority_offsets,
                     RegexMatcher * const matcher, bool * const modified_record)
{
    for (auto &field : record->getTagRange("689")) {
        std::string _689_content(field.getContents());
        if (matcher->matched(_689_content)) {
             MARC::Record authority_record(std::string(MARC::Record::LEADER_LENGTH, ' '));
             if (GetAuthorityRecordFromPPN((*matcher)[1], &authority_record, authority_reader, authority_offsets)) {
                 UpdateTitleDataField(&field, authority_record);
                 *modified_record = true;
             }
        }
    }
}

void AugmentKeywordsAndAuthors(MARC::Reader * const marc_reader, MARC::Reader * const authority_reader, MARC::Writer * const marc_writer,
                               const std::unordered_map<std::string, off_t>& authority_offsets)
{
    std::string err_msg;
    RegexMatcher * const matcher(RegexMatcher::RegexMatcherFactory("\x1F""0\\(DE-576\\)([^\x1F]+).*\x1F?", &err_msg));

    if (matcher == nullptr)
        LOG_ERROR("Failed to compile standardized keywords regex matcher: " + err_msg);

    unsigned record_count(0), modified_count(0);
    while (MARC::Record record = marc_reader->read()) {
       ++record_count;
       bool modified_record(false);
       AugmentAuthors(&record, authority_reader, authority_offsets, matcher, &modified_record);
       AugmentKeywords(&record, authority_reader, authority_offsets, matcher, &modified_record);
       if (modified_record)
           ++modified_count;
       marc_writer->write(record);
    }

    LOG_INFO("Modified " + std::to_string(modified_count) + " of " + std::to_string(record_count) + " records.");
}

} // unnamed namespace

int Main(int argc, char **argv) {
    if (argc != 4)
        Usage();

    const std::string marc_input_filename(argv[1]);
    const std::string authority_data_marc_input_filename(argv[2]);
    const std::string marc_output_filename(argv[3]);
    if (unlikely(marc_input_filename == marc_output_filename))
        LOG_ERROR("Title data input file name equals output file name!");
    if (unlikely(authority_data_marc_input_filename == marc_output_filename))
        LOG_ERROR("Authority data input file name equals output file name!");

    std::unique_ptr<MARC::Reader> marc_reader(MARC::Reader::Factory(marc_input_filename));
    std::unique_ptr<MARC::Reader> authority_reader(MARC::Reader::Factory(authority_data_marc_input_filename,
                                                                         MARC::FileType::BINARY));
    std::unique_ptr<MARC::Writer> marc_writer(MARC::Writer::Factory(marc_output_filename));
    std::unordered_map<std::string, off_t> authority_offsets;

    MARC::CollectRecordOffsets(authority_reader.get(), &authority_offsets);
    AugmentKeywordsAndAuthors(marc_reader.get(), authority_reader.get(), marc_writer.get(), authority_offsets);

    return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>// Copyright (C) 2014, Richard Thomson.  All rights reserved.
#define BOOST_DATE_TIME_NO_LIB
#include <boost/date_time/gregorian/gregorian.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/spirit/include/qi.hpp>
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/spirit/include/phoenix.hpp>

#include <cmath>
#include <sstream>
#include <stdexcept>

#include "cfws_skipper.h"
#include "date_time.h"

using namespace boost::spirit::qi;

BOOST_FUSION_ADAPT_STRUCT(::date_time::date,
    (::date_time::days, week_day)
    (unsigned, day)
    (::date_time::months, month)
    (unsigned, year)
);

BOOST_FUSION_ADAPT_STRUCT(::date_time::time,
    (unsigned, hour)
    (unsigned, minute)
    (unsigned, second)
    (int, time_zone_offset)
);

BOOST_FUSION_ADAPT_STRUCT(::date_time::moment,
    (::date_time::date, first)
    (::date_time::time, second)
);

namespace
{

void validate_min_max(char const* name,
    unsigned value, unsigned min_value, unsigned max_value)
{
    if (value < min_value || value > max_value) {
        std::ostringstream message;
        message << name << ' ' << value << " out of range "
            << min_value << '-' << max_value;
        throw std::domain_error(message.str());
    }
}

void validate_day(unsigned day)
{
    validate_min_max("day", day, 1U, 31U);
}

void validate_year(unsigned year)
{
    validate_min_max("year", year, 1900U, 9999U);
}

void validate_date(const ::date_time::date& date)
{
    try {
        boost::gregorian::date check(date.year, date.month, date.day);
        if (date.week_day == date_time::Unspecified) {
            return;
        }
        if (static_cast<unsigned>(date.week_day) != check.day_of_week().as_number()) {
            throw std::domain_error("day name doesn't match day of date");
        }
    } catch (const std::out_of_range&) {
        throw std::domain_error("day "
            + boost::lexical_cast<std::string>(date.day)
            + " invalid for month");
    }
}

void validate_hour(unsigned hour)
{
    validate_min_max("hour", hour, 0U, 23U);
}

void validate_minute(unsigned minute)
{
    validate_min_max("minute", minute, 0U, 59U);
}

void validate_second(unsigned second)
{
    validate_min_max("second", second, 0U, 60U);
}

bool last_day_of_June_or_December(::date_time::date const& date)
{
    return (date.month == ::date_time::June && date.day == 30)
        || (date.month == ::date_time::December && date.day == 31);
}

void validate_date_time(::date_time::moment const& moment)
{
    if (moment.second.second == 60
        && !(last_day_of_June_or_December(moment.first)
            && moment.second.hour == 23
            && moment.second.minute == 59)) {
        throw std::domain_error(
            "leap second only allowed on last day of June or December");
    }
}

void validate_time_zone_offset(int offset)
{
    validate_min_max("timezone offset hour",
        static_cast<unsigned>(std::abs(offset) / 100), 0U, 23U);
    validate_min_max("timezone offset minute",
        static_cast<unsigned>(offset % 100), 0U, 59U);
}

template <typename Iter>
struct date_time_grammar : grammar<Iter, date_time::moment(), cfws::skipper<Iter>>
{
    typedef cfws::skipper<Iter> skipper;

    date_time_grammar() : date_time_grammar::base_type{start}
    {
        uint_parser<unsigned, 10, 1, 2> digit_1_2;
        uint_parser<unsigned, 10, 2, 2> digit_2;
        uint_parser<unsigned, 10, 3, 3> digit_3;
        uint_parser<unsigned, 10, 4, 4> digit_4;

        month_names.add("Jan", date_time::January)
            ("Feb", date_time::February)
            ("Mar", date_time::March)
            ("Apr", date_time::April)
            ("May", date_time::May)
            ("Jun", date_time::June)
            ("Jul", date_time::July)
            ("Aug", date_time::August)
            ("Sep", date_time::September)
            ("Oct", date_time::October)
            ("Nov", date_time::November)
            ("Dec", date_time::December);
        day_names.add("Mon", date_time::Monday)
            ("Tue", date_time::Tuesday)
            ("Wed", date_time::Wednesday)
            ("Thu", date_time::Thursday)
            ("Fri", date_time::Friday)
            ("Sat", date_time::Saturday)
            ("Sun", date_time::Sunday);
        week_day = (day_names >> ',') | attr(date_time::Unspecified);
        day_number %= digit_1_2[&validate_day];
        year_2 %= digit_2[_val += if_else(_1 < 50U, 2000U, 1900U)];
        year_3 %= digit_3[_val += 1900];
        year_number %= (digit_4 | year_3 | year_2)[&validate_year];
        date_part = week_day >> day_number >> month_names >> year_number;

        seconds = (':' >> digit_2) | attr(0);
        int_parser<int, 10, 4, 4> time_zone_offset;
        time_zone_names.add("UT", +000)("GMT", +000)
            ("EST", -500)("EDT", -400)
            ("CST", -600)("CDT", -500)
            ("MST", -700)("MDT", -600)
            ("PST", -800)("PDT", -700)
            ("A", -100)("B", - 200)("C", - 300)("D", -400)
            ("E", -500)("F", - 600)("G", - 700)("H", -800)
            ("I", -900)("K", -1000)("L", -1100)("M", -1200)
            ("N", +100)("O", + 200)("P", + 300)("Q", +400)
            ("R", +500)("S", + 600)("T", + 700)("U", +800)
            ("V", +900)("W", +1000)("X", +1100)("Y", +1200)
            ("Z", +000);
        time_zone %= time_zone_names
            | (&(lit('+') | '-') >> time_zone_offset)[&validate_time_zone_offset];
        time_part %= digit_2[&validate_hour]
            >> lit(':') >> digit_2[&validate_minute]
            >> seconds[&validate_second]
            >> time_zone;
        date_time %= date_part[&validate_date] >> time_part;
        start %= date_time[&validate_date_time];
    };

    symbols<char const, date_time::days> day_names;
    rule<Iter, date_time::days()> week_day;
    rule<Iter, unsigned()> day_number;
    symbols<char const, date_time::months> month_names;
    rule<Iter, unsigned()> year_number;
    rule<Iter, unsigned()> year_3;
    rule<Iter, unsigned()> year_2;
    rule<Iter, date_time::date(), skipper> date_part;
    rule<Iter, unsigned(), skipper> seconds;
    symbols<char const, int> time_zone_names;
    rule<Iter, int()> time_zone;
    rule<Iter, date_time::time(), skipper> time_part;
    rule<Iter, date_time::moment(), skipper> date_time;
    rule<Iter, date_time::moment(), skipper> start;
};

}

namespace date_time
{

moment parse(std::string const& text)
{
    moment result{};
    std::string::const_iterator start{text.begin()};
    if (phrase_parse(start, text.end(),
            date_time_grammar<std::string::const_iterator>{},
            cfws::skipper<std::string::const_iterator>{}, result)
        && start == text.end())
    {
        return result;
    }

    throw std::domain_error("invalid date time");
}

}
<commit_msg>Refactor: Remove unnecessary preprocessor define<commit_after>// Copyright (C) 2014, Richard Thomson.  All rights reserved.
#define BOOST_DATE_TIME_NO_LIB
#include <boost/date_time/gregorian/gregorian.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>

#include <cmath>
#include <sstream>
#include <stdexcept>

#include "cfws_skipper.h"
#include "date_time.h"

using namespace boost::spirit::qi;

BOOST_FUSION_ADAPT_STRUCT(::date_time::date,
    (::date_time::days, week_day)
    (unsigned, day)
    (::date_time::months, month)
    (unsigned, year)
);

BOOST_FUSION_ADAPT_STRUCT(::date_time::time,
    (unsigned, hour)
    (unsigned, minute)
    (unsigned, second)
    (int, time_zone_offset)
);

BOOST_FUSION_ADAPT_STRUCT(::date_time::moment,
    (::date_time::date, first)
    (::date_time::time, second)
);

namespace
{

void validate_min_max(char const* name,
    unsigned value, unsigned min_value, unsigned max_value)
{
    if (value < min_value || value > max_value) {
        std::ostringstream message;
        message << name << ' ' << value << " out of range "
            << min_value << '-' << max_value;
        throw std::domain_error(message.str());
    }
}

void validate_day(unsigned day)
{
    validate_min_max("day", day, 1U, 31U);
}

void validate_year(unsigned year)
{
    validate_min_max("year", year, 1900U, 9999U);
}

void validate_date(const ::date_time::date& date)
{
    try {
        boost::gregorian::date check(date.year, date.month, date.day);
        if (date.week_day == date_time::Unspecified) {
            return;
        }
        if (static_cast<unsigned>(date.week_day) != check.day_of_week().as_number()) {
            throw std::domain_error("day name doesn't match day of date");
        }
    } catch (const std::out_of_range&) {
        throw std::domain_error("day "
            + boost::lexical_cast<std::string>(date.day)
            + " invalid for month");
    }
}

void validate_hour(unsigned hour)
{
    validate_min_max("hour", hour, 0U, 23U);
}

void validate_minute(unsigned minute)
{
    validate_min_max("minute", minute, 0U, 59U);
}

void validate_second(unsigned second)
{
    validate_min_max("second", second, 0U, 60U);
}

bool last_day_of_June_or_December(::date_time::date const& date)
{
    return (date.month == ::date_time::June && date.day == 30)
        || (date.month == ::date_time::December && date.day == 31);
}

void validate_date_time(::date_time::moment const& moment)
{
    if (moment.second.second == 60
        && !(last_day_of_June_or_December(moment.first)
            && moment.second.hour == 23
            && moment.second.minute == 59)) {
        throw std::domain_error(
            "leap second only allowed on last day of June or December");
    }
}

void validate_time_zone_offset(int offset)
{
    validate_min_max("timezone offset hour",
        static_cast<unsigned>(std::abs(offset) / 100), 0U, 23U);
    validate_min_max("timezone offset minute",
        static_cast<unsigned>(offset % 100), 0U, 59U);
}

template <typename Iter>
struct date_time_grammar : grammar<Iter, date_time::moment(), cfws::skipper<Iter>>
{
    typedef cfws::skipper<Iter> skipper;

    date_time_grammar() : date_time_grammar::base_type{start}
    {
        uint_parser<unsigned, 10, 1, 2> digit_1_2;
        uint_parser<unsigned, 10, 2, 2> digit_2;
        uint_parser<unsigned, 10, 3, 3> digit_3;
        uint_parser<unsigned, 10, 4, 4> digit_4;

        month_names.add("Jan", date_time::January)
            ("Feb", date_time::February)
            ("Mar", date_time::March)
            ("Apr", date_time::April)
            ("May", date_time::May)
            ("Jun", date_time::June)
            ("Jul", date_time::July)
            ("Aug", date_time::August)
            ("Sep", date_time::September)
            ("Oct", date_time::October)
            ("Nov", date_time::November)
            ("Dec", date_time::December);
        day_names.add("Mon", date_time::Monday)
            ("Tue", date_time::Tuesday)
            ("Wed", date_time::Wednesday)
            ("Thu", date_time::Thursday)
            ("Fri", date_time::Friday)
            ("Sat", date_time::Saturday)
            ("Sun", date_time::Sunday);
        week_day = (day_names >> ',') | attr(date_time::Unspecified);
        day_number %= digit_1_2[&validate_day];
        year_2 %= digit_2[_val += if_else(_1 < 50U, 2000U, 1900U)];
        year_3 %= digit_3[_val += 1900];
        year_number %= (digit_4 | year_3 | year_2)[&validate_year];
        date_part = week_day >> day_number >> month_names >> year_number;

        seconds = (':' >> digit_2) | attr(0);
        int_parser<int, 10, 4, 4> time_zone_offset;
        time_zone_names.add("UT", +000)("GMT", +000)
            ("EST", -500)("EDT", -400)
            ("CST", -600)("CDT", -500)
            ("MST", -700)("MDT", -600)
            ("PST", -800)("PDT", -700)
            ("A", -100)("B", - 200)("C", - 300)("D", -400)
            ("E", -500)("F", - 600)("G", - 700)("H", -800)
            ("I", -900)("K", -1000)("L", -1100)("M", -1200)
            ("N", +100)("O", + 200)("P", + 300)("Q", +400)
            ("R", +500)("S", + 600)("T", + 700)("U", +800)
            ("V", +900)("W", +1000)("X", +1100)("Y", +1200)
            ("Z", +000);
        time_zone %= time_zone_names
            | (&(lit('+') | '-') >> time_zone_offset)[&validate_time_zone_offset];
        time_part %= digit_2[&validate_hour]
            >> lit(':') >> digit_2[&validate_minute]
            >> seconds[&validate_second]
            >> time_zone;
        date_time %= date_part[&validate_date] >> time_part;
        start %= date_time[&validate_date_time];
    };

    symbols<char const, date_time::days> day_names;
    rule<Iter, date_time::days()> week_day;
    rule<Iter, unsigned()> day_number;
    symbols<char const, date_time::months> month_names;
    rule<Iter, unsigned()> year_number;
    rule<Iter, unsigned()> year_3;
    rule<Iter, unsigned()> year_2;
    rule<Iter, date_time::date(), skipper> date_part;
    rule<Iter, unsigned(), skipper> seconds;
    symbols<char const, int> time_zone_names;
    rule<Iter, int()> time_zone;
    rule<Iter, date_time::time(), skipper> time_part;
    rule<Iter, date_time::moment(), skipper> date_time;
    rule<Iter, date_time::moment(), skipper> start;
};

}

namespace date_time
{

moment parse(std::string const& text)
{
    moment result{};
    std::string::const_iterator start{text.begin()};
    if (phrase_parse(start, text.end(),
            date_time_grammar<std::string::const_iterator>{},
            cfws::skipper<std::string::const_iterator>{}, result)
        && start == text.end())
    {
        return result;
    }

    throw std::domain_error("invalid date time");
}

}
<|endoftext|>


<commit_before>/*M///////////////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2015, Youssef Kashef
// Copyright (c) 2015, ELM Library Project
// 3-clause BSD License
//
//M*/
/** @file test out VecMat1f visitor,
 * some fixtures may be scattered somewhat awkwardly in this file due to conditional support (e.g. PCL support)
 */
#include "elm/core/visitors/visitorvecmat1f.h"

#include "elm/core/exception.h"
#include "elm/core/pcl/cloud_.h"
#include "elm/core/pcl/vertices.h"
#include "elm/ts/ts.h"

using namespace std;
using namespace cv;
using namespace elm;

namespace {

/**
 * @brief test class around VisitorMat1f
 */
class VisitorVecMat1fTest : public ::testing::Test
{
protected:
    VisitorVecMat1f to_;    ///< test object
};

TEST_F(VisitorVecMat1fTest, EmptyVecMat1f)
{
    EXPECT_TRUE(to_(VecMat1f()).empty());
    EXPECT_TRUE(to_(VecMat1f(0)).empty());
}

TEST_F(VisitorVecMat1fTest, EmptyVecMat1f_size)
{
    EXPECT_SIZE(size_t(0), to_(VecMat1f()));
    EXPECT_SIZE(size_t(0), to_(VecMat1f(0)));
}

TEST_F(VisitorVecMat1fTest, FromVecMat1f_size)
{
    int N=5;

    VecMat1f in;
    for(int i=0; i<N; i++) {

        Mat1f m(2, 3, static_cast<float>(N-i));

        in.push_back(m);

        VecMat1f v = to_(in);
        EXPECT_SIZE(size_t(i+1), v);

        for(size_t j=0; j<v.size(); j++) {

            Mat1f m2 = v[j];

            EXPECT_MAT_EQ(m2, Mat1f(2, 3, static_cast<float>(N-j))) << "Matrices are not equal";
            EXPECT_EQ(in[j].data, m2.data) << "Mats are not pointing to the same data in memory. Expecting shared copy.";
        }
    }
}

TEST_F(VisitorVecMat1fTest, FromMat1f)
{
    int N=5;

    while(N--) {

        const int MAT_ROWS = abs(randu<int>()) % 10 + 1;
        const int MAT_COLS = abs(randu<int>()) % 100 + 1;

        Mat1f m(MAT_ROWS, MAT_COLS);
        randn(m, 0.f, 100.f);

        VecMat1f v = to_(m);

        EXPECT_SIZE(size_t(1), v);

        Mat1f m2 = v[0];

        EXPECT_MAT_EQ(m, m2) << "Matrices are not equal";
        EXPECT_EQ(m.data, m2.data) << "Mats are not pointing to the same data in memory. Expecting shared copy.";
    }
}

TEST_F(VisitorVecMat1fTest, EmptySparseMat1f)
{
    EXPECT_SIZE(1, to_(SparseMat1f()));
    EXPECT_TRUE(to_(SparseMat1f())[0].empty());

    SparseMat m(Mat1f(1, 1, 1.f));
    EXPECT_SIZE(1, to_(SparseMat1f()));
    m.clear();  // clearing is equivalent to setting elements to zero

    EXPECT_TRUE(to_(SparseMat1f())[0].empty());
}

TEST_F(VisitorVecMat1fTest, FromSparseMat1f)
{
    int N=5;

    while(N--) {

        const int MAT_ROWS = abs(randu<int>()) % 10 + 1;
        const int MAT_COLS = abs(randu<int>()) % 100 + 1;

        Mat1f m(MAT_ROWS, MAT_COLS);
        randn(m, 0.f, 100.f);

        SparseMat1f m2 = to_(SparseMat1f(m))[0];

        EXPECT_MAT_EQ(m, m2) << "Matrices are not equal";
    }
}

TEST_F(VisitorVecMat1fTest, Reset)
{
    EXPECT_NO_THROW(to_.Reset());
    to_(Mat1f(4, 3, 1.f));
    EXPECT_NO_THROW(to_.Reset());
}

//#ifndef __WITH_PCL
//    #warning "Disabling unit tests that require PCL support."
//#else // __WITH_PCL

//TEST_F(VisitorVecMat1fTest, EmptyCLoud)
//{
//    CloudXYZPtr cld(new CloudXYZ);
//    EXPECT_TRUE(to_(cld).empty());
//}

//TEST_F(VisitorVecMat1fTest, EmptyCloudSize)
//{
//    CloudXYZPtr cld(new CloudXYZ);
//    EXPECT_MAT_DIMS_EQ(to_(cld), Size2i(0, 0));
//}

//TEST_F(VisitorVecMat1fTest, FromCloud)
//{
//    Mat1f m(4, 3);
//    randn(m, 0.f, 100.f);
//    CloudXYZPtr cloud = Mat2PointCloud_<pcl::PointXYZ>(m);

//    Mat1f m2 = to_(cloud);
//    hconcat(m, Mat1f(m.rows, 1, 1), m);

//    EXPECT_MAT_EQ(m, m2);
//    EXPECT_NE(m.data, m2.data) << "Expecting deep copy. If intentionally optimized to be a shared copy, please update test.";
//}

//TEST_F(VisitorVecMat1fTest, Reset_with_cloud)
//{
//    EXPECT_NO_THROW(to_.Reset());

//    Mat1f m(4, 3);
//    randn(m, 0.f, 100.f);

//    Mat1f m2 = to_(m);
//    EXPECT_NO_THROW(to_.Reset());

//    CloudXYZPtr cloud = Mat2PointCloud_<pcl::PointXYZ>(m);
//    m2 = to_(cloud);
//    EXPECT_NO_THROW(to_.Reset());

//    m2 = to_(m);
//    EXPECT_NO_THROW(to_.Reset());
//}

//// fixtures for VecVertices

//TEST_F(VisitorVecMat1fTest, Empty_VecVertices)
//{
//    EXPECT_TRUE(to_(VecVertices()).empty());
//}

//TEST_F(VisitorVecMat1fTest, Empty_VecVertices_Size)
//{
//    EXPECT_MAT_DIMS_EQ(to_(VecVertices()), Size2i(0, 0));
//}

//TEST_F(VisitorVecMat1fTest, From_VecVertices)
//{
//    Mat1f m(4, 3);
//    for(size_t i=0; i<m.total(); i++) {

//        m(i) = static_cast<float>(randu<uint32_t>() % 256);
//    }
//    VecVertices vv = Mat2VecVertices(m.clone());

//    Mat1f m2 = to_(vv);

//    EXPECT_MAT_EQ(m, m2);
//    EXPECT_NE(m.data, m2.data) << "Expecting deep copy. If intentionally optimized to be a shared copy, please update test.";
//}

//TEST_F(VisitorVecMat1fTest, DISABLED_Reset_with_VecVertices)
//{
////    EXPECT_NO_THROW(to_.Reset());

////    Mat1f m(4, 3);
////    randn(m, 0.f, 100.f);

////    Mat1f m2 = to_(m);
////    EXPECT_NO_THROW(to_.Reset());

////    CloudXYZPtr cloud = Mat2PointCloud(m);
////    m2 = to_(cloud);
////    EXPECT_NO_THROW(to_.Reset());

////    m2 = to_(m);
////    EXPECT_NO_THROW(to_.Reset());
//}

//#endif // __WITH_PCL

} // annonymous namespace for visitors' test fixtures

<commit_msg>test visitor with cloud input<commit_after>/*M///////////////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2015, Youssef Kashef
// Copyright (c) 2015, ELM Library Project
// 3-clause BSD License
//
//M*/
/** @file test out VecMat1f visitor,
 * some fixtures may be scattered somewhat awkwardly in this file due to conditional support (e.g. PCL support)
 */
#include "elm/core/visitors/visitorvecmat1f.h"

#include "elm/core/exception.h"
#include "elm/core/pcl/cloud_.h"
#include "elm/core/pcl/vertices.h"
#include "elm/ts/ts.h"

using namespace std;
using namespace cv;
using namespace elm;

namespace {

/**
 * @brief test class around VisitorMat1f
 */
class VisitorVecMat1fTest : public ::testing::Test
{
protected:
    VisitorVecMat1f to_;    ///< test object
};

TEST_F(VisitorVecMat1fTest, EmptyVecMat1f)
{
    EXPECT_TRUE(to_(VecMat1f()).empty());
    EXPECT_TRUE(to_(VecMat1f(0)).empty());
}

TEST_F(VisitorVecMat1fTest, EmptyVecMat1f_size)
{
    EXPECT_SIZE(size_t(0), to_(VecMat1f()));
    EXPECT_SIZE(size_t(0), to_(VecMat1f(0)));
}

TEST_F(VisitorVecMat1fTest, FromVecMat1f_size)
{
    int N=5;

    VecMat1f in;
    for(int i=0; i<N; i++) {

        Mat1f m(2, 3, static_cast<float>(N-i));

        in.push_back(m);

        VecMat1f v = to_(in);
        EXPECT_SIZE(size_t(i+1), v);

        for(size_t j=0; j<v.size(); j++) {

            Mat1f m2 = v[j];

            EXPECT_MAT_EQ(m2, Mat1f(2, 3, static_cast<float>(N-j))) << "Matrices are not equal";
            EXPECT_EQ(in[j].data, m2.data) << "Mats are not pointing to the same data in memory. Expecting shared copy.";
        }
    }
}

TEST_F(VisitorVecMat1fTest, FromMat1f)
{
    int N=5;

    while(N--) {

        const int MAT_ROWS = abs(randu<int>()) % 10 + 1;
        const int MAT_COLS = abs(randu<int>()) % 100 + 1;

        Mat1f m(MAT_ROWS, MAT_COLS);
        randn(m, 0.f, 100.f);

        VecMat1f v = to_(m);

        EXPECT_SIZE(size_t(1), v);

        Mat1f m2 = v[0];

        EXPECT_MAT_EQ(m, m2) << "Matrices are not equal";
        EXPECT_EQ(m.data, m2.data) << "Mats are not pointing to the same data in memory. Expecting shared copy.";
    }
}

TEST_F(VisitorVecMat1fTest, EmptySparseMat1f)
{
    EXPECT_SIZE(1, to_(SparseMat1f()));
    EXPECT_TRUE(to_(SparseMat1f())[0].empty());

    SparseMat m(Mat1f(1, 1, 1.f));
    EXPECT_SIZE(1, to_(SparseMat1f()));
    m.clear();  // clearing is equivalent to setting elements to zero

    EXPECT_TRUE(to_(SparseMat1f())[0].empty());
}

TEST_F(VisitorVecMat1fTest, FromSparseMat1f)
{
    int N=5;

    while(N--) {

        const int MAT_ROWS = abs(randu<int>()) % 10 + 1;
        const int MAT_COLS = abs(randu<int>()) % 100 + 1;

        Mat1f m(MAT_ROWS, MAT_COLS);
        randn(m, 0.f, 100.f);

        SparseMat1f m2 = to_(SparseMat1f(m))[0];

        EXPECT_MAT_EQ(m, m2) << "Matrices are not equal";
    }
}

TEST_F(VisitorVecMat1fTest, Reset)
{
    EXPECT_NO_THROW(to_.Reset());
    to_(Mat1f(4, 3, 1.f));
    EXPECT_NO_THROW(to_.Reset());
}

#ifdef __WITH_PCL

TEST_F(VisitorVecMat1fTest, FromCLoud_empty)
{
    CloudXYZPtr cld(new CloudXYZ);
    EXPECT_TRUE(to_(cld).empty());
}

TEST_F(VisitorVecMat1fTest, FromCloud)
{
    Mat1f m(4, 3);
    randn(m, 0.f, 100.f);
    CloudXYZPtr cloud = Mat2PointCloud_<pcl::PointXYZ>(m);

    VecMat1f v = to_(cloud);
    EXPECT_SIZE(m.rows, v);

    hconcat(m, Mat1f(m.rows, 1, 1), m); // account for padding

    for(size_t i=0; i<v.size(); i++) {

        Mat1f m2 = v[i];

        EXPECT_MAT_EQ(m.row(i), m2);
        EXPECT_NE(m.data, m2.data) << "Expecting deep copy. If intentionally optimized to be a shared copy, please update test.";
    }
}

TEST_F(VisitorVecMat1fTest, Reset_with_cloud)
{
    EXPECT_NO_THROW(to_.Reset());

    Mat1f m(4, 3, 1.f);

    VecMat1f v = to_(m);
    EXPECT_NO_THROW(to_.Reset());

    CloudXYZPtr cloud = Mat2PointCloud_<pcl::PointXYZ>(m);
    v = to_(cloud);
    EXPECT_NO_THROW(to_.Reset());

    v = to_(m);
    EXPECT_NO_THROW(to_.Reset());
}

//// fixtures for VecVertices

//TEST_F(VisitorVecMat1fTest, Empty_VecVertices)
//{
//    EXPECT_TRUE(to_(VecVertices()).empty());
//}

//TEST_F(VisitorVecMat1fTest, Empty_VecVertices_Size)
//{
//    EXPECT_MAT_DIMS_EQ(to_(VecVertices()), Size2i(0, 0));
//}

//TEST_F(VisitorVecMat1fTest, From_VecVertices)
//{
//    Mat1f m(4, 3);
//    for(size_t i=0; i<m.total(); i++) {

//        m(i) = static_cast<float>(randu<uint32_t>() % 256);
//    }
//    VecVertices vv = Mat2VecVertices(m.clone());

//    Mat1f m2 = to_(vv);

//    EXPECT_MAT_EQ(m, m2);
//    EXPECT_NE(m.data, m2.data) << "Expecting deep copy. If intentionally optimized to be a shared copy, please update test.";
//}

//TEST_F(VisitorVecMat1fTest, DISABLED_Reset_with_VecVertices)
//{
////    EXPECT_NO_THROW(to_.Reset());

////    Mat1f m(4, 3);
////    randn(m, 0.f, 100.f);

////    Mat1f m2 = to_(m);
////    EXPECT_NO_THROW(to_.Reset());

////    CloudXYZPtr cloud = Mat2PointCloud(m);
////    m2 = to_(cloud);
////    EXPECT_NO_THROW(to_.Reset());

////    m2 = to_(m);
////    EXPECT_NO_THROW(to_.Reset());
//}

#endif // __WITH_PCL

} // annonymous namespace for visitors' test fixtures

<|endoftext|>


<commit_before>//! @author @TungWah @Alan
//! @date   4 Oct,2014.
//!
//! Exercise 9.45:
//! Write a funtion that takes a string representing a name and two other
//! strings representing a prefix, such as “Mr.” or “Ms.” and a suffix,
//! such as “Jr.” or “III”. Using iterators and the insert and append functions,
//! generate and return a new string with the suffix and prefix added to the
//! given name.
//!

#include <iostream>
#include <string>

//! Exercise 9.45
std::string
pre_suffix(const std::string &name, const std::string &pre, const std::string &su);

int main()
{
    std::string name("alan");
    std::cout << pre_suffix(name, "Mr.", ",Jr.");

    return 0;
}

inline std::string
pre_suffix(const std::string &name, const std::string &pre, const std::string &su)
{
    std::string ret(name);
    ret.insert(0, pre);
    ret.append(su);

    return ret;
}
<commit_msg>revert to old version and fixed #267<commit_after>//! @author @TungWah @Alan
//! @date   4 Oct,2014.
//!
//! Exercise 9.45:
//! Write a funtion that takes a string representing a name and two other
//! strings representing a prefix, such as “Mr.” or “Ms.” and a suffix,
//! such as “Jr.” or “III”. Using iterators and the insert and append functions,
//! generate and return a new string with the suffix and prefix added to the
//! given name.
//!

#include <iostream>
#include <string>

//! Exercise 9.45
std::string
pre_suffix(const std::string &name, const std::string &pre, const std::string &su);

int main()
{
    std::string name("alan");
    std::cout << pre_suffix(name, "Mr.", ",Jr.") << std::endl;

    return 0;
}

inline std::string
pre_suffix(const std::string &name, const std::string &pre, const std::string &su)
{
    auto ret = name;
    ret.insert(ret.begin(), pre.begin(), pre.end());
    ret.append(su);

    return ret;
}
<|endoftext|>


<commit_before>#include "memcache_client_pool.h"

#include "vbucket_config.h"
#include "evpp/event_loop_thread_pool.h"

namespace evmc {

thread_local std::map<std::string, MemcacheClientPtr> MemcacheClientPool::memc_clients_;

MemcacheClientPool::~MemcacheClientPool() {
}

void MemcacheClientPool::Stop(bool wait_thread_exit) {
    loop_.Stop();
    loop_pool_.Stop(wait_thread_exit);
}

static evpp::Duration reload_delay(1.0);
void MemcacheClientPool::OnReloadConfTimer() {
    DoReloadConf();
    loop_pool_.GetNextLoop()->RunAfter(reload_delay, std::bind(&MemcacheClientPool::OnReloadConfTimer, this));
}

bool MemcacheClientPool::DoReloadConf() {
    // atomic set
    VbucketConfigPtr vbconf = std::make_shared<VbucketConfig>();
    bool success = vbconf->Load(vbucket_conf_file_.c_str());

    if (success) {
        // std::atomic_store(&vbucket_config_, vbconf);
        {
            std::lock_guard<std::mutex> lock(vbucket_config_mutex_);
            vbucket_config_ = vbconf;
        }
        LOG_DEBUG << "DoReloadConf load ok, file=" << vbucket_conf_file_;
    } else {
        LOG_WARN << "DoReloadConf load err, file=" << vbucket_conf_file_;
    }

    return success;
}

VbucketConfigPtr MemcacheClientPool::vbucket_config() {
    // return std::atomic_load(&vbucket_config_);

    std::lock_guard<std::mutex> lock(vbucket_config_mutex_);
    return vbucket_config_;
}

bool MemcacheClientPool::Start() {
    if (!DoReloadConf()) {
        return false;
    }

    bool ok = loop_pool_.Start(true);
    if (!ok) {
        return false;
    }
    loop_pool_.GetNextLoop()->RunAfter(reload_delay, std::bind(&MemcacheClientPool::OnReloadConfTimer, this));

    for (int i = 0; i < loop_pool_.thread_num(); ++i) {
        evpp::EventLoop* loop = loop_pool_.GetNextLoopWithHash(i);
        memc_client_map_.push_back(MemcClientMap());
        loop->set_context(evpp::Any(&memc_client_map_.back()));
    }

    return ok;
}

MemcClientMap* MemcacheClientPool::GetMemcClientMap(int hash) {
    evpp::EventLoop* loop = loop_pool_.GetNextLoopWithHash(hash);
    return evpp::any_cast<MemcClientMap*>(loop->context());
}

void MemcacheClientPool::Set(EventLoopPtr caller_loop, const std::string& key, const std::string& value, SetCallback callback) {
    Set(caller_loop, key.c_str(), value.c_str(), value.size(), 0, 0, callback);
}
void MemcacheClientPool::Set(EventLoopPtr caller_loop, const char* key, const char* value, size_t val_len, uint32_t flags,
              uint32_t expire, SetCallback callback) {
    uint16_t vbucket = vbucket_config()->GetVbucketByKey(key, strlen(key));
    CommandPtr command(new SetCommand(caller_loop, vbucket, key, value, val_len, flags, expire, callback));

    caller_loop->RunInLoop(std::bind(&MemcacheClientPool::LaunchCommand, this, command));
}

void MemcacheClientPool::Remove(EventLoopPtr caller_loop, const char* key, RemoveCallback callback) {
    uint16_t vbucket = vbucket_config()->GetVbucketByKey(key, strlen(key));
    CommandPtr command(new RemoveCommand(caller_loop, vbucket, key, callback));

    caller_loop->RunInLoop(std::bind(&MemcacheClientPool::LaunchCommand, this, command));
}

void MemcacheClientPool::Get(EventLoopPtr caller_loop, const char* key, GetCallback callback) {
    uint16_t vbucket = vbucket_config()->GetVbucketByKey(key, strlen(key));
    CommandPtr command(new GetCommand(caller_loop, vbucket, key, callback));

    caller_loop->RunInLoop(std::bind(&MemcacheClientPool::LaunchCommand, this, command));
}

class MultiGetCollector {
public:
    MultiGetCollector(EventLoopPtr caller_loop, int count, const MultiGetCallback& cb)
            : caller_loop_(caller_loop), collect_counter_(count), callback_(cb) {}
    void Collect(const MultiGetResult& res) {
        collect_result_.code = res.code; // TODO : 部分失败时，code如何指定?
        for(auto it = res.get_result_map_.begin(); it != res.get_result_map_.end(); ++it) {
            collect_result_.get_result_map_.insert(*it);
        }
        LOG_DEBUG << "MultiGetCollector count=" << collect_counter_;
        if (--collect_counter_ <= 0) {
            if (caller_loop_) {
                caller_loop_->RunInLoop(std::bind(callback_, collect_result_));
            } else {
                callback_(collect_result_);
            }
        }
    }
private:
    EventLoopPtr caller_loop_;
    int collect_counter_;
    MultiGetResult collect_result_;
    MultiGetCallback callback_;
};
typedef std::shared_ptr<MultiGetCollector> MultiGetCollectorPtr;

void MemcacheClientPool::MultiGet(EventLoopPtr caller_loop, const std::vector<std::string>& keys, MultiGetCallback callback) {
    if (keys.size() <= 0) {
        return;
    }
    // TODO : rand() performance fix
    uint32_t thread_hash = rand();
    std::map<uint16_t, std::vector<std::string> > vbucket_keys;

    VbucketConfigPtr vbconf = vbucket_config();
    for(size_t i = 0; i < keys.size(); ++i) {
        uint16_t vbucket = vbconf->GetVbucketByKey(keys[i].c_str(), keys[i].size());
        vbucket_keys[vbucket].push_back(keys[i]);
    }
    MultiGetCollectorPtr collector(new MultiGetCollector(caller_loop, vbucket_keys.size(), callback));

    for(auto it = vbucket_keys.begin(); it != vbucket_keys.end(); ++it) {
        CommandPtr command(new MultiGetCommand(caller_loop, it->first, thread_hash, it->second,
            std::bind(&MultiGetCollector::Collect, collector, std::placeholders::_1)));
        caller_loop->RunInLoop(std::bind(&MemcacheClientPool::LaunchCommand, this, command));
    }
}

void MemcacheClientPool::OnClientConnection(const evpp::TCPConnPtr& conn, MemcacheClientPtr memc_client) {
    LOG_INFO << "OnClientConnection conn=" << conn.get() << " memc_conn=" << memc_client->conn().get();
    if (conn && conn->IsConnected()) {
        LOG_INFO << "OnClientConnection connect ok";
        CommandPtr command;
        while(command = memc_client->pop_waiting_command()) {
            memc_client->PushRunningCommand(command);
            command->Launch(memc_client);
            // command->caller_loop()->AssertInLoopThread();
        }
    } else {
        if (conn) {
            LOG_INFO << "Disconnected from " << conn->remote_addr();
        } else {
            LOG_INFO << "Connect init error";
        }

        CommandPtr command;
        while(command = memc_client->PopRunningCommand()) {
            if (command->ShouldRetry()) {
                LOG_INFO << "OnClientConnection running retry";
                // LaunchCommand(command);
                command->caller_loop()->RunInLoop(std::bind(&MemcacheClientPool::LaunchCommand, this, command));
            } else {
                command->OnError(ERR_CODE_NETWORK);
            }
        }

        while(command = memc_client->pop_waiting_command()) {
            if (command->ShouldRetry()) {
                LOG_INFO << "OnClientConnection waiting retry";
                // LaunchCommand(command);
                command->caller_loop()->RunInLoop(std::bind(&MemcacheClientPool::LaunchCommand, this, command));
            } else {
                command->OnError(ERR_CODE_NETWORK);
            }
        }
    }
}

void MemcacheClientPool::LaunchCommand(CommandPtr command) {
    LOG_INFO << "LaunchCommand";
    loop_pool_.GetNextLoopWithHash(command->thread_hash())->RunInLoop(
            std::bind(&MemcacheClientPool::DoLaunchCommand, this, command));
}

void MemcacheClientPool::DoLaunchCommand(CommandPtr command) {
    uint16_t vbucket = command->vbucket_id();
    VbucketConfigPtr vbconf = vbucket_config();
    uint16_t server_id = vbconf->SelectServerId(vbucket, command->server_id());
    if (server_id == BAD_SERVER_ID) {
         command->OnError(ERR_CODE_DISCONNECT);
         return;
    }
    command->set_server_id(server_id);
    std::string server_addr = vbconf->GetServerAddrById(server_id);
    MemcClientMap* client_map = GetMemcClientMap(command->thread_hash());
    if (client_map == NULL) {
        command->OnError(ERR_CODE_DISCONNECT);
        LOG_INFO << "DoLaunchCommand thread pool empty";
        return;
    }
    auto it = client_map->find(server_addr);

    if (it == client_map->end()) {
        evpp::TCPClient * tcp_client = new evpp::TCPClient(loop_pool_.GetNextLoopWithHash(command->thread_hash()),
                server_addr, "MemcacheBinaryClient");
        MemcacheClientPtr memc_client(new MemcacheClient(loop_pool_.GetNextLoopWithHash(command->thread_hash()),
                tcp_client, this, timeout_ms_));
        LOG_INFO << "DoLaunchCommand new tcp_client=" << tcp_client << " memc_client=" << memc_client;

        tcp_client->SetConnectionCallback(std::bind(&MemcacheClientPool::OnClientConnection, this,
                    std::placeholders::_1, memc_client));
        tcp_client->SetMessageCallback(std::bind(&MemcacheClient::OnResponseData, memc_client,
                    std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        tcp_client->Connect();

        client_map->insert(std::make_pair(server_addr, memc_client));

        memc_client->push_waiting_command(command);
        return;
    }

    if (!it->second->conn() || it->second->conn()->status() == evpp::TCPConn::kConnecting) {
        it->second->push_waiting_command(command);
    } else if (it->second->conn()->IsConnected()) {
        it->second->PushRunningCommand(command);
        command->Launch(it->second);
    } else {
        if (command->ShouldRetry()) {
            LOG_INFO << "OnClientConnection disconnect retry";
            LaunchCommand(command);
        } else {
            command->OnError(ERR_CODE_DISCONNECT);
        }
    }
}

}

<commit_msg>memc client map init fix<commit_after>#include "memcache_client_pool.h"

#include "vbucket_config.h"
#include "evpp/event_loop_thread_pool.h"

namespace evmc {

thread_local std::map<std::string, MemcacheClientPtr> MemcacheClientPool::memc_clients_;

MemcacheClientPool::~MemcacheClientPool() {
}

void MemcacheClientPool::Stop(bool wait_thread_exit) {
    loop_.Stop();
    loop_pool_.Stop(wait_thread_exit);
}

static evpp::Duration reload_delay(1.0);
void MemcacheClientPool::OnReloadConfTimer() {
    DoReloadConf();
    loop_pool_.GetNextLoop()->RunAfter(reload_delay, std::bind(&MemcacheClientPool::OnReloadConfTimer, this));
}

bool MemcacheClientPool::DoReloadConf() {
    // atomic set
    VbucketConfigPtr vbconf = std::make_shared<VbucketConfig>();
    bool success = vbconf->Load(vbucket_conf_file_.c_str());

    if (success) {
        // std::atomic_store(&vbucket_config_, vbconf);
        {
            std::lock_guard<std::mutex> lock(vbucket_config_mutex_);
            vbucket_config_ = vbconf;
        }
        LOG_DEBUG << "DoReloadConf load ok, file=" << vbucket_conf_file_;
    } else {
        LOG_WARN << "DoReloadConf load err, file=" << vbucket_conf_file_;
    }

    return success;
}

VbucketConfigPtr MemcacheClientPool::vbucket_config() {
    // return std::atomic_load(&vbucket_config_);

    std::lock_guard<std::mutex> lock(vbucket_config_mutex_);
    return vbucket_config_;
}

bool MemcacheClientPool::Start() {
    if (!DoReloadConf()) {
        return false;
    }

    bool ok = loop_pool_.Start(true);
    if (!ok) {
        return false;
    }
    loop_pool_.GetNextLoop()->RunAfter(reload_delay, std::bind(&MemcacheClientPool::OnReloadConfTimer, this));

    for (int i = 0; i < loop_pool_.thread_num(); ++i) {
        memc_client_map_.push_back(MemcClientMap());
    }

    // 须先构造memc_client_map_数组，再各个元素取地址，否则地址不稳定，可能崩溃
    for (int i = 0; i < loop_pool_.thread_num(); ++i) {
        evpp::EventLoop* loop = loop_pool_.GetNextLoopWithHash(i);
        loop->set_context(evpp::Any(&memc_client_map_[i]));
    }

    return ok;
}

MemcClientMap* MemcacheClientPool::GetMemcClientMap(int hash) {
    evpp::EventLoop* loop = loop_pool_.GetNextLoopWithHash(hash);
    return evpp::any_cast<MemcClientMap*>(loop->context());
}

void MemcacheClientPool::Set(EventLoopPtr caller_loop, const std::string& key, const std::string& value, SetCallback callback) {
    Set(caller_loop, key.c_str(), value.c_str(), value.size(), 0, 0, callback);
}
void MemcacheClientPool::Set(EventLoopPtr caller_loop, const char* key, const char* value, size_t val_len, uint32_t flags,
              uint32_t expire, SetCallback callback) {
    uint16_t vbucket = vbucket_config()->GetVbucketByKey(key, strlen(key));
    CommandPtr command(new SetCommand(caller_loop, vbucket, key, value, val_len, flags, expire, callback));

    caller_loop->RunInLoop(std::bind(&MemcacheClientPool::LaunchCommand, this, command));
}

void MemcacheClientPool::Remove(EventLoopPtr caller_loop, const char* key, RemoveCallback callback) {
    uint16_t vbucket = vbucket_config()->GetVbucketByKey(key, strlen(key));
    CommandPtr command(new RemoveCommand(caller_loop, vbucket, key, callback));

    caller_loop->RunInLoop(std::bind(&MemcacheClientPool::LaunchCommand, this, command));
}

void MemcacheClientPool::Get(EventLoopPtr caller_loop, const char* key, GetCallback callback) {
    uint16_t vbucket = vbucket_config()->GetVbucketByKey(key, strlen(key));
    CommandPtr command(new GetCommand(caller_loop, vbucket, key, callback));

    caller_loop->RunInLoop(std::bind(&MemcacheClientPool::LaunchCommand, this, command));
}

class MultiGetCollector {
public:
    MultiGetCollector(EventLoopPtr caller_loop, int count, const MultiGetCallback& cb)
            : caller_loop_(caller_loop), collect_counter_(count), callback_(cb) {}
    void Collect(const MultiGetResult& res) {
        collect_result_.code = res.code; // TODO : 部分失败时，code如何指定?
        for(auto it = res.get_result_map_.begin(); it != res.get_result_map_.end(); ++it) {
            collect_result_.get_result_map_.insert(*it);
        }
        LOG_DEBUG << "MultiGetCollector count=" << collect_counter_;
        if (--collect_counter_ <= 0) {
            if (caller_loop_) {
                caller_loop_->RunInLoop(std::bind(callback_, collect_result_));
            } else {
                callback_(collect_result_);
            }
        }
    }
private:
    EventLoopPtr caller_loop_;
    int collect_counter_;
    MultiGetResult collect_result_;
    MultiGetCallback callback_;
};
typedef std::shared_ptr<MultiGetCollector> MultiGetCollectorPtr;

void MemcacheClientPool::MultiGet(EventLoopPtr caller_loop, const std::vector<std::string>& keys, MultiGetCallback callback) {
    if (keys.size() <= 0) {
        return;
    }
    // TODO : rand() performance fix
    uint32_t thread_hash = rand();
    std::map<uint16_t, std::vector<std::string> > vbucket_keys;

    VbucketConfigPtr vbconf = vbucket_config();
    for(size_t i = 0; i < keys.size(); ++i) {
        uint16_t vbucket = vbconf->GetVbucketByKey(keys[i].c_str(), keys[i].size());
        vbucket_keys[vbucket].push_back(keys[i]);
    }
    MultiGetCollectorPtr collector(new MultiGetCollector(caller_loop, vbucket_keys.size(), callback));

    for(auto it = vbucket_keys.begin(); it != vbucket_keys.end(); ++it) {
        CommandPtr command(new MultiGetCommand(caller_loop, it->first, thread_hash, it->second,
            std::bind(&MultiGetCollector::Collect, collector, std::placeholders::_1)));
        caller_loop->RunInLoop(std::bind(&MemcacheClientPool::LaunchCommand, this, command));
    }
}

void MemcacheClientPool::OnClientConnection(const evpp::TCPConnPtr& conn, MemcacheClientPtr memc_client) {
    LOG_INFO << "OnClientConnection conn=" << conn.get() << " memc_conn=" << memc_client->conn().get();
    if (conn && conn->IsConnected()) {
        LOG_INFO << "OnClientConnection connect ok";
        CommandPtr command;
        while(command = memc_client->pop_waiting_command()) {
            memc_client->PushRunningCommand(command);
            command->Launch(memc_client);
            // command->caller_loop()->AssertInLoopThread();
        }
    } else {
        if (conn) {
            LOG_INFO << "Disconnected from " << conn->remote_addr();
        } else {
            LOG_INFO << "Connect init error";
        }

        CommandPtr command;
        while(command = memc_client->PopRunningCommand()) {
            if (command->ShouldRetry()) {
                LOG_INFO << "OnClientConnection running retry";
                // LaunchCommand(command);
                command->caller_loop()->RunInLoop(std::bind(&MemcacheClientPool::LaunchCommand, this, command));
            } else {
                command->OnError(ERR_CODE_NETWORK);
            }
        }

        while(command = memc_client->pop_waiting_command()) {
            if (command->ShouldRetry()) {
                LOG_INFO << "OnClientConnection waiting retry";
                // LaunchCommand(command);
                command->caller_loop()->RunInLoop(std::bind(&MemcacheClientPool::LaunchCommand, this, command));
            } else {
                command->OnError(ERR_CODE_NETWORK);
            }
        }
    }
}

void MemcacheClientPool::LaunchCommand(CommandPtr command) {
    LOG_INFO << "LaunchCommand";
    loop_pool_.GetNextLoopWithHash(command->thread_hash())->RunInLoop(
            std::bind(&MemcacheClientPool::DoLaunchCommand, this, command));
}

void MemcacheClientPool::DoLaunchCommand(CommandPtr command) {
    uint16_t vbucket = command->vbucket_id();
    VbucketConfigPtr vbconf = vbucket_config();
    uint16_t server_id = vbconf->SelectServerId(vbucket, command->server_id());
    if (server_id == BAD_SERVER_ID) {
         command->OnError(ERR_CODE_DISCONNECT);
         return;
    }
    command->set_server_id(server_id);
    std::string server_addr = vbconf->GetServerAddrById(server_id);
    MemcClientMap* client_map = GetMemcClientMap(command->thread_hash());
    if (client_map == NULL) {
        command->OnError(ERR_CODE_DISCONNECT);
        LOG_INFO << "DoLaunchCommand thread pool empty";
        return;
    }
    auto it = client_map->find(server_addr);

    if (it == client_map->end()) {
        evpp::TCPClient * tcp_client = new evpp::TCPClient(loop_pool_.GetNextLoopWithHash(command->thread_hash()),
                server_addr, "MemcacheBinaryClient");
        MemcacheClientPtr memc_client(new MemcacheClient(loop_pool_.GetNextLoopWithHash(command->thread_hash()),
                tcp_client, this, timeout_ms_));
        LOG_INFO << "DoLaunchCommand new tcp_client=" << tcp_client << " memc_client=" << memc_client;

        tcp_client->SetConnectionCallback(std::bind(&MemcacheClientPool::OnClientConnection, this,
                    std::placeholders::_1, memc_client));
        tcp_client->SetMessageCallback(std::bind(&MemcacheClient::OnResponseData, memc_client,
                    std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        tcp_client->Connect();

        client_map->insert(std::make_pair(server_addr, memc_client));

        memc_client->push_waiting_command(command);
        return;
    }

    if (!it->second->conn() || it->second->conn()->status() == evpp::TCPConn::kConnecting) {
        it->second->push_waiting_command(command);
    } else if (it->second->conn()->IsConnected()) {
        it->second->PushRunningCommand(command);
        command->Launch(it->second);
    } else {
        if (command->ShouldRetry()) {
            LOG_INFO << "OnClientConnection disconnect retry";
            LaunchCommand(command);
        } else {
            command->OnError(ERR_CODE_DISCONNECT);
        }
    }
}

}

<|endoftext|>


<commit_before>// Time:  O(n)
// Space: O(1)

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> res;
        TreeNode *prev = nullptr;
        TreeNode *cur = root;
        while (cur) {
            if (!cur->left) {
                res.emplace_back(cur->val);
                prev = cur;
                cur = cur->right;
            } else {
                TreeNode *node = cur->left;
                while (node->right && node->right != cur) {
                    node = node->right;
                }
                if (!node->right) {
                    node->right = cur;
                    cur = cur->left;
                } else {
                    res.emplace_back(cur->val);
                    prev = cur;
                    node->right = nullptr;
                    cur = cur->right;
                }
            }
        }
        return res;
    }
};

// Time:  O(n)
// Space: O(h)
class Solution2 {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> res;
        stack<pair<TreeNode *, bool>> s;
        s.emplace(root, false);
        bool visited;
        while (!s.empty()) {
            tie(root, visited) = s.top();
            s.pop();
            if (root == nullptr) {
                continue;
            }
            if (visited) {
                res.emplace_back(root->val);
            } else {
                s.emplace(root->right, false);
                s.emplace(root, true);
                s.emplace(root->left, false);
            }
        }
        return res;
    }
};
<commit_msg>Update binary-tree-inorder-traversal.cpp<commit_after>// Time:  O(n)
// Space: O(1)

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> res;
        TreeNode *prev = nullptr;
        TreeNode *cur = root;
        while (cur) {
            if (!cur->left) {
                res.emplace_back(cur->val);
                prev = cur;
                cur = cur->right;
            } else {
                TreeNode *node = cur->left;
                while (node->right && node->right != cur) {
                    node = node->right;
                }
                if (!node->right) {
                    node->right = cur;
                    cur = cur->left;
                } else {
                    res.emplace_back(cur->val);
                    prev = cur;
                    node->right = nullptr;
                    cur = cur->right;
                }
            }
        }
        return res;
    }
};

// Time:  O(n)
// Space: O(h)
class Solution2 {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> res;
        stack<pair<TreeNode *, bool>> s;
        s.emplace(root, false);
        while (!s.empty()) {
            bool visited;
            tie(root, visited) = s.top();
            s.pop();
            if (root == nullptr) {
                continue;
            }
            if (visited) {
                res.emplace_back(root->val);
            } else {
                s.emplace(root->right, false);
                s.emplace(root, true);
                s.emplace(root->left, false);
            }
        }
        return res;
    }
};
<|endoftext|>