<commit_before>/*
  ESP8266WebServer.cpp - Dead simple web-server.
  Supports only one simultaneous client, knows how to handle GET and POST.

  Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
*/

#include <Arduino.h>
#include "WiFiServer.h"
#include "WiFiClient.h"
#include "ESP8266WebServer.h"
#include "FS.h"
#include "detail/RequestHandler.h"
// #define DEBUG
#define DEBUG_OUTPUT Serial

ESP8266WebServer::ESP8266WebServer(int port)
: _server(port)
, _firstHandler(0)
, _lastHandler(0)
, _currentArgCount(0)
, _currentArgs(0)
{
}

ESP8266WebServer::~ESP8266WebServer()
{
  if (!_firstHandler)
    return;
  RequestHandler* handler = _firstHandler;
  while (handler) {
    RequestHandler* next = handler->next;
    delete handler;
    handler = next;
  }
}

void ESP8266WebServer::begin() {
  _server.begin();
}

void ESP8266WebServer::on(const char* uri, ESP8266WebServer::THandlerFunction handler)
{
  on(uri, HTTP_ANY, handler);
}

void ESP8266WebServer::on(const char* uri, HTTPMethod method, ESP8266WebServer::THandlerFunction fn)
{
  _addRequestHandler(new FunctionRequestHandler(fn, uri, method));
}

void ESP8266WebServer::_addRequestHandler(RequestHandler* handler) {
    if (!_lastHandler) {
      _firstHandler = handler;
      _lastHandler = handler;
    }
    else {
      _lastHandler->next = handler;
      _lastHandler = handler;
    }
}

void ESP8266WebServer::serveStatic(const char* uri, FS& fs, const char* path) {
    _addRequestHandler(new StaticRequestHandler(fs, uri));
}

void ESP8266WebServer::handleClient()
{
  WiFiClient client = _server.available();
  if (!client) {
    return;
  }

#ifdef DEBUG
  DEBUG_OUTPUT.println("New client");
#endif

  // Wait for data from client to become available
  uint16_t maxWait = HTTP_MAX_DATA_WAIT;
  while(client.connected() && !client.available() && maxWait--){
    delay(1);
  }

  if (!_parseRequest(client)) {
    return;
  }

  _currentClient = client;
  _contentLength = CONTENT_LENGTH_NOT_SET;
  _handleRequest();
}

void ESP8266WebServer::sendHeader(const String& name, const String& value, bool first) {
  String headerLine = name;
  headerLine += ": ";
  headerLine += value;
  headerLine += "\r\n";

  if (first) {
    _responseHeaders = headerLine + _responseHeaders;
  }
  else {
    _responseHeaders += headerLine;
  }
}

void ESP8266WebServer::_prepareHeader(String& response, int code, const char* content_type, size_t contentLength) {
    response = "HTTP/1.1 ";
    response += String(code);
    response += " ";
    response += _responseCodeToString(code);
    response += "\r\n";

    if (!content_type)
        content_type = "text/html";

    sendHeader("Content-Type", content_type, true);
    if (_contentLength != CONTENT_LENGTH_UNKNOWN && _contentLength != CONTENT_LENGTH_NOT_SET) {
        sendHeader("Content-Length", String(_contentLength).c_str());
    }
    else if (contentLength > 0){
        sendHeader("Content-Length", String(contentLength).c_str());
    }
    sendHeader("Connection", "close");
    sendHeader("Access-Control-Allow-Origin", "*");

    response += _responseHeaders;
    response += "\r\n";
    _responseHeaders = String();
}

void ESP8266WebServer::send(int code, const char* content_type, const String& content) {
    String header;
    _prepareHeader(header, code, content_type, content.length());
    sendContent(header);

    sendContent(content);
}

void ESP8266WebServer::send_P(int code, PGM_P content_type, PGM_P content) {
    size_t contentLength = 0;

    if (content != NULL) {
        contentLength = strlen_P(content);
    }

    String header;
    _prepareHeader(header, code, String(FPSTR(content_type)).c_str(), contentLength);
    sendContent(header);
    sendContent_P(content);
}

void ESP8266WebServer::send(int code, char* content_type, const String& content) {
  send(code, (const char*)content_type, content);
}

void ESP8266WebServer::send(int code, const String& content_type, const String& content) {
  send(code, (const char*)content_type.c_str(), content);
}

void ESP8266WebServer::sendContent(const String& content) {
  const size_t unit_size = HTTP_DOWNLOAD_UNIT_SIZE;
  size_t size_to_send = content.length();
  const char* send_start = content.c_str();

  while (size_to_send) {
    size_t will_send = (size_to_send < unit_size) ? size_to_send : unit_size;
    size_t sent = _currentClient.write(send_start, will_send);
    if (sent == 0) {
      break;
    }
    size_to_send -= sent;
    send_start += sent;
  }
}

void ESP8266WebServer::sendContent_P(PGM_P content) {
    char contentUnit[HTTP_DOWNLOAD_UNIT_SIZE + 1];

    contentUnit[HTTP_DOWNLOAD_UNIT_SIZE] = '\0';

    while (content != NULL) {
        size_t contentUnitLen;
        PGM_P contentNext;

        // due to the memccpy signature, lots of casts are needed
        contentNext = (PGM_P)memccpy_P((void*)contentUnit, (PGM_VOID_P)content, 0, HTTP_DOWNLOAD_UNIT_SIZE);

        if (contentNext == NULL) {
            // no terminator, more data available
            content += HTTP_DOWNLOAD_UNIT_SIZE;
            contentUnitLen = HTTP_DOWNLOAD_UNIT_SIZE;
        }
        else {
            // reached terminator
            contentUnitLen = contentNext - content;
            content = NULL;
        }

        // write is so overloaded, had to use the cast to get it pick the right one
        _currentClient.write((const char*)contentUnit, contentUnitLen);
    }
}

void ESP8266WebServer::sendContent_P(PGM_P content, size_t size) {
    char contentUnit[HTTP_DOWNLOAD_UNIT_SIZE + 1];

    contentUnit[HTTP_DOWNLOAD_UNIT_SIZE] = '\0';

    while (content != NULL) {
        size_t contentUnitLen;
        PGM_P contentNext;

        // due to the memcpy signature, lots of casts are needed
        contentNext = (PGM_P)memcpy_P((void*)contentUnit, (PGM_VOID_P)content, HTTP_DOWNLOAD_UNIT_SIZE);

        if (contentNext == NULL) {
            // no terminator, more data available
            content += HTTP_DOWNLOAD_UNIT_SIZE;
            contentUnitLen = HTTP_DOWNLOAD_UNIT_SIZE;
        }
        else {
            // reached terminator
            contentUnitLen = contentNext - content;
            content = NULL;
        }

        if (size < WIFICLIENT_MAX_PACKET_SIZE) contentUnitLen = size;
        // write is so overloaded, had to use the cast to get it pick the right one
        _currentClient.write((const char*)contentUnit, contentUnitLen);
    }
}

String ESP8266WebServer::arg(const char* name) {
  for (int i = 0; i < _currentArgCount; ++i) {
    if (_currentArgs[i].key == name)
      return _currentArgs[i].value;
  }
  return String();
}

String ESP8266WebServer::arg(int i) {
  if (i < _currentArgCount)
    return _currentArgs[i].value;
  return String();
}

String ESP8266WebServer::argName(int i) {
  if (i < _currentArgCount)
    return _currentArgs[i].key;
  return String();
}

int ESP8266WebServer::args() {
  return _currentArgCount;
}

bool ESP8266WebServer::hasArg(const char* name) {
  for (int i = 0; i < _currentArgCount; ++i) {
    if (_currentArgs[i].key == name)
      return true;
  }
  return false;
}

String ESP8266WebServer::hostHeader() {
  return _hostHeader;
}

void ESP8266WebServer::onFileUpload(THandlerFunction fn) {
  _fileUploadHandler = fn;
}

void ESP8266WebServer::onNotFound(THandlerFunction fn) {
  _notFoundHandler = fn;
}

void ESP8266WebServer::_handleRequest() {
  RequestHandler* handler;
  for (handler = _firstHandler; handler; handler = handler->next) {
    if (handler->handle(*this, _currentMethod, _currentUri))
      break;
  }

  if (!handler){
#ifdef DEBUG
    DEBUG_OUTPUT.println("request handler not found");
#endif

    if(_notFoundHandler) {
      _notFoundHandler();
    }
    else {
      send(404, "text/plain", String("Not found: ") + _currentUri);
    }
  }

  uint16_t maxWait = HTTP_MAX_CLOSE_WAIT;
  while(_currentClient.connected() && maxWait--) {
    delay(1);
  }
  _currentClient   = WiFiClient();
  _currentUri      = String();
}

const char* ESP8266WebServer::_responseCodeToString(int code) {
  switch (code) {
    case 101: return "Switching Protocols";
    case 200: return "OK";
    case 403: return "Forbidden";
    case 404: return "Not found";
    case 500: return "Fail";
    default:  return "";
  }
}
<commit_msg>fix bug in WiFiClient::write_P/ESP8266WebServer::sendContent_P introduced few minutes ago when changing memccpy_P to memcpy_P<commit_after>/*
  ESP8266WebServer.cpp - Dead simple web-server.
  Supports only one simultaneous client, knows how to handle GET and POST.

  Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
*/

#include <Arduino.h>
#include "WiFiServer.h"
#include "WiFiClient.h"
#include "ESP8266WebServer.h"
#include "FS.h"
#include "detail/RequestHandler.h"
// #define DEBUG
#define DEBUG_OUTPUT Serial

ESP8266WebServer::ESP8266WebServer(int port)
: _server(port)
, _firstHandler(0)
, _lastHandler(0)
, _currentArgCount(0)
, _currentArgs(0)
{
}

ESP8266WebServer::~ESP8266WebServer()
{
  if (!_firstHandler)
    return;
  RequestHandler* handler = _firstHandler;
  while (handler) {
    RequestHandler* next = handler->next;
    delete handler;
    handler = next;
  }
}

void ESP8266WebServer::begin() {
  _server.begin();
}

void ESP8266WebServer::on(const char* uri, ESP8266WebServer::THandlerFunction handler)
{
  on(uri, HTTP_ANY, handler);
}

void ESP8266WebServer::on(const char* uri, HTTPMethod method, ESP8266WebServer::THandlerFunction fn)
{
  _addRequestHandler(new FunctionRequestHandler(fn, uri, method));
}

void ESP8266WebServer::_addRequestHandler(RequestHandler* handler) {
    if (!_lastHandler) {
      _firstHandler = handler;
      _lastHandler = handler;
    }
    else {
      _lastHandler->next = handler;
      _lastHandler = handler;
    }
}

void ESP8266WebServer::serveStatic(const char* uri, FS& fs, const char* path) {
    _addRequestHandler(new StaticRequestHandler(fs, uri));
}

void ESP8266WebServer::handleClient()
{
  WiFiClient client = _server.available();
  if (!client) {
    return;
  }

#ifdef DEBUG
  DEBUG_OUTPUT.println("New client");
#endif

  // Wait for data from client to become available
  uint16_t maxWait = HTTP_MAX_DATA_WAIT;
  while(client.connected() && !client.available() && maxWait--){
    delay(1);
  }

  if (!_parseRequest(client)) {
    return;
  }

  _currentClient = client;
  _contentLength = CONTENT_LENGTH_NOT_SET;
  _handleRequest();
}

void ESP8266WebServer::sendHeader(const String& name, const String& value, bool first) {
  String headerLine = name;
  headerLine += ": ";
  headerLine += value;
  headerLine += "\r\n";

  if (first) {
    _responseHeaders = headerLine + _responseHeaders;
  }
  else {
    _responseHeaders += headerLine;
  }
}

