<commit_before>#define BOOST_TEST_MODULE basic
#define BOOST_LOG_DYN_LINK
#define BOOST_TEST_DYN_LINK

#include <future>
#include <iostream>
#include <sstream>
#include <thread>
#include <boost/log/trivial.hpp>
#include <boost/test/unit_test.hpp>

#include <curl/curl.h>

#include "chunky.hpp"

using namespace chunky;
#define LOG(LEVEL) BOOST_LOG_TRIVIAL(LEVEL)

class Server {
   boost::asio::io_service io_;
   boost::asio::ip::tcp::acceptor acceptor_;
   std::thread t_;

public:
   typedef boost::system::error_code error_code;

   Server()
      : acceptor_(io_, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 0)) {
      using namespace std::placeholders;
      LOG(info) << "listening on port " << port();
      TCP::async_connect(acceptor_, std::bind(&Server::acceptHandler, this, _1, _2));
      std::thread([this]() { io_.run(); }).swap(t_);
   }

   virtual ~Server() {
      t_.join();
   }

   virtual unsigned short port() {
      return acceptor_.local_endpoint().port();
   }

   virtual void acceptHandler(const error_code& error, const std::shared_ptr<TCP>& tcp) {
      if (error) {
         LOG(error) << "accept" << error.message();
         return;
      }

      serve(tcp);
   }

   virtual void serve(const std::shared_ptr<TCP>& tcp) {
      using namespace std::placeholders;
      HTTP::async_create(tcp, std::bind(&Server::serveHandlerWrapper, this, _1, _2));
   }

   virtual void serveHandlerWrapper(const error_code& error, const std::shared_ptr<HTTP>& http) {
      if (error) {
         LOG(info) << error.message();
         return;
      }

      LOG(info) << boost::format("%s %s")
         % http->request_method()
         % http->request_resource();

      serveHandler(http);
   }

   virtual void serveHandler(const std::shared_ptr<HTTP>& http) = 0;
};

static size_t writeCB(char *s, size_t size, size_t n, void *data) {
   auto os = static_cast<std::ostream*>(data);
   os->write(s, size*n);
   return size*n;
}

static size_t readCB(char *s, size_t size, size_t n, void *data) {
   auto is = static_cast<std::istream*>(data);
   is->read(s, size*n);
   return is->gcount();
}

BOOST_AUTO_TEST_CASE(Minimal) {
   class MyServer : public Server {
      void serveHandler(const std::shared_ptr<HTTP>& http) {
         BOOST_CHECK_EQUAL(http->request_method(), "GET");
         BOOST_CHECK_EQUAL(http->request_resource(), "/Minimal");

         http->response_status() = 200;
         http->response_headers()["Content-Type"] = "text/plain";
         http->finish();

         serve(http->stream());
      }
   } server;

   CURL *curl = curl_easy_init();
   BOOST_REQUIRE(curl);

   auto url = (boost::format("http://localhost:%d/Minimal") % server.port()).str();
   curl_easy_setopt(curl, CURLOPT_URL, url.c_str());

   auto status = curl_easy_perform(curl);
   BOOST_CHECK_EQUAL(status, CURLE_OK);
   curl_easy_cleanup(curl);
}

static const std::string upData = "foo bar baz";
static const std::string dnData = "how now brown cow";

BOOST_AUTO_TEST_CASE(ContentLength) {
   class MyServer : public Server {
      void serveHandler(const std::shared_ptr<HTTP>& http) {
         BOOST_CHECK_EQUAL(http->request_method(), "PUT");
         BOOST_CHECK_EQUAL(http->request_resource(), "/ContentLength");

         boost::asio::streambuf body;
         boost::system::error_code error;
         boost::asio::read(*http, body, error);
         std::string s(boost::asio::buffers_begin(body.data()), boost::asio::buffers_end(body.data()));
         BOOST_CHECK_EQUAL(s, upData);

         http->response_status() = 200;
         http->response_headers()["Content-Type"] = "text/plain";

         http->response_headers()["Content-Length"] = std::to_string(dnData.size());
         for (char c : dnData)
            boost::asio::write(*http, boost::asio::buffer(&c, 1));

         http->finish();

         serve(http->stream());
      }
   } server;

   CURL *curl = curl_easy_init();
   BOOST_REQUIRE(curl);

   for (int i = 0; i < 8; ++i) {
      curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);

      auto url = (boost::format("http://localhost:%d/ContentLength") % server.port()).str();
      curl_easy_setopt(curl, CURLOPT_URL, url.c_str());

      curl_slist* headers = curl_slist_append(nullptr, "Expect:");
      curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

      std::istringstream is(upData);
      curl_easy_setopt(curl, CURLOPT_READFUNCTION, &readCB);
      curl_easy_setopt(curl, CURLOPT_READDATA, &is);
      curl_easy_setopt(curl, CURLOPT_INFILESIZE, static_cast<long>(upData.size()));

      std::ostringstream os;
      curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeCB);
      curl_easy_setopt(curl, CURLOPT_WRITEDATA, &os);

      auto status = curl_easy_perform(curl);
      BOOST_CHECK_EQUAL(status, CURLE_OK);
      BOOST_CHECK_EQUAL(os.str(), dnData);

      curl_slist_free_all(headers);
   }

   curl_easy_cleanup(curl);
}

BOOST_AUTO_TEST_CASE(Chunked) {
   class MyServer : public Server {
      void serveHandler(const std::shared_ptr<HTTP>& http) {
         BOOST_CHECK_EQUAL(http->request_method(), "PUT");
         BOOST_CHECK_EQUAL(http->request_resource(), "/Chunked");

         boost::asio::streambuf body;
         boost::system::error_code error;
         boost::asio::read(*http, body, error);
         std::string s(boost::asio::buffers_begin(body.data()), boost::asio::buffers_end(body.data()));
         BOOST_CHECK_EQUAL(s, upData);

         http->response_status() = 200;
         http->response_headers()["Content-Type"] = "text/plain";

         for (char c : dnData)
            boost::asio::write(*http, boost::asio::buffer(&c, 1));

         http->finish();

         serve(http->stream());
      }
   } server;

   CURL *curl = curl_easy_init();
   BOOST_REQUIRE(curl);

   for (int i = 0; i < 8; ++i) {
      curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);

      auto url = (boost::format("http://localhost:%d/Chunked") % server.port()).str();
      curl_easy_setopt(curl, CURLOPT_URL, url.c_str());

      curl_slist* headers = curl_slist_append(nullptr, "Expect:");
      headers = curl_slist_append(headers, "Transfer-Encoding: chunked");
      curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

      std::istringstream is(upData);
      curl_easy_setopt(curl, CURLOPT_READFUNCTION, &readCB);
      curl_easy_setopt(curl, CURLOPT_READDATA, &is);
      curl_easy_setopt(curl, CURLOPT_INFILESIZE, static_cast<long>(upData.size()));

      std::ostringstream os;
      curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeCB);
      curl_easy_setopt(curl, CURLOPT_WRITEDATA, &os);

      auto status = curl_easy_perform(curl);
      BOOST_CHECK_EQUAL(status, CURLE_OK);
      BOOST_CHECK_EQUAL(os.str(), dnData);

      curl_slist_free_all(headers);
   }

   curl_easy_cleanup(curl);
}

BOOST_AUTO_TEST_CASE(AsyncContentLength) {
   class MyServer : public Server {
      void serveHandler(const std::shared_ptr<HTTP>& http) {
         BOOST_CHECK_EQUAL(http->request_method(), "PUT");
         BOOST_CHECK_EQUAL(http->request_resource(), "/AsyncContentLength");

         auto body = std::make_shared<boost::asio::streambuf>();
         boost::asio::async_read(
            *http, *body,
            [=](const error_code& error, size_t nBytes) {
               std::string s(boost::asio::buffers_begin(body->data()), boost::asio::buffers_end(body->data()));
               BOOST_CHECK_EQUAL(s, upData);

               http->response_status() = 200;
               http->response_headers()["Content-Type"] = "text/plain";

               http->response_headers()["Content-Length"] = std::to_string(dnData.size());
               for (char c : dnData)
                  boost::asio::write(*http, boost::asio::buffer(&c, 1));

               http->async_finish([=](const error_code& error) {
                     if (error) {
                        LOG(error) << error.message();
                        return;
                     }
                     serve(http->stream());
                  });
            });
      }
   } server;

   CURL *curl = curl_easy_init();
   BOOST_REQUIRE(curl);

   for (int i = 0; i < 8; ++i) {
      curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);

      auto url = (boost::format("http://localhost:%d/AsyncContentLength") % server.port()).str();
      curl_easy_setopt(curl, CURLOPT_URL, url.c_str());

      curl_slist* headers = curl_slist_append(nullptr, "Expect:");
      curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

      std::istringstream is(upData);
      curl_easy_setopt(curl, CURLOPT_READFUNCTION, &readCB);
      curl_easy_setopt(curl, CURLOPT_READDATA, &is);
      curl_easy_setopt(curl, CURLOPT_INFILESIZE, static_cast<long>(upData.size()));

      std::ostringstream os;
      curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeCB);
      curl_easy_setopt(curl, CURLOPT_WRITEDATA, &os);

      auto status = curl_easy_perform(curl);
      BOOST_CHECK_EQUAL(status, CURLE_OK);
      BOOST_CHECK_EQUAL(os.str(), dnData);

      curl_slist_free_all(headers);
   }

   curl_easy_cleanup(curl);
}

BOOST_AUTO_TEST_CASE(AsyncChunked) {
   class MyServer : public Server {
      void serveHandler(const std::shared_ptr<HTTP>& http) {
         BOOST_CHECK_EQUAL(http->request_method(), "PUT");
         BOOST_CHECK_EQUAL(http->request_resource(), "/AsyncChunked");

         auto body = std::make_shared<boost::asio::streambuf>();
         boost::asio::async_read(
            *http, *body,
            [=](const error_code& error, size_t nBytes) {
               std::string s(boost::asio::buffers_begin(body->data()), boost::asio::buffers_end(body->data()));
               BOOST_CHECK_EQUAL(s, upData);

               http->response_status() = 200;
               http->response_headers()["Content-Type"] = "text/plain";

               for (char c : dnData)
                  boost::asio::write(*http, boost::asio::buffer(&c, 1));

               http->async_finish([=](const error_code& error) {
                     if (error) {
                        LOG(error) << error.message();
                        return;
                     }
                     serve(http->stream());
                  });
            });
      }
   } server;

   CURL *curl = curl_easy_init();
   BOOST_REQUIRE(curl);

   for (int i = 0; i < 8; ++i) {
      curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);

      auto url = (boost::format("http://localhost:%d/AsyncChunked") % server.port()).str();
      curl_easy_setopt(curl, CURLOPT_URL, url.c_str());

      curl_slist* headers = curl_slist_append(nullptr, "Expect:");
      headers = curl_slist_append(headers, "Transfer-Encoding: chunked");
      curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

      std::istringstream is(upData);
      curl_easy_setopt(curl, CURLOPT_READFUNCTION, &readCB);
      curl_easy_setopt(curl, CURLOPT_READDATA, &is);
      curl_easy_setopt(curl, CURLOPT_INFILESIZE, static_cast<long>(upData.size()));

      std::ostringstream os;
      curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeCB);
      curl_easy_setopt(curl, CURLOPT_WRITEDATA, &os);

      auto status = curl_easy_perform(curl);
      BOOST_CHECK_EQUAL(status, CURLE_OK);
      BOOST_CHECK_EQUAL(os.str(), dnData);

      curl_slist_free_all(headers);
   }

   curl_easy_cleanup(curl);
}
<commit_msg>Use asynchronous writes.<commit_after>#define BOOST_TEST_MODULE basic
#define BOOST_LOG_DYN_LINK
#define BOOST_TEST_DYN_LINK

