<commit_before>/*===================================================================

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.

This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.

See LICENSE.txt or http://www.mitk.org for details.

===================================================================*/
#include "QmitkCreatePolygonModelAction.h"

// MITK
#include <mitkShowSegmentationAsSmoothedSurface.h>
#include <mitkShowSegmentationAsSurface.h>
#include <mitkProgressBar.h>
#include <mitkStatusBar.h>

#include <mitkIRenderWindowPart.h>
#include <mitkIRenderingManager.h>

// Blueberry
#include <berryIPreferencesService.h>
#include <berryIPreferences.h>
#include <berryPlatform.h>
#include <berryIWorkbenchPage.h>

using namespace berry;
using namespace mitk;
using namespace std;

QmitkCreatePolygonModelAction::QmitkCreatePolygonModelAction()
{
}

QmitkCreatePolygonModelAction::~QmitkCreatePolygonModelAction()
{
}

void QmitkCreatePolygonModelAction::Run(const QList<DataNode::Pointer> &selectedNodes)
{
  DataNode::Pointer selectedNode = selectedNodes[0];
  Image::Pointer image = dynamic_cast<mitk::Image *>(selectedNode->GetData());

  if (image.IsNull())
  {
    return;
  }

  try
  {
    // Get preference properties for smoothing and decimation
    IPreferencesService* prefService = Platform::GetPreferencesService();
    IPreferences::Pointer segPref = prefService->GetSystemPreferences()->Node("/org.mitk.views.segmentation");

    bool smoothingHint = segPref->GetBool("smoothing hint", true);
    ScalarType smoothing = segPref->GetDouble("smoothing value", 1.0);
    ScalarType decimation = segPref->GetDouble("decimation rate", 0.5);

    if (smoothingHint)
    {
      smoothing = 0.0;
      Vector3D spacing = image->GetGeometry()->GetSpacing();

      for (Vector3D::Iterator iter = spacing.Begin(); iter != spacing.End(); ++iter)
        smoothing = max(smoothing, *iter);
    }

    ShowSegmentationAsSurface::Pointer surfaceFilter = ShowSegmentationAsSurface::New();

    // Activate callback functions
    itk::SimpleMemberCommand<QmitkCreatePolygonModelAction>::Pointer successCommand = itk::SimpleMemberCommand<QmitkCreatePolygonModelAction>::New();
    successCommand->SetCallbackFunction(this, &QmitkCreatePolygonModelAction::OnSurfaceCalculationDone);
    surfaceFilter->AddObserver(ResultAvailable(), successCommand);

    itk::SimpleMemberCommand<QmitkCreatePolygonModelAction>::Pointer errorCommand = itk::SimpleMemberCommand<QmitkCreatePolygonModelAction>::New();
    errorCommand->SetCallbackFunction(this, &QmitkCreatePolygonModelAction::OnSurfaceCalculationDone);
    surfaceFilter->AddObserver(ProcessingError(), errorCommand);

    // set filter parameter
    surfaceFilter->SetDataStorage(*m_DataStorage);
    surfaceFilter->SetPointerParameter("Input", image);
    surfaceFilter->SetPointerParameter("Group node", selectedNode);
    surfaceFilter->SetParameter("Show result", true);
    surfaceFilter->SetParameter("Sync visibility", false);
    surfaceFilter->SetParameter("Median kernel size", 3u);
    surfaceFilter->SetParameter("Decimate mesh", m_IsDecimated);
    surfaceFilter->SetParameter("Decimation rate", (float) decimation);

    if (m_IsSmoothed)
    {
      surfaceFilter->SetParameter("Apply median", true);
      surfaceFilter->SetParameter("Smooth", true);
      surfaceFilter->SetParameter("Gaussian SD", sqrt(smoothing)); // use sqrt to account for setting of variance in preferences
      StatusBar::GetInstance()->DisplayText("Smoothed surface creation started in background...");
    }
    else
    {
      surfaceFilter->SetParameter("Apply median", false);
      surfaceFilter->SetParameter("Smooth", false);
      StatusBar::GetInstance()->DisplayText("Surface creation started in background...");
    }

    surfaceFilter->StartAlgorithm();
  }
  catch(...)
  {
    MITK_ERROR << "Surface creation failed!";
  }
}

void QmitkCreatePolygonModelAction::OnSurfaceCalculationDone()
{
  StatusBar::GetInstance()->Clear();
}

void QmitkCreatePolygonModelAction::SetDataStorage(DataStorage *dataStorage)
{
  m_DataStorage = dataStorage;
}

void QmitkCreatePolygonModelAction::SetSmoothed(bool smoothed)
{
  m_IsSmoothed = smoothed;
}

void QmitkCreatePolygonModelAction::SetDecimated(bool decimated)
{
  m_IsDecimated = decimated;
}

void QmitkCreatePolygonModelAction::SetFunctionality(QtViewPart *)
{
}
<commit_msg>Unify parameter type<commit_after>/*===================================================================

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.

This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.

See LICENSE.txt or http://www.mitk.org for details.

===================================================================*/
#include "QmitkCreatePolygonModelAction.h"

// MITK
#include <mitkShowSegmentationAsSmoothedSurface.h>
#include <mitkShowSegmentationAsSurface.h>
#include <mitkProgressBar.h>
#include <mitkStatusBar.h>

#include <mitkIRenderWindowPart.h>
#include <mitkIRenderingManager.h>

// Blueberry
#include <berryIPreferencesService.h>
#include <berryIPreferences.h>
#include <berryPlatform.h>
#include <berryIWorkbenchPage.h>

using namespace berry;
using namespace mitk;
using namespace std;

QmitkCreatePolygonModelAction::QmitkCreatePolygonModelAction()
{
}

QmitkCreatePolygonModelAction::~QmitkCreatePolygonModelAction()
{
}

void QmitkCreatePolygonModelAction::Run(const QList<DataNode::Pointer> &selectedNodes)
{
  DataNode::Pointer selectedNode = selectedNodes[0];
  Image::Pointer image = dynamic_cast<mitk::Image *>(selectedNode->GetData());

  if (image.IsNull())
  {
    return;
  }

  try
  {
    // Get preference properties for smoothing and decimation
    IPreferencesService* prefService = Platform::GetPreferencesService();
    IPreferences::Pointer segPref = prefService->GetSystemPreferences()->Node("/org.mitk.views.segmentation");

    bool smoothingHint = segPref->GetBool("smoothing hint", true);
    ScalarType smoothing = segPref->GetDouble("smoothing value", 1.0);
    ScalarType decimation = segPref->GetDouble("decimation rate", 0.5);

    if (smoothingHint)
    {
      smoothing = 0.0;
      Vector3D spacing = image->GetGeometry()->GetSpacing();

      for (Vector3D::Iterator iter = spacing.Begin(); iter != spacing.End(); ++iter)
        smoothing = max(smoothing, *iter);
    }

    ShowSegmentationAsSurface::Pointer surfaceFilter = ShowSegmentationAsSurface::New();

    // Activate callback functions
    itk::SimpleMemberCommand<QmitkCreatePolygonModelAction>::Pointer successCommand = itk::SimpleMemberCommand<QmitkCreatePolygonModelAction>::New();
    successCommand->SetCallbackFunction(this, &QmitkCreatePolygonModelAction::OnSurfaceCalculationDone);
    surfaceFilter->AddObserver(ResultAvailable(), successCommand);

    itk::SimpleMemberCommand<QmitkCreatePolygonModelAction>::Pointer errorCommand = itk::SimpleMemberCommand<QmitkCreatePolygonModelAction>::New();
    errorCommand->SetCallbackFunction(this, &QmitkCreatePolygonModelAction::OnSurfaceCalculationDone);
    surfaceFilter->AddObserver(ProcessingError(), errorCommand);

    // set filter parameter
    surfaceFilter->SetDataStorage(*m_DataStorage);
    surfaceFilter->SetPointerParameter("Input", image);
    surfaceFilter->SetPointerParameter("Group node", selectedNode);
    surfaceFilter->SetParameter("Show result", true);
    surfaceFilter->SetParameter("Sync visibility", false);
    surfaceFilter->SetParameter("Median kernel size", 3u);
    surfaceFilter->SetParameter("Decimate mesh", m_IsDecimated);
    surfaceFilter->SetParameter("Decimation rate", decimation);

    if (m_IsSmoothed)
    {
      surfaceFilter->SetParameter("Apply median", true);
      surfaceFilter->SetParameter("Smooth", true);
      surfaceFilter->SetParameter("Gaussian SD", sqrt(smoothing)); // use sqrt to account for setting of variance in preferences
      StatusBar::GetInstance()->DisplayText("Smoothed surface creation started in background...");
    }
    else
    {
      surfaceFilter->SetParameter("Apply median", false);
      surfaceFilter->SetParameter("Smooth", false);
      StatusBar::GetInstance()->DisplayText("Surface creation started in background...");
    }

    surfaceFilter->StartAlgorithm();
  }
  catch(...)
  {
    MITK_ERROR << "Surface creation failed!";
  }
}

void QmitkCreatePolygonModelAction::OnSurfaceCalculationDone()
{
  StatusBar::GetInstance()->Clear();
}

void QmitkCreatePolygonModelAction::SetDataStorage(DataStorage *dataStorage)
{
  m_DataStorage = dataStorage;
}

void QmitkCreatePolygonModelAction::SetSmoothed(bool smoothed)
{
  m_IsSmoothed = smoothed;
}

void QmitkCreatePolygonModelAction::SetDecimated(bool decimated)
{
  m_IsDecimated = decimated;
}