void ESP8266WebServer::_prepareHeader(String& response, int code, const char* content_type, size_t contentLength) {
    response = "HTTP/1.1 ";
    response += String(code);
    response += " ";
    response += _responseCodeToString(code);
    response += "\r\n";

    if (!content_type)
        content_type = "text/html";

    sendHeader("Content-Type", content_type, true);
    if (_contentLength != CONTENT_LENGTH_UNKNOWN && _contentLength != CONTENT_LENGTH_NOT_SET) {
        sendHeader("Content-Length", String(_contentLength).c_str());
    }
    else if (contentLength > 0){
        sendHeader("Content-Length", String(contentLength).c_str());
    }
    sendHeader("Connection", "close");
    sendHeader("Access-Control-Allow-Origin", "*");

    response += _responseHeaders;
    response += "\r\n";
    _responseHeaders = String();
}

void ESP8266WebServer::send(int code, const char* content_type, const String& content) {
    String header;
    _prepareHeader(header, code, content_type, content.length());
    sendContent(header);

    sendContent(content);
}

void ESP8266WebServer::send_P(int code, PGM_P content_type, PGM_P content) {
    size_t contentLength = 0;

    if (content != NULL) {
        contentLength = strlen_P(content);
    }

    String header;
    _prepareHeader(header, code, String(FPSTR(content_type)).c_str(), contentLength);
    sendContent(header);
    sendContent_P(content);
}

void ESP8266WebServer::send(int code, char* content_type, const String& content) {
  send(code, (const char*)content_type, content);
}

void ESP8266WebServer::send(int code, const String& content_type, const String& content) {
  send(code, (const char*)content_type.c_str(), content);
}

void ESP8266WebServer::sendContent(const String& content) {
  const size_t unit_size = HTTP_DOWNLOAD_UNIT_SIZE;
  size_t size_to_send = content.length();
  const char* send_start = content.c_str();

  while (size_to_send) {
    size_t will_send = (size_to_send < unit_size) ? size_to_send : unit_size;
    size_t sent = _currentClient.write(send_start, will_send);
    if (sent == 0) {
      break;
    }
    size_to_send -= sent;
    send_start += sent;
  }
}

void ESP8266WebServer::sendContent_P(PGM_P content) {
    char contentUnit[HTTP_DOWNLOAD_UNIT_SIZE + 1];

    contentUnit[HTTP_DOWNLOAD_UNIT_SIZE] = '\0';

    while (content != NULL) {
        size_t contentUnitLen;
        PGM_P contentNext;

        // due to the memccpy signature, lots of casts are needed
        contentNext = (PGM_P)memccpy_P((void*)contentUnit, (PGM_VOID_P)content, 0, HTTP_DOWNLOAD_UNIT_SIZE);

        if (contentNext == NULL) {
            // no terminator, more data available
            content += HTTP_DOWNLOAD_UNIT_SIZE;
            contentUnitLen = HTTP_DOWNLOAD_UNIT_SIZE;
        }
        else {
            // reached terminator
            contentUnitLen = contentNext - content;
            content = NULL;
        }

        // write is so overloaded, had to use the cast to get it pick the right one
        _currentClient.write((const char*)contentUnit, contentUnitLen);
    }
}

void ESP8266WebServer::sendContent_P(PGM_P content, size_t size) {
    char contentUnit[HTTP_DOWNLOAD_UNIT_SIZE + 1];
    contentUnit[HTTP_DOWNLOAD_UNIT_SIZE] = '\0';
    size_t remaining_size = size;

    while (content != NULL && remaining_size > 0) {
        size_t contentUnitLen = HTTP_DOWNLOAD_UNIT_SIZE;

        if (remaining_size < HTTP_DOWNLOAD_UNIT_SIZE) contentUnitLen = remaining_size;
        // due to the memcpy signature, lots of casts are needed
        memcpy_P((void*)contentUnit, (PGM_VOID_P)content, contentUnitLen);

        content += contentUnitLen;
        remaining_size -= contentUnitLen;

        // write is so overloaded, had to use the cast to get it pick the right one
        _currentClient.write((const char*)contentUnit, contentUnitLen);
    }
}

String ESP8266WebServer::arg(const char* name) {
  for (int i = 0; i < _currentArgCount; ++i) {
    if (_currentArgs[i].key == name)
      return _currentArgs[i].value;
  }
  return String();
}

String ESP8266WebServer::arg(int i) {
  if (i < _currentArgCount)
    return _currentArgs[i].value;
  return String();
}

String ESP8266WebServer::argName(int i) {
  if (i < _currentArgCount)
    return _currentArgs[i].key;
  return String();
}

int ESP8266WebServer::args() {
  return _currentArgCount;
}

bool ESP8266WebServer::hasArg(const char* name) {
  for (int i = 0; i < _currentArgCount; ++i) {
    if (_currentArgs[i].key == name)
      return true;
  }
  return false;
}

String ESP8266WebServer::hostHeader() {
  return _hostHeader;
}

void ESP8266WebServer::onFileUpload(THandlerFunction fn) {
  _fileUploadHandler = fn;
}

void ESP8266WebServer::onNotFound(THandlerFunction fn) {
  _notFoundHandler = fn;
}

void ESP8266WebServer::_handleRequest() {
  RequestHandler* handler;
  for (handler = _firstHandler; handler; handler = handler->next) {
    if (handler->handle(*this, _currentMethod, _currentUri))
      break;
  }

  if (!handler){
#ifdef DEBUG
    DEBUG_OUTPUT.println("request handler not found");
#endif

    if(_notFoundHandler) {
      _notFoundHandler();
    }
    else {
      send(404, "text/plain", String("Not found: ") + _currentUri);
    }
  }

  uint16_t maxWait = HTTP_MAX_CLOSE_WAIT;
  while(_currentClient.connected() && maxWait--) {
    delay(1);
  }
  _currentClient   = WiFiClient();
  _currentUri      = String();
}

const char* ESP8266WebServer::_responseCodeToString(int code) {
  switch (code) {
    case 101: return "Switching Protocols";
    case 200: return "OK";
    case 403: return "Forbidden";
    case 404: return "Not found";
    case 500: return "Fail";
    default:  return "";
  }
}
<|endoftext|>


<commit_before>/**
 * Throughout these tests, you might see some notation indicating the state of
 * a placement allocator. General things to know:
 *
 * - R = active region structure in memory
 * - S = inactive region structure in the stack
 * - space = a free piece of memory
 *
 * I denote disjoint regions using brackets ([ and ]). I separate cells of
 * memory using vertical bars (|).
 */

#include "scoped-pass.hpp"
#include <analloc2/free-list>

using namespace analloc;
using namespace ansa;

template <size_t Capacity>
void TestAll();

template <size_t Capacity>
void TestSimplePlace();

template <size_t Capacity>
void TestOffsetPlace();

template <size_t Capacity>
void TestNormalAllocOverflow();

template <size_t Capacity>
void TestDoubleAllocOverflow();

template <size_t Capacity>
size_t ComputePreambleSize();

template <size_t Capacity>
size_t ComputeRegionSize();

int main() {
  TestAll<3>();
  TestAll<4>();
  TestAll<5>();
  TestAll<6>();
  TestAll<0x10>();
  return 0;
}

template <size_t Capacity>
void TestAll() {
  TestSimplePlace<Capacity>();
  TestOffsetPlace<Capacity>();
  TestNormalAllocOverflow<Capacity>();
  TestDoubleAllocOverflow<Capacity>();
}

template <size_t Capacity>
void TestSimplePlace() {
  ScopedPass pass("PlacedFreeList<", Capacity, ">::Place() [simple]");

  typedef PlacedFreeList<Capacity> Pfl;

  size_t regionSize = ComputeRegionSize<Capacity>();
  size_t preamble = ComputePreambleSize<Capacity>();

  // Create a buffer that gives everything the proper alignment.
  void * buffer;
  assert(!posix_memalign(&buffer, regionSize, preamble + regionSize * 3));
  uintptr_t start = (uintptr_t)buffer;
  uintptr_t addr;
  assert(!Pfl::Place(start, preamble));
  assert(!Pfl::Place(start, preamble + regionSize - 1));
  Pfl * pfl = Pfl::Place(start, preamble + regionSize);
  assert(!pfl->Alloc(addr, 1));
  assert(!pfl->Alloc(addr, 0));
  pfl = Pfl::Place(start, preamble + regionSize * 2);
  assert(!pfl->Alloc(addr, 1));
  assert(!pfl->Alloc(addr, 0));
  pfl = Pfl::Place(start, preamble + regionSize * 3);
  assert(!pfl->Alloc(addr, regionSize + 1));
  assert(pfl->Alloc(addr, regionSize));
  assert(addr == start + preamble + regionSize * 2);
  assert(!pfl->Alloc(addr, 1));

  free(buffer);
}

template <size_t Capacity>
void TestOffsetPlace() {
  ScopedPass pass("PlacedFreeList<", Capacity, ">::Place() [offset]");

  typedef PlacedFreeList<Capacity> Pfl;

  size_t objectAlign = sizeof(void *);
  size_t regionSize = (size_t)1 << Log2Ceil(sizeof(typename Pfl::FreeRegion));
  regionSize = Align2(regionSize, objectAlign);
  size_t instanceSize = Align2(sizeof(Pfl), objectAlign);
  size_t stackSize = Align2(sizeof(typename Pfl::StackType), objectAlign);
  size_t preamble = instanceSize + stackSize;
  size_t misalignment = (preamble + objectAlign) % regionSize;
  if (misalignment) {
    preamble += regionSize - misalignment;
  }

  // Create a buffer that gives everything the proper alignment.
  void * buffer;
  assert(!posix_memalign(&buffer, regionSize, objectAlign + preamble +
                         regionSize * 3));
  uintptr_t start = (uintptr_t)buffer + objectAlign;
  uintptr_t addr;
  assert(!Pfl::Place(start, preamble));
  assert(!Pfl::Place(start, preamble + regionSize - 1));
  Pfl * pfl = Pfl::Place(start, preamble + regionSize);
  assert(!pfl->Alloc(addr, 1));
  assert(!pfl->Alloc(addr, 0));
  pfl = Pfl::Place(start, preamble + regionSize * 2);
  assert(!pfl->Alloc(addr, 1));
  assert(!pfl->Alloc(addr, 0));
  pfl = Pfl::Place(start, preamble + regionSize * 3);
  assert(!pfl->Alloc(addr, regionSize + 1));
  assert(pfl->Alloc(addr, regionSize));
  assert(addr == start + preamble + regionSize * 2);
  assert(!pfl->Alloc(addr, 1));

  free(buffer);
}

template <size_t Capacity>
void TestNormalAllocOverflow() {

}