#include <future>
#include <iostream>
#include <sstream>
#include <thread>
#include <boost/log/trivial.hpp>
#include <boost/test/unit_test.hpp>

#include <curl/curl.h>

#include "chunky.hpp"

using namespace chunky;
#define LOG(LEVEL) BOOST_LOG_TRIVIAL(LEVEL)

class Server {
   boost::asio::io_service io_;
   boost::asio::ip::tcp::acceptor acceptor_;
   std::thread t_;

public:
   typedef boost::system::error_code error_code;

   Server()
      : acceptor_(io_, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 0)) {
      using namespace std::placeholders;
      LOG(info) << "listening on port " << port();
      TCP::async_connect(acceptor_, std::bind(&Server::acceptHandler, this, _1, _2));
      std::thread([this]() { io_.run(); }).swap(t_);
   }

   virtual ~Server() {
      t_.join();
   }

   virtual unsigned short port() {
      return acceptor_.local_endpoint().port();
   }

   virtual void acceptHandler(const error_code& error, const std::shared_ptr<TCP>& tcp) {
      if (error) {
         LOG(error) << "accept" << error.message();
         return;
      }

      serve(tcp);
   }

   virtual void serve(const std::shared_ptr<TCP>& tcp) {
      using namespace std::placeholders;
      HTTP::async_create(tcp, std::bind(&Server::serveHandlerWrapper, this, _1, _2));
   }

   virtual void serveHandlerWrapper(const error_code& error, const std::shared_ptr<HTTP>& http) {
      if (error) {
         LOG(info) << error.message();
         return;
      }

      LOG(info) << boost::format("%s %s")
         % http->request_method()
         % http->request_resource();

      serveHandler(http);
   }

   virtual void serveHandler(const std::shared_ptr<HTTP>& http) = 0;
};

static size_t writeCB(char *s, size_t size, size_t n, void *data) {
   auto os = static_cast<std::ostream*>(data);
   os->write(s, size*n);
   return size*n;
}

static size_t readCB(char *s, size_t size, size_t n, void *data) {
   auto is = static_cast<std::istream*>(data);
   is->read(s, size*n);
   return is->gcount();
}

BOOST_AUTO_TEST_CASE(Minimal) {
   class MyServer : public Server {
      void serveHandler(const std::shared_ptr<HTTP>& http) {
         BOOST_CHECK_EQUAL(http->request_method(), "GET");
         BOOST_CHECK_EQUAL(http->request_resource(), "/Minimal");

         http->response_status() = 200;
         http->response_headers()["Content-Type"] = "text/plain";
         http->finish();

         serve(http->stream());
      }
   } server;

   CURL *curl = curl_easy_init();
   BOOST_REQUIRE(curl);

   auto url = (boost::format("http://localhost:%d/Minimal") % server.port()).str();
   curl_easy_setopt(curl, CURLOPT_URL, url.c_str());

   auto status = curl_easy_perform(curl);
   BOOST_CHECK_EQUAL(status, CURLE_OK);
   curl_easy_cleanup(curl);
}

static const std::string upData = "foo bar baz";
static const std::string dnData = "how now brown cow";

BOOST_AUTO_TEST_CASE(ContentLength) {
   class MyServer : public Server {
      void serveHandler(const std::shared_ptr<HTTP>& http) {
         BOOST_CHECK_EQUAL(http->request_method(), "PUT");
         BOOST_CHECK_EQUAL(http->request_resource(), "/ContentLength");

         boost::asio::streambuf body;
         boost::system::error_code error;
         boost::asio::read(*http, body, error);
         std::string s(boost::asio::buffers_begin(body.data()), boost::asio::buffers_end(body.data()));
         BOOST_CHECK_EQUAL(s, upData);

         http->response_status() = 200;
         http->response_headers()["Content-Type"] = "text/plain";

         http->response_headers()["Content-Length"] = std::to_string(dnData.size());
         for (char c : dnData)
            boost::asio::write(*http, boost::asio::buffer(&c, 1));

         http->finish();

         serve(http->stream());
      }
   } server;

   CURL *curl = curl_easy_init();
   BOOST_REQUIRE(curl);

   for (int i = 0; i < 8; ++i) {
      curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);

      auto url = (boost::format("http://localhost:%d/ContentLength") % server.port()).str();
      curl_easy_setopt(curl, CURLOPT_URL, url.c_str());

      curl_slist* headers = curl_slist_append(nullptr, "Expect:");
      curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

      std::istringstream is(upData);
      curl_easy_setopt(curl, CURLOPT_READFUNCTION, &readCB);
      curl_easy_setopt(curl, CURLOPT_READDATA, &is);
      curl_easy_setopt(curl, CURLOPT_INFILESIZE, static_cast<long>(upData.size()));

      std::ostringstream os;
      curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeCB);
      curl_easy_setopt(curl, CURLOPT_WRITEDATA, &os);

      auto status = curl_easy_perform(curl);
      BOOST_CHECK_EQUAL(status, CURLE_OK);
      BOOST_CHECK_EQUAL(os.str(), dnData);

      curl_slist_free_all(headers);
   }

   curl_easy_cleanup(curl);
}

BOOST_AUTO_TEST_CASE(Chunked) {
   class MyServer : public Server {
      void serveHandler(const std::shared_ptr<HTTP>& http) {
         BOOST_CHECK_EQUAL(http->request_method(), "PUT");
         BOOST_CHECK_EQUAL(http->request_resource(), "/Chunked");

         boost::asio::streambuf body;
         boost::system::error_code error;
         boost::asio::read(*http, body, error);
         std::string s(boost::asio::buffers_begin(body.data()), boost::asio::buffers_end(body.data()));
         BOOST_CHECK_EQUAL(s, upData);

         http->response_status() = 200;
         http->response_headers()["Content-Type"] = "text/plain";

         for (char c : dnData)
            boost::asio::write(*http, boost::asio::buffer(&c, 1));

         http->finish();

         serve(http->stream());
      }
   } server;

   CURL *curl = curl_easy_init();
   BOOST_REQUIRE(curl);

   for (int i = 0; i < 8; ++i) {
      curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);

      auto url = (boost::format("http://localhost:%d/Chunked") % server.port()).str();
      curl_easy_setopt(curl, CURLOPT_URL, url.c_str());

      curl_slist* headers = curl_slist_append(nullptr, "Expect:");
      headers = curl_slist_append(headers, "Transfer-Encoding: chunked");
      curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

      std::istringstream is(upData);
      curl_easy_setopt(curl, CURLOPT_READFUNCTION, &readCB);
      curl_easy_setopt(curl, CURLOPT_READDATA, &is);
      curl_easy_setopt(curl, CURLOPT_INFILESIZE, static_cast<long>(upData.size()));

      std::ostringstream os;
      curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeCB);
      curl_easy_setopt(curl, CURLOPT_WRITEDATA, &os);

      auto status = curl_easy_perform(curl);
      BOOST_CHECK_EQUAL(status, CURLE_OK);
      BOOST_CHECK_EQUAL(os.str(), dnData);

      curl_slist_free_all(headers);
   }

   curl_easy_cleanup(curl);
}

BOOST_AUTO_TEST_CASE(AsyncContentLength) {
   class MyServer : public Server {
      void serveHandler(const std::shared_ptr<HTTP>& http) {
         BOOST_CHECK_EQUAL(http->request_method(), "PUT");
         BOOST_CHECK_EQUAL(http->request_resource(), "/AsyncContentLength");

         auto body = std::make_shared<boost::asio::streambuf>();
         boost::asio::async_read(
            *http, *body,
            [=](const error_code& error, size_t nBytes) {
               if (error)
                  LOG(error) << error.message();
               std::string s(boost::asio::buffers_begin(body->data()), boost::asio::buffers_end(body->data()));
               BOOST_CHECK_EQUAL(s, upData);

               http->response_status() = 200;
               http->response_headers()["Content-Type"] = "text/plain";

               http->response_headers()["Content-Length"] = std::to_string(dnData.size());
               boost::asio::async_write(
                  *http, boost::asio::buffer(dnData),
                  [=](const error_code& error, size_t nBytes) {
                     BOOST_CHECK_EQUAL(nBytes, dnData.size());
                     if (error) {
                        LOG(error) << error.message();
                        return;
                     }

                     http->async_finish([=](const error_code& error) {
                           if (error) {
                              LOG(error) << error.message();
                              return;
                           }
                           serve(http->stream());
                        });
                  });
            });
      }
   } server;

   CURL *curl = curl_easy_init();
   BOOST_REQUIRE(curl);

   for (int i = 0; i < 8; ++i) {
      curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);

      auto url = (boost::format("http://localhost:%d/AsyncContentLength") % server.port()).str();
      curl_easy_setopt(curl, CURLOPT_URL, url.c_str());

      curl_slist* headers = curl_slist_append(nullptr, "Expect:");
      curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

      std::istringstream is(upData);
      curl_easy_setopt(curl, CURLOPT_READFUNCTION, &readCB);
      curl_easy_setopt(curl, CURLOPT_READDATA, &is);
      curl_easy_setopt(curl, CURLOPT_INFILESIZE, static_cast<long>(upData.size()));

      std::ostringstream os;
      curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeCB);
      curl_easy_setopt(curl, CURLOPT_WRITEDATA, &os);

      auto status = curl_easy_perform(curl);
      BOOST_CHECK_EQUAL(status, CURLE_OK);
      BOOST_CHECK_EQUAL(os.str(), dnData);

      curl_slist_free_all(headers);
   }

   curl_easy_cleanup(curl);
}

