<commit_before>#pragma once

// libraries
#include <algorithm>

#include "depthai-shared/common/Point2f.hpp"
#include "depthai-shared/common/Size2f.hpp"
#include "nlohmann/json.hpp"

namespace dai {

/**
 * Rect structure
 *
 * x,y coordinates together with width and height that define a rectangle.
 * Can be either normalized [0,1] or absolute representation.
 */
struct Rect {
    // default constructor
    Rect() : x(0), y(0), width(0), height(0) {}
    Rect(float x, float y, float width, float height) {
        this->x = x;
        this->y = y;
        this->width = width;
        this->height = height;
    }

    Rect(const Rect& r) : x(r.x), y(r.y), width(r.width), height(r.height) {}
    Rect(const Point2f& org, const Size2f& sz) : x(org.x), y(org.y), width(sz.width), height(sz.height) {}
    Rect(const Point2f& pt1, const Point2f& pt2) {
        x = std::min(pt1.x, pt2.x);
        y = std::min(pt1.y, pt2.y);
        width = std::max(pt1.x, pt2.x) - x;
        height = std::max(pt1.y, pt2.y) - y;
    }

    Rect& operator=(const Rect& r) {
        x = r.x;
        y = r.y;
        width = r.width;
        height = r.height;
        return *this;
    }

    /**
     * The top-left corner.
     */
    Point2f topLeft() const {
        return Point2f(x, y);
    }

    /**
     * The bottom-right corner
     */
    Point2f bottomRight() const {
        return Point2f(x + width, y + height);
    }

    /**
     * Size (width, height) of the rectangle
     */
    Size2f size() const {
        return Size2f(width, height);
    }

    /**
     * Area (width*height) of the rectangle
     */
    float area() const {
        return width * height;
    }

    /**
     * True if rectangle is empty.
     */
    bool empty() const {
        return width <= 0 || height <= 0;
    }

    /**
     * Checks whether the rectangle contains the point.
     */
    bool contains(const Point2f& pt) const {
        return x <= pt.x && pt.x < x + width && y <= pt.y && pt.y < y + height;
    }

    /**
     * Whether rectangle is normalized (coordinates in [0,1] range). or not.
     */
    bool isNormalized() const {
        return x + width <= 1.f && y + height <= 1.f;
    }

    /**
     * Denormalize rectangle.
     * @param width Destination frame width.
     * @param height Destination frame height.
     */
    Rect denormalize(int width, int height) {
        if(isNormalized()) {
            float _x = std::round(this->x * width);
            float _y = std::round(this->y * height);
            float _width = std::round(this->width * width);
            float _height = std::round(this->height * height);
            return Rect(_x, _y, _width, _height);
        }
        return *this;
    }

    /**
     * Normalize rectangle.
     * @param width Source frame width.
     * @param height Source frame height.
     */
    Rect normalize(int width, int height) {
        if(isNormalized()) {
            return *this;
        }
        float _x = this->x / width;
        float _y = this->y / height;
        float _width = this->width / width;
        float _height = this->height / height;
        return Rect(_x, _y, _width, _height);
    }

    float x;       // x coordinate of the top-left corner
    float y;       // y coordinate of the top-left corner
    float width;   // width of the rectangle
    float height;  // height of the rectangle
};
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Rect, x, y, width, height);

}  // namespace dai<commit_msg>Update shared with formatting<commit_after>#pragma once

// libraries
#include <algorithm>

#include "depthai-shared/common/Point2f.hpp"
#include "depthai-shared/common/Size2f.hpp"
#include "nlohmann/json.hpp"

namespace dai {

/**
 * Rect structure
 *
 * x,y coordinates together with width and height that define a rectangle.
 * Can be either normalized [0,1] or absolute representation.
 */
struct Rect {
    // default constructor
    Rect() : x(0), y(0), width(0), height(0) {}
    Rect(float x, float y, float width, float height) {
        this->x = x;
        this->y = y;
        this->width = width;
        this->height = height;
    }

    Rect(const Rect& r) : x(r.x), y(r.y), width(r.width), height(r.height) {}
    Rect(const Point2f& org, const Size2f& sz) : x(org.x), y(org.y), width(sz.width), height(sz.height) {}
    Rect(const Point2f& pt1, const Point2f& pt2) {
        x = std::min(pt1.x, pt2.x);
        y = std::min(pt1.y, pt2.y);
        width = std::max(pt1.x, pt2.x) - x;
        height = std::max(pt1.y, pt2.y) - y;
    }

    Rect& operator=(const Rect& r) {
        x = r.x;
        y = r.y;
        width = r.width;
        height = r.height;
        return *this;
    }

    /**
     * The top-left corner.
     */
    Point2f topLeft() const {
        return Point2f(x, y);
    }

    /**
     * The bottom-right corner
     */
    Point2f bottomRight() const {
        return Point2f(x + width, y + height);
    }

    /**
     * Size (width, height) of the rectangle
     */
    Size2f size() const {
        return Size2f(width, height);
    }

    /**
     * Area (width*height) of the rectangle
     */
    float area() const {
        return width * height;
    }

    /**
     * True if rectangle is empty.
     */
    bool empty() const {
        return width <= 0 || height <= 0;
    }

    /**
     * Checks whether the rectangle contains the point.
     */
    bool contains(const Point2f& pt) const {
        return x <= pt.x && pt.x < x + width && y <= pt.y && pt.y < y + height;
    }

    /**
     * Whether rectangle is normalized (coordinates in [0,1] range) or not.
     */
    bool isNormalized() const {
        if(x + width <= 1.f && y + height <= 1.f) return true;
        return !(x == static_cast<int>(x) && y == static_cast<int>(y) && width == static_cast<int>(width) && height == static_cast<int>(height));
    }

    /**
     * Denormalize rectangle.
     * @param width Destination frame width.
     * @param height Destination frame height.
     */
    Rect denormalize(int width, int height) {
        if(isNormalized()) {
            float _x = std::round(this->x * width);
            float _y = std::round(this->y * height);
            float _width = std::round(this->width * width);
            float _height = std::round(this->height * height);
            return Rect(_x, _y, _width, _height);
        }
        return *this;
    }

    /**
     * Normalize rectangle.
     * @param width Source frame width.
     * @param height Source frame height.
     */
    Rect normalize(int width, int height) {
        if(isNormalized()) {
            return *this;
        }
        float _x = this->x / width;
        float _y = this->y / height;
        float _width = this->width / width;
        float _height = this->height / height;
        return Rect(_x, _y, _width, _height);
    }

    float x;       // x coordinate of the top-left corner
    float y;       // y coordinate of the top-left corner
    float width;   // width of the rectangle
    float height;  // height of the rectangle
};
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Rect, x, y, width, height);

}  // namespace dai<|endoftext|>


<commit_before>// Copyright (c) 2015-present, Qihoo, Inc.  All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.

#include "pink/include/redis_conn.h"

#include <stdlib.h>
#include <limits.h>

#include <string>
#include <sstream>

#include "slash/include/xdebug.h"
#include "slash/include/slash_string.h"