template <size_t Capacity>
void TestDoubleAllocOverflow() {
  ScopedPass pass("PlacedFreeList<", Capacity, ">::Alloc() [double overflow]");

  typedef PlacedFreeList<Capacity> Pfl;

  uintptr_t addr;

  size_t preambleSize = ComputePreambleSize<Capacity>();
  size_t regionSize = ComputeRegionSize<Capacity>();

  // Let R=regionSize, N=Capacity
  // We will have:
  // - one region of size R*(N+2)
  // - N-1 regions of size R*(N+3)
  // - Every region will be separated by R bytes
  // Total size needed = R*(N+2) + (N-1)*R*(N+3) + R*N

  size_t headingSize = regionSize * (Capacity + 2);
  size_t blockSize = regionSize * (Capacity + 3);

  size_t totalSize = headingSize + blockSize * (Capacity - 1) +
      regionSize * Capacity;

  void * buffer;
  assert(!posix_memalign(&buffer, regionSize, totalSize));
  uintptr_t start = (uintptr_t)buffer;
  Pfl * pfl = Pfl::Place(start, regionSize * (Capacity + 1));
  assert(pfl->GetStackCount() == 1);

  // Currently, the allocator looks like this: [S|R| |...]

  // Add a bunch of blocks
  for (size_t i = 1; i < Capacity; ++i) {
    size_t offset = headingSize + i * regionSize + (i - 1) * blockSize;
    pfl->Dealloc(start + offset, blockSize);
    assert(pfl->GetStackCount() == 1);
  }

  // Currently, the allocator looks like this:
  // [R|R|...|S| ] [ |...] ... [ |...]

  // Each time we Alloc() another block, it moves a region structure to the
  // stack as follows:
  // [S|R|...|S| ]
  // [S|R|S|R...|S| ]
  // [S|R|S|S|R...|S| ]
  // Thus, after Capacity - 2 Alloc()s, the stack will contain Capacity - 1
  // items:
  // [S|R|S|S|S|...|R|S| ]

  for (size_t i = Capacity - 1; i > 1; --i) {
    size_t offset = headingSize + i * regionSize + (i - 1) * blockSize;
    assert(pfl->Alloc(addr, blockSize));
    assert(addr == start + offset);
    size_t stackCount = 1 + (Capacity - i);
    assert(pfl->GetStackCount() == stackCount);
  }

  // When we allocate the next block, the heading will at first look like this:
  // [S|R|S|S|...|S|S|S| ]
  // To address this, the stack will remove the last element from the stack:
  // [S|R|S|S|...|S|X|S| ]
  // Then, it will need a new region, so it will remove the next element from
  // the stack as well:
  // [S|R|S|S|...|R| |S| ]

  assert(pfl->Alloc(addr, regionSize));
  assert(addr == start + headingSize + regionSize);
  assert(pfl->GetStackCount() == Capacity - 2);

  // The first free region is now located near the end of the heading.
  uintptr_t addr;
  assert(pfl->Alloc(addr, regionSize));
  assert(addr == start + headingSize - (regionSize * 3));
}

template <size_t Capacity>
size_t ComputePreambleSize() {
  typedef PlacedFreeList<Capacity> Pfl;

  size_t objectAlign = sizeof(void *);
  size_t regionSize = ComputeRegionSize<Capacity>();
  size_t instanceSize = Align2(sizeof(Pfl), objectAlign);
  size_t stackSize = Align2(sizeof(typename Pfl::StackType), objectAlign);
  return Align2(instanceSize + stackSize, regionSize);
}

template <size_t Capacity>
size_t ComputeRegionSize() {
  typedef PlacedFreeList<Capacity> Pfl;
  size_t objectAlign = sizeof(void *);
  size_t regionSize = (size_t)1 << Log2Ceil(sizeof(typename Pfl::FreeRegion));
  return Align2(regionSize, objectAlign);
}
<commit_msg>double overflow test passes<commit_after>/**
 * Throughout these tests, you might see some notation indicating the state of
 * a placement allocator. General things to know:
 *
 * - R = active region structure in memory
 * - S = inactive region structure in the stack
 * - space = a free piece of memory
 *
 * I denote disjoint regions using brackets ([ and ]). I separate cells of
 * memory using vertical bars (|).
 */

#include "scoped-pass.hpp"
#include <analloc2/free-list>

using namespace analloc;
using namespace ansa;

template <size_t Capacity>
void TestAll();

template <size_t Capacity>
void TestSimplePlace();

template <size_t Capacity>
void TestOffsetPlace();

template <size_t Capacity>
void TestNormalAllocOverflow();

template <size_t Capacity>
void TestDoubleAllocOverflow();

template <size_t Capacity>
size_t ComputePreambleSize();

template <size_t Capacity>
size_t ComputeRegionSize();

int main() {
  TestAll<3>();
  TestAll<4>();
  TestAll<5>();
  TestAll<6>();
  TestAll<0x10>();
  return 0;
}

template <size_t Capacity>
void TestAll() {
  TestSimplePlace<Capacity>();
  TestOffsetPlace<Capacity>();
  TestNormalAllocOverflow<Capacity>();
  TestDoubleAllocOverflow<Capacity>();
}

template <size_t Capacity>
void TestSimplePlace() {
  ScopedPass pass("PlacedFreeList<", Capacity, ">::Place() [simple]");

  typedef PlacedFreeList<Capacity> Pfl;

  size_t regionSize = ComputeRegionSize<Capacity>();
  size_t preamble = ComputePreambleSize<Capacity>();

  // Create a buffer that gives everything the proper alignment.
  void * buffer;
  assert(!posix_memalign(&buffer, regionSize, preamble + regionSize * 3));
  uintptr_t start = (uintptr_t)buffer;
  uintptr_t addr;
  assert(!Pfl::Place(start, preamble));
  assert(!Pfl::Place(start, preamble + regionSize - 1));
  Pfl * pfl = Pfl::Place(start, preamble + regionSize);
  assert(!pfl->Alloc(addr, 1));
  assert(!pfl->Alloc(addr, 0));
  pfl = Pfl::Place(start, preamble + regionSize * 2);
  assert(!pfl->Alloc(addr, 1));
  assert(!pfl->Alloc(addr, 0));
  pfl = Pfl::Place(start, preamble + regionSize * 3);
  assert(!pfl->Alloc(addr, regionSize + 1));
  assert(pfl->Alloc(addr, regionSize));
  assert(addr == start + preamble + regionSize * 2);
  assert(!pfl->Alloc(addr, 1));

  free(buffer);
}

template <size_t Capacity>
void TestOffsetPlace() {
  ScopedPass pass("PlacedFreeList<", Capacity, ">::Place() [offset]");

  typedef PlacedFreeList<Capacity> Pfl;

  size_t objectAlign = sizeof(void *);
  size_t regionSize = (size_t)1 << Log2Ceil(sizeof(typename Pfl::FreeRegion));
  regionSize = Align2(regionSize, objectAlign);
  size_t instanceSize = Align2(sizeof(Pfl), objectAlign);
  size_t stackSize = Align2(sizeof(typename Pfl::StackType), objectAlign);
  size_t preamble = instanceSize + stackSize;
  size_t misalignment = (preamble + objectAlign) % regionSize;
  if (misalignment) {
    preamble += regionSize - misalignment;
  }

  // Create a buffer that gives everything the proper alignment.
  void * buffer;
  assert(!posix_memalign(&buffer, regionSize, objectAlign + preamble +
                         regionSize * 3));
  uintptr_t start = (uintptr_t)buffer + objectAlign;
  uintptr_t addr;
  assert(!Pfl::Place(start, preamble));
  assert(!Pfl::Place(start, preamble + regionSize - 1));
  Pfl * pfl = Pfl::Place(start, preamble + regionSize);
  assert(!pfl->Alloc(addr, 1));
  assert(!pfl->Alloc(addr, 0));
  pfl = Pfl::Place(start, preamble + regionSize * 2);
  assert(!pfl->Alloc(addr, 1));
  assert(!pfl->Alloc(addr, 0));
  pfl = Pfl::Place(start, preamble + regionSize * 3);
  assert(!pfl->Alloc(addr, regionSize + 1));
  assert(pfl->Alloc(addr, regionSize));
  assert(addr == start + preamble + regionSize * 2);
  assert(!pfl->Alloc(addr, 1));

  free(buffer);
}

template <size_t Capacity>
void TestNormalAllocOverflow() {

}

template <size_t Capacity>
void TestDoubleAllocOverflow() {
  // NOTE: most of the visual examples in this test make use of a large
  // Capacity to give a good depiction of what's going on. This test works for
  // Capacity >= 3, but the visual examples don't.

  ScopedPass pass("PlacedFreeList<", Capacity, ">::Alloc() [double overflow]");

  typedef PlacedFreeList<Capacity> Pfl;

  uintptr_t addr;

  size_t preambleSize = ComputePreambleSize<Capacity>();
  size_t regionSize = ComputeRegionSize<Capacity>();

  // Let R=regionSize, N=Capacity
  // We will have:
  // - one region of size R*(N+2)
  // - N-1 regions of size R*(N+3)
  // - Every region will be separated by R bytes
  // Total size needed = R*(N+2) + (N-1)*R*(N+3) + R*(N - 1)

  size_t headingSize = regionSize * (Capacity + 2);
  size_t blockSize = regionSize * (Capacity + 3);

  size_t totalSize = preambleSize + headingSize + blockSize * (Capacity - 1) +
      regionSize * (Capacity - 1);

  void * buffer;
  assert(!posix_memalign(&buffer, regionSize, totalSize));

  Pfl * pfl = Pfl::Place((uintptr_t)buffer, preambleSize + headingSize);
  assert(pfl != nullptr);
  assert(pfl->GetStackCount() == 1);

  uintptr_t start = (uintptr_t)buffer + preambleSize;

  // Currently, the allocator looks like this: [S|R| |...]

  // Add a bunch of blocks
  for (size_t i = 1; i < Capacity; ++i) {
    size_t offset = headingSize + i * regionSize + (i - 1) * blockSize;
    pfl->Dealloc(start + offset, blockSize);
    assert(pfl->GetStackCount() == 1);
  }

  // Currently, the allocator looks like this:
  // [R|R|...|S| ] [ |...] ... [ |...]

  // Each time we Alloc() another block, it moves a region structure to the
  // stack as follows:
  // [S|R|...|S| ]
  // [S|R|S|R...|S| ]
  // [S|R|S|S|R...|S| ]
  // Thus, after Capacity - 2 Alloc()s, the stack will contain Capacity - 1
  // items:
  // [S|R|S|S|S|...|R|S| ]

  for (size_t i = 1; i < Capacity - 1; ++i) {
    assert(pfl->GetStackCount() == i);
    size_t offset = headingSize + i * regionSize + (i - 1) * blockSize;
    assert(pfl->Alloc(addr, blockSize));
    assert(addr == start + offset);
    assert(pfl->GetStackCount() == i + 1);
  }

  // When we allocate the next block, the heading will at first look like this:
  // [S|R|S|S|...|S|S|S| ]
  // To address this, the stack will remove the last element from the stack:
  // [S|R|S|S|...|S|X|S| ]
  // Then, it will need a new region, so it will remove the next element from
  // the stack as well:
  // [S|R|S|S|...|R| |S| ]

  assert(pfl->Alloc(addr, blockSize));
  assert(addr == start + headingSize + regionSize * (Capacity - 1) +
                 blockSize * (Capacity - 2));
  assert(pfl->GetStackCount() == Capacity - 2);

  // The first free region is now located near the end of the heading.
  assert(pfl->Alloc(addr, regionSize));
  assert(addr == start + headingSize - (regionSize * 3));
}

template <size_t Capacity>
size_t ComputePreambleSize() {
  typedef PlacedFreeList<Capacity> Pfl;

  size_t objectAlign = sizeof(void *);
  size_t regionSize = ComputeRegionSize<Capacity>();
  size_t instanceSize = Align2(sizeof(Pfl), objectAlign);
  size_t stackSize = Align2(sizeof(typename Pfl::StackType), objectAlign);
  return Align2(instanceSize + stackSize, regionSize);
}

template <size_t Capacity>
size_t ComputeRegionSize() {
  typedef PlacedFreeList<Capacity> Pfl;
  size_t objectAlign = sizeof(void *);
  size_t regionSize = (size_t)1 << Log2Ceil(sizeof(typename Pfl::FreeRegion));
  return Align2(regionSize, objectAlign);
}
<|endoftext|>


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

#include "modules/prediction/predictor/sequence/sequence_predictor.h"

#include <cmath>
#include <limits>
#include <memory>
#include <utility>

