<commit_before>
#include <sched.h>

#include <algorithm>

#include "absl/strings/match.h"
#include "kernel/ghost_uapi.h"
#include "lib/base.h"
#include "lib/ghost.h"
#include "lib/logging.h"

#include "schedulers/netPreemptDelay/cgroup_watcher.h"

#include <string>
#include <fstream>
#include <streambuf>
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;
namespace ghost {
namespace cgroup_watcher {

std::string GetTaskName(pid_t tid) {
  const std::string comm_path = absl::StrFormat("/proc/%d/comm", tid);

  std::ifstream t(comm_path);
  std::string contents((std::istreambuf_iterator<char>(t)),
               std::istreambuf_iterator<char>());
  return contents;
}

namespace {
// Return `true` if the thread with thread identifier `tid` was moved to the
// `scheduler` scheduling class and `false` otherwise.
bool MoveToScheduler(pid_t tid, int scheduler) {
  if (sched_getscheduler(tid) == scheduler) {
    // The task is already in the `scheduler` scheduling class.
    return false;
  }

  int ret = 0;
  if (scheduler == SCHED_GHOST) {
    ret = SchedTaskEnterGhost(tid);
  } else {
    sched_param param = {0};
    ret = sched_setscheduler(tid, scheduler, &param);
  }

  if (ret == 0) {
    // Trust but verify.
    CHECK_EQ(sched_getscheduler(tid), scheduler);
    return true;
  }

  // The `setscheduler` operation could fail if thread `tid` is dying, which
  // is not an error case.
  //PCHECK(ret == -1 && errno == ESRCH);
  return false;
}

// Moves the tasks in the control group at `control_group_path` to ghOSt
// and returns the number moved.
int MoveTasksToGhost(absl::string_view control_group_path,
                     absl::string_view name_pattern) {
  constexpr absl::string_view kTasksFileName = "tasks";

  fs::path dir(control_group_path);
  fs::path file (kTasksFileName);
  std::string tasks_path = dir / file;

  // If no tasks file, the container is probably on its way out.
  if( !fs::exists(tasks_path) ){
    return 0;
  }

  // The file at `tasks_path` contains one TID per line.
  int moved = 0;
  std::ifstream file_to_read(tasks_path);
  std::string line;
  while (std::getline(file_to_read, line)){
    pid_t tid = std::stoi(line);
    std::string task_name = GetTaskName(tid);

    if (name_pattern == "*" || absl::StrContains(task_name, name_pattern)) {
      if (MoveToScheduler(tid, SCHED_GHOST)) {
        ++moved;
        absl::FPrintF(stderr, "Moved task %s under %s\n",
                      task_name, control_group_path);

	std::string task_name = GetTaskName(tid);
	std::string task_added = absl::StrFormat("Moved task %s pid=%d under %s\n",
                       				task_name, tid, control_group_path);
        std::ofstream ghost_log;
        ghost_log.open("/tmp/logging", std::ios_base::app);
        ghost_log << task_added;
        ghost_log.close();

      }
    }
  }
  return moved;
}

// Moves tasks in the control group at `path` to ghOSt (if specified by the
// control group) and then recursively checks all sub-cgroups.
//
// This is the recursive helper method for `ScrapeCgroups`.
//
// Returns the number of tasks moved to ghOSt.
int ScrapeDirectory(absl::string_view skipdir,
                    absl::string_view path,
                    const absl::flat_hash_map<std::string, std::string>&
                                                containers_to_tasks_rules) {
  if (skipdir == path) {
    return 0;
  }

  int moved = 0;
  absl::string_view cgroup_name = path.substr(kCgroupPathPrefix.size());

  if (containers_to_tasks_rules.contains(cgroup_name)) {
    const std::string& task_name_pattern =
        containers_to_tasks_rules.find(cgroup_name)->second;
    moved += MoveTasksToGhost(path, task_name_pattern);
  }

  for (const auto& [cgroup_pattern, task_name_pattern] :
           containers_to_tasks_rules) {
    if (absl::StrContains(cgroup_name, cgroup_pattern)) {
      moved += MoveTasksToGhost(path, task_name_pattern);
    }
  }

  // Recurse on all subdirectories.
//  std::vector<std::pair<std::string, FileStat>> results;
//  CHECK_OK(file::MatchAndStat(file::JoinPath(path, "*"), &results, file::Defaults()));
//  for (const auto& [child_path, stat] : results) {
//    if (stat.IsDirectory()) {
//      moved += ScrapeDirectory(skipdir, child_path, containers_to_tasks_rules);
//    }
//  }

  for (const auto & file : fs::directory_iterator(path)){
    absl::string_view child_path = file.path().c_str();
    if(fs::is_directory(file.path()))
      moved += ScrapeDirectory(skipdir, child_path, containers_to_tasks_rules);
  }

  return moved;
}
}  // namespace

int ScrapeCgroups(absl::string_view skipdir,
                  absl::flat_hash_map<std::string, std::string>&
                                                containers_to_tasks_rules) {
  return ScrapeDirectory(skipdir, kCgroupPathPrefix, containers_to_tasks_rules);
}

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

#include <sched.h>

#include <algorithm>

#include "absl/strings/match.h"
#include "kernel/ghost_uapi.h"
#include "lib/base.h"
#include "lib/ghost.h"
#include "lib/logging.h"

#include "schedulers/netPreemptDelay/cgroup_watcher.h"

#include <string>
#include <fstream>
#include <streambuf>
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;
namespace ghost {
namespace cgroup_watcher {

std::string GetTaskName(pid_t tid) {
  const std::string comm_path = absl::StrFormat("/proc/%d/comm", tid);

  std::ifstream t(comm_path);
  std::string contents((std::istreambuf_iterator<char>(t)),
               std::istreambuf_iterator<char>());
  return contents;
}

namespace {
// Return `true` if the thread with thread identifier `tid` was moved to the
// `scheduler` scheduling class and `false` otherwise.
bool MoveToScheduler(pid_t tid, int scheduler) {
  if (sched_getscheduler(tid) == scheduler) {
    // The task is already in the `scheduler` scheduling class.
    return false;
  }

  int ret = 0;
  if (scheduler == SCHED_GHOST) {
    ret = SchedTaskEnterGhost(tid);
  } else {
    sched_param param = {0};
    ret = sched_setscheduler(tid, scheduler, &param);
  }

  if (ret == 0) {
    // Trust but verify.
    CHECK_EQ(sched_getscheduler(tid), scheduler);
    return true;
  }

  // The `setscheduler` operation could fail if thread `tid` is dying, which
  // is not an error case.
  //PCHECK(ret == -1 && errno == ESRCH);
  return false;
}

// Moves the tasks in the control group at `control_group_path` to ghOSt
// and returns the number moved.
int MoveTasksToGhost(absl::string_view control_group_path,
                     absl::string_view name_pattern) {
  constexpr absl::string_view kTasksFileName = "tasks";

  fs::path dir(control_group_path);
  fs::path file (kTasksFileName);
  std::string tasks_path = dir / file;

  // If no tasks file, the container is probably on its way out.
  if( !fs::exists(tasks_path) ){
    return 0;
  }

  // The file at `tasks_path` contains one TID per line.
  int moved = 0;
  std::ifstream file_to_read(tasks_path);
  std::string line;
  while (std::getline(file_to_read, line)){
    pid_t tid = std::stoi(line);
    std::string task_name = GetTaskName(tid);

    if (name_pattern == "*" || absl::StrContains(task_name, name_pattern)) {
      if (MoveToScheduler(tid, SCHED_GHOST)) {
        ++moved;
        absl::FPrintF(stderr, "Moved task %s under %s\n",
                      task_name, control_group_path);

	std::string task_name = GetTaskName(tid);
	std::string task_added = absl::StrFormat("Moved task %s pid=%d under %s\n",
                       				task_name, tid, control_group_path);
        std::ofstream ghost_log;
        ghost_log.open("/tmp/logging", std::ios_base::app);
        ghost_log << task_added;
        ghost_log.close();

      }
    }
  }
  return moved;
}

// Moves tasks in the control group at `path` to ghOSt (if specified by the
// control group) and then recursively checks all sub-cgroups.
//
// This is the recursive helper method for `ScrapeCgroups`.
//
// Returns the number of tasks moved to ghOSt.
int ScrapeDirectory(absl::string_view skipdir,
                    absl::string_view path,
                    const absl::flat_hash_map<std::string, std::string>&
                                                containers_to_tasks_rules) {
  if (skipdir == path) {
    return 0;
  }

  int moved = 0;
  absl::string_view cgroup_name = path.substr(kCgroupPathPrefix.size());

  if (containers_to_tasks_rules.contains(cgroup_name)) {
    const std::string& task_name_pattern =
        containers_to_tasks_rules.find(cgroup_name)->second;
    moved += MoveTasksToGhost(path, task_name_pattern);
  }

  for (const auto& [cgroup_pattern, task_name_pattern] :
           containers_to_tasks_rules) {
    if (absl::StrContains(cgroup_name, cgroup_pattern)) {
      moved += MoveTasksToGhost(path, task_name_pattern);
    }
  }

  // Recurse on all subdirectories.
//  std::vector<std::pair<std::string, FileStat>> results;
//  CHECK_OK(file::MatchAndStat(file::JoinPath(path, "*"), &results, file::Defaults()));
//  for (const auto& [child_path, stat] : results) {
//    if (stat.IsDirectory()) {
//      moved += ScrapeDirectory(skipdir, child_path, containers_to_tasks_rules);
//    }
//  }

  for (const auto & file : fs::directory_iterator(path)){
    absl::string_view child_path = file.path().c_str();
    if(fs::is_directory(file.path()))
      moved += ScrapeDirectory(skipdir, child_path, containers_to_tasks_rules);
  }

  return moved;
}
}  // namespace

int ScrapeCgroups(absl::string_view skipdir,
                  absl::flat_hash_map<std::string, std::string>&
                                                containers_to_tasks_rules) {
  return ScrapeDirectory(skipdir, kCgroupPathPrefix, containers_to_tasks_rules);
}

}  // namespace cgroup_watcher
}  // namespace ghost
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of meegotouch-controlpanelapplets.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include "ut_warrantyapplet.h"
#include "warrantyapplet.h"
#include "warrantywidget.h"

#include <dcpwidgettypes.h>

#include <mdesktopentry.h>
#include <MApplication>
#include <MAction>
#include <QVector>

#define DEBUG
#include "../../src/debug.h"

/******************************************************************************
 * Ut_WarrantyApplet implementation.
 */
void
Ut_WarrantyApplet::init()
{
}

void
Ut_WarrantyApplet::cleanup()
{
}

static int argc = 1;
static char *app_name = (char*) "./Ut_WarrantyApplet";

void
Ut_WarrantyApplet::initTestCase()
{
    m_App = new MApplication (argc, &app_name);
    m_Applet = new WarrantyApplet;

    QVERIFY (!m_Applet->m_MainWidget);
    m_Applet->init ();

    /*
     * Testing if the widget is not created yet.
     */
    QVERIFY (!m_Applet->m_MainWidget);
}

void
Ut_WarrantyApplet::cleanupTestCase()
{
    delete m_Applet;
    m_App->deleteLater ();
}

void
Ut_WarrantyApplet::testTitle ()
{
    QString title = m_Applet->title();
    QVERIFY (!title.isEmpty());
}

void
Ut_WarrantyApplet::testConstructWidget ()
{
    DcpWidget *widget;
    bool       backAccepted;

    /*
     * Testing if the applet creates a widget the first time.
     */
    widget = m_Applet->constructWidget (0);
    QVERIFY (widget);
    QVERIFY (m_Applet->m_MainWidget == widget);

    /*
     * Testing if the widget accepts the back. Our applets always accept back.
     */
    backAccepted = widget->back();
    QVERIFY (backAccepted);

    /*
     * Testing if the applet knows about the destruction of the widget.
     */
    delete widget;
    QVERIFY (!m_Applet->m_MainWidget);
}

void
Ut_WarrantyApplet::testMenuItems ()
{
    QVector<MAction*> items = m_Applet->viewMenuItems ();

    QVERIFY (items.size() == 0);
}

QTEST_APPLESS_MAIN(Ut_WarrantyApplet)

<commit_msg>Fixes: a compilation error in ut_warrantyapplet<commit_after>/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of meegotouch-controlpanelapplets.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include "ut_warrantyapplet.h"
#include "warrantyapplet.h"
#include "warrantywidget.h"

#include <dcpwidgettypes.h>
#include <dcpstylablewidget.h>

#include <mdesktopentry.h>
#include <MApplication>
#include <MAction>
#include <QVector>

#define DEBUG
#include "../../src/debug.h"

/******************************************************************************
 * Ut_WarrantyApplet implementation.
 */
void
Ut_WarrantyApplet::init()
{
}

void
Ut_WarrantyApplet::cleanup()
{
}

static int argc = 1;
static char *app_name = (char*) "./Ut_WarrantyApplet";

void
Ut_WarrantyApplet::initTestCase()
{
    m_App = new MApplication (argc, &app_name);
    m_Applet = new WarrantyApplet;

    QVERIFY (!m_Applet->m_MainWidget);
    m_Applet->init ();

    /*
     * Testing if the widget is not created yet.
     */
    QVERIFY (!m_Applet->m_MainWidget);
}

void
Ut_WarrantyApplet::cleanupTestCase()
{
    delete m_Applet;
    m_App->deleteLater ();
}

void
Ut_WarrantyApplet::testTitle ()
{
    QString title = m_Applet->title();
    QVERIFY (!title.isEmpty());
}

void
Ut_WarrantyApplet::testConstructWidget ()
{
    DcpStylableWidget   *widget;
    bool                 backAccepted;

    /*
     * Testing if the applet creates a widget the first time.
     */
    widget = m_Applet->constructStylableWidget (0);
    QVERIFY (widget);
    QVERIFY (m_Applet->m_MainWidget == widget);

    /*
     * Testing if the widget accepts the back. Our applets always accept back.
     */
    backAccepted = widget->back();
    QVERIFY (backAccepted);

    /*
     * Testing if the applet knows about the destruction of the widget.
     */
    delete widget;
    QVERIFY (!m_Applet->m_MainWidget);
}

void
Ut_WarrantyApplet::testMenuItems ()
{
    QVector<MAction*> items = m_Applet->viewMenuItems ();

    QVERIFY (items.size() == 0);
}

QTEST_APPLESS_MAIN(Ut_WarrantyApplet)

<|endoftext|>


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

#include "modules/common/adapters/adapter_manager.h"

#include "modules/common/adapters/adapter_gflags.h"
#include "modules/common/util/util.h"

namespace apollo {
namespace common {
namespace adapter {

AdapterManager::AdapterManager() {}

void AdapterManager::Observe() {
  for (const auto observe : instance()->observers_) {
    observe();
  }
}

bool AdapterManager::Initialized() {
  return instance()->initialized_;
}

void AdapterManager::Reset() {
  instance()->initialized_ = false;
}

void AdapterManager::Init(const std::string &adapter_config_filename) {
  // Parse config file
  AdapterManagerConfig configs;
  CHECK(util::GetProtoFromFile(adapter_config_filename, &configs))
      << "Unable to parse adapter config file " << adapter_config_filename;
  AINFO << "Init AdapterManger config:" << configs.DebugString();
  Init(configs);
}

void AdapterManager::Init(const AdapterManagerConfig &configs) {
  if (Initialized()) {
    return;
  }

  instance()->initialized_ = true;
  if (configs.is_ros()) {
    instance()->node_handle_.reset(new ros::NodeHandle());
  }

  for (const auto &config : configs.config()) {
    switch (config.type()) {
      case AdapterConfig::POINT_CLOUD:
        EnablePointCloud(FLAGS_pointcloud_topic, config.mode(),
                         config.message_history_limit());
        break;
      case AdapterConfig::GPS:
        EnableGps(FLAGS_gps_topic, config.mode(),
                  config.message_history_limit());
        break;
      case AdapterConfig::IMU:
        EnableImu(FLAGS_imu_topic, config.mode(),
                  config.message_history_limit());
        break;
      case AdapterConfig::CHASSIS:
        EnableChassis(FLAGS_chassis_topic, config.mode(),
                      config.message_history_limit());
        break;
      case AdapterConfig::LOCALIZATION:
        EnableLocalization(FLAGS_localization_topic, config.mode(),
                           config.message_history_limit());
        break;
      case AdapterConfig::PERCEPTION_OBSTACLES:
        EnablePerceptionObstacles(FLAGS_perception_obstacle_topic,
                                  config.mode(),
                                  config.message_history_limit());
        break;
      case AdapterConfig::TRAFFIC_LIGHT_DETECTION:
        EnableTrafficLightDetection(FLAGS_traffic_light_detection_topic,
                                    config.mode(),
                                    config.message_history_limit());
      case AdapterConfig::PAD:
        EnablePad(FLAGS_pad_topic, config.mode(),
                  config.message_history_limit());
        break;
      case AdapterConfig::CONTROL_COMMAND:
        EnableControlCommand(FLAGS_control_command_topic, config.mode(),
                             config.message_history_limit());
        break;
      case AdapterConfig::ROUTING_REQUEST:
        EnableRoutingRequest(FLAGS_routing_request_topic, config.mode(),
                             config.message_history_limit());
        break;
      case AdapterConfig::ROUTING_RESPONSE:
        EnableRoutingResponse(FLAGS_routing_response_topic, config.mode(),
                              config.message_history_limit());
        break;
      case AdapterConfig::PLANNING_TRAJECTORY:
        EnablePlanning(FLAGS_planning_trajectory_topic, config.mode(),
                       config.message_history_limit());
        break;
      case AdapterConfig::PREDICTION:
        EnablePrediction(FLAGS_prediction_topic, config.mode(),
                         config.message_history_limit());
        break;
      case AdapterConfig::MONITOR:
        EnableMonitor(FLAGS_monitor_topic, config.mode(),
                      config.message_history_limit());
        break;
      case AdapterConfig::CHASSIS_DETAIL:
        EnableChassisDetail(FLAGS_chassis_detail_topic, config.mode(),
                            config.message_history_limit());
        break;
      case AdapterConfig::RELATIVE_ODOMETRY:
        EnableRelativeOdometry(FLAGS_relative_odometry_topic, config.mode(),
                               config.message_history_limit());
        break;
      case AdapterConfig::INS_STAT:
        EnableInsStat(FLAGS_ins_stat_topic, config.mode(),
                      config.message_history_limit());
        break;
      case AdapterConfig::HMI_COMMAND:
        EnableHMICommand(FLAGS_hmi_command_topic, config.mode(),
                         config.message_history_limit());
        break;
      case AdapterConfig::MOBILEYE:
        EnableMobileye(FLAGS_mobileye_topic, config.mode(),
                       config.message_history_limit());
        break;
      case AdapterConfig::DELPHIESR:
        EnableDelphiESR(FLAGS_delphi_esr_topic, config.mode(),
                        config.message_history_limit());
        break;
      case AdapterConfig::COMPRESSED_IMAGE:
        EnableCompressedImage(FLAGS_compressed_image_topic, config.mode(),
                              config.message_history_limit());
        break;
      case AdapterConfig::HMI_STATUS:
        EnableHMIStatus(FLAGS_hmi_status_topic, config.mode(),
                        config.message_history_limit());
        break;
      default:
        AERROR << "Unknown adapter config type!";
        break;
    }
  }
}

}  // namespace adapter
}  // namespace common
}  // namespace apollo
<commit_msg>Common: Fix reset (#920)<commit_after>/******************************************************************************
 * Copyright 2017 The Apollo Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *****************************************************************************/

#include "modules/common/adapters/adapter_manager.h"

#include "modules/common/adapters/adapter_gflags.h"
#include "modules/common/util/util.h"

namespace apollo {
namespace common {
namespace adapter {

AdapterManager::AdapterManager() {}

void AdapterManager::Observe() {
  for (const auto observe : instance()->observers_) {
    observe();
  }
}

bool AdapterManager::Initialized() {
  return instance()->initialized_;
}

void AdapterManager::Reset() {
  instance()->initialized_ = false;
  instance()->observers_.clear();
}

void AdapterManager::Init(const std::string &adapter_config_filename) {
  // Parse config file
  AdapterManagerConfig configs;
  CHECK(util::GetProtoFromFile(adapter_config_filename, &configs))
      << "Unable to parse adapter config file " << adapter_config_filename;
  AINFO << "Init AdapterManger config:" << configs.DebugString();
  Init(configs);
}

void AdapterManager::Init(const AdapterManagerConfig &configs) {
  if (Initialized()) {
    return;
  }

  instance()->initialized_ = true;
  if (configs.is_ros()) {
    instance()->node_handle_.reset(new ros::NodeHandle());
  }

  for (const auto &config : configs.config()) {
    switch (config.type()) {
      case AdapterConfig::POINT_CLOUD:
        EnablePointCloud(FLAGS_pointcloud_topic, config.mode(),
                         config.message_history_limit());
        break;
      case AdapterConfig::GPS:
        EnableGps(FLAGS_gps_topic, config.mode(),
                  config.message_history_limit());
        break;
      case AdapterConfig::IMU:
        EnableImu(FLAGS_imu_topic, config.mode(),
                  config.message_history_limit());
        break;
      case AdapterConfig::CHASSIS:
        EnableChassis(FLAGS_chassis_topic, config.mode(),
                      config.message_history_limit());
        break;
      case AdapterConfig::LOCALIZATION:
        EnableLocalization(FLAGS_localization_topic, config.mode(),
                           config.message_history_limit());
        break;
      case AdapterConfig::PERCEPTION_OBSTACLES:
        EnablePerceptionObstacles(FLAGS_perception_obstacle_topic,
                                  config.mode(),
                                  config.message_history_limit());
        break;
      case AdapterConfig::TRAFFIC_LIGHT_DETECTION:
        EnableTrafficLightDetection(FLAGS_traffic_light_detection_topic,
                                    config.mode(),
                                    config.message_history_limit());
      case AdapterConfig::PAD:
        EnablePad(FLAGS_pad_topic, config.mode(),
                  config.message_history_limit());
        break;
      case AdapterConfig::CONTROL_COMMAND:
        EnableControlCommand(FLAGS_control_command_topic, config.mode(),
                             config.message_history_limit());
        break;
      case AdapterConfig::ROUTING_REQUEST:
        EnableRoutingRequest(FLAGS_routing_request_topic, config.mode(),
                             config.message_history_limit());
        break;
      case AdapterConfig::ROUTING_RESPONSE:
        EnableRoutingResponse(FLAGS_routing_response_topic, config.mode(),
                              config.message_history_limit());
        break;
      case AdapterConfig::PLANNING_TRAJECTORY:
        EnablePlanning(FLAGS_planning_trajectory_topic, config.mode(),
                       config.message_history_limit());
        break;
      case AdapterConfig::PREDICTION:
        EnablePrediction(FLAGS_prediction_topic, config.mode(),
                         config.message_history_limit());
        break;
      case AdapterConfig::MONITOR:
        EnableMonitor(FLAGS_monitor_topic, config.mode(),
                      config.message_history_limit());
        break;
      case AdapterConfig::CHASSIS_DETAIL:
        EnableChassisDetail(FLAGS_chassis_detail_topic, config.mode(),
                            config.message_history_limit());
        break;
      case AdapterConfig::RELATIVE_ODOMETRY:
        EnableRelativeOdometry(FLAGS_relative_odometry_topic, config.mode(),
                               config.message_history_limit());
        break;
      case AdapterConfig::INS_STAT:
        EnableInsStat(FLAGS_ins_stat_topic, config.mode(),
                      config.message_history_limit());
        break;
      case AdapterConfig::HMI_COMMAND:
        EnableHMICommand(FLAGS_hmi_command_topic, config.mode(),
                         config.message_history_limit());
        break;
      case AdapterConfig::MOBILEYE:
        EnableMobileye(FLAGS_mobileye_topic, config.mode(),
                       config.message_history_limit());
        break;
      case AdapterConfig::DELPHIESR:
        EnableDelphiESR(FLAGS_delphi_esr_topic, config.mode(),
                        config.message_history_limit());
        break;
      case AdapterConfig::COMPRESSED_IMAGE:
        EnableCompressedImage(FLAGS_compressed_image_topic, config.mode(),
                              config.message_history_limit());
        break;
      case AdapterConfig::HMI_STATUS:
        EnableHMIStatus(FLAGS_hmi_status_topic, config.mode(),
                        config.message_history_limit());
        break;
      default:
        AERROR << "Unknown adapter config type!";
        break;
    }
  }
}

}  // namespace adapter
}  // namespace common
}  // namespace apollo
<|endoftext|>


<commit_before>/*
Copyright 2017 VMware, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

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

#include "tableKeyNames.h"

namespace P4 {

class KeyNameGenerator : public Inspector {
    std::map<const IR::Expression*, cstring> name;

 public:
    KeyNameGenerator() { setName("KeyNameGenerator"); }

    void error(const IR::Expression* expression) {
        ::error("%1%: Complex key expression requires a @name annotation", expression);
    }

    void postorder(const IR::Expression* expression) override { error(expression); }

    void postorder(const IR::PathExpression* expression) override {
        name.emplace(expression, expression->path->toString());
    }

    void postorder(const IR::Member* expression) override {
        cstring n = getName(expression->expr);
        if (n.isNullOrEmpty())
            return;
        name.emplace(expression, n + "." + expression->member);
    }

    void postorder(const IR::ArrayIndex* expression) override {
        cstring l = getName(expression->left);
        cstring r = getName(expression->right);
        if (l.isNullOrEmpty() || r.isNullOrEmpty())
            return;
        name.emplace(expression, l + "[" + r + "]");
    }

    void postorder(const IR::Constant* expression) override {
        name.emplace(expression, expression->toString());
    }

    void postorder(const IR::Slice* expression) override {
        cstring e0 = getName(expression->e0);
        cstring e1 = getName(expression->e1);
        cstring e2 = getName(expression->e2);
        if (e0.isNullOrEmpty() || e1.isNullOrEmpty() || e2.isNullOrEmpty())
            return;
        name.emplace(expression, e0 + "[" + e1 + ":" + e2 + "]");
    }

    void postorder(const IR::MethodCallExpression* expression) override {
        cstring m = getName(expression->method);
        if (m.isNullOrEmpty())
            return;
        if (!m.endsWith(IR::Type_Header::isValid) || expression->arguments->size() != 0) {
            // This is a heuristic
            error(expression);
            return;
        }
        name.emplace(expression, m + "()");
    }

    cstring getName(const IR::Expression* expression) {
        if (name.find(expression) == name.end())
            return "";
        return ::get(name, expression);
    }
};

const IR::Node* TableKeyNames::postorder(IR::KeyElement* keyElement) {
    LOG3("Visiting " << getOriginal());
    if (keyElement->annotations->getSingle(IR::Annotation::nameAnnotation) != nullptr)
        // already present: no changes
        return keyElement;
    KeyNameGenerator kng;
    (void)keyElement->expression->apply(kng);
    cstring name = kng.getName(keyElement->expression);

    LOG3("Generated name " << name);
    if (name.isNullOrEmpty())
        return keyElement;
    keyElement->annotations = keyElement->annotations->addAnnotation(
        IR::Annotation::nameAnnotation,
        new IR::StringLiteral(keyElement->expression->srcInfo, name));
    return keyElement;
}

}  // namespace P4
<commit_msg>Use cstring() instead of the empty cstring<commit_after>/*
Copyright 2017 VMware, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

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

#include "tableKeyNames.h"

namespace P4 {

class KeyNameGenerator : public Inspector {
    std::map<const IR::Expression*, cstring> name;

 public:
    KeyNameGenerator() { setName("KeyNameGenerator"); }

    void error(const IR::Expression* expression) {
        ::error("%1%: Complex key expression requires a @name annotation", expression);
    }

    void postorder(const IR::Expression* expression) override { error(expression); }

    void postorder(const IR::PathExpression* expression) override {
        name.emplace(expression, expression->path->toString());
    }

    void postorder(const IR::Member* expression) override {
        cstring n = getName(expression->expr);
        if (!n)
            return;
        name.emplace(expression, n + "." + expression->member);
    }

    void postorder(const IR::ArrayIndex* expression) override {
        cstring l = getName(expression->left);
        cstring r = getName(expression->right);
        if (!l || !r)
            return;
        name.emplace(expression, l + "[" + r + "]");
    }

    void postorder(const IR::Constant* expression) override {
        name.emplace(expression, expression->toString());
    }

    void postorder(const IR::Slice* expression) override {
        cstring e0 = getName(expression->e0);
        cstring e1 = getName(expression->e1);
        cstring e2 = getName(expression->e2);
        if (!e0 || !e1 || !e2)
            return;
        name.emplace(expression, e0 + "[" + e1 + ":" + e2 + "]");
    }

    void postorder(const IR::MethodCallExpression* expression) override {
        cstring m = getName(expression->method);
        if (!m)
            return;
        if (!m.endsWith(IR::Type_Header::isValid) || expression->arguments->size() != 0) {
            // This is a heuristic
            error(expression);
            return;
        }
        name.emplace(expression, m + "()");
    }

    cstring getName(const IR::Expression* expression) {
        if (name.find(expression) == name.end())
            return cstring();
        return ::get(name, expression);
    }
};

const IR::Node* TableKeyNames::postorder(IR::KeyElement* keyElement) {
    LOG3("Visiting " << getOriginal());
    if (keyElement->annotations->getSingle(IR::Annotation::nameAnnotation) != nullptr)
        // already present: no changes
        return keyElement;
    KeyNameGenerator kng;
    (void)keyElement->expression->apply(kng);
    cstring name = kng.getName(keyElement->expression);

    LOG3("Generated name " << name);
    if (!name)
        return keyElement;
    keyElement->annotations = keyElement->annotations->addAnnotation(
        IR::Annotation::nameAnnotation,
        new IR::StringLiteral(keyElement->expression->srcInfo, name));
    return keyElement;
}

}  // namespace P4
<|endoftext|>


<commit_before>#include <iostream>
#include "RPN/Parser.h"

#ifndef _MSC_VER
#define lest_FEATURE_COLOURISE 1
#endif

#include "lest.hpp"

using namespace std;

bool TestParse(const std::string &expr)
{
	auto p = RPN::Parser::Default().Parse(expr);
	return p != nullptr;
}

float TestValue(const std::string &expr)
{

	auto p = RPN::Parser::Default().Parse(expr);
	if (!p)
		throw std::exception("Can't parse");
    auto c = RPN::Parser::Default().Compile(expr);
	if (!c)
		throw std::exception("Can't compile");

	auto pv = p->value();
	auto cv = c();
	if (pv != cv)
		throw std::exception("Compiled & interpreted result doesn't match");

	return cv;
}

const lest::test specification[] =
{
	CASE("Simplest Parse")
	{
		EXPECT(TestParse("2+2"));
		EXPECT(TestParse("2+2*3"));
		EXPECT(TestParse("2+2*+3") == false);
		EXPECT(TestParse("2 22") == false);
	},
	CASE("Parenthesis Parse")
	{
		EXPECT(TestParse("(1+1)"));
		EXPECT(TestParse("(2+2) * (2+3)"));
	},

	CASE("Simplest Value")
	{
		EXPECT(TestValue("2+2") == 4.0f);
		EXPECT(TestValue("2+2*3") == 8.0f);
	},
	CASE("Binary operators")
	{
		EXPECT(TestValue("2+2") == 4.0f);
		EXPECT(TestValue("2-2") == 0.0f);
		EXPECT(TestValue("2*3") == 6.0f);
		EXPECT(TestValue("6/3") == 2.0f);
	},

	CASE("Comparision operators")
	{
		EXPECT(TestValue("2<3") == 1.0f);
		EXPECT(TestValue("3<2") == 0.0f);
		EXPECT(TestValue("2<2") == 0.0f);

		EXPECT(TestValue("2<=3") == 1.0f);
		EXPECT(TestValue("3<=2") == 0.0f);
		EXPECT(TestValue("2<=2") == 1.0f);

		EXPECT(TestValue("3>2") == 1.0f);
		EXPECT(TestValue("2>3") == 0.0f);
		EXPECT(TestValue("2>2") == 0.0f);

		EXPECT(TestValue("3>=2") == 1.0f);
		EXPECT(TestValue("2>=3") == 0.0f);
		EXPECT(TestValue("2>=2") == 1.0f);
	},

	CASE("Equality operators")
	{
		EXPECT(TestValue("2==2") == 1.0f);
		EXPECT(TestValue("3==2") == 0.0f);

		EXPECT(TestValue("2!=2") == 0.0f);
		EXPECT(TestValue("3!=2") == 1.0f);
	},

	CASE("Boolean operators")
	{
		EXPECT(TestValue("2 && 2") == 1.0f);
		EXPECT(TestValue("0 && 2") == 0.0f);
		EXPECT(TestValue("0 && 0") == 0.0f);
		EXPECT(TestValue("(1 && 1) && (1 && 1)") == 1.0f);

		EXPECT(TestValue("2 || 2") == 1.0f);
		EXPECT(TestValue("0 || 2") == 1.0f);
		EXPECT(TestValue("0 || 0") == 0.0f);
		EXPECT(TestValue("(1 || 0) || (1 || 1)") == 1.0f);

		EXPECT(TestValue("(1 || 0) && (1 || 1)") == 1.0f);
		EXPECT(TestValue("(1 && 0) || (1 && 1)") == 1.0f);
	},

	CASE("Functions")
	{
		EXPECT(TestValue("math.min(-1,1) + 3") == 2.0f);
		EXPECT(TestValue("math.max(-1,1) + 3") == 4.0f);

		EXPECT(TestValue("if(1,2,3) + 2") == 4.0f);
		EXPECT(TestValue("if(0,2,3) + 2") == 5.0f);

		EXPECT(TestValue("math.min(math.max(-1,1),6) + 3") == 4.0f);
	},
};

int main (int argc, char * argv[])
{
	return lest::run(specification, argc, argv);
}
<commit_msg>Why VS must be nonstandard?<commit_after>#include <iostream>
#include <stdexcept>

#include "RPN/Parser.h"

#ifndef _MSC_VER
#define lest_FEATURE_COLOURISE 1
#endif

#include "lest.hpp"

using namespace std;

bool TestParse(const std::string &expr)
{
	auto p = RPN::Parser::Default().Parse(expr);
	return p != nullptr;
}

float TestValue(const std::string &expr)
{

	auto p = RPN::Parser::Default().Parse(expr);
	if (!p)
		throw std::runtime_error("Can't parse");
    auto c = RPN::Parser::Default().Compile(expr);
	if (!c)
		throw std::runtime_error("Can't compile");

	auto pv = p->value();
	auto cv = c();
	if (pv != cv)
		throw std::runtime_error("Compiled & interpreted result doesn't match");

	return cv;
}

const lest::test specification[] =
{
	CASE("Simplest Parse")
	{
		EXPECT(TestParse("2+2"));
		EXPECT(TestParse("2+2*3"));
		EXPECT(TestParse("2+2*+3") == false);
		EXPECT(TestParse("2 22") == false);
	},
	CASE("Parenthesis Parse")
	{
		EXPECT(TestParse("(1+1)"));
		EXPECT(TestParse("(2+2) * (2+3)"));
	},

	CASE("Simplest Value")
	{
		EXPECT(TestValue("2+2") == 4.0f);
		EXPECT(TestValue("2+2*3") == 8.0f);
	},
	CASE("Binary operators")
	{
		EXPECT(TestValue("2+2") == 4.0f);
		EXPECT(TestValue("2-2") == 0.0f);
		EXPECT(TestValue("2*3") == 6.0f);
		EXPECT(TestValue("6/3") == 2.0f);
	},

	CASE("Comparision operators")
	{
		EXPECT(TestValue("2<3") == 1.0f);
		EXPECT(TestValue("3<2") == 0.0f);
		EXPECT(TestValue("2<2") == 0.0f);

		EXPECT(TestValue("2<=3") == 1.0f);
		EXPECT(TestValue("3<=2") == 0.0f);
		EXPECT(TestValue("2<=2") == 1.0f);

		EXPECT(TestValue("3>2") == 1.0f);
		EXPECT(TestValue("2>3") == 0.0f);
		EXPECT(TestValue("2>2") == 0.0f);

		EXPECT(TestValue("3>=2") == 1.0f);
		EXPECT(TestValue("2>=3") == 0.0f);
		EXPECT(TestValue("2>=2") == 1.0f);
	},

	CASE("Equality operators")
	{
		EXPECT(TestValue("2==2") == 1.0f);
		EXPECT(TestValue("3==2") == 0.0f);

		EXPECT(TestValue("2!=2") == 0.0f);
		EXPECT(TestValue("3!=2") == 1.0f);
	},

	CASE("Boolean operators")
	{
		EXPECT(TestValue("2 && 2") == 1.0f);
		EXPECT(TestValue("0 && 2") == 0.0f);
		EXPECT(TestValue("0 && 0") == 0.0f);
		EXPECT(TestValue("(1 && 1) && (1 && 1)") == 1.0f);

		EXPECT(TestValue("2 || 2") == 1.0f);
		EXPECT(TestValue("0 || 2") == 1.0f);
		EXPECT(TestValue("0 || 0") == 0.0f);
		EXPECT(TestValue("(1 || 0) || (1 || 1)") == 1.0f);

		EXPECT(TestValue("(1 || 0) && (1 || 1)") == 1.0f);
		EXPECT(TestValue("(1 && 0) || (1 && 1)") == 1.0f);
	},

	CASE("Functions")
	{
		EXPECT(TestValue("math.min(-1,1) + 3") == 2.0f);
		EXPECT(TestValue("math.max(-1,1) + 3") == 4.0f);

		EXPECT(TestValue("if(1,2,3) + 2") == 4.0f);
		EXPECT(TestValue("if(0,2,3) + 2") == 5.0f);

		EXPECT(TestValue("math.min(math.max(-1,1),6) + 3") == 4.0f);
	},
};

int main (int argc, char * argv[])
{
	return lest::run(specification, argc, argv);
}
<|endoftext|>


<commit_before>/*
 *  yosys -- Yosys Open SYnthesis Suite
 *
 *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 *  ---
 *
 *  The Verilog frontend.
 *
 *  This frontend is using the AST frontend library (see frontends/ast/).
 *  Thus this frontend does not generate RTLIL code directly but creates an
 *  AST directly from the Verilog parse tree and then passes this AST to
 *  the AST frontend library.
 *
 *  ---
 *
 *  This file contains an ad-hoc parser for Verilog constants. The Verilog
 *  lexer does only recognize a constant but does not actually split it to its
 *  components. I.e. it just passes the Verilog code for the constant to the
 *  bison parser. The parser then uses the function const2ast() from this file
 *  to create an AST node for the constant.
 *
 */

#include "verilog_frontend.h"
#include "kernel/log.h"
#include <string.h>
#include <math.h>

YOSYS_NAMESPACE_BEGIN

using namespace AST;

// divide an arbitrary length decimal number by two and return the rest
static int my_decimal_div_by_two(std::vector<uint8_t> &digits)
{
	int carry = 0;
	for (size_t i = 0; i < digits.size(); i++) {
		if (digits[i] >= 10)
			log_error("Invalid use of [a-fxz?] in decimal constant at %s:%d.\n",
				current_filename.c_str(), get_line_num());
		digits[i] += carry * 10;
		carry = digits[i] % 2;
		digits[i] /= 2;
	}
	while (!digits.empty() && !digits.front())
		digits.erase(digits.begin());
	return carry;
}

// find the number of significant bits in a binary number (not including the sign bit)
static int my_ilog2(int x)
{
	int ret = 0;
	while (x != 0 && x != -1) {
		x = x >> 1;
		ret++;
	}
	return ret;
}

// parse a binary, decimal, hexadecimal or octal number with support for special bits ('x', 'z' and '?')
static void my_strtobin(std::vector<RTLIL::State> &data, const char *str, int len_in_bits, int base, char case_type)
{
	// all digits in string (MSB at index 0)
	std::vector<uint8_t> digits;

	while (*str) {
		if ('0' <= *str && *str <= '9')
			digits.push_back(*str - '0');
		else if ('a' <= *str && *str <= 'f')
			digits.push_back(10 + *str - 'a');
		else if ('A' <= *str && *str <= 'F')
			digits.push_back(10 + *str - 'A');
		else if (*str == 'x' || *str == 'X')
			digits.push_back(0xf0);
		else if (*str == 'z' || *str == 'Z')
			digits.push_back(0xf1);
		else if (*str == '?')
			digits.push_back(0xf2);
		str++;
	}

	if (base == 10 && GetSize(digits) == 1 && digits.front() >= 0xf0)
		base = 2;

	data.clear();

	if (base == 10) {
		while (!digits.empty())
			data.push_back(my_decimal_div_by_two(digits) ? RTLIL::S1 : RTLIL::S0);
	} else {
		int bits_per_digit = my_ilog2(base-1);
		for (auto it = digits.rbegin(), e = digits.rend(); it != e; it++) {
			for (int i = 0; i < bits_per_digit; i++) {
				int bitmask = 1 << i;
				if (*it == 0xf0)
					data.push_back(case_type == 'x' ? RTLIL::Sa : RTLIL::Sx);
				else if (*it == 0xf1)
					data.push_back(case_type == 'x' || case_type == 'z' ? RTLIL::Sa : RTLIL::Sz);
				else if (*it == 0xf2)
					data.push_back(RTLIL::Sa);
				else
					data.push_back((*it & bitmask) ? RTLIL::S1 : RTLIL::S0);
			}
		}
	}

	int len = GetSize(data);
	RTLIL::State msb = data.back();

	if (len_in_bits < 0) {
		if (len < 32)
			data.resize(32, msb == RTLIL::S0 || msb == RTLIL::S1 ? RTLIL::S0 : msb);
		return;
	}

	for (len = len - 1; len >= 0; len--)
		if (data[len] == RTLIL::S1)
			break;
	if (msb == RTLIL::S0 || msb == RTLIL::S1) {
		len += 1;
		data.resize(len_in_bits, RTLIL::S0);
	} else {
		len += 2;
		data.resize(len_in_bits, msb);
	}

	if (len > len_in_bits)
		log_warning("(Literal has a width of %d bit, but value requires %d bit. (%s:%d)\n",
			len_in_bits, len, current_filename.c_str(), get_line_num());
}

// convert the Verilog code for a constant to an AST node
AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn_z)
{
	if (warn_z) {
		AstNode *ret = const2ast(code, case_type);
		if (std::find(ret->bits.begin(), ret->bits.end(), RTLIL::State::Sz) != ret->bits.end())
			log_warning("Yosys does not support tri-state logic at the moment. (%s:%d)\n",
				current_filename.c_str(), get_line_num());
		return ret;
	}

	const char *str = code.c_str();

	// Strings
	if (*str == '"') {
		int len = strlen(str) - 2;
		std::vector<RTLIL::State> data;
		data.reserve(len * 8);
		for (int i = 0; i < len; i++) {
			unsigned char ch = str[len - i];
			for (int j = 0; j < 8; j++) {
				data.push_back((ch & 1) ? RTLIL::S1 : RTLIL::S0);
				ch = ch >> 1;
			}
		}
		AstNode *ast = AstNode::mkconst_bits(data, false);
		ast->str = code;
		return ast;
	}

	for (size_t i = 0; i < code.size(); i++)
		if (code[i] == '_' || code[i] == ' ' || code[i] == '\t' || code[i] == '\r' || code[i] == '\n')
			code.erase(code.begin()+(i--));
	str = code.c_str();

	char *endptr;
	long len_in_bits = strtol(str, &endptr, 10);

	// Simple base-10 integer
	if (*endptr == 0) {
		std::vector<RTLIL::State> data;
		my_strtobin(data, str, -1, 10, case_type);
		if (data.back() == RTLIL::S1)
			data.push_back(RTLIL::S0);
		return AstNode::mkconst_bits(data, true);
	}

	// unsized constant
	if (str == endptr)
		len_in_bits = -1;

	// The "<bits>'s?[bodhBODH]<digits>" syntax
	if (*endptr == '\'')
	{
		std::vector<RTLIL::State> data;
		bool is_signed = false;
		if (*(endptr+1) == 's') {
			is_signed = true;
			endptr++;
		}
		switch (*(endptr+1))
		{
		case 'b':
		case 'B':
			my_strtobin(data, endptr+2, len_in_bits, 2, case_type);
			break;
		case 'o':
		case 'O':
			my_strtobin(data, endptr+2, len_in_bits, 8, case_type);
			break;
		case 'd':
		case 'D':
			my_strtobin(data, endptr+2, len_in_bits, 10, case_type);
			break;
		case 'h':
		case 'H':
			my_strtobin(data, endptr+2, len_in_bits, 16, case_type);
			break;
		default:
			return NULL;
		}
		if (len_in_bits < 0) {
			if (is_signed && data.back() == RTLIL::S1)
				data.push_back(RTLIL::S0);
		}
		return AstNode::mkconst_bits(data, is_signed);
	}

	return NULL;
}

YOSYS_NAMESPACE_END

<commit_msg>Check base-n literals only contain valid digits<commit_after>/*
 *  yosys -- Yosys Open SYnthesis Suite
 *
 *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 *  ---
 *
 *  The Verilog frontend.
 *
 *  This frontend is using the AST frontend library (see frontends/ast/).
 *  Thus this frontend does not generate RTLIL code directly but creates an
 *  AST directly from the Verilog parse tree and then passes this AST to
 *  the AST frontend library.
 *
 *  ---
 *
 *  This file contains an ad-hoc parser for Verilog constants. The Verilog
 *  lexer does only recognize a constant but does not actually split it to its
 *  components. I.e. it just passes the Verilog code for the constant to the
 *  bison parser. The parser then uses the function const2ast() from this file
 *  to create an AST node for the constant.
 *
 */

#include "verilog_frontend.h"
#include "kernel/log.h"
#include <string.h>
#include <math.h>

YOSYS_NAMESPACE_BEGIN

using namespace AST;

// divide an arbitrary length decimal number by two and return the rest
static int my_decimal_div_by_two(std::vector<uint8_t> &digits)
{
	int carry = 0;
	for (size_t i = 0; i < digits.size(); i++) {
		if (digits[i] >= 10)
			log_error("Invalid use of [a-fxz?] in decimal constant at %s:%d.\n",
				current_filename.c_str(), get_line_num());
		digits[i] += carry * 10;
		carry = digits[i] % 2;
		digits[i] /= 2;
	}
	while (!digits.empty() && !digits.front())
		digits.erase(digits.begin());
	return carry;
}

// find the number of significant bits in a binary number (not including the sign bit)
static int my_ilog2(int x)
{
	int ret = 0;
	while (x != 0 && x != -1) {
		x = x >> 1;
		ret++;
	}
	return ret;
}

// parse a binary, decimal, hexadecimal or octal number with support for special bits ('x', 'z' and '?')
static void my_strtobin(std::vector<RTLIL::State> &data, const char *str, int len_in_bits, int base, char case_type)
{
	// all digits in string (MSB at index 0)
	std::vector<uint8_t> digits;

	while (*str) {
		if ('0' <= *str && *str <= '9')
			digits.push_back(*str - '0');
		else if ('a' <= *str && *str <= 'f')
			digits.push_back(10 + *str - 'a');
		else if ('A' <= *str && *str <= 'F')
			digits.push_back(10 + *str - 'A');
		else if (*str == 'x' || *str == 'X')
			digits.push_back(0xf0);
		else if (*str == 'z' || *str == 'Z')
			digits.push_back(0xf1);
		else if (*str == '?')
			digits.push_back(0xf2);
		str++;
	}

	if (base == 10 && GetSize(digits) == 1 && digits.front() >= 0xf0)
		base = 2;

	data.clear();

	if (base == 10) {
		while (!digits.empty())
			data.push_back(my_decimal_div_by_two(digits) ? RTLIL::S1 : RTLIL::S0);
	} else {
		int bits_per_digit = my_ilog2(base-1);
		for (auto it = digits.rbegin(), e = digits.rend(); it != e; it++) {
			if (*it > (base-1) && *it < 0xf0)
				log_error("Digit larger than %d used in in base-%d constant at %s:%d.\n",
					base-1, base, current_filename.c_str(), get_line_num());
			for (int i = 0; i < bits_per_digit; i++) {
				int bitmask = 1 << i;
				if (*it == 0xf0)
					data.push_back(case_type == 'x' ? RTLIL::Sa : RTLIL::Sx);
				else if (*it == 0xf1)
					data.push_back(case_type == 'x' || case_type == 'z' ? RTLIL::Sa : RTLIL::Sz);
				else if (*it == 0xf2)
					data.push_back(RTLIL::Sa);
				else
					data.push_back((*it & bitmask) ? RTLIL::S1 : RTLIL::S0);
			}
		}
	}

	int len = GetSize(data);
	RTLIL::State msb = data.back();

	if (len_in_bits < 0) {
		if (len < 32)
			data.resize(32, msb == RTLIL::S0 || msb == RTLIL::S1 ? RTLIL::S0 : msb);
		return;
	}

	for (len = len - 1; len >= 0; len--)
		if (data[len] == RTLIL::S1)
			break;
	if (msb == RTLIL::S0 || msb == RTLIL::S1) {
		len += 1;
		data.resize(len_in_bits, RTLIL::S0);
	} else {
		len += 2;
		data.resize(len_in_bits, msb);
	}

	if (len > len_in_bits)
		log_warning("(Literal has a width of %d bit, but value requires %d bit. (%s:%d)\n",
			len_in_bits, len, current_filename.c_str(), get_line_num());
}

// convert the Verilog code for a constant to an AST node
AstNode *VERILOG_FRONTEND::const2ast(std::string code, char case_type, bool warn_z)
{
	if (warn_z) {
		AstNode *ret = const2ast(code, case_type);
		if (std::find(ret->bits.begin(), ret->bits.end(), RTLIL::State::Sz) != ret->bits.end())
			log_warning("Yosys does not support tri-state logic at the moment. (%s:%d)\n",
				current_filename.c_str(), get_line_num());
		return ret;
	}

	const char *str = code.c_str();

	// Strings
	if (*str == '"') {
		int len = strlen(str) - 2;
		std::vector<RTLIL::State> data;
		data.reserve(len * 8);
		for (int i = 0; i < len; i++) {
			unsigned char ch = str[len - i];
			for (int j = 0; j < 8; j++) {
				data.push_back((ch & 1) ? RTLIL::S1 : RTLIL::S0);
				ch = ch >> 1;
			}
		}
		AstNode *ast = AstNode::mkconst_bits(data, false);
		ast->str = code;
		return ast;
	}

	for (size_t i = 0; i < code.size(); i++)
		if (code[i] == '_' || code[i] == ' ' || code[i] == '\t' || code[i] == '\r' || code[i] == '\n')
			code.erase(code.begin()+(i--));
	str = code.c_str();

	char *endptr;
	long len_in_bits = strtol(str, &endptr, 10);

	// Simple base-10 integer
	if (*endptr == 0) {
		std::vector<RTLIL::State> data;
		my_strtobin(data, str, -1, 10, case_type);
		if (data.back() == RTLIL::S1)
			data.push_back(RTLIL::S0);
		return AstNode::mkconst_bits(data, true);
	}

	// unsized constant
	if (str == endptr)
		len_in_bits = -1;

	// The "<bits>'s?[bodhBODH]<digits>" syntax
	if (*endptr == '\'')
	{
		std::vector<RTLIL::State> data;
		bool is_signed = false;
		if (*(endptr+1) == 's') {
			is_signed = true;
			endptr++;
		}
		switch (*(endptr+1))
		{
		case 'b':
		case 'B':
			my_strtobin(data, endptr+2, len_in_bits, 2, case_type);
			break;
		case 'o':
		case 'O':
			my_strtobin(data, endptr+2, len_in_bits, 8, case_type);
			break;
		case 'd':
		case 'D':
			my_strtobin(data, endptr+2, len_in_bits, 10, case_type);
			break;
		case 'h':
		case 'H':
			my_strtobin(data, endptr+2, len_in_bits, 16, case_type);
			break;
		default:
			return NULL;
		}
		if (len_in_bits < 0) {
			if (is_signed && data.back() == RTLIL::S1)
				data.push_back(RTLIL::S0);
		}
		return AstNode::mkconst_bits(data, is_signed);
	}

	return NULL;
}

YOSYS_NAMESPACE_END

<|endoftext|>


<commit_before>#include <SDL.h>
#include <setjmp.h>

#include "MainLoop.h"
#include "Mutex.h"
#include "ReadWriteLock.h"
#include "AuxLib.h"
#include "EventQueue.h"
#include "Options.h"
#include "LieroX.h"
#include "Cache.h"
#include "OLXCommand.h"
#include "InputEvents.h"
#include "TaskManager.h"
#include "Timer.h"
#include "DeprecatedGUI/Menu.h"
#include "SkinnedGUI/CGuiSkin.h"
#include "CServer.h"
#include "IRC.h"
#include "CrashHandler.h"

/*
Notes:

The following things need to be run on the main thread,
because of implementation details on SDL & the underlying OS,
so we cannot do much about it:
- OpenGL stuff
- SDL_Render*
- SDL events

So, this is exactly what we do in the main thread, and not much more.
Check via isMainThread(), whether you are in the main thread.

Then, there is the main game thread, where we run the game logic
itself, and also do much of the software pixel drawing, and
most other things.
We call it the gameloop thread, because the game gameloop runs in it.
Check via isGameloopThread(), whether you are in the gameloop thread.

To be able to do the software drawing in the mainloop thread,
and in parallel do the system screen drawing, we need to have two
main screen surface, which are handled by the VideoPostProcessor.

Then gameloop thread draws to the VideoPostProcessor::videoSurface().
The main thread draws the VideoPostProcessor::m_videoBufferSurface
to the screen.

*/

struct VideoHandler {
	SDL_mutex* mutex;
	SDL_cond* sign;
	int framesInQueue;
	bool videoModeReady;