namespace pink {

static bool IsHexDigit(char ch) {
  return (ch>='0' && ch<='9') || (ch>='a' && ch<='f') || (ch>='A' && ch<'F');
}

static int HexDigitToInt32(char ch) {
  if (ch <= '9' && ch >= '0') {
    return ch-'0';
  } else if (ch <= 'F' && ch >= 'A') {
    return ch-'A';
  } else if (ch <= 'f' && ch >= 'a') {
    return ch-'a';
  } else {
    return 0;
  }
}

static int split2args(const std::string& req_buf, RedisCmdArgsType& argv) {
  const char *p = req_buf.data();
  std::string arg;

  while (1) {
    // skip blanks
    while (*p && isspace(*p)) p++;
    if (*p) {
      // get a token
      int inq = 0;  // set to 1 if we are in "quotes"
      int insq = 0;  // set to 1 if we are in 'single quotes'
      int done = 0;

      arg.clear();
      while (!done) {
        if (inq) {
          if (*p == '\\' && *(p+1) == 'x' &&
              IsHexDigit(*(p+2)) &&
              IsHexDigit(*(p+3))) {
            unsigned char byte = HexDigitToInt32(*(p+2))*16 + HexDigitToInt32(*(p+3));
            arg.append(1, byte);
            p += 3;
          } else if (*p == '\\' && *(p + 1)) {
            char c;

            p++;
            switch (*p) {
              case 'n': c = '\n'; break;
              case 'r': c = '\r'; break;
              case 't': c = '\t'; break;
              case 'b': c = '\b'; break;
              case 'a': c = '\a'; break;
              default: c = *p; break;
            }
            arg.append(1, c);
          } else if (*p == '"') {
            /* closing quote must be followed by a space or
            * nothing at all. */
            if (*(p+1) && !isspace(*(p+1))) {
              argv.clear();
              return -1;
            }
            done = 1;
          } else if (!*p) {
            // unterminated quotes
            argv.clear();
            return -1;
          } else {
            arg.append(1, *p);
          }
        } else if (insq) {
          if (*p == '\\' && *(p+1) == '\'') {
            p++;
            arg.append(1, '\'');
          } else if (*p == '\'') {
            /* closing quote must be followed by a space or
            * nothing at all. */
            if (*(p+1) && !isspace(*(p+1))) {
              argv.clear();
              return -1;
            }
            done = 1;
          } else if (!*p) {
            // unterminated quotes
            argv.clear();
            return -1;
          } else {
            arg.append(1, *p);
          }
        } else {
          switch (*p) {
            case ' ':
            case '\n':
            case '\r':
            case '\t':
            case '\0':
              done = 1;
            break;
            case '"':
              inq = 1;
            break;
            case '\'':
              insq = 1;
            break;
            default:
              // current = sdscatlen(current,p,1);
              arg.append(1, *p);
              break;
          }
        }
        if (*p) p++;
      }
      argv.push_back(arg);
    } else {
      return 0;
    }
  }
}

RedisConn::RedisConn(const int fd,
                     const std::string &ip_port,
                     ServerThread *thread)
    : PinkConn(fd, ip_port, thread),
      rbuf_(nullptr),
      rbuf_len_(0),
      msg_peak_(0),
      wbuf_pos_(0),
      last_read_pos_(-1),
      next_parse_pos_(0),
      req_type_(0),
      multibulk_len_(0),
      bulk_len_(-1) {
}

RedisConn::~RedisConn() {
  free(rbuf_);
}

ReadStatus RedisConn::ProcessInlineBuffer() {
  int pos, ret;
  pos = FindNextSeparators();
  if (pos == -1) {
    return rbuf_len_ > REDIS_INLINE_MAXLEN ? kFullError : kReadHalf;
  }
  // args \r\n
  std::string req_buf(rbuf_ + next_parse_pos_, pos + 1 - next_parse_pos_);

  argv_.clear();
  ret = split2args(req_buf, argv_);
  next_parse_pos_ = pos + 1;

  return ret == -1 ? kParseError : kReadAll;
}

ReadStatus RedisConn::ProcessMultibulkBuffer() {
  int pos = 0;
  if (multibulk_len_ == 0) {
    /* The client should have been reset */
    pos = FindNextSeparators();
    if (pos != -1) {
      if (GetNextNum(pos, &multibulk_len_) != 0) {
        // Protocol error: invalid multibulk length
        return kParseError;
      }
      next_parse_pos_ = pos + 1;
      argv_.clear();
      if (next_parse_pos_ > last_read_pos_) {
        return kReadHalf;
      }
    } else {
      return kReadHalf;  // HALF
    }
  }
  while (multibulk_len_) {
    if (bulk_len_ == -1) {
      pos = FindNextSeparators();
      if (pos != -1) {
        if (rbuf_[next_parse_pos_] != '$') {
           return kParseError;  // PARSE_ERROR
        }

        if (GetNextNum(pos, &bulk_len_) != 0) {
            // Protocol error: invalid bulk length
            return kParseError;
        }
        next_parse_pos_ = pos + 1;
      }
      if (pos == -1 || next_parse_pos_ > last_read_pos_) {
        return kReadHalf;
      }
    }
    if (last_read_pos_ - next_parse_pos_ + 1 < bulk_len_ + 2) {
      // Data not enough
      break;
    } else {
      argv_.emplace_back(rbuf_ + next_parse_pos_, bulk_len_);
      next_parse_pos_ = next_parse_pos_ + bulk_len_ + 2;
      bulk_len_ = -1;
      multibulk_len_--;
    }
  }

  if (multibulk_len_ == 0) {
    return kReadAll;  // OK
  } else {
    return kReadHalf;  // HALF
  }
}

ReadStatus RedisConn::ProcessInputBuffer() {
  ReadStatus ret;
  while (next_parse_pos_ <= last_read_pos_) {
    if (!req_type_) {
      if (rbuf_[next_parse_pos_] == '*') {
        req_type_ = REDIS_REQ_MULTIBULK;
      } else {
        req_type_ = REDIS_REQ_INLINE;
      }
    }

    if (req_type_ == REDIS_REQ_INLINE) {
      ret = ProcessInlineBuffer();
      if (ret != kReadAll) {
        return ret;
      }
    } else if (req_type_ == REDIS_REQ_MULTIBULK) {
      ret = ProcessMultibulkBuffer();
      if (ret != kReadAll) { // FULL_ERROR || HALF || PARSE_ERROR
        return ret;
      }
    } else {
      // Unknown requeset type;
      return kParseError;
    }

    if (!argv_.empty()) {
      DealMessage(argv_, &response_);
      if (!response_.empty()) {
        set_is_reply(true);
      }
    }
    // ResetClient
    argv_.clear();
    req_type_ = 0;
    multibulk_len_ = 0;
    bulk_len_ = -1;
  }

  return kReadAll; // OK
}

ReadStatus RedisConn::GetRequest() {
  ssize_t nread = 0;
  int next_read_pos = last_read_pos_ + 1;

  int remain = rbuf_len_ - next_read_pos;  // Remain buffer size
  int new_size = 0;
  if (remain == 0) {
    new_size = rbuf_len_ + REDIS_IOBUF_LEN;
    remain += REDIS_IOBUF_LEN;
  } else if (remain < bulk_len_) {
    new_size = next_read_pos + bulk_len_;
    remain = bulk_len_;
  }
  if (new_size > rbuf_len_) {
    if (new_size > REDIS_MAX_MESSAGE) {
      return kFullError;
    }
    rbuf_ = static_cast<char*>(realloc(rbuf_, new_size));
    if (rbuf_ == nullptr) {
      return kFullError;
    }
    rbuf_len_ = new_size;
  }

  nread = read(fd(), rbuf_ + next_read_pos, remain);
  if (nread == -1) {
    if (errno == EAGAIN) {
      nread = 0;
      return kReadHalf; // HALF
    } else {
      // error happened, close client
      return kReadError;
    }
  } else if (nread == 0) {
    // client closed, close client
    return kReadClose;
  }

  // assert(nread > 0);
  last_read_pos_ += nread;
  msg_peak_ = last_read_pos_;

  ReadStatus ret = ProcessInputBuffer();
  if (ret == kReadAll) {
    next_parse_pos_ = 0;
    last_read_pos_ = -1;
  }

  return ret; // OK || HALF || FULL_ERROR || PARSE_ERROR
}

WriteStatus RedisConn::SendReply() {
  ssize_t nwritten = 0;
  size_t wbuf_len = response_.size();
  while (wbuf_len > 0) {
    nwritten = write(fd(), response_.data() + wbuf_pos_, wbuf_len - wbuf_pos_);
    if (nwritten <= 0) {
      break;
    }
    wbuf_pos_ += nwritten;
    if (wbuf_pos_ == wbuf_len) {
      // Have sended all response data
      if (wbuf_len > DEFAULT_WBUF_SIZE) {
        std::string buf;
        buf.reserve(DEFAULT_WBUF_SIZE);
        response_.swap(buf);
      }
      response_.clear();

      wbuf_len = 0;
      wbuf_pos_ = 0;
    }
  }
  if (nwritten == -1) {
    if (errno == EAGAIN) {
      return kWriteHalf;
    } else {
      // Here we should close the connection
      return kWriteError;
    }
  }
  if (wbuf_len == 0) {
    return kWriteAll;
  } else {
    return kWriteHalf;
  }
}

void RedisConn::WriteResp(const std::string& resp) {
  response_.append(resp);
  set_is_reply(true);
}

void RedisConn::TryResizeBuffer() {
  log_info("Current buffer size: %d", rbuf_len_);
  struct timeval now;
  gettimeofday(&now, nullptr);
  int idletime = now.tv_sec - last_interaction().tv_sec;
  if (rbuf_len_ > REDIS_MBULK_BIG_ARG &&
      ((rbuf_len_ / (msg_peak_ + 1)) > 2 || idletime > 2)) {
    int new_size =
      ((last_read_pos_ + REDIS_IOBUF_LEN) / REDIS_IOBUF_LEN) * REDIS_IOBUF_LEN;
    if (new_size < rbuf_len_) {
      rbuf_ = static_cast<char*>(realloc(rbuf_, new_size));
      rbuf_len_ = new_size;
      log_info("Resize buffer to %d, last_read_pos_: %d\n",
               rbuf_len_, last_read_pos_);
    }
    msg_peak_ = 0;
  }
}

int RedisConn::FindNextSeparators() {
  int pos = next_parse_pos_ <= last_read_pos_ ? next_parse_pos_ : 0;
  while (pos <= last_read_pos_) {
    if (rbuf_[pos] == '\n') {
      return pos;
    }
    pos++;
  }
  return -1;
}

int RedisConn::GetNextNum(int pos, long* value) {
  std::string tmp;
  assert(pos > next_parse_pos_);
  // next_parse_pos_    pos
  //      |    ----------|
  //      |    |
  //      *3\r\n
  // [next_parse_pos_ + 1, pos - next_parse_pos_- 2]

  if (slash::string2l(rbuf_ + next_parse_pos_ + 1,
                            pos - next_parse_pos_ - 2,
                            value)) {
    return 0; // Success
  }
  return -1; // Failed
}

}  // namespace pink
<commit_msg>Bugfix of redis protocol<commit_after>// Copyright (c) 2015-present, Qihoo, Inc.  All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.

#include "pink/include/redis_conn.h"

#include <stdlib.h>
#include <limits.h>

#include <string>
#include <sstream>

#include "slash/include/xdebug.h"
#include "slash/include/slash_string.h"