BOOST_AUTO_TEST_CASE(AsyncChunked) {
   class MyServer : public Server {
      void serveHandler(const std::shared_ptr<HTTP>& http) {
         BOOST_CHECK_EQUAL(http->request_method(), "PUT");
         BOOST_CHECK_EQUAL(http->request_resource(), "/AsyncChunked");

         auto body = std::make_shared<boost::asio::streambuf>();
         boost::asio::async_read(
            *http, *body,
            [=](const error_code& error, size_t nBytes) {
               if (error)
                  LOG(error) << error.message();

               std::string s(boost::asio::buffers_begin(body->data()), boost::asio::buffers_end(body->data()));
               BOOST_CHECK_EQUAL(s, upData);

               http->response_status() = 200;
               http->response_headers()["Content-Type"] = "text/plain";

               boost::asio::async_write(
                  *http, boost::asio::buffer(dnData),
                  [=](const error_code& error, size_t nBytes) {
                     BOOST_CHECK_EQUAL(nBytes, dnData.size());
                     if (error) {
                        LOG(error) << error.message();
                        return;
                     }

                     http->async_finish([=](const error_code& error) {
                           if (error) {
                              LOG(error) << error.message();
                              return;
                           }
                           serve(http->stream());
                        });
                  });
            });
      }
   } server;

   CURL *curl = curl_easy_init();
   BOOST_REQUIRE(curl);

   for (int i = 0; i < 8; ++i) {
      curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);

      auto url = (boost::format("http://localhost:%d/AsyncChunked") % server.port()).str();
      curl_easy_setopt(curl, CURLOPT_URL, url.c_str());

      curl_slist* headers = curl_slist_append(nullptr, "Expect:");
      headers = curl_slist_append(headers, "Transfer-Encoding: chunked");
      curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

      std::istringstream is(upData);
      curl_easy_setopt(curl, CURLOPT_READFUNCTION, &readCB);
      curl_easy_setopt(curl, CURLOPT_READDATA, &is);
      curl_easy_setopt(curl, CURLOPT_INFILESIZE, static_cast<long>(upData.size()));

      std::ostringstream os;
      curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeCB);
      curl_easy_setopt(curl, CURLOPT_WRITEDATA, &os);

      auto status = curl_easy_perform(curl);
      BOOST_CHECK_EQUAL(status, CURLE_OK);
      BOOST_CHECK_EQUAL(os.str(), dnData);

      curl_slist_free_all(headers);
   }

   curl_easy_cleanup(curl);
}
<|endoftext|>


<commit_before>//  Copyright (c) 2013, Facebook, 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.
//
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <list>

#include "rocksdb/cache.h"
#include "port/port.h"
#include "util/hash.h"
#include "util/mutexlock.h"

namespace rocksdb {

Cache::~Cache() {
}

namespace {

// LRU cache implementation

// An entry is a variable length heap-allocated structure.  Entries
// are kept in a circular doubly linked list ordered by access time.
struct LRUHandle {
  void* value;
  void (*deleter)(const Slice&, void* value);
  LRUHandle* next_hash;
  LRUHandle* next;
  LRUHandle* prev;
  size_t charge;      // TODO(opt): Only allow uint32_t?
  size_t key_length;
  uint32_t refs;
  uint32_t hash;      // Hash of key(); used for fast sharding and comparisons
  char key_data[1];   // Beginning of key

  Slice key() const {
    // For cheaper lookups, we allow a temporary Handle object
    // to store a pointer to a key in "value".
    if (next == this) {
      return *(reinterpret_cast<Slice*>(value));
    } else {
      return Slice(key_data, key_length);
    }
  }
};

// We provide our own simple hash table since it removes a whole bunch
// of porting hacks and is also faster than some of the built-in hash
// table implementations in some of the compiler/runtime combinations
// we have tested.  E.g., readrandom speeds up by ~5% over the g++
// 4.4.3's builtin hashtable.
class HandleTable {
 public:
  HandleTable() : length_(0), elems_(0), list_(nullptr) { Resize(); }
  ~HandleTable() { delete[] list_; }

  LRUHandle* Lookup(const Slice& key, uint32_t hash) {
    return *FindPointer(key, hash);
  }

  LRUHandle* Insert(LRUHandle* h) {
    LRUHandle** ptr = FindPointer(h->key(), h->hash);
    LRUHandle* old = *ptr;
    h->next_hash = (old == nullptr ? nullptr : old->next_hash);
    *ptr = h;
    if (old == nullptr) {
      ++elems_;
      if (elems_ > length_) {
        // Since each cache entry is fairly large, we aim for a small
        // average linked list length (<= 1).
        Resize();
      }
    }
    return old;
  }

  LRUHandle* Remove(const Slice& key, uint32_t hash) {
    LRUHandle** ptr = FindPointer(key, hash);
    LRUHandle* result = *ptr;
    if (result != nullptr) {
      *ptr = result->next_hash;
      --elems_;
    }
    return result;
  }

 private:
  // The table consists of an array of buckets where each bucket is
  // a linked list of cache entries that hash into the bucket.
  uint32_t length_;
  uint32_t elems_;
  LRUHandle** list_;

  // Return a pointer to slot that points to a cache entry that
  // matches key/hash.  If there is no such cache entry, return a
  // pointer to the trailing slot in the corresponding linked list.
  LRUHandle** FindPointer(const Slice& key, uint32_t hash) {
    LRUHandle** ptr = &list_[hash & (length_ - 1)];
    while (*ptr != nullptr &&
           ((*ptr)->hash != hash || key != (*ptr)->key())) {
      ptr = &(*ptr)->next_hash;
    }
    return ptr;
  }

  void Resize() {
    uint32_t new_length = 4;
    while (new_length < elems_) {
      new_length *= 2;
    }
    LRUHandle** new_list = new LRUHandle*[new_length];
    memset(new_list, 0, sizeof(new_list[0]) * new_length);
    uint32_t count = 0;
    for (uint32_t i = 0; i < length_; i++) {
      LRUHandle* h = list_[i];
      while (h != nullptr) {
        LRUHandle* next = h->next_hash;
        uint32_t hash = h->hash;
        LRUHandle** ptr = &new_list[hash & (new_length - 1)];
        h->next_hash = *ptr;
        *ptr = h;
        h = next;
        count++;
      }
    }
    assert(elems_ == count);
    delete[] list_;
    list_ = new_list;
    length_ = new_length;
  }
};

// A single shard of sharded cache.
class LRUCache {
 public:
  LRUCache();
  ~LRUCache();

  // Separate from constructor so caller can easily make an array of LRUCache
  void SetCapacity(size_t capacity) { capacity_ = capacity; }
  void SetRemoveScanCountLimit(size_t remove_scan_count_limit) {
    remove_scan_count_limit_ = remove_scan_count_limit;
  }

  // Like Cache methods, but with an extra "hash" parameter.
  Cache::Handle* Insert(const Slice& key, uint32_t hash,
                        void* value, size_t charge,
                        void (*deleter)(const Slice& key, void* value));
  Cache::Handle* Lookup(const Slice& key, uint32_t hash);
  void Release(Cache::Handle* handle);
  void Erase(const Slice& key, uint32_t hash);

 private:
  void LRU_Remove(LRUHandle* e);
  void LRU_Append(LRUHandle* e);
  // Just reduce the reference count by 1.
  // Return true if last reference
  bool Unref(LRUHandle* e);
  // Call deleter and free
  void FreeEntry(LRUHandle* e);

  // Initialized before use.
  size_t capacity_;
  uint32_t remove_scan_count_limit_;

  // mutex_ protects the following state.
  port::Mutex mutex_;
  size_t usage_;

  // Dummy head of LRU list.
  // lru.prev is newest entry, lru.next is oldest entry.
  LRUHandle lru_;

  HandleTable table_;
};

LRUCache::LRUCache()
    : usage_(0) {
  // Make empty circular linked list
  lru_.next = &lru_;
  lru_.prev = &lru_;
}

LRUCache::~LRUCache() {
  for (LRUHandle* e = lru_.next; e != &lru_; ) {
    LRUHandle* next = e->next;
    assert(e->refs == 1);  // Error if caller has an unreleased handle
    if (Unref(e)) {
      FreeEntry(e);
    }
    e = next;
  }
}

bool LRUCache::Unref(LRUHandle* e) {
  assert(e->refs > 0);
  e->refs--;
  return e->refs == 0;
}

void LRUCache::FreeEntry(LRUHandle* e) {
  assert(e->refs == 0);
  (*e->deleter)(e->key(), e->value);
  free(e);
}

void LRUCache::LRU_Remove(LRUHandle* e) {
  e->next->prev = e->prev;
  e->prev->next = e->next;
  usage_ -= e->charge;
}

void LRUCache::LRU_Append(LRUHandle* e) {
  // Make "e" newest entry by inserting just before lru_
  e->next = &lru_;
  e->prev = lru_.prev;
  e->prev->next = e;
  e->next->prev = e;
  usage_ += e->charge;
}

Cache::Handle* LRUCache::Lookup(const Slice& key, uint32_t hash) {
  MutexLock l(&mutex_);
  LRUHandle* e = table_.Lookup(key, hash);
  if (e != nullptr) {
    e->refs++;
    LRU_Remove(e);
    LRU_Append(e);
  }
  return reinterpret_cast<Cache::Handle*>(e);
}

void LRUCache::Release(Cache::Handle* handle) {
  LRUHandle* e = reinterpret_cast<LRUHandle*>(handle);
  bool last_reference = false;
  {
    MutexLock l(&mutex_);
    last_reference = Unref(e);
  }
  if (last_reference) {
    FreeEntry(e);
  }
}

Cache::Handle* LRUCache::Insert(
    const Slice& key, uint32_t hash, void* value, size_t charge,
    void (*deleter)(const Slice& key, void* value)) {

  LRUHandle* e = reinterpret_cast<LRUHandle*>(
      malloc(sizeof(LRUHandle)-1 + key.size()));
  std::list<LRUHandle*> last_reference_list;

  {
    MutexLock l(&mutex_);

    e->value = value;
    e->deleter = deleter;
    e->charge = charge;
    e->key_length = key.size();
    e->hash = hash;
    e->refs = 2;  // One from LRUCache, one for the returned handle
    memcpy(e->key_data, key.data(), key.size());
    LRU_Append(e);

    LRUHandle* old = table_.Insert(e);
    if (old != nullptr) {
      LRU_Remove(old);
      if (Unref(old)) {
        last_reference_list.push_back(old);
      }
    }

    if (remove_scan_count_limit_ > 0) {
      // Try to free the space by evicting the entries that are only
      // referenced by the cache first.
      LRUHandle* cur = lru_.next;
      for (unsigned int scanCount = 0;
           usage_ > capacity_ && cur != &lru_
           && scanCount < remove_scan_count_limit_; scanCount++) {
        LRUHandle* next = cur->next;
        if (cur->refs <= 1) {
          LRU_Remove(cur);
          table_.Remove(cur->key(), cur->hash);
          if (Unref(cur)) {
            last_reference_list.push_back(cur);
          }
        }
        cur = next;
      }
    }

    // Free the space following strict LRU policy until enough space
    // is freed.
    while (usage_ > capacity_ && lru_.next != &lru_) {
      LRUHandle* old = lru_.next;
      LRU_Remove(old);
      table_.Remove(old->key(), old->hash);
      if (Unref(old)) {
        last_reference_list.push_back(old);
      }
    }
  }

  // we free the entries here outside of mutex for
  // performance reasons
  for (auto entry : last_reference_list) {
    FreeEntry(entry);
  }

  return reinterpret_cast<Cache::Handle*>(e);
}

void LRUCache::Erase(const Slice& key, uint32_t hash) {
  LRUHandle* e;
  bool last_reference = false;
  {
    MutexLock l(&mutex_);
    e = table_.Remove(key, hash);
    if (e != nullptr) {
      LRU_Remove(e);
      last_reference = Unref(e);
    }
  }
  // mutex not held here
  // last_reference will only be true if e != nullptr
  if (last_reference) {
    FreeEntry(e);
  }
}

static int kNumShardBits = 4;          // default values, can be overridden
static int kRemoveScanCountLimit = 0; // default values, can be overridden

class ShardedLRUCache : public Cache {
 private:
  LRUCache* shard_;
  port::Mutex id_mutex_;
  uint64_t last_id_;
  int numShardBits;
  size_t capacity_;