	VideoHandler() : mutex(NULL), sign(NULL), framesInQueue(0), videoModeReady(true) {
		mutex = SDL_CreateMutex();
		sign = SDL_CreateCond();
	}

	~VideoHandler() {
		SDL_DestroyMutex(mutex); mutex = NULL;
		SDL_DestroyCond(sign); sign = NULL;
	}

	// Runs on the main thread.
	void frame() {
		{
			ScopedLock lock(mutex);
			if(!videoModeReady) return;
			SDL_CondBroadcast(sign);
			if(framesInQueue > 0) framesInQueue--;
			else return;
			// This must be done while locked because we don't want a flipBuffers meanwhile.
			VideoPostProcessor::process();
		}

		// This can be done unlocked, because we only access it through the main thread.
		VideoPostProcessor::render();
	}

	// Runs on the main thread.
	void doSingleDirectFrame() {
		assert(isMainThread());

		{
			ScopedLock lock(mutex);
			VideoPostProcessor::flipBuffers();
			VideoPostProcessor::process();
		}

		VideoPostProcessor::render();
	}

	// Runs on the main thread.
	void setVideoMode() {
		bool makeFrame = false;
		{
			ScopedLock lock(mutex);
			SetVideoMode();
			videoModeReady = true;
			SDL_CondBroadcast(sign);

			if(framesInQueue > 0) {
				framesInQueue = 0;
				makeFrame = true;
				// This must be done while locked because we don't want a flipBuffers meanwhile.
				VideoPostProcessor::process();
			}
		}

		if(makeFrame)
			// This can be done unlocked, because we only access it through the main thread.
			VideoPostProcessor::render();
	}