namespace pink {

static bool IsHexDigit(char ch) {
  return (ch>='0' && ch<='9') || (ch>='a' && ch<='f') || (ch>='A' && ch<'F');
}

static int HexDigitToInt32(char ch) {
  if (ch <= '9' && ch >= '0') {
    return ch-'0';
  } else if (ch <= 'F' && ch >= 'A') {
    return ch-'A';
  } else if (ch <= 'f' && ch >= 'a') {
    return ch-'a';
  } else {
    return 0;
  }
}

static int split2args(const std::string& req_buf, RedisCmdArgsType& argv) {
  const char *p = req_buf.data();
  std::string arg;

  while (1) {
    // skip blanks
    while (*p && isspace(*p)) p++;
    if (*p) {
      // get a token
      int inq = 0;  // set to 1 if we are in "quotes"
      int insq = 0;  // set to 1 if we are in 'single quotes'
      int done = 0;

      arg.clear();
      while (!done) {
        if (inq) {
          if (*p == '\\' && *(p+1) == 'x' &&
              IsHexDigit(*(p+2)) &&
              IsHexDigit(*(p+3))) {
            unsigned char byte = HexDigitToInt32(*(p+2))*16 + HexDigitToInt32(*(p+3));
            arg.append(1, byte);
            p += 3;
          } else if (*p == '\\' && *(p + 1)) {
            char c;

            p++;
            switch (*p) {
              case 'n': c = '\n'; break;
              case 'r': c = '\r'; break;
              case 't': c = '\t'; break;
              case 'b': c = '\b'; break;
              case 'a': c = '\a'; break;
              default: c = *p; break;
            }
            arg.append(1, c);
          } else if (*p == '"') {
            /* closing quote must be followed by a space or
            * nothing at all. */
            if (*(p+1) && !isspace(*(p+1))) {
              argv.clear();
              return -1;
            }
            done = 1;
          } else if (!*p) {
            // unterminated quotes
            argv.clear();
            return -1;
          } else {
            arg.append(1, *p);
          }
        } else if (insq) {
          if (*p == '\\' && *(p+1) == '\'') {
            p++;
            arg.append(1, '\'');
          } else if (*p == '\'') {
            /* closing quote must be followed by a space or
            * nothing at all. */
            if (*(p+1) && !isspace(*(p+1))) {
              argv.clear();
              return -1;
            }
            done = 1;
          } else if (!*p) {
            // unterminated quotes
            argv.clear();
            return -1;
          } else {
            arg.append(1, *p);
          }
        } else {
          switch (*p) {
            case ' ':
            case '\n':
            case '\r':
            case '\t':
            case '\0':
              done = 1;
            break;
            case '"':
              inq = 1;
            break;
            case '\'':
              insq = 1;
            break;
            default:
              // current = sdscatlen(current,p,1);
              arg.append(1, *p);
              break;
          }
        }
        if (*p) p++;
      }
      argv.push_back(arg);
    } else {
      return 0;
    }
  }
}

RedisConn::RedisConn(const int fd,
                     const std::string &ip_port,
                     ServerThread *thread)
    : PinkConn(fd, ip_port, thread),
      rbuf_(nullptr),
      rbuf_len_(0),
      msg_peak_(0),
      wbuf_pos_(0),
      last_read_pos_(-1),
      next_parse_pos_(0),
      req_type_(0),
      multibulk_len_(0),
      bulk_len_(-1) {
}

RedisConn::~RedisConn() {
  free(rbuf_);
}

ReadStatus RedisConn::ProcessInlineBuffer() {
  int pos, ret;
  pos = FindNextSeparators();
  if (pos == -1) {
    return rbuf_len_ > REDIS_INLINE_MAXLEN ? kFullError : kReadHalf;
  }
  // args \r\n
  std::string req_buf(rbuf_ + next_parse_pos_, pos + 1 - next_parse_pos_);

  argv_.clear();
  ret = split2args(req_buf, argv_);
  next_parse_pos_ = pos + 1;

  return ret == -1 ? kParseError : kReadAll;
}

ReadStatus RedisConn::ProcessMultibulkBuffer() {
  int pos = 0;
  if (multibulk_len_ == 0) {
    /* The client should have been reset */
    pos = FindNextSeparators();
    if (pos != -1) {
      if (GetNextNum(pos, &multibulk_len_) != 0) {
        // Protocol error: invalid multibulk length
        return kParseError;
      }
      next_parse_pos_ = pos + 1;
      argv_.clear();
      if (next_parse_pos_ > last_read_pos_) {
        return kReadHalf;
      }
    } else {
      return kReadHalf;  // HALF
    }
  }
  while (multibulk_len_) {
    if (bulk_len_ == -1) {
      pos = FindNextSeparators();
      if (pos != -1) {
        if (rbuf_[next_parse_pos_] != '$') {
           return kParseError;  // PARSE_ERROR
        }

        if (GetNextNum(pos, &bulk_len_) != 0) {
            // Protocol error: invalid bulk length
            return kParseError;
        }
        next_parse_pos_ = pos + 1;
      }
      if (pos == -1 || next_parse_pos_ > last_read_pos_) {
        return kReadHalf;
      }
    }
    if (last_read_pos_ - next_parse_pos_ + 1 < bulk_len_ + 2) {
      // Data not enough
      break;
    } else {
      argv_.emplace_back(rbuf_ + next_parse_pos_, bulk_len_);
      next_parse_pos_ = next_parse_pos_ + bulk_len_ + 2;
      bulk_len_ = -1;
      multibulk_len_--;
    }
  }

  if (multibulk_len_ == 0) {
    return kReadAll;  // OK
  } else {
    return kReadHalf;  // HALF
  }
}

ReadStatus RedisConn::ProcessInputBuffer() {
  ReadStatus ret;
  while (next_parse_pos_ <= last_read_pos_) {
    if (!req_type_) {
      if (rbuf_[next_parse_pos_] == '*') {
        req_type_ = REDIS_REQ_MULTIBULK;
      } else {
        req_type_ = REDIS_REQ_INLINE;
      }
    }

    if (req_type_ == REDIS_REQ_INLINE) {
      ret = ProcessInlineBuffer();
      if (ret != kReadAll) {
        return ret;
      }
    } else if (req_type_ == REDIS_REQ_MULTIBULK) {
      ret = ProcessMultibulkBuffer();
      if (ret != kReadAll) { // FULL_ERROR || HALF || PARSE_ERROR
        return ret;
      }
    } else {
      // Unknown requeset type;
      return kParseError;
    }

    if (!argv_.empty()) {
      DealMessage(argv_, &response_);
      if (!response_.empty()) {
        set_is_reply(true);
      }
    }
    // ResetClient
    argv_.clear();
    req_type_ = 0;
    multibulk_len_ = 0;
    bulk_len_ = -1;
  }

  return kReadAll; // OK
}

ReadStatus RedisConn::GetRequest() {
  ssize_t nread = 0;
  int next_read_pos = last_read_pos_ + 1;

  int remain = rbuf_len_ - next_read_pos;  // Remain buffer size
  int new_size = 0;
  if (remain == 0) {
    new_size = rbuf_len_ + REDIS_IOBUF_LEN;
    remain += REDIS_IOBUF_LEN;
  } else if (remain < bulk_len_) {
    new_size = next_read_pos + bulk_len_;
    remain = bulk_len_;
  }
  if (new_size > rbuf_len_) {
    if (new_size > REDIS_MAX_MESSAGE) {
      return kFullError;
    }
    rbuf_ = static_cast<char*>(realloc(rbuf_, new_size));
    if (rbuf_ == nullptr) {
      return kFullError;
    }
    rbuf_len_ = new_size;
  }

  nread = read(fd(), rbuf_ + next_read_pos, remain);
  if (nread == -1) {
    if (errno == EAGAIN) {
      nread = 0;
      return kReadHalf; // HALF
    } else {
      // error happened, close client
      return kReadError;
    }
  } else if (nread == 0) {
    // client closed, close client
    return kReadClose;
  }

  // assert(nread > 0);
  last_read_pos_ += nread;
  msg_peak_ = last_read_pos_;

  ReadStatus ret = ProcessInputBuffer();
  if (ret == kReadAll) {
    next_parse_pos_ = 0;
    last_read_pos_ = -1;
  }

  return ret; // OK || HALF || FULL_ERROR || PARSE_ERROR
}

WriteStatus RedisConn::SendReply() {
  ssize_t nwritten = 0;
  size_t wbuf_len = response_.size();
  while (wbuf_len > 0) {
    nwritten = write(fd(), response_.data() + wbuf_pos_, wbuf_len - wbuf_pos_);
    if (nwritten <= 0) {
      break;
    }
    wbuf_pos_ += nwritten;
    if (wbuf_pos_ == wbuf_len) {
      // Have sended all response data
      if (wbuf_len > DEFAULT_WBUF_SIZE) {
        std::string buf;
        buf.reserve(DEFAULT_WBUF_SIZE);
        response_.swap(buf);
      }
      response_.clear();

      wbuf_len = 0;
      wbuf_pos_ = 0;
    }
  }
  if (nwritten == -1) {
    if (errno == EAGAIN) {
      return kWriteHalf;
    } else {
      // Here we should close the connection
      return kWriteError;
    }
  }
  if (wbuf_len == 0) {
    return kWriteAll;
  } else {
    return kWriteHalf;
  }
}

void RedisConn::WriteResp(const std::string& resp) {
  response_.append(resp);
  set_is_reply(true);
}

void RedisConn::TryResizeBuffer() {
  log_info("Current buffer size: %d", rbuf_len_);
  struct timeval now;
  gettimeofday(&now, nullptr);
  int idletime = now.tv_sec - last_interaction().tv_sec;
  if (rbuf_len_ > REDIS_MBULK_BIG_ARG &&
      ((rbuf_len_ / (msg_peak_ + 1)) > 2 || idletime > 2)) {
    int new_size =
      ((last_read_pos_ + REDIS_IOBUF_LEN) / REDIS_IOBUF_LEN) * REDIS_IOBUF_LEN;
    if (new_size < rbuf_len_) {
      rbuf_ = static_cast<char*>(realloc(rbuf_, new_size));
      rbuf_len_ = new_size;
      log_info("Resize buffer to %d, last_read_pos_: %d\n",
               rbuf_len_, last_read_pos_);
    }
    msg_peak_ = 0;
  }
}

int RedisConn::FindNextSeparators() {
  if (next_parse_pos_ > last_read_pos_) {
    return -1;
  }
  int pos = next_parse_pos_;
  while (pos <= last_read_pos_) {
    if (rbuf_[pos] == '\n') {
      return pos;
    }
    pos++;
  }
  return -1;
}

int RedisConn::GetNextNum(int pos, long* value) {
  std::string tmp;
  assert(pos > next_parse_pos_);
  // next_parse_pos_    pos
  //      |    ----------|
  //      |    |
  //      *3\r\n
  // [next_parse_pos_ + 1, pos - next_parse_pos_- 2]

  if (slash::string2l(rbuf_ + next_parse_pos_ + 1,
                            pos - next_parse_pos_ - 2,
                            value)) {
    return 0; // Success
  }
  return -1; // Failed
}

}  // namespace pink
<|endoftext|>


<commit_before>#ifndef ENTT_CORE_ANY_HPP
#define ENTT_CORE_ANY_HPP

#include <functional>
#include <new>
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "type_info.hpp"
#include "type_traits.hpp"

namespace entt {

/*! @brief A SBO friendly, type-safe container for single values of any type. */
class any {
    enum class operation { COPY, MOVE, DTOR, COMP, ADDR, CADDR, REF, CREF, TYPE };

    using storage_type = std::aligned_storage_t<sizeof(double[2]), alignof(double[2])>;
    using vtable_type = const void *(const operation, const any &, const void *);

    template<typename Type>
    static constexpr auto in_situ = sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v<Type>;

    template<typename Type>
    [[nodiscard]] static bool compare(const void *lhs, const void *rhs) {
        if constexpr(!std::is_function_v<Type> && is_equality_comparable_v<Type>) {
            return *static_cast<const Type *>(lhs) == *static_cast<const Type *>(rhs);
        } else {
            return lhs == rhs;
        }
    }

    static type_info & as_type_info(const void *data) {
        return *const_cast<type_info *>(static_cast<const type_info *>(data));
    }

