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

#include "webkit/plugins/ppapi/ppb_url_request_info_impl.h"

#include "base/logging.h"
#include "base/string_util.h"
#include "googleurl/src/gurl.h"
#include "net/http/http_util.h"
#include "ppapi/c/pp_var.h"
#include "third_party/WebKit/WebKit/chromium/public/WebData.h"
#include "third_party/WebKit/WebKit/chromium/public/WebDocument.h"
#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
#include "third_party/WebKit/WebKit/chromium/public/WebHTTPBody.h"
#include "third_party/WebKit/WebKit/chromium/public/WebURL.h"
#include "third_party/WebKit/WebKit/chromium/public/WebURLRequest.h"
#include "webkit/plugins/ppapi/common.h"
#include "webkit/plugins/ppapi/plugin_module.h"
#include "webkit/plugins/ppapi/ppb_file_ref_impl.h"
#include "webkit/plugins/ppapi/string.h"
#include "webkit/plugins/ppapi/var.h"
#include "webkit/glue/webkit_glue.h"

using WebKit::WebData;
using WebKit::WebHTTPBody;
using WebKit::WebString;
using WebKit::WebFrame;
using WebKit::WebURL;
using WebKit::WebURLRequest;

namespace webkit {
namespace ppapi {

namespace {

// If any of these request headers are specified, they will not be sent.
// TODO(darin): Add more based on security considerations?
const char* const kIgnoredRequestHeaders[] = {
  "content-length"
};

PP_Bool IsIgnoredRequestHeader(const std::string& name) {
  for (size_t i = 0; i < arraysize(kIgnoredRequestHeaders); ++i) {
    if (LowerCaseEqualsASCII(name, kIgnoredRequestHeaders[i]))
      return PP_TRUE;
  }
  return PP_FALSE;
}

PP_Resource Create(PP_Module module_id) {
  PluginModule* module = ResourceTracker::Get()->GetModule(module_id);
  if (!module)
    return 0;

  PPB_URLRequestInfo_Impl* request = new PPB_URLRequestInfo_Impl(module);

  return request->GetReference();
}

PP_Bool IsURLRequestInfo(PP_Resource resource) {
  return BoolToPPBool(!!Resource::GetAs<PPB_URLRequestInfo_Impl>(resource));
}

PP_Bool SetProperty(PP_Resource request_id,
                    PP_URLRequestProperty property,
                    PP_Var var) {
  scoped_refptr<PPB_URLRequestInfo_Impl> request(
      Resource::GetAs<PPB_URLRequestInfo_Impl>(request_id));
  if (!request)
    return PP_FALSE;

  if (var.type == PP_VARTYPE_BOOL) {
    return BoolToPPBool(
        request->SetBooleanProperty(property,
                                    PPBoolToBool(var.value.as_bool)));
  }

  if (var.type == PP_VARTYPE_STRING) {
    scoped_refptr<StringVar> string(StringVar::FromPPVar(var));
    if (string) {
      return BoolToPPBool(request->SetStringProperty(property,
                                                     string->value()));
    }
  }

  return PP_FALSE;
}

PP_Bool AppendDataToBody(PP_Resource request_id,
                         const char* data,
                         uint32_t len) {
  scoped_refptr<PPB_URLRequestInfo_Impl> request(
      Resource::GetAs<PPB_URLRequestInfo_Impl>(request_id));
  if (!request)
    return PP_FALSE;

  return BoolToPPBool(request->AppendDataToBody(std::string(data, len)));
}

PP_Bool AppendFileToBody(PP_Resource request_id,
                      PP_Resource file_ref_id,
                      int64_t start_offset,
                      int64_t number_of_bytes,
                      PP_Time expected_last_modified_time) {
  scoped_refptr<PPB_URLRequestInfo_Impl> request(
      Resource::GetAs<PPB_URLRequestInfo_Impl>(request_id));
  if (!request)
    return PP_FALSE;

  scoped_refptr<PPB_FileRef_Impl> file_ref(
      Resource::GetAs<PPB_FileRef_Impl>(file_ref_id));
  if (!file_ref)
    return PP_FALSE;

  return BoolToPPBool(request->AppendFileToBody(file_ref,
                                                start_offset,
                                                number_of_bytes,
                                                expected_last_modified_time));
}

const PPB_URLRequestInfo ppb_urlrequestinfo = {
  &Create,
  &IsURLRequestInfo,
  &SetProperty,
  &AppendDataToBody,
  &AppendFileToBody
};

}  // namespace

struct PPB_URLRequestInfo_Impl::BodyItem {
  BodyItem(const std::string& data)
      : data(data),
        start_offset(0),
        number_of_bytes(-1),
        expected_last_modified_time(0.0) {
  }

  BodyItem(PPB_FileRef_Impl* file_ref,
           int64_t start_offset,
           int64_t number_of_bytes,
           PP_Time expected_last_modified_time)
      : file_ref(file_ref),
        start_offset(start_offset),
        number_of_bytes(number_of_bytes),
        expected_last_modified_time(expected_last_modified_time) {
  }

  std::string data;
  scoped_refptr<PPB_FileRef_Impl> file_ref;
  int64_t start_offset;
  int64_t number_of_bytes;
  PP_Time expected_last_modified_time;
};

PPB_URLRequestInfo_Impl::PPB_URLRequestInfo_Impl(PluginModule* module)
    : Resource(module),
      stream_to_file_(false),
      follow_redirects_(true),
      record_download_progress_(false),
      record_upload_progress_(false) {
}

PPB_URLRequestInfo_Impl::~PPB_URLRequestInfo_Impl() {
}

// static
const PPB_URLRequestInfo* PPB_URLRequestInfo_Impl::GetInterface() {
  return &ppb_urlrequestinfo;
}

PPB_URLRequestInfo_Impl* PPB_URLRequestInfo_Impl::AsPPB_URLRequestInfo_Impl() {
  return this;
}

bool PPB_URLRequestInfo_Impl::SetBooleanProperty(PP_URLRequestProperty property,
                                                 bool value) {
  switch (property) {
    case PP_URLREQUESTPROPERTY_STREAMTOFILE:
      stream_to_file_ = value;
      return true;
    case PP_URLREQUESTPROPERTY_FOLLOWREDIRECTS:
      follow_redirects_ = value;
      return true;
    case PP_URLREQUESTPROPERTY_RECORDDOWNLOADPROGRESS:
      record_download_progress_ = value;
      return true;
    case PP_URLREQUESTPROPERTY_RECORDUPLOADPROGRESS:
      record_upload_progress_ = value;
      return true;
    default:
      //NOTIMPLEMENTED();  // TODO(darin): Implement me!
      return false;
  }
}

bool PPB_URLRequestInfo_Impl::SetStringProperty(PP_URLRequestProperty property,
                                                const std::string& value) {
  // TODO(darin): Validate input.  Perhaps at a different layer?
  switch (property) {
    case PP_URLREQUESTPROPERTY_URL:
      url_ = value;  // NOTE: This may be a relative URL.
      return true;
    case PP_URLREQUESTPROPERTY_METHOD:
      method_ = value;
      return true;
    case PP_URLREQUESTPROPERTY_HEADERS:
      headers_ = value;
      return true;
    default:
      return false;
  }
}

bool PPB_URLRequestInfo_Impl::AppendDataToBody(const std::string& data) {
  if (!data.empty())
    body_.push_back(BodyItem(data));
  return true;
}

bool PPB_URLRequestInfo_Impl::AppendFileToBody(
    PPB_FileRef_Impl* file_ref,
    int64_t start_offset,
    int64_t number_of_bytes,
    PP_Time expected_last_modified_time) {
  // Ignore a call to append nothing.
  if (number_of_bytes == 0)
    return true;

  // Check for bad values.  (-1 means read until end of file.)
  if (start_offset < 0 || number_of_bytes < -1)
    return false;

  body_.push_back(BodyItem(file_ref,
                           start_offset,
                           number_of_bytes,
                           expected_last_modified_time));
  return true;
}

WebURLRequest PPB_URLRequestInfo_Impl::ToWebURLRequest(WebFrame* frame) const {
  WebURLRequest web_request;
  web_request.initialize();
  web_request.setURL(frame->document().completeURL(WebString::fromUTF8(url_)));
  web_request.setDownloadToFile(stream_to_file_);

  if (!method_.empty())
    web_request.setHTTPMethod(WebString::fromUTF8(method_));

  if (!headers_.empty()) {
    net::HttpUtil::HeadersIterator it(headers_.begin(), headers_.end(), "\n");
    while (it.GetNext()) {
      if (!IsIgnoredRequestHeader(it.name())) {
        web_request.addHTTPHeaderField(
            WebString::fromUTF8(it.name()),
            WebString::fromUTF8(it.values()));
      }
    }
  }

  if (!body_.empty()) {
    WebHTTPBody http_body;
    http_body.initialize();
    for (size_t i = 0; i < body_.size(); ++i) {
      if (body_[i].file_ref) {
        http_body.appendFileRange(
            webkit_glue::FilePathToWebString(
                body_[i].file_ref->GetSystemPath()),
            body_[i].start_offset,
            body_[i].number_of_bytes,
            body_[i].expected_last_modified_time);
      } else {
        DCHECK(!body_[i].data.empty());
        http_body.appendData(WebData(body_[i].data));
      }
    }
    web_request.setHTTPBody(http_body);
  }

  frame->setReferrerForRequest(web_request, WebURL());  // Use default.
  return web_request;
}

}  // namespace ppapi
}  // namespace webkit

<commit_msg>Restrict HTTP headers by checking in URLRequestInfo::SetProperty. BUG=47354 TEST=none Review URL: http://codereview.chromium.org/5138010<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "webkit/plugins/ppapi/ppb_url_request_info_impl.h"

#include "base/logging.h"
#include "base/string_util.h"
#include "googleurl/src/gurl.h"
#include "net/http/http_util.h"
#include "ppapi/c/pp_var.h"
#include "third_party/WebKit/WebKit/chromium/public/WebData.h"
#include "third_party/WebKit/WebKit/chromium/public/WebDocument.h"
#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
#include "third_party/WebKit/WebKit/chromium/public/WebHTTPBody.h"
#include "third_party/WebKit/WebKit/chromium/public/WebURL.h"
#include "third_party/WebKit/WebKit/chromium/public/WebURLRequest.h"
#include "webkit/plugins/ppapi/common.h"
#include "webkit/plugins/ppapi/plugin_module.h"
#include "webkit/plugins/ppapi/ppb_file_ref_impl.h"
#include "webkit/plugins/ppapi/string.h"
#include "webkit/plugins/ppapi/var.h"
#include "webkit/glue/webkit_glue.h"