	// This must not be run on the main thread.
	void pushFrame() {
		assert(!isMainThread());

		// wait for current drawing
		ScopedLock lock(mutex);

		while(framesInQueue > 0)
			SDL_CondWait(sign, mutex);

		SDL_Event ev;
		ev.type = SDL_USEREVENT;
		ev.user.code = UE_DoVideoFrame;
		if(SDL_PushEvent(&ev) == 0) {
			framesInQueue++;
			VideoPostProcessor::flipBuffers();
		} else
			warnings << "failed to push videoframeevent" << endl;

	}

	void requestSetVideoMode() {
		ScopedLock lock(mutex);
		SDL_Event ev;
		ev.type = SDL_USEREVENT;
		ev.user.code = UE_DoSetVideoMode;
		if(SDL_PushEvent(&ev) == 0) {
			videoModeReady = false;
			while(!videoModeReady)
				SDL_CondWait(sign, mutex);
		}
		else
			warnings << "failed to push setvideomode event" << endl;
	}
};

static VideoHandler videoHandler;

#ifndef WIN32
sigjmp_buf longJumpBuffer;
#endif

static SDL_Event QuitEventThreadEvent() {
	SDL_Event ev;
	ev.type = SDL_USEREVENT;
	ev.user.code = UE_QuitEventThread;
	return ev;
}

struct CoutPrint : PrintOutFct {
	void print(const std::string& str) const {
		printf("%s", str.c_str());
	}
};

void startMainLockDetector() {
	if(!tLXOptions->bUseMainLockDetector) return;

	struct MainLockDetector : Action {
		bool wait(Uint32 time) {
			if(!tLX) return false;
			AbsTime oldTime = tLX->currentTime;
			for(Uint32 t = 0; t < time; t += 100) {
				if(!tLX) return false;
				if(game.state == Game::S_Quit) return false;
				if(oldTime != tLX->currentTime) return true;
				SDL_Delay(100);
			}
			return true;
		}
		Result handle() {
			// We should always have tLX!=NULL here as we uninit it after the thread-shutdown now.
			while(game.state != Game::S_Quit) {
				AbsTime oldTime = tLX->currentTime;
				if(!wait(1000)) return true;
				if(!tLX) return true;
				if(game.state == Game::S_Quit) return true;
				if(IsWaitingForEvent()) continue;

				// HINT: Comment that out and you'll find a lot of things in OLX which could be improved.
				// WARNING: This is the only code here which could lead to very rare crashes.
				//if(!cClient || cClient->getStatus() != NET_PLAYING) continue;

				// check if the mainthread is hanging
				if(oldTime == tLX->currentTime) {
					warnings << "possible lock of game thread detected" << endl;
					notes << "current game state: " << game.state << endl;
					//OlxWriteCoreDump("mainlock");
					//RaiseDebugger();

					if(!wait(5*1000)) return true;
					if(tLX && game.state != Game::S_Quit && oldTime == tLX->currentTime) {
						hints << "Still locked after 5 seconds. Current threads:" << endl;
						threadPool->dumpState(stdoutCLI());
						DumpAllThreadsCallstack(CoutPrint());
						hints << "Free system memory: " << (GetFreeSysMemory() / 1024) << " KB" << endl;
						hints << "Cache size: " << (cCache.GetCacheSize() / 1024) << " KB" << endl;
					}
					else continue;

					// pause for a while, don't be so hard
					if(!wait(25*1000)) return true;
					if(tLX && game.state != Game::S_Quit && oldTime == tLX->currentTime) {
						warnings << "we still are locked after 30 seconds" << endl;
						DumpAllThreadsCallstack(CoutPrint());
						if(tLXOptions && tLXOptions->bFullscreen) {
							notes << "we are in fullscreen, going to window mode now" << endl;
							tLXOptions->bFullscreen = false;
							doSetVideoModeInMainThread();
							notes << "setting window mode sucessfull" << endl;
						}
					}
					else continue;

					// pause for a while, don't be so hard
					if(!wait(25*1000)) return true;
					if(tLX && game.state != Game::S_Quit && oldTime == tLX->currentTime) {
						errors << "we still are locked after 60 seconds" << endl;
						DumpAllThreadsCallstack(CoutPrint());
						if(!AmIBeingDebugged()) {
							errors << "aborting now" << endl;
							abort();
						}
					}
				}
			}
			return true;
		}
	};
	threadPool->start(new MainLockDetector(), "main lock detector", true);
}

struct MainLoopTask : LoopTask {
	enum State {
		State_Startup,
		State_Loop,
		State_Quit
	};
	State state;
	MainLoopTask() : state(State_Startup) {}
	Result handle_Startup();
	Result handle_Loop();
	Result handle_Quit();
	Result handleFrame();
};

// Runs on the video thread.
// Returns false on quit.
static bool handleSDLEvent(SDL_Event& ev) {
	if(ev.type == SDL_USEREVENT) {
		switch(ev.user.code) {
		case UE_QuitEventThread:
			return false;
		case UE_DoVideoFrame:
			videoHandler.frame();
			return true;
		case UE_DoSetVideoMode:
			videoHandler.setVideoMode();
			return true;
		case UE_DoActionInMainThread:
			((Action*)ev.user.data1)->handle();
			delete (Action*)ev.user.data1;
			return true;
		case UE_NopWakeup:
			// do nothing, it's just a wakeup
			return true;
		}
		warnings << "handleSDLEvent: got unknown user event " << (int)ev.user.code << endl;
		return true;
	}
	if( ev.type == SDL_SYSWMEVENT ) {
		EvHndl_SysWmEvent_MainThread( &ev );
		return true;
	}
	mainQueue->push(ev);
	return true;
}

// Get all SDL events and push them to our event queue.
// We have to do that in the same thread where we inited the video because of SDL.
bool handleSDLEvents(bool wait) {
	assert(isMainThread());

	SDL_Event ev;
	memset( &ev, 0, sizeof(ev) );

	if(wait) {
		if( SDL_WaitEvent(&ev) ) {
			if(!handleSDLEvent(ev))
				return false;
		}
		else {
			notes << "error while waiting for next event" << endl;
			SDL_Delay(200);
			return true;
		}
	}

	while( SDL_PollEvent(&ev) ) {
		if(!handleSDLEvent(ev))
			return false;
	}

	return true;
}

void doMainLoop() {
#ifdef SINGLETHREADED
	MainLoopTask mainLoop;
	while(true) {
		// we handle SDL events in doVideoFrameInMainThread
		if(NegResult r = mainLoop.handleFrame())
			break;
	}
#else
	ThreadPoolItem* mainLoopThread = threadPool->start(new MainLoopTask(), "main loop", false);

	startMainLockDetector();

	if(!bDedicated) {
		while(true) {
			if(!handleSDLEvents(true))
				goto quit;
		}
	}

quit:
	threadPool->wait(mainLoopThread, NULL);
	mainLoopThread = NULL;
#endif
}

// note: important to have const char* here because std::string is too dynamic, could be screwed up when returning
void SetCrashHandlerReturnPoint(const char* name) {
#ifndef WIN32
	if(sigsetjmp(longJumpBuffer, true) != 0) {
		hints << "returned from sigsetjmp in " << name << endl;
		if(!tLXOptions) {
			notes << "we already have tLXOptions uninitialised, exiting now" << endl;
			exit(10);
			return;
		}
		if(tLXOptions->bFullscreen) {
			notes << "we are in fullscreen, going to window mode now" << endl;
			tLXOptions->bFullscreen = false;
			doSetVideoModeInMainThread();
			notes << "back in window mode" << endl;
		}
	}
#endif
}

void doVideoFrameInMainThread() {
	if(bDedicated) return;
	if(isMainThread())
		videoHandler.doSingleDirectFrame();
	else
		videoHandler.pushFrame();
}

void doSetVideoModeInMainThread() {
	if(bDedicated) return;
	if(isMainThread())
		videoHandler.setVideoMode();
	else
		videoHandler.requestSetVideoMode();
}

void doVppOperation(Action* act) {
	{
		ScopedLock lock(videoHandler.mutex);
		act->handle();
	}
	delete act;
}

void doActionInMainThread(Action* act) {
	if(bDedicated) {
		warnings << "doActionInMainThread cannot work correctly in dedicated mode" << endl;
		// we will just put it to the main queue instead
		mainQueue->push(act);
		return;
	}

	if(isMainThread()) {
		act->handle();
		delete act;
	}
	else {
		SDL_Event ev;
		ev.type = SDL_USEREVENT;
		ev.user.code = UE_DoActionInMainThread;
		ev.user.data1 = act;
		if(SDL_PushEvent(&ev) != 0) {
			errors << "failed to push custom action event" << endl;
		}
	}
}

Result MainLoopTask::handle_Startup() {
	gameloopThreadId = getCurrentThreadId();
	assert(gameloopThreadId != (ThreadId)-1);
	setCurThreadPriority(0.5f);

	// NOTE: This code is really old and outdated.
	// We might just merge that with the new code.
	// Otherwise, it will never work like this because of
	// missing stuff, e.g. no game.init call (which does
	// also init the old menu, so we cannot simply call it
	// like this right now).
	if( tLXOptions->bNewSkinnedGUI )
	{
		// Just for test - it's not real handler yet
		SkinnedGUI::cMainSkin->Load("default");
		SkinnedGUI::cMainSkin->OpenLayout("test.skn");
		while (game.state != Game::S_Quit)  {
			tLX->fDeltaTime = GetTime() - tLX->currentTime;
			tLX->currentTime = GetTime();

			ProcessEvents();
			SkinnedGUI::cMainSkin->Frame();
		}

		return "quit";
	}

	state = State_Loop;
	return true;
}

Result MainLoopTask::handle_Loop() {
	if(game.state == Game::S_Quit) {
		state = State_Quit;
		return true;
	}

	game.frame();
	return true;
}

Result MainLoopTask::handle_Quit() {
	if(!isMainThread()) {
		SDL_Event quitEv = QuitEventThreadEvent();
		if(!bDedicated)
			while(SDL_PushEvent(&quitEv) < 0) {}
	}
	gameloopThreadId = (ThreadId)-1;
	return "quit";
}

Result MainLoopTask::handleFrame() {
	switch(state) {
	case State_Startup: return handle_Startup();
	case State_Loop: return handle_Loop();
	case State_Quit: return handle_Quit();
	}
	return "invalid state";
}

<commit_msg>comment<commit_after>#include <SDL.h>
#include <setjmp.h>

#include "MainLoop.h"
#include "Mutex.h"
#include "ReadWriteLock.h"
#include "AuxLib.h"
#include "EventQueue.h"
#include "Options.h"
#include "LieroX.h"
#include "Cache.h"
#include "OLXCommand.h"
#include "InputEvents.h"
#include "TaskManager.h"
#include "Timer.h"
#include "DeprecatedGUI/Menu.h"
#include "SkinnedGUI/CGuiSkin.h"
#include "CServer.h"
#include "IRC.h"
#include "CrashHandler.h"

/*
Notes:

The following things need to be run on the main thread,
because of implementation details on SDL & the underlying OS,
so we cannot do much about it:
- OpenGL stuff
- SDL_Render*
- SDL events

So, this is exactly what we do in the main thread, and not much more.
Check via isMainThread(), whether you are in the main thread.

Then, there is the main game thread, where we run the game logic
itself, and also do much of the software pixel drawing, and
most other things.
We call it the gameloop thread, because the game gameloop runs in it.
Check via isGameloopThread(), whether you are in the gameloop thread.

To be able to do the software drawing in the mainloop thread,
and in parallel do the system screen drawing, we need to have two
main screen surface, which are handled by the VideoPostProcessor.

Then gameloop thread draws to the VideoPostProcessor::videoSurface().
The main thread draws the VideoPostProcessor::m_videoBufferSurface
to the screen.

*/

struct VideoHandler {
	SDL_mutex* mutex;
	SDL_cond* sign;
	int framesInQueue;
	bool videoModeReady;