    static any & as_any(const void *data) {
        return *const_cast<any *>(static_cast<const any *>(data));
    }

    template<typename Type>
    static const void * basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const any &from, [[maybe_unused]] const void *to) {
        if constexpr(std::is_void_v<Type>) {
            return nullptr;
        } else if constexpr(std::is_lvalue_reference_v<Type>) {
            using base_type = std::remove_reference_t<Type>;

            switch(op) {
            case operation::REF:
            case operation::CREF:
                as_any(to).vtable = (op == operation::REF) ? basic_vtable<Type> : basic_vtable<const base_type &>;
                [[fallthrough]];
            case operation::COPY:
            case operation::MOVE:
                as_any(to).instance = from.instance;
                [[fallthrough]];
            case operation::DTOR:
                break;
            case operation::COMP:
                return compare<std::remove_const_t<base_type>>(from.instance, to) ? to : nullptr;
            case operation::ADDR:
                return std::is_const_v<base_type> ? nullptr : from.instance;
            case operation::CADDR:
                return from.instance;
            case operation::TYPE:
                as_type_info(to) = type_id<std::remove_const_t<base_type>>();
                break;
            }
        } else if constexpr(in_situ<Type>) {
            auto *instance = const_cast<Type *>(std::launder(reinterpret_cast<const Type *>(&from.storage)));

            switch(op) {
            case operation::COPY:
                new (&as_any(to).storage) Type{std::as_const(*instance)};
                break;
            case operation::MOVE:
                new (&as_any(to).storage) Type{std::move(*instance)};
                [[fallthrough]];
            case operation::DTOR:
                instance->~Type();
                break;
            case operation::COMP:
                return compare<Type>(instance, to) ? to : nullptr;
            case operation::ADDR:
            case operation::CADDR:
                return instance;
            case operation::REF:
            case operation::CREF:
                as_any(to).vtable = (op == operation::REF) ? basic_vtable<Type &> : basic_vtable<const Type &>;
                as_any(to).instance = instance;
                break;
            case operation::TYPE:
                as_type_info(to) = type_id<Type>();
                break;
            }
        } else {
            switch(op) {
            case operation::COPY:
                as_any(to).instance = new Type{*static_cast<const Type *>(from.instance)};
                break;
            case operation::REF:
            case operation::CREF:
                as_any(to).vtable = (op == operation::REF) ? basic_vtable<Type &> : basic_vtable<const Type &>;
                [[fallthrough]];
            case operation::MOVE:
                as_any(to).instance = from.instance;
                break;
            case operation::DTOR:
                delete static_cast<const Type *>(from.instance);
                break;
            case operation::COMP:
                return compare<Type>(from.instance, to) ? to : nullptr;
            case operation::ADDR:
            case operation::CADDR:
                return from.instance;
            case operation::TYPE:
                as_type_info(to) = type_id<Type>();
                break;
            }
        }

        return nullptr;
    }

public:
    /*! @brief Default constructor. */
    any() ENTT_NOEXCEPT
        : any{std::in_place_type<void>}
    {}

    /**
     * @brief Constructs an any by directly initializing the new object.
     * @tparam Type Type of object to use to initialize the wrapper.
     * @tparam Args Types of arguments to use to construct the new instance.
     * @param args Parameters to use to construct the instance.
     */
    template<typename Type, typename... Args>
    explicit any(std::in_place_type_t<Type>, [[maybe_unused]] Args &&... args)
        : vtable{&basic_vtable<Type>},
          instance{}
    {
        if constexpr(!std::is_void_v<Type>) {
            if constexpr(std::is_lvalue_reference_v<Type>) {
                static_assert(sizeof...(Args) == 1u && (std::is_pointer_v<std::remove_reference_t<Args>> && ...));
                ENTT_ASSERT(((args != nullptr) && ...));
                instance = (args, ...);
            } else if constexpr(in_situ<Type>) {
                new (&storage) Type{std::forward<Args>(args)...};
            } else {
                instance = new Type{std::forward<Args>(args)...};
            }
        }
    }

    /**
     * @brief Constructs an any that holds an unmanaged object.
     * @tparam Type Type of object to use to initialize the wrapper.
     * @param value An instance of an object to use to initialize the wrapper.
     */
    template<typename Type>
    any(std::reference_wrapper<Type> value) ENTT_NOEXCEPT
        : any{std::in_place_type<Type &>, &value.get()}
    {}

    /**
     * @brief Constructs an any from a given value.
     * @tparam Type Type of object to use to initialize the wrapper.
     * @param value An instance of an object to use to initialize the wrapper.
     */
    template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, any>>>
    any(Type &&value)
        : any{std::in_place_type<std::remove_cv_t<std::remove_reference_t<Type>>>, std::forward<Type>(value)}
    {}

    /**
     * @brief Copy constructor.
     * @param other The instance to copy from.
     */
    any(const any &other)
        : any{}
    {
        vtable = other.vtable;
        vtable(operation::COPY, other, this);
    }

    /**
     * @brief Move constructor.
     * @param other The instance to move from.
     */
    any(any &&other) ENTT_NOEXCEPT
        : any{}
    {
        vtable = std::exchange(other.vtable, &basic_vtable<void>);
        vtable(operation::MOVE, other, this);
    }

    /*! @brief Frees the internal storage, whatever it means. */
    ~any() {
        vtable(operation::DTOR, *this, nullptr);
    }

    /**
     * @brief Assignment operator.
     * @param other The instance to assign from.
     * @return This any object.
     */
    any & operator=(any other) {
        swap(*this, other);
        return *this;
    }

    /**
     * @brief Returns the type of the contained object.
     * @return The type of the contained object, if any.
     */
    [[nodiscard]] type_info type() const ENTT_NOEXCEPT {
        type_info info;
        vtable(operation::TYPE, *this, &info);
        return info;
    }

    /**
     * @brief Returns an opaque pointer to the contained instance.
     * @return An opaque pointer the contained instance, if any.
     */
    [[nodiscard]] const void * data() const ENTT_NOEXCEPT {
        return vtable(operation::CADDR, *this, nullptr);
    }

    /*! @copydoc data */
    [[nodiscard]] void * data() ENTT_NOEXCEPT {
        return const_cast<void *>(vtable(operation::ADDR, *this, nullptr));
    }

    /**
     * @brief Replaces the contained object by creating a new instance directly.
     * @tparam Type Type of object to use to initialize the wrapper.
     * @tparam Args Types of arguments to use to construct the new instance.
     * @param args Parameters to use to construct the instance.
     */
    template<typename Type, typename... Args>
    void emplace(Args &&... args) {
        *this = any{std::in_place_type<Type>, std::forward<Args>(args)...};
    }

    /**
     * @brief Returns false if a wrapper is empty, true otherwise.
     * @return False if the wrapper is empty, true otherwise.
     */
    [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
        return !(vtable(operation::CADDR, *this, nullptr) == nullptr);
    }

    /**
     * @brief Checks if two wrappers differ in their content.
     * @param other Wrapper with which to compare.
     * @return False if the two objects differ in their content, true otherwise.
     */
    bool operator==(const any &other) const ENTT_NOEXCEPT {
        return type() == other.type() && (vtable(operation::COMP, *this, other.data()) == other.data());
    }

    /**
     * @brief Swaps two any objects.
     * @param lhs A valid any object.
     * @param rhs A valid any object.
     */
    friend void swap(any &lhs, any &rhs) {
        any tmp{};
        lhs.vtable(operation::MOVE, lhs, &tmp);
        rhs.vtable(operation::MOVE, rhs, &lhs);
        lhs.vtable(operation::MOVE, tmp, &rhs);
        std::swap(lhs.vtable, rhs.vtable);
    }

    /**
     * @brief Aliasing constructor.
     * @param other A reference to an object that isn't necessarily initialized.
     * @return An any that shares a reference to an unmanaged object.
     */
    [[nodiscard]] friend any as_ref(any &other) ENTT_NOEXCEPT {
        any ref{};
        other.vtable(operation::REF, other, &ref);
        return ref;
    }

    /*! @copydoc as_ref */
    [[nodiscard]] friend any as_ref(const any &other) ENTT_NOEXCEPT {
        any ref{};
        other.vtable(operation::CREF, other, &ref);
        return ref;
    }

private:
    vtable_type *vtable;
    union { const void *instance; storage_type storage; };
};

/**
 * @brief Checks if two wrappers differ in their content.
 * @param lhs A wrapper, either empty or not.
 * @param rhs A wrapper, either empty or not.
 * @return True if the two wrappers differ in their content, false otherwise.
 */
[[nodiscard]] inline bool operator!=(const any &lhs, const any &rhs) ENTT_NOEXCEPT {
    return !(lhs == rhs);
}

/**
 * @brief Performs type-safe access to the contained object.
 * @param data Target any object.
 * @return The element converted to the requested type.
 */
template<typename Type>
Type any_cast(const any &data) ENTT_NOEXCEPT {
    const auto * const instance = any_cast<std::remove_reference_t<Type>>(&data);
    ENTT_ASSERT(instance);
    return static_cast<Type>(*instance);
}

/*! @copydoc any_cast */
template<typename Type>
Type any_cast(any &data) ENTT_NOEXCEPT {
    // forces const on non-reference types to make them work also with wrappers for const references
    auto * const instance = any_cast<std::conditional_t<std::is_reference_v<Type>, std::remove_reference_t<Type>, const Type>>(&data);
    ENTT_ASSERT(instance);
    return static_cast<Type>(*instance);
}

/*! @copydoc any_cast */
template<typename Type>
Type any_cast(any &&data) ENTT_NOEXCEPT {
    // forces const on non-reference types to make them work also with wrappers for const references
    auto * const instance = any_cast<std::conditional_t<std::is_reference_v<Type>, std::remove_reference_t<Type>, const Type>>(&data);
    ENTT_ASSERT(instance);
    return static_cast<Type>(std::move(*instance));
}

/*! @copydoc any_cast */
template<typename Type>
const Type * any_cast(const any *data) ENTT_NOEXCEPT {
    return (data->type() == type_id<Type>() ? static_cast<const Type *>(data->data()) : nullptr);
}

/*! @copydoc any_cast */
template<typename Type>
Type * any_cast(any *data) ENTT_NOEXCEPT {
    // last attempt to make wrappers for const references return their values
    return (data->type() == type_id<Type>() ? static_cast<Type *>(static_cast<constness_as_t<any, Type> *>(data)->data()) : nullptr);
}

}