using WebKit::WebData;
using WebKit::WebHTTPBody;
using WebKit::WebString;
using WebKit::WebFrame;
using WebKit::WebURL;
using WebKit::WebURLRequest;

namespace webkit {
namespace ppapi {

namespace {

// A header string containing any of the following fields will cause
// an error. The list comes from the XMLHttpRequest standard.
// http://www.w3.org/TR/XMLHttpRequest/#the-setrequestheader-method
const char* const kForbiddenHeaderFields[] = {
  "accept-charset",
  "accept-encoding",
  "connection",
  "content-length",
  "cookie",
  "cookie2",
  "content-transfer-encoding",
  "date",
  "expect",
  "host",
  "keep-alive",
  "origin",
  "referer",
  "te",
  "trailer",
  "transfer-encoding",
  "upgrade",
  "user-agent",
  "via",
};

bool IsValidHeaderField(const std::string& name) {
  for (size_t i = 0; i < arraysize(kForbiddenHeaderFields); ++i) {
    if (LowerCaseEqualsASCII(name, kForbiddenHeaderFields[i]))
      return false;
  }
  if (StartsWithASCII(name, "proxy-", false))
    return false;
  if (StartsWithASCII(name, "sec-", false))
    return false;

  return true;
}

bool AreValidHeaders(const std::string& headers) {
  net::HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\n");
  while (it.GetNext()) {
    if (!IsValidHeaderField(it.name()))
      return false;
  }
  return true;
}

PP_Resource Create(PP_Module module_id) {
  PluginModule* module = ResourceTracker::Get()->GetModule(module_id);
  if (!module)
    return 0;

  PPB_URLRequestInfo_Impl* request = new PPB_URLRequestInfo_Impl(module);

  return request->GetReference();
}

PP_Bool IsURLRequestInfo(PP_Resource resource) {
  return BoolToPPBool(!!Resource::GetAs<PPB_URLRequestInfo_Impl>(resource));
}

PP_Bool SetProperty(PP_Resource request_id,
                    PP_URLRequestProperty property,
                    PP_Var var) {
  scoped_refptr<PPB_URLRequestInfo_Impl> request(
      Resource::GetAs<PPB_URLRequestInfo_Impl>(request_id));
  if (!request)
    return PP_FALSE;

  if (var.type == PP_VARTYPE_BOOL) {
    return BoolToPPBool(
        request->SetBooleanProperty(property,
                                    PPBoolToBool(var.value.as_bool)));
  }

  if (var.type == PP_VARTYPE_STRING) {
    scoped_refptr<StringVar> string(StringVar::FromPPVar(var));
    if (string) {
      return BoolToPPBool(request->SetStringProperty(property,
                                                     string->value()));
    }
  }

  return PP_FALSE;
}

PP_Bool AppendDataToBody(PP_Resource request_id,
                         const char* data,
                         uint32_t len) {
  scoped_refptr<PPB_URLRequestInfo_Impl> request(
      Resource::GetAs<PPB_URLRequestInfo_Impl>(request_id));
  if (!request)
    return PP_FALSE;

  return BoolToPPBool(request->AppendDataToBody(std::string(data, len)));
}

PP_Bool AppendFileToBody(PP_Resource request_id,
                      PP_Resource file_ref_id,
                      int64_t start_offset,
                      int64_t number_of_bytes,
                      PP_Time expected_last_modified_time) {
  scoped_refptr<PPB_URLRequestInfo_Impl> request(
      Resource::GetAs<PPB_URLRequestInfo_Impl>(request_id));
  if (!request)
    return PP_FALSE;

  scoped_refptr<PPB_FileRef_Impl> file_ref(
      Resource::GetAs<PPB_FileRef_Impl>(file_ref_id));
  if (!file_ref)
    return PP_FALSE;

  return BoolToPPBool(request->AppendFileToBody(file_ref,
                                                start_offset,
                                                number_of_bytes,
                                                expected_last_modified_time));
}

const PPB_URLRequestInfo ppb_urlrequestinfo = {
  &Create,
  &IsURLRequestInfo,
  &SetProperty,
  &AppendDataToBody,
  &AppendFileToBody
};

}  // namespace

struct PPB_URLRequestInfo_Impl::BodyItem {
  BodyItem(const std::string& data)
      : data(data),
        start_offset(0),
        number_of_bytes(-1),
        expected_last_modified_time(0.0) {
  }

  BodyItem(PPB_FileRef_Impl* file_ref,
           int64_t start_offset,
           int64_t number_of_bytes,
           PP_Time expected_last_modified_time)
      : file_ref(file_ref),
        start_offset(start_offset),
        number_of_bytes(number_of_bytes),
        expected_last_modified_time(expected_last_modified_time) {
  }

  std::string data;
  scoped_refptr<PPB_FileRef_Impl> file_ref;
  int64_t start_offset;
  int64_t number_of_bytes;
  PP_Time expected_last_modified_time;
};

PPB_URLRequestInfo_Impl::PPB_URLRequestInfo_Impl(PluginModule* module)
    : Resource(module),
      stream_to_file_(false),
      follow_redirects_(true),
      record_download_progress_(false),
      record_upload_progress_(false) {
}

PPB_URLRequestInfo_Impl::~PPB_URLRequestInfo_Impl() {
}

// static
const PPB_URLRequestInfo* PPB_URLRequestInfo_Impl::GetInterface() {
  return &ppb_urlrequestinfo;
}

PPB_URLRequestInfo_Impl* PPB_URLRequestInfo_Impl::AsPPB_URLRequestInfo_Impl() {
  return this;
}

bool PPB_URLRequestInfo_Impl::SetBooleanProperty(PP_URLRequestProperty property,
                                                 bool value) {
  switch (property) {
    case PP_URLREQUESTPROPERTY_STREAMTOFILE:
      stream_to_file_ = value;
      return true;
    case PP_URLREQUESTPROPERTY_FOLLOWREDIRECTS:
      follow_redirects_ = value;
      return true;
    case PP_URLREQUESTPROPERTY_RECORDDOWNLOADPROGRESS:
      record_download_progress_ = value;
      return true;
    case PP_URLREQUESTPROPERTY_RECORDUPLOADPROGRESS:
      record_upload_progress_ = value;
      return true;
    default:
      return false;
  }
}

bool PPB_URLRequestInfo_Impl::SetStringProperty(PP_URLRequestProperty property,
                                                const std::string& value) {
  // TODO(darin): Validate input.  Perhaps at a different layer?
  switch (property) {
    case PP_URLREQUESTPROPERTY_URL:
      url_ = value;  // NOTE: This may be a relative URL.
      return true;
    case PP_URLREQUESTPROPERTY_METHOD:
      method_ = value;
      return true;
    case PP_URLREQUESTPROPERTY_HEADERS:
      if (!AreValidHeaders(value))
        return false;
      headers_ = value;
      return true;
    default:
      return false;
  }
}

bool PPB_URLRequestInfo_Impl::AppendDataToBody(const std::string& data) {
  if (!data.empty())
    body_.push_back(BodyItem(data));
  return true;
}

bool PPB_URLRequestInfo_Impl::AppendFileToBody(
    PPB_FileRef_Impl* file_ref,
    int64_t start_offset,
    int64_t number_of_bytes,
    PP_Time expected_last_modified_time) {
  // Ignore a call to append nothing.
  if (number_of_bytes == 0)
    return true;

  // Check for bad values.  (-1 means read until end of file.)
  if (start_offset < 0 || number_of_bytes < -1)
    return false;

  body_.push_back(BodyItem(file_ref,
                           start_offset,
                           number_of_bytes,
                           expected_last_modified_time));
  return true;
}

WebURLRequest PPB_URLRequestInfo_Impl::ToWebURLRequest(WebFrame* frame) const {
  WebURLRequest web_request;
  web_request.initialize();
  web_request.setURL(frame->document().completeURL(WebString::fromUTF8(url_)));
  web_request.setDownloadToFile(stream_to_file_);

  if (!method_.empty())
    web_request.setHTTPMethod(WebString::fromUTF8(method_));

  if (!headers_.empty()) {
    net::HttpUtil::HeadersIterator it(headers_.begin(), headers_.end(), "\n");
    while (it.GetNext()) {
      web_request.addHTTPHeaderField(
          WebString::fromUTF8(it.name()),
          WebString::fromUTF8(it.values()));
    }
  }

  if (!body_.empty()) {
    WebHTTPBody http_body;
    http_body.initialize();
    for (size_t i = 0; i < body_.size(); ++i) {
      if (body_[i].file_ref) {
        http_body.appendFileRange(
            webkit_glue::FilePathToWebString(
                body_[i].file_ref->GetSystemPath()),
            body_[i].start_offset,
            body_[i].number_of_bytes,
            body_[i].expected_last_modified_time);
      } else {
        DCHECK(!body_[i].data.empty());
        http_body.appendData(WebData(body_[i].data));
      }
    }
    web_request.setHTTPBody(http_body);
  }

  frame->setReferrerForRequest(web_request, WebURL());  // Use default.
  return web_request;
}

}  // namespace ppapi
}  // namespace webkit

<|endoftext|>


<commit_before>#ifndef _CONNECT_H
#define _CONNECT_H

#include <vector>
#include <memory>
#include <functional>

#define emit
#define slots
#define signals public
#define connect(sender, signal, slot) ((sender)->signal.bind(slot))

template<typename... Args>
class Slot
{
public:
    using OnFunc = std::function<void(Args...)>;

    Slot(const OnFunc& func)
        : m_func(func)
    {
        // Do nothing
    }

    void exec(Args... args)
    {
        m_func(args...);
    }

private:
    OnFunc m_func = nullptr;
};

template<typename... Args>
class Signal
{
public:
    using SlotPtr = std::shared_ptr<Slot<Args...>>;
    using OnFunc = std::function<void(Args...)>;

    void bind(const OnFunc& func)
    {
        m_slotVec.push_back(SlotPtr(new Slot<Args...>(func)));
    }

    void operator()(Args... args)
    {
        for (auto& iter : m_slotVec)
        {
            iter->exec(args...);
        }
    }

private:
    std::vector<SlotPtr> m_slotVec;
};

#endif
<commit_msg>Update connect<commit_after>#ifndef _CONNECT_H
#define _CONNECT_H