	VideoHandler() : mutex(NULL), sign(NULL), framesInQueue(0), videoModeReady(true) {
		mutex = SDL_CreateMutex();
		sign = SDL_CreateCond();
	}

	~VideoHandler() {
		SDL_DestroyMutex(mutex); mutex = NULL;
		SDL_DestroyCond(sign); sign = NULL;
	}

	// Runs on the main thread.
	void frame() {
		{
			ScopedLock lock(mutex);
			if(!videoModeReady) return;
			SDL_CondBroadcast(sign);
			if(framesInQueue > 0) framesInQueue--;
			else return;
			// This must be done while locked because we don't want a flipBuffers meanwhile.
			VideoPostProcessor::process();
		}

		// This can be done unlocked, because we only access it through the main thread.
		VideoPostProcessor::render();
	}

	// Runs on the main thread.
	void doSingleDirectFrame() {
		assert(isMainThread());

		{
			ScopedLock lock(mutex);
			VideoPostProcessor::flipBuffers();
			VideoPostProcessor::process();
		}

		VideoPostProcessor::render();
	}

	// Runs on the main thread.
	void setVideoMode() {
		bool makeFrame = false;
		{
			ScopedLock lock(mutex);
			SetVideoMode();
			videoModeReady = true;
			SDL_CondBroadcast(sign);

			if(framesInQueue > 0) {
				framesInQueue = 0;
				makeFrame = true;
				// This must be done while locked because we don't want a flipBuffers meanwhile.
				VideoPostProcessor::process();
			}
		}

		if(makeFrame)
			// This can be done unlocked, because we only access it through the main thread.
			VideoPostProcessor::render();
	}