#endif
<commit_msg>doc: minor changes<commit_after>#ifndef ENTT_CORE_ANY_HPP
#define ENTT_CORE_ANY_HPP

#include <functional>
#include <new>
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "type_info.hpp"
#include "type_traits.hpp"

namespace entt {

/*! @brief A SBO friendly, type-safe container for single values of any type. */
class any {
    enum class operation { COPY, MOVE, DTOR, COMP, ADDR, CADDR, REF, CREF, TYPE };

    using storage_type = std::aligned_storage_t<sizeof(double[2]), alignof(double[2])>;
    using vtable_type = const void *(const operation, const any &, const void *);

    template<typename Type>
    static constexpr auto in_situ = sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v<Type>;

    template<typename Type>
    [[nodiscard]] static bool compare(const void *lhs, const void *rhs) {
        if constexpr(!std::is_function_v<Type> && is_equality_comparable_v<Type>) {
            return *static_cast<const Type *>(lhs) == *static_cast<const Type *>(rhs);
        } else {
            return lhs == rhs;
        }
    }

    static type_info & as_type_info(const void *data) {
        return *const_cast<type_info *>(static_cast<const type_info *>(data));
    }

    static any & as_any(const void *data) {
        return *const_cast<any *>(static_cast<const any *>(data));
    }

    template<typename Type>
    static const void * basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const any &from, [[maybe_unused]] const void *to) {
        if constexpr(std::is_void_v<Type>) {
            return nullptr;
        } else if constexpr(std::is_lvalue_reference_v<Type>) {
            using base_type = std::remove_reference_t<Type>;

            switch(op) {
            case operation::REF:
            case operation::CREF:
                as_any(to).vtable = (op == operation::REF) ? basic_vtable<Type> : basic_vtable<const base_type &>;
                [[fallthrough]];
            case operation::COPY:
            case operation::MOVE:
                as_any(to).instance = from.instance;
                [[fallthrough]];
            case operation::DTOR:
                break;
            case operation::COMP:
                return compare<std::remove_const_t<base_type>>(from.instance, to) ? to : nullptr;
            case operation::ADDR:
                return std::is_const_v<base_type> ? nullptr : from.instance;
            case operation::CADDR:
                return from.instance;
            case operation::TYPE:
                as_type_info(to) = type_id<std::remove_const_t<base_type>>();
                break;
            }
        } else if constexpr(in_situ<Type>) {
            auto *instance = const_cast<Type *>(std::launder(reinterpret_cast<const Type *>(&from.storage)));

            switch(op) {
            case operation::COPY:
                new (&as_any(to).storage) Type{std::as_const(*instance)};
                break;
            case operation::MOVE:
                new (&as_any(to).storage) Type{std::move(*instance)};
                [[fallthrough]];
            case operation::DTOR:
                instance->~Type();
                break;
            case operation::COMP:
                return compare<Type>(instance, to) ? to : nullptr;
            case operation::ADDR:
            case operation::CADDR:
                return instance;
            case operation::REF:
            case operation::CREF:
                as_any(to).vtable = (op == operation::REF) ? basic_vtable<Type &> : basic_vtable<const Type &>;
                as_any(to).instance = instance;
                break;
            case operation::TYPE:
                as_type_info(to) = type_id<Type>();
                break;
            }
        } else {
            switch(op) {
            case operation::COPY:
                as_any(to).instance = new Type{*static_cast<const Type *>(from.instance)};
                break;
            case operation::REF:
            case operation::CREF:
                as_any(to).vtable = (op == operation::REF) ? basic_vtable<Type &> : basic_vtable<const Type &>;
                [[fallthrough]];
            case operation::MOVE:
                as_any(to).instance = from.instance;
                break;
            case operation::DTOR:
                delete static_cast<const Type *>(from.instance);
                break;
            case operation::COMP:
                return compare<Type>(from.instance, to) ? to : nullptr;
            case operation::ADDR:
            case operation::CADDR:
                return from.instance;
            case operation::TYPE:
                as_type_info(to) = type_id<Type>();
                break;
            }
        }

        return nullptr;
    }

public:
    /*! @brief Default constructor. */
    any() ENTT_NOEXCEPT
        : any{std::in_place_type<void>}
    {}

    /**
     * @brief Constructs an any by directly initializing the new object.
     * @tparam Type Type of object to use to initialize the wrapper.
     * @tparam Args Types of arguments to use to construct the new instance.
     * @param args Parameters to use to construct the instance.
     */
    template<typename Type, typename... Args>
    explicit any(std::in_place_type_t<Type>, [[maybe_unused]] Args &&... args)
        : vtable{&basic_vtable<Type>},
          instance{}
    {
        if constexpr(!std::is_void_v<Type>) {
            if constexpr(std::is_lvalue_reference_v<Type>) {
                static_assert(sizeof...(Args) == 1u && (std::is_pointer_v<std::remove_reference_t<Args>> && ...));
                ENTT_ASSERT(((args != nullptr) && ...));
                instance = (args, ...);
            } else if constexpr(in_situ<Type>) {
                new (&storage) Type{std::forward<Args>(args)...};
            } else {
                instance = new Type{std::forward<Args>(args)...};
            }
        }
    }

    /**
     * @brief Constructs an any that holds an unmanaged object.
     * @tparam Type Type of object to use to initialize the wrapper.
     * @param value An instance of an object to use to initialize the wrapper.
     */
    template<typename Type>
    any(std::reference_wrapper<Type> value) ENTT_NOEXCEPT
        : any{std::in_place_type<Type &>, &value.get()}
    {}

    /**
     * @brief Constructs an any from a given value.
     * @tparam Type Type of object to use to initialize the wrapper.
     * @param value An instance of an object to use to initialize the wrapper.
     */
    template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, any>>>
    any(Type &&value)
        : any{std::in_place_type<std::remove_cv_t<std::remove_reference_t<Type>>>, std::forward<Type>(value)}
    {}

    /**
     * @brief Copy constructor.
     * @param other The instance to copy from.
     */
    any(const any &other)
        : any{}
    {
        vtable = other.vtable;
        vtable(operation::COPY, other, this);
    }

    /**
     * @brief Move constructor.
     * @param other The instance to move from.
     */
    any(any &&other) ENTT_NOEXCEPT
        : any{}
    {
        vtable = std::exchange(other.vtable, &basic_vtable<void>);
        vtable(operation::MOVE, other, this);
    }

    /*! @brief Frees the internal storage, whatever it means. */
    ~any() {
        vtable(operation::DTOR, *this, nullptr);
    }

    /**
     * @brief Assignment operator.
     * @param other The instance to assign from.
     * @return This any object.
     */
    any & operator=(any other) {
        swap(*this, other);
        return *this;
    }

    /**
     * @brief Returns the type of the contained object.
     * @return The type of the contained object, if any.
     */
    [[nodiscard]] type_info type() const ENTT_NOEXCEPT {
        type_info info;
        vtable(operation::TYPE, *this, &info);
        return info;
    }

    /**
     * @brief Returns an opaque pointer to the contained instance.
     * @return An opaque pointer the contained instance, if any.
     */
    [[nodiscard]] const void * data() const ENTT_NOEXCEPT {
        return vtable(operation::CADDR, *this, nullptr);
    }

    /*! @copydoc data */
    [[nodiscard]] void * data() ENTT_NOEXCEPT {
        return const_cast<void *>(vtable(operation::ADDR, *this, nullptr));
    }

    /**
     * @brief Replaces the contained object by creating a new instance directly.
     * @tparam Type Type of object to use to initialize the wrapper.
     * @tparam Args Types of arguments to use to construct the new instance.
     * @param args Parameters to use to construct the instance.
     */
    template<typename Type, typename... Args>
    void emplace(Args &&... args) {
        *this = any{std::in_place_type<Type>, std::forward<Args>(args)...};
    }

    /**
     * @brief Returns false if a wrapper is empty, true otherwise.
     * @return False if the wrapper is empty, true otherwise.
     */
    [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
        return !(vtable(operation::CADDR, *this, nullptr) == nullptr);
    }

    /**
     * @brief Checks if two wrappers differ in their content.
     * @param other Wrapper with which to compare.
     * @return False if the two objects differ in their content, true otherwise.
     */
    bool operator==(const any &other) const ENTT_NOEXCEPT {
        return type() == other.type() && (vtable(operation::COMP, *this, other.data()) == other.data());
    }

    /**
     * @brief Swaps two any objects.
     * @param lhs A valid any object.
     * @param rhs A valid any object.
     */
    friend void swap(any &lhs, any &rhs) {
        any tmp{};
        lhs.vtable(operation::MOVE, lhs, &tmp);
        rhs.vtable(operation::MOVE, rhs, &lhs);
        lhs.vtable(operation::MOVE, tmp, &rhs);
        std::swap(lhs.vtable, rhs.vtable);
    }

    /**
     * @brief Aliasing constructor.
     * @param other A reference to an object that isn't necessarily initialized.
     * @return An any that shares a reference to an unmanaged object.
     */
    [[nodiscard]] friend any as_ref(any &other) ENTT_NOEXCEPT {
        any ref{};
        other.vtable(operation::REF, other, &ref);
        return ref;
    }

    /*! @copydoc as_ref */
    [[nodiscard]] friend any as_ref(const any &other) ENTT_NOEXCEPT {
        any ref{};
        other.vtable(operation::CREF, other, &ref);
        return ref;
    }

private:
    vtable_type *vtable;
    union { const void *instance; storage_type storage; };
};

/**
 * @brief Checks if two wrappers differ in their content.
 * @param lhs A wrapper, either empty or not.
 * @param rhs A wrapper, either empty or not.
 * @return True if the two wrappers differ in their content, false otherwise.
 */
[[nodiscard]] inline bool operator!=(const any &lhs, const any &rhs) ENTT_NOEXCEPT {
    return !(lhs == rhs);
}

/**
 * @brief Performs type-safe access to the contained object.
 * @tparam Type Type to which conversion is required.
 * @param data Target any object.
 * @return The element converted to the requested type.
 */