#include <vector>
#include <memory>
#include <functional>

#define emit
#define slots
#define signals public
#define connect(sender, signal, slot) ((sender)->signal.bind(slot))

template<typename... Args>
class Slot
{
public:
    using OnFunc = std::function<void(Args&&...)>;

    Slot(const OnFunc& func)
        : m_func(func)
    {
        // Do nothing
    }

    void exec(Args&&... args)
    {
        m_func(std::forward<Args>(args)...);
    }

private:
    OnFunc m_func = nullptr;
};

template<typename... Args>
class Signal
{
public:
    using SlotPtr = std::shared_ptr<Slot<Args&&...>>;
    using OnFunc = std::function<void(Args&&...)>;

    void bind(const OnFunc& func)
    {
        m_slotVec.push_back(SlotPtr(new Slot<Args&&...>(func)));
    }

    void operator()(Args&&... args)
    {
        for (auto& iter : m_slotVec)
        {
            iter->exec(std::forward<Args>(args)...);
        }
    }

private:
    std::vector<SlotPtr> m_slotVec;
};

#endif
<|endoftext|>


<commit_before>//
//  MaxPoolLayer.cpp
//  abs
//
//  Created by jiangxiaotang on 15/10/2016.
//  Copyright © 2016 jiangxiaotang. All rights reserved.
//

#include "MaxPoolLayer.hpp"
#include <fstream>
#include "LayerFactoryRegistor.hpp"

namespace ALCNN {
    MaxPoolLayer::MaxPoolLayer(int stride, int width, int height, int depth):ILayer(width*height*depth, width*height*depth/stride/stride, 0, 0, width*height*depth/stride/stride, 1)
    {
        ALASSERT(stride>=2);
        ALASSERT(width>0);
        ALASSERT(height>0);
        ALASSERT(depth>0);
        ALASSERT(width%stride==0);
        ALASSERT(height%stride==0);
        mInput.iExpand = 0;
        mInput.iHeight = height;
        mInput.iWidth = width;
        mInput.iDepth = depth;

        mOutput.iDepth = depth;
        mOutput.iExpand = 0;
        mOutput.iWidth = width/stride;
        mOutput.iHeight = height/stride;
        mStride = stride;
    }
    MaxPoolLayer::~ MaxPoolLayer()
    {
    }

    void MaxPoolLayer::vForward(const ALFloatMatrix* before, ALFloatMatrix* after, const ALFloatMatrix* parameters, ALFloatMatrix* cache) const
    {
        ALLEARNAUTOTIME;
        ALASSERT(NULL!=after);
        ALASSERT(NULL!=before);
        ALASSERT(before->height() == after->height());
        ALASSERT(before->width() == mInput.getTotalWidth());
        ALASSERT(after->width() == mOutput.getTotalWidth());
        auto batchSize = after->height();
        ALAUTOSTORAGE(srcLines, ALFLOAT*, mStride);
        auto h = mOutput.iHeight;
        auto w = mOutput.iWidth;
        for (int z=0; z<batchSize; ++z)
        {
            for (int p=0; p<mInput.iDepth; ++p)
            {
                auto output_p = after->vGetAddr(z)+p*mOutput.iWidth*mOutput.iHeight;
                auto input_p = before->vGetAddr(z)+p*mInput.iWidth*mInput.iHeight;
                auto cache_p = cache->vGetAddr(z)+p*mOutput.iWidth*mOutput.iHeight;

                for (int i=0; i<h; ++i)
                {
                    auto dst = output_p + mOutput.iWidth*i;
                    auto cache_dst = cache_p + mOutput.iWidth*i;
                    for (int k=0; k<mStride; ++k)
                    {
                        srcLines[k] = input_p + (mStride*i+k)*mInput.iWidth;
                    }
                    for (int j=0; j<w; ++j)
                    {
                        ALFLOAT maxNumber = srcLines[0][0];
                        size_t maxX = 0;
                        size_t maxY = 0;
                        for (int x=0; x<mStride; ++x)
                        {
                            for (int y=0; y<mStride; ++y)
                            {
                                auto s = srcLines[x][y+mStride*j];
                                if (maxNumber < s)
                                {
                                    maxNumber = s;
                                    maxX = x;
                                    maxY = y;
                                }
                            }
                        }
                        dst[j] = maxNumber;
                        cache_dst[j] = maxX + mStride*maxY;
                    }
                }
            }
        }
    }
    void MaxPoolLayer::vBackward(const ALFloatMatrix* after_diff, const ALFloatMatrix* after, const ALFloatMatrix* parameters, const ALFloatMatrix* before, ALFloatMatrix* before_diff, ALFloatMatrix* parameters_diff, ALFloatMatrix* cache) const
    {
        ALLEARNAUTOTIME;
        ALASSERT(NULL!=after_diff);
        ALASSERT(NULL!=after);
        ALASSERT(after->height() == after_diff->height());
        ALASSERT(before_diff->width() == mInput.getTotalWidth());
        ALASSERT(after_diff->width() == mOutput.getTotalWidth());
        auto batchSize = after_diff->height();
        ALFloatMatrix::zero(before_diff);
        auto h = mOutput.iHeight;
        auto w = mOutput.iWidth;
        for (int z=0; z<batchSize; ++z)
        {
            for (int p=0; p<mInput.iDepth; ++p)
            {
                auto output_p = after_diff->vGetAddr(z)+p*mOutput.iWidth*mOutput.iHeight;
                auto input_diff_p = before_diff->vGetAddr(z)+p*mInput.iWidth*mInput.iHeight;
                auto cache_p = cache->vGetAddr(z)+p*mOutput.iWidth*mOutput.iHeight;

                for (int i=0; i<h; ++i)
                {
                    auto dst = output_p + i*mOutput.iWidth;
                    auto cache_dst = cache_p + mOutput.iWidth*i;
                    for (int j=0; j<w; ++j)
                    {
                        auto mask = (size_t)(cache_dst[j]);
                        auto maxX = mask % mStride;
                        auto maxY = (mask - maxX)/mStride;
                        *(input_diff_p+(mStride*i+maxY)*mInput.iWidth + mStride*j+maxX) = dst[j];
                    }
                }
            }
        }
    }
    static auto gCreateFunction = [](const LayerParameters& p) {
        return new MaxPoolLayer(p.get("stride"), p.mMatrixInfo.iWidth, p.mMatrixInfo.iHeight, p.mMatrixInfo.iDepth);
    };

    static LayerFactoryRegister __reg(gCreateFunction, "MaxPool");

}
<commit_msg>Revert MaxPoolLayer opt for bug<commit_after>//
//  MaxPoolLayer.cpp
//  abs
//
//  Created by jiangxiaotang on 15/10/2016.
//  Copyright © 2016 jiangxiaotang. All rights reserved.
//

#include "MaxPoolLayer.hpp"
#include <fstream>
#include "LayerFactoryRegistor.hpp"

namespace ALCNN {
    MaxPoolLayer::MaxPoolLayer(int stride, int width, int height, int depth):ILayer(width*height*depth, width*height*depth/stride/stride, 0, 0, 0, 0)
    {
        ALASSERT(stride>=2);
        ALASSERT(width>0);
        ALASSERT(height>0);
        ALASSERT(depth>0);
        ALASSERT(width%stride==0);
        ALASSERT(height%stride==0);
        mInput.iExpand = 0;
        mInput.iHeight = height;
        mInput.iWidth = width;
        mInput.iDepth = depth;

        mOutput.iDepth = depth;
        mOutput.iExpand = 0;
        mOutput.iWidth = width/stride;
        mOutput.iHeight = height/stride;
        mStride = stride;
    }
    MaxPoolLayer::~ MaxPoolLayer()
    {
    }

    void MaxPoolLayer::vForward(const ALFloatMatrix* before, ALFloatMatrix* after, const ALFloatMatrix* parameters, ALFloatMatrix* cache) const
    {
        ALLEARNAUTOTIME;
        ALASSERT(NULL!=after);
        ALASSERT(NULL!=before);
        ALASSERT(before->height() == after->height());
        ALASSERT(before->width() == mInput.getTotalWidth());
        ALASSERT(after->width() == mOutput.getTotalWidth());
        auto batchSize = after->height();
        ALAUTOSTORAGE(srcLines, ALFLOAT*, mStride);
        for (int z=0; z<batchSize; ++z)
        {
            for (int p=0; p<mInput.iDepth; ++p)
            {
                ALSp<ALFloatMatrix> input = ALFloatMatrix::createRefMatrix(before->vGetAddr(z)+p*mInput.iWidth*mInput.iHeight, mInput.iWidth, mInput.iHeight);
                ALSp<ALFloatMatrix> output = ALFloatMatrix::createRefMatrix(after->vGetAddr(z)+p*mOutput.iWidth*mOutput.iHeight, mOutput.iWidth, mOutput.iHeight);
                auto h = mOutput.iHeight;
                auto w = mOutput.iWidth;
                for (int i=0; i<h; ++i)
                {
                    auto dst = output->vGetAddr(i);
                    for (int k=0; k<mStride; ++k)
                    {
                        srcLines[k] = input->vGetAddr(mStride*i+k);
                    }
                    for (int j=0; j<w; ++j)
                    {
                        ALFLOAT maxNumber = srcLines[0][0];
                        for (int x=0; x<mStride; ++x)
                        {
                            for (int y=0; y<mStride; ++y)
                            {
                                auto s = srcLines[x][y+mStride*j];
                                if (maxNumber < s)
                                {
                                    maxNumber = s;
                                }
                            }
                        }
                        dst[j] = maxNumber;
                    }
                }
            }
        }
    }
    void MaxPoolLayer::vBackward(const ALFloatMatrix* after_diff, const ALFloatMatrix* after, const ALFloatMatrix* parameters, const ALFloatMatrix* before, ALFloatMatrix* before_diff, ALFloatMatrix* parameters_diff, ALFloatMatrix* cache) const
    {
        ALLEARNAUTOTIME;
        ALASSERT(NULL!=after_diff);
        ALASSERT(NULL!=after);
        ALASSERT(after->height() == after_diff->height());
        ALASSERT(before_diff->width() == mInput.getTotalWidth());
        ALASSERT(after_diff->width() == mOutput.getTotalWidth());
        auto batchSize = after_diff->height();
        ALAUTOSTORAGE(srcLines, ALFLOAT*, mStride);
        ALAUTOSTORAGE(srcDiffLines, ALFLOAT*, mStride);
        ALFloatMatrix::zero(before_diff);
        for (int z=0; z<batchSize; ++z)
        {
            for (int p=0; p<mInput.iDepth; ++p)
            {
                ALSp<ALFloatMatrix> input = ALFloatMatrix::createRefMatrix(before->vGetAddr(z)+p*mInput.iWidth*mInput.iHeight, mInput.iWidth, mInput.iHeight);
                ALSp<ALFloatMatrix> input_diff = ALFloatMatrix::createRefMatrix(before_diff->vGetAddr(z)+p*mInput.iWidth*mInput.iHeight, mInput.iWidth, mInput.iHeight);
                ALSp<ALFloatMatrix> output = ALFloatMatrix::createRefMatrix(after_diff->vGetAddr(z)+p*mOutput.iWidth*mOutput.iHeight, mOutput.iWidth, mOutput.iHeight);
                auto h = mOutput.iHeight;
                auto w = mOutput.iWidth;
                for (int i=0; i<h; ++i)
                {
                    auto dst = output->vGetAddr(i);
                    for (int k=0; k<mStride; ++k)
                    {
                        srcLines[k] = input->vGetAddr(mStride*i+k);
                        srcDiffLines[k] = input_diff->vGetAddr(mStride*i+k);
                    }
                    for (int j=0; j<w; ++j)
                    {
                        ALFLOAT maxNumber = srcLines[0][0];
                        int maxX = 0;
                        int maxY = 0;
                        for (int x=0; x<mStride; ++x)
                        {
                            for (int y=0; y<mStride; ++y)
                            {
                                auto s = srcLines[x][y+mStride*j];
                                if (maxNumber < s)
                                {
                                    maxNumber = s;
                                    maxX = x;
                                    maxY = y;
                                }
                            }
                        }
                        srcDiffLines[maxX][maxY+mStride*j] = dst[j];
                    }
                }
            }
        }
    }
    static auto gCreateFunction = [](const LayerParameters& p) {
        return new MaxPoolLayer(p.get("stride"), p.mMatrixInfo.iWidth, p.mMatrixInfo.iHeight, p.mMatrixInfo.iDepth);
    };