	// This must not be run on the main thread.
	void pushFrame() {
		assert(!isMainThread());

		// wait for current drawing
		ScopedLock lock(mutex);

		while(framesInQueue > 0)
			SDL_CondWait(sign, mutex);

		SDL_Event ev;
		ev.type = SDL_USEREVENT;
		ev.user.code = UE_DoVideoFrame;
		if(SDL_PushEvent(&ev) == 0) {
			framesInQueue++;
			VideoPostProcessor::flipBuffers();
		} else
			warnings << "failed to push videoframeevent" << endl;

	}

	void requestSetVideoMode() {
		ScopedLock lock(mutex);
		SDL_Event ev;
		ev.type = SDL_USEREVENT;
		ev.user.code = UE_DoSetVideoMode;
		if(SDL_PushEvent(&ev) == 0) {
			videoModeReady = false;
			while(!videoModeReady)
				SDL_CondWait(sign, mutex);
		}
		else
			warnings << "failed to push setvideomode event" << endl;
	}
};

static VideoHandler videoHandler;

#ifndef WIN32
sigjmp_buf longJumpBuffer;
#endif

static SDL_Event QuitEventThreadEvent() {
	SDL_Event ev;
	ev.type = SDL_USEREVENT;
	ev.user.code = UE_QuitEventThread;
	return ev;
}

struct CoutPrint : PrintOutFct {
	void print(const std::string& str) const {
		printf("%s", str.c_str());
	}
};

void startMainLockDetector() {
	if(!tLXOptions->bUseMainLockDetector) return;

	struct MainLockDetector : Action {
		bool wait(Uint32 time) {
			if(!tLX) return false;
			AbsTime oldTime = tLX->currentTime;
			for(Uint32 t = 0; t < time; t += 100) {
				if(!tLX) return false;
				if(game.state == Game::S_Quit) return false;
				if(oldTime != tLX->currentTime) return true;
				SDL_Delay(100);
			}
			return true;
		}
		Result handle() {
			// We should always have tLX!=NULL here as we uninit it after the thread-shutdown now.
			while(game.state != Game::S_Quit) {
				AbsTime oldTime = tLX->currentTime;
				if(!wait(1000)) return true;
				if(!tLX) return true;
				if(game.state == Game::S_Quit) return true;
				if(IsWaitingForEvent()) continue;

				// HINT: Comment that out and you'll find a lot of things in OLX which could be improved.
				// WARNING: This is the only code here which could lead to very rare crashes.
				//if(!cClient || cClient->getStatus() != NET_PLAYING) continue;

				// check if the mainthread is hanging
				if(oldTime == tLX->currentTime) {
					warnings << "possible lock of game thread detected" << endl;
					notes << "current game state: " << game.state << endl;
					//OlxWriteCoreDump("mainlock");
					//RaiseDebugger();

					if(!wait(5*1000)) return true;
					if(tLX && game.state != Game::S_Quit && oldTime == tLX->currentTime) {
						hints << "Still locked after 5 seconds. Current threads:" << endl;
						threadPool->dumpState(stdoutCLI());
						DumpAllThreadsCallstack(CoutPrint());
						hints << "Free system memory: " << (GetFreeSysMemory() / 1024) << " KB" << endl;
						hints << "Cache size: " << (cCache.GetCacheSize() / 1024) << " KB" << endl;
					}
					else continue;

					// pause for a while, don't be so hard
					if(!wait(25*1000)) return true;
					if(tLX && game.state != Game::S_Quit && oldTime == tLX->currentTime) {
						warnings << "we still are locked after 30 seconds" << endl;
						DumpAllThreadsCallstack(CoutPrint());
						if(tLXOptions && tLXOptions->bFullscreen) {
							notes << "we are in fullscreen, going to window mode now" << endl;
							tLXOptions->bFullscreen = false;
							doSetVideoModeInMainThread();
							notes << "setting window mode sucessfull" << endl;
						}
					}
					else continue;

					// pause for a while, don't be so hard
					if(!wait(25*1000)) return true;
					if(tLX && game.state != Game::S_Quit && oldTime == tLX->currentTime) {
						errors << "we still are locked after 60 seconds" << endl;
						DumpAllThreadsCallstack(CoutPrint());
						if(!AmIBeingDebugged()) {
							errors << "aborting now" << endl;
							abort();
						}
					}
				}
			}
			return true;
		}
	};
	threadPool->start(new MainLockDetector(), "main lock detector", true);
}

struct MainLoopTask : LoopTask {
	enum State {
		State_Startup,
		State_Loop,
		State_Quit
	};
	State state;
	MainLoopTask() : state(State_Startup) {}
	Result handle_Startup();
	Result handle_Loop();
	Result handle_Quit();
	Result handleFrame();
};

// Runs on the main thread.
// Returns false on quit.
static bool handleSDLEvent(SDL_Event& ev) {
	if(ev.type == SDL_USEREVENT) {
		switch(ev.user.code) {
		case UE_QuitEventThread:
			return false;
		case UE_DoVideoFrame:
			videoHandler.frame();
			return true;
		case UE_DoSetVideoMode:
			videoHandler.setVideoMode();
			return true;
		case UE_DoActionInMainThread:
			((Action*)ev.user.data1)->handle();
			delete (Action*)ev.user.data1;
			return true;
		case UE_NopWakeup:
			// do nothing, it's just a wakeup
			return true;
		}
		warnings << "handleSDLEvent: got unknown user event " << (int)ev.user.code << endl;
		return true;
	}
	if( ev.type == SDL_SYSWMEVENT ) {
		EvHndl_SysWmEvent_MainThread( &ev );
		return true;
	}
	mainQueue->push(ev);
	return true;
}

// Get all SDL events and push them to our event queue.
// We have to do that in the same thread where we inited the video because of SDL.
bool handleSDLEvents(bool wait) {
	assert(isMainThread());

	SDL_Event ev;
	memset( &ev, 0, sizeof(ev) );

	if(wait) {
		if( SDL_WaitEvent(&ev) ) {
			if(!handleSDLEvent(ev))
				return false;
		}
		else {
			notes << "error while waiting for next event" << endl;
			SDL_Delay(200);
			return true;
		}
	}

	while( SDL_PollEvent(&ev) ) {
		if(!handleSDLEvent(ev))
			return false;
	}

	return true;
}

void doMainLoop() {
#ifdef SINGLETHREADED
	MainLoopTask mainLoop;
	while(true) {
		// we handle SDL events in doVideoFrameInMainThread
		if(NegResult r = mainLoop.handleFrame())
			break;
	}
#else
	ThreadPoolItem* mainLoopThread = threadPool->start(new MainLoopTask(), "main loop", false);

	startMainLockDetector();

	if(!bDedicated) {
		while(true) {
			if(!handleSDLEvents(true))
				goto quit;
		}
	}

quit:
	threadPool->wait(mainLoopThread, NULL);
	mainLoopThread = NULL;
#endif
}

// note: important to have const char* here because std::string is too dynamic, could be screwed up when returning
void SetCrashHandlerReturnPoint(const char* name) {
#ifndef WIN32
	if(sigsetjmp(longJumpBuffer, true) != 0) {
		hints << "returned from sigsetjmp in " << name << endl;
		if(!tLXOptions) {
			notes << "we already have tLXOptions uninitialised, exiting now" << endl;
			exit(10);
			return;
		}
		if(tLXOptions->bFullscreen) {
			notes << "we are in fullscreen, going to window mode now" << endl;
			tLXOptions->bFullscreen = false;
			doSetVideoModeInMainThread();
			notes << "back in window mode" << endl;
		}
	}
#endif
}

void doVideoFrameInMainThread() {
	if(bDedicated) return;
	if(isMainThread())
		videoHandler.doSingleDirectFrame();
	else
		videoHandler.pushFrame();
}

void doSetVideoModeInMainThread() {
	if(bDedicated) return;
	if(isMainThread())
		videoHandler.setVideoMode();
	else
		videoHandler.requestSetVideoMode();
}

void doVppOperation(Action* act) {
	{
		ScopedLock lock(videoHandler.mutex);
		act->handle();
	}
	delete act;
}

void doActionInMainThread(Action* act) {
	if(bDedicated) {
		warnings << "doActionInMainThread cannot work correctly in dedicated mode" << endl;
		// we will just put it to the main queue instead
		mainQueue->push(act);
		return;
	}

	if(isMainThread()) {
		act->handle();
		delete act;
	}
	else {
		SDL_Event ev;
		ev.type = SDL_USEREVENT;
		ev.user.code = UE_DoActionInMainThread;
		ev.user.data1 = act;
		if(SDL_PushEvent(&ev) != 0) {
			errors << "failed to push custom action event" << endl;
		}
	}
}

Result MainLoopTask::handle_Startup() {
	gameloopThreadId = getCurrentThreadId();
	assert(gameloopThreadId != (ThreadId)-1);
	setCurThreadPriority(0.5f);

	// NOTE: This code is really old and outdated.
	// We might just merge that with the new code.
	// Otherwise, it will never work like this because of
	// missing stuff, e.g. no game.init call (which does
	// also init the old menu, so we cannot simply call it
	// like this right now).
	if( tLXOptions->bNewSkinnedGUI )
	{
		// Just for test - it's not real handler yet
		SkinnedGUI::cMainSkin->Load("default");
		SkinnedGUI::cMainSkin->OpenLayout("test.skn");
		while (game.state != Game::S_Quit)  {
			tLX->fDeltaTime = GetTime() - tLX->currentTime;
			tLX->currentTime = GetTime();

			ProcessEvents();
			SkinnedGUI::cMainSkin->Frame();
		}

		return "quit";
	}

	state = State_Loop;
	return true;
}

Result MainLoopTask::handle_Loop() {
	if(game.state == Game::S_Quit) {
		state = State_Quit;
		return true;
	}

	game.frame();
	return true;
}

Result MainLoopTask::handle_Quit() {
	if(!isMainThread()) {
		SDL_Event quitEv = QuitEventThreadEvent();
		if(!bDedicated)
			while(SDL_PushEvent(&quitEv) < 0) {}
	}
	gameloopThreadId = (ThreadId)-1;
	return "quit";
}

Result MainLoopTask::handleFrame() {
	switch(state) {
	case State_Startup: return handle_Startup();
	case State_Loop: return handle_Loop();
	case State_Quit: return handle_Quit();
	}
	return "invalid state";
}

<|endoftext|>


<commit_before>#pragma once

#include <stdexcept>
#include <variant>
#include <string>
#include <string_view>
#include <vector>
#include <iostream>

#include "acmacs-base/float.hh"
#include "acmacs-base/filesystem.hh"

// ----------------------------------------------------------------------

namespace rjson
{
    namespace implementation { class NumberHandler; }