  static inline uint32_t HashSlice(const Slice& s) {
    return Hash(s.data(), s.size(), 0);
  }

  uint32_t Shard(uint32_t hash) {
    // Note, hash >> 32 yields hash in gcc, not the zero we expect!
    return (numShardBits > 0) ? (hash >> (32 - numShardBits)) : 0;
  }

  void init(size_t capacity, int numbits, int removeScanCountLimit) {
    numShardBits = numbits;
    capacity_ = capacity;
    int numShards = 1 << numShardBits;
    shard_ = new LRUCache[numShards];
    const size_t per_shard = (capacity + (numShards - 1)) / numShards;
    for (int s = 0; s < numShards; s++) {
      shard_[s].SetCapacity(per_shard);
      shard_[s].SetRemoveScanCountLimit(removeScanCountLimit);
    }
  }

 public:
  explicit ShardedLRUCache(size_t capacity)
      : last_id_(0) {
    init(capacity, kNumShardBits, kRemoveScanCountLimit);
  }
  ShardedLRUCache(size_t capacity, int numShardBits,
                  int removeScanCountLimit)
     : last_id_(0) {
    init(capacity, numShardBits, removeScanCountLimit);
  }
  virtual ~ShardedLRUCache() {
    delete[] shard_;
  }
  virtual Handle* Insert(const Slice& key, void* value, size_t charge,
                         void (*deleter)(const Slice& key, void* value)) {
    const uint32_t hash = HashSlice(key);
    return shard_[Shard(hash)].Insert(key, hash, value, charge, deleter);
  }
  virtual Handle* Lookup(const Slice& key) {
    const uint32_t hash = HashSlice(key);
    return shard_[Shard(hash)].Lookup(key, hash);
  }
  virtual void Release(Handle* handle) {
    LRUHandle* h = reinterpret_cast<LRUHandle*>(handle);
    shard_[Shard(h->hash)].Release(handle);
  }
  virtual void Erase(const Slice& key) {
    const uint32_t hash = HashSlice(key);
    shard_[Shard(hash)].Erase(key, hash);
  }
  virtual void* Value(Handle* handle) {
    return reinterpret_cast<LRUHandle*>(handle)->value;
  }
  virtual uint64_t NewId() {
    MutexLock l(&id_mutex_);
    return ++(last_id_);
  }
  virtual size_t GetCapacity() {
    return capacity_;
  }
};

}  // end anonymous namespace

shared_ptr<Cache> NewLRUCache(size_t capacity) {
  return NewLRUCache(capacity, kNumShardBits);
}

shared_ptr<Cache> NewLRUCache(size_t capacity, int numShardBits) {
  return NewLRUCache(capacity, numShardBits, kRemoveScanCountLimit);
}

shared_ptr<Cache> NewLRUCache(size_t capacity, int numShardBits,
                              int removeScanCountLimit) {
  if (numShardBits >= 20) {
    return nullptr;  // the cache cannot be sharded into too many fine pieces
  }
  return std::make_shared<ShardedLRUCache>(capacity,
                                           numShardBits,
                                           removeScanCountLimit);
}

}  // namespace rocksdb
<commit_msg>[RocksDB perf] Cache speedup<commit_after>//  Copyright (c) 2013, Facebook, 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.
//
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <vector>

#include "rocksdb/cache.h"
#include "port/port.h"
#include "util/hash.h"
#include "util/mutexlock.h"

namespace rocksdb {

Cache::~Cache() {
}

namespace {

// LRU cache implementation

// An entry is a variable length heap-allocated structure.  Entries
// are kept in a circular doubly linked list ordered by access time.
struct LRUHandle {
  void* value;
  void (*deleter)(const Slice&, void* value);
  LRUHandle* next_hash;
  LRUHandle* next;
  LRUHandle* prev;
  size_t charge;      // TODO(opt): Only allow uint32_t?
  size_t key_length;
  uint32_t refs;
  uint32_t hash;      // Hash of key(); used for fast sharding and comparisons
  char key_data[1];   // Beginning of key

  Slice key() const {
    // For cheaper lookups, we allow a temporary Handle object
    // to store a pointer to a key in "value".
    if (next == this) {
      return *(reinterpret_cast<Slice*>(value));
    } else {
      return Slice(key_data, key_length);
    }
  }
};

// We provide our own simple hash table since it removes a whole bunch
// of porting hacks and is also faster than some of the built-in hash
// table implementations in some of the compiler/runtime combinations
// we have tested.  E.g., readrandom speeds up by ~5% over the g++
// 4.4.3's builtin hashtable.
class HandleTable {
 public:
  HandleTable() : length_(0), elems_(0), list_(nullptr) { Resize(); }
  ~HandleTable() { delete[] list_; }

  LRUHandle* Lookup(const Slice& key, uint32_t hash) {
    return *FindPointer(key, hash);
  }

  LRUHandle* Insert(LRUHandle* h) {
    LRUHandle** ptr = FindPointer(h->key(), h->hash);
    LRUHandle* old = *ptr;
    h->next_hash = (old == nullptr ? nullptr : old->next_hash);
    *ptr = h;
    if (old == nullptr) {
      ++elems_;
      if (elems_ > length_) {
        // Since each cache entry is fairly large, we aim for a small
        // average linked list length (<= 1).
        Resize();
      }
    }
    return old;
  }

  LRUHandle* Remove(const Slice& key, uint32_t hash) {
    LRUHandle** ptr = FindPointer(key, hash);
    LRUHandle* result = *ptr;
    if (result != nullptr) {
      *ptr = result->next_hash;
      --elems_;
    }
    return result;
  }

 private:
  // The table consists of an array of buckets where each bucket is
  // a linked list of cache entries that hash into the bucket.
  uint32_t length_;
  uint32_t elems_;
  LRUHandle** list_;

  // Return a pointer to slot that points to a cache entry that
  // matches key/hash.  If there is no such cache entry, return a
  // pointer to the trailing slot in the corresponding linked list.
  LRUHandle** FindPointer(const Slice& key, uint32_t hash) {
    LRUHandle** ptr = &list_[hash & (length_ - 1)];
    while (*ptr != nullptr &&
           ((*ptr)->hash != hash || key != (*ptr)->key())) {
      ptr = &(*ptr)->next_hash;
    }
    return ptr;
  }

  void Resize() {
    uint32_t new_length = 16;
    while (new_length < elems_ * 1.5) {
      new_length *= 2;
    }
    LRUHandle** new_list = new LRUHandle*[new_length];
    memset(new_list, 0, sizeof(new_list[0]) * new_length);
    uint32_t count = 0;
    for (uint32_t i = 0; i < length_; i++) {
      LRUHandle* h = list_[i];
      while (h != nullptr) {
        LRUHandle* next = h->next_hash;
        uint32_t hash = h->hash;
        LRUHandle** ptr = &new_list[hash & (new_length - 1)];
        h->next_hash = *ptr;
        *ptr = h;
        h = next;
        count++;
      }
    }
    assert(elems_ == count);
    delete[] list_;
    list_ = new_list;
    length_ = new_length;
  }
};

// A single shard of sharded cache.
class LRUCache {
 public:
  LRUCache();
  ~LRUCache();

  // Separate from constructor so caller can easily make an array of LRUCache
  void SetCapacity(size_t capacity) { capacity_ = capacity; }
  void SetRemoveScanCountLimit(size_t remove_scan_count_limit) {
    remove_scan_count_limit_ = remove_scan_count_limit;
  }

  // Like Cache methods, but with an extra "hash" parameter.
  Cache::Handle* Insert(const Slice& key, uint32_t hash,
                        void* value, size_t charge,
                        void (*deleter)(const Slice& key, void* value));
  Cache::Handle* Lookup(const Slice& key, uint32_t hash);
  void Release(Cache::Handle* handle);
  void Erase(const Slice& key, uint32_t hash);

 private:
  void LRU_Remove(LRUHandle* e);
  void LRU_Append(LRUHandle* e);
  // Just reduce the reference count by 1.
  // Return true if last reference
  bool Unref(LRUHandle* e);
  // Call deleter and free
  void FreeEntry(LRUHandle* e);

  // Initialized before use.
  size_t capacity_;
  uint32_t remove_scan_count_limit_;

  // mutex_ protects the following state.
  port::Mutex mutex_;
  size_t usage_;

  // Dummy head of LRU list.
  // lru.prev is newest entry, lru.next is oldest entry.
  LRUHandle lru_;