    static LayerFactoryRegister __reg(gCreateFunction, "MaxPool");

}
<|endoftext|>


<commit_before>//
// Created by Jan de Visser on 2021-10-07.
//

#include <gtest/gtest.h>
#include <lexer/Tokenizer.h>
#include <lexer/test/LexerTest.h>

namespace Obelix {

class KeywordTest : public ::LexerTest {
protected:
    unsigned int prepareWithBig()
    {
        initialize();
        add_scanner<Obelix::KeywordScanner>(Token(200, "Big"));
        return 200;
    }

    void prepareWithBigBad(unsigned int* big, unsigned int* bad)
    {
        initialize();
        add_scanner<Obelix::KeywordScanner>(Token(200, "Big"), Token(201, "Bad"));
        *big = 200;
        *bad = 201;
    }

    unsigned int prepareWithAbc()
    {
        return 205;
    }

    void tokenizeBig(const char* s, int total_count, int big_count)
    {
        auto code = prepareWithBig();
        tokenize(s);
        EXPECT_EQ(tokens.size(), total_count);
        for (auto& token : tokens) {
            debug(lexer, "{}", token.to_string());
        }
        EXPECT_EQ(tokens_by_code[(Obelix::TokenCode)code].size(), big_count);
    }

    void tokenizeBigBad(const char* s, int total_count, int big_count, int bad_count)
    {
        initialize();
        unsigned int big, bad;
        prepareWithBigBad(&big, &bad);
        tokenize(s);
        debug(lexer, "{}", tokens[0].to_string());
        EXPECT_EQ(tokens.size(), total_count);
        EXPECT_EQ(tokens_by_code[(Obelix::TokenCode)big].size(), big_count);
        EXPECT_EQ(tokens_by_code[(Obelix::TokenCode)bad].size(), bad_count);
    }

    bool debugOn() override {
        return false;
    }

};

TEST_F(KeywordTest, Keyword)
{
    tokenizeBig("Big", 2, 1);
}

TEST_F(KeywordTest, KeywordSpace)
{
    tokenizeBig("Big ", 3, 1);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Whitespace].size(), 1);
}

TEST_F(KeywordTest, KeywordIsPrefix)
{
    tokenizeBig("Bigger", 3, 1);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Identifier].size(), 1);
}

TEST_F(KeywordTest, KeywordAndIdentifiers)
{
    tokenizeBig("Hello Big World", 6, 1);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Identifier].size(), 2);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Whitespace].size(), 2);
}

TEST_F(KeywordTest, TwoKeywords)
{
    tokenizeBig("Hello Big Big Beautiful World", 10, 2);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Identifier].size(), 3);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Whitespace].size(), 4);
}

TEST_F(KeywordTest, keyword_two_keywords_separated)
{
    tokenizeBig("Hello Big Beautiful Big World", 10, 2);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Identifier].size(), 3);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Whitespace].size(), 4);
}

TEST_F(KeywordTest, keyword_big_bad_big)
{
    tokenizeBigBad("Hello Big World", 6, 1, 0);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Identifier].size(), 2);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Whitespace].size(), 2);
}

TEST_F(KeywordTest, keyword_big_bad_bad)
{
    tokenizeBigBad("Hello Bad World", 6, 0, 1);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Identifier].size(), 2);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Whitespace].size(), 2);
}

TEST_F(KeywordTest, keyword_big_bad_big_bad)
{
    tokenizeBigBad("Hello Big Bad World", 8, 1, 1);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Identifier].size(), 2);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Whitespace].size(), 3);
}

TEST_F(KeywordTest, keyword_big_bad_bad_big)
{
    tokenizeBigBad("Hello Bad Big World", 8, 1, 1);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Identifier].size(), 2);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Whitespace].size(), 3);
}

TEST_F(KeywordTest, keyword_abc)
{
    initialize();
    add_scanner<Obelix::KeywordScanner>(
        Token(TokenCode::Keyword0, "abb"),
        Token(TokenCode::Keyword1, "aca"),
        Token(TokenCode::Keyword2, "aba"),
        Token(TokenCode::Keyword3, "aaa"),
        Token(TokenCode::Keyword4, "aab"),
        Token(TokenCode::Keyword5, "abc"),
        Token(TokenCode::Keyword6, "aac"),
        Token(TokenCode::Keyword7, "acc"),
        Token(TokenCode::Keyword8, "acb"));

    tokenize("yyz abc ams");
    EXPECT_EQ(tokens.size(), 6);
    EXPECT_EQ(tokens_by_code[TokenCode::Keyword5].size(), 1);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Identifier].size(), 2);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Whitespace].size(), 2);
}

TEST_F(KeywordTest, keyword_for_format)
{
    //    Logger::get_logger().enable("lexer");
    initialize();
    add_scanner<Obelix::KeywordScanner>(
        Token(TokenCode::Keyword0, "for"),
        Token(TokenCode::Keyword1, "format"),
        Token(TokenCode::Keyword2, "font"),
        TokenCode::GreaterEqualThan,
        Token(TokenCode::Keyword4, "aab"),
        Token(TokenCode::Keyword5, "abc"),
        Token(TokenCode::Keyword6, "aac"),
        Token(TokenCode::Keyword7, "acc"),
        Token(TokenCode::Keyword8, "acb"));

    tokenize("xxx for format font fo formatting >=xxx");
    EXPECT_EQ(tokens.size(), 15);
    EXPECT_EQ(tokens_by_code[TokenCode::Keyword0].size(), 1);
    EXPECT_EQ(tokens_by_code[TokenCode::Keyword1].size(), 1);
    EXPECT_EQ(tokens_by_code[TokenCode::Keyword2].size(), 1);
    EXPECT_EQ(tokens_by_code[TokenCode::GreaterEqualThan].size(), 1);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Identifier].size(), 4);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Whitespace].size(), 6);
}

}<commit_msg>Fix test<commit_after>//
// Created by Jan de Visser on 2021-10-07.
//

#include <gtest/gtest.h>
#include <lexer/Tokenizer.h>
#include <lexer/test/LexerTest.h>

namespace Obelix {

class KeywordTest : public ::LexerTest {
protected:
    unsigned int prepareWithBig()
    {
        initialize();
        add_scanner<Obelix::KeywordScanner>(Token(200, "Big"));
        return 200;
    }

    void prepareWithBigBad(unsigned int* big, unsigned int* bad)
    {
        initialize();
        add_scanner<Obelix::KeywordScanner>(Token(200, "Big"), Token(201, "Bad"));
        *big = 200;
        *bad = 201;
    }

    unsigned int prepareWithAbc()
    {
        return 205;
    }

    void tokenizeBig(const char* s, int total_count, int big_count)
    {
        auto code = prepareWithBig();
        tokenize(s);
        EXPECT_EQ(tokens.size(), total_count);
        for (auto& token : tokens) {
            debug(lexer, "{}", token.to_string());
        }
        EXPECT_EQ(tokens_by_code[(Obelix::TokenCode)code].size(), big_count);
    }

    void tokenizeBigBad(const char* s, int total_count, int big_count, int bad_count)
    {
        initialize();
        unsigned int big, bad;
        prepareWithBigBad(&big, &bad);
        tokenize(s);
        debug(lexer, "{}", tokens[0].to_string());
        EXPECT_EQ(tokens.size(), total_count);
        EXPECT_EQ(tokens_by_code[(Obelix::TokenCode)big].size(), big_count);
        EXPECT_EQ(tokens_by_code[(Obelix::TokenCode)bad].size(), bad_count);
    }

    bool debugOn() override {
        return false;
    }

};

TEST_F(KeywordTest, Keyword)
{
    tokenizeBig("Big", 2, 1);
}

TEST_F(KeywordTest, KeywordSpace)
{
    tokenizeBig("Big ", 3, 1);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Whitespace].size(), 1);
}

TEST_F(KeywordTest, KeywordIsPrefix)
{
    tokenizeBig("Bigger", 2, 0);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Identifier].size(), 1);
}

TEST_F(KeywordTest, KeywordAndIdentifiers)
{
    tokenizeBig("Hello Big World", 6, 1);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Identifier].size(), 2);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Whitespace].size(), 2);
}

TEST_F(KeywordTest, TwoKeywords)
{
    tokenizeBig("Hello Big Big Beautiful World", 10, 2);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Identifier].size(), 3);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Whitespace].size(), 4);
}