template<typename Type>
Type any_cast(const any &data) ENTT_NOEXCEPT {
    const auto * const instance = any_cast<std::remove_reference_t<Type>>(&data);
    ENTT_ASSERT(instance);
    return static_cast<Type>(*instance);
}

/*! @copydoc any_cast */
template<typename Type>
Type any_cast(any &data) ENTT_NOEXCEPT {
    // forces const on non-reference types to make them work also with wrappers for const references
    auto * const instance = any_cast<std::conditional_t<std::is_reference_v<Type>, std::remove_reference_t<Type>, const Type>>(&data);
    ENTT_ASSERT(instance);
    return static_cast<Type>(*instance);
}

/*! @copydoc any_cast */
template<typename Type>
Type any_cast(any &&data) ENTT_NOEXCEPT {
    // forces const on non-reference types to make them work also with wrappers for const references
    auto * const instance = any_cast<std::conditional_t<std::is_reference_v<Type>, std::remove_reference_t<Type>, const Type>>(&data);
    ENTT_ASSERT(instance);
    return static_cast<Type>(std::move(*instance));
}

/*! @copydoc any_cast */
template<typename Type>
const Type * any_cast(const any *data) ENTT_NOEXCEPT {
    return (data->type() == type_id<Type>() ? static_cast<const Type *>(data->data()) : nullptr);
}

/*! @copydoc any_cast */
template<typename Type>
Type * any_cast(any *data) ENTT_NOEXCEPT {
    // last attempt to make wrappers for const references return their values
    return (data->type() == type_id<Type>() ? static_cast<Type *>(static_cast<constness_as_t<any, Type> *>(data)->data()) : nullptr);
}

}

#endif
<|endoftext|>


<commit_before>/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve.

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 <Python.h>
#include <fstream>
#include <vector>

#include "paddle/framework/backward.h"
#include "paddle/framework/op_registry.h"
#include "paddle/operators/net_op.h"
#include "paddle/operators/recurrent_op.h"
#include "paddle/platform/enforce.h"
#include "paddle/platform/place.h"
#include "paddle/pybind/tensor_py.h"
#include "paddle/string/to_string.h"
#include "pybind11/numpy.h"
#include "pybind11/pybind11.h"
#include "pybind11/stl.h"

namespace py = pybind11;

USE_OP(add_two);
USE_OP(onehot_cross_entropy);
USE_OP(sgd);
USE_OP(mul);
USE_OP(mean);
USE_OP(sigmoid);
USE_OP(softmax);
USE_OP(rowwise_add);
USE_OP(fill_zeros_like);
USE_NO_KERNEL_OP(recurrent);
USE_OP(gaussian_random);
USE_OP(uniform_random);
USE_OP(lookup_table);
USE_OP(scale);
USE_NO_KERNEL_OP(identity);
USE_OP(minus);
USE_OP(cos_sim);
USE_CPU_ONLY_OP(gather);
USE_CPU_ONLY_OP(scatter);

namespace paddle {
namespace framework {

using Tensor = framework::Tensor;

static size_t UniqueIntegerGenerator() {
  static std::atomic<size_t> generator;
  return generator.fetch_add(1);
}

bool IsCompileGPU() {
#ifdef PADDLE_ONLY_CPU
  return false;
#else
  return true;
#endif
}

PYBIND11_PLUGIN(core) {
  py::module m("core", "C++ core of PaddlePaddle");

  py::class_<Tensor>(m, "Tensor", py::buffer_protocol())
      .def_buffer(
          [](Tensor &self) -> py::buffer_info { return CastToPyBuffer(self); })
      .def("get_dims",
           [](const Tensor &self) { return vectorize(self.dims()); })
      .def("set_dims",
           [](Tensor &self, const std::vector<int64_t> &dim) {
             self.Resize(make_ddim(dim));
           })
      .def("alloc_float",
           [](Tensor &self, paddle::platform::GPUPlace &place) {
             self.mutable_data<float>(place);
           })
      .def("alloc_float",
           [](Tensor &self, paddle::platform::CPUPlace &place) {
             self.mutable_data<float>(place);
           })
      .def("alloc_int",
           [](Tensor &self, paddle::platform::CPUPlace &place) {
             self.mutable_data<int>(place);
           })
      .def("alloc_int",
           [](Tensor &self, paddle::platform::GPUPlace &place) {
             self.mutable_data<int>(place);
           })
      .def("set", PyCPUTensorSetFromArray<float>)
      .def("set", PyCPUTensorSetFromArray<int>)
#ifndef PADDLE_ONLY_CPU
      .def("set", PyCUDATensorSetFromArray<float>)
      .def("set", PyCUDATensorSetFromArray<int>)
#endif
      .def("shape", [](Tensor &self) { return vectorize(self.dims()); })
      .def("set_float_element",
           [](Tensor &self, size_t offset, float f) {
             // TODO(yuyang18): Only support GPU now.
             self.data<float>()[offset] = f;
           })
      .def("get_float_element", [](Tensor &self, size_t offset) -> float {
        // TODO(yuyang18): Only support GPU now.
        return self.data<float>()[offset];
      });

  py::class_<Variable>(m, "Variable", R"DOC(Variable Class.

All parameter, weight, gradient are variables in Paddle.
)DOC")
      .def("is_int", [](const Variable &var) { return var.IsType<int>(); })
      .def("set_int",
           [](Variable &var, int val) -> void { *var.GetMutable<int>() = val; })
      .def("get_int", [](const Variable &var) -> int { return var.Get<int>(); })
      .def("get_tensor",
           [](Variable &self) -> Tensor * { return self.GetMutable<Tensor>(); },
           py::return_value_policy::reference)
      .def("get_net",
           [](Variable &self) -> operators::NetOp * {
             return self.GetMutable<operators::NetOp>();
           },
           py::return_value_policy::reference);

  py::class_<Scope>(m, "Scope", "")
      .def("new_var",
           [](Scope &self, const std::string &name) -> Variable * {
             return self.NewVar(name);
           },
           py::return_value_policy::reference)
      .def("find_var", &Scope::FindVar, py::return_value_policy::reference)
      .def(py::init<>())
      .def("new_scope",
           [](Scope &self) -> Scope * { return &self.NewScope(); },
           py::return_value_policy::reference)
      .def("drop_kids", &Scope::DropKids);

  //! @note: Be careful! PyBind will return std::string as an unicode, not
  //! Python str. If you want a str object, you should cast them in Python.
  m.def("get_all_op_protos", []() -> std::vector<py::bytes> {
    std::vector<py::bytes> ret_values;

    OpInfoMap::Instance().IterAllInfo([&ret_values](const std::string &type,
                                                    const OpInfo &info) {
      if (!info.HasOpProtoAndChecker()) return;
      std::string str;
      PADDLE_ENFORCE(info.Proto().SerializeToString(&str),
                     "Serialize OpProto Error. This could be a bug of Paddle.");
      ret_values.emplace_back(str);
    });
    return ret_values;
  });
  m.def_submodule(
       "var_names",
       "The module will return special predefined variable name in Paddle")
      .def("empty", []() { return kEmptyVarName; })
      .def("temp", []() { return kTempVarName; });
  // clang-format off
  py::class_<paddle::platform::DeviceContext>(m, "DeviceContext")
      .def_static("create",
                  [](paddle::platform::CPUPlace& place)
                      -> paddle::platform::DeviceContext* {
                    return new paddle::platform::CPUDeviceContext();
                  })
      .def_static("create",
                  [](paddle::platform::GPUPlace& place)
                      -> paddle::platform::DeviceContext* {
#ifdef PADDLE_ONLY_CPU
                    PADDLE_THROW("GPUPlace is not supported in CPU device.");
#else
                    return new paddle::platform::CUDADeviceContext(place);
#endif
                  });
  // clang-format on

  py::class_<platform::GPUPlace>(m, "GPUPlace")
      .def(py::init<int>())
      .def("__str__", string::to_string<const platform::GPUPlace &>);

  py::class_<paddle::platform::CPUPlace>(m, "CPUPlace")
      .def(py::init<>())
      .def("__str__", string::to_string<const platform::CPUPlace &>);

  py::class_<OperatorBase>(m, "Operator")
      .def_static("create",
                  [](py::bytes protobin) {
                    OpDesc desc;
                    PADDLE_ENFORCE(desc.ParsePartialFromString(protobin),
                                   "Cannot parse user input to OpDesc");
                    PADDLE_ENFORCE(desc.IsInitialized(),
                                   "User OpDesc is not initialized, reason %s",
                                   desc.InitializationErrorString());
                    return OpRegistry::CreateOp(desc);
                  })
      .def("backward",
           [](const OperatorBase &forwardOp,
              const std::unordered_set<std::string> &no_grad_vars) {
             return Backward(forwardOp, no_grad_vars).release();
           })
      .def("infer_shape", &OperatorBase::InferShape)
      .def("run", &OperatorBase::Run)
      .def("type",
           [](const OperatorBase &op) -> std::string { return op.Type(); })
      .def("outputs",
           [](const OperatorBase &op)
               -> std::map<std::string, std::vector<std::string>> {
                 return op.Outputs();
               })
      .def("inputs", [](const OperatorBase &op) { return op.Inputs(); })
      .def("__str__", &OperatorBase::DebugString)
      .def("no_intermediate_outputs",
           [](const OperatorBase &op) { return op.OutputVars(false); })
      .def("support_gpu", &OperatorBase::SupportGPU);

  py::class_<operators::NetOp, OperatorBase>(m, "Net")
      .def_static("create",
                  []() -> operators::NetOp * {
                    auto *retv = new operators::NetOp;
                    retv->SetType("plain_net");
                    return retv;
                  })
      .def("append_op",
           [](operators::NetOp &self, const OperatorBase &op) {
             self.AppendOp(op);
           })
      .def("complete_add_op", &operators::NetOp::CompleteAddOp)
      .def("complete_add_op", [](std::shared_ptr<operators::NetOp> &self) {
        self->CompleteAddOp();
      });

  // recurrent_op
  py::class_<operators::RecurrentOp, OperatorBase>(m, "RecurrentOp")
      .def_static(
          "create",
          [](py::bytes protobin) -> operators::RecurrentOp * {
            OpDesc desc;
            PADDLE_ENFORCE(desc.ParsePartialFromString(protobin),
                           "Cannot parse user input to OpDesc");
            PADDLE_ENFORCE(desc.IsInitialized(),
                           "User OpDesc is not initialized, reason %s",
                           desc.InitializationErrorString());
            auto rnn_op = OpRegistry::CreateOp(desc);
            return static_cast<operators::RecurrentOp *>(rnn_op.release());
          })
      .def("set_stepnet",
           [](operators::RecurrentOp &self, const operators::NetOp &net)
               -> void { self.set_stepnet(net.Clone()); });

  m.def("unique_integer", UniqueIntegerGenerator);

  m.def("is_compile_gpu", IsCompileGPU);

  return m.ptr();
}
}  // namespace framework
}  // namespace paddle
<commit_msg>Expose LoDTensor to pybind.<commit_after>/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve.

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 <Python.h>
#include <fstream>
#include <vector>