  HandleTable table_;
};

LRUCache::LRUCache()
    : usage_(0) {
  // Make empty circular linked list
  lru_.next = &lru_;
  lru_.prev = &lru_;
}

LRUCache::~LRUCache() {
  for (LRUHandle* e = lru_.next; e != &lru_; ) {
    LRUHandle* next = e->next;
    assert(e->refs == 1);  // Error if caller has an unreleased handle
    if (Unref(e)) {
      FreeEntry(e);
    }
    e = next;
  }
}

bool LRUCache::Unref(LRUHandle* e) {
  assert(e->refs > 0);
  e->refs--;
  return e->refs == 0;
}

void LRUCache::FreeEntry(LRUHandle* e) {
  assert(e->refs == 0);
  (*e->deleter)(e->key(), e->value);
  free(e);
}

void LRUCache::LRU_Remove(LRUHandle* e) {
  e->next->prev = e->prev;
  e->prev->next = e->next;
  usage_ -= e->charge;
}

void LRUCache::LRU_Append(LRUHandle* e) {
  // Make "e" newest entry by inserting just before lru_
  e->next = &lru_;
  e->prev = lru_.prev;
  e->prev->next = e;
  e->next->prev = e;
  usage_ += e->charge;
}

Cache::Handle* LRUCache::Lookup(const Slice& key, uint32_t hash) {
  MutexLock l(&mutex_);
  LRUHandle* e = table_.Lookup(key, hash);
  if (e != nullptr) {
    e->refs++;
    LRU_Remove(e);
    LRU_Append(e);
  }
  return reinterpret_cast<Cache::Handle*>(e);
}

void LRUCache::Release(Cache::Handle* handle) {
  LRUHandle* e = reinterpret_cast<LRUHandle*>(handle);
  bool last_reference = false;
  {
    MutexLock l(&mutex_);
    last_reference = Unref(e);
  }
  if (last_reference) {
    FreeEntry(e);
  }
}

Cache::Handle* LRUCache::Insert(
    const Slice& key, uint32_t hash, void* value, size_t charge,
    void (*deleter)(const Slice& key, void* value)) {

  LRUHandle* e = reinterpret_cast<LRUHandle*>(
      malloc(sizeof(LRUHandle)-1 + key.size()));
  std::vector<LRUHandle*> last_reference_list;
  last_reference_list.reserve(1);

  e->value = value;
  e->deleter = deleter;
  e->charge = charge;
  e->key_length = key.size();
  e->hash = hash;
  e->refs = 2;  // One from LRUCache, one for the returned handle
  memcpy(e->key_data, key.data(), key.size());

  {
    MutexLock l(&mutex_);

    LRU_Append(e);

    LRUHandle* old = table_.Insert(e);
    if (old != nullptr) {
      LRU_Remove(old);
      if (Unref(old)) {
        last_reference_list.push_back(old);
      }
    }

    if (remove_scan_count_limit_ > 0) {
      // Try to free the space by evicting the entries that are only
      // referenced by the cache first.
      LRUHandle* cur = lru_.next;
      for (unsigned int scanCount = 0;
           usage_ > capacity_ && cur != &lru_
           && scanCount < remove_scan_count_limit_; scanCount++) {
        LRUHandle* next = cur->next;
        if (cur->refs <= 1) {
          LRU_Remove(cur);
          table_.Remove(cur->key(), cur->hash);
          if (Unref(cur)) {
            last_reference_list.push_back(cur);
          }
        }
        cur = next;
      }
    }

    // Free the space following strict LRU policy until enough space
    // is freed.
    while (usage_ > capacity_ && lru_.next != &lru_) {
      LRUHandle* old = lru_.next;
      LRU_Remove(old);
      table_.Remove(old->key(), old->hash);
      if (Unref(old)) {
        last_reference_list.push_back(old);
      }
    }
  }

  // we free the entries here outside of mutex for
  // performance reasons
  for (auto entry : last_reference_list) {
    FreeEntry(entry);
  }

  return reinterpret_cast<Cache::Handle*>(e);
}

void LRUCache::Erase(const Slice& key, uint32_t hash) {
  LRUHandle* e;
  bool last_reference = false;
  {
    MutexLock l(&mutex_);
    e = table_.Remove(key, hash);
    if (e != nullptr) {
      LRU_Remove(e);
      last_reference = Unref(e);
    }
  }
  // mutex not held here
  // last_reference will only be true if e != nullptr
  if (last_reference) {
    FreeEntry(e);
  }
}

static int kNumShardBits = 4;          // default values, can be overridden
static int kRemoveScanCountLimit = 0; // default values, can be overridden

class ShardedLRUCache : public Cache {
 private:
  LRUCache* shard_;
  port::Mutex id_mutex_;
  uint64_t last_id_;
  int numShardBits;
  size_t capacity_;

  static inline uint32_t HashSlice(const Slice& s) {
    return Hash(s.data(), s.size(), 0);
  }

  uint32_t Shard(uint32_t hash) {
    // Note, hash >> 32 yields hash in gcc, not the zero we expect!
    return (numShardBits > 0) ? (hash >> (32 - numShardBits)) : 0;
  }

  void init(size_t capacity, int numbits, int removeScanCountLimit) {
    numShardBits = numbits;
    capacity_ = capacity;
    int numShards = 1 << numShardBits;
    shard_ = new LRUCache[numShards];
    const size_t per_shard = (capacity + (numShards - 1)) / numShards;
    for (int s = 0; s < numShards; s++) {
      shard_[s].SetCapacity(per_shard);
      shard_[s].SetRemoveScanCountLimit(removeScanCountLimit);
    }
  }

 public:
  explicit ShardedLRUCache(size_t capacity)
      : last_id_(0) {
    init(capacity, kNumShardBits, kRemoveScanCountLimit);
  }
  ShardedLRUCache(size_t capacity, int numShardBits,
                  int removeScanCountLimit)
     : last_id_(0) {
    init(capacity, numShardBits, removeScanCountLimit);
  }
  virtual ~ShardedLRUCache() {
    delete[] shard_;
  }
  virtual Handle* Insert(const Slice& key, void* value, size_t charge,
                         void (*deleter)(const Slice& key, void* value)) {
    const uint32_t hash = HashSlice(key);
    return shard_[Shard(hash)].Insert(key, hash, value, charge, deleter);
  }
  virtual Handle* Lookup(const Slice& key) {
    const uint32_t hash = HashSlice(key);
    return shard_[Shard(hash)].Lookup(key, hash);
  }
  virtual void Release(Handle* handle) {
    LRUHandle* h = reinterpret_cast<LRUHandle*>(handle);
    shard_[Shard(h->hash)].Release(handle);
  }
  virtual void Erase(const Slice& key) {
    const uint32_t hash = HashSlice(key);
    shard_[Shard(hash)].Erase(key, hash);
  }
  virtual void* Value(Handle* handle) {
    return reinterpret_cast<LRUHandle*>(handle)->value;
  }
  virtual uint64_t NewId() {
    MutexLock l(&id_mutex_);
    return ++(last_id_);
  }
  virtual size_t GetCapacity() {
    return capacity_;
  }
};

}  // end anonymous namespace

shared_ptr<Cache> NewLRUCache(size_t capacity) {
  return NewLRUCache(capacity, kNumShardBits);
}

shared_ptr<Cache> NewLRUCache(size_t capacity, int numShardBits) {
  return NewLRUCache(capacity, numShardBits, kRemoveScanCountLimit);
}

shared_ptr<Cache> NewLRUCache(size_t capacity, int numShardBits,
                              int removeScanCountLimit) {
  if (numShardBits >= 20) {
    return nullptr;  // the cache cannot be sharded into too many fine pieces
  }
  return std::make_shared<ShardedLRUCache>(capacity,
                                           numShardBits,
                                           removeScanCountLimit);
}

}  // namespace rocksdb
<|endoftext|>


<commit_before>/*
* Copyright (C) 2007-2013 German Aerospace Center (DLR/SC)
*
* Created: 2010-08-13 Markus Litz <Markus.Litz@dlr.de>
* Changed: $Id$
*
* Version: $Revision$
*
* 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.
*/
/**
* @file
* @brief  Implementation of CPACS wings handling routines.
*/

#include "CCPACSWings.h"
#include "CCPACSConfiguration.h"
#include "CTiglError.h"
#include "TixiSaveExt.h"
#include "IOHelper.h"
#include <iostream>
#include <sstream>

namespace tigl
{

// Constructor
CCPACSWings::CCPACSWings(CCPACSConfiguration* config)
    : configuration(config)
{
    Cleanup();
}

// Destructor
CCPACSWings::~CCPACSWings(void)
{
    Cleanup();
}

// Invalidates internal state
void CCPACSWings::Invalidate(void)
{
    profiles.Invalidate();
    for (int i = 1; i <= GetWingCount(); i++) {
        GetWing(i).Invalidate();
    }
}

// Cleanup routine
void CCPACSWings::Cleanup(void)
{
    for (CCPACSWingContainer::size_type i = 0; i < wings.size(); i++) {
        delete wings[i];
    }
    wings.clear();
}

void CCPACSWings::ReadCPACS(TixiDocumentHandle tixiHandle, const char* configurationUID)
{
    Cleanup();
    profiles.ReadCPACS(tixiHandle);

    char* tmpString = NULL;
    if (tixiUIDGetXPath(tixiHandle, configurationUID, &tmpString) != SUCCESS) {
        throw CTiglError("XML error: tixiUIDGetXPath failed in CCPACSWings::ReadCPACS", TIGL_XML_ERROR);
    }
    const std::string wingXPath = std::string(tmpString) + "[@uID=\"" + configurationUID + "\"]/wings";
    ReadContainerElement(tixiHandle, wingXPath, "wing", 1, wings, configuration);

    if (configuration->IsRotorcraft()) {
         // read rotor blades
         const std::string rotorbladeXPath = std::string(tmpString) + "[@uID=\"" + configurationUID + "\"]/rotorBlades";
         ReadContainerElement(tixiHandle, rotorbladeXPath, "rotorBlade", 1, wings, configuration);
    }
}

// Write CPACS wings elements
void CCPACSWings::WriteCPACS(TixiDocumentHandle tixiHandle, const std::string& configurationUID)
{
    // save the wing profiles
    profiles.WriteCPACS(tixiHandle);

    // tixi frees tmpString internally when finished
    char *tmpString = NULL;
    if (tixiUIDGetXPath(tixiHandle, configurationUID.c_str(), &tmpString) != SUCCESS) {
        throw CTiglError("XML error: tixiUIDGetXPath failed in CCPACSWings::WriteCPACS", TIGL_XML_ERROR);
    }
    if (tmpString == "") {
        throw CTiglError("XML error in CCPACSWings::WriteCPACS : Path not found", TIGL_XML_ERROR);
    }

    // write wings
    std::string xpath = std::string(tmpString) + "[@uID=\"" + configurationUID + "\"]/wings";
    WriteContainerElement(tixiHandle, xpath, "wing", wings);

    // TODO: write rotor blades!!!

}

bool CCPACSWings::HasProfile(std::string uid) const
{
    return profiles.HasProfile(uid);
}

// Returns the total count of wing profiles in this configuration
int CCPACSWings::GetProfileCount(void) const
{
    return profiles.GetProfileCount();
}

CCPACSWingProfiles& CCPACSWings::GetProfiles(void)
{
    return profiles;
}

// Returns the wing profile for a given uid.
CCPACSWingProfile& CCPACSWings::GetProfile(std::string uid) const
{
    return profiles.GetProfile(uid);
}

// Returns the wing profile for a given index.
CCPACSWingProfile& CCPACSWings::GetProfile(int index) const
{
    return profiles.GetProfile(index);
}

// Returns the total count of wings in a configuration
int CCPACSWings::GetWingCount(void) const
{
    return (static_cast<int>(wings.size()));
}

// Returns the count of wings in a configuration with the property isRotorBlade set to true
int CCPACSWings::GetRotorBladeCount(void) const
{
    int nRotorBlades = 0;
    for (int i = 1; i <= GetWingCount(); i++) {
        if (GetWing(i).IsRotorBlade()) {
            nRotorBlades++;
        }
    }
    return nRotorBlades;
}

// Returns the wing for a given index.
CCPACSWing& CCPACSWings::GetWing(int index) const
{
    index --;
    if (index < 0 || index >= GetWingCount()) {
        throw CTiglError("Error: Invalid index in CCPACSWings::GetWing", TIGL_INDEX_ERROR);
    }
    return (*wings[index]);
}

// Returns the wing for a given UID.
CCPACSWing& CCPACSWings::GetWing(const std::string& UID) const
{
    return (*wings[GetWingIndex(UID)-1]);
}

// Returns the wing index for a given UID.
int CCPACSWings::GetWingIndex(const std::string& UID) const
{
    for (int i=0; i < GetWingCount(); i++) {
        const std::string tmpUID(wings[i]->GetUID());
        if (tmpUID == UID) {
            return i+1;
        }
    }

    // UID not there
    throw CTiglError("Error: Invalid UID in CCPACSWings::GetWingIndex", TIGL_UID_ERROR);
}

void CCPACSWings::AddWing(CCPACSWing* wing)
{
    // Check whether the same wing already exists if yes remove it before adding the new one
    CCPACSWingContainer::iterator it;
    for (it = wings.begin(); it != wings.end(); ++it) {
        if ((*it)->GetUID() == wing->GetUID()) {
            delete (*it);
            wings.erase(it);
            break;
        }
    }

    // Add the new wing to the wing list
    wings.push_back(wing);
}

} // end namespace tigl
<commit_msg>Rotorblades should be also written now<commit_after>/*
* Copyright (C) 2007-2013 German Aerospace Center (DLR/SC)
*
* Created: 2010-08-13 Markus Litz <Markus.Litz@dlr.de>
* Changed: $Id$
*
* Version: $Revision$
*
* 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.
*/
/**
* @file
* @brief  Implementation of CPACS wings handling routines.
*/