TEST_F(KeywordTest, keyword_two_keywords_separated)
{
    tokenizeBig("Hello Big Beautiful Big World", 10, 2);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Identifier].size(), 3);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Whitespace].size(), 4);
}

TEST_F(KeywordTest, keyword_big_bad_big)
{
    tokenizeBigBad("Hello Big World", 6, 1, 0);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Identifier].size(), 2);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Whitespace].size(), 2);
}

TEST_F(KeywordTest, keyword_big_bad_bad)
{
    tokenizeBigBad("Hello Bad World", 6, 0, 1);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Identifier].size(), 2);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Whitespace].size(), 2);
}

TEST_F(KeywordTest, keyword_big_bad_big_bad)
{
    tokenizeBigBad("Hello Big Bad World", 8, 1, 1);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Identifier].size(), 2);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Whitespace].size(), 3);
}

TEST_F(KeywordTest, keyword_big_bad_bad_big)
{
    tokenizeBigBad("Hello Bad Big World", 8, 1, 1);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Identifier].size(), 2);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Whitespace].size(), 3);
}

TEST_F(KeywordTest, keyword_abc)
{
    initialize();
    add_scanner<Obelix::KeywordScanner>(
        Token(TokenCode::Keyword0, "abb"),
        Token(TokenCode::Keyword1, "aca"),
        Token(TokenCode::Keyword2, "aba"),
        Token(TokenCode::Keyword3, "aaa"),
        Token(TokenCode::Keyword4, "aab"),
        Token(TokenCode::Keyword5, "abc"),
        Token(TokenCode::Keyword6, "aac"),
        Token(TokenCode::Keyword7, "acc"),
        Token(TokenCode::Keyword8, "acb"));

    tokenize("yyz abc ams");
    EXPECT_EQ(tokens.size(), 6);
    EXPECT_EQ(tokens_by_code[TokenCode::Keyword5].size(), 1);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Identifier].size(), 2);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Whitespace].size(), 2);
}

TEST_F(KeywordTest, keyword_for_format)
{
    //    Logger::get_logger().enable("lexer");
    initialize();
    add_scanner<Obelix::KeywordScanner>(
        Token(TokenCode::Keyword0, "for"),
        Token(TokenCode::Keyword1, "format"),
        Token(TokenCode::Keyword2, "font"),
        TokenCode::GreaterEqualThan,
        Token(TokenCode::Keyword4, "aab"),
        Token(TokenCode::Keyword5, "abc"),
        Token(TokenCode::Keyword6, "aac"),
        Token(TokenCode::Keyword7, "acc"),
        Token(TokenCode::Keyword8, "acb"));

    tokenize("xxx for format font fo formatting >=xxx");
    EXPECT_EQ(tokens.size(), 15);
    EXPECT_EQ(tokens_by_code[TokenCode::Keyword0].size(), 1);
    EXPECT_EQ(tokens_by_code[TokenCode::Keyword1].size(), 1);
    EXPECT_EQ(tokens_by_code[TokenCode::Keyword2].size(), 1);
    EXPECT_EQ(tokens_by_code[TokenCode::GreaterEqualThan].size(), 1);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Identifier].size(), 4);
    EXPECT_EQ(tokens_by_code[Obelix::TokenCode::Whitespace].size(), 6);
}

}<|endoftext|>


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

#include "fastrtps/subscriber/Subscriber.h"

#include "rmw/error_handling.h"
#include "rmw/rmw.h"

#include "rmw_fastrtps_cpp/custom_client_info.hpp"
#include "rmw_fastrtps_cpp/custom_service_info.hpp"
#include "rmw_fastrtps_cpp/custom_subscriber_info.hpp"
#include "types/custom_wait_set_info.hpp"
#include "types/guard_condition.hpp"

// helper function for wait
bool
check_wait_set_for_data(
  const rmw_subscriptions_t * subscriptions,
  const rmw_guard_conditions_t * guard_conditions,
  const rmw_services_t * services,
  const rmw_clients_t * clients)
{
  if (subscriptions) {
    for (size_t i = 0; i < subscriptions->subscriber_count; ++i) {
      void * data = subscriptions->subscribers[i];
      auto custom_subscriber_info = static_cast<CustomSubscriberInfo *>(data);
      // Short circuiting out of this function is possible
      if (custom_subscriber_info && custom_subscriber_info->listener_->hasData()) {
        return true;
      }
    }
  }

  if (clients) {
    for (size_t i = 0; i < clients->client_count; ++i) {
      void * data = clients->clients[i];
      CustomClientInfo * custom_client_info = static_cast<CustomClientInfo *>(data);
      if (custom_client_info && custom_client_info->listener_->hasData()) {
        return true;
      }
    }
  }

  if (services) {
    for (size_t i = 0; i < services->service_count; ++i) {
      void * data = services->services[i];
      CustomServiceInfo * custom_service_info = static_cast<CustomServiceInfo *>(data);
      if (custom_service_info && custom_service_info->listener_->hasData()) {
        return true;
      }
    }
  }

  if (guard_conditions) {
    for (size_t i = 0; i < guard_conditions->guard_condition_count; ++i) {
      void * data = guard_conditions->guard_conditions[i];
      auto guard_condition = static_cast<GuardCondition *>(data);
      if (guard_condition && guard_condition->hasTriggered()) {
        return true;
      }
    }
  }
  return false;
}