#include "paddle/framework/backward.h"
#include "paddle/framework/lod_tensor.h"
#include "paddle/framework/op_registry.h"
#include "paddle/operators/net_op.h"
#include "paddle/operators/recurrent_op.h"
#include "paddle/platform/enforce.h"
#include "paddle/platform/place.h"
#include "paddle/pybind/tensor_py.h"
#include "paddle/string/to_string.h"
#include "pybind11/numpy.h"
#include "pybind11/pybind11.h"
#include "pybind11/stl.h"

namespace py = pybind11;

USE_OP(add_two);
USE_OP(onehot_cross_entropy);
USE_OP(sgd);
USE_OP(mul);
USE_OP(mean);
USE_OP(sigmoid);
USE_OP(softmax);
USE_OP(rowwise_add);
USE_OP(fill_zeros_like);
USE_NO_KERNEL_OP(recurrent);
USE_OP(gaussian_random);
USE_OP(uniform_random);
USE_OP(lookup_table);
USE_OP(scale);
USE_NO_KERNEL_OP(identity);
USE_OP(minus);
USE_OP(cos_sim);
USE_CPU_ONLY_OP(gather);
USE_CPU_ONLY_OP(scatter);

namespace paddle {
namespace framework {

using Tensor = framework::Tensor;
using LODTensor = framework::LODTensor;

static size_t UniqueIntegerGenerator() {
  static std::atomic<size_t> generator;
  return generator.fetch_add(1);
}

bool IsCompileGPU() {
#ifdef PADDLE_ONLY_CPU
  return false;
#else
  return true;
#endif
}

PYBIND11_PLUGIN(core) {
  py::module m("core", "C++ core of PaddlePaddle");

  py::class_<Tensor>(m, "Tensor", py::buffer_protocol())
      .def_buffer(
          [](Tensor &self) -> py::buffer_info { return CastToPyBuffer(self); })
      .def("get_dims",
           [](const Tensor &self) { return vectorize(self.dims()); })
      .def("set_dims",
           [](Tensor &self, const std::vector<int64_t> &dim) {
             self.Resize(make_ddim(dim));
           })
      .def("alloc_float",
           [](Tensor &self, paddle::platform::GPUPlace &place) {
             self.mutable_data<float>(place);
           })
      .def("alloc_float",
           [](Tensor &self, paddle::platform::CPUPlace &place) {
             self.mutable_data<float>(place);
           })
      .def("alloc_int",
           [](Tensor &self, paddle::platform::CPUPlace &place) {
             self.mutable_data<int>(place);
           })
      .def("alloc_int",
           [](Tensor &self, paddle::platform::GPUPlace &place) {
             self.mutable_data<int>(place);
           })
      .def("set", PyCPUTensorSetFromArray<float>)
      .def("set", PyCPUTensorSetFromArray<int>)
#ifndef PADDLE_ONLY_CPU
      .def("set", PyCUDATensorSetFromArray<float>)
      .def("set", PyCUDATensorSetFromArray<int>)
#endif
      .def("shape", [](Tensor &self) { return vectorize(self.dims()); })
      .def("set_float_element",
           [](Tensor &self, size_t offset, float f) {
             // TODO(yuyang18): Only support GPU now.
             self.data<float>()[offset] = f;
           })
      .def("get_float_element", [](Tensor &self, size_t offset) -> float {
        // TODO(yuyang18): Only support GPU now.
        return self.data<float>()[offset];
      });

  py::class_<LODTensor>(m, "LODTensor", R"DOC(LOD(Leval of Ddetails) Tensor.

The tensor and LOD info should be created before creating the LODTensor, then
call the set_tensor and set_lod functions to set them.

)DOC")
      .def("set_tensor",
           [](LODTensor &self, Tensor *tensor) { self.set_tensor(tensor); })
      .def("set_lod",
           [](LODTensor &self, std::vector<std::vector<size_t>> &lod) {
             self.set_lod(lod);
           })
      .def("get_tensor",
           [](LODTensor &self) -> Tensor & { return self.tensor(); },
           py::return_value_policy::reference)
      .def("get_lod", [](LODTensor &self) -> std::vector<std::vector<size_t>> {
        return self.lod();
      });

  py::class_<Variable>(m, "Variable", R"DOC(Variable Class.

All parameter, weight, gradient are variables in Paddle.
)DOC")
      .def("is_int", [](const Variable &var) { return var.IsType<int>(); })
      .def("set_int",
           [](Variable &var, int val) -> void { *var.GetMutable<int>() = val; })
      .def("get_int", [](const Variable &var) -> int { return var.Get<int>(); })
      .def("get_tensor",
           [](Variable &self) -> Tensor * { return self.GetMutable<Tensor>(); },
           py::return_value_policy::reference)
      .def("get_lod_tensor",
           [](Variable &self) -> LODTensor * {
             return self.GetMutable<LODTensor>();
           },
           py::return_value_policy::reference)
      .def("get_net",
           [](Variable &self) -> operators::NetOp * {
             return self.GetMutable<operators::NetOp>();
           },
           py::return_value_policy::reference);

  py::class_<Scope>(m, "Scope", "")
      .def("new_var",
           [](Scope &self, const std::string &name) -> Variable * {
             return self.NewVar(name);
           },
           py::return_value_policy::reference)
      .def("find_var", &Scope::FindVar, py::return_value_policy::reference)
      .def(py::init<>())
      .def("new_scope",
           [](Scope &self) -> Scope * { return &self.NewScope(); },
           py::return_value_policy::reference)
      .def("drop_kids", &Scope::DropKids);

  //! @note: Be careful! PyBind will return std::string as an unicode, not
  //! Python str. If you want a str object, you should cast them in Python.
  m.def("get_all_op_protos", []() -> std::vector<py::bytes> {
    std::vector<py::bytes> ret_values;

    OpInfoMap::Instance().IterAllInfo([&ret_values](const std::string &type,
                                                    const OpInfo &info) {
      if (!info.HasOpProtoAndChecker()) return;
      std::string str;
      PADDLE_ENFORCE(info.Proto().SerializeToString(&str),
                     "Serialize OpProto Error. This could be a bug of Paddle.");
      ret_values.emplace_back(str);
    });
    return ret_values;
  });
  m.def_submodule(
       "var_names",
       "The module will return special predefined variable name in Paddle")
      .def("empty", []() { return kEmptyVarName; })
      .def("temp", []() { return kTempVarName; });
  // clang-format off
  py::class_<paddle::platform::DeviceContext>(m, "DeviceContext")
      .def_static("create",
                  [](paddle::platform::CPUPlace& place)
                      -> paddle::platform::DeviceContext* {
                    return new paddle::platform::CPUDeviceContext();
                  })
      .def_static("create",
                  [](paddle::platform::GPUPlace& place)
                      -> paddle::platform::DeviceContext* {
#ifdef PADDLE_ONLY_CPU
                    PADDLE_THROW("GPUPlace is not supported in CPU device.");
#else
                    return new paddle::platform::CUDADeviceContext(place);
#endif
                  });
  // clang-format on

  py::class_<platform::GPUPlace>(m, "GPUPlace")
      .def(py::init<int>())
      .def("__str__", string::to_string<const platform::GPUPlace &>);

  py::class_<paddle::platform::CPUPlace>(m, "CPUPlace")
      .def(py::init<>())
      .def("__str__", string::to_string<const platform::CPUPlace &>);

  py::class_<OperatorBase>(m, "Operator")
      .def_static("create",
                  [](py::bytes protobin) {
                    OpDesc desc;
                    PADDLE_ENFORCE(desc.ParsePartialFromString(protobin),
                                   "Cannot parse user input to OpDesc");
                    PADDLE_ENFORCE(desc.IsInitialized(),
                                   "User OpDesc is not initialized, reason %s",
                                   desc.InitializationErrorString());
                    return OpRegistry::CreateOp(desc);
                  })
      .def("backward",
           [](const OperatorBase &forwardOp,
              const std::unordered_set<std::string> &no_grad_vars) {
             return Backward(forwardOp, no_grad_vars).release();
           })
      .def("infer_shape", &OperatorBase::InferShape)
      .def("run", &OperatorBase::Run)
      .def("type",
           [](const OperatorBase &op) -> std::string { return op.Type(); })
      .def("outputs",
           [](const OperatorBase &op)
               -> std::map<std::string, std::vector<std::string>> {
                 return op.Outputs();
               })
      .def("inputs", [](const OperatorBase &op) { return op.Inputs(); })
      .def("__str__", &OperatorBase::DebugString)
      .def("no_intermediate_outputs",
           [](const OperatorBase &op) { return op.OutputVars(false); })
      .def("support_gpu", &OperatorBase::SupportGPU);

  py::class_<operators::NetOp, OperatorBase>(m, "Net")
      .def_static("create",
                  []() -> operators::NetOp * {
                    auto *retv = new operators::NetOp;
                    retv->SetType("plain_net");
                    return retv;
                  })
      .def("append_op",
           [](operators::NetOp &self, const OperatorBase &op) {
             self.AppendOp(op);
           })
      .def("complete_add_op", &operators::NetOp::CompleteAddOp)
      .def("complete_add_op", [](std::shared_ptr<operators::NetOp> &self) {
        self->CompleteAddOp();
      });

  // recurrent_op
  py::class_<operators::RecurrentOp, OperatorBase>(m, "RecurrentOp")
      .def_static(
          "create",
          [](py::bytes protobin) -> operators::RecurrentOp * {
            OpDesc desc;
            PADDLE_ENFORCE(desc.ParsePartialFromString(protobin),
                           "Cannot parse user input to OpDesc");
            PADDLE_ENFORCE(desc.IsInitialized(),
                           "User OpDesc is not initialized, reason %s",
                           desc.InitializationErrorString());
            auto rnn_op = OpRegistry::CreateOp(desc);
            return static_cast<operators::RecurrentOp *>(rnn_op.release());
          })
      .def("set_stepnet",
           [](operators::RecurrentOp &self, const operators::NetOp &net)
               -> void { self.set_stepnet(net.Clone()); });

  m.def("unique_integer", UniqueIntegerGenerator);

  m.def("is_compile_gpu", IsCompileGPU);

  return m.ptr();
}
}  // namespace framework
}  // namespace paddle
<|endoftext|>