#include "CCPACSWings.h"
#include "CCPACSConfiguration.h"
#include "CTiglError.h"
#include "TixiSaveExt.h"
#include "IOHelper.h"
#include <iostream>
#include <sstream>

namespace tigl
{

// Constructor
CCPACSWings::CCPACSWings(CCPACSConfiguration* config)
    : configuration(config)
{
    Cleanup();
}

// Destructor
CCPACSWings::~CCPACSWings(void)
{
    Cleanup();
}

// Invalidates internal state
void CCPACSWings::Invalidate(void)
{
    profiles.Invalidate();
    for (int i = 1; i <= GetWingCount(); i++) {
        GetWing(i).Invalidate();
    }
}

// Cleanup routine
void CCPACSWings::Cleanup(void)
{
    for (CCPACSWingContainer::size_type i = 0; i < wings.size(); i++) {
        delete wings[i];
    }
    wings.clear();
}

void CCPACSWings::ReadCPACS(TixiDocumentHandle tixiHandle, const char* configurationUID)
{
    Cleanup();
    profiles.ReadCPACS(tixiHandle);

    char* tmpString = NULL;
    if (tixiUIDGetXPath(tixiHandle, configurationUID, &tmpString) != SUCCESS) {
        throw CTiglError("XML error: tixiUIDGetXPath failed in CCPACSWings::ReadCPACS", TIGL_XML_ERROR);
    }
    const std::string wingXPath = std::string(tmpString) + "[@uID=\"" + configurationUID + "\"]/wings";
    ReadContainerElement(tixiHandle, wingXPath, "wing", 1, wings, configuration);

    if (configuration->IsRotorcraft()) {
         // read rotor blades
         const std::string rotorbladeXPath = std::string(tmpString) + "[@uID=\"" + configurationUID + "\"]/rotorBlades";
         ReadContainerElement(tixiHandle, rotorbladeXPath, "rotorBlade", 1, wings, configuration);
    }
}

// Write CPACS wings elements
void CCPACSWings::WriteCPACS(TixiDocumentHandle tixiHandle, const std::string& configurationUID)
{
    // save the wing profiles
    profiles.WriteCPACS(tixiHandle);

    // tixi frees tmpString internally when finished
    char *tmpString = NULL;
    if (tixiUIDGetXPath(tixiHandle, configurationUID.c_str(), &tmpString) != SUCCESS) {
        throw CTiglError("XML error: tixiUIDGetXPath failed in CCPACSWings::WriteCPACS", TIGL_XML_ERROR);
    }
    if (tmpString == "") {
        throw CTiglError("XML error in CCPACSWings::WriteCPACS : Path not found", TIGL_XML_ERROR);
    }

    // determine, which are wings and which are rotorbaldes
    CCPACSWingContainer theWings, theRotorblades;

    for (CCPACSWingContainer::iterator iter = wings.begin(); iter != wings.end(); ++iter) {
        CCPACSWing* wing = *iter;
        if (wing->IsRotorBlade()) {
            theRotorblades.push_back(wing);
        }
        else {
            theWings.push_back(wing);
        }
    }
    // write wings
    std::string xpath = std::string(tmpString) + "[@uID=\"" + configurationUID + "\"]/wings";
    WriteContainerElement(tixiHandle, xpath, "wing", theWings);

    // write rotorblades
    xpath = std::string(tmpString) + "[@uID=\"" + configurationUID + "\"]/rotorBlades";
    WriteContainerElement(tixiHandle, xpath, "rotorBlade", theRotorblades);

}

bool CCPACSWings::HasProfile(std::string uid) const
{
    return profiles.HasProfile(uid);
}

// Returns the total count of wing profiles in this configuration
int CCPACSWings::GetProfileCount(void) const
{
    return profiles.GetProfileCount();
}

CCPACSWingProfiles& CCPACSWings::GetProfiles(void)
{
    return profiles;
}

// Returns the wing profile for a given uid.
CCPACSWingProfile& CCPACSWings::GetProfile(std::string uid) const
{
    return profiles.GetProfile(uid);
}

// Returns the wing profile for a given index.
CCPACSWingProfile& CCPACSWings::GetProfile(int index) const
{
    return profiles.GetProfile(index);
}

// Returns the total count of wings in a configuration
int CCPACSWings::GetWingCount(void) const
{
    return (static_cast<int>(wings.size()));
}

// Returns the count of wings in a configuration with the property isRotorBlade set to true
int CCPACSWings::GetRotorBladeCount(void) const
{
    int nRotorBlades = 0;
    for (int i = 1; i <= GetWingCount(); i++) {
        if (GetWing(i).IsRotorBlade()) {
            nRotorBlades++;
        }
    }
    return nRotorBlades;
}

// Returns the wing for a given index.
CCPACSWing& CCPACSWings::GetWing(int index) const
{
    index --;
    if (index < 0 || index >= GetWingCount()) {
        throw CTiglError("Error: Invalid index in CCPACSWings::GetWing", TIGL_INDEX_ERROR);
    }
    return (*wings[index]);
}

// Returns the wing for a given UID.
CCPACSWing& CCPACSWings::GetWing(const std::string& UID) const
{
    return (*wings[GetWingIndex(UID)-1]);
}

// Returns the wing index for a given UID.
int CCPACSWings::GetWingIndex(const std::string& UID) const
{
    for (int i=0; i < GetWingCount(); i++) {
        const std::string tmpUID(wings[i]->GetUID());
        if (tmpUID == UID) {
            return i+1;
        }
    }

    // UID not there
    throw CTiglError("Error: Invalid UID in CCPACSWings::GetWingIndex", TIGL_UID_ERROR);
}

void CCPACSWings::AddWing(CCPACSWing* wing)
{
    // Check whether the same wing already exists if yes remove it before adding the new one
    CCPACSWingContainer::iterator it;
    for (it = wings.begin(); it != wings.end(); ++it) {
        if ((*it)->GetUID() == wing->GetUID()) {
            delete (*it);
            wings.erase(it);
            break;
        }
    }

    // Add the new wing to the wing list
    wings.push_back(wing);
}

} // end namespace tigl
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "webrtc/modules/audio_processing/audio_buffer.h"

#include "webrtc/common_audio/include/audio_util.h"
#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h"

namespace webrtc {
namespace {

enum {
  kSamplesPer8kHzChannel = 80,
  kSamplesPer16kHzChannel = 160,
  kSamplesPer32kHzChannel = 320
};

void StereoToMono(const int16_t* left, const int16_t* right,
                  int16_t* out, int samples_per_channel) {
  assert(left != NULL && right != NULL && out != NULL);
  for (int i = 0; i < samples_per_channel; i++) {
    int32_t data32 = (static_cast<int32_t>(left[i]) +
                      static_cast<int32_t>(right[i])) >> 1;

    out[i] = WebRtcSpl_SatW32ToW16(data32);
  }
}
}  // namespace

struct AudioChannel {
  AudioChannel() {
    memset(data, 0, sizeof(data));
  }

  int16_t data[kSamplesPer32kHzChannel];
};

struct SplitAudioChannel {
  SplitAudioChannel() {
    memset(low_pass_data, 0, sizeof(low_pass_data));
    memset(high_pass_data, 0, sizeof(high_pass_data));
    memset(analysis_filter_state1, 0, sizeof(analysis_filter_state1));
    memset(analysis_filter_state2, 0, sizeof(analysis_filter_state2));
    memset(synthesis_filter_state1, 0, sizeof(synthesis_filter_state1));
    memset(synthesis_filter_state2, 0, sizeof(synthesis_filter_state2));
  }

  int16_t low_pass_data[kSamplesPer16kHzChannel];
  int16_t high_pass_data[kSamplesPer16kHzChannel];