extern "C"
{
rmw_ret_t
rmw_wait(
  rmw_subscriptions_t * subscriptions,
  rmw_guard_conditions_t * guard_conditions,
  rmw_services_t * services,
  rmw_clients_t * clients,
  rmw_wait_set_t * wait_set,
  const rmw_time_t * wait_timeout)
{
  if (!wait_set) {
    RMW_SET_ERROR_MSG("wait set handle is null");
    return RMW_RET_ERROR;
  }
  CustomWaitsetInfo * wait_set_info = static_cast<CustomWaitsetInfo *>(wait_set->data);
  if (!wait_set_info) {
    RMW_SET_ERROR_MSG("Waitset info struct is null");
    return RMW_RET_ERROR;
  }
  std::mutex * conditionMutex = &wait_set_info->condition_mutex;
  std::condition_variable * conditionVariable = &wait_set_info->condition;
  if (!conditionMutex) {
    RMW_SET_ERROR_MSG("Mutex for wait set was null");
    return RMW_RET_ERROR;
  }
  if (!conditionVariable) {
    RMW_SET_ERROR_MSG("Condition variable for wait set was null");
    return RMW_RET_ERROR;
  }

  if (subscriptions) {
    for (size_t i = 0; i < subscriptions->subscriber_count; ++i) {
      void * data = subscriptions->subscribers[i];
      auto custom_subscriber_info = static_cast<CustomSubscriberInfo *>(data);
      custom_subscriber_info->listener_->attachCondition(conditionMutex, conditionVariable);
    }
  }

  if (clients) {
    for (size_t i = 0; i < clients->client_count; ++i) {
      void * data = clients->clients[i];
      CustomClientInfo * custom_client_info = static_cast<CustomClientInfo *>(data);
      custom_client_info->listener_->attachCondition(conditionMutex, conditionVariable);
    }
  }

  if (services) {
    for (size_t i = 0; i < services->service_count; ++i) {
      void * data = services->services[i];
      auto custom_service_info = static_cast<CustomServiceInfo *>(data);
      custom_service_info->listener_->attachCondition(conditionMutex, conditionVariable);
    }
  }

  if (guard_conditions) {
    for (size_t i = 0; i < guard_conditions->guard_condition_count; ++i) {
      void * data = guard_conditions->guard_conditions[i];
      auto guard_condition = static_cast<GuardCondition *>(data);
      guard_condition->attachCondition(conditionMutex, conditionVariable);
    }
  }

  // This mutex prevents any of the listeners
  // to change the internal state and notify the condition
  // between the call to hasData() / hasTriggered() and wait()
  // otherwise the decision to wait might be incorrect
  std::unique_lock<std::mutex> lock(*conditionMutex);

  // First check variables.
  // If wait_timeout is null, wait indefinitely (so we have to wait)
  // If wait_timeout is not null and either of its fields are nonzero, we have to wait
  bool hasToWait = (wait_timeout && (wait_timeout->sec > 0 || wait_timeout->nsec > 0)) ||
    !wait_timeout;

  if (subscriptions) {
    for (size_t i = 0; hasToWait && i < subscriptions->subscriber_count; ++i) {
      void * data = subscriptions->subscribers[i];
      auto custom_subscriber_info = static_cast<CustomSubscriberInfo *>(data);
      if (custom_subscriber_info->listener_->hasData()) {
        hasToWait = false;
      }
    }
  }

  if (clients) {
    for (size_t i = 0; hasToWait && i < clients->client_count; ++i) {
      void * data = clients->clients[i];
      CustomClientInfo * custom_client_info = static_cast<CustomClientInfo *>(data);
      if (custom_client_info->listener_->hasData()) {
        hasToWait = false;
      }
    }
  }

  if (services) {
    for (size_t i = 0; hasToWait && i < services->service_count; ++i) {
      void * data = services->services[i];
      auto custom_service_info = static_cast<CustomServiceInfo *>(data);
      if (custom_service_info->listener_->hasData()) {
        hasToWait = false;
      }
    }
  }

  if (guard_conditions) {
    for (size_t i = 0; hasToWait && i < guard_conditions->guard_condition_count; ++i) {
      void * data = guard_conditions->guard_conditions[i];
      auto guard_condition = static_cast<GuardCondition *>(data);
      if (guard_condition->hasTriggered()) {
        hasToWait = false;
      }
    }
  }

  bool timeout = false;

  if (hasToWait) {
    if (!wait_timeout) {
      conditionVariable->wait(lock);
    } else {
      auto predicate = [subscriptions, guard_conditions, services, clients]() {
          return check_wait_set_for_data(subscriptions, guard_conditions, services, clients);
        };
      auto n = std::chrono::duration_cast<std::chrono::nanoseconds>(
        std::chrono::seconds(wait_timeout->sec));
      n += std::chrono::nanoseconds(wait_timeout->nsec);
      timeout = !conditionVariable->wait_for(lock, n, predicate);
    }
  }

  // Unlock the condition variable mutex to prevent deadlocks that can occur if
  // a listener triggers while the condition variable is being detached.
  // Listeners will no longer be prevented from changing their internal state,
  // but that should not cause issues (if a listener has data / has triggered
  // after we check, it will be caught on the next call to this function).
  lock.unlock();

  // Even if this was a non-blocking wait, signal a timeout if there's no data.
  // This makes the return behavior consistent with rcl expectations for zero timeout value.
  // Do this before detaching the listeners because the data gets cleared for guard conditions.
  bool hasData = check_wait_set_for_data(subscriptions, guard_conditions, services, clients);
  if (!hasData && wait_timeout && wait_timeout->sec == 0 && wait_timeout->nsec == 0) {
    timeout = true;
  }

  if (subscriptions) {
    for (size_t i = 0; i < subscriptions->subscriber_count; ++i) {
      void * data = subscriptions->subscribers[i];
      auto custom_subscriber_info = static_cast<CustomSubscriberInfo *>(data);
      custom_subscriber_info->listener_->detachCondition();
      if (!custom_subscriber_info->listener_->hasData()) {
        subscriptions->subscribers[i] = 0;
      }
    }
  }

  if (clients) {
    for (size_t i = 0; i < clients->client_count; ++i) {
      void * data = clients->clients[i];
      CustomClientInfo * custom_client_info = static_cast<CustomClientInfo *>(data);
      custom_client_info->listener_->detachCondition();
      if (!custom_client_info->listener_->hasData()) {
        clients->clients[i] = 0;
      }
    }
  }

  if (services) {
    for (size_t i = 0; i < services->service_count; ++i) {
      void * data = services->services[i];
      auto custom_service_info = static_cast<CustomServiceInfo *>(data);
      custom_service_info->listener_->detachCondition();
      if (!custom_service_info->listener_->hasData()) {
        services->services[i] = 0;
      }
    }
  }

  if (guard_conditions) {
    for (size_t i = 0; i < guard_conditions->guard_condition_count; ++i) {
      void * data = guard_conditions->guard_conditions[i];
      auto guard_condition = static_cast<GuardCondition *>(data);
      guard_condition->detachCondition();
      if (!guard_condition->getHasTriggered()) {
        guard_conditions->guard_conditions[i] = 0;
      }
    }
  }

  return timeout ? RMW_RET_TIMEOUT : RMW_RET_OK;
}
}  // extern "C"
<commit_msg>use existing check_wait_set_for_data to avoid duplicated code (#185)<commit_after>// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "fastrtps/subscriber/Subscriber.h"

#include "rmw/error_handling.h"
#include "rmw/rmw.h"

#include "rmw_fastrtps_cpp/custom_client_info.hpp"
#include "rmw_fastrtps_cpp/custom_service_info.hpp"
#include "rmw_fastrtps_cpp/custom_subscriber_info.hpp"
#include "types/custom_wait_set_info.hpp"
#include "types/guard_condition.hpp"

// helper function for wait
bool
check_wait_set_for_data(
  const rmw_subscriptions_t * subscriptions,
  const rmw_guard_conditions_t * guard_conditions,
  const rmw_services_t * services,
  const rmw_clients_t * clients)
{
  if (subscriptions) {
    for (size_t i = 0; i < subscriptions->subscriber_count; ++i) {
      void * data = subscriptions->subscribers[i];
      auto custom_subscriber_info = static_cast<CustomSubscriberInfo *>(data);
      // Short circuiting out of this function is possible
      if (custom_subscriber_info && custom_subscriber_info->listener_->hasData()) {
        return true;
      }
    }
  }

  if (clients) {
    for (size_t i = 0; i < clients->client_count; ++i) {
      void * data = clients->clients[i];
      CustomClientInfo * custom_client_info = static_cast<CustomClientInfo *>(data);
      if (custom_client_info && custom_client_info->listener_->hasData()) {
        return true;
      }
    }
  }

  if (services) {
    for (size_t i = 0; i < services->service_count; ++i) {
      void * data = services->services[i];
      CustomServiceInfo * custom_service_info = static_cast<CustomServiceInfo *>(data);
      if (custom_service_info && custom_service_info->listener_->hasData()) {
        return true;
      }
    }
  }

  if (guard_conditions) {
    for (size_t i = 0; i < guard_conditions->guard_condition_count; ++i) {
      void * data = guard_conditions->guard_conditions[i];
      auto guard_condition = static_cast<GuardCondition *>(data);
      if (guard_condition && guard_condition->hasTriggered()) {
        return true;
      }
    }
  }
  return false;
}

extern "C"
{
rmw_ret_t
rmw_wait(
  rmw_subscriptions_t * subscriptions,
  rmw_guard_conditions_t * guard_conditions,
  rmw_services_t * services,
  rmw_clients_t * clients,
  rmw_wait_set_t * wait_set,
  const rmw_time_t * wait_timeout)
{
  if (!wait_set) {
    RMW_SET_ERROR_MSG("wait set handle is null");
    return RMW_RET_ERROR;
  }
  CustomWaitsetInfo * wait_set_info = static_cast<CustomWaitsetInfo *>(wait_set->data);
  if (!wait_set_info) {
    RMW_SET_ERROR_MSG("Waitset info struct is null");
    return RMW_RET_ERROR;
  }
  std::mutex * conditionMutex = &wait_set_info->condition_mutex;
  std::condition_variable * conditionVariable = &wait_set_info->condition;
  if (!conditionMutex) {
    RMW_SET_ERROR_MSG("Mutex for wait set was null");
    return RMW_RET_ERROR;
  }
  if (!conditionVariable) {
    RMW_SET_ERROR_MSG("Condition variable for wait set was null");
    return RMW_RET_ERROR;
  }

  if (subscriptions) {
    for (size_t i = 0; i < subscriptions->subscriber_count; ++i) {
      void * data = subscriptions->subscribers[i];
      auto custom_subscriber_info = static_cast<CustomSubscriberInfo *>(data);
      custom_subscriber_info->listener_->attachCondition(conditionMutex, conditionVariable);
    }
  }

  if (clients) {
    for (size_t i = 0; i < clients->client_count; ++i) {
      void * data = clients->clients[i];
      CustomClientInfo * custom_client_info = static_cast<CustomClientInfo *>(data);
      custom_client_info->listener_->attachCondition(conditionMutex, conditionVariable);
    }
  }

  if (services) {
    for (size_t i = 0; i < services->service_count; ++i) {
      void * data = services->services[i];
      auto custom_service_info = static_cast<CustomServiceInfo *>(data);
      custom_service_info->listener_->attachCondition(conditionMutex, conditionVariable);
    }
  }

  if (guard_conditions) {
    for (size_t i = 0; i < guard_conditions->guard_condition_count; ++i) {
      void * data = guard_conditions->guard_conditions[i];
      auto guard_condition = static_cast<GuardCondition *>(data);
      guard_condition->attachCondition(conditionMutex, conditionVariable);
    }
  }

  // This mutex prevents any of the listeners
  // to change the internal state and notify the condition
  // between the call to hasData() / hasTriggered() and wait()
  // otherwise the decision to wait might be incorrect
  std::unique_lock<std::mutex> lock(*conditionMutex);

  // First check variables.
  // If wait_timeout is null, wait indefinitely (so we have to wait)
  // If wait_timeout is not null and either of its fields are nonzero, we have to wait
  bool hasToWait = (wait_timeout && (wait_timeout->sec > 0 || wait_timeout->nsec > 0)) ||
    !wait_timeout;
  hasToWait &= !check_wait_set_for_data(subscriptions, guard_conditions, services, clients);

  bool timeout = false;

  if (hasToWait) {
    if (!wait_timeout) {
      conditionVariable->wait(lock);
    } else {
      auto predicate = [subscriptions, guard_conditions, services, clients]() {
          return check_wait_set_for_data(subscriptions, guard_conditions, services, clients);
        };
      auto n = std::chrono::duration_cast<std::chrono::nanoseconds>(
        std::chrono::seconds(wait_timeout->sec));
      n += std::chrono::nanoseconds(wait_timeout->nsec);
      timeout = !conditionVariable->wait_for(lock, n, predicate);
    }
  }

  // Unlock the condition variable mutex to prevent deadlocks that can occur if
  // a listener triggers while the condition variable is being detached.
  // Listeners will no longer be prevented from changing their internal state,
  // but that should not cause issues (if a listener has data / has triggered
  // after we check, it will be caught on the next call to this function).
  lock.unlock();

  // Even if this was a non-blocking wait, signal a timeout if there's no data.
  // This makes the return behavior consistent with rcl expectations for zero timeout value.
  // Do this before detaching the listeners because the data gets cleared for guard conditions.
  bool hasData = check_wait_set_for_data(subscriptions, guard_conditions, services, clients);
  if (!hasData && wait_timeout && wait_timeout->sec == 0 && wait_timeout->nsec == 0) {
    timeout = true;
  }

  if (subscriptions) {
    for (size_t i = 0; i < subscriptions->subscriber_count; ++i) {
      void * data = subscriptions->subscribers[i];
      auto custom_subscriber_info = static_cast<CustomSubscriberInfo *>(data);
      custom_subscriber_info->listener_->detachCondition();
      if (!custom_subscriber_info->listener_->hasData()) {
        subscriptions->subscribers[i] = 0;
      }
    }
  }

  if (clients) {
    for (size_t i = 0; i < clients->client_count; ++i) {
      void * data = clients->clients[i];
      CustomClientInfo * custom_client_info = static_cast<CustomClientInfo *>(data);
      custom_client_info->listener_->detachCondition();
      if (!custom_client_info->listener_->hasData()) {
        clients->clients[i] = 0;
      }
    }
  }

  if (services) {
    for (size_t i = 0; i < services->service_count; ++i) {
      void * data = services->services[i];
      auto custom_service_info = static_cast<CustomServiceInfo *>(data);
      custom_service_info->listener_->detachCondition();
      if (!custom_service_info->listener_->hasData()) {
        services->services[i] = 0;
      }
    }
  }

  if (guard_conditions) {
    for (size_t i = 0; i < guard_conditions->guard_condition_count; ++i) {
      void * data = guard_conditions->guard_conditions[i];
      auto guard_condition = static_cast<GuardCondition *>(data);
      guard_condition->detachCondition();
      if (!guard_condition->getHasTriggered()) {
        guard_conditions->guard_conditions[i] = 0;
      }
    }
  }

  return timeout ? RMW_RET_TIMEOUT : RMW_RET_OK;
}
}  // extern "C"
<|endoftext|>


<commit_before>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright 2011-2018 Dominik Charousset                                     *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#ifndef CAF_BROADCAST_SCATTERER_HPP
#define CAF_BROADCAST_SCATTERER_HPP

#include "caf/buffered_scatterer.hpp"
#include "caf/outbound_path.hpp"

#include "caf/detail/algorithms.hpp"
#include "caf/detail/path_state.hpp"
#include "caf/detail/select_all.hpp"

namespace caf {

template <class T, class Filter = unit_t, class Select = detail::select_all>
class broadcast_scatterer : public buffered_scatterer<T> {
public:
  // -- member types -----------------------------------------------------------

  /// Base type.
  using super = buffered_scatterer<T>;

  /// Type of `paths_`.
  using typename super::map_type;

  /// Enables or disables output per path.
  using filter_type = Filter;

  /// Function object for evaluating filters.
  using select_type = Select;

  /// Container for caching `T`s per path with active filter.
  using path_state = detail::path_state<Filter, T>;

  /// Maps slot IDs to caches.
  using state_map_type = detail::unordered_flat_map<stream_slot, path_state>;

  // -- constructors, destructors, and assignment operators --------------------

  broadcast_scatterer(scheduled_actor* selfptr) : super(selfptr) {
    // nop
  }

  // -- properties -------------------------------------------------------------

  /// Sets the filter for `slot` to `filter`. Inserts a new element if `slot`
  /// is a new path.
  void set_filter(stream_slot slot, filter_type filter) {
    CAF_LOG_TRACE(CAF_ARG(slot) << CAF_ARG(filter));
    state_map_[slot].filter = std::move(filter);
  }

  /// Returns the filter for `slot`. Inserts a new element if `slot` is a new
  /// path.
  filter_type& filter(stream_slot slot) {
    CAF_LOG_TRACE(CAF_ARG(slot));
    return state_map_[slot].filter;
  }

  /// Returns the broadcast states for all paths.
  state_map_type& states() {
    return state_map_;
  }

  /// Returns the broadcast states for all paths.
  const state_map_type& states() const {
    return state_map_;
  }

  /// Returns the selector for filtering outgoing data.
  select_type& selector() {
    return select_;
  }

  /// Returns the selector for filtering outgoing data.
  const select_type& selector() const {
    return select_;
  }

  // -- overridden functions ---------------------------------------------------

  typename super::path_ptr add_path(stream_slots slots,
                                    strong_actor_ptr target) override {
    CAF_LOG_TRACE(CAF_ARG(slots) << CAF_ARG(target));
    // Make sure we have state for the slot.
    state_map_.emplace(slots.sender, path_state{});
    // Append to `paths_`.
    auto index = this->paths_.size();
    auto result = super::add_path(slots, target);
    if (result == nullptr)
      return nullptr;
    // Make sure state_map_ and paths_ are always equally sorted, otherwise
    // we'll run into UB when calling `zip_foreach`.
    CAF_ASSERT(index == this->paths_.size() - 1);
    CAF_ASSERT(result->slots == slots);
    CAF_ASSERT(this->paths_.container().back().first == slots.sender);
    CAF_ASSERT(this->paths_.container().back().second.get() == result);
    auto& ys = state_map_.container();
    if (ys[index].first != slots.sender) {
      auto i = state_map_.find(slots.sender);
      CAF_ASSERT(i != ys.end());
      CAF_ASSERT(std::distance(ys.begin(), i) > static_cast<ptrdiff_t>(index));
      using std::swap;
      swap(ys[index], *i);
    }
    return result;
  }

  void emit_batches() override {
    CAF_LOG_TRACE(CAF_ARG2("buffered", this->buffered())
                  << CAF_ARG2("paths", this->paths_.size()));
    emit_batches_impl(false);
  }

  void force_emit_batches() override {
    CAF_LOG_TRACE(CAF_ARG2("buffered", this->buffered())
                  << CAF_ARG2("paths", this->paths_.size()));
    emit_batches_impl(true);
  }

protected:
  void about_to_erase(outbound_path* ptr, bool silent,
                      error* reason) override {
    CAF_LOG_DEBUG("remove cache:" << CAF_ARG2("slot", ptr->slots.sender));
    state_map_.erase(ptr->slots.sender);
    super::about_to_erase(ptr, silent, reason);
  }

private:
  void emit_batches_impl(bool force_underfull) {
    CAF_ASSERT(this->paths_.size() <= state_map_.size());
    if (this->paths_.empty())
      return;
    // Calculate the chunk size, i.e., how many more items we can put to our
    // caches at the most.
    struct get_credit {
      inline size_t operator()(typename map_type::value_type& x) {
        return static_cast<size_t>(x.second->open_credit);
      }
    };
    struct get_cache_size {
      inline size_t operator()(typename state_map_type::value_type& x) {
        return x.second.buf.size();
      }
    };
    auto f = [](size_t x, size_t credit, size_t cache_size) {
      auto y = credit > cache_size ? credit - cache_size : 0u;
      return x < y ? x : y;
    };
    auto chunk_size = detail::zip_fold(
      f, std::numeric_limits<size_t>::max(),
      detail::make_container_view<get_credit>(this->paths_.container()),
      detail::make_container_view<get_cache_size>(state_map_.container()));
    auto chunk = this->get_chunk(chunk_size);
    if (chunk.empty()) {
      auto g = [&](typename map_type::value_type& x,
                   typename state_map_type::value_type& y) {
        x.second->emit_batches(this->self_, y.second.buf, force_underfull);
      };
      detail::zip_foreach(g, this->paths_.container(), state_map_.container());
    } else {
      auto g = [&](typename map_type::value_type& x,
                   typename state_map_type::value_type& y) {
        // TODO: replace with `if constexpr` when switching to C++17
        auto& st = y.second;
        if (std::is_same<select_type, detail::select_all>::value) {
          st.buf.insert(st.buf.end(), chunk.begin(), chunk.end());
        } else {
          for (auto& piece : chunk)
            if (select_(st.filter, piece))
              st.buf.emplace_back(piece);
        }
        x.second->emit_batches(this->self_, st.buf, force_underfull);
      };
      detail::zip_foreach(g, this->paths_.container(), state_map_.container());
    }
  }

  state_map_type state_map_;
  select_type select_;
};

} // namespace caf

#endif // CAF_BROADCAST_SCATTERER_HPP
<commit_msg>Add convenience algorithms for filters<commit_after>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright 2011-2018 Dominik Charousset                                     *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#ifndef CAF_BROADCAST_SCATTERER_HPP
#define CAF_BROADCAST_SCATTERER_HPP

#include <algorithm>

#include "caf/buffered_scatterer.hpp"
#include "caf/outbound_path.hpp"

#include "caf/detail/algorithms.hpp"
#include "caf/detail/path_state.hpp"
#include "caf/detail/select_all.hpp"

namespace caf {

template <class T, class Filter = unit_t, class Select = detail::select_all>
class broadcast_scatterer : public buffered_scatterer<T> {
public:
  // -- member types -----------------------------------------------------------

  /// Base type.
  using super = buffered_scatterer<T>;

  /// Type of `paths_`.
  using typename super::map_type;

  /// Enables or disables output per path.
  using filter_type = Filter;

  /// Function object for evaluating filters.
  using select_type = Select;

  /// Container for caching `T`s per path with active filter.
  using path_state = detail::path_state<Filter, T>;

  /// Maps slot IDs to caches.
  using state_map_type = detail::unordered_flat_map<stream_slot, path_state>;

  // -- constructors, destructors, and assignment operators --------------------

  broadcast_scatterer(scheduled_actor* selfptr) : super(selfptr) {
    // nop
  }

  // -- properties -------------------------------------------------------------

  /// Sets the filter for `slot` to `filter`. Inserts a new element if `slot`
  /// is a new path.
  void set_filter(stream_slot slot, filter_type filter) {
    CAF_LOG_TRACE(CAF_ARG(slot) << CAF_ARG(filter));
    state_map_[slot].filter = std::move(filter);
  }

  /// Returns the filter for `slot`. Inserts a new element if `slot` is a new
  /// path.
  filter_type& filter(stream_slot slot) {
    CAF_LOG_TRACE(CAF_ARG(slot));
    return state_map_[slot].filter;
  }

  /// Returns whether all filters satisfy the predicate as if applying
  /// `std::all_of`.
  template <class UnaryPredicate>
  bool all_filters(UnaryPredicate predicate) {
    return std::all_of(state_map_.begin(), state_map_.end(),
                       [&](const typename state_map_type::value_type& kvp) {
                         return predicate(kvp.second);
                       });
  }

  /// Returns whether any filter satisfies the predicate as if applying
  /// `std::any_of`.
  template <class UnaryPredicate>
  bool any_filter(UnaryPredicate predicate) {
    return std::any_of(state_map_.begin(), state_map_.end(),
                       [&](const typename state_map_type::value_type& kvp) {
                         return predicate(kvp.second);
                       });
  }

  /// Returns whether no filter satisfies the predicate as if applying
  /// `std::none_of`.
  template <class UnaryPredicate>
  bool no_filter(UnaryPredicate predicate) {
    return std::none_of(state_map_.begin(), state_map_.end(),
                        [&](const typename state_map_type::value_type& kvp) {
                          return predicate(kvp.second);
                        });
  }

  /// Returns the broadcast states for all paths.
  state_map_type& states() {
    return state_map_;
  }

  /// Returns the broadcast states for all paths.
  const state_map_type& states() const {
    return state_map_;
  }

  /// Returns the selector for filtering outgoing data.
  select_type& selector() {
    return select_;
  }

  /// Returns the selector for filtering outgoing data.
  const select_type& selector() const {
    return select_;
  }

  // -- overridden functions ---------------------------------------------------

  typename super::path_ptr add_path(stream_slots slots,
                                    strong_actor_ptr target) override {
    CAF_LOG_TRACE(CAF_ARG(slots) << CAF_ARG(target));
    // Make sure we have state for the slot.
    state_map_.emplace(slots.sender, path_state{});
    // Append to `paths_`.
    auto index = this->paths_.size();
    auto result = super::add_path(slots, target);
    if (result == nullptr)
      return nullptr;
    // Make sure state_map_ and paths_ are always equally sorted, otherwise
    // we'll run into UB when calling `zip_foreach`.
    CAF_ASSERT(index == this->paths_.size() - 1);
    CAF_ASSERT(result->slots == slots);
    CAF_ASSERT(this->paths_.container().back().first == slots.sender);
    CAF_ASSERT(this->paths_.container().back().second.get() == result);
    auto& ys = state_map_.container();
    if (ys[index].first != slots.sender) {
      auto i = state_map_.find(slots.sender);
      CAF_ASSERT(i != ys.end());
      CAF_ASSERT(std::distance(ys.begin(), i) > static_cast<ptrdiff_t>(index));
      using std::swap;
      swap(ys[index], *i);
    }
    return result;
  }

  void emit_batches() override {
    CAF_LOG_TRACE(CAF_ARG2("buffered", this->buffered())
                  << CAF_ARG2("paths", this->paths_.size()));
    emit_batches_impl(false);
  }

  void force_emit_batches() override {
    CAF_LOG_TRACE(CAF_ARG2("buffered", this->buffered())
                  << CAF_ARG2("paths", this->paths_.size()));
    emit_batches_impl(true);
  }

protected:
  void about_to_erase(outbound_path* ptr, bool silent,
                      error* reason) override {
    CAF_LOG_DEBUG("remove cache:" << CAF_ARG2("slot", ptr->slots.sender));
    state_map_.erase(ptr->slots.sender);
    super::about_to_erase(ptr, silent, reason);
  }

private:
  void emit_batches_impl(bool force_underfull) {
    CAF_ASSERT(this->paths_.size() <= state_map_.size());
    if (this->paths_.empty())
      return;
    // Calculate the chunk size, i.e., how many more items we can put to our
    // caches at the most.
    struct get_credit {
      inline size_t operator()(typename map_type::value_type& x) {
        return static_cast<size_t>(x.second->open_credit);
      }
    };
    struct get_cache_size {
      inline size_t operator()(typename state_map_type::value_type& x) {
        return x.second.buf.size();
      }
    };
    auto f = [](size_t x, size_t credit, size_t cache_size) {
      auto y = credit > cache_size ? credit - cache_size : 0u;
      return x < y ? x : y;
    };
    auto chunk_size = detail::zip_fold(
      f, std::numeric_limits<size_t>::max(),
      detail::make_container_view<get_credit>(this->paths_.container()),
      detail::make_container_view<get_cache_size>(state_map_.container()));
    auto chunk = this->get_chunk(chunk_size);
    if (chunk.empty()) {
      auto g = [&](typename map_type::value_type& x,
                   typename state_map_type::value_type& y) {
        x.second->emit_batches(this->self_, y.second.buf, force_underfull);
      };
      detail::zip_foreach(g, this->paths_.container(), state_map_.container());
    } else {
      auto g = [&](typename map_type::value_type& x,
                   typename state_map_type::value_type& y) {
        // TODO: replace with `if constexpr` when switching to C++17
        auto& st = y.second;
        if (std::is_same<select_type, detail::select_all>::value) {
          st.buf.insert(st.buf.end(), chunk.begin(), chunk.end());
        } else {
          for (auto& piece : chunk)
            if (select_(st.filter, piece))
              st.buf.emplace_back(piece);
        }
        x.second->emit_batches(this->self_, st.buf, force_underfull);
      };
      detail::zip_foreach(g, this->paths_.container(), state_map_.container());
    }
  }

  state_map_type state_map_;
  select_type select_;
};

} // namespace caf

#endif // CAF_BROADCAST_SCATTERER_HPP
<|endoftext|>


<commit_before>/*
* Copyright (C) 2008-2013 The Communi Project
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*/

#include "treedelegate.h"
#include "treerole.h"
#include <QStyleOptionViewItem>
#include <QLineEdit>
#include <QPalette>
#include <QPainter>

TreeDelegate::TreeDelegate(QObject* parent) : QStyledItemDelegate(parent)
{
}

QSize TreeDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    QSize size = QStyledItemDelegate::sizeHint(option, index);
    static int height = 0;
    if (!height) {
        QLineEdit lineEdit;
        lineEdit.setStyleSheet("QLineEdit { border: 1px solid transparent; }");
        height = lineEdit.sizeHint().height();
    }
    size.setHeight(height);
    return size;
}

void TreeDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    if (!index.parent().isValid()) {
        const bool selected = option.state & QStyle::State_Selected;
        const_cast<QStyleOptionViewItem&>(option).state &= ~(QStyle::State_Selected | QStyle::State_MouseOver);

        QColor c1 = option.palette.color(QPalette::Light);
        QColor c2 = option.palette.color(QPalette::Button);
        if (selected)
            qSwap(c1, c2);

        QLinearGradient gradient(option.rect.topLeft(), option.rect.bottomLeft());
        gradient.setColorAt(0.0, c1);
        gradient.setColorAt(1.0, c2);
        painter->fillRect(option.rect, gradient);

        QVector<QLine> lines;
        if (index.row() > 0)
            lines += QLine(option.rect.topLeft(), option.rect.topRight());
        lines += QLine(option.rect.bottomLeft(), option.rect.bottomRight());
        QPen oldPen = painter->pen();
        painter->setPen(option.palette.color(QPalette::Dark));
        painter->drawLines(lines);
        painter->setPen(oldPen);
    }

    if (index.parent().isValid() && index.column() == 1) {
        QRect rect;
        rect.setWidth(option.rect.width() - 2);
        const int ascent = option.fontMetrics.ascent();
        rect.setHeight(ascent + qMax(option.rect.height() % 2, ascent % 2));
        rect.moveCenter(option.rect.center());

        QColor color = index.data(Qt::BackgroundRole).value<QColor>();
        if (!color.isValid())
            color = option.palette.color(QPalette::Button);

        painter->save();
        painter->setPen(Qt::NoPen);
        painter->setBrush(color);
        painter->setRenderHint(QPainter::Antialiasing);
        painter->drawRoundedRect(rect, 40, 80, Qt::RelativeSize);
        painter->restore();
    }

    QStyledItemDelegate::paint(painter, option, index);

    if (index.parent().isValid() && index.column() == 1) {
        int badge = index.data(TreeRole::Badge).toInt();
        if (badge > 0) {
            painter->save();
            QFont font;
            if (font.pointSize() != -1)
                font.setPointSizeF(0.8 * font.pointSizeF());
            painter->setFont(font);

            QString txt;
            if (badge > 999)
                txt = QLatin1String("...");
            else
                txt = QFontMetrics(font).elidedText(QString::number(badge), Qt::ElideRight, option.rect.width());

            painter->setPen(option.palette.color(QPalette::ButtonText));
            painter->drawText(option.rect, Qt::AlignCenter, txt);
            painter->restore();
        }
    }
}