#include "modules/common/adapters/proto/adapter_config.pb.h"

#include "modules/common/math/vec2d.h"
#include "modules/prediction/common/prediction_gflags.h"
#include "modules/prediction/common/prediction_map.h"
#include "modules/prediction/common/road_graph.h"
#include "modules/prediction/container/adc_trajectory/adc_trajectory_container.h"
#include "modules/prediction/container/container_manager.h"
#include "modules/prediction/container/pose/pose_container.h"

namespace apollo {
namespace prediction {

using ::apollo::common::PathPoint;
using ::apollo::common::TrajectoryPoint;
using ::apollo::common::adapter::AdapterConfig;
using ::apollo::common::math::Vec2d;
using ::apollo::hdmap::LaneInfo;

void SequencePredictor::Predict(Obstacle* obstacle) {
  Clear();

  CHECK_NOTNULL(obstacle);
  CHECK_GT(obstacle->history_size(), 0);
}

void SequencePredictor::Clear() { Predictor::Clear(); }

std::string SequencePredictor::ToString(const LaneSequence& sequence) {
  std::string str_lane_sequence = "";
  if (sequence.lane_segment_size() > 0) {
    str_lane_sequence += sequence.lane_segment(0).lane_id();
  }
  for (int i = 1; i < sequence.lane_segment_size(); ++i) {
    str_lane_sequence += ("->" + sequence.lane_segment(i).lane_id());
  }
  return str_lane_sequence;
}

void SequencePredictor::FilterLaneSequences(
    const Feature& feature, const std::string& lane_id,
    std::vector<bool>* enable_lane_sequence) {
  if (!feature.has_lane() || !feature.lane().has_lane_graph()) {
    return;
  }
  const LaneGraph& lane_graph = feature.lane().lane_graph();
  int num_lane_sequence = lane_graph.lane_sequence_size();
  std::vector<LaneChangeType> lane_change_type(num_lane_sequence,
                                               LaneChangeType::INVALID);
  std::pair<int, double> change(-1, -1.0);
  std::pair<int, double> all(-1, -1.0);

  for (int i = 0; i < num_lane_sequence; ++i) {
    const LaneSequence& sequence = lane_graph.lane_sequence(i);
    lane_change_type[i] = GetLaneChangeType(lane_id, sequence);

    if (lane_change_type[i] != LaneChangeType::LEFT &&
        lane_change_type[i] != LaneChangeType::RIGHT &&
        lane_change_type[i] != LaneChangeType::ONTO_LANE) {
      ADEBUG "Ignore lane sequence [" << ToString(sequence) << "].";
      continue;
    }

    // The obstacle has interference with ADC within a small distance
    double distance = GetLaneChangeDistanceWithADC(sequence);
    ADEBUG << "Distance to ADC " << std::fixed << std::setprecision(6)
           << distance;
    if (distance > 0.0 && distance < FLAGS_lane_change_dist) {
      (*enable_lane_sequence)[i] = false;
      ADEBUG << "Filter trajectory [" << ToString(sequence)
             << "] due to small distance " << distance << ".";
    }
  }

  for (int i = 0; i < num_lane_sequence; ++i) {
    const LaneSequence& sequence = lane_graph.lane_sequence(i);

    if (!(*enable_lane_sequence)[i]) {
      ADEBUG << "Disabled lane sequence [" << ToString(sequence) << "].";
      continue;
    }

    double probability = sequence.probability();
    if (LaneSequenceWithMaxProb(lane_change_type[i], probability, all.second)) {
      all.first = i;
      all.second = probability;
    }
    if (LaneChangeWithMaxProb(lane_change_type[i], probability,
                              change.second)) {
      change.first = i;
      change.second = probability;
    }
  }

  for (int i = 0; i < num_lane_sequence; ++i) {
    const LaneSequence& sequence = lane_graph.lane_sequence(i);

    if (!(*enable_lane_sequence)[i]) {
      ADEBUG << "Disabled lane sequence [" << ToString(sequence) << "].";
      continue;
    }

    double probability = sequence.probability();
    if (probability < FLAGS_lane_sequence_threshold && i != all.first) {
      (*enable_lane_sequence)[i] = false;
    } else if (change.first >= 0 && change.first < num_lane_sequence &&
               (lane_change_type[i] == LaneChangeType::LEFT ||
                lane_change_type[i] == LaneChangeType::RIGHT) &&
               lane_change_type[i] != lane_change_type[change.first]) {
      (*enable_lane_sequence)[i] = false;
    }
  }
}

SequencePredictor::LaneChangeType SequencePredictor::GetLaneChangeType(
    const std::string& lane_id, const LaneSequence& lane_sequence) {
  if (lane_id.empty()) {
    return LaneChangeType::ONTO_LANE;
  }

  std::string lane_change_id = lane_sequence.lane_segment(0).lane_id();
  if (lane_id == lane_change_id) {
    return LaneChangeType::STRAIGHT;
  } else {
    if (PredictionMap::IsLeftNeighborLane(
            PredictionMap::LaneById(lane_change_id),
            PredictionMap::LaneById(lane_id))) {
      return LaneChangeType::LEFT;
    } else if (PredictionMap::IsRightNeighborLane(
                   PredictionMap::LaneById(lane_change_id),
                   PredictionMap::LaneById(lane_id))) {
      return LaneChangeType::RIGHT;
    }
  }
  return LaneChangeType::INVALID;
}

double SequencePredictor::GetLaneChangeDistanceWithADC(
    const LaneSequence& lane_sequence) {
  PoseContainer* pose_container = dynamic_cast<PoseContainer*>(
      ContainerManager::Instance()->GetContainer(AdapterConfig::LOCALIZATION));
  ADCTrajectoryContainer* adc_container = dynamic_cast<ADCTrajectoryContainer*>(
      ContainerManager::Instance()->GetContainer(
          AdapterConfig::PLANNING_TRAJECTORY));
  CHECK_NOTNULL(pose_container);
  CHECK_NOTNULL(adc_container);

  if (!adc_container->HasOverlap(lane_sequence)) {
    ADEBUG << "The sequence [" << ToString(lane_sequence)
           << "] has no overlap with ADC.";
    return std::numeric_limits<double>::max();
  }

  Eigen::Vector2d adc_position;
  if (pose_container->ToPerceptionObstacle() != nullptr) {
    adc_position[0] = pose_container->ToPerceptionObstacle()->position().x();
    adc_position[1] = pose_container->ToPerceptionObstacle()->position().y();

    std::string obstacle_lane_id = lane_sequence.lane_segment(0).lane_id();
    double obstacle_lane_s = lane_sequence.lane_segment(0).start_s();
    double lane_s = 0.0;
    double lane_l = 0.0;
    if (PredictionMap::GetProjection(adc_position,
                                     PredictionMap::LaneById(obstacle_lane_id),
                                     &lane_s, &lane_l)) {
      ADEBUG << "Distance with ADC is " << std::fabs(lane_s - obstacle_lane_s);
      return obstacle_lane_s - lane_s;
    }
  }

  ADEBUG << "Invalid ADC pose.";
  return std::numeric_limits<double>::max();
}

bool SequencePredictor::LaneSequenceWithMaxProb(const LaneChangeType& type,
                                                const double probability,
                                                const double max_prob) {
  if (probability > max_prob) {
    return true;
  } else {
    double prob_diff = std::fabs(probability - max_prob);
    if (prob_diff <= std::numeric_limits<double>::epsilon() &&
        type == LaneChangeType::STRAIGHT) {
      return true;
    }
  }
  return false;
}

bool SequencePredictor::LaneChangeWithMaxProb(const LaneChangeType& type,
                                              const double probability,
                                              const double max_prob) {
  if (type == LaneChangeType::LEFT || type == LaneChangeType::RIGHT) {
    if (probability > max_prob) {
      return true;
    }
  }
  return false;
}

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

#include "modules/prediction/predictor/sequence/sequence_predictor.h"

#include <cmath>
#include <limits>
#include <memory>
#include <utility>

#include "modules/common/adapters/proto/adapter_config.pb.h"

#include "modules/common/math/vec2d.h"
#include "modules/prediction/common/prediction_gflags.h"
#include "modules/prediction/common/prediction_map.h"
#include "modules/prediction/common/road_graph.h"
#include "modules/prediction/container/adc_trajectory/adc_trajectory_container.h"
#include "modules/prediction/container/container_manager.h"
#include "modules/prediction/container/pose/pose_container.h"

namespace apollo {
namespace prediction {

using ::apollo::common::PathPoint;
using ::apollo::common::TrajectoryPoint;
using ::apollo::common::adapter::AdapterConfig;
using ::apollo::common::math::Vec2d;
using ::apollo::hdmap::LaneInfo;

void SequencePredictor::Predict(Obstacle* obstacle) {
  Clear();

  CHECK_NOTNULL(obstacle);
  CHECK_GT(obstacle->history_size(), 0);
}

void SequencePredictor::Clear() { Predictor::Clear(); }

std::string SequencePredictor::ToString(const LaneSequence& sequence) {
  std::string str_lane_sequence = "";
  if (sequence.lane_segment_size() > 0) {
    str_lane_sequence += sequence.lane_segment(0).lane_id();
  }
  for (int i = 1; i < sequence.lane_segment_size(); ++i) {
    str_lane_sequence += ("->" + sequence.lane_segment(i).lane_id());
  }
  return str_lane_sequence;
}

void SequencePredictor::FilterLaneSequences(
    const Feature& feature, const std::string& lane_id,
    std::vector<bool>* enable_lane_sequence) {
  if (!feature.has_lane() || !feature.lane().has_lane_graph()) {
    return;
  }
  const LaneGraph& lane_graph = feature.lane().lane_graph();
  int num_lane_sequence = lane_graph.lane_sequence_size();
  std::vector<LaneChangeType> lane_change_type(num_lane_sequence,
                                               LaneChangeType::INVALID);
  std::pair<int, double> change(-1, -1.0);
  std::pair<int, double> all(-1, -1.0);

  /**
    * Filter out those obstacles that are close to the ADC
    * so that we will ignore them and drive normally unless
    * they really kick into our lane.
    */
  for (int i = 0; i < num_lane_sequence; ++i) {
    const LaneSequence& sequence = lane_graph.lane_sequence(i);
    lane_change_type[i] = GetLaneChangeType(lane_id, sequence);

    if (lane_change_type[i] != LaneChangeType::LEFT &&
        lane_change_type[i] != LaneChangeType::RIGHT &&
        lane_change_type[i] != LaneChangeType::ONTO_LANE) {
      ADEBUG "Ignore lane sequence [" << ToString(sequence) << "].";
      continue;
    }

    // The obstacle has interference with ADC within a small distance
    double distance = GetLaneChangeDistanceWithADC(sequence);
    ADEBUG << "Distance to ADC " << std::fixed << std::setprecision(6)
           << distance;
    if (distance > 0.0 && distance < FLAGS_lane_change_dist) {
      (*enable_lane_sequence)[i] = false;
      ADEBUG << "Filter trajectory [" << ToString(sequence)
             << "] due to small distance " << distance << ".";
    }
  }

  /**
    * Pick the most probable lane-sequence and lane-change
    */
  for (int i = 0; i < num_lane_sequence; ++i) {
    const LaneSequence& sequence = lane_graph.lane_sequence(i);

    if (!(*enable_lane_sequence)[i]) {
      ADEBUG << "Disabled lane sequence [" << ToString(sequence) << "].";
      continue;
    }

    double probability = sequence.probability();
    if (LaneSequenceWithMaxProb(lane_change_type[i], probability, all.second)) {
      all.first = i;
      all.second = probability;
    }
    if (LaneChangeWithMaxProb(lane_change_type[i], probability,
                              change.second)) {
      change.first = i;
      change.second = probability;
    }
  }

  for (int i = 0; i < num_lane_sequence; ++i) {
    const LaneSequence& sequence = lane_graph.lane_sequence(i);

    if (!(*enable_lane_sequence)[i]) {
      ADEBUG << "Disabled lane sequence [" << ToString(sequence) << "].";
      continue;
    }

    double probability = sequence.probability();
    if (probability < FLAGS_lane_sequence_threshold && i != all.first) {
      (*enable_lane_sequence)[i] = false;
    } else if (change.first >= 0 && change.first < num_lane_sequence &&
               (lane_change_type[i] == LaneChangeType::LEFT ||
                lane_change_type[i] == LaneChangeType::RIGHT) &&
               lane_change_type[i] != lane_change_type[change.first]) {
      (*enable_lane_sequence)[i] = false;
    }
  }
}

SequencePredictor::LaneChangeType SequencePredictor::GetLaneChangeType(
    const std::string& lane_id, const LaneSequence& lane_sequence) {
  if (lane_id.empty()) {
    return LaneChangeType::ONTO_LANE;
  }

  std::string lane_change_id = lane_sequence.lane_segment(0).lane_id();
  if (lane_id == lane_change_id) {
    return LaneChangeType::STRAIGHT;
  } else {
    if (PredictionMap::IsLeftNeighborLane(
            PredictionMap::LaneById(lane_change_id),
            PredictionMap::LaneById(lane_id))) {
      return LaneChangeType::LEFT;
    } else if (PredictionMap::IsRightNeighborLane(
                   PredictionMap::LaneById(lane_change_id),
                   PredictionMap::LaneById(lane_id))) {
      return LaneChangeType::RIGHT;
    }
  }
  return LaneChangeType::INVALID;
}

double SequencePredictor::GetLaneChangeDistanceWithADC(
    const LaneSequence& lane_sequence) {
  PoseContainer* pose_container = dynamic_cast<PoseContainer*>(
      ContainerManager::Instance()->GetContainer(AdapterConfig::LOCALIZATION));
  ADCTrajectoryContainer* adc_container = dynamic_cast<ADCTrajectoryContainer*>(
      ContainerManager::Instance()->GetContainer(
          AdapterConfig::PLANNING_TRAJECTORY));
  CHECK_NOTNULL(pose_container);
  CHECK_NOTNULL(adc_container);

  if (!adc_container->HasOverlap(lane_sequence)) {
    ADEBUG << "The sequence [" << ToString(lane_sequence)
           << "] has no overlap with ADC.";
    return std::numeric_limits<double>::max();
  }

  Eigen::Vector2d adc_position;
  if (pose_container->ToPerceptionObstacle() != nullptr) {
    adc_position[0] = pose_container->ToPerceptionObstacle()->position().x();
    adc_position[1] = pose_container->ToPerceptionObstacle()->position().y();

    std::string obstacle_lane_id = lane_sequence.lane_segment(0).lane_id();
    double obstacle_lane_s = lane_sequence.lane_segment(0).start_s();
    double lane_s = 0.0;
    double lane_l = 0.0;
    if (PredictionMap::GetProjection(adc_position,
                                     PredictionMap::LaneById(obstacle_lane_id),
                                     &lane_s, &lane_l)) {
      ADEBUG << "Distance with ADC is " << std::fabs(lane_s - obstacle_lane_s);
      return obstacle_lane_s - lane_s;
    }
  }

  ADEBUG << "Invalid ADC pose.";
  return std::numeric_limits<double>::max();
}

bool SequencePredictor::LaneSequenceWithMaxProb(const LaneChangeType& type,
                                                const double probability,
                                                const double max_prob) {
  if (probability > max_prob) {
    return true;
  } else {
    double prob_diff = std::fabs(probability - max_prob);
    if (prob_diff <= std::numeric_limits<double>::epsilon() &&
        type == LaneChangeType::STRAIGHT) {
      return true;
    }
  }
  return false;
}

bool SequencePredictor::LaneChangeWithMaxProb(const LaneChangeType& type,
                                              const double probability,
                                              const double max_prob) {
  if (type == LaneChangeType::LEFT || type == LaneChangeType::RIGHT) {
    if (probability > max_prob) {
      return true;
    }
  }
  return false;
}

}  // namespace prediction
}  // namespace apollo
<|endoftext|>


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

#include "net/instaweb/rewriter/public/redirect_on_size_limit_filter.h"

#include <algorithm>

#include "net/instaweb/rewriter/public/rewrite_driver.h"
#include "net/instaweb/rewriter/public/rewrite_options.h"
#include "net/instaweb/rewriter/public/rewrite_test_base.h"
#include "net/instaweb/rewriter/public/test_rewrite_driver_factory.h"
#include "pagespeed/kernel/base/gtest.h"
#include "pagespeed/kernel/base/string.h"
#include "pagespeed/kernel/base/string_util.h"
#include "pagespeed/kernel/base/string_writer.h"

namespace net_instaweb {

namespace {

static const char kScript[] = "";

}  // namespace

class RedirectOnSizeLimitFilterTest : public RewriteTestBase {
 public:
  RedirectOnSizeLimitFilterTest() : writer_(&output_) {}