    class value;

    class string
    {
     public:
        inline string(std::string aData) : mData{aData} {}
        inline string(const char* aData) : mData{aData} {}
        inline std::string to_json() const { return std::string{"\""} + static_cast<std::string>(mData) + "\""; }
        inline operator std::string() const { return mData; }
        inline string& operator=(std::string aSrc) { mData = aSrc; return *this; }
        inline bool operator==(const std::string aToCompare) const { return mData == aToCompare; }
        inline bool operator==(const string& aToCompare) const { return mData == aToCompare.mData; }

     private:
        std::string mData;
    };

    class boolean
    {
     public:
        inline boolean(bool aValue) : mValue{aValue} {}
        inline std::string to_json() const { return mValue ? "true" : "false"; }
        inline operator bool() const { return mValue; }
        inline boolean& operator=(bool aSrc) { mValue = aSrc; return *this; }

     private:
        bool mValue;
    };

    class null
    {
     public:
        inline null() {}
        inline std::string to_json() const { return "null"; }
    };

    class number
    {
     public:
        inline std::string to_json() const { return mValue; } // { return double_to_string(mValue); }
        inline operator double() const { return std::stod(mValue); } // { return mValue; }
        inline number& operator=(double aSrc) { mValue = double_to_string(aSrc); return *this; }