void TreeDelegate::initStyleOption(QStyleOptionViewItem* option, const QModelIndex& index) const
{
    QStyledItemDelegate::initStyleOption(option, index);
    if (index.parent().isValid() && index.column() == 1) {
        QStyleOptionViewItemV4* v4 = qstyleoption_cast<QStyleOptionViewItemV4*>(option);
        if (v4)
            v4->backgroundBrush = Qt::transparent;
    }
}
<commit_msg>Fix badge background<commit_after>/*
* Copyright (C) 2008-2013 The Communi Project
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*/

#include "treedelegate.h"
#include "treerole.h"
#include <QStyleOptionViewItem>
#include <QLineEdit>
#include <QPalette>
#include <QPainter>

TreeDelegate::TreeDelegate(QObject* parent) : QStyledItemDelegate(parent)
{
}

QSize TreeDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    QSize size = QStyledItemDelegate::sizeHint(option, index);
    static int height = 0;
    if (!height) {
        QLineEdit lineEdit;
        lineEdit.setStyleSheet("QLineEdit { border: 1px solid transparent; }");
        height = lineEdit.sizeHint().height();
    }
    size.setHeight(height);
    return size;
}

void TreeDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    int badge = 0;

    if (!index.parent().isValid()) {
        const bool selected = option.state & QStyle::State_Selected;
        const_cast<QStyleOptionViewItem&>(option).state &= ~(QStyle::State_Selected | QStyle::State_MouseOver);

        QColor c1 = option.palette.color(QPalette::Light);
        QColor c2 = option.palette.color(QPalette::Button);
        if (selected)
            qSwap(c1, c2);

        QLinearGradient gradient(option.rect.topLeft(), option.rect.bottomLeft());
        gradient.setColorAt(0.0, c1);
        gradient.setColorAt(1.0, c2);
        painter->fillRect(option.rect, gradient);

        QVector<QLine> lines;
        if (index.row() > 0)
            lines += QLine(option.rect.topLeft(), option.rect.topRight());
        lines += QLine(option.rect.bottomLeft(), option.rect.bottomRight());
        QPen oldPen = painter->pen();
        painter->setPen(option.palette.color(QPalette::Dark));
        painter->drawLines(lines);
        painter->setPen(oldPen);
    } else if (index.column() == 1) {
        badge = index.data(TreeRole::Badge).toInt();
    }

    if (badge > 0) {
        QRect rect;
        rect.setWidth(option.rect.width() - 2);
        const int ascent = option.fontMetrics.ascent();
        rect.setHeight(ascent + qMax(option.rect.height() % 2, ascent % 2));
        rect.moveCenter(option.rect.center());

        QColor color = index.data(Qt::BackgroundRole).value<QColor>();
        if (!color.isValid())
            color = option.palette.color(QPalette::Button);

        painter->save();
        painter->setPen(Qt::NoPen);
        painter->setBrush(color);
        painter->setRenderHint(QPainter::Antialiasing);
        painter->drawRoundedRect(rect, 40, 80, Qt::RelativeSize);
        painter->restore();
    }

    QStyledItemDelegate::paint(painter, option, index);

    if (badge > 0) {
        painter->save();
        QFont font;
        if (font.pointSize() != -1)
            font.setPointSizeF(0.8 * font.pointSizeF());
        painter->setFont(font);

        QString txt;
        if (badge > 999)
            txt = QLatin1String("...");
        else
            txt = QFontMetrics(font).elidedText(QString::number(badge), Qt::ElideRight, option.rect.width());

        painter->setPen(option.palette.color(QPalette::ButtonText));
        painter->drawText(option.rect, Qt::AlignCenter, txt);
        painter->restore();
    }
}

void TreeDelegate::initStyleOption(QStyleOptionViewItem* option, const QModelIndex& index) const
{
    QStyledItemDelegate::initStyleOption(option, index);
    if (index.parent().isValid() && index.column() == 1) {
        QStyleOptionViewItemV4* v4 = qstyleoption_cast<QStyleOptionViewItemV4*>(option);
        if (v4)
            v4->backgroundBrush = Qt::transparent;
    }
}
<|endoftext|>