  int32_t analysis_filter_state1[6];
  int32_t analysis_filter_state2[6];
  int32_t synthesis_filter_state1[6];
  int32_t synthesis_filter_state2[6];
};

// TODO(andrew): check range of input parameters?
AudioBuffer::AudioBuffer(int max_num_channels,
                         int samples_per_channel)
  : max_num_channels_(max_num_channels),
    num_channels_(0),
    num_mixed_channels_(0),
    num_mixed_low_pass_channels_(0),
    data_was_mixed_(false),
    samples_per_channel_(samples_per_channel),
    samples_per_split_channel_(samples_per_channel),
    reference_copied_(false),
    activity_(AudioFrame::kVadUnknown),
    is_muted_(false),
    data_(NULL),
    channels_(NULL),
    split_channels_(NULL),
    mixed_channels_(NULL),
    mixed_low_pass_channels_(NULL),
    low_pass_reference_channels_(NULL) {
  channels_.reset(new AudioChannel[max_num_channels_]);
  mixed_channels_.reset(new AudioChannel[max_num_channels_]);
  mixed_low_pass_channels_.reset(new AudioChannel[max_num_channels_]);
  low_pass_reference_channels_.reset(new AudioChannel[max_num_channels_]);

  if (samples_per_channel_ == kSamplesPer32kHzChannel) {
    split_channels_.reset(new SplitAudioChannel[max_num_channels_]);
    samples_per_split_channel_ = kSamplesPer16kHzChannel;
  }
}

AudioBuffer::~AudioBuffer() {}

void AudioBuffer::InitForNewData(int num_channels) {
  num_channels_ = num_channels;
  data_ = NULL;
  data_was_mixed_ = false;
  num_mixed_channels_ = 0;
  num_mixed_low_pass_channels_ = 0;
  reference_copied_ = false;
  activity_ = AudioFrame::kVadUnknown;
  is_muted_ = false;
}

int16_t* AudioBuffer::data(int channel) const {
  assert(channel >= 0 && channel < num_channels_);
  if (data_ != NULL) {
    return data_;
  }

  return channels_[channel].data;
}

int16_t* AudioBuffer::low_pass_split_data(int channel) const {
  assert(channel >= 0 && channel < num_channels_);
  if (split_channels_.get() == NULL) {
    return data(channel);
  }

  return split_channels_[channel].low_pass_data;
}

int16_t* AudioBuffer::high_pass_split_data(int channel) const {
  assert(channel >= 0 && channel < num_channels_);
  if (split_channels_.get() == NULL) {
    return NULL;
  }

  return split_channels_[channel].high_pass_data;
}

int16_t* AudioBuffer::mixed_data(int channel) const {
  assert(channel >= 0 && channel < num_mixed_channels_);

  return mixed_channels_[channel].data;
}

int16_t* AudioBuffer::mixed_low_pass_data(int channel) const {
  assert(channel >= 0 && channel < num_mixed_low_pass_channels_);

  return mixed_low_pass_channels_[channel].data;
}

int16_t* AudioBuffer::low_pass_reference(int channel) const {
  assert(channel >= 0 && channel < num_channels_);
  if (!reference_copied_) {
    return NULL;
  }

  return low_pass_reference_channels_[channel].data;
}

int32_t* AudioBuffer::analysis_filter_state1(int channel) const {
  assert(channel >= 0 && channel < num_channels_);
  return split_channels_[channel].analysis_filter_state1;
}

int32_t* AudioBuffer::analysis_filter_state2(int channel) const {
  assert(channel >= 0 && channel < num_channels_);
  return split_channels_[channel].analysis_filter_state2;
}

int32_t* AudioBuffer::synthesis_filter_state1(int channel) const {
  assert(channel >= 0 && channel < num_channels_);
  return split_channels_[channel].synthesis_filter_state1;
}

int32_t* AudioBuffer::synthesis_filter_state2(int channel) const {
  assert(channel >= 0 && channel < num_channels_);
  return split_channels_[channel].synthesis_filter_state2;
}

void AudioBuffer::set_activity(AudioFrame::VADActivity activity) {
  activity_ = activity;
}

AudioFrame::VADActivity AudioBuffer::activity() const {
  return activity_;
}

bool AudioBuffer::is_muted() const {
  return is_muted_;
}

int AudioBuffer::num_channels() const {
  return num_channels_;
}

int AudioBuffer::samples_per_channel() const {
  return samples_per_channel_;
}

int AudioBuffer::samples_per_split_channel() const {
  return samples_per_split_channel_;
}

// TODO(andrew): Do deinterleaving and mixing in one step?
void AudioBuffer::DeinterleaveFrom(AudioFrame* frame) {
  assert(frame->num_channels_ <= max_num_channels_);
  assert(frame->samples_per_channel_ ==  samples_per_channel_);

  InitForNewData(frame->num_channels_);
  activity_ = frame->vad_activity_;
  if (frame->energy_ == 0) {
    is_muted_ = true;
  }

  if (num_channels_ == 1) {
    // We can get away with a pointer assignment in this case.
    data_ = frame->data_;
    return;
  }

  int16_t* interleaved = frame->data_;
  for (int i = 0; i < num_channels_; i++) {
    int16_t* deinterleaved = channels_[i].data;
    int interleaved_idx = i;
    for (int j = 0; j < samples_per_channel_; j++) {
      deinterleaved[j] = interleaved[interleaved_idx];
      interleaved_idx += num_channels_;
    }
  }
}

void AudioBuffer::InterleaveTo(AudioFrame* frame, bool data_changed) const {
  assert(frame->num_channels_ == num_channels_);
  assert(frame->samples_per_channel_ == samples_per_channel_);
  frame->vad_activity_ = activity_;

  if (!data_changed) {
    return;
  }

  if (num_channels_ == 1) {
    if (data_was_mixed_) {
      memcpy(frame->data_,
             channels_[0].data,
             sizeof(int16_t) * samples_per_channel_);
    } else {
      // These should point to the same buffer in this case.
      assert(data_ == frame->data_);
    }

    return;
  }

  int16_t* interleaved = frame->data_;
  for (int i = 0; i < num_channels_; i++) {
    int16_t* deinterleaved = channels_[i].data;
    int interleaved_idx = i;
    for (int j = 0; j < samples_per_channel_; j++) {
      interleaved[interleaved_idx] = deinterleaved[j];
      interleaved_idx += num_channels_;
    }
  }
}

void AudioBuffer::CopyFrom(const float* const* data, int samples_per_channel,
                           int num_channels) {
  assert(num_channels <= max_num_channels_);
  assert(samples_per_channel ==  samples_per_channel_);

  InitForNewData(num_channels);
  for (int i = 0; i < num_channels_; ++i) {
    ScaleAndRoundToInt16(data[i], samples_per_channel, channels_[i].data);
  }
}

void AudioBuffer::CopyTo(int samples_per_channel, int num_channels,
                         float* const* data) const {
  assert(num_channels == num_channels_);
  assert(samples_per_channel == samples_per_channel_);
  for (int i = 0; i < num_channels_; ++i) {
    ScaleToFloat(channels_[i].data, samples_per_channel, data[i]);
  }
}

// TODO(andrew): would be good to support the no-mix case with pointer
// assignment.
// TODO(andrew): handle mixing to multiple channels?
void AudioBuffer::Mix(int num_mixed_channels) {
  // We currently only support the stereo to mono case.
  assert(num_channels_ == 2);
  assert(num_mixed_channels == 1);

  StereoToMono(channels_[0].data,
               channels_[1].data,
               channels_[0].data,
               samples_per_channel_);

  num_channels_ = num_mixed_channels;
  data_was_mixed_ = true;
}

void AudioBuffer::CopyAndMix(int num_mixed_channels) {
  // We currently only support the stereo to mono case.
  assert(num_channels_ == 2);
  assert(num_mixed_channels == 1);

  StereoToMono(channels_[0].data,
               channels_[1].data,
               mixed_channels_[0].data,
               samples_per_channel_);

  num_mixed_channels_ = num_mixed_channels;
}

void AudioBuffer::CopyAndMixLowPass(int num_mixed_channels) {
  // We currently only support the stereo to mono case.
  assert(num_channels_ == 2);
  assert(num_mixed_channels == 1);

  StereoToMono(low_pass_split_data(0),
               low_pass_split_data(1),
               mixed_low_pass_channels_[0].data,
               samples_per_split_channel_);

  num_mixed_low_pass_channels_ = num_mixed_channels;
}

void AudioBuffer::CopyLowPassToReference() {
  reference_copied_ = true;
  for (int i = 0; i < num_channels_; i++) {
    memcpy(low_pass_reference_channels_[i].data,
           low_pass_split_data(i),
           sizeof(int16_t) * samples_per_split_channel_);
  }
}
}  // namespace webrtc
<commit_msg>StereoToMono: Remove useless call to WebRtcSpl_SatW32ToW16<commit_after>/*
 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "webrtc/modules/audio_processing/audio_buffer.h"

#include "webrtc/common_audio/include/audio_util.h"
#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h"

namespace webrtc {
namespace {

enum {
  kSamplesPer8kHzChannel = 80,
  kSamplesPer16kHzChannel = 160,
  kSamplesPer32kHzChannel = 320
};

void StereoToMono(const int16_t* left, const int16_t* right,
                  int16_t* out, int samples_per_channel) {
  assert(left != NULL && right != NULL && out != NULL);
  for (int i = 0; i < samples_per_channel; i++)
    out[i] = (static_cast<int32_t>(left[i]) +
              static_cast<int32_t>(right[i])) >> 1;
}
}  // namespace

struct AudioChannel {
  AudioChannel() {
    memset(data, 0, sizeof(data));
  }

  int16_t data[kSamplesPer32kHzChannel];
};

struct SplitAudioChannel {
  SplitAudioChannel() {
    memset(low_pass_data, 0, sizeof(low_pass_data));
    memset(high_pass_data, 0, sizeof(high_pass_data));
    memset(analysis_filter_state1, 0, sizeof(analysis_filter_state1));
    memset(analysis_filter_state2, 0, sizeof(analysis_filter_state2));
    memset(synthesis_filter_state1, 0, sizeof(synthesis_filter_state1));
    memset(synthesis_filter_state2, 0, sizeof(synthesis_filter_state2));
  }

  int16_t low_pass_data[kSamplesPer16kHzChannel];
  int16_t high_pass_data[kSamplesPer16kHzChannel];

  int32_t analysis_filter_state1[6];
  int32_t analysis_filter_state2[6];
  int32_t synthesis_filter_state1[6];
  int32_t synthesis_filter_state2[6];
};

// TODO(andrew): check range of input parameters?
AudioBuffer::AudioBuffer(int max_num_channels,
                         int samples_per_channel)
  : max_num_channels_(max_num_channels),
    num_channels_(0),
    num_mixed_channels_(0),
    num_mixed_low_pass_channels_(0),
    data_was_mixed_(false),
    samples_per_channel_(samples_per_channel),
    samples_per_split_channel_(samples_per_channel),
    reference_copied_(false),
    activity_(AudioFrame::kVadUnknown),
    is_muted_(false),
    data_(NULL),
    channels_(NULL),
    split_channels_(NULL),
    mixed_channels_(NULL),
    mixed_low_pass_channels_(NULL),
    low_pass_reference_channels_(NULL) {
  channels_.reset(new AudioChannel[max_num_channels_]);
  mixed_channels_.reset(new AudioChannel[max_num_channels_]);
  mixed_low_pass_channels_.reset(new AudioChannel[max_num_channels_]);
  low_pass_reference_channels_.reset(new AudioChannel[max_num_channels_]);

  if (samples_per_channel_ == kSamplesPer32kHzChannel) {
    split_channels_.reset(new SplitAudioChannel[max_num_channels_]);
    samples_per_split_channel_ = kSamplesPer16kHzChannel;
  }
}

AudioBuffer::~AudioBuffer() {}

void AudioBuffer::InitForNewData(int num_channels) {
  num_channels_ = num_channels;
  data_ = NULL;
  data_was_mixed_ = false;
  num_mixed_channels_ = 0;
  num_mixed_low_pass_channels_ = 0;
  reference_copied_ = false;
  activity_ = AudioFrame::kVadUnknown;
  is_muted_ = false;
}

int16_t* AudioBuffer::data(int channel) const {
  assert(channel >= 0 && channel < num_channels_);
  if (data_ != NULL) {
    return data_;
  }

  return channels_[channel].data;
}

int16_t* AudioBuffer::low_pass_split_data(int channel) const {
  assert(channel >= 0 && channel < num_channels_);
  if (split_channels_.get() == NULL) {
    return data(channel);
  }

  return split_channels_[channel].low_pass_data;
}

int16_t* AudioBuffer::high_pass_split_data(int channel) const {
  assert(channel >= 0 && channel < num_channels_);
  if (split_channels_.get() == NULL) {
    return NULL;
  }

  return split_channels_[channel].high_pass_data;
}

int16_t* AudioBuffer::mixed_data(int channel) const {
  assert(channel >= 0 && channel < num_mixed_channels_);

  return mixed_channels_[channel].data;
}

int16_t* AudioBuffer::mixed_low_pass_data(int channel) const {
  assert(channel >= 0 && channel < num_mixed_low_pass_channels_);

  return mixed_low_pass_channels_[channel].data;
}

int16_t* AudioBuffer::low_pass_reference(int channel) const {
  assert(channel >= 0 && channel < num_channels_);
  if (!reference_copied_) {
    return NULL;
  }

  return low_pass_reference_channels_[channel].data;
}

int32_t* AudioBuffer::analysis_filter_state1(int channel) const {
  assert(channel >= 0 && channel < num_channels_);
  return split_channels_[channel].analysis_filter_state1;
}

int32_t* AudioBuffer::analysis_filter_state2(int channel) const {
  assert(channel >= 0 && channel < num_channels_);
  return split_channels_[channel].analysis_filter_state2;
}

int32_t* AudioBuffer::synthesis_filter_state1(int channel) const {
  assert(channel >= 0 && channel < num_channels_);
  return split_channels_[channel].synthesis_filter_state1;
}

int32_t* AudioBuffer::synthesis_filter_state2(int channel) const {
  assert(channel >= 0 && channel < num_channels_);
  return split_channels_[channel].synthesis_filter_state2;
}

void AudioBuffer::set_activity(AudioFrame::VADActivity activity) {
  activity_ = activity;
}

AudioFrame::VADActivity AudioBuffer::activity() const {
  return activity_;
}

bool AudioBuffer::is_muted() const {
  return is_muted_;
}

int AudioBuffer::num_channels() const {
  return num_channels_;
}

int AudioBuffer::samples_per_channel() const {
  return samples_per_channel_;
}

int AudioBuffer::samples_per_split_channel() const {
  return samples_per_split_channel_;
}

// TODO(andrew): Do deinterleaving and mixing in one step?
void AudioBuffer::DeinterleaveFrom(AudioFrame* frame) {
  assert(frame->num_channels_ <= max_num_channels_);
  assert(frame->samples_per_channel_ ==  samples_per_channel_);

  InitForNewData(frame->num_channels_);
  activity_ = frame->vad_activity_;
  if (frame->energy_ == 0) {
    is_muted_ = true;
  }

  if (num_channels_ == 1) {
    // We can get away with a pointer assignment in this case.
    data_ = frame->data_;
    return;
  }

  int16_t* interleaved = frame->data_;
  for (int i = 0; i < num_channels_; i++) {
    int16_t* deinterleaved = channels_[i].data;
    int interleaved_idx = i;
    for (int j = 0; j < samples_per_channel_; j++) {
      deinterleaved[j] = interleaved[interleaved_idx];
      interleaved_idx += num_channels_;
    }
  }
}

void AudioBuffer::InterleaveTo(AudioFrame* frame, bool data_changed) const {
  assert(frame->num_channels_ == num_channels_);
  assert(frame->samples_per_channel_ == samples_per_channel_);
  frame->vad_activity_ = activity_;

  if (!data_changed) {
    return;
  }

  if (num_channels_ == 1) {
    if (data_was_mixed_) {
      memcpy(frame->data_,
             channels_[0].data,
             sizeof(int16_t) * samples_per_channel_);
    } else {
      // These should point to the same buffer in this case.
      assert(data_ == frame->data_);
    }

    return;
  }

  int16_t* interleaved = frame->data_;
  for (int i = 0; i < num_channels_; i++) {
    int16_t* deinterleaved = channels_[i].data;
    int interleaved_idx = i;
    for (int j = 0; j < samples_per_channel_; j++) {
      interleaved[interleaved_idx] = deinterleaved[j];
      interleaved_idx += num_channels_;
    }
  }
}

void AudioBuffer::CopyFrom(const float* const* data, int samples_per_channel,
                           int num_channels) {
  assert(num_channels <= max_num_channels_);
  assert(samples_per_channel ==  samples_per_channel_);

  InitForNewData(num_channels);
  for (int i = 0; i < num_channels_; ++i) {
    ScaleAndRoundToInt16(data[i], samples_per_channel, channels_[i].data);
  }
}

void AudioBuffer::CopyTo(int samples_per_channel, int num_channels,
                         float* const* data) const {
  assert(num_channels == num_channels_);
  assert(samples_per_channel == samples_per_channel_);
  for (int i = 0; i < num_channels_; ++i) {
    ScaleToFloat(channels_[i].data, samples_per_channel, data[i]);
  }
}

// TODO(andrew): would be good to support the no-mix case with pointer
// assignment.
// TODO(andrew): handle mixing to multiple channels?
void AudioBuffer::Mix(int num_mixed_channels) {
  // We currently only support the stereo to mono case.
  assert(num_channels_ == 2);
  assert(num_mixed_channels == 1);

  StereoToMono(channels_[0].data,
               channels_[1].data,
               channels_[0].data,
               samples_per_channel_);

  num_channels_ = num_mixed_channels;
  data_was_mixed_ = true;
}

void AudioBuffer::CopyAndMix(int num_mixed_channels) {
  // We currently only support the stereo to mono case.
  assert(num_channels_ == 2);
  assert(num_mixed_channels == 1);

  StereoToMono(channels_[0].data,
               channels_[1].data,
               mixed_channels_[0].data,
               samples_per_channel_);

  num_mixed_channels_ = num_mixed_channels;
}

void AudioBuffer::CopyAndMixLowPass(int num_mixed_channels) {
  // We currently only support the stereo to mono case.
  assert(num_channels_ == 2);
  assert(num_mixed_channels == 1);

  StereoToMono(low_pass_split_data(0),
               low_pass_split_data(1),
               mixed_low_pass_channels_[0].data,
               samples_per_split_channel_);

  num_mixed_low_pass_channels_ = num_mixed_channels;
}

void AudioBuffer::CopyLowPassToReference() {
  reference_copied_ = true;
  for (int i = 0; i < num_channels_; i++) {
    memcpy(low_pass_reference_channels_[i].data,
           low_pass_split_data(i),
           sizeof(int16_t) * samples_per_split_channel_);
  }
}
}  // namespace webrtc
<|endoftext|>


<commit_before>//------------------------------------------------------------------------------
// File: moduletmpl.cpp
//
// Desc: Module template for Data Mining.
//
// Copyright (c) 2014-2018. veyesys.com All rights reserved.
//------------------------------------------------------------------------------

#include "moduletmpl.hpp"

ModuleTmpl::ModuleTmpl(u32 id)
:m_id(id)
{
}
ModuleTmpl::~ModuleTmpl()
{
}

/* Process decoded or compressed data */
BOOL ModuleTmpl::Process(VideoFrame& frame)
{
	printf("%s id %d len %d\n", __FUNCTION__, m_id, frame.dataLen);
	return TRUE;
}
BOOL ModuleTmpl::ProcessRaw(RawFrame& frame)
{
	printf("%s id %d (%d, %d)\n", __FUNCTION__, m_id, frame.width, frame.height);
	return TRUE;
}