<commit_before>#ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_SERVICE_MAP_HPP
#define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_SERVICE_MAP_HPP

#include "traits.hpp"

namespace kgr {

/*
 * This class is the type when referring to a group of maps.
 * This can be used with kgr::autocall and mapped operator services.
 */
template<typename...>
struct map {};

/*
 * This is the type you receive in a service_map function.
 * This was previously kgr::Map in kangaru 3.x.y
 */
template<typename = void>
struct map_t {};

namespace detail {

/*
 * Trait that determines if a type is a map type.
 */
template<typename T>
struct is_map : std::false_type {};

/*
 * This is the specialization when T is a kgr::map.
 */
template<typename... Ts>
struct is_map<map<Ts...>> : std::true_type {};

/*
 * Trait that returns the return type of the service_map function
 */
template<typename, typename, typename = void>
struct map_result {};

/*
* Specialization of map_result when service_map() exist for S with the map M.
*/
template<typename S, typename M>
struct map_result<S, M, void_t<decltype(service_map(std::declval<S>(), std::declval<M>()))>> {
	using type = decltype(service_map(std::declval<S>(), std::declval<M>()));
};

/*
* Specialization of map_result when service_map() exist for S with no map.
*/
template<typename S>
struct map_result<S, void, void_t<decltype(service_map(std::declval<S>()))>> {
	using type = decltype(service_map(std::declval<S>()));
};

/*
 * Alias for map_result::type
 */
template<typename S, typename M>
using map_result_t = typename map_result<S, M>::type;

/*
 * This is a trait that tell if the service S is mapped in M
 */
template<typename, typename, typename = void>
struct is_mapped : std::false_type {};

/*
 * Trait that check if the result of the map is a valid service that forwards something convertible to S.
 */
template<typename M, typename S>
using is_valid_entry = std::is_convertible<service_type<map_result_t<S, M>>, S>;

/*
 * Specialization when map M maps the service S
 */
template<typename M, typename S>
struct is_mapped<map<M>, S, enable_if_t<is_valid_entry<map_t<M>, S>::value>> : std::true_type {};

/*
 * Specialization for an empty map, equivalent for a void map
 */
template<typename S>
struct is_mapped<map<>, S, enable_if_t<is_valid_entry<map_t<>, S>::value>> : std::true_type {};

/*
 * Specialization when no map is specified.
 */
template<typename S>
struct is_mapped<void, S, enable_if_t<is_valid_entry<void, S>::value>> : std::true_type {};

/*
 * Trait that extranct the mapped service type given the parameter and a group of maps.
 */
template<typename, typename, typename = void>
struct map_entry {};

/*
 * When no service map entry has been found for First, continue with the next map
 */
template<typename First, typename... Maps, typename P>
struct map_entry<map<First, Maps...>, P, enable_if_t<!is_mapped<map<First>, P>::value>> : map_entry<map<Maps...>, P> {};

/*
 * Case when a map has been found.
 * We proceed to create an alias for the mapped definition.
 */
template<typename First, typename... Maps, typename P>
struct map_entry<map<First, Maps...>, P, enable_if_t<is_mapped<map<First>, P>::value>> {
	using mapped_service = map_result_t<P, map_t<First>>;
};

/*
 * Case when a map has been found for an empty map.
 * We proceed to create an alias for the mapped definition.
 */
template<typename P>
struct map_entry<map<>, P, enable_if_t<is_mapped<map<>, P>::value>> {
	using mapped_service = map_result_t<P, map_t<>>;
};

/*
 * Case when the empty map does not yeild any mapped service.
 * We proceed to extend an try the void map.
 */
template<typename P>
struct map_entry<map<>, P, enable_if_t<!is_mapped<map<>, P>::value>> : map_entry<void, P> {};

/*
 * Case when the only service map found has no map specified at all.
 */
template<typename P>
struct map_entry<void, P, enable_if_t<is_mapped<void, P>::value>> {
	using mapped_service = map_result_t<P, void>;
};

/*
 * Trait to tell if a parameter is mapped using the map group Map.
 */
template<typename Map, typename T, typename = void>
struct is_complete_map : std::false_type {};

/*
* Specialization of is_complete_map when the service T is mapped in Map
*/
template<typename Map, typename T>
struct is_complete_map<Map, T, void_t<typename detail::map_entry<Map, T>::mapped_service>> : std::true_type {};

} // namespace detail

/*
 * This is an alias for the mapped definition in a service map.
 */
template<typename T, typename Map = map<>>
using mapped_service_t = typename detail::map_entry<Map, T>::mapped_service;

} // namespace kgr

#endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_SERVICE_MAP_HPP
<commit_msg>Fix service map not working with non reference type and non copiable services<commit_after>#ifndef KGR_KANGARU_INCLUDE_KANGARU_DETAIL_SERVICE_MAP_HPP
#define KGR_KANGARU_INCLUDE_KANGARU_DETAIL_SERVICE_MAP_HPP

#include "traits.hpp"

namespace kgr {

/*
 * This class is the type when referring to a group of maps.
 * This can be used with kgr::autocall and mapped operator services.
 */
template<typename...>
struct map {};

/*
 * This is the type you receive in a service_map function.
 * This was previously kgr::Map in kangaru 3.x.y
 */
template<typename = void>
struct map_t {};

namespace detail {

/*
 * Trait that determines if a type is a map type.
 */
template<typename T>
struct is_map : std::false_type {};

/*
 * This is the specialization when T is a kgr::map.
 */
template<typename... Ts>
struct is_map<map<Ts...>> : std::true_type {};

/*
 * Trait that returns the return type of the service_map function
 */
template<typename, typename, typename = void>
struct map_result {};

/*
* Specialization of map_result when service_map() exist for S with the map M.
*/
template<typename S, typename M>
struct map_result<S, M, void_t<decltype(service_map(std::declval<S>(), std::declval<M>()))>> {
	using type = decltype(service_map(std::declval<S>(), std::declval<M>()));
};

/*
* Specialization of map_result when service_map() exist for S with no map.
*/
template<typename S>
struct map_result<S, void, void_t<decltype(service_map(std::declval<S>()))>> {
	using type = decltype(service_map(std::declval<S>()));
};

/*
 * Alias for map_result::type
 */
template<typename S, typename M>
using map_result_t = typename map_result<S, M>::type;

/*
 * This is a trait that tell if the service S is mapped in M
 */
template<typename, typename, typename = void>
struct is_mapped : std::false_type {};

/*
 * Trait that check if the result of the map is a valid service that forwards something convertible to S.
 */
template<typename M, typename S>
using is_valid_entry = std::integral_constant<bool,
	std::is_convertible<typename std::remove_cv<service_type<map_result_t<S, M>>>::type&&, S&>::value ||
	std::is_convertible<typename std::remove_cv<service_type<map_result_t<S, M>>>::type&&, S&&>::value
>;

/*
 * Specialization when map M maps the service S
 */
template<typename M, typename S>
struct is_mapped<map<M>, S, enable_if_t<is_valid_entry<map_t<M>, S>::value>> : std::true_type {};

/*
 * Specialization for an empty map, equivalent for a void map
 */
template<typename S>
struct is_mapped<map<>, S, enable_if_t<is_valid_entry<map_t<>, S>::value>> : std::true_type {};

/*
 * Specialization when no map is specified.
 */
template<typename S>
struct is_mapped<void, S, enable_if_t<is_valid_entry<void, S>::value>> : std::true_type {};

/*
 * Trait that extranct the mapped service type given the parameter and a group of maps.
 */
template<typename, typename, typename = void>
struct map_entry {};

/*
 * When no service map entry has been found for First, continue with the next map
 */
template<typename First, typename... Maps, typename P>
struct map_entry<map<First, Maps...>, P, enable_if_t<!is_mapped<map<First>, P>::value>> : map_entry<map<Maps...>, P> {};

/*
 * Case when a map has been found.
 * We proceed to create an alias for the mapped definition.
 */
template<typename First, typename... Maps, typename P>
struct map_entry<map<First, Maps...>, P, enable_if_t<is_mapped<map<First>, P>::value>> {
	using mapped_service = map_result_t<P, map_t<First>>;
};

/*
 * Case when a map has been found for an empty map.
 * We proceed to create an alias for the mapped definition.
 */
template<typename P>
struct map_entry<map<>, P, enable_if_t<is_mapped<map<>, P>::value>> {
	using mapped_service = map_result_t<P, map_t<>>;
};

/*
 * Case when the empty map does not yeild any mapped service.
 * We proceed to extend an try the void map.
 */
template<typename P>
struct map_entry<map<>, P, enable_if_t<!is_mapped<map<>, P>::value>> : map_entry<void, P> {};

/*
 * Case when the only service map found has no map specified at all.
 */
template<typename P>
struct map_entry<void, P, enable_if_t<is_mapped<void, P>::value>> {
	using mapped_service = map_result_t<P, void>;
};

/*
 * Trait to tell if a parameter is mapped using the map group Map.
 */
template<typename Map, typename T, typename = void>
struct is_complete_map : std::false_type {};

/*
* Specialization of is_complete_map when the service T is mapped in Map
*/
template<typename Map, typename T>
struct is_complete_map<Map, T, void_t<typename detail::map_entry<Map, T>::mapped_service>> : std::true_type {};

} // namespace detail

/*
 * This is an alias for the mapped definition in a service map.
 */
template<typename T, typename Map = map<>>
using mapped_service_t = typename detail::map_entry<Map, T>::mapped_service;

} // namespace kgr

#endif // KGR_KANGARU_INCLUDE_KANGARU_DETAIL_SERVICE_MAP_HPP
<|endoftext|>