void QmitkCreatePolygonModelAction::SetFunctionality(QtViewPart *)
{
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2019 Arm Limited and affiliates.
 * SPDX-License-Identifier: Apache-2.0
 *
 * 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.
 */
#ifdef DEVICE_VIRTUAL_WATCHDOG

#include "VirtualWatchdog.h"
#include "Watchdog.h"

namespace mbed {

VirtualWatchdog *VirtualWatchdog::_first = NULL;

VirtualWatchdog::VirtualWatchdog(uint32_t timeout, const char *const str): _name(str)
{
    _current_count = 0;
    _is_initialized = false;
    _next = NULL;
    _max_timeout = timeout;
    // start watchdog
     Watchdog& watchdog = Watchdog::get_instance();
     watchdog.start();
}

VirtualWatchdog::~VirtualWatchdog()
{
    if (_is_initialized) {
        stop();
        // we do not need to stop hw watchdog, it's ticking by itself
    }
}

void VirtualWatchdog::start()
{
    MBED_ASSERT(!_is_initialized);
    core_util_critical_section_enter();
    add_to_list();
    core_util_critical_section_exit();
}

void VirtualWatchdog::kick()
{
    MBED_ASSERT(_is_initialized);
    core_util_critical_section_enter();
    _current_count = 0;
    core_util_critical_section_exit();
}

void VirtualWatchdog::stop()
{
    MBED_ASSERT(_is_initialized);
    core_util_critical_section_enter();
    remove_from_list();
    core_util_critical_section_exit();
}

void VirtualWatchdog::add_to_list()
{
    this->_next = _first;
    _first = this;
    _is_initialized =  true;
}

void VirtualWatchdog::remove_from_list()
{
    VirtualWatchdog *cur_ptr = _first,
              *prev_ptr = NULL;
    while (cur_ptr != NULL) {
        if (cur_ptr == this) {
            if (cur_ptr == _first) {
                prev_ptr = _first;
                _first = cur_ptr->_next;
                prev_ptr->_next = NULL;
            } else {
                prev_ptr->_next = cur_ptr->_next;
                cur_ptr->_next = NULL;
            }
            _is_initialized = false;
            break;
        } else {
            prev_ptr = cur_ptr;
            cur_ptr = cur_ptr->_next;
        }
    }
}

void VirtualWatchdog::process(uint32_t elapsed_ms)
{
    VirtualWatchdog *cur_ptr =  _first;
    while (cur_ptr != NULL) {
        if (cur_ptr->_current_count > cur_ptr->_max_timeout) {
            system_reset();
        } else {
            cur_ptr->_current_count += elapsed_ms;
        }
        cur_ptr = cur_ptr->_next;
    }
}

} // namespace mbed

#endif // DEVICE_VIRTUAL_WATCHDOG
<commit_msg>VirtualWatchdog: fix protection macro<commit_after>/*
 * Copyright (c) 2019 Arm Limited and affiliates.
 * SPDX-License-Identifier: Apache-2.0
 *
 * 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.
 */
#ifdef DEVICE_WATCHDOG

#include "VirtualWatchdog.h"
#include "Watchdog.h"

namespace mbed {

VirtualWatchdog *VirtualWatchdog::_first = NULL;

VirtualWatchdog::VirtualWatchdog(uint32_t timeout, const char *const str): _name(str)
{
    _current_count = 0;
    _is_initialized = false;
    _next = NULL;
    _max_timeout = timeout;
    // start watchdog
     Watchdog& watchdog = Watchdog::get_instance();
     watchdog.start();
}

VirtualWatchdog::~VirtualWatchdog()
{
    if (_is_initialized) {
        stop();
        // we do not need to stop hw watchdog, it's ticking by itself
    }
}

void VirtualWatchdog::start()
{
    MBED_ASSERT(!_is_initialized);
    core_util_critical_section_enter();
    add_to_list();
    core_util_critical_section_exit();
}

void VirtualWatchdog::kick()
{
    MBED_ASSERT(_is_initialized);
    core_util_critical_section_enter();
    _current_count = 0;
    core_util_critical_section_exit();
}

void VirtualWatchdog::stop()
{
    MBED_ASSERT(_is_initialized);
    core_util_critical_section_enter();
    remove_from_list();
    core_util_critical_section_exit();
}

void VirtualWatchdog::add_to_list()
{
    this->_next = _first;
    _first = this;
    _is_initialized =  true;
}

void VirtualWatchdog::remove_from_list()
{
    VirtualWatchdog *cur_ptr = _first,
              *prev_ptr = NULL;
    while (cur_ptr != NULL) {
        if (cur_ptr == this) {
            if (cur_ptr == _first) {
                prev_ptr = _first;
                _first = cur_ptr->_next;
                prev_ptr->_next = NULL;
            } else {
                prev_ptr->_next = cur_ptr->_next;
                cur_ptr->_next = NULL;
            }
            _is_initialized = false;
            break;
        } else {
            prev_ptr = cur_ptr;
            cur_ptr = cur_ptr->_next;
        }
    }
}

void VirtualWatchdog::process(uint32_t elapsed_ms)
{
    VirtualWatchdog *cur_ptr =  _first;
    while (cur_ptr != NULL) {
        if (cur_ptr->_current_count > cur_ptr->_max_timeout) {
            system_reset();
        } else {
            cur_ptr->_current_count += elapsed_ms;
        }
        cur_ptr = cur_ptr->_next;
    }
}

} // namespace mbed

#endif // DEVICE_WATCHDOG
<|endoftext|>


<commit_before>#pragma once

#include <iostream>
#include <iomanip>
#include <string.h>

// void bar();
// void print(const int it, const double energy);
// std::string red(const std::string&);

template<typename T>
std::string to_string(const T& t)
{
    return std::to_string(t);
}

        std::string red(const std::string& s)
        {
            return "\033[31m" + s + "\033[0m";
        }

        template<typename T>
        std::string red(const T& t)
        {
            return "\033[31m" + to_string(t) + "\033[0m";
        }

        std::string green(const std::string& s)
        {
            return "\033[32m" + s + "\033[0m";
        }

        template<typename T>
        std::string green(const T& t)
        {
            return "\033[32m" + to_string(t) + "\033[0m";
        }

        std::string white(const std::string& s)
        {
            return "\033[37m" + s + "\033[0m";
        }

        template<typename T>
        std::string white(const T& t)
        {
            return "\033[37m" + to_string(t) + "\033[0m";
        }

void bar()
{
    // std::cout << "it\tT\tDT\tE\tDE" << std::endl;
    std::cout << "T\tDT\tE\tDE" << std::endl;
    std::cout << std::string(60, '-') << std::endl;
}

// template<typename Solver, typename Color>
// template<typename Solver, typename T, typename Color = std::string (*Color)(T&)>
// template<typename T, std::string (*Color)(const T&), typename Solver>
// void print(
//       const Solver& s
//     // , const Color& c
//     )
// {
//     std::cout << Color(s.current_it) << "\t";
//     std::cout << Color(s.current_temperature) << "\t";
//     std::cout << Color(s.current_temperature - s.temperature(s.current_temperature)) << "\t";
//     std::cout << Color(s.current_energy) << "\t";
//     // // std::cout << s.d_energy << "\t";
//     std::cout << std::endl;
// }

struct Accepted{};
struct Rejected{};

template<typename Solver>
void print(
      const Solver& s
    , Accepted
    )
{
    // std::cout << green(s.current_it) << "\t";
    std::cout << green(s.current_temperature) << "\t";
    std::cout << green(s.current_temperature - s.temperature()) << "\t";
    // std::cout << std::hexfloat << std::setprecision(3) << green(s.current_energy) << "\t";
    std::cout << green(s.current_energy) << "\t";
    // // std::cout << s.d_energy << "\t";
    std::cout << std::endl;
}

template<typename Solver>
void print(
      const Solver& s
    , Rejected
    )
{
    // std::cout << red(s.current_it) << "\t";
    std::cout << red(s.current_temperature) << "\t";
    std::cout << red(s.current_temperature - s.temperature()) << "\t";
    // std::cout << std::hexfloat << std::setprecision(3) << red(s.current_energy) << "\t";
    std::cout << red(s.current_energy) << "\t";
    // // std::cout << s.d_energy << "\t";

    // std::cout << std::string(60, '\b');
    std::cout << std::endl;
}

template<typename Solver>
void final_print(const Solver& s)
{
    std::cout << std::endl;
    std::cout << "Final temperature: " << s.current_temperature << "\n";
    std::cout << "Final energy: " << s.current_energy << "\n";
    std::cout << std::endl;
}
<commit_msg>[display] added two terminal colors<commit_after>#pragma once

#include <iostream>
#include <iomanip>
#include <string.h>

// void bar();
// void print(const int it, const double energy);
// std::string red(const std::string&);

template<typename T>
std::string to_string(const T& t)
{
    return std::to_string(t);
}

        std::string red(const std::string& s)
        {
            return "\033[31m" + s + "\033[0m";
        }

        template<typename T>
        std::string red(const T& t)
        {
            return "\033[31m" + to_string(t) + "\033[0m";
        }

        std::string green(const std::string& s)
        {
            return "\033[32m" + s + "\033[0m";
        }

        template<typename T>
        std::string green(const T& t)
        {
            return "\033[32m" + to_string(t) + "\033[0m";
        }

        std::string yellow(const std::string& s)
        {
            return "\033[33m" + s + "\033[0m";
        }

        template<typename T>
        std::string yellow(const T& t)
        {
            return "\033[33m" + to_string(t) + "\033[0m";
        }

        std::string blue(const std::string& s)
        {
            return "\033[34m" + s + "\033[0m";
        }

        template<typename T>
        std::string blue(const T& t)
        {
            return "\033[34m" + to_string(t) + "\033[0m";
        }

        std::string white(const std::string& s)
        {
            return "\033[37m" + s + "\033[0m";
        }

        template<typename T>
        std::string white(const T& t)
        {
            return "\033[37m" + to_string(t) + "\033[0m";
        }

void bar()
{
    // std::cout << "it\tT\tDT\tE\tDE" << std::endl;
    std::cout << "T\tDT\tE\tDE" << std::endl;
    std::cout << std::string(60, '-') << std::endl;
}

// template<typename Solver, typename Color>
// template<typename Solver, typename T, typename Color = std::string (*Color)(T&)>
// template<typename T, std::string (*Color)(const T&), typename Solver>
// void print(
//       const Solver& s
//     // , const Color& c
//     )
// {
//     std::cout << Color(s.current_it) << "\t";
//     std::cout << Color(s.current_temperature) << "\t";
//     std::cout << Color(s.current_temperature - s.temperature(s.current_temperature)) << "\t";
//     std::cout << Color(s.current_energy) << "\t";
//     // // std::cout << s.d_energy << "\t";
//     std::cout << std::endl;
// }

struct Accepted{};
struct Rejected{};

template<typename Solver>
void print(
      const Solver& s
    , Accepted
    )
{
    // std::cout << green(s.current_it) << "\t";
    std::cout << green(s.current_temperature) << "\t";
    std::cout << green(s.current_temperature - s.temperature()) << "\t";
    // std::cout << std::hexfloat << std::setprecision(3) << green(s.current_energy) << "\t";
    std::cout << green(s.current_energy) << "\t";
    // // std::cout << s.d_energy << "\t";
    std::cout << std::endl;
}

template<typename Solver>
void print(
      const Solver& s
    , Rejected
    )
{
    // std::cout << red(s.current_it) << "\t";
    std::cout << red(s.current_temperature) << "\t";
    std::cout << red(s.current_temperature - s.temperature()) << "\t";
    // std::cout << std::hexfloat << std::setprecision(3) << red(s.current_energy) << "\t";
    std::cout << red(s.current_energy) << "\t";
    // // std::cout << s.d_energy << "\t";

    // std::cout << std::string(60, '\b');
    std::cout << std::endl;
}

template<typename Solver>
void final_print(const Solver& s)
{
    std::cout << std::endl;
    std::cout << "Final temperature: " << s.current_temperature << "\n";
    std::cout << "Final energy: " << s.current_energy << "\n";
    std::cout << std::endl;
}
<|endoftext|>


<commit_before>#include <cstdio>
#include <cstddef>
#include <cstring>
#include <string>
#include <iostream>
#include <system_error>

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <pwd.h>

#include "control-cmds.h"
#include "service-constants.h"
#include "cpbuffer.h"

// dinitctl:  utility to control the Dinit daemon, including starting and stopping of services.

// This utility communicates with the dinit daemon via a unix socket (/dev/initctl).

using handle_t = uint32_t;

class ReadCPException
{
    public:
    int errcode;
    ReadCPException(int err) : errcode(err) { }
};

static void fillBufferTo(CPBuffer *buf, int fd, int rlength)
{
    int r = buf->fillTo(fd, rlength);
    if (r == -1) {
        throw ReadCPException(errno);
    }
    else if (r == 0) {
        throw ReadCPException(0);
    }
}

static const char * describeState(bool stopped)
{
    return stopped ? "stopped" : "started";
}

static const char * describeVerb(bool stop)
{
    return stop ? "stop" : "start";
}

// Wait for a reply packet, skipping over any information packets
// that are received in the meantime.
static void wait_for_reply(CPBuffer &rbuffer, int fd)
{
    fillBufferTo(&rbuffer, fd, 1);

    while (rbuffer[0] >= 100) {
        // Information packet; discard.
        fillBufferTo(&rbuffer, fd, 1);
        int pktlen = (unsigned char) rbuffer[1];

        rbuffer.consume(1);  // Consume one byte so we'll read one byte of the next packet
        fillBufferTo(&rbuffer, fd, pktlen);
        rbuffer.consume(pktlen - 1);
    }
}

// Write *all* the requested buffer and re-try if necessary until
// the buffer is written or an unrecoverable error occurs.
static int write_all(int fd, const void *buf, size_t count)
{
    const char *cbuf = static_cast<const char *>(buf);
    int w = 0;
    while (count > 0) {
        int r = write(fd, cbuf, count);
        if (r == -1) {
            if (errno == EINTR) continue;
            return r;
        }
        w += r;
        cbuf += r;
        count -= r;
    }
    return w;
}

static int unpinService(int socknum, const char *service_name);

// Entry point.
int main(int argc, char **argv)
{
    using namespace std;

    bool do_stop = false;
    bool show_help = argc < 2;
    char *service_name = nullptr;

    std::string control_socket_str;
    const char * control_socket_path = nullptr;

    bool verbose = true;
    bool sys_dinit = false;  // communicate with system daemon
    bool wait_for_service = true;
    bool do_pin = false;

    int command = 0;

    constexpr int START_SERVICE = 1;
    constexpr int STOP_SERVICE = 2;
    constexpr int UNPIN_SERVICE = 3;

    for (int i = 1; i < argc; i++) {
        if (argv[i][0] == '-') {
            if (strcmp(argv[i], "--help") == 0) {
                show_help = true;
                break;
            }
            else if (strcmp(argv[i], "--no-wait") == 0) {
                wait_for_service = false;
            }
            else if (strcmp(argv[i], "--quiet") == 0) {
                verbose = false;
            }
            else if (strcmp(argv[i], "--system") == 0 || strcmp(argv[i], "-s") == 0) {
                sys_dinit = true;
            }
            else if (strcmp(argv[i], "--pin") == 0) {
                do_pin = true;
            }
            else {
                cerr << "Unrecognized command-line parameter: " << argv[i] << endl;
                return 1;
            }
        }
        else if (command == 0) {
            if (strcmp(argv[i], "start") == 0) {
                command = START_SERVICE;
            }
            else if (strcmp(argv[i], "stop") == 0) {
                command = STOP_SERVICE;
            }
            else if (strcmp(argv[i], "unpin") == 0) {
                command = UNPIN_SERVICE;
            }
            else {
                show_help = true;
                break;
            }
        }
        else {
            // service name
            service_name = argv[i];
            // TODO support multiple services (or at least give error if multiple
            //      services supplied)
        }
    }

    if (service_name == nullptr || command == 0) {
        show_help = true;
    }

    if (show_help) {
        cout << "dinitctl:   control Dinit services" << endl;

        cout << "\nUsage:" << endl;
        cout << "    dinitctl [options] start [options] <service-name> : start and activate service" << endl;
        cout << "    dinitctl [options] stop [options] <service-name>  : stop service and cancel explicit activation" << endl;
        // TODO:
        // cout << "    dinitctl [options] wake <service-name>  : start but don't activate service" << endl;

        cout << "\nNote: An activated service keeps its dependencies running when possible." << endl;

        cout << "\nGeneral options:" << endl;
        cout << "  -s, --system     : control system daemon instead of user daemon" << endl;
        cout << "  --quiet          : suppress output (except errors)" << endl;

        cout << "\nCommand options:" << endl;
        cout << "  --help           : show this help" << endl;
        cout << "  --no-wait        : don't wait for service startup/shutdown to complete" << endl;
        cout << "  --pin            : pin the service in the requested (started/stopped) state" << endl;
        return 1;
    }

    control_socket_path = "/dev/dinitctl";

    if (! sys_dinit) {
        char * userhome = getenv("HOME");
        if (userhome == nullptr) {
            struct passwd * pwuid_p = getpwuid(getuid());
            if (pwuid_p != nullptr) {
                userhome = pwuid_p->pw_dir;
            }
        }

        if (userhome != nullptr) {
            control_socket_str = userhome;
            control_socket_str += "/.dinitctl";
            control_socket_path = control_socket_str.c_str();
        }
        else {
            cerr << "Cannot locate user home directory (set HOME or check /etc/passwd file)" << endl;
            return 1;
        }
    }

    int socknum = socket(AF_UNIX, SOCK_STREAM, 0);
    if (socknum == -1) {
        perror("socket");
        return 1;
    }

    struct sockaddr_un * name;
    uint sockaddr_size = offsetof(struct sockaddr_un, sun_path) + strlen(control_socket_path) + 1;
    name = (struct sockaddr_un *) malloc(sockaddr_size);
    if (name == nullptr) {
        cerr << "dinit-start: out of memory" << endl;
        return 1;
    }

    name->sun_family = AF_UNIX;
    strcpy(name->sun_path, control_socket_path);

    int connr = connect(socknum, (struct sockaddr *) name, sockaddr_size);
    if (connr == -1) {
        perror("connect");
        return 1;
    }

    // TODO should start by querying protocol version

    if (command == UNPIN_SERVICE) {
        return unpinService(socknum, service_name);
    }

    do_stop = (command == STOP_SERVICE);

    // Build buffer;
    uint16_t sname_len = strlen(service_name);
    int bufsize = 3 + sname_len;
    char * buf = new char[bufsize];

    buf[0] = DINIT_CP_LOADSERVICE;
    memcpy(buf + 1, &sname_len, 2);
    memcpy(buf + 3, service_name, sname_len);

    int r = write_all(socknum, buf, bufsize);
    delete [] buf;
    if (r == -1) {
        perror("write");
        return 1;
    }

    // Now we expect a reply:

    try {
        CPBuffer rbuffer;
        wait_for_reply(rbuffer, socknum);

        //ServiceState state;
        //ServiceState target_state;
        handle_t handle;

        if (rbuffer[0] == DINIT_RP_SERVICERECORD) {
            fillBufferTo(&rbuffer, socknum, 2 + sizeof(handle));
            rbuffer.extract((char *) &handle, 2, sizeof(handle));
            //state = static_cast<ServiceState>(rbuffer[1]);
            //target_state = static_cast<ServiceState>(rbuffer[2 + sizeof(handle)]);
            rbuffer.consume(3 + sizeof(handle));
        }
        else if (rbuffer[0] == DINIT_RP_NOSERVICE) {
            cerr << "Failed to find/load service." << endl;
            return 1;
        }
        else {
            cerr << "Protocol error." << endl;
            return 1;
        }

        // ServiceState wanted_state = do_stop ? ServiceState::STOPPED : ServiceState::STARTED;
        int command = do_stop ? DINIT_CP_STOPSERVICE : DINIT_CP_STARTSERVICE;

        // Need to issue STOPSERVICE/STARTSERVICE
        // We'll do this regardless of the current service state / target state, since issuing
        // start/stop also sets or clears the "explicitly started" flag on the service.
        //if (target_state != wanted_state) {
        {
            buf = new char[2 + sizeof(handle)];
            buf[0] = command;
            buf[1] = do_pin ? 1 : 0;
            memcpy(buf + 2, &handle, sizeof(handle));
            r = write_all(socknum, buf, 2 + sizeof(handle));
            delete buf;

            if (r == -1) {
                perror("write");
                return 1;
            }

            wait_for_reply(rbuffer, socknum);
            if (rbuffer[0] == DINIT_RP_ALREADYSS) {
                if (verbose) {
                    cout << "Service already " << describeState(do_stop) << "." << endl;
                }
                return 0; // success!
            }
            if (rbuffer[0] != DINIT_RP_ACK) {
                cerr << "Protocol error." << endl;
                return 1;
            }
            rbuffer.consume(1);
        }

        /*
        if (state == wanted_state) {
            if (verbose) {
                cout << "Service already " << describeState(do_stop) << "." << endl;
            }
            return 0; // success!
        }
        */

        if (! wait_for_service) {
            if (verbose) {
                cout << "Issued " << describeVerb(do_stop) << " command successfully." << endl;
            }
            return 0;
        }

        ServiceEvent completionEvent;
        ServiceEvent cancelledEvent;

        if (do_stop) {
            completionEvent = ServiceEvent::STOPPED;
            cancelledEvent = ServiceEvent::STOPCANCELLED;
        }
        else {
            completionEvent = ServiceEvent::STARTED;
            cancelledEvent = ServiceEvent::STARTCANCELLED;
        }

        // Wait until service started:
        r = rbuffer.fillTo(socknum, 2);
        while (r > 0) {
            if (rbuffer[0] >= 100) {
                int pktlen = (unsigned char) rbuffer[1];
                fillBufferTo(&rbuffer, socknum, pktlen);

                if (rbuffer[0] == DINIT_IP_SERVICEEVENT) {
                    handle_t ev_handle;
                    rbuffer.extract((char *) &ev_handle, 2, sizeof(ev_handle));
                    ServiceEvent event = static_cast<ServiceEvent>(rbuffer[2 + sizeof(ev_handle)]);
                    if (ev_handle == handle) {
                        if (event == completionEvent) {
                            if (verbose) {
                                cout << "Service " << describeState(do_stop) << "." << endl;
                            }
                            return 0;
                        }
                        else if (event == cancelledEvent) {
                            if (verbose) {
                                cout << "Service " << describeVerb(do_stop) << " cancelled." << endl;
                            }
                            return 1;
                        }
                        else if (! do_stop && event == ServiceEvent::FAILEDSTART) {
                            if (verbose) {
                                cout << "Service failed to start." << endl;
                            }
                            return 1;
                        }
                    }
                }

                rbuffer.consume(pktlen);
                r = rbuffer.fillTo(socknum, 2);
            }
            else {
                // Not an information packet?
                cerr << "protocol error" << endl;
                return 1;
            }
        }

        if (r == -1) {
            perror("read");
        }
        else {
            cerr << "protocol error (connection closed by server)" << endl;
        }
        return 1;
    }
    catch (ReadCPException &exc) {
        cerr << "control socket read failure or protocol error" << endl;
        return 1;
    }
    catch (std::bad_alloc &exc) {
        cerr << "out of memory" << endl;
        return 1;
    }

    return 0;
}

// TODO refactor shared code with above
static int unpinService(int socknum, const char *service_name)
{
    using namespace std;

    // Build buffer;
    uint16_t sname_len = strlen(service_name);
    int bufsize = 3 + sname_len;
    char * buf = new char[bufsize];

    buf[0] = DINIT_CP_LOADSERVICE;
    memcpy(buf + 1, &sname_len, 2);
    memcpy(buf + 3, service_name, sname_len);

    int r = write_all(socknum, buf, bufsize);
    delete [] buf;
    if (r == -1) {
        perror("write");
        return 1;
    }

    // Now we expect a reply:

    try {
        CPBuffer rbuffer;
        wait_for_reply(rbuffer, socknum);

        //ServiceState state;
        //ServiceState target_state;
        handle_t handle;

        if (rbuffer[0] == DINIT_RP_SERVICERECORD) {
            fillBufferTo(&rbuffer, socknum, 2 + sizeof(handle));
            rbuffer.extract((char *) &handle, 2, sizeof(handle));
            //state = static_cast<ServiceState>(rbuffer[1]);
            //target_state = static_cast<ServiceState>(rbuffer[2 + sizeof(handle)]);
            rbuffer.consume(3 + sizeof(handle));
        }
        else if (rbuffer[0] == DINIT_RP_NOSERVICE) {
            cerr << "Failed to find/load service." << endl;
            return 1;
        }
        else {
            cerr << "Protocol error." << endl;
            return 1;
        }

        // Issue UNPIN command.
        {
            buf = new char[1 + sizeof(handle)];
            buf[0] = DINIT_CP_UNPINSERVICE;
            memcpy(buf + 1, &handle, sizeof(handle));
            r = write_all(socknum, buf, 2 + sizeof(handle));
            delete buf;

            if (r == -1) {
                perror("write");
                return 1;
            }

            wait_for_reply(rbuffer, socknum);
            if (rbuffer[0] != DINIT_RP_ACK) {
                cerr << "Protocol error." << endl;
                return 1;
            }
            rbuffer.consume(1);
        }
    }
    catch (ReadCPException &exc) {
        cerr << "control socket read failure or protocol error" << endl;
        return 1;
    }
    catch (std::bad_alloc &exc) {
        cerr << "out of memory" << endl;
        return 1;
    }

    cout << "Service unpinned." << endl;
    return 0;
}
<commit_msg>Use delete[] to delete buffers allocated via new[] (thank you LLVM static analyzer...)<commit_after>#include <cstdio>
#include <cstddef>
#include <cstring>
#include <string>
#include <iostream>
#include <system_error>

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <pwd.h>

#include "control-cmds.h"
#include "service-constants.h"
#include "cpbuffer.h"

// dinitctl:  utility to control the Dinit daemon, including starting and stopping of services.

// This utility communicates with the dinit daemon via a unix socket (/dev/initctl).

using handle_t = uint32_t;

class ReadCPException
{
    public:
    int errcode;
    ReadCPException(int err) : errcode(err) { }
};

static void fillBufferTo(CPBuffer *buf, int fd, int rlength)
{
    int r = buf->fillTo(fd, rlength);
    if (r == -1) {
        throw ReadCPException(errno);
    }
    else if (r == 0) {
        throw ReadCPException(0);
    }
}

static const char * describeState(bool stopped)
{
    return stopped ? "stopped" : "started";
}

static const char * describeVerb(bool stop)
{
    return stop ? "stop" : "start";
}

// Wait for a reply packet, skipping over any information packets
// that are received in the meantime.
static void wait_for_reply(CPBuffer &rbuffer, int fd)
{
    fillBufferTo(&rbuffer, fd, 1);

    while (rbuffer[0] >= 100) {
        // Information packet; discard.
        fillBufferTo(&rbuffer, fd, 1);
        int pktlen = (unsigned char) rbuffer[1];

        rbuffer.consume(1);  // Consume one byte so we'll read one byte of the next packet
        fillBufferTo(&rbuffer, fd, pktlen);
        rbuffer.consume(pktlen - 1);
    }
}

// Write *all* the requested buffer and re-try if necessary until
// the buffer is written or an unrecoverable error occurs.
static int write_all(int fd, const void *buf, size_t count)
{
    const char *cbuf = static_cast<const char *>(buf);
    int w = 0;
    while (count > 0) {
        int r = write(fd, cbuf, count);
        if (r == -1) {
            if (errno == EINTR) continue;
            return r;
        }
        w += r;
        cbuf += r;
        count -= r;
    }
    return w;
}

static int unpinService(int socknum, const char *service_name);

// Entry point.
int main(int argc, char **argv)
{
    using namespace std;

    bool do_stop = false;
    bool show_help = argc < 2;
    char *service_name = nullptr;

    std::string control_socket_str;
    const char * control_socket_path = nullptr;

    bool verbose = true;
    bool sys_dinit = false;  // communicate with system daemon
    bool wait_for_service = true;
    bool do_pin = false;

    int command = 0;

    constexpr int START_SERVICE = 1;
    constexpr int STOP_SERVICE = 2;
    constexpr int UNPIN_SERVICE = 3;

    for (int i = 1; i < argc; i++) {
        if (argv[i][0] == '-') {
            if (strcmp(argv[i], "--help") == 0) {
                show_help = true;
                break;
            }
            else if (strcmp(argv[i], "--no-wait") == 0) {
                wait_for_service = false;
            }
            else if (strcmp(argv[i], "--quiet") == 0) {
                verbose = false;
            }
            else if (strcmp(argv[i], "--system") == 0 || strcmp(argv[i], "-s") == 0) {
                sys_dinit = true;
            }
            else if (strcmp(argv[i], "--pin") == 0) {
                do_pin = true;
            }
            else {
                cerr << "Unrecognized command-line parameter: " << argv[i] << endl;
                return 1;
            }
        }
        else if (command == 0) {
            if (strcmp(argv[i], "start") == 0) {
                command = START_SERVICE;
            }
            else if (strcmp(argv[i], "stop") == 0) {
                command = STOP_SERVICE;
            }
            else if (strcmp(argv[i], "unpin") == 0) {
                command = UNPIN_SERVICE;
            }
            else {
                show_help = true;
                break;
            }
        }
        else {
            // service name
            service_name = argv[i];
            // TODO support multiple services (or at least give error if multiple
            //      services supplied)
        }
    }

    if (service_name == nullptr || command == 0) {
        show_help = true;
    }

    if (show_help) {
        cout << "dinitctl:   control Dinit services" << endl;

        cout << "\nUsage:" << endl;
        cout << "    dinitctl [options] start [options] <service-name> : start and activate service" << endl;
        cout << "    dinitctl [options] stop [options] <service-name>  : stop service and cancel explicit activation" << endl;
        // TODO:
        // cout << "    dinitctl [options] wake <service-name>  : start but don't activate service" << endl;

        cout << "\nNote: An activated service keeps its dependencies running when possible." << endl;

        cout << "\nGeneral options:" << endl;
        cout << "  -s, --system     : control system daemon instead of user daemon" << endl;
        cout << "  --quiet          : suppress output (except errors)" << endl;

        cout << "\nCommand options:" << endl;
        cout << "  --help           : show this help" << endl;
        cout << "  --no-wait        : don't wait for service startup/shutdown to complete" << endl;
        cout << "  --pin            : pin the service in the requested (started/stopped) state" << endl;
        return 1;
    }

    control_socket_path = "/dev/dinitctl";

    if (! sys_dinit) {
        char * userhome = getenv("HOME");
        if (userhome == nullptr) {
            struct passwd * pwuid_p = getpwuid(getuid());
            if (pwuid_p != nullptr) {
                userhome = pwuid_p->pw_dir;
            }
        }

        if (userhome != nullptr) {
            control_socket_str = userhome;
            control_socket_str += "/.dinitctl";
            control_socket_path = control_socket_str.c_str();
        }
        else {
            cerr << "Cannot locate user home directory (set HOME or check /etc/passwd file)" << endl;
            return 1;
        }
    }

    int socknum = socket(AF_UNIX, SOCK_STREAM, 0);
    if (socknum == -1) {
        perror("socket");
        return 1;
    }

    struct sockaddr_un * name;
    uint sockaddr_size = offsetof(struct sockaddr_un, sun_path) + strlen(control_socket_path) + 1;
    name = (struct sockaddr_un *) malloc(sockaddr_size);
    if (name == nullptr) {
        cerr << "dinit-start: out of memory" << endl;
        return 1;
    }

    name->sun_family = AF_UNIX;
    strcpy(name->sun_path, control_socket_path);

    int connr = connect(socknum, (struct sockaddr *) name, sockaddr_size);
    if (connr == -1) {
        perror("connect");
        return 1;
    }

    // TODO should start by querying protocol version

    if (command == UNPIN_SERVICE) {
        return unpinService(socknum, service_name);
    }

    do_stop = (command == STOP_SERVICE);

    // Build buffer;
    uint16_t sname_len = strlen(service_name);
    int bufsize = 3 + sname_len;
    char * buf = new char[bufsize];

    buf[0] = DINIT_CP_LOADSERVICE;
    memcpy(buf + 1, &sname_len, 2);
    memcpy(buf + 3, service_name, sname_len);

    int r = write_all(socknum, buf, bufsize);
    delete [] buf;
    if (r == -1) {
        perror("write");
        return 1;
    }

    // Now we expect a reply:

    try {
        CPBuffer rbuffer;
        wait_for_reply(rbuffer, socknum);

        //ServiceState state;
        //ServiceState target_state;
        handle_t handle;

        if (rbuffer[0] == DINIT_RP_SERVICERECORD) {
            fillBufferTo(&rbuffer, socknum, 2 + sizeof(handle));
            rbuffer.extract((char *) &handle, 2, sizeof(handle));
            //state = static_cast<ServiceState>(rbuffer[1]);
            //target_state = static_cast<ServiceState>(rbuffer[2 + sizeof(handle)]);
            rbuffer.consume(3 + sizeof(handle));
        }
        else if (rbuffer[0] == DINIT_RP_NOSERVICE) {
            cerr << "Failed to find/load service." << endl;
            return 1;
        }
        else {
            cerr << "Protocol error." << endl;
            return 1;
        }

        // ServiceState wanted_state = do_stop ? ServiceState::STOPPED : ServiceState::STARTED;
        int command = do_stop ? DINIT_CP_STOPSERVICE : DINIT_CP_STARTSERVICE;

        // Need to issue STOPSERVICE/STARTSERVICE
        // We'll do this regardless of the current service state / target state, since issuing
        // start/stop also sets or clears the "explicitly started" flag on the service.
        //if (target_state != wanted_state) {
        {
            buf = new char[2 + sizeof(handle)];
            buf[0] = command;
            buf[1] = do_pin ? 1 : 0;
            memcpy(buf + 2, &handle, sizeof(handle));
            r = write_all(socknum, buf, 2 + sizeof(handle));
            delete [] buf;

            if (r == -1) {
                perror("write");
                return 1;
            }

            wait_for_reply(rbuffer, socknum);
            if (rbuffer[0] == DINIT_RP_ALREADYSS) {
                if (verbose) {
                    cout << "Service already " << describeState(do_stop) << "." << endl;
                }
                return 0; // success!
            }
            if (rbuffer[0] != DINIT_RP_ACK) {
                cerr << "Protocol error." << endl;
                return 1;
            }
            rbuffer.consume(1);
        }

        /*
        if (state == wanted_state) {
            if (verbose) {
                cout << "Service already " << describeState(do_stop) << "." << endl;
            }
            return 0; // success!
        }
        */

        if (! wait_for_service) {
            if (verbose) {
                cout << "Issued " << describeVerb(do_stop) << " command successfully." << endl;
            }
            return 0;
        }

        ServiceEvent completionEvent;
        ServiceEvent cancelledEvent;

        if (do_stop) {
            completionEvent = ServiceEvent::STOPPED;
            cancelledEvent = ServiceEvent::STOPCANCELLED;
        }
        else {
            completionEvent = ServiceEvent::STARTED;
            cancelledEvent = ServiceEvent::STARTCANCELLED;
        }

        // Wait until service started:
        r = rbuffer.fillTo(socknum, 2);
        while (r > 0) {
            if (rbuffer[0] >= 100) {
                int pktlen = (unsigned char) rbuffer[1];
                fillBufferTo(&rbuffer, socknum, pktlen);

                if (rbuffer[0] == DINIT_IP_SERVICEEVENT) {
                    handle_t ev_handle;
                    rbuffer.extract((char *) &ev_handle, 2, sizeof(ev_handle));
                    ServiceEvent event = static_cast<ServiceEvent>(rbuffer[2 + sizeof(ev_handle)]);
                    if (ev_handle == handle) {
                        if (event == completionEvent) {
                            if (verbose) {
                                cout << "Service " << describeState(do_stop) << "." << endl;
                            }
                            return 0;
                        }
                        else if (event == cancelledEvent) {
                            if (verbose) {
                                cout << "Service " << describeVerb(do_stop) << " cancelled." << endl;
                            }
                            return 1;
                        }
                        else if (! do_stop && event == ServiceEvent::FAILEDSTART) {
                            if (verbose) {
                                cout << "Service failed to start." << endl;
                            }
                            return 1;
                        }
                    }
                }

                rbuffer.consume(pktlen);
                r = rbuffer.fillTo(socknum, 2);
            }
            else {
                // Not an information packet?
                cerr << "protocol error" << endl;
                return 1;
            }
        }

        if (r == -1) {
            perror("read");
        }
        else {
            cerr << "protocol error (connection closed by server)" << endl;
        }
        return 1;
    }
    catch (ReadCPException &exc) {
        cerr << "control socket read failure or protocol error" << endl;
        return 1;
    }
    catch (std::bad_alloc &exc) {
        cerr << "out of memory" << endl;
        return 1;
    }

    return 0;
}

// TODO refactor shared code with above
static int unpinService(int socknum, const char *service_name)
{
    using namespace std;

    // Build buffer;
    uint16_t sname_len = strlen(service_name);
    int bufsize = 3 + sname_len;
    char * buf = new char[bufsize];

    buf[0] = DINIT_CP_LOADSERVICE;
    memcpy(buf + 1, &sname_len, 2);
    memcpy(buf + 3, service_name, sname_len);

    int r = write_all(socknum, buf, bufsize);
    delete [] buf;
    if (r == -1) {
        perror("write");
        return 1;
    }

    // Now we expect a reply:

    try {
        CPBuffer rbuffer;
        wait_for_reply(rbuffer, socknum);

        //ServiceState state;
        //ServiceState target_state;
        handle_t handle;

        if (rbuffer[0] == DINIT_RP_SERVICERECORD) {
            fillBufferTo(&rbuffer, socknum, 2 + sizeof(handle));
            rbuffer.extract((char *) &handle, 2, sizeof(handle));
            //state = static_cast<ServiceState>(rbuffer[1]);
            //target_state = static_cast<ServiceState>(rbuffer[2 + sizeof(handle)]);
            rbuffer.consume(3 + sizeof(handle));
        }
        else if (rbuffer[0] == DINIT_RP_NOSERVICE) {
            cerr << "Failed to find/load service." << endl;
            return 1;
        }
        else {
            cerr << "Protocol error." << endl;
            return 1;
        }

        // Issue UNPIN command.
        {
            buf = new char[1 + sizeof(handle)];
            buf[0] = DINIT_CP_UNPINSERVICE;
            memcpy(buf + 1, &handle, sizeof(handle));
            r = write_all(socknum, buf, 2 + sizeof(handle));
            delete [] buf;

            if (r == -1) {
                perror("write");
                return 1;
            }

            wait_for_reply(rbuffer, socknum);
            if (rbuffer[0] != DINIT_RP_ACK) {
                cerr << "Protocol error." << endl;
                return 1;
            }
            rbuffer.consume(1);
        }
    }
    catch (ReadCPException &exc) {
        cerr << "control socket read failure or protocol error" << endl;
        return 1;
    }
    catch (std::bad_alloc &exc) {
        cerr << "out of memory" << endl;
        return 1;
    }

    cout << "Service unpinned." << endl;
    return 0;
}
<|endoftext|>


<commit_before><commit_msg>Skip runs with no glyphs when drawing text in RenderTextWin.<commit_after><|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: indexentrysupplier_asian.cxx,v $
 *
 *  $Revision: 1.8 $
 *
 *  last change: $Author: kz $ $Date: 2006-07-19 16:29:37 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    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.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#include <rtl/ustrbuf.hxx>
#include <indexentrysupplier_asian.hxx>
#include <data/indexdata_alphanumeric.h>

using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::lang;
using namespace ::rtl;

namespace com { namespace sun { namespace star { namespace i18n {

IndexEntrySupplier_asian::IndexEntrySupplier_asian(
    const Reference < XMultiServiceFactory >& rxMSF ) : IndexEntrySupplier_Common(rxMSF)
{
    implementationName = "com.sun.star.i18n.IndexEntrySupplier_asian";
#ifdef SAL_DLLPREFIX
    OUString lib=OUString::createFromAscii(SAL_DLLPREFIX"index_data"SAL_DLLEXTENSION);
#else
    OUString lib=OUString::createFromAscii("index_data"SAL_DLLEXTENSION);
#endif
    hModule = osl_loadModule( lib.pData, SAL_LOADMODULE_DEFAULT );
}

IndexEntrySupplier_asian::~IndexEntrySupplier_asian()
{
    if (hModule) osl_unloadModule(hModule);
}

OUString SAL_CALL
IndexEntrySupplier_asian::getIndexCharacter( const OUString& rIndexEntry,
    const Locale& rLocale, const OUString& rAlgorithm ) throw (RuntimeException)
{
    sal_Unicode ch = rIndexEntry.toChar();
    if (hModule) {
        OUString get=OUString::createFromAscii("get_indexdata_");
        int (*func)()=NULL;
        if (rLocale.Language.equalsAscii("zh") && OUString::createFromAscii("TW HK MO").indexOf(rLocale.Country) >= 0)
            func=(int (*)())osl_getFunctionSymbol(hModule, (get+rLocale.Language+OUString::createFromAscii("_TW_")+rAlgorithm).pData);
        if (!func)
            func=(int (*)())osl_getFunctionSymbol(hModule, (get+rLocale.Language+OUString('_')+rAlgorithm).pData);
        if (func) {
            sal_uInt16** idx=(sal_uInt16**)func();
            sal_uInt16 address=idx[0][ch >> 8];
            if (address != 0xFFFF) {
                address=idx[1][address+(ch & 0xFF)];
                return idx[2] ? OUString(&idx[2][address]) : OUString(address);
            }
        }
    }
    // using alphanumeric index for non-define stirng
    return OUString(&idxStr[(ch & 0xFF00) ? 0 : ch], 1);
}

OUString SAL_CALL
IndexEntrySupplier_asian::getIndexKey( const OUString& rIndexEntry,
    const OUString& rPhoneticEntry, const Locale& rLocale) throw (RuntimeException)
{
    return getIndexCharacter(getEntry(rIndexEntry, rPhoneticEntry, rLocale), rLocale, aAlgorithm);
}

sal_Int16 SAL_CALL
IndexEntrySupplier_asian::compareIndexEntry(
    const OUString& rIndexEntry1, const OUString& rPhoneticEntry1, const Locale& rLocale1,
    const OUString& rIndexEntry2, const OUString& rPhoneticEntry2, const Locale& rLocale2 )
    throw (RuntimeException)
{
    sal_Int32 result = collator->compareString(getEntry(rIndexEntry1, rPhoneticEntry1, rLocale1),
                                    getEntry(rIndexEntry2, rPhoneticEntry2, rLocale2));

    // equivalent of phonetic entries does not mean equivalent of index entries.
    // we have to continue comparing index entry here.
    if (result == 0 && usePhonetic && rPhoneticEntry1.getLength() > 0 &&
            rLocale1.Language == rLocale2.Language && rLocale1.Country == rLocale2.Country &&
            rLocale1.Variant == rLocale2.Variant)
        result = collator->compareString(rIndexEntry1, rIndexEntry2);
    return sal::static_int_cast< sal_Int16 >(result); // result in { -1, 0, 1 }
}

OUString SAL_CALL
IndexEntrySupplier_asian::getPhoneticCandidate( const OUString& rIndexEntry,
        const Locale& rLocale ) throw (RuntimeException)
{
    if (hModule) {
        int (*func)()=NULL;
        const sal_Char *func_name=NULL;
        if (rLocale.Language.equalsAscii("zh"))
            func_name=(OUString::createFromAscii("TW HK MO").indexOf(rLocale.Country) >= 0) ?  "get_zh_zhuyin" : "get_zh_pinyin";
        else if (rLocale.Language.equalsAscii("ko"))
            func_name="get_ko_phonetic";
        if (func_name)
            func=(int (*)())osl_getFunctionSymbol(hModule, OUString::createFromAscii(func_name).pData);
        if (func) {
            OUStringBuffer candidate;
            sal_uInt16** idx=(sal_uInt16**)func();
            for (sal_Int32 i=0; i < rIndexEntry.getLength(); i++) {
                sal_Unicode ch = rIndexEntry[i];
                sal_uInt16 address = idx[0][ch>>8];
                if (address != 0xFFFF) {
                    address = idx[1][address + (ch & 0xFF)];
                    if (i > 0 && rLocale.Language.equalsAscii("zh"))
                        candidate.appendAscii(" ");
                    if (idx[2])
                        candidate.append(&idx[2][address]);
                    else
                        candidate.append(address);
                } else
                    candidate.appendAscii(" ");
            }
            return candidate.makeStringAndClear();
        }
    }
    return OUString();
}
} } } }
<commit_msg>INTEGRATION: CWS pchfix02 (1.8.8); FILE MERGED 2006/09/01 17:30:43 kaib 1.8.8.1: #i68856# Added header markers and pch files<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: indexentrysupplier_asian.cxx,v $
 *
 *  $Revision: 1.9 $
 *
 *  last change: $Author: obo $ $Date: 2006-09-17 09:18:27 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    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.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_i18npool.hxx"

#include <rtl/ustrbuf.hxx>
#include <indexentrysupplier_asian.hxx>
#include <data/indexdata_alphanumeric.h>

using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::lang;
using namespace ::rtl;

namespace com { namespace sun { namespace star { namespace i18n {

IndexEntrySupplier_asian::IndexEntrySupplier_asian(
    const Reference < XMultiServiceFactory >& rxMSF ) : IndexEntrySupplier_Common(rxMSF)
{
    implementationName = "com.sun.star.i18n.IndexEntrySupplier_asian";
#ifdef SAL_DLLPREFIX
    OUString lib=OUString::createFromAscii(SAL_DLLPREFIX"index_data"SAL_DLLEXTENSION);
#else
    OUString lib=OUString::createFromAscii("index_data"SAL_DLLEXTENSION);
#endif
    hModule = osl_loadModule( lib.pData, SAL_LOADMODULE_DEFAULT );
}

IndexEntrySupplier_asian::~IndexEntrySupplier_asian()
{
    if (hModule) osl_unloadModule(hModule);
}

OUString SAL_CALL
IndexEntrySupplier_asian::getIndexCharacter( const OUString& rIndexEntry,
    const Locale& rLocale, const OUString& rAlgorithm ) throw (RuntimeException)
{
    sal_Unicode ch = rIndexEntry.toChar();
    if (hModule) {
        OUString get=OUString::createFromAscii("get_indexdata_");
        int (*func)()=NULL;
        if (rLocale.Language.equalsAscii("zh") && OUString::createFromAscii("TW HK MO").indexOf(rLocale.Country) >= 0)
            func=(int (*)())osl_getFunctionSymbol(hModule, (get+rLocale.Language+OUString::createFromAscii("_TW_")+rAlgorithm).pData);
        if (!func)
            func=(int (*)())osl_getFunctionSymbol(hModule, (get+rLocale.Language+OUString('_')+rAlgorithm).pData);
        if (func) {
            sal_uInt16** idx=(sal_uInt16**)func();
            sal_uInt16 address=idx[0][ch >> 8];
            if (address != 0xFFFF) {
                address=idx[1][address+(ch & 0xFF)];
                return idx[2] ? OUString(&idx[2][address]) : OUString(address);
            }
        }
    }
    // using alphanumeric index for non-define stirng
    return OUString(&idxStr[(ch & 0xFF00) ? 0 : ch], 1);
}

OUString SAL_CALL
IndexEntrySupplier_asian::getIndexKey( const OUString& rIndexEntry,
    const OUString& rPhoneticEntry, const Locale& rLocale) throw (RuntimeException)
{
    return getIndexCharacter(getEntry(rIndexEntry, rPhoneticEntry, rLocale), rLocale, aAlgorithm);
}

sal_Int16 SAL_CALL
IndexEntrySupplier_asian::compareIndexEntry(
    const OUString& rIndexEntry1, const OUString& rPhoneticEntry1, const Locale& rLocale1,
    const OUString& rIndexEntry2, const OUString& rPhoneticEntry2, const Locale& rLocale2 )
    throw (RuntimeException)
{
    sal_Int32 result = collator->compareString(getEntry(rIndexEntry1, rPhoneticEntry1, rLocale1),
                                    getEntry(rIndexEntry2, rPhoneticEntry2, rLocale2));

    // equivalent of phonetic entries does not mean equivalent of index entries.
    // we have to continue comparing index entry here.
    if (result == 0 && usePhonetic && rPhoneticEntry1.getLength() > 0 &&
            rLocale1.Language == rLocale2.Language && rLocale1.Country == rLocale2.Country &&
            rLocale1.Variant == rLocale2.Variant)
        result = collator->compareString(rIndexEntry1, rIndexEntry2);
    return sal::static_int_cast< sal_Int16 >(result); // result in { -1, 0, 1 }
}

OUString SAL_CALL
IndexEntrySupplier_asian::getPhoneticCandidate( const OUString& rIndexEntry,
        const Locale& rLocale ) throw (RuntimeException)
{
    if (hModule) {
        int (*func)()=NULL;
        const sal_Char *func_name=NULL;
        if (rLocale.Language.equalsAscii("zh"))
            func_name=(OUString::createFromAscii("TW HK MO").indexOf(rLocale.Country) >= 0) ?  "get_zh_zhuyin" : "get_zh_pinyin";
        else if (rLocale.Language.equalsAscii("ko"))
            func_name="get_ko_phonetic";
        if (func_name)
            func=(int (*)())osl_getFunctionSymbol(hModule, OUString::createFromAscii(func_name).pData);
        if (func) {
            OUStringBuffer candidate;
            sal_uInt16** idx=(sal_uInt16**)func();
            for (sal_Int32 i=0; i < rIndexEntry.getLength(); i++) {
                sal_Unicode ch = rIndexEntry[i];
                sal_uInt16 address = idx[0][ch>>8];
                if (address != 0xFFFF) {
                    address = idx[1][address + (ch & 0xFF)];
                    if (i > 0 && rLocale.Language.equalsAscii("zh"))
                        candidate.appendAscii(" ");
                    if (idx[2])
                        candidate.append(&idx[2][address]);
                    else
                        candidate.append(address);
                } else
                    candidate.appendAscii(" ");
            }
            return candidate.makeStringAndClear();
        }
    }
    return OUString();
}
} } } }
<|endoftext|>


<commit_before>#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include "dissect-protobuf.hpp"

#include "ggp-field.hpp"

static GGPFieldBlob protobuf_blob
	("unknown string", "gg.protobuf_unknown_string", NULL);

class ProtobufDissector
{
private:
	gint offset = 0;
	tvbuff_t *tvb;
	proto_tree *tree;

public:
	ProtobufDissector(tvbuff_t *tvb, proto_tree *tree):
		tvb(tvb),tree(tree)
	{
	}

	bool still_remaining() const
	{
		return (tvb_length_remaining(tvb, offset) > 0);
	}

	void dissect_str(int id)
	{
		guint8 length = tvb_get_guint8(tvb, offset++); //TODO: varint

		DISSECTOR_ASSERT(tvb_length_remaining(tvb, offset) >= length);

		proto_tree_add_bytes_format(tree, protobuf_blob, tvb, offset, length, NULL,
			"unknown string (%d)", id);

		offset += length;
	}

	void dissect_more()
	{
		guint8 type = tvb_get_guint8(tvb, offset++);
		guint8 id = type >> 3;
		type &= 7;

		if (type == 2)
			dissect_str(id);
		else
			DISSECTOR_ASSERT_NOT_REACHED();
	}
};

void
dissect_protobuf(tvbuff_t *tvb, proto_tree *tree)
{
	ProtobufDissector pbdis(tvb, tree);

	while (pbdis.still_remaining()) {
		pbdis.dissect_more();
	}
}
<commit_msg>Dissect varint and fixedint<commit_after>#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include "dissect-protobuf.hpp"

#include "ggp-field.hpp"

static GGPFieldBlob protobuf_blob
	("unknown string", "gg.protobuf_unknown_string", NULL);
static GGPFieldBlob protobuf_varint
	("unknown varint", "gg.protobuf_unknown_varint", NULL);
static GGPFieldUINT32 protobuf_fixedint
	("unknown fixedint", "gg.protobuf_unknown_fixedint", NULL);

class ProtobufDissector
{
private:
	gint offset = 0;
	tvbuff_t *tvb;
	proto_tree *tree;

public:
	ProtobufDissector(tvbuff_t *tvb, proto_tree *tree):
		tvb(tvb),tree(tree)
	{
	}

	bool still_remaining() const
	{
		return (tvb_length_remaining(tvb, offset) > 0);
	}

	void dissect_fixedint(int id)
	{
		guint32 val = tvb_get_letohl(tvb, offset);

		proto_tree_add_uint_format(tree, protobuf_fixedint, tvb, offset, 4, val,
			"unknown field %d: %d", id, val);

		offset += 4;
	}

	void dissect_varint(int id)
	{
		guint8 val = tvb_get_guint8(tvb, offset); //TODO: varint

		proto_tree_add_bytes_format(tree, protobuf_varint, tvb, offset, 1, NULL,
			"unknown field %d: %d", id, val);

		offset += 1;
	}

	void dissect_str(int id)
	{
		guint8 length = tvb_get_guint8(tvb, offset++); //TODO: varint

		DISSECTOR_ASSERT(tvb_length_remaining(tvb, offset) >= length);

		proto_tree_add_bytes_format(tree, protobuf_blob, tvb, offset, length, NULL,
			"unknown field %d: string[%d]", id, length);

		offset += length;
	}

	void dissect_more()
	{
		guint8 type = tvb_get_guint8(tvb, offset++);
		guint8 id = type >> 3;
		type &= 7;

		if (type == 0)
			dissect_varint(id);
		else if (type == 2)
			dissect_str(id);
		else if (type == 5)
			dissect_fixedint(id);
		else
			DISSECTOR_ASSERT_NOT_REACHED();
	}
};

void
dissect_protobuf(tvbuff_t *tvb, proto_tree *tree)
{
	ProtobufDissector pbdis(tvb, tree);

	while (pbdis.still_remaining()) {
		pbdis.dissect_more();
	}
}
<|endoftext|>


<commit_before>/*
 * Generic cache class.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "cache.hxx"
#include "hashmap.hxx"
#include "AllocatorStats.hxx"
#include "pool.hxx"
#include "event/CleanupTimer.hxx"
#include "system/clock.h"

#include <assert.h>
#include <time.h>
#include <event.h>

/* #define ENABLE_EXCESSIVE_CACHE_CHECKS */

struct cache {
    struct pool &pool;

    const struct cache_class &cls;
    const size_t max_size;
    size_t size;
    struct hashmap *const items;

    /**
     * A linked list of all cache items, sorted by last_accessed,
     * oldest first.
     */
    boost::intrusive::list<struct cache_item,
                           boost::intrusive::member_hook<struct cache_item,
                                                         cache_item::SiblingsHook,
                                                         &cache_item::sorted_siblings>,
                           boost::intrusive::constant_time_size<false>> sorted_items;

    CleanupTimer cleanup_timer;

    cache(struct pool &_pool, const struct cache_class &_cls,
          unsigned hashtable_capacity, size_t _max_size)
        :pool(_pool), cls(_cls),
         max_size(_max_size), size(0),
         items(hashmap_new(&_pool, hashtable_capacity)) {
        cleanup_timer.Init(60, ExpireCallback, this);
    }

    ~cache();

    /** clean up expired cache items every 60 seconds */
    bool ExpireCallback();
    static bool ExpireCallback(void *ctx);

    void Check() const;

    void ItemRemoved(struct cache_item *item);

    void RemoveItem(struct cache_item &item) {
        assert(!item.removed);

        hashmap_remove_existing(items, item.key, &item);
        ItemRemoved(&item);
    }
};

struct cache *
cache_new(struct pool &pool, const struct cache_class *cls,
          unsigned hashtable_capacity, size_t max_size)
{
    assert(cls != nullptr);

    return new cache(pool, *cls, hashtable_capacity, max_size);
}

inline
cache::~cache()
{
    cleanup_timer.Deinit();

    Check();

    if (cls.destroy != nullptr) {
        hashmap_rewind(items);

        const struct hashmap_pair *pair;
        while ((pair = hashmap_next(items)) != nullptr) {
            struct cache_item *item = (struct cache_item *)pair->value;

            assert(item->lock == 0);
            assert(size >= item->size);
            size -= item->size;

#ifndef NDEBUG
            sorted_items.erase(sorted_items.iterator_to(*item));
#endif

            cls.destroy(item);
        }

        assert(size == 0);
        assert(sorted_items.empty());
    }
}

void
cache_close(struct cache *cache)
{
    delete cache;
}

AllocatorStats
cache_get_stats(const struct cache &cache)
{
    AllocatorStats stats;
    stats.netto_size = pool_children_netto_size(&cache.pool);
    stats.brutto_size = pool_children_brutto_size(&cache.pool);
    return stats;
}

inline void
cache::Check() const
{
#if !defined(NDEBUG) && defined(ENABLE_EXCESSIVE_CACHE_CHECKS)
    const struct hashmap_pair *pair;
    size_t s = 0;

    assert(size <= max_size);

    hashmap_rewind(items);
    while ((pair = hashmap_next(items)) != nullptr) {
        struct cache_item *item = (struct cache_item *)pair->value;

        s += item->size;
        assert(s <= size);
    }

    assert(s == size);
#endif
}

static void
cache_destroy_item(struct cache *cache, struct cache_item *item)
{
    if (cache->cls.destroy != nullptr)
        cache->cls.destroy(item);
}

void
cache::ItemRemoved(struct cache_item *item)
{
    assert(item != nullptr);
    assert(item->size > 0);
    assert(item->lock > 0 || !item->removed);
    assert(size >= item->size);

    sorted_items.erase(sorted_items.iterator_to(*item));

    size -= item->size;

    if (item->lock == 0)
        cache_destroy_item(this, item);
    else
        /* this item is locked - postpone the destroy() call */
        item->removed = true;

    if (size == 0)
        cleanup_timer.Disable();
}

static bool
cache_flush_callback(gcc_unused const char *key, void *value, void *ctx)
{
    struct cache *cache = (struct cache *)ctx;
    struct cache_item *item = (struct cache_item *)value;

    cache->ItemRemoved(item);
    return true;
}

void
cache_flush(struct cache *cache)
{
    cache->Check();

    hashmap_remove_all_match(cache->items, cache_flush_callback, cache);

    cache->Check();
}

static bool
cache_item_validate(const struct cache *cache, struct cache_item *item,
                    unsigned now)
{
    return now < item->expires &&
        (cache->cls.validate == nullptr || cache->cls.validate(item));
}

static void
cache_refresh_item(struct cache *cache, struct cache_item *item, unsigned now)
{
    item->last_accessed = now;

    /* move to the front of the linked list */
    cache->sorted_items.erase(cache->sorted_items.iterator_to(*item));
    cache->sorted_items.push_back(*item);
}

struct cache_item *
cache_get(struct cache *cache, const char *key)
{
    struct cache_item *item = (struct cache_item *)
        hashmap_get(cache->items, key);
    if (item == nullptr)
        return nullptr;

    const unsigned now = now_s();

    if (!cache_item_validate(cache, item, now)) {
        cache->Check();
        cache->RemoveItem(*item);
        cache->Check();
        return nullptr;
    }

    cache_refresh_item(cache, item, now);
    return item;
}

struct cache_item *
cache_get_match(struct cache *cache, const char *key,
                bool (*match)(const struct cache_item *, void *),
                void *ctx)
{
    const unsigned now = now_s();
    const struct hashmap_pair *pair = nullptr;

    while (true) {
        if (pair != nullptr) {
            struct cache_item *item = (struct cache_item *)pair->value;

            if (!cache_item_validate(cache, item, now)) {
                /* expired cache item: delete it, and re-start the
                   search */

                cache->Check();
                cache->RemoveItem(*item);
                cache->Check();

                pair = nullptr;
                continue;
            }

            if (match(item, ctx)) {
                /* this one matches: return it to the caller */
                cache_refresh_item(cache, item, now);
                return item;
            }

            /* find the next cache_item for this key */
            pair = hashmap_lookup_next(pair);
        } else {
            /* find the first cache_item for this key */
            pair = hashmap_lookup_first(cache->items, key);
        }

        if (pair == nullptr)
            /* no match */
            return nullptr;
    };
}

static void
cache_destroy_oldest_item(struct cache *cache)
{
    if (cache->sorted_items.empty())
        return;

    struct cache_item &item = cache->sorted_items.front();

    cache->Check();
    cache->RemoveItem(item);
    cache->Check();
}

static bool
cache_need_room(struct cache *cache, size_t size)
{
    if (size > cache->max_size)
        return false;

    while (1) {
        if (cache->size + size <= cache->max_size)
            return true;

        cache_destroy_oldest_item(cache);
    }
}

bool
cache_add(struct cache *cache, const char *key,
          struct cache_item *item)
{
    /* XXX size constraints */
    if (!cache_need_room(cache, item->size)) {
        if (cache->cls.destroy != nullptr)
            cache->cls.destroy(item);
        return false;
    }

    cache->Check();

    item->key = key;
    hashmap_add(cache->items, key, item);
    cache->sorted_items.push_back(*item);

    cache->size += item->size;
    item->last_accessed = now_s();

    cache->Check();

    cache->cleanup_timer.Enable();
    return true;
}

bool
cache_put(struct cache *cache, const char *key,
          struct cache_item *item)
{
    /* XXX size constraints */

    assert(item != nullptr);
    assert(item->size > 0);
    assert(item->lock == 0);
    assert(!item->removed);

    if (!cache_need_room(cache, item->size)) {
        if (cache->cls.destroy != nullptr)
            cache->cls.destroy(item);
        return false;
    }

    cache->Check();

    item->key = key;

    struct cache_item *old = (struct cache_item *)
        hashmap_set(cache->items, key, item);
    if (old != nullptr)
        cache->ItemRemoved(old);

    cache->size += item->size;
    item->last_accessed = now_s();

    cache->sorted_items.push_back(*item);

    cache->Check();

    cache->cleanup_timer.Enable();
    return true;
}

bool
cache_put_match(struct cache *cache, const char *key,
                struct cache_item *item,
                bool (*match)(const struct cache_item *, void *),
                void *ctx)
{
    struct cache_item *old = cache_get_match(cache, key, match, ctx);

    assert(item != nullptr);
    assert(item->size > 0);
    assert(item->lock == 0);
    assert(!item->removed);
    assert(old == nullptr || !old->removed);

    if (old != nullptr)
        cache_remove_item(cache, key, old);

    return cache_add(cache, key, item);
}

void
cache_remove(struct cache *cache, const char *key)
{
    struct cache_item *item;

    while ((item = (struct cache_item *)hashmap_remove(cache->items, key)) != nullptr)
        cache->ItemRemoved(item);

    cache->Check();
}

struct cache_remove_match_data {
    struct cache *cache;
    bool (*match)(const struct cache_item *, void *);
    void *ctx;
};

static bool
cache_remove_match_callback(void *value, void *ctx)
{
    const struct cache_remove_match_data *data =
        (const struct cache_remove_match_data *)ctx;
    struct cache_item *item = (struct cache_item *)value;

    if (data->match(item, data->ctx)) {
        data->cache->ItemRemoved(item);
        return true;
    } else
        return false;
}

void
cache_remove_match(struct cache *cache, const char *key,
                   bool (*match)(const struct cache_item *, void *),
                   void *ctx)
{
    struct cache_remove_match_data data = {
        .cache = cache,
        .match = match,
        .ctx = ctx,
    };

    cache->Check();
    hashmap_remove_match(cache->items, key,
                         cache_remove_match_callback, &data);
    cache->Check();
}

void
cache_remove_item(struct cache *cache, const char *key,
                  struct cache_item *item)
{
    if (item->removed) {
        /* item has already been removed by somebody else */
        assert(item->lock > 0);
        return;
    }

    bool found = hashmap_remove_value(cache->items, key, item);
    if (!found) {
        /* the specified item has been removed before */
        cache->Check();
        return;
    }

    cache->ItemRemoved(item);

    cache->Check();
}

struct cache_remove_all_match_data {
    struct cache *cache;
    bool (*match)(const struct cache_item *, void *);
    void *ctx;
};

static bool
cache_remove_all_match_callback(gcc_unused const char *key, void *value,
                                void *ctx)
{
    const struct cache_remove_all_match_data *data =
        (const struct cache_remove_all_match_data *)ctx;
    struct cache_item *item = (struct cache_item *)value;

    if (data->match(item, data->ctx)) {
        data->cache->ItemRemoved(item);
        return true;
    } else
        return false;
}

unsigned
cache_remove_all_match(struct cache *cache,
                       bool (*match)(const struct cache_item *, void *),
                       void *ctx)
{
    struct cache_remove_all_match_data data = {
        .cache = cache,
        .match = match,
        .ctx = ctx,
    };
    unsigned removed;

    cache->Check();
    removed = hashmap_remove_all_match(cache->items,
                                       cache_remove_all_match_callback, &data);
    cache->Check();

    return removed;
}

void
cache_item_init_absolute(struct cache_item *item, time_t expires, size_t size)
{
    time_t now = time(nullptr);
    unsigned monotonic_expires = expires > now
        ? now_s() + (expires - now)
        : 1;

    cache_item_init(item, monotonic_expires, size);
}

void
cache_item_init_relative(struct cache_item *item, unsigned max_age,
                         size_t size)
{
    cache_item_init(item, now_s() + max_age, size);
}

void
cache_item_lock(struct cache_item *item)
{
    assert(item != nullptr);

    ++item->lock;
}

void
cache_item_unlock(struct cache *cache, struct cache_item *item)
{
    assert(item != nullptr);
    assert(item->lock > 0);

    if (--item->lock == 0 && item->removed)
        /* postponed destroy */
        cache_destroy_item(cache, item);
}

/** clean up expired cache items every 60 seconds */
bool
cache::ExpireCallback()
{
    const unsigned now = now_s();

    Check();

    for (auto i = sorted_items.begin(), end = sorted_items.end(); i != end;) {
        struct cache_item &item = *i++;

        if (item.expires > now)
            /* not yet expired */
            continue;

        RemoveItem(item);
    }

    Check();

    return size > 0;
}

bool
cache::ExpireCallback(void *ctx)
{
    struct cache *cache = (struct cache *)ctx;

    return cache->ExpireCallback();
}

void
cache_event_add(struct cache *cache)
{
    if (cache->size > 0)
        cache->cleanup_timer.Enable();
}

void
cache_event_del(struct cache *cache)
{
    cache->cleanup_timer.Disable();
}
<commit_msg>cache: use RemoveItem() in cache_put_match()<commit_after>/*
 * Generic cache class.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "cache.hxx"
#include "hashmap.hxx"
#include "AllocatorStats.hxx"
#include "pool.hxx"
#include "event/CleanupTimer.hxx"
#include "system/clock.h"

#include <assert.h>
#include <time.h>
#include <event.h>

/* #define ENABLE_EXCESSIVE_CACHE_CHECKS */

struct cache {
    struct pool &pool;

    const struct cache_class &cls;
    const size_t max_size;
    size_t size;
    struct hashmap *const items;

    /**
     * A linked list of all cache items, sorted by last_accessed,
     * oldest first.
     */
    boost::intrusive::list<struct cache_item,
                           boost::intrusive::member_hook<struct cache_item,
                                                         cache_item::SiblingsHook,
                                                         &cache_item::sorted_siblings>,
                           boost::intrusive::constant_time_size<false>> sorted_items;

    CleanupTimer cleanup_timer;

    cache(struct pool &_pool, const struct cache_class &_cls,
          unsigned hashtable_capacity, size_t _max_size)
        :pool(_pool), cls(_cls),
         max_size(_max_size), size(0),
         items(hashmap_new(&_pool, hashtable_capacity)) {
        cleanup_timer.Init(60, ExpireCallback, this);
    }

    ~cache();

    /** clean up expired cache items every 60 seconds */
    bool ExpireCallback();
    static bool ExpireCallback(void *ctx);

    void Check() const;

    void ItemRemoved(struct cache_item *item);

    void RemoveItem(struct cache_item &item) {
        assert(!item.removed);

        hashmap_remove_existing(items, item.key, &item);
        ItemRemoved(&item);
    }
};

struct cache *
cache_new(struct pool &pool, const struct cache_class *cls,
          unsigned hashtable_capacity, size_t max_size)
{
    assert(cls != nullptr);

    return new cache(pool, *cls, hashtable_capacity, max_size);
}

inline
cache::~cache()
{
    cleanup_timer.Deinit();

    Check();

    if (cls.destroy != nullptr) {
        hashmap_rewind(items);

        const struct hashmap_pair *pair;
        while ((pair = hashmap_next(items)) != nullptr) {
            struct cache_item *item = (struct cache_item *)pair->value;

            assert(item->lock == 0);
            assert(size >= item->size);
            size -= item->size;

#ifndef NDEBUG
            sorted_items.erase(sorted_items.iterator_to(*item));
#endif

            cls.destroy(item);
        }

        assert(size == 0);
        assert(sorted_items.empty());
    }
}

void
cache_close(struct cache *cache)
{
    delete cache;
}

AllocatorStats
cache_get_stats(const struct cache &cache)
{
    AllocatorStats stats;
    stats.netto_size = pool_children_netto_size(&cache.pool);
    stats.brutto_size = pool_children_brutto_size(&cache.pool);
    return stats;
}

inline void
cache::Check() const
{
#if !defined(NDEBUG) && defined(ENABLE_EXCESSIVE_CACHE_CHECKS)
    const struct hashmap_pair *pair;
    size_t s = 0;

    assert(size <= max_size);

    hashmap_rewind(items);
    while ((pair = hashmap_next(items)) != nullptr) {
        struct cache_item *item = (struct cache_item *)pair->value;

        s += item->size;
        assert(s <= size);
    }

    assert(s == size);
#endif
}

static void
cache_destroy_item(struct cache *cache, struct cache_item *item)
{
    if (cache->cls.destroy != nullptr)
        cache->cls.destroy(item);
}

void
cache::ItemRemoved(struct cache_item *item)
{
    assert(item != nullptr);
    assert(item->size > 0);
    assert(item->lock > 0 || !item->removed);
    assert(size >= item->size);

    sorted_items.erase(sorted_items.iterator_to(*item));

    size -= item->size;

    if (item->lock == 0)
        cache_destroy_item(this, item);
    else
        /* this item is locked - postpone the destroy() call */
        item->removed = true;

    if (size == 0)
        cleanup_timer.Disable();
}

static bool
cache_flush_callback(gcc_unused const char *key, void *value, void *ctx)
{
    struct cache *cache = (struct cache *)ctx;
    struct cache_item *item = (struct cache_item *)value;

    cache->ItemRemoved(item);
    return true;
}

void
cache_flush(struct cache *cache)
{
    cache->Check();

    hashmap_remove_all_match(cache->items, cache_flush_callback, cache);

    cache->Check();
}

static bool
cache_item_validate(const struct cache *cache, struct cache_item *item,
                    unsigned now)
{
    return now < item->expires &&
        (cache->cls.validate == nullptr || cache->cls.validate(item));
}

static void
cache_refresh_item(struct cache *cache, struct cache_item *item, unsigned now)
{
    item->last_accessed = now;

    /* move to the front of the linked list */
    cache->sorted_items.erase(cache->sorted_items.iterator_to(*item));
    cache->sorted_items.push_back(*item);
}

struct cache_item *
cache_get(struct cache *cache, const char *key)
{
    struct cache_item *item = (struct cache_item *)
        hashmap_get(cache->items, key);
    if (item == nullptr)
        return nullptr;

    const unsigned now = now_s();

    if (!cache_item_validate(cache, item, now)) {
        cache->Check();
        cache->RemoveItem(*item);
        cache->Check();
        return nullptr;
    }

    cache_refresh_item(cache, item, now);
    return item;
}

struct cache_item *
cache_get_match(struct cache *cache, const char *key,
                bool (*match)(const struct cache_item *, void *),
                void *ctx)
{
    const unsigned now = now_s();
    const struct hashmap_pair *pair = nullptr;

    while (true) {
        if (pair != nullptr) {
            struct cache_item *item = (struct cache_item *)pair->value;

            if (!cache_item_validate(cache, item, now)) {
                /* expired cache item: delete it, and re-start the
                   search */

                cache->Check();
                cache->RemoveItem(*item);
                cache->Check();

                pair = nullptr;
                continue;
            }

            if (match(item, ctx)) {
                /* this one matches: return it to the caller */
                cache_refresh_item(cache, item, now);
                return item;
            }

            /* find the next cache_item for this key */
            pair = hashmap_lookup_next(pair);
        } else {
            /* find the first cache_item for this key */
            pair = hashmap_lookup_first(cache->items, key);
        }

        if (pair == nullptr)
            /* no match */
            return nullptr;
    };
}

static void
cache_destroy_oldest_item(struct cache *cache)
{
    if (cache->sorted_items.empty())
        return;

    struct cache_item &item = cache->sorted_items.front();

    cache->Check();
    cache->RemoveItem(item);
    cache->Check();
}

static bool
cache_need_room(struct cache *cache, size_t size)
{
    if (size > cache->max_size)
        return false;

    while (1) {
        if (cache->size + size <= cache->max_size)
            return true;

        cache_destroy_oldest_item(cache);
    }
}

bool
cache_add(struct cache *cache, const char *key,
          struct cache_item *item)
{
    /* XXX size constraints */
    if (!cache_need_room(cache, item->size)) {
        if (cache->cls.destroy != nullptr)
            cache->cls.destroy(item);
        return false;
    }

    cache->Check();

    item->key = key;
    hashmap_add(cache->items, key, item);
    cache->sorted_items.push_back(*item);

    cache->size += item->size;
    item->last_accessed = now_s();

    cache->Check();

    cache->cleanup_timer.Enable();
    return true;
}

bool
cache_put(struct cache *cache, const char *key,
          struct cache_item *item)
{
    /* XXX size constraints */

    assert(item != nullptr);
    assert(item->size > 0);
    assert(item->lock == 0);
    assert(!item->removed);

    if (!cache_need_room(cache, item->size)) {
        if (cache->cls.destroy != nullptr)
            cache->cls.destroy(item);
        return false;
    }

    cache->Check();

    item->key = key;

    struct cache_item *old = (struct cache_item *)
        hashmap_set(cache->items, key, item);
    if (old != nullptr)
        cache->ItemRemoved(old);

    cache->size += item->size;
    item->last_accessed = now_s();

    cache->sorted_items.push_back(*item);

    cache->Check();

    cache->cleanup_timer.Enable();
    return true;
}

bool
cache_put_match(struct cache *cache, const char *key,
                struct cache_item *item,
                bool (*match)(const struct cache_item *, void *),
                void *ctx)
{
    struct cache_item *old = cache_get_match(cache, key, match, ctx);

    assert(item != nullptr);
    assert(item->size > 0);
    assert(item->lock == 0);
    assert(!item->removed);

    if (old != nullptr)
        cache->RemoveItem(*old);

    return cache_add(cache, key, item);
}

void
cache_remove(struct cache *cache, const char *key)
{
    struct cache_item *item;

    while ((item = (struct cache_item *)hashmap_remove(cache->items, key)) != nullptr)
        cache->ItemRemoved(item);

    cache->Check();
}

struct cache_remove_match_data {
    struct cache *cache;
    bool (*match)(const struct cache_item *, void *);
    void *ctx;
};

static bool
cache_remove_match_callback(void *value, void *ctx)
{
    const struct cache_remove_match_data *data =
        (const struct cache_remove_match_data *)ctx;
    struct cache_item *item = (struct cache_item *)value;

    if (data->match(item, data->ctx)) {
        data->cache->ItemRemoved(item);
        return true;
    } else
        return false;
}

void
cache_remove_match(struct cache *cache, const char *key,
                   bool (*match)(const struct cache_item *, void *),
                   void *ctx)
{
    struct cache_remove_match_data data = {
        .cache = cache,
        .match = match,
        .ctx = ctx,
    };

    cache->Check();
    hashmap_remove_match(cache->items, key,
                         cache_remove_match_callback, &data);
    cache->Check();
}

void
cache_remove_item(struct cache *cache, const char *key,
                  struct cache_item *item)
{
    if (item->removed) {
        /* item has already been removed by somebody else */
        assert(item->lock > 0);
        return;
    }

    bool found = hashmap_remove_value(cache->items, key, item);
    if (!found) {
        /* the specified item has been removed before */
        cache->Check();
        return;
    }

    cache->ItemRemoved(item);

    cache->Check();
}

struct cache_remove_all_match_data {
    struct cache *cache;
    bool (*match)(const struct cache_item *, void *);
    void *ctx;
};

static bool
cache_remove_all_match_callback(gcc_unused const char *key, void *value,
                                void *ctx)
{
    const struct cache_remove_all_match_data *data =
        (const struct cache_remove_all_match_data *)ctx;
    struct cache_item *item = (struct cache_item *)value;

    if (data->match(item, data->ctx)) {
        data->cache->ItemRemoved(item);
        return true;
    } else
        return false;
}

unsigned
cache_remove_all_match(struct cache *cache,
                       bool (*match)(const struct cache_item *, void *),
                       void *ctx)
{
    struct cache_remove_all_match_data data = {
        .cache = cache,
        .match = match,
        .ctx = ctx,
    };
    unsigned removed;

    cache->Check();
    removed = hashmap_remove_all_match(cache->items,
                                       cache_remove_all_match_callback, &data);
    cache->Check();

    return removed;
}

void
cache_item_init_absolute(struct cache_item *item, time_t expires, size_t size)
{
    time_t now = time(nullptr);
    unsigned monotonic_expires = expires > now
        ? now_s() + (expires - now)
        : 1;

    cache_item_init(item, monotonic_expires, size);
}

void
cache_item_init_relative(struct cache_item *item, unsigned max_age,
                         size_t size)
{
    cache_item_init(item, now_s() + max_age, size);
}

void
cache_item_lock(struct cache_item *item)
{
    assert(item != nullptr);

    ++item->lock;
}

void
cache_item_unlock(struct cache *cache, struct cache_item *item)
{
    assert(item != nullptr);
    assert(item->lock > 0);

    if (--item->lock == 0 && item->removed)
        /* postponed destroy */
        cache_destroy_item(cache, item);
}

/** clean up expired cache items every 60 seconds */
bool
cache::ExpireCallback()
{
    const unsigned now = now_s();

    Check();

    for (auto i = sorted_items.begin(), end = sorted_items.end(); i != end;) {
        struct cache_item &item = *i++;

        if (item.expires > now)
            /* not yet expired */
            continue;

        RemoveItem(item);
    }

    Check();

    return size > 0;
}

bool
cache::ExpireCallback(void *ctx)
{
    struct cache *cache = (struct cache *)ctx;

    return cache->ExpireCallback();
}

void
cache_event_add(struct cache *cache)
{
    if (cache->size > 0)
        cache->cleanup_timer.Enable();
}

void
cache_event_del(struct cache *cache)
{
    cache->cleanup_timer.Disable();
}
<|endoftext|>