 protected:
  virtual void SetUp() {}

  void SetupDriver(int size_limit) {
    options_->set_max_html_parse_bytes(size_limit);
    options_->EnableFilter(RewriteOptions::kHtmlWriterFilter);
    RewriteTestBase::SetUp();
    rewrite_driver()->AddFilters();
    rewrite_driver()->SetWriter(&writer_);
  }

  void ClearAndResetDriver(int size_limit) {
    delete rewrite_driver();
    delete other_rewrite_driver();
    output_.clear();
    options_ = new RewriteOptions(factory()->thread_system());
    other_options_ = new RewriteOptions(factory()->thread_system());
    SetupDriver(size_limit);
  }

  void CheckOutput(int start_index, int end_index,
                   bool should_flush_before_size,
                   const GoogleString& input,
                   const GoogleString& expected_output) {
    for (int i = start_index; i < end_index; ++i) {
      ClearAndResetDriver(i);
      if (should_flush_before_size && i > 2) {
        html_parse()->StartParse("http://test.com/in.html");
        int split = std::min(static_cast<int>(input.size()) - 1, i);
        html_parse()->ParseText(input.substr(0, split));
        html_parse()->Flush();
        html_parse()->ParseText(input.substr(split, input.size() - 1));
        html_parse()->FinishParse();
      } else {
        Parse("in", input);
      }
      EXPECT_EQ(expected_output, output_);
    }
  }

  virtual bool AddHtmlTags() const { return false; }
  virtual bool AddBody() const { return false; }

  GoogleString output_;

 private:
  StringWriter writer_;