     private:
        inline number(std::string_view&& aData) : mValue{aData} {} // mValue{std::stod(static_cast<std::string>(aData))} {}

          // double mValue;
        std::string mValue;

        friend class implementation::NumberHandler;
    };

    class integer
    {
     public:
        inline std::string to_json() const { return mValue; } // { return std::to_string(mValue); }
        inline operator double() const { return std::stod(mValue); } // { return mValue; }
        inline operator long() const { return std::stol(mValue); } // { return mValue; }
        inline integer& operator=(long aSrc) { mValue = std::to_string(aSrc); return *this; }

     private:
        inline integer(std::string_view&& aData) : mValue{aData} {} // mValue{std::stol(static_cast<std::string>(aData))} {}

          // long mValue;
        std::string mValue;

        friend class implementation::NumberHandler;
    };

    class object
    {
     public:
        std::string to_json() const;

        void insert(value&& aKey, value&& aValue);

          // returns reference to the value at the passed key.
          // if key not found, inserts aDefault with the passed key and returns reference to the inserted
        value& get_ref(std::string aKey, value&& aDefault);
        void set_field(std::string aKey, value&& aValue);

     private:
        std::vector<std::pair<string, value>> mContent;
    };

    class array
    {
     public:
        std::string to_json() const;

        void insert(value&& aValue);

     private:
        std::vector<value> mContent;
    };

    namespace implementation
    {
        template <typename F> struct content_type;
        template <> struct content_type<double> { using type = rjson::number; };
        template <> struct content_type<long> { using type = rjson::integer; };
        template <> struct content_type<int> { using type = rjson::integer; };
        template <> struct content_type<bool> { using type = rjson::boolean; };
        template <> struct content_type<std::string> { using type = rjson::string; };
    }

    using value_base = std::variant<null, object, array, string, integer, number, boolean>; // null must be the first alternative, it is the default value;

    class value : public value_base
    {
     public:
        using value_base::operator=;
        using value_base::value_base;
        inline value(const value&) = default; // gcc7 wants this, otherwise it is deleted
        inline value& operator=(const value&) = default; // gcc7 wants this, otherwise it is deleted

          // returns reference to the value at the passed key.
          // if key not found, inserts aDefault with the passed key and returns reference to the inserted
          // if this is not an object, throws  std::bad_variant_access
        inline value& get_ref(std::string aKey, value&& aDefault)
            {
                return std::get<object>(*this).get_ref(aKey, std::forward<value>(aDefault));
            }

        template <typename F> inline std::decay_t<F> get_field(std::string aFieldName, F&& aDefaultValue)
            {
                using type = typename implementation::content_type<std::decay_t<F>>::type;
                return std::get<type>(get_ref(aFieldName, std::forward<value>(aDefaultValue)));
            }

        template <typename F> inline void set_field(std::string aFieldName, F&& aValue)
            {
                std::get<object>(*this).set_field(aFieldName, std::forward<value>(aValue));
            }

        std::string to_json() const;
    };

      // ----------------------------------------------------------------------

    template <typename FValue> class field_get_set
    {
     public:
        inline field_get_set(std::string aFieldName, FValue&& aDefault) : mFieldName{aFieldName}, mDefault{std::move(aDefault)} {}
        inline operator FValue() const { return mContainer->get_field(mFieldName, mDefault); }
        inline void operator = (FValue&& aValue) { mContainer->set_field(mFieldName, std::forward<FValue>(aValue)); }
        inline void container(value* aContainer) { mContainer = aContainer; }

     private:
        mutable value* mContainer = nullptr;
        std::string mFieldName;
        FValue mDefault;
    };

      // ----------------------------------------------------------------------