BOOL ModuleTmpl::ProcessSeq(VideoSeqFrame& frame)
{
	printf("%s id %d (%d, %d)\n", __FUNCTION__, m_id, frame.start, frame.end);
	return TRUE;
}

/* Get the stream type of this module */
BOOL ModuleTmpl::GetReqStream(MMReqStream& type)
{
	/* use subraw video for processing */
	type = MM_STREAM_SUB_RAW | MM_STREAM_SEQ;
	return TRUE;
}

BOOL ModuleTmpl::RegRetCallback(MiningCallbackFunctionPtr pCallback, void * pParam)
{
	return TRUE;
}
BOOL ModuleTmpl::UnRegDataCallback(void * pParam)
{
	return TRUE;
}
u32 ModuleTmpl::GetFlags()
{

	return MM_TRACK | MM_PEOPLE_CNT | MM_MOT;
}

astring ModuleTmpl::GetVersion()
{
	return "0.0.0";
}

VE_LIBRARY_API MiningInterface * MiningCreateDevice(u32 id)
{
	return new ModuleTmpl(id);
}<commit_msg>update moudule tmpl<commit_after>//------------------------------------------------------------------------------
// File: moduletmpl.cpp
//
// Desc: Module template for Data Mining.
//
// Copyright (c) 2014-2018. veyesys.com All rights reserved.
//------------------------------------------------------------------------------

#include "moduletmpl.hpp"

ModuleTmpl::ModuleTmpl(u32 id)
:m_id(id)
{
}
ModuleTmpl::~ModuleTmpl()
{
}

/* Process decoded or compressed data */
BOOL ModuleTmpl::Process(VideoFrame& frame)
{
	printf("%s id %d len %d\n", __FUNCTION__, m_id, frame.dataLen);
	return TRUE;
}
BOOL ModuleTmpl::ProcessRaw(RawFrame& frame)
{
	//printf("%s id %d (%d, %d)\n", __FUNCTION__, m_id, frame.width, frame.height);
	return TRUE;
}

BOOL ModuleTmpl::ProcessSeq(VideoSeqFrame& frame)
{
	printf("%s id %d (%d, %d)\n", __FUNCTION__, m_id, frame.start, frame.end);
	return TRUE;
}

/* Get the stream type of this module */
BOOL ModuleTmpl::GetReqStream(MMReqStream& type)
{
	/* use subraw video for processing */
	int typeAll = MM_STREAM_SUB_RAW | MM_STREAM_SEQ;
	type = (MMReqStream)typeAll;
	return TRUE;
}

BOOL ModuleTmpl::RegRetCallback(MiningCallbackFunctionPtr pCallback, void * pParam)
{
	return TRUE;
}
BOOL ModuleTmpl::UnRegDataCallback(void * pParam)
{
	return TRUE;
}
u32 ModuleTmpl::GetFlags()
{

	return MM_TRACK | MM_PEOPLE_CNT | MM_MOT;
}

astring ModuleTmpl::GetVersion()
{
	return "0.0.0";
}

VE_LIBRARY_API MiningInterface * MiningCreateDevice(u32 id)
{
	return new ModuleTmpl(id);
}<|endoftext|>