  DISALLOW_COPY_AND_ASSIGN(RedirectOnSizeLimitFilterTest);
};

TEST_F(RedirectOnSizeLimitFilterTest, TestOneFlushWindow) {
  static const char input[] =
      ""  // 6 chars
      "<input type=\"text\"/>"  // 20 chars
      ""  // 53 chars
      ""  // 27 chars
      "

blah

"  // 37 chars
      "";  // 7 chars

  SetupDriver(-1);
  Parse("in", input);
  EXPECT_EQ(input, output_);

  CheckOutput(0, 1, false, input, input);

  CheckOutput(1, 149, false, input,  StringPrintf("%s", kScript));

  CheckOutput(150, 180, false, input, input);
}

TEST_F(RedirectOnSizeLimitFilterTest, TestFlushBeforeLimit) {
  const char input[] =
      ""  // 6 chars
      "<input type=\"text\"/>"  // 20 chars
      ""  // 53 chars
      ""  // 27 chars
      "

blah

"  // 37 chars
      "";  // 7 chars

  SetupDriver(-1);
  Parse("in", input);
  EXPECT_EQ(input, output_);

  CheckOutput(0, 1, true, input, input);

  CheckOutput(1, 6, true, input,  StringPrintf("%s", kScript));

  CheckOutput(6, 26, true, input,
              StringPrintf("%s<input type=\"text\"/>",  kScript));

  CheckOutput(26, 57, true, input,
      StringPrintf("<input type=\"text\"/>%s"
                   "",
                   kScript));

  CheckOutput(57, 79, true, input,
      StringPrintf("<input type=\"text\"/>"
                   "%s"
                   "", kScript));

  CheckOutput(79, 113, true, input,
      StringPrintf("<input type=\"text\"/>"
                   ""
                   "%s

",
                   kScript));

  CheckOutput(113, 117, true, input,
      StringPrintf("<input type=\"text\"/>"
      ""
      ""
      "
%s

", kScript));

  CheckOutput(117, 121, true, input,
      StringPrintf("<input type=\"text\"/>"
                   ""
                   ""
                   "

%s

", kScript));

  CheckOutput(121, 130, true, input,
      StringPrintf("<input type=\"text\"/>"
                   ""
                   ""
                   "

blah	%s

", kScript));

  CheckOutput(130, 135, true, input,
      StringPrintf("<input type=\"text\"/>"
                   ""
                   ""
                   "

blah
%s
", kScript));

  CheckOutput(135, 150, true, input,
      StringPrintf("<input type=\"text\"/>"
                   ""
                   ""
                   "

blah

%s", kScript));

  CheckOutput(150, 160, true, input, input);
}

TEST_F(RedirectOnSizeLimitFilterTest, TestEscapingAndFlush) {
  SetupDriver(100);
  // Depending on the version of Chromium GURL used, we get different quoting
  // for a single-quote.  Either is acceptable.
  static const char kOutput[] =
      ""
      "<input type=\"text\"/>"
      ""
      ""
      "";

  static const char kOutputAlt[] =
      ""
      "<input type=\"text\"/>"
      ""
      ""
      "";

  html_parse()->StartParse("http://test.com/in.html?'(");
  html_parse()->ParseText(
      "<input type=\"text\"/>"
      ""
      ""
      "

blah

");
  html_parse()->FinishParse();

  EXPECT_TRUE((kOutput == output_) || (kOutputAlt == output_)) << output_;
}

}  // namespace net_instaweb
<commit_msg>Simplify this to only support current revision, based on feedback.<commit_after>/*
 * Copyright 2012 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
// Author: nikhilmadan@google.com (Nikhil Madan)

#include "net/instaweb/rewriter/public/redirect_on_size_limit_filter.h"

#include <algorithm>

#include "net/instaweb/rewriter/public/rewrite_driver.h"
#include "net/instaweb/rewriter/public/rewrite_options.h"
#include "net/instaweb/rewriter/public/rewrite_test_base.h"
#include "net/instaweb/rewriter/public/test_rewrite_driver_factory.h"
#include "pagespeed/kernel/base/gtest.h"
#include "pagespeed/kernel/base/string.h"
#include "pagespeed/kernel/base/string_util.h"
#include "pagespeed/kernel/base/string_writer.h"

namespace net_instaweb {

namespace {

static const char kScript[] = "";

}  // namespace

class RedirectOnSizeLimitFilterTest : public RewriteTestBase {
 public:
  RedirectOnSizeLimitFilterTest() : writer_(&output_) {}

 protected:
  virtual void SetUp() {}

  void SetupDriver(int size_limit) {
    options_->set_max_html_parse_bytes(size_limit);
    options_->EnableFilter(RewriteOptions::kHtmlWriterFilter);
    RewriteTestBase::SetUp();
    rewrite_driver()->AddFilters();
    rewrite_driver()->SetWriter(&writer_);
  }

  void ClearAndResetDriver(int size_limit) {
    delete rewrite_driver();
    delete other_rewrite_driver();
    output_.clear();
    options_ = new RewriteOptions(factory()->thread_system());
    other_options_ = new RewriteOptions(factory()->thread_system());
    SetupDriver(size_limit);
  }

  void CheckOutput(int start_index, int end_index,
                   bool should_flush_before_size,
                   const GoogleString& input,
                   const GoogleString& expected_output) {
    for (int i = start_index; i < end_index; ++i) {
      ClearAndResetDriver(i);
      if (should_flush_before_size && i > 2) {
        html_parse()->StartParse("http://test.com/in.html");
        int split = std::min(static_cast<int>(input.size()) - 1, i);
        html_parse()->ParseText(input.substr(0, split));
        html_parse()->Flush();
        html_parse()->ParseText(input.substr(split, input.size() - 1));
        html_parse()->FinishParse();
      } else {
        Parse("in", input);
      }
      EXPECT_EQ(expected_output, output_);
    }
  }

  virtual bool AddHtmlTags() const { return false; }
  virtual bool AddBody() const { return false; }

  GoogleString output_;

 private:
  StringWriter writer_;

  DISALLOW_COPY_AND_ASSIGN(RedirectOnSizeLimitFilterTest);
};

TEST_F(RedirectOnSizeLimitFilterTest, TestOneFlushWindow) {
  static const char input[] =
      ""  // 6 chars
      "<input type=\"text\"/>"  // 20 chars
      ""  // 53 chars
      ""  // 27 chars
      "

blah

"  // 37 chars
      "";  // 7 chars

  SetupDriver(-1);
  Parse("in", input);
  EXPECT_EQ(input, output_);

  CheckOutput(0, 1, false, input, input);

  CheckOutput(1, 149, false, input,  StringPrintf("%s", kScript));

  CheckOutput(150, 180, false, input, input);
}

TEST_F(RedirectOnSizeLimitFilterTest, TestFlushBeforeLimit) {
  const char input[] =
      ""  // 6 chars
      "<input type=\"text\"/>"  // 20 chars
      ""  // 53 chars
      ""  // 27 chars
      "

blah

"  // 37 chars
      "";  // 7 chars

  SetupDriver(-1);
  Parse("in", input);
  EXPECT_EQ(input, output_);

  CheckOutput(0, 1, true, input, input);

  CheckOutput(1, 6, true, input,  StringPrintf("%s", kScript));

  CheckOutput(6, 26, true, input,
              StringPrintf("%s<input type=\"text\"/>",  kScript));

  CheckOutput(26, 57, true, input,
      StringPrintf("<input type=\"text\"/>%s"
                   "",
                   kScript));

  CheckOutput(57, 79, true, input,
      StringPrintf("<input type=\"text\"/>"
                   "%s"
                   "", kScript));

  CheckOutput(79, 113, true, input,
      StringPrintf("<input type=\"text\"/>"
                   ""
                   "%s

",
                   kScript));

  CheckOutput(113, 117, true, input,
      StringPrintf("<input type=\"text\"/>"
      ""
      ""
      "
%s

", kScript));

  CheckOutput(117, 121, true, input,
      StringPrintf("<input type=\"text\"/>"
                   ""
                   ""
                   "

%s

", kScript));

  CheckOutput(121, 130, true, input,
      StringPrintf("<input type=\"text\"/>"
                   ""
                   ""
                   "

blah	%s

", kScript));

  CheckOutput(130, 135, true, input,
      StringPrintf("<input type=\"text\"/>"
                   ""
                   ""
                   "

blah
%s
", kScript));

  CheckOutput(135, 150, true, input,
      StringPrintf("<input type=\"text\"/>"
                   ""
                   ""
                   "

blah

%s", kScript));

  CheckOutput(150, 160, true, input, input);
}

TEST_F(RedirectOnSizeLimitFilterTest, TestEscapingAndFlush) {
  SetupDriver(100);
  static const char kOutput[] =
      ""
      "<input type=\"text\"/>"
      ""
      ""
      "";

  html_parse()->StartParse("http://test.com/in.html?'(");
  html_parse()->ParseText(
      "<input type=\"text\"/>"
      ""
      ""
      "

blah

");
  html_parse()->FinishParse();

  EXPECT_EQ(kOutput, output_);
}

}  // namespace net_instaweb
<|endoftext|>


<commit_before>/**
 * \file BaseFilter.cpp
 */

#include "BaseFilter.h"

#include <cassert>
#include <cstdint>
#include <iostream>
#include <stdexcept>

#if ATK_USE_THREADPOOL == 1
#include <tbb/task_group.h>
#endif

namespace ATK
{
  BaseFilter::BaseFilter(unsigned int nb_input_ports, unsigned int nb_output_ports)
  :nb_input_ports(nb_input_ports), nb_output_ports(nb_output_ports),
   input_sampling_rate(0), output_sampling_rate(0),
   connections(nb_input_ports, std::make_pair(-1, nullptr)), input_delay(0), output_delay(0),
   latency(0), input_mandatory_connection(nb_input_ports), is_reset(false)
#if ATK_PROFILING == 1
  , input_conversion_time(0), output_conversion_time(0), process_time(0)
#endif
  {
  }

  BaseFilter::BaseFilter(BaseFilter&& other)
  :nb_input_ports(other.nb_input_ports), nb_output_ports(other.nb_output_ports), input_sampling_rate(other.input_sampling_rate), output_sampling_rate(other.output_sampling_rate), connections(std::move(other.connections)), input_delay(other.input_delay), output_delay(std::move(other.output_delay)), latency(std::move(other.latency)), input_mandatory_connection(std::move(other.input_mandatory_connection)), is_reset(std::move(other.is_reset))
#if ATK_PROFILING == 1
  , input_conversion_time(0), output_conversion_time(0), process_time(0)
#endif
  {
  }

  BaseFilter::~BaseFilter()
  {
#if ATK_PROFILING == 1
    std::cerr << "Object of type " << class_name << std::endl;
    std::cerr << "Input conversion time " << std::chrono::duration_cast<std::chrono::microseconds>(input_conversion_time).count() << "us" << std::endl;
    std::cerr << "Output conversion time " << std::chrono::duration_cast<std::chrono::microseconds>(output_conversion_time).count() << "us" << std::endl;
    std::cerr << "Process time " << std::chrono::duration_cast<std::chrono::microseconds>(process_time).count() << "us" << std::endl;
#endif
  }

  void BaseFilter::reset()
  {
    if (!is_reset)
    {
      for (auto it = connections.begin(); it != connections.end(); ++it)
      {
        if (it->second)
        {
          it->second->reset();
        }
      }
      is_reset = true;
    }
  }

  void BaseFilter::setup()
  {
  }

  void BaseFilter::full_setup()
  {
#if ATK_PROFILING == 1
    class_name = typeid(*this).name();
#endif
    setup();
  }

  void BaseFilter::set_input_port(unsigned int input_port, BaseFilter* filter, unsigned int output_port)
  {
    if(output_port >= filter->nb_output_ports)
    {
      throw std::runtime_error("Output port does not exist for this filter");
    }
    if(input_port < nb_input_ports)
    {
      connections[input_port] = std::make_pair(output_port, filter);
      if(filter->get_output_sampling_rate() != get_input_sampling_rate())
      {
        throw std::runtime_error("Input sample rate from this filter must be equal to the output sample rate of the connected filter");
      }
    }
    else
    {
      throw std::runtime_error("Input port doesn't exist for this filter");
    }
  }

  void BaseFilter::set_input_sampling_rate(std::size_t rate)
  {
    input_sampling_rate = rate;
    if(output_sampling_rate == 0)
    {
      output_sampling_rate = rate;
    }
    full_setup();
  }

  std::size_t BaseFilter::get_input_sampling_rate() const
  {
    return input_sampling_rate;
  }

  void BaseFilter::set_output_sampling_rate(std::size_t rate)
  {
    output_sampling_rate = rate;
    full_setup();
  }

  std::size_t BaseFilter::get_output_sampling_rate() const
  {
    return output_sampling_rate;
  }

  std::size_t BaseFilter::get_input_delay() const
  {
    return input_delay;
  }

  std::size_t BaseFilter::get_output_delay() const
  {
    return output_delay;
  }

  void BaseFilter::process(std::size_t size)
  {
    reset();
    process_conditionnally(size);
  }

#if ATK_USE_THREADPOOL == 1
  void BaseFilter::process_parallel(std::size_t size)
  {
    reset();
    process_conditionnally_parallel(size);
  }
#endif

  void BaseFilter::process_conditionnally(std::size_t size)
  {
    if(size == 0)
    {
      return;
    }
    if(output_sampling_rate == 0)
    {
      throw std::runtime_error("Output sampling rate is 0, must be non 0 to compute the needed size for filters processing");
    }
    if(!is_reset)
    {
      return;
    }
    for(std::size_t port = 0; port < connections.size(); ++port)
    {
      if(connections[port].second == nullptr)
      {
        if(!input_mandatory_connection[port])
          throw std::runtime_error("Input port " + std::to_string(port) + " is not connected");
      }
      else
      {
        assert(output_sampling_rate);
        connections[port].second->process_conditionnally(uint64_t(size) * input_sampling_rate / output_sampling_rate);
      }
    }
#if ATK_PROFILING == 1
    auto timer = std::chrono::steady_clock::now();
#endif
    prepare_process(uint64_t(size) * input_sampling_rate / output_sampling_rate);
#if ATK_PROFILING == 1
    auto timer2 = std::chrono::steady_clock::now();
    input_conversion_time += (timer2 - timer);
    timer = timer2;
#endif
    prepare_outputs(size);
#if ATK_PROFILING == 1
    timer2 = std::chrono::steady_clock::now();
    output_conversion_time += (timer2 - timer);
    timer = timer2;
#endif
    process_impl(size);
#if ATK_PROFILING == 1
    timer2 = std::chrono::steady_clock::now();
    process_time += (timer2 - timer);
    timer = timer2;
#endif
    is_reset = false;
    last_size = size;
  }

#if ATK_USE_THREADPOOL == 1
  void BaseFilter::process_conditionnally_parallel(std::size_t size)
  {
    if (size == 0)
    {
      return;
    }
    if (output_sampling_rate == 0)
    {
      throw std::runtime_error("Output sampling rate is 0, must be non 0 to compute the needed size for filters processing");
    }
    { // lock this entire loop, as we only want to do the processing if we are not reseted
      tbb::queuing_mutex::scoped_lock lock(mutex);
      if (!is_reset)
      {
        return;
      }
      tbb::task_group g;
      for(std::size_t port = 0; port < connections.size(); ++port)
      {
        if(connections[port].second == nullptr)
        {
          if(!input_mandatory_connection[port])
          throw std::runtime_error("Input port " + std::to_string(port) + " is not connected");
        }
        else
        {
          assert(output_sampling_rate);
          auto filter = connections[port];
          g.run([=]{filter->process_conditionnally_parallel(size * input_sampling_rate / output_sampling_rate); });
        }
      }
      g.wait();
#if ATK_PROFILING == 1
      boost::timer::cpu_timer timer;
#endif
      prepare_process(size * input_sampling_rate / output_sampling_rate);
#if ATK_PROFILING == 1
      boost::timer::cpu_times const input_elapsed_times(timer.elapsed());
      input_conversion_time += (input_elapsed_times.system + input_elapsed_times.user);
#endif
      prepare_outputs(size);
#if ATK_PROFILING == 1
      boost::timer::cpu_times const output_elapsed_times(timer.elapsed());
      output_conversion_time += (output_elapsed_times.system + output_elapsed_times.user);
#endif
      process_impl(size);
#if ATK_PROFILING == 1
      boost::timer::cpu_times const process_elapsed_times(timer.elapsed());
      process_time += (process_elapsed_times.system + process_elapsed_times.user);
#endif
      is_reset = false;
      last_size = size;
    }
  }
#endif

  std::size_t BaseFilter::get_nb_input_ports() const
  {
    return nb_input_ports;
  }

  void BaseFilter::set_nb_input_ports(std::size_t nb_ports)
  {
    connections.resize(nb_ports, std::make_pair(-1, nullptr));
    input_mandatory_connection.reset(nb_ports);
    nb_input_ports = nb_ports;
  }

  void BaseFilter::allow_inactive_connection(unsigned int port)
  {
    input_mandatory_connection[port] = true;
  }

  std::size_t BaseFilter::get_nb_output_ports() const
  {
    return nb_output_ports;
  }

  void BaseFilter::set_nb_output_ports(std::size_t nb_ports)
  {
    nb_output_ports = nb_ports;
  }

  void BaseFilter::set_latency(std::size_t latency)
  {
    this->latency = latency;
  }

  std::size_t BaseFilter::get_latency() const
  {
    return latency;
  }

  std::size_t BaseFilter::get_global_latency() const
  {
    std::size_t global_latency = 0;
    for(auto it = connections.begin(); it != connections.end(); ++it)
    {
      if(it->second == nullptr)
      {
        throw std::runtime_error("Input port " + std::to_string(it - connections.begin()) + " is not connected");
      }

      global_latency = std::max(global_latency, it->second->get_global_latency());
    }
    return global_latency + latency;
  }

}
<commit_msg>Fix bitset resize<commit_after>/**
 * \file BaseFilter.cpp
 */

#include "BaseFilter.h"

#include <cassert>
#include <cstdint>
#include <stdexcept>

#if ATK_PROFILING == 1
#include <iostream>
#endif

#if ATK_USE_THREADPOOL == 1
#include <tbb/task_group.h>
#endif

namespace ATK
{
  BaseFilter::BaseFilter(unsigned int nb_input_ports, unsigned int nb_output_ports)
  :nb_input_ports(nb_input_ports), nb_output_ports(nb_output_ports),
   input_sampling_rate(0), output_sampling_rate(0),
   connections(nb_input_ports, std::make_pair(-1, nullptr)), input_delay(0), output_delay(0),
   latency(0), input_mandatory_connection(nb_input_ports), is_reset(false)
#if ATK_PROFILING == 1
  , input_conversion_time(0), output_conversion_time(0), process_time(0)
#endif
  {
  }

  BaseFilter::BaseFilter(BaseFilter&& other)
  :nb_input_ports(other.nb_input_ports), nb_output_ports(other.nb_output_ports), input_sampling_rate(other.input_sampling_rate), output_sampling_rate(other.output_sampling_rate), connections(std::move(other.connections)), input_delay(other.input_delay), output_delay(std::move(other.output_delay)), latency(std::move(other.latency)), input_mandatory_connection(std::move(other.input_mandatory_connection)), is_reset(std::move(other.is_reset))
#if ATK_PROFILING == 1
  , input_conversion_time(0), output_conversion_time(0), process_time(0)
#endif
  {
  }

  BaseFilter::~BaseFilter()
  {
#if ATK_PROFILING == 1
    std::cerr << "Object of type " << class_name << std::endl;
    std::cerr << "Input conversion time " << std::chrono::duration_cast<std::chrono::microseconds>(input_conversion_time).count() << "us" << std::endl;
    std::cerr << "Output conversion time " << std::chrono::duration_cast<std::chrono::microseconds>(output_conversion_time).count() << "us" << std::endl;
    std::cerr << "Process time " << std::chrono::duration_cast<std::chrono::microseconds>(process_time).count() << "us" << std::endl;
#endif
  }

  void BaseFilter::reset()
  {
    if (!is_reset)
    {
      for (auto it = connections.begin(); it != connections.end(); ++it)
      {
        if (it->second)
        {
          it->second->reset();
        }
      }
      is_reset = true;
    }
  }

  void BaseFilter::setup()
  {
  }

  void BaseFilter::full_setup()
  {
#if ATK_PROFILING == 1
    class_name = typeid(*this).name();
#endif
    setup();
  }

  void BaseFilter::set_input_port(unsigned int input_port, BaseFilter* filter, unsigned int output_port)
  {
    if(output_port >= filter->nb_output_ports)
    {
      throw std::runtime_error("Output port does not exist for this filter");
    }
    if(input_port < nb_input_ports)
    {
      connections[input_port] = std::make_pair(output_port, filter);
      if(filter->get_output_sampling_rate() != get_input_sampling_rate())
      {
        throw std::runtime_error("Input sample rate from this filter must be equal to the output sample rate of the connected filter");
      }
    }
    else
    {
      throw std::runtime_error("Input port doesn't exist for this filter");
    }
  }

  void BaseFilter::set_input_sampling_rate(std::size_t rate)
  {
    input_sampling_rate = rate;
    if(output_sampling_rate == 0)
    {
      output_sampling_rate = rate;
    }
    full_setup();
  }

  std::size_t BaseFilter::get_input_sampling_rate() const
  {
    return input_sampling_rate;
  }

  void BaseFilter::set_output_sampling_rate(std::size_t rate)
  {
    output_sampling_rate = rate;
    full_setup();
  }

  std::size_t BaseFilter::get_output_sampling_rate() const
  {
    return output_sampling_rate;
  }

  std::size_t BaseFilter::get_input_delay() const
  {
    return input_delay;
  }

  std::size_t BaseFilter::get_output_delay() const
  {
    return output_delay;
  }

  void BaseFilter::process(std::size_t size)
  {
    reset();
    process_conditionnally(size);
  }

#if ATK_USE_THREADPOOL == 1
  void BaseFilter::process_parallel(std::size_t size)
  {
    reset();
    process_conditionnally_parallel(size);
  }
#endif

  void BaseFilter::process_conditionnally(std::size_t size)
  {
    if(size == 0)
    {
      return;
    }
    if(output_sampling_rate == 0)
    {
      throw std::runtime_error("Output sampling rate is 0, must be non 0 to compute the needed size for filters processing");
    }
    if(!is_reset)
    {
      return;
    }
    for(std::size_t port = 0; port < connections.size(); ++port)
    {
      if(connections[port].second == nullptr)
      {
        if(!input_mandatory_connection[port])
          throw std::runtime_error("Input port " + std::to_string(port) + " is not connected");
      }
      else
      {
        assert(output_sampling_rate);
        connections[port].second->process_conditionnally(uint64_t(size) * input_sampling_rate / output_sampling_rate);
      }
    }
#if ATK_PROFILING == 1
    auto timer = std::chrono::steady_clock::now();
#endif
    prepare_process(uint64_t(size) * input_sampling_rate / output_sampling_rate);
#if ATK_PROFILING == 1
    auto timer2 = std::chrono::steady_clock::now();
    input_conversion_time += (timer2 - timer);
    timer = timer2;
#endif
    prepare_outputs(size);
#if ATK_PROFILING == 1
    timer2 = std::chrono::steady_clock::now();
    output_conversion_time += (timer2 - timer);
    timer = timer2;
#endif
    process_impl(size);
#if ATK_PROFILING == 1
    timer2 = std::chrono::steady_clock::now();
    process_time += (timer2 - timer);
    timer = timer2;
#endif
    is_reset = false;
    last_size = size;
  }

#if ATK_USE_THREADPOOL == 1
  void BaseFilter::process_conditionnally_parallel(std::size_t size)
  {
    if (size == 0)
    {
      return;
    }
    if (output_sampling_rate == 0)
    {
      throw std::runtime_error("Output sampling rate is 0, must be non 0 to compute the needed size for filters processing");
    }
    { // lock this entire loop, as we only want to do the processing if we are not reseted
      tbb::queuing_mutex::scoped_lock lock(mutex);
      if (!is_reset)
      {
        return;
      }
      tbb::task_group g;
      for(std::size_t port = 0; port < connections.size(); ++port)
      {
        if(connections[port].second == nullptr)
        {
          if(!input_mandatory_connection[port])
          throw std::runtime_error("Input port " + std::to_string(port) + " is not connected");
        }
        else
        {
          assert(output_sampling_rate);
          auto filter = connections[port];
          g.run([=]{filter->process_conditionnally_parallel(size * input_sampling_rate / output_sampling_rate); });
        }
      }
      g.wait();
#if ATK_PROFILING == 1
      boost::timer::cpu_timer timer;
#endif
      prepare_process(size * input_sampling_rate / output_sampling_rate);
#if ATK_PROFILING == 1
      boost::timer::cpu_times const input_elapsed_times(timer.elapsed());
      input_conversion_time += (input_elapsed_times.system + input_elapsed_times.user);
#endif
      prepare_outputs(size);
#if ATK_PROFILING == 1
      boost::timer::cpu_times const output_elapsed_times(timer.elapsed());
      output_conversion_time += (output_elapsed_times.system + output_elapsed_times.user);
#endif
      process_impl(size);
#if ATK_PROFILING == 1
      boost::timer::cpu_times const process_elapsed_times(timer.elapsed());
      process_time += (process_elapsed_times.system + process_elapsed_times.user);
#endif
      is_reset = false;
      last_size = size;
    }
  }
#endif

  std::size_t BaseFilter::get_nb_input_ports() const
  {
    return nb_input_ports;
  }

  void BaseFilter::set_nb_input_ports(std::size_t nb_ports)
  {
    connections.resize(nb_ports, std::make_pair(-1, nullptr));
    input_mandatory_connection.resize(nb_ports);
    nb_input_ports = nb_ports;
  }

  void BaseFilter::allow_inactive_connection(unsigned int port)
  {
    input_mandatory_connection[port] = true;
  }

  std::size_t BaseFilter::get_nb_output_ports() const
  {
    return nb_output_ports;
  }

  void BaseFilter::set_nb_output_ports(std::size_t nb_ports)
  {
    nb_output_ports = nb_ports;
  }

  void BaseFilter::set_latency(std::size_t latency)
  {
    this->latency = latency;
  }

  std::size_t BaseFilter::get_latency() const
  {
    return latency;
  }

  std::size_t BaseFilter::get_global_latency() const
  {
    std::size_t global_latency = 0;
    for(auto it = connections.begin(); it != connections.end(); ++it)
    {
      if(it->second == nullptr)
      {
        throw std::runtime_error("Input port " + std::to_string(it - connections.begin()) + " is not connected");
      }

      global_latency = std::max(global_latency, it->second->get_global_latency());
    }
    return global_latency + latency;
  }

}
<|endoftext|>


<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2012.  All rights reserved
 */
#include    "../../StroikaPreComp.h"

#include    "../../Characters/Format.h"

#include    "Writer.h"

using   namespace   Stroika;
using   namespace   Stroika::Foundation;
using   namespace   Stroika::Foundation::Characters;
using   namespace   Stroika::Foundation::DataExchangeFormat;
using   namespace   Stroika::Foundation::DataExchangeFormat::XML;

/*
 ********************************************************************************
 ****************************** QuoteForXMLAttribute ****************************
 ********************************************************************************
 */
string  XML::QuoteForXMLAttribute (const string& s)
{
    string  r;
    r.reserve (s.size () * 6 / 5);  // wild guess about good estimate
    for (string::const_iterator i = s.begin (); i != s.end (); ++i) {
        Containers::ReserveSpeedTweekAdd1 (r);
        switch (*i) {
            case    '&':
                r += "&";
                break;
            case    '<':
                r += "<";
                break;
            case    '>':
                r += ">";
                break;
            case    '\"':
                r += """;
                break;
            case    '\'':
                r += "'";
                break;
            default:
                r.push_back (*i);
                break;
        }
    }
    return r;
}

string  XML::QuoteForXMLAttribute (const wstring& s)
{
    string  r;
    r.reserve (s.size () * 7 / 5);  // wild guess about good estimate
    for (wstring::const_iterator i = s.begin (); i != s.end (); ++i) {
        Containers::ReserveSpeedTweekAdd1 (r);
        switch (*i) {
            case    '&':
                r += "&";
                break;
            case    '<':
                r += "<";
                break;
            case    '>':
                r += ">";
                break;
            case    '\"':
                r += """;
                break;
            case    '\'':
                r += "'";
                break;
            default:        {
                    wchar_t ccode   =   *i;
                    if (ccode != '\t' and ccode != '\n' and ccode != '\r' and (ccode < 32 || ccode > 127)) {
                        r +=  Format ("&#%d;", ccode);
                    }
                    else {
                        r.push_back (static_cast<char> (ccode));
                    }
                }
                break;
        }
    }
    return r;
}

string  XML::QuoteForXMLAttribute (const String& s)
{
    return QuoteForXMLAttribute (s.As<wstring> ());
}

string  XML::QuoteForXMLAttribute (const Memory::Optional<String>& s)
{
    if (s.empty ()) {
        return string ();
    }
    return QuoteForXMLAttribute (*s);
}

wstring XML::QuoteForXMLAttributeW (const wstring& s)
{
    string  tmp =   QuoteForXMLAttribute (s);
    return NarrowSDKStringToWide (tmp);
}

/*
 ********************************************************************************
 ******************************** QuoteForXML ***********************************
 ********************************************************************************
 */
string  XML::QuoteForXML (const string& s)
{
    string  r;
    r.reserve (s.size () * 6 / 5);  // wild guess about good estimate
    for (string::const_iterator i = s.begin (); i != s.end (); ++i) {
        Containers::ReserveSpeedTweekAdd1 (r);
        switch (*i) {
            case    '&':
                r += "&";
                break;
            case    '<':
                r += "<";
                break;
            case    '>':
                r += ">";
                break;
            case    '-':    {
                    // A 'dash' or 'minus-sign' can be dangerous in XML - if you get two in a row (start/end comment designator).
                    // So avoid any leading/trailing dash, and any double-dashes.
                    //
                    // NB: This code WOULD be simpler if we just always mapped, but then much ordinary and safe text with dashes becomes
                    // less readable (and produces huge diffs in my regression tests - but thats a one-time annoyance).
                    //
                    if ((i == s.begin ()) or (i + 1 == s.end ()) or (*(i - 1) == '-')) {
                        r += "-";
                    }
                    else {
                        r.push_back ('-');
                    }
                }
                break;
            default:
                r.push_back (*i);
                break;
        }
    }
    return r;
}

string  XML::QuoteForXML (const wstring& s)
{
    string  r;
    r.reserve (s.size () * 7 / 5);  // wild guess about good estimate
    for (wstring::const_iterator i = s.begin (); i != s.end (); ++i) {
        Containers::ReserveSpeedTweekAdd1 (r);
        switch (*i) {
            case    '&':
                r += "&";
                break;
            case    '<':
                r += "<";
                break;
            case    '>':
                r += ">";
                break;
            case    '-':    {
                    // A 'dash' or 'minus-sign' can be dangerous in XML - if you get two in a row (start/end comment designator).
                    // So avoid any leading/trailing dash, and any double-dashes.
                    //
                    // NB: This code WOULD be simpler if we just always mapped, but then much ordinary and safe text with dashes becomes
                    // less readable (and produces huge diffs in my regression tests - but thats a one-time annoyance).
                    //
                    if ((i == s.begin ()) or (i + 1 == s.end ()) or (*(i - 1) == '-')) {
                        r += "-";
                    }
                    else {
                        r.push_back ('-');
                    }
                }
                break;
            default:        {
                    wchar_t ccode   =   *i;
                    if (ccode != '\t' and ccode != '\n' and ccode != '\r' and (ccode < 32 || ccode > 127)) {
                        r +=  Format ("&#%d;", ccode);
                    }
                    else {
                        r.push_back (static_cast<char> (ccode));
                    }
                }
                break;
        }
    }
    return r;
}

wstring XML::QuoteForXMLW (const wstring& s)
{
    string  tmp =   QuoteForXML (s);
    return NarrowSDKStringToWide (tmp);
}

string  XML::QuoteForXML (const String& s)
{
    return QuoteForXML (s.As<wstring> ());
}

string  XML::QuoteForXML (const Memory::Optional<String>& s)
{
    if (s.empty ()) {
        return string ();
    }
    return QuoteForXML (*s);
}

/*
 ********************************************************************************
 ****************************** XML::Indenter ***********************************
 ********************************************************************************
 */
Indenter::Indenter (const String& indentText)
    : fTabS_ (indentText.AsUTF8<string> ())
    , fTabW_ (indentText.As<wstring> ())
{
}

void    Indenter::Indent (unsigned indentLevel, ostream& out) const
{
    for (unsigned int i = 0; i < indentLevel; ++i) {
        out << fTabS_;
    }
}

void    Indenter::Indent (unsigned int indentLevel, wostream& out) const
{
    for (unsigned int i = 0; i < indentLevel; ++i) {
        out << fTabW_;
    }
}

string  XML::Format4XML (bool v)
{
    return v ? "true" : "false";
}

<commit_msg>added missing include (for gcc)<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2012.  All rights reserved
 */
#include    "../../StroikaPreComp.h"

#include    <ostream>

#include    "../../Characters/Format.h"

#include    "Writer.h"

using   namespace   Stroika;
using   namespace   Stroika::Foundation;
using   namespace   Stroika::Foundation::Characters;
using   namespace   Stroika::Foundation::DataExchangeFormat;
using   namespace   Stroika::Foundation::DataExchangeFormat::XML;

/*
 ********************************************************************************
 ****************************** QuoteForXMLAttribute ****************************
 ********************************************************************************
 */
string  XML::QuoteForXMLAttribute (const string& s)
{
    string  r;
    r.reserve (s.size () * 6 / 5);  // wild guess about good estimate
    for (string::const_iterator i = s.begin (); i != s.end (); ++i) {
        Containers::ReserveSpeedTweekAdd1 (r);
        switch (*i) {
            case    '&':
                r += "&";
                break;
            case    '<':
                r += "<";
                break;
            case    '>':
                r += ">";
                break;
            case    '\"':
                r += """;
                break;
            case    '\'':
                r += "'";
                break;
            default:
                r.push_back (*i);
                break;
        }
    }
    return r;
}

string  XML::QuoteForXMLAttribute (const wstring& s)
{
    string  r;
    r.reserve (s.size () * 7 / 5);  // wild guess about good estimate
    for (wstring::const_iterator i = s.begin (); i != s.end (); ++i) {
        Containers::ReserveSpeedTweekAdd1 (r);
        switch (*i) {
            case    '&':
                r += "&";
                break;
            case    '<':
                r += "<";
                break;
            case    '>':
                r += ">";
                break;
            case    '\"':
                r += """;
                break;
            case    '\'':
                r += "'";
                break;
            default:        {
                    wchar_t ccode   =   *i;
                    if (ccode != '\t' and ccode != '\n' and ccode != '\r' and (ccode < 32 || ccode > 127)) {
                        r +=  Format ("&#%d;", ccode);
                    }
                    else {
                        r.push_back (static_cast<char> (ccode));
                    }
                }
                break;
        }
    }
    return r;
}

string  XML::QuoteForXMLAttribute (const String& s)
{
    return QuoteForXMLAttribute (s.As<wstring> ());
}

string  XML::QuoteForXMLAttribute (const Memory::Optional<String>& s)
{
    if (s.empty ()) {
        return string ();
    }
    return QuoteForXMLAttribute (*s);
}

wstring XML::QuoteForXMLAttributeW (const wstring& s)
{
    string  tmp =   QuoteForXMLAttribute (s);
    return NarrowSDKStringToWide (tmp);
}

/*
 ********************************************************************************
 ******************************** QuoteForXML ***********************************
 ********************************************************************************
 */
string  XML::QuoteForXML (const string& s)
{
    string  r;
    r.reserve (s.size () * 6 / 5);  // wild guess about good estimate
    for (string::const_iterator i = s.begin (); i != s.end (); ++i) {
        Containers::ReserveSpeedTweekAdd1 (r);
        switch (*i) {
            case    '&':
                r += "&";
                break;
            case    '<':
                r += "<";
                break;
            case    '>':
                r += ">";
                break;
            case    '-':    {
                    // A 'dash' or 'minus-sign' can be dangerous in XML - if you get two in a row (start/end comment designator).
                    // So avoid any leading/trailing dash, and any double-dashes.
                    //
                    // NB: This code WOULD be simpler if we just always mapped, but then much ordinary and safe text with dashes becomes
                    // less readable (and produces huge diffs in my regression tests - but thats a one-time annoyance).
                    //
                    if ((i == s.begin ()) or (i + 1 == s.end ()) or (*(i - 1) == '-')) {
                        r += "-";
                    }
                    else {
                        r.push_back ('-');
                    }
                }
                break;
            default:
                r.push_back (*i);
                break;
        }
    }
    return r;
}

string  XML::QuoteForXML (const wstring& s)
{
    string  r;
    r.reserve (s.size () * 7 / 5);  // wild guess about good estimate
    for (wstring::const_iterator i = s.begin (); i != s.end (); ++i) {
        Containers::ReserveSpeedTweekAdd1 (r);
        switch (*i) {
            case    '&':
                r += "&";
                break;
            case    '<':
                r += "<";
                break;
            case    '>':
                r += ">";
                break;
            case    '-':    {
                    // A 'dash' or 'minus-sign' can be dangerous in XML - if you get two in a row (start/end comment designator).
                    // So avoid any leading/trailing dash, and any double-dashes.
                    //
                    // NB: This code WOULD be simpler if we just always mapped, but then much ordinary and safe text with dashes becomes
                    // less readable (and produces huge diffs in my regression tests - but thats a one-time annoyance).
                    //
                    if ((i == s.begin ()) or (i + 1 == s.end ()) or (*(i - 1) == '-')) {
                        r += "-";
                    }
                    else {
                        r.push_back ('-');
                    }
                }
                break;
            default:        {
                    wchar_t ccode   =   *i;
                    if (ccode != '\t' and ccode != '\n' and ccode != '\r' and (ccode < 32 || ccode > 127)) {
                        r +=  Format ("&#%d;", ccode);
                    }
                    else {
                        r.push_back (static_cast<char> (ccode));
                    }
                }
                break;
        }
    }
    return r;
}

wstring XML::QuoteForXMLW (const wstring& s)
{
    string  tmp =   QuoteForXML (s);
    return NarrowSDKStringToWide (tmp);
}

string  XML::QuoteForXML (const String& s)
{
    return QuoteForXML (s.As<wstring> ());
}

string  XML::QuoteForXML (const Memory::Optional<String>& s)
{
    if (s.empty ()) {
        return string ();
    }
    return QuoteForXML (*s);
}

/*
 ********************************************************************************
 ****************************** XML::Indenter ***********************************
 ********************************************************************************
 */
Indenter::Indenter (const String& indentText)
    : fTabS_ (indentText.AsUTF8<string> ())
    , fTabW_ (indentText.As<wstring> ())
{
}

void    Indenter::Indent (unsigned indentLevel, ostream& out) const
{
    for (unsigned int i = 0; i < indentLevel; ++i) {
        out << fTabS_;
    }
}

void    Indenter::Indent (unsigned int indentLevel, wostream& out) const
{
    for (unsigned int i = 0; i < indentLevel; ++i) {
        out << fTabW_;
    }
}

string  XML::Format4XML (bool v)
{
    return v ? "true" : "false";
}

<|endoftext|>