    class Error : public std::exception
    {
     public:
        // inline Error(size_t aLine, size_t aColumn, std::string aMessage)
        //     : mMessage{std::to_string(aLine) + ":" + std::to_string(aColumn) + ": " + aMessage} //, mLine{aLine}, mColumn{aColumn}
        //     {}

        inline Error(size_t aLine, size_t aColumn, std::string&& aMessage)
            : mMessage{std::to_string(aLine) + ":" + std::to_string(aColumn) + ": " + std::move(aMessage)} //, mLine{aLine}, mColumn{aColumn}
            {}

        inline const char* what() const noexcept override { return mMessage.c_str(); }

     private:
        std::string mMessage;
          //size_t mLine, mColumn;

    }; // class Error

    value parse_string(std::string aJsonData);
    value parse_file(std::string aFilename);

} // namespace rjson

// ----------------------------------------------------------------------
// gcc-7 support
// ----------------------------------------------------------------------

#if __GNUC__ == 7
namespace std
{
      // gcc 7.2 wants those, if we derive from std::variant
    template<> struct variant_size<rjson::value> : variant_size<rjson::value_base> {};
    template<size_t _Np> struct variant_alternative<_Np, rjson::value> : variant_alternative<_Np, rjson::value_base> {};
}
#endif

// ----------------------------------------------------------------------
// inline
// ----------------------------------------------------------------------

namespace rjson
{
    inline void object::insert(value&& aKey, value&& aValue)
    {
        mContent.emplace_back(std::get<rjson::string>(aKey), aValue);
    }

    inline value& object::get_ref(std::string aKey, value&& aDefault)
    {
        auto found = std::find_if(std::begin(mContent), std::end(mContent), [&aKey](const auto& entry) { return entry.first == aKey; });
        if (found == std::end(mContent)) {
              // std::cerr << "DEBUG: object::get_ref: not found: " << aKey << ' ' << to_json() << " default:" << aDefault.to_json() << '\n';
            return mContent.emplace_back(std::move(aKey), std::forward<value>(aDefault)).second;
        }
        else {
              // std::d::cerr << "DEBUG: object::get_ref: found: " << aKey << ' ' << found->second.to_json() << '\n';
            return found->second;
        }
    }

    inline void object::set_field(std::string aKey, value&& aValue)
    {
        auto found = std::find_if(std::begin(mContent), std::end(mContent), [&aKey](const auto& entry) { return entry.first == aKey; });
        if (found == std::end(mContent)) {
            mContent.emplace_back(std::move(aKey), std::forward<value>(aValue));
        }
        else {
            found->second = std::forward<value>(aValue);
        }
    }

      // ----------------------------------------------------------------------

    inline void array::insert(value&& aValue)
    {
        mContent.push_back(std::move(aValue));
    }

      // ----------------------------------------------------------------------

    inline std::string value::to_json() const
    {
        return std::visit([](auto&& arg) -> std::string { return arg.to_json(); }, *this);
    }
}

// ----------------------------------------------------------------------

inline std::ostream& operator<<(std::ostream& out, const rjson::value& aValue)
{
    return out << aValue.to_json();
}

template <typename FValue> inline std::ostream& operator<<(std::ostream& out, const rjson::field_get_set<FValue>& aValue)
{
    return out << static_cast<FValue>(aValue);
}

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<commit_msg>rjson development (rjson::field_container support)<commit_after>#pragma once

#include <stdexcept>
#include <variant>
#include <string>
#include <string_view>
#include <vector>
#include <iostream>
#include <cassert>

#include "acmacs-base/float.hh"
#include "acmacs-base/filesystem.hh"

// ----------------------------------------------------------------------

namespace rjson
{
    namespace implementation { class NumberHandler; }

    class value;

    class string
    {
     public:
        inline string(std::string aData) : mData{aData} {}
        inline string(const char* aData) : mData{aData} {}
        inline std::string to_json() const { return std::string{"\""} + static_cast<std::string>(mData) + "\""; }
        inline operator std::string() const { return mData; }
        inline string& operator=(std::string aSrc) { mData = aSrc; return *this; }
        inline bool operator==(const std::string aToCompare) const { return mData == aToCompare; }
        inline bool operator==(const string& aToCompare) const { return mData == aToCompare.mData; }

     private:
        std::string mData;
    };

    class boolean
    {
     public:
        inline boolean(bool aValue) : mValue{aValue} {}
        inline std::string to_json() const { return mValue ? "true" : "false"; }
        inline operator bool() const { return mValue; }
        inline boolean& operator=(bool aSrc) { mValue = aSrc; return *this; }

     private:
        bool mValue;
    };

    class null
    {
     public:
        inline null() {}
        inline std::string to_json() const { return "null"; }
    };

    class number
    {
     public:
        inline std::string to_json() const { return mValue; } // { return double_to_string(mValue); }
        inline operator double() const { return std::stod(mValue); } // { return mValue; }
        inline number& operator=(double aSrc) { mValue = double_to_string(aSrc); return *this; }

     private:
        inline number(std::string_view&& aData) : mValue{aData} {} // mValue{std::stod(static_cast<std::string>(aData))} {}

          // double mValue;
        std::string mValue;

        friend class implementation::NumberHandler;
    };

    class integer
    {
     public:
        inline std::string to_json() const { return mValue; } // { return std::to_string(mValue); }
        inline operator double() const { return std::stod(mValue); } // { return mValue; }
        inline operator long() const { return std::stol(mValue); } // { return mValue; }
        inline integer& operator=(long aSrc) { mValue = std::to_string(aSrc); return *this; }

     private:
        inline integer(std::string_view&& aData) : mValue{aData} {} // mValue{std::stol(static_cast<std::string>(aData))} {}

          // long mValue;
        std::string mValue;

        friend class implementation::NumberHandler;
    };

    class object
    {
     public:
        std::string to_json() const;

        void insert(value&& aKey, value&& aValue);

          // returns reference to the value at the passed key.
          // if key not found, inserts aDefault with the passed key and returns reference to the inserted
        value& get_ref(std::string aKey, value&& aDefault);
        void set_field(std::string aKey, value&& aValue);

     private:
        std::vector<std::pair<string, value>> mContent;
    };

    class array
    {
     public:
        std::string to_json() const;

        void insert(value&& aValue);

     private:
        std::vector<value> mContent;
    };

    namespace implementation
    {
        template <typename F> struct content_type;
        template <> struct content_type<double> { using type = rjson::number; };
        template <> struct content_type<long> { using type = rjson::integer; };
        template <> struct content_type<int> { using type = rjson::integer; };
        template <> struct content_type<bool> { using type = rjson::boolean; };
        template <> struct content_type<std::string> { using type = rjson::string; };
    }

    using value_base = std::variant<null, object, array, string, integer, number, boolean>; // null must be the first alternative, it is the default value;

    class value : public value_base
    {
     public:
        using value_base::operator=;
        using value_base::value_base;
        inline value(const value&) = default; // gcc7 wants this, otherwise it is deleted
        inline value& operator=(const value&) = default; // gcc7 wants this, otherwise it is deleted

          // returns reference to the value at the passed key.
          // if key not found, inserts aDefault with the passed key and returns reference to the inserted
          // if this is not an object, throws  std::bad_variant_access
        inline value& get_ref(std::string aKey, value&& aDefault)
            {
                return std::get<object>(*this).get_ref(aKey, std::forward<value>(aDefault));
            }

        template <typename F> inline std::decay_t<F> get_field(std::string aFieldName, F&& aDefaultValue)
            {
                using type = typename implementation::content_type<std::decay_t<F>>::type;
                return std::get<type>(get_ref(aFieldName, std::forward<value>(aDefaultValue)));
            }

        template <typename F> inline void set_field(std::string aFieldName, F&& aValue)
            {
                std::get<object>(*this).set_field(aFieldName, std::forward<value>(aValue));
            }

        std::string to_json() const;
    };

      // ----------------------------------------------------------------------

    class field_container
    {
     public:
        inline void use_json(value& aData) { mData = &aData; }
        inline operator value&() { assert(mData); return *mData; }
        inline operator const value&() const { assert(mData); return *mData; }
        inline std::string json() const { return static_cast<const value&>(*this).to_json(); }

     private:
        mutable value* mData = nullptr;
    };

    template <typename FValue> class field_get_set
    {
     public:
        inline field_get_set(field_container* aContainer, std::string aFieldName, FValue&& aDefault) : mContainer{*aContainer}, mFieldName{aFieldName}, mDefault{std::move(aDefault)} {}
        inline operator FValue() const { return static_cast<value&>(mContainer).get_field(mFieldName, mDefault); } // static_cast to non-const because we need to set to mDefault
        inline field_get_set<FValue>& operator = (FValue&& aValue) { static_cast<value&>(mContainer).set_field(mFieldName, std::forward<FValue>(aValue)); return *this; }
        inline field_get_set<FValue>& operator = (const field_get_set<FValue>& aSource) { return operator=(static_cast<FValue>(aSource)); }

     private:
        field_container& mContainer;
        std::string mFieldName;
        FValue mDefault;
    };

      // ----------------------------------------------------------------------

    class Error : public std::exception
    {
     public:
        // inline Error(size_t aLine, size_t aColumn, std::string aMessage)
        //     : mMessage{std::to_string(aLine) + ":" + std::to_string(aColumn) + ": " + aMessage} //, mLine{aLine}, mColumn{aColumn}
        //     {}

        inline Error(size_t aLine, size_t aColumn, std::string&& aMessage)
            : mMessage{std::to_string(aLine) + ":" + std::to_string(aColumn) + ": " + std::move(aMessage)} //, mLine{aLine}, mColumn{aColumn}
            {}

        inline const char* what() const noexcept override { return mMessage.c_str(); }

     private:
        std::string mMessage;
          //size_t mLine, mColumn;

    }; // class Error

    value parse_string(std::string aJsonData);
    value parse_file(std::string aFilename);

} // namespace rjson

// ----------------------------------------------------------------------
// gcc-7 support
// ----------------------------------------------------------------------

#if __GNUC__ == 7
namespace std
{
      // gcc 7.2 wants those, if we derive from std::variant
    template<> struct variant_size<rjson::value> : variant_size<rjson::value_base> {};
    template<size_t _Np> struct variant_alternative<_Np, rjson::value> : variant_alternative<_Np, rjson::value_base> {};
}
#endif

// ----------------------------------------------------------------------
// inline
// ----------------------------------------------------------------------

namespace rjson
{
    inline void object::insert(value&& aKey, value&& aValue)
    {
        mContent.emplace_back(std::get<rjson::string>(aKey), aValue);
    }

    inline value& object::get_ref(std::string aKey, value&& aDefault)
    {
        auto found = std::find_if(std::begin(mContent), std::end(mContent), [&aKey](const auto& entry) { return entry.first == aKey; });
        if (found == std::end(mContent)) {
              // std::cerr << "DEBUG: object::get_ref: not found: " << aKey << ' ' << to_json() << " default:" << aDefault.to_json() << '\n';
            return mContent.emplace_back(std::move(aKey), std::forward<value>(aDefault)).second;
        }
        else {
              // std::d::cerr << "DEBUG: object::get_ref: found: " << aKey << ' ' << found->second.to_json() << '\n';
            return found->second;
        }
    }

    inline void object::set_field(std::string aKey, value&& aValue)
    {
        auto found = std::find_if(std::begin(mContent), std::end(mContent), [&aKey](const auto& entry) { return entry.first == aKey; });
        if (found == std::end(mContent)) {
            mContent.emplace_back(std::move(aKey), std::forward<value>(aValue));
        }
        else {
            found->second = std::forward<value>(aValue);
        }
    }

      // ----------------------------------------------------------------------

    inline void array::insert(value&& aValue)
    {
        mContent.push_back(std::move(aValue));
    }

      // ----------------------------------------------------------------------

    inline std::string value::to_json() const
    {
        return std::visit([](auto&& arg) -> std::string { return arg.to_json(); }, *this);
    }
}

// ----------------------------------------------------------------------

inline std::ostream& operator<<(std::ostream& out, const rjson::value& aValue)
{
    return out << aValue.to_json();
}

template <typename FValue> inline std::ostream& operator<<(std::ostream& out, const rjson::field_get_set<FValue>& aValue)
{
    return out << static_cast<FValue>(aValue);
}

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<|endoftext|>