<commit_before>/*
 * Copyright 2012 Bram van der Kroef
 *
 * This file is part of LESS CSS Compiler.
 *
 * LESS CSS Compiler is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * LESS CSS Compiler is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with LESS CSS Compiler.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Author: Bram van der Kroef <bram@vanderkroef.net>
 */

#include "UrlValue.h"

#include <config.h>

#ifdef WITH_LIBPNG
#include <png.h>
#endif

#ifdef WITH_LIBJPEG
#include <jpeglib.h>
#include <setjmp.h>

struct urlvalue_jpeg_error_mgr {
  struct jpeg_error_mgr pub;	/* "public" fields */

  jmp_buf setjmp_buffer;	/* for return to caller */
};

typedef struct urlvalue_jpeg_error_mgr * urlvalue_jpeg_error_ptr;

METHODDEF(void)
urlvalue_jpeg_error_exit (j_common_ptr cinfo)
{
  /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
  urlvalue_jpeg_error_ptr myerr = (urlvalue_jpeg_error_ptr) cinfo->err;

  /* Always display the message. */
  /* We could postpone this until after returning, if we chose. */
  (*cinfo->err->output_message) (cinfo);

  /* Return control to the setjmp point */
  longjmp(myerr->setjmp_buffer, 1);
}

#endif

UrlValue::UrlValue(Token* token, string path): Value() {
  tokens.push(token);
  this->path = path;
  type = Value::URL;
  loaded = false;
}

UrlValue::~UrlValue() {
}

Value* UrlValue::add(Value* v) {
  (void)v;
  throw new ValueException("You can not add images.");
}
Value* UrlValue::substract(Value* v) {
  (void)v;
  throw new ValueException("You can not substract images.");
}
Value* UrlValue::multiply(Value* v) {
  (void)v;
  throw new ValueException("You can not multiply images.");
}
Value* UrlValue::divide(Value* v) {
  (void)v;
  throw new ValueException("You can not divide images.");
}
int UrlValue::compare(Value* v) {
  if (v->type == URL) {
    return 0;
  } else {
    throw new ValueException("You can only compare images with images.");
  }
}

bool UrlValue::loadImg() {
  loaded = loadPng() || loadJpeg();

  return loaded;
}

bool UrlValue::loadPng() {
  if (loaded != false)
    return true;

#ifdef WITH_LIBPNG
  unsigned char header[8];    // 8 is the maximum size that can be checked
  /* open file and test for it being a png */

  png_structp png_ptr;
  png_infop info_ptr;

  FILE *fp = fopen(path.c_str(), "rb");
  if (!fp)
    return false; //"Image file could not be opened"

  fread(header, 1, 8, fp);
  if (png_sig_cmp(header, 0, 8))
    return false; //"Image is not a PNG file"

  /* initialize stuff */
  png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);

  if (!png_ptr)
    throw new ValueException("png_create_read_struct failed");

  info_ptr = png_create_info_struct(png_ptr);
  if (!info_ptr)
    throw new ValueException("png_create_info_struct failed");

  if (setjmp(png_jmpbuf(png_ptr)))
    throw new ValueException("Error during init_io");

  png_init_io(png_ptr, fp);
  png_set_sig_bytes(png_ptr, 8);

  png_read_info(png_ptr, info_ptr);

  width = png_get_image_width(png_ptr, info_ptr);
  height = png_get_image_height(png_ptr, info_ptr);

  fclose(fp);
  return true;

#else
  return false;
#endif
}

bool UrlValue::loadJpeg() {
  if (loaded != false)
    return true;

#ifdef WITH_LIBJPEG
  struct jpeg_decompress_struct cinfo;

  struct urlvalue_jpeg_error_mgr jerr;
  /* More stuff */
  FILE * infile;
  JSAMPARRAY buffer;	/* Output row buffer */
  int row_stride;	/* physical row width in output buffer */

  if ((infile = fopen(path.c_str(), "rb")) == NULL) {
    return false;
  }

  /* Step 1: allocate and initialize JPEG decompression object */

  /* We set up the normal JPEG error routines, then override error_exit. */
  cinfo.err = jpeg_std_error(&jerr.pub);
  jerr.pub.error_exit = urlvalue_jpeg_error_exit;
  /* Establish the setjmp return context for urlvalue_jpeg_error_exit to use. */
  if (setjmp(jerr.setjmp_buffer)) {
    /* If we get here, the JPEG code has signaled an error.
     * We need to clean up the JPEG object, close the input file, and return.
     */
    jpeg_destroy_decompress(&cinfo);
    fclose(infile);
    return false;
  }
  /* Now we can initialize the JPEG decompression object. */
  jpeg_create_decompress(&cinfo);

  /* Step 2: specify data source (eg, a file) */

  jpeg_stdio_src(&cinfo, infile);

  /* Step 3: read file parameters with jpeg_read_header() */

  (void) jpeg_read_header(&cinfo, TRUE);
  /* We can ignore the return value from jpeg_read_header since
   * (a) suspension is not possible with the stdio data source, and
   * (b) we passed TRUE to reject a tables-only JPEG file as an error.
   * See libjpeg.txt for more info.
   */

  /* Step 4: set parameters for decompression */

  /* In this example, we don't need to change any of the defaults set by
   * jpeg_read_header(), so we do nothing here.
   */

  /* Step 5: Start decompressor */

  (void) jpeg_start_decompress(&cinfo);
  /* We can ignore the return value since suspension is not possible
   * with the stdio data source.
   */

  width = cinfo.output_width;
  height = cinfo.output_height;

  /* We may need to do some setup of our own at this point before reading
   * the data. After jpeg_start_decompress() we have the correct scaled
   * output image dimensions available, as well as the output colormap
   * if we asked for color quantization.
   * In this example, we need to make an output work buffer of the right size.
   */
  /* JSAMPLEs per row in output buffer */
  row_stride = cinfo.output_width * cinfo.output_components;
  /* Make a one-row-high sample array that will go away when done with image */
  buffer = (*cinfo.mem->alloc_sarray)
    ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);

  /* Step 6: while (scan lines remain to be read) */
  /* jpeg_read_scanlines(...); */

  /* Here we use the library's state variable cinfo.output_scanline as the
   * loop counter, so that we don't have to keep track ourselves.
   */
  while (cinfo.output_scanline < cinfo.output_height) {
    /* jpeg_read_scanlines expects an array of pointers to scanlines.
     * Here the array is only one element long, but you could ask for
     * more than one scanline at a time if that's more convenient.
     */
    (void) jpeg_read_scanlines(&cinfo, buffer, 1);
    /* Assume put_scanline_someplace wants a pointer and sample count. */
    //put_scanline_someplace(buffer[0], row_stride);
  }

  /* Step 7: Finish decompression */

  (void) jpeg_finish_decompress(&cinfo);
  /* We can ignore the return value since suspension is not possible
   * with the stdio data source.
   */

  /* Step 8: Release JPEG decompression object */

  /* This is an important step since it will release a good deal of memory. */
  jpeg_destroy_decompress(&cinfo);

  /* After finish_decompress, we can close the input file.
   * Here we postpone it until after no more JPEG errors are possible,
   * so as to simplify the setjmp error logic above. (Actually, I don't
   * think that jpeg_destroy can do an error exit, but why assume anything...)
   */
  fclose(infile);
  return true;
#else
  return false;
#endif
}

unsigned int UrlValue::getImageWidth() {
  return (loadImg() ? width : 0);
}
unsigned int UrlValue::getImageHeight() {
  return (loadImg() ? height : 0);
}

void UrlValue::loadFunctions(FunctionLibrary* lib) {
  lib->push("imgheight", "R", &UrlValue::imgheight);
  lib->push("imgwidth", "R", &UrlValue::imgwidth);
}

Value* UrlValue::imgheight(vector<Value*> arguments) {
  NumberValue* val = new NumberValue(new Token("1", Token::NUMBER));
  val->setValue(((UrlValue*)arguments[0])->getImageHeight());
  val->setUnit("px");
  return val;
}
Value* UrlValue::imgwidth(vector<Value*> arguments) {
  NumberValue* val = new NumberValue(new Token("1", Token::NUMBER));

  val->setUnit("px");
  val->setValue((double)((UrlValue*)arguments[0])->getImageWidth());
  return val;
}
<commit_msg>Include stdio.h. It was sneaking in through png.h If you ./configure --without-libpng then stdio.h was not getting pulled in, but is required in the jpeg code. Ensure it comes in.<commit_after>/*
 * Copyright 2012 Bram van der Kroef
 *
 * This file is part of LESS CSS Compiler.
 *
 * LESS CSS Compiler is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * LESS CSS Compiler is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with LESS CSS Compiler.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Author: Bram van der Kroef <bram@vanderkroef.net>
 */

#include "UrlValue.h"

#include <config.h>

#ifdef WITH_LIBPNG
#include <png.h>
#endif

#ifdef WITH_LIBJPEG
#include <jpeglib.h>
#include <setjmp.h>
#include <stdio.h>

struct urlvalue_jpeg_error_mgr {
  struct jpeg_error_mgr pub;	/* "public" fields */

  jmp_buf setjmp_buffer;	/* for return to caller */
};

typedef struct urlvalue_jpeg_error_mgr * urlvalue_jpeg_error_ptr;

METHODDEF(void)
urlvalue_jpeg_error_exit (j_common_ptr cinfo)
{
  /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
  urlvalue_jpeg_error_ptr myerr = (urlvalue_jpeg_error_ptr) cinfo->err;

  /* Always display the message. */
  /* We could postpone this until after returning, if we chose. */
  (*cinfo->err->output_message) (cinfo);

  /* Return control to the setjmp point */
  longjmp(myerr->setjmp_buffer, 1);
}

#endif

UrlValue::UrlValue(Token* token, string path): Value() {
  tokens.push(token);
  this->path = path;
  type = Value::URL;
  loaded = false;
}

UrlValue::~UrlValue() {
}

Value* UrlValue::add(Value* v) {
  (void)v;
  throw new ValueException("You can not add images.");
}
Value* UrlValue::substract(Value* v) {
  (void)v;
  throw new ValueException("You can not substract images.");
}
Value* UrlValue::multiply(Value* v) {
  (void)v;
  throw new ValueException("You can not multiply images.");
}
Value* UrlValue::divide(Value* v) {
  (void)v;
  throw new ValueException("You can not divide images.");
}
int UrlValue::compare(Value* v) {
  if (v->type == URL) {
    return 0;
  } else {
    throw new ValueException("You can only compare images with images.");
  }
}

bool UrlValue::loadImg() {
  loaded = loadPng() || loadJpeg();

  return loaded;
}

bool UrlValue::loadPng() {
  if (loaded != false)
    return true;

#ifdef WITH_LIBPNG
  unsigned char header[8];    // 8 is the maximum size that can be checked
  /* open file and test for it being a png */

  png_structp png_ptr;
  png_infop info_ptr;

  FILE *fp = fopen(path.c_str(), "rb");
  if (!fp)
    return false; //"Image file could not be opened"

  fread(header, 1, 8, fp);
  if (png_sig_cmp(header, 0, 8))
    return false; //"Image is not a PNG file"

  /* initialize stuff */
  png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);

  if (!png_ptr)
    throw new ValueException("png_create_read_struct failed");

  info_ptr = png_create_info_struct(png_ptr);
  if (!info_ptr)
    throw new ValueException("png_create_info_struct failed");

  if (setjmp(png_jmpbuf(png_ptr)))
    throw new ValueException("Error during init_io");

  png_init_io(png_ptr, fp);
  png_set_sig_bytes(png_ptr, 8);

  png_read_info(png_ptr, info_ptr);

  width = png_get_image_width(png_ptr, info_ptr);
  height = png_get_image_height(png_ptr, info_ptr);

  fclose(fp);
  return true;

#else
  return false;
#endif
}

bool UrlValue::loadJpeg() {
  if (loaded != false)
    return true;

#ifdef WITH_LIBJPEG
  struct jpeg_decompress_struct cinfo;

  struct urlvalue_jpeg_error_mgr jerr;
  /* More stuff */
  FILE * infile;
  JSAMPARRAY buffer;	/* Output row buffer */
  int row_stride;	/* physical row width in output buffer */

  if ((infile = fopen(path.c_str(), "rb")) == NULL) {
    return false;
  }

  /* Step 1: allocate and initialize JPEG decompression object */

  /* We set up the normal JPEG error routines, then override error_exit. */
  cinfo.err = jpeg_std_error(&jerr.pub);
  jerr.pub.error_exit = urlvalue_jpeg_error_exit;
  /* Establish the setjmp return context for urlvalue_jpeg_error_exit to use. */
  if (setjmp(jerr.setjmp_buffer)) {
    /* If we get here, the JPEG code has signaled an error.
     * We need to clean up the JPEG object, close the input file, and return.
     */
    jpeg_destroy_decompress(&cinfo);
    fclose(infile);
    return false;
  }
  /* Now we can initialize the JPEG decompression object. */
  jpeg_create_decompress(&cinfo);

  /* Step 2: specify data source (eg, a file) */

  jpeg_stdio_src(&cinfo, infile);

  /* Step 3: read file parameters with jpeg_read_header() */

  (void) jpeg_read_header(&cinfo, TRUE);
  /* We can ignore the return value from jpeg_read_header since
   * (a) suspension is not possible with the stdio data source, and
   * (b) we passed TRUE to reject a tables-only JPEG file as an error.
   * See libjpeg.txt for more info.
   */

  /* Step 4: set parameters for decompression */

  /* In this example, we don't need to change any of the defaults set by
   * jpeg_read_header(), so we do nothing here.
   */

  /* Step 5: Start decompressor */

  (void) jpeg_start_decompress(&cinfo);
  /* We can ignore the return value since suspension is not possible
   * with the stdio data source.
   */

  width = cinfo.output_width;
  height = cinfo.output_height;

  /* We may need to do some setup of our own at this point before reading
   * the data. After jpeg_start_decompress() we have the correct scaled
   * output image dimensions available, as well as the output colormap
   * if we asked for color quantization.
   * In this example, we need to make an output work buffer of the right size.
   */
  /* JSAMPLEs per row in output buffer */
  row_stride = cinfo.output_width * cinfo.output_components;
  /* Make a one-row-high sample array that will go away when done with image */
  buffer = (*cinfo.mem->alloc_sarray)
    ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);

  /* Step 6: while (scan lines remain to be read) */
  /* jpeg_read_scanlines(...); */

  /* Here we use the library's state variable cinfo.output_scanline as the
   * loop counter, so that we don't have to keep track ourselves.
   */
  while (cinfo.output_scanline < cinfo.output_height) {
    /* jpeg_read_scanlines expects an array of pointers to scanlines.
     * Here the array is only one element long, but you could ask for
     * more than one scanline at a time if that's more convenient.
     */
    (void) jpeg_read_scanlines(&cinfo, buffer, 1);
    /* Assume put_scanline_someplace wants a pointer and sample count. */
    //put_scanline_someplace(buffer[0], row_stride);
  }

  /* Step 7: Finish decompression */

  (void) jpeg_finish_decompress(&cinfo);
  /* We can ignore the return value since suspension is not possible
   * with the stdio data source.
   */

  /* Step 8: Release JPEG decompression object */

  /* This is an important step since it will release a good deal of memory. */
  jpeg_destroy_decompress(&cinfo);

  /* After finish_decompress, we can close the input file.
   * Here we postpone it until after no more JPEG errors are possible,
   * so as to simplify the setjmp error logic above. (Actually, I don't
   * think that jpeg_destroy can do an error exit, but why assume anything...)
   */
  fclose(infile);
  return true;
#else
  return false;
#endif
}

unsigned int UrlValue::getImageWidth() {
  return (loadImg() ? width : 0);
}
unsigned int UrlValue::getImageHeight() {
  return (loadImg() ? height : 0);
}

void UrlValue::loadFunctions(FunctionLibrary* lib) {
  lib->push("imgheight", "R", &UrlValue::imgheight);
  lib->push("imgwidth", "R", &UrlValue::imgwidth);
}

Value* UrlValue::imgheight(vector<Value*> arguments) {
  NumberValue* val = new NumberValue(new Token("1", Token::NUMBER));
  val->setValue(((UrlValue*)arguments[0])->getImageHeight());
  val->setUnit("px");
  return val;
}
Value* UrlValue::imgwidth(vector<Value*> arguments) {
  NumberValue* val = new NumberValue(new Token("1", Token::NUMBER));

  val->setUnit("px");
  val->setValue((double)((UrlValue*)arguments[0])->getImageWidth());
  return val;
}
<|endoftext|>


<commit_before>/****************************************************************************
 **
 ** This file is part of the CAMP library.
 **
 ** The MIT License (MIT)
 **
 ** Copyright (C) 2009-2010 TECHNOGERMA Systems France and/or its subsidiary(-ies).
 ** Contact: Technogerma Systems France Information (contact@technogerma.fr)
 **
 ** Permission is hereby granted, free of charge, to any person obtaining a copy
 ** of this software and associated documentation files (the "Software"), to deal
 ** in the Software without restriction, including without limitation the rights
 ** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 ** copies of the Software, and to permit persons to whom the Software is
 ** furnished to do so, subject to the following conditions:
 **
 ** The above copyright notice and this permission notice shall be included in
 ** all copies or substantial portions of the Software.
 **
 ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 ** THE SOFTWARE.
 **
 ****************************************************************************/

#include "traits.hpp"
#include <camp/detail/functiontraits.hpp>
#include <camp/detail/objecttraits.hpp>
#include <boost/test/unit_test.hpp>

static void foo() {}

static int bar(float) {return 0;}

struct Callable {
    void operator () () {}
};

struct NonCallable {
    int x;
};

struct Methods
{
    void foo() {}
};

int intArray[10];

using namespace TraitsTest;

//-----------------------------------------------------------------------------
//                         Tests for camp::*traits
//-----------------------------------------------------------------------------
BOOST_AUTO_TEST_SUITE(TRAITS)

// Sanity check: C++11 features and syntax.
BOOST_AUTO_TEST_CASE(cpp11)
{
    foo(); bar(0.f); // to stop warning

    static_assert(std::is_function<decltype(foo)>::value, "std::is_function failed");
    static_assert(std::is_function<void ()>::value, "std::is_function failed");
    static_assert(!std::is_function<Callable>::value, "std::is_function failed");
    static_assert(!std::is_function<NonCallable>::value, "std::is_function failed");

    typedef void (*foo_t)();
    static_assert(std::is_void< std::result_of<decltype(foo)& ()>::type >::value, "std::result_of failed");
    static_assert(std::is_void< std::result_of<decltype(&foo) ()>::type >::value, "std::result_of failed");
    static_assert(std::is_void< std::result_of<foo_t ()>::type >::value, "std::result_of failed");

    typedef int (*bar_t)(float);
    static_assert(std::is_same< std::result_of<decltype(bar)& (float)>::type, int >::value, "std::result_of failed");
    static_assert(std::is_same< std::result_of<decltype(&bar) (float)>::type, int >::value, "std::result_of failed");
    static_assert(std::is_same< std::result_of<bar_t (float)>::type, int >::value, "std::result_of failed");
}

//-----------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(functionTraits)
{
    static_assert( ! camp::detail::FunctionTraits<int>::isFunction, "FunctionTraits<>::isFunction failed");
    static_assert( ! camp::detail::FunctionTraits<float>::isFunction, "FunctionTraits<>::isFunction failed");
    static_assert( ! camp::detail::FunctionTraits<int*>::isFunction, "FunctionTraits<>::isFunction failed");
    static_assert( ! camp::detail::FunctionTraits<char*>::isFunction, "FunctionTraits<>::isFunction failed");
    static_assert( ! camp::detail::FunctionTraits<int**>::isFunction, "FunctionTraits<>::isFunction failed");
    static_assert( ! camp::detail::FunctionTraits<std::string>::isFunction, "FunctionTraits<>::isFunction failed");

    static_assert(camp::detail::FunctionTraits<void(void)>::isFunction, "FunctionTraits<>::isFunction failed");
    static_assert(camp::detail::FunctionTraits<void(int)>::isFunction, "FunctionTraits<>::isFunction failed");
    static_assert(camp::detail::FunctionTraits<int(void)>::isFunction, "FunctionTraits<>::isFunction failed");
    static_assert(camp::detail::FunctionTraits<int(char*)>::isFunction, "FunctionTraits<>::isFunction failed");

    static_assert(camp::detail::FunctionTraits<decltype(foo)>::isFunction, "FunctionTraits<>::isFunction failed");
    static_assert(camp::detail::FunctionTraits<decltype(bar)>::isFunction, "FunctionTraits<>::isFunction failed");

    static_assert( ! camp::detail::FunctionTraits<NonCallable>::isFunction, "FunctionTraits<>::isFunction failed");
    // Only reports: function and pointer-to-member types & functors
    static_assert( ! camp::detail::FunctionTraits<Callable>::isFunction, "FunctionTraits<>::isFunction failed");

    static_assert(camp::detail::FunctionTraits<void(Methods::*)()>::isFunction, "FunctionTraits<>::isFunction failed");
    void (Methods::*meth_t)() = &Methods::foo;
    static_assert(camp::detail::FunctionTraits<decltype(meth_t)>::isFunction, "FunctionTraits<>::isFunction failed");
}

//-----------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(objectTraits_writable)
{
    // is writable
    static_assert(camp::detail::ObjectTraits<int*>::isWritable, "ObjectTraits<>::isWriteable failed");
    static_assert(camp::detail::ObjectTraits<char**>::isWritable, "ObjectTraits<>::isWriteable failed");

    // is not writable
    static_assert( ! camp::detail::ObjectTraits<int>::isWritable, "ObjectTraits<>::isWriteable failed");
    static_assert( ! camp::detail::ObjectTraits<const int*>::isWritable, "ObjectTraits<>::isWriteable failed");
    static_assert( ! camp::detail::ObjectTraits<const char * const *>::isWritable, "ObjectTraits<>::isWriteable failed");
    static_assert( ! camp::detail::ObjectTraits<void(...)>::isRef, "ObjectTraits<>::isRef failed");
    static_assert( ! camp::detail::ObjectTraits<decltype(intArray)>::isWritable, "ObjectTraits<>::isWriteable failed");
}

//-----------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(objectTraits_ref)
{
    // is ref
    static_assert(camp::detail::ObjectTraits<int*>::isRef, "ObjectTraits<>::isRef failed");
    static_assert(camp::detail::ObjectTraits<char**>::isRef, "ObjectTraits<>::isRef failed");
    static_assert(camp::detail::ObjectTraits<decltype(intArray)>::isRef, "ObjectTraits<>::isRef failed");

    // is not ref
    static_assert( ! camp::detail::ObjectTraits<int>::isRef, "ObjectTraits<>::isRef failed");
    static_assert( ! camp::detail::ObjectTraits<float>::isRef, "ObjectTraits<>::isRef failed");
    static_assert( ! camp::detail::ObjectTraits<void(...)>::isRef, "ObjectTraits<>::isRef failed");
}

//-----------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(objectTraits_refReturnType)
{
    static_assert(std::is_same<int&, camp::detail::ObjectTraits<int>::RefReturnType>::value, "ObjectTraits<>::RefReturnType failed");
    static_assert(std::is_same<float&, camp::detail::ObjectTraits<const float>::RefReturnType>::value, "ObjectTraits<>::RefReturnType failed");

    // ref return
    static_assert(std::is_same<int*, camp::detail::ObjectTraits<int*>::RefReturnType>::value, "ObjectTraits<>::RefReturnType failed");
    static_assert(std::is_same<const int*, camp::detail::ObjectTraits<const int*>::RefReturnType>::value, "ObjectTraits<>::RefReturnType failed");
}

//-----------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(objectTraits_pointerType)
{
    // pointer type
    static_assert(std::is_same<int*, camp::detail::ObjectTraits<int*>::PointerType>::value, "ObjectTraits<>::PointerType failed");
    static_assert(std::is_same<const int*, camp::detail::ObjectTraits<const int*>::PointerType>::value, "ObjectTraits<>::PointerType failed");
}

//-----------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(objectTraits_dataType)
{
    static_assert(std::is_same<int, camp::detail::ObjectTraits<int>::DataType>::value, "ObjectTraits<>::DataType failed");
    static_assert(std::is_same<int, camp::detail::ObjectTraits<int*>::DataType>::value, "ObjectTraits<>::DataType failed");
    static_assert(std::is_same<int, camp::detail::ObjectTraits<const int>::DataType>::value, "ObjectTraits<>::DataType failed");
    static_assert(std::is_same<int, camp::detail::ObjectTraits<const int*>::DataType>::value, "ObjectTraits<>::DataType failed");
    static_assert(std::is_same<int, camp::detail::ObjectTraits<int **>::DataType>::value, "ObjectTraits<>::DataType failed");
    static_assert(std::is_same<int, camp::detail::ObjectTraits<decltype(intArray)>::DataType>::value, "ObjectTraits<>::DataType failed");
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>Boost function sanity units tests.<commit_after>/****************************************************************************
 **
 ** This file is part of the CAMP library.
 **
 ** The MIT License (MIT)
 **
 ** Copyright (C) 2009-2010 TECHNOGERMA Systems France and/or its subsidiary(-ies).
 ** Contact: Technogerma Systems France Information (contact@technogerma.fr)
 **
 ** Permission is hereby granted, free of charge, to any person obtaining a copy
 ** of this software and associated documentation files (the "Software"), to deal
 ** in the Software without restriction, including without limitation the rights
 ** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 ** copies of the Software, and to permit persons to whom the Software is
 ** furnished to do so, subject to the following conditions:
 **
 ** The above copyright notice and this permission notice shall be included in
 ** all copies or substantial portions of the Software.
 **
 ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 ** THE SOFTWARE.
 **
 ****************************************************************************/

#include "traits.hpp"
#include <camp/detail/functiontraits.hpp>
#include <camp/detail/objecttraits.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/function_types/function_type.hpp>

static void foo() {}

static int bar(float) {return 0;}

struct Callable {
    void operator () () {}
};

struct NonCallable {
    int x;
};

struct Methods
{
    void foo() {}
};

int intArray[10];

using namespace TraitsTest;

//-----------------------------------------------------------------------------
//                         Tests for camp::*traits
//-----------------------------------------------------------------------------
BOOST_AUTO_TEST_SUITE(TRAITS)

// Sanity check: C++11 features and syntax.
BOOST_AUTO_TEST_CASE(cpp11)
{
    foo(); bar(0.f); // to stop warning

    static_assert(std::is_function<decltype(foo)>::value, "std::is_function failed");
    static_assert(std::is_function<void ()>::value, "std::is_function failed");
    static_assert(!std::is_function<Callable>::value, "std::is_function failed");
    static_assert(!std::is_function<NonCallable>::value, "std::is_function failed");

    typedef void (*foo_t)();
    static_assert(std::is_void< std::result_of<decltype(foo)& ()>::type >::value, "std::result_of failed");
    static_assert(std::is_void< std::result_of<decltype(&foo) ()>::type >::value, "std::result_of failed");
    static_assert(std::is_void< std::result_of<foo_t ()>::type >::value, "std::result_of failed");

    typedef int (*bar_t)(float);
    static_assert(std::is_same< std::result_of<decltype(bar)& (float)>::type, int >::value, "std::result_of failed");
    static_assert(std::is_same< std::result_of<decltype(&bar) (float)>::type, int >::value, "std::result_of failed");
    static_assert(std::is_same< std::result_of<bar_t (float)>::type, int >::value, "std::result_of failed");
}

//-----------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(functionTraits)
{
    static_assert( ! camp::detail::FunctionTraits<int>::isFunction, "FunctionTraits<>::isFunction failed");
    static_assert( ! camp::detail::FunctionTraits<float>::isFunction, "FunctionTraits<>::isFunction failed");
    static_assert( ! camp::detail::FunctionTraits<int*>::isFunction, "FunctionTraits<>::isFunction failed");
    static_assert( ! camp::detail::FunctionTraits<char*>::isFunction, "FunctionTraits<>::isFunction failed");
    static_assert( ! camp::detail::FunctionTraits<int**>::isFunction, "FunctionTraits<>::isFunction failed");
    static_assert( ! camp::detail::FunctionTraits<std::string>::isFunction, "FunctionTraits<>::isFunction failed");

    static_assert(camp::detail::FunctionTraits<void(void)>::isFunction, "FunctionTraits<>::isFunction failed");
    static_assert(camp::detail::FunctionTraits<void(int)>::isFunction, "FunctionTraits<>::isFunction failed");
    static_assert(camp::detail::FunctionTraits<int(void)>::isFunction, "FunctionTraits<>::isFunction failed");
    static_assert(camp::detail::FunctionTraits<int(char*)>::isFunction, "FunctionTraits<>::isFunction failed");

    static_assert(camp::detail::FunctionTraits<decltype(foo)>::isFunction, "FunctionTraits<>::isFunction failed");
    static_assert(camp::detail::FunctionTraits<decltype(bar)>::isFunction, "FunctionTraits<>::isFunction failed");

    static_assert( ! camp::detail::FunctionTraits<NonCallable>::isFunction, "FunctionTraits<>::isFunction failed");
    // Only reports: function and pointer-to-member types & functors
    static_assert( ! camp::detail::FunctionTraits<Callable>::isFunction, "FunctionTraits<>::isFunction failed");

    static_assert(camp::detail::FunctionTraits<void(Methods::*)()>::isFunction, "FunctionTraits<>::isFunction failed");
    void (Methods::*meth_t)() = &Methods::foo;
    static_assert(camp::detail::FunctionTraits<decltype(meth_t)>::isFunction, "FunctionTraits<>::isFunction failed");
}

//-----------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(objectTraits_writable)
{
    // is writable
    static_assert(camp::detail::ObjectTraits<int*>::isWritable, "ObjectTraits<>::isWriteable failed");
    static_assert(camp::detail::ObjectTraits<char**>::isWritable, "ObjectTraits<>::isWriteable failed");

    // is not writable
    static_assert( ! camp::detail::ObjectTraits<int>::isWritable, "ObjectTraits<>::isWriteable failed");
    static_assert( ! camp::detail::ObjectTraits<const int*>::isWritable, "ObjectTraits<>::isWriteable failed");
    static_assert( ! camp::detail::ObjectTraits<const char * const *>::isWritable, "ObjectTraits<>::isWriteable failed");
    static_assert( ! camp::detail::ObjectTraits<void(...)>::isRef, "ObjectTraits<>::isRef failed");
    static_assert( ! camp::detail::ObjectTraits<decltype(intArray)>::isWritable, "ObjectTraits<>::isWriteable failed");
}

//-----------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(objectTraits_ref)
{
    // is ref
    static_assert(camp::detail::ObjectTraits<int*>::isRef, "ObjectTraits<>::isRef failed");
    static_assert(camp::detail::ObjectTraits<char**>::isRef, "ObjectTraits<>::isRef failed");
    static_assert(camp::detail::ObjectTraits<decltype(intArray)>::isRef, "ObjectTraits<>::isRef failed");

    // is not ref
    static_assert( ! camp::detail::ObjectTraits<int>::isRef, "ObjectTraits<>::isRef failed");
    static_assert( ! camp::detail::ObjectTraits<float>::isRef, "ObjectTraits<>::isRef failed");
    static_assert( ! camp::detail::ObjectTraits<void(...)>::isRef, "ObjectTraits<>::isRef failed");
}

//-----------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(objectTraits_refReturnType)
{
    static_assert(std::is_same<int&, camp::detail::ObjectTraits<int>::RefReturnType>::value, "ObjectTraits<>::RefReturnType failed");
    static_assert(std::is_same<float&, camp::detail::ObjectTraits<const float>::RefReturnType>::value, "ObjectTraits<>::RefReturnType failed");

    // ref return
    static_assert(std::is_same<int*, camp::detail::ObjectTraits<int*>::RefReturnType>::value, "ObjectTraits<>::RefReturnType failed");
    static_assert(std::is_same<const int*, camp::detail::ObjectTraits<const int*>::RefReturnType>::value, "ObjectTraits<>::RefReturnType failed");
}

//-----------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(objectTraits_pointerType)
{
    // pointer type
    static_assert(std::is_same<int*, camp::detail::ObjectTraits<int*>::PointerType>::value, "ObjectTraits<>::PointerType failed");
    static_assert(std::is_same<const int*, camp::detail::ObjectTraits<const int*>::PointerType>::value, "ObjectTraits<>::PointerType failed");
}

//-----------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(objectTraits_dataType)
{
    static_assert(std::is_same<int, camp::detail::ObjectTraits<int>::DataType>::value, "ObjectTraits<>::DataType failed");
    static_assert(std::is_same<int, camp::detail::ObjectTraits<int*>::DataType>::value, "ObjectTraits<>::DataType failed");
    static_assert(std::is_same<int, camp::detail::ObjectTraits<const int>::DataType>::value, "ObjectTraits<>::DataType failed");
    static_assert(std::is_same<int, camp::detail::ObjectTraits<const int*>::DataType>::value, "ObjectTraits<>::DataType failed");
    static_assert(std::is_same<int, camp::detail::ObjectTraits<int **>::DataType>::value, "ObjectTraits<>::DataType failed");
    static_assert(std::is_same<int, camp::detail::ObjectTraits<decltype(intArray)>::DataType>::value, "ObjectTraits<>::DataType failed");
}

//-----------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(boost_function)
{
    typedef void (*fn1_t)(void);
    static_assert(std::is_same<void(), boost::function_types::function_type<fn1_t>::type>::value, "boost::function_types problem");

    typedef int (*fn2_t)(int,const char*,float&);
    static_assert(std::is_same<int(int,const char*,float&), boost::function_types::function_type<fn2_t>::type>::value, "boost::function_types problem");

    struct TestClass {
        int foo(float) {return 0;}
    };

    typedef int (TestClass::*fn3_t)(float);
    static_assert(std::is_same<int(TestClass&,float), boost::function_types::function_type<fn3_t>::type>::value, "boost::function_types problem");
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


<commit_before>#include <iostream>
#include <functional>

#include "core/argparse.h"
#include "core/image.h"
#include "core/random.h"
#include "core/io.h"
#include "core/vec2.h"
#include "core/palette.h"
#include "core/closestpoint.h"
#include "core/rect.h"

#include "core/palette_tableu.h"
#include "core/poisson.h"

using namespace euphoria::core;

float
i2f(int i)
{
    return static_cast<float>(i);
}

std::vector<vec2f> GenerateRandomPoints(int count, const Rectf& size, Random* random)
{
    std::vector<vec2f> r;
    for(int i=0; i<count; i+=1)
    {
        r.emplace_back( size.RandomPoint(random) );
    }
    return r;
}

enum class DistanceFunction
{
    Euclidian, Manhattan
};
DECLARE_ENUM_LIST(DistanceFunction)
BEGIN_ENUM_LIST(DistanceFunction)
    ENUM_VALUE(DistanceFunction, Euclidian)
    ENUM_VALUE(DistanceFunction, Manhattan)
END_ENUM_LIST()

enum class PointGeneration
{
    Random, Poisson
};
DECLARE_ENUM_LIST(PointGeneration)
BEGIN_ENUM_LIST(PointGeneration)
    ENUM_VALUE(PointGeneration, Random)
    ENUM_VALUE(PointGeneration, Poisson)
END_ENUM_LIST()

int
main(int argc, char* argv[])
{
    auto size = 512;

    auto distance_function = DistanceFunction::Euclidian;

    auto point_generation = PointGeneration::Random;
    auto number_of_points = 30;
    auto poisson_radius = 10.0f;
    auto use_colorblind = false;
    auto crazy_distance = false;
    std::string output_path = "voronoi.png";

    auto parser = argparse::Parser {"voronoi generator"};

    parser.AddSimple("-nopoints", &number_of_points).Help("number of points (in random)");
    parser.AddSimple("-radius", &poisson_radius).Help("point radius (in poisson)");
    parser.AddSimple("-imsize", &size).Help("image size");
    parser.AddSimple("-output", &output_path).Help("where to save the result");
    parser.SetTrue("-colorblind", &use_colorblind).Help("Switch to a colorblind palette");
    parser.SetTrue("-crazy", &crazy_distance).Help("Sort distance acording to abs(size-distance)");
    parser.AddEnum("-distance", &distance_function).Help("How to calculate distance");
    parser.AddEnum("-gen", &point_generation).Help("How to generate points");

    if(parser.Parse(argc, argv) != argparse::ParseResult::Ok) { return -1; }

    Random rand;

    const auto area = Rectf::FromWidthHeight(size, size);
    const auto random_points =
        point_generation == PointGeneration::Random
        ? GenerateRandomPoints(number_of_points, area, &rand)
        : PoissonSample(area, &rand, poisson_radius*2, poisson_radius);

    using DistFunc = std::function<float(const vec2f&, const vec2f&)>;

    DistFunc euclidian_distance = [](const vec2f& lhs, const vec2f& rhs)
    {
        return (lhs-rhs).GetLengthSquared();
    };

    DistFunc manhattan_distance = [](const vec2f& lhs, const vec2f& rhs)
    {
        const auto d = (lhs-rhs);
        return Abs(d.x) + Abs(d.y);
    };

    auto pal = use_colorblind
        ? palette::ColorBlind_10()
        : Palette::Rainbow(random_points.size());
    Image image;
    image.SetupNoAlphaSupport(size, size);

    const float max_distance = (area.GetWidth() + area.GetHeight())/2;

    auto points = ClosestPoint<vec2f, int, DistFunc, float>
        {
            [&](const vec2f& lhs, const vec2f& rhs)
            {
                const auto dist_func = distance_function == DistanceFunction::Euclidian
                    ? euclidian_distance
                    : manhattan_distance;
                const auto dist = dist_func(lhs, rhs);
                if(crazy_distance)
                {
                    return Abs(max_distance - dist);
                }
                else
                {
                    return dist;
                }
            }
        }
        ;
    {
        int index = 0;
        for(auto p: random_points)
        {
            points.Add(p, index);
            index += 1;
        }
    }

    image.SetAllBottomTop([&](int x, int y) {
        const auto index = points.FindClosest(vec2f{i2f(x), i2f(y)});
        return pal.GetSafeIndex(index);
    });

    io::ChunkToFile(image.Write(ImageWriteFormat::PNG), output_path);

    return 0;
}
<commit_msg>moar distances and stuff<commit_after>#include <iostream>
#include <functional>

#include "core/argparse.h"
#include "core/image.h"
#include "core/random.h"
#include "core/io.h"
#include "core/vec2.h"
#include "core/palette.h"
#include "core/closestpoint.h"
#include "core/rect.h"

#include "core/palette_tableu.h"
#include "core/poisson.h"

using namespace euphoria::core;

float
i2f(int i)
{
    return static_cast<float>(i);
}

std::vector<vec2f> GenerateRandomPoints(int count, const Rectf& size, Random* random)
{
    std::vector<vec2f> r;
    for(int i=0; i<count; i+=1)
    {
        r.emplace_back( size.RandomPoint(random) );
    }
    return r;
}

enum class DistanceFunction
{
    Euclidian, Manhattan, Min, Max
};
DECLARE_ENUM_LIST(DistanceFunction)
BEGIN_ENUM_LIST(DistanceFunction)
    ENUM_VALUE(DistanceFunction, Euclidian)
    ENUM_VALUE(DistanceFunction, Manhattan)
    ENUM_VALUE(DistanceFunction, Min)
    ENUM_VALUE(DistanceFunction, Max)
END_ENUM_LIST()

enum class PointGeneration
{
    Random, Poisson
};
DECLARE_ENUM_LIST(PointGeneration)
BEGIN_ENUM_LIST(PointGeneration)
    ENUM_VALUE(PointGeneration, Random)
    ENUM_VALUE(PointGeneration, Poisson)
END_ENUM_LIST()

vec2f Abs(const vec2f& a)
{
    return {Abs(a.x), Abs(a.y)};
}

float euclidian_distance(const vec2f& lhs, const vec2f& rhs)
{
    return (lhs-rhs).GetLengthSquared();
}

float manhattan_distance(const vec2f& lhs, const vec2f& rhs)
{
    const auto d = Abs(lhs-rhs);
    return d.x + d.y;
}

float min_distance(const vec2f& lhs, const vec2f& rhs)
{
    const auto d = Abs(lhs-rhs);
    return Min(d.x, d.y);
}

float max_distance(const vec2f& lhs, const vec2f& rhs)
{
    const auto d = Abs(lhs-rhs);
    return Max(d.x, d.y);
}

float GetDistance(DistanceFunction f, const vec2f& lhs, const vec2f& rhs)
{
    switch(f)
    {
        case DistanceFunction::Euclidian: return euclidian_distance(lhs, rhs);
        case DistanceFunction::Manhattan: return manhattan_distance(lhs, rhs);
        case DistanceFunction::Min: return min_distance(lhs, rhs);
        case DistanceFunction::Max: return max_distance(lhs, rhs);
    }
    DIE("Unhandled distancfe function");
};

int
main(int argc, char* argv[])
{
    auto size = 512;

    auto distance_function = DistanceFunction::Euclidian;

    auto point_generation = PointGeneration::Random;
    auto number_of_points = 30;
    auto poisson_radius = 10.0f;
    auto use_colorblind = false;
    auto cos_distance = false;
    auto crazy_distance = 0.0f;
    std::string output_path = "voronoi.png";

    auto parser = argparse::Parser {"voronoi generator"};

    parser.AddSimple("-nopoints", &number_of_points).Help("number of points (in random)");
    parser.AddSimple("-radius", &poisson_radius).Help("point radius (in poisson)");
    parser.AddSimple("-imsize", &size).Help("image size");
    parser.AddSimple("-output", &output_path).Help("where to save the result");
    parser.SetTrue("-colorblind", &use_colorblind).Help("Switch to a colorblind palette");
    parser.AddSimple("-crazy", &crazy_distance).Help("Sort distance acording to abs(value-distance)");
    parser.AddSimple("-cos", &crazy_distance).Help("Instead of distance, cos(distance)");
    parser.AddEnum("-distance", &distance_function).Help("How to calculate distance");
    parser.AddEnum("-gen", &point_generation).Help("How to generate points");

    if(parser.Parse(argc, argv) != argparse::ParseResult::Ok) { return -1; }

    Random rand;

    const auto area = Rectf::FromWidthHeight(size, size);
    const auto random_points =
        point_generation == PointGeneration::Random
        ? GenerateRandomPoints(number_of_points, area, &rand)
        : PoissonSample(area, &rand, poisson_radius*2, poisson_radius);

    auto pal = use_colorblind
        ? palette::ColorBlind_10()
        : Palette::Rainbow(random_points.size());
    Image image;
    image.SetupNoAlphaSupport(size, size);

    auto points = ClosestPoint<vec2f, int, std::function<float (const vec2f&, const vec2f&)>, float>
        {
            [&](const vec2f& lhs, const vec2f& rhs)
            {
                const auto dist = GetDistance(distance_function, lhs, rhs);
                if(cos_distance)
                {
                    return Cos(Angle::FromDegrees(dist/crazy_distance));
                }
                else
                {
                    return Abs(crazy_distance - dist);
                }
            }
        }
        ;
    {
        int index = 0;
        for(auto p: random_points)
        {
            points.Add(p, index);
            index += 1;
        }
    }

    image.SetAllBottomTop([&](int x, int y) {
        const auto index = points.FindClosest(vec2f{i2f(x), i2f(y)});
        return pal.GetSafeIndex(index);
    });

    io::ChunkToFile(image.Write(ImageWriteFormat::PNG), output_path);

    return 0;
}
<|endoftext|>


<commit_before>// video_odometry.cpp - ROS node to publish odometry from video images.

#include <ros/ros.h>
#include <std_msgs/String.h>
#include <sensor_msgs/Image.h>
#include <sensor_msgs/image_encodings.h>
#include <nav_msgs/Odometry.h>
#include <image_transport/image_transport.h>
#include <cv_bridge/cv_bridge.h>
#include <opencv2/opencv.hpp>
#include <opencv2/features2d.hpp>

#include <iostream>

int horizonWidth, horizonHeight;
int odomWidth, odomHeight, odomMargin;

int horizonFeatures;
double horizonQuality;
int horizonMinDistance;
int horizonWindowSize;

int odomFeatures;
double odomQuality;
int odomMinDistance;
int odomWindowSize;

double headingScaling;
double odomScaling;

bool publishFlow;

long frameCount;

cv::Mat horizonMask;
cv::Mat odomMask;

cv::Size horizonWindow;
cv::Size odomWindow;

cv::Ptr<cv::Feature2D> horizonDetector;
cv::Ptr<cv::Feature2D> odomDetector;

cv::Mat lastGray;
std::vector<cv::Point2f> lastHorizonPoints;
std::vector<cv::Point2f> lastOdomPoints;

ros::Publisher imagePub;

void makeMasks(const cv::Mat &frame) {
  cv::Scalar ones(255, 255, 255);

  cv::Point2f horizonNW(0, frame.cols/2 - horizonWidth/2);
  cv::Point2f horizonSE(horizonHeight, frame.cols/2 + horizonWidth/2);
  horizonMask = cv::Mat::zeros(frame.rows, frame.cols, CV_8UC1);
  cv::rectangle(horizonMask, horizonNW, horizonSE, ones);

  cv::Point2f odomNW(frame.rows - odomMargin - odomHeight,
		     frame.cols/2 - odomWidth/2);
  cv::Point2f odomSE(frame.rows - odomMargin,
		     frame.cols/2 + odomWidth/2);
  odomMask = cv::Mat::zeros(frame.rows, frame.cols, CV_8UC1);
  cv::rectangle(odomMask, odomNW, odomSE, ones);

  horizonWindow = cv::Size(horizonWindowSize, horizonWindowSize);
  odomWindow = cv::Size(odomWindowSize, odomWindowSize);
}

void findFlow(cv::Mat &frame, const cv::Mat &gray,
	      std::vector<cv::Point2f> horizonPoints,
	      std::vector<cv::Point2f> odomPoints) {

  std::vector<uchar> status;
  std::vector<float> error;

  cv::calcOpticalFlowPyrLK(lastGray, gray, lastHorizonPoints, horizonPoints,
			   status, error, horizonWindow);
  cv::Mat horizonInliers;
  cv::Mat horizonH = cv::findHomography(lastHorizonPoints, horizonPoints,
					cv::RANSAC, 2.5F, horizonInliers);

  if (horizonH.total() == 0) {
    // Cannot calculate anything if we don't have horizon homography.
    return;
  }

  cv::Mat horizonP = cv::Mat(cv::Point3d(frame.cols/2, frame.rows/2, 1));
  cv::Mat horizonP1;
  if (horizonH.total() == 0) {
    horizonP1 = horizonP;
  } else {
    horizonP1 = horizonH * horizonP;
    // Normalize the translated point.
    horizonP1 /= horizonP1.at<double>(2);
  }

  cv::calcOpticalFlowPyrLK(lastGray, gray, lastOdomPoints, odomPoints,
			   status, error, odomWindow);
  cv::Mat odomInliers;
  cv::Mat odomH = cv::findHomography(lastOdomPoints, odomPoints,
				     cv::RANSAC, 2.5F, odomInliers);
  cv::Mat odomP = cv::Mat(cv::Point3d(frame.cols/2,
				      frame.rows - odomMargin - odomHeight/2,
				      1));
  cv::Mat odomP1;
  if (odomH.total() == 0) {
    odomP1 = odomP;
  } else {
    odomP1 = odomH * odomP;
    odomP1 /= odomP1.at<double>(2);
  }

  cv::Point2f offset(1,1);
  cv::Scalar vectorColor(0, 255, 80);
  cv::Scalar consensusColor(0, 0, 255);

  for (int i=0; i < horizonPoints.size(); ++i) {
    if (horizonInliers.at<uchar>(i)) {
      cv::rectangle(frame, lastHorizonPoints[i]-offset,
		    lastHorizonPoints[i]+offset,
		    vectorColor);
      cv::line(frame, lastHorizonPoints[i], horizonPoints[i], vectorColor);
    }
  }

  for (int i=0; i < odomPoints.size(); ++i) {
    if (odomInliers.at<uchar>(i)) {
      cv::rectangle(frame, lastOdomPoints[i]-offset,
		    lastOdomPoints[i]+offset,
		    vectorColor);
      cv::line(frame, lastOdomPoints[i], odomPoints[i], vectorColor);
    }
  }

  if (publishFlow) {
    sensor_msgs::ImagePtr msg =
      cv_bridge::CvImage(std_msgs::Header(), "bgr8", frame).toImageMsg();
    imagePub.publish(msg);
  }
}

void processImage(const sensor_msgs::Image::ConstPtr& msg) {
  cv::Mat frame = cv_bridge::toCvShare(msg, "bgr8")->image;

  if (frameCount == 0) {
    makeMasks(frame);
  }

  ++frameCount;
  ROS_INFO("Processing frame %ld", frameCount);

  cv::Mat gray;
  cv::cvtColor(frame, gray, cv::COLOR_BGR2GRAY);

  std::vector<cv::KeyPoint> keypoints;
  std::vector<cv::Point2f> horizonPoints;
  std::vector<cv::Point2f> odomPoints;

  horizonDetector->detect(gray, keypoints, horizonMask);
  cv::KeyPoint::convert(keypoints, lastHorizonPoints);

  odomDetector->detect(gray, keypoints, odomMask);
  cv::KeyPoint::convert(keypoints, lastOdomPoints);

  if (frameCount >= 2) {
    findFlow(frame, gray, horizonPoints, odomPoints);
  }

  lastGray = gray;

  lastHorizonPoints = horizonPoints;
  lastOdomPoints = odomPoints;
}

/**
 * Starts the ROS node.
 */
int main(int argc, char **argv) {
  ros::init(argc, argv, "video_odometry");
  ros::NodeHandle n("~");

  n.param("horizon_width", horizonWidth, 400);
  n.param("horizon_height", horizonHeight, 150);

  n.param("odom_width", odomWidth, 300);
  n.param("odom_height", odomHeight, 150);
  n.param("odom_margin", odomMargin, 50);

  n.param("horizon_features", horizonFeatures, 500);
  n.param("horizon_quality", horizonQuality, 0.01);
  n.param("horizon_min_distance", horizonMinDistance, 1);
  n.param("horizon_window_size", horizonWindowSize, 21);

  n.param("odom_features", odomFeatures, 500);
  n.param("odom_quality", odomQuality, 0.01);
  n.param("odom_min_distance", odomMinDistance, 1);
  n.param("odom_window_size", odomWindowSize, 21);

  n.param("heading_scaling", headingScaling, 0.002);
  n.param("odom_scaling", odomScaling, 0.003);

  n.param("publish_flow", publishFlow, false);

  ros::Subscriber imageSub = n.subscribe("/color/image_raw", 1000, processImage);

  ros::Publisher odomPub = n.advertise<nav_msgs::Odometry>("/odom", 1000);
  imagePub = n.advertise<sensor_msgs::Image>("/color/image_flow", 1000);

  horizonDetector =
    cv::GFTTDetector::create(horizonFeatures, horizonQuality,
			     horizonMinDistance, 3);
  odomDetector =
    cv::GFTTDetector::create(odomFeatures, odomQuality,
			     odomMinDistance, 3);
  frameCount = 0;

  ros::spin();

  return 0;
}
<commit_msg>Fix mask rectangle calculation<commit_after>// video_odometry.cpp - ROS node to publish odometry from video images.

#include <ros/ros.h>
#include <std_msgs/String.h>
#include <sensor_msgs/Image.h>
#include <sensor_msgs/image_encodings.h>
#include <nav_msgs/Odometry.h>
#include <image_transport/image_transport.h>
#include <cv_bridge/cv_bridge.h>
#include <opencv2/opencv.hpp>
#include <opencv2/features2d.hpp>

#include <iostream>

int horizonWidth, horizonHeight;
int odomWidth, odomHeight, odomMargin;

int horizonFeatures;
double horizonQuality;
int horizonMinDistance;
int horizonWindowSize;

int odomFeatures;
double odomQuality;
int odomMinDistance;
int odomWindowSize;

double headingScaling;
double odomScaling;

bool publishFlow;

long frameCount;

cv::Mat horizonMask;
cv::Mat odomMask;

cv::Size horizonWindow;
cv::Size odomWindow;

cv::Ptr<cv::Feature2D> horizonDetector;
cv::Ptr<cv::Feature2D> odomDetector;

cv::Mat lastGray;
std::vector<cv::Point2f> lastHorizonPoints;
std::vector<cv::Point2f> lastOdomPoints;

ros::Publisher imagePub;

void makeMasks(const cv::Mat &frame) {
  cv::Scalar ones(255, 255, 255);

  cv::Point2f horizonNW(frame.cols/2 - horizonWidth/2, 0);
  cv::Point2f horizonSE(frame.cols/2 + horizonWidth/2, horizonHeight);
  horizonMask = cv::Mat::zeros(frame.rows, frame.cols, CV_8UC1);
  cv::rectangle(horizonMask, horizonNW, horizonSE, ones, CV_FILLED);

  cv::Point2f odomNW(frame.cols/2 - odomWidth/2,
		     frame.rows - odomMargin - odomHeight);
  cv::Point2f odomSE(frame.cols/2 + odomWidth/2,
		     frame.rows - odomMargin);
  odomMask = cv::Mat::zeros(frame.rows, frame.cols, CV_8UC1);
  cv::rectangle(odomMask, odomNW, odomSE, ones, CV_FILLED);

  horizonWindow = cv::Size(horizonWindowSize, horizonWindowSize);
  odomWindow = cv::Size(odomWindowSize, odomWindowSize);
}

void findFlow(cv::Mat &frame, const cv::Mat &gray,
	      std::vector<cv::Point2f> horizonPoints,
	      std::vector<cv::Point2f> odomPoints) {

  std::vector<uchar> status;
  std::vector<float> error;

  cv::calcOpticalFlowPyrLK(lastGray, gray, lastHorizonPoints, horizonPoints,
			   status, error, horizonWindow);
  cv::Mat horizonInliers;
  cv::Mat horizonH = cv::findHomography(lastHorizonPoints, horizonPoints,
					cv::RANSAC, 2.5F, horizonInliers);

  if (horizonH.total() == 0) {
    // Cannot calculate anything if we don't have horizon homography.
    return;
  }

  cv::Mat horizonP = cv::Mat(cv::Point3d(frame.cols/2, frame.rows/2, 1));
  cv::Mat horizonP1;
  if (horizonH.total() == 0) {
    horizonP1 = horizonP;
  } else {
    horizonP1 = horizonH * horizonP;
    // Normalize the translated point.
    horizonP1 /= horizonP1.at<double>(2);
  }

  cv::calcOpticalFlowPyrLK(lastGray, gray, lastOdomPoints, odomPoints,
			   status, error, odomWindow);
  cv::Mat odomInliers;
  cv::Mat odomH = cv::findHomography(lastOdomPoints, odomPoints,
				     cv::RANSAC, 2.5F, odomInliers);
  cv::Mat odomP = cv::Mat(cv::Point3d(frame.cols/2,
				      frame.rows - odomMargin - odomHeight/2,
				      1));
  cv::Mat odomP1;
  if (odomH.total() == 0) {
    odomP1 = odomP;
  } else {
    odomP1 = odomH * odomP;
    odomP1 /= odomP1.at<double>(2);
  }

  cv::Point2f offset(1,1);
  cv::Scalar vectorColor(0, 255, 80);
  cv::Scalar consensusColor(0, 0, 255);

  for (int i=0; i < horizonPoints.size(); ++i) {
    if (horizonInliers.at<uchar>(i)) {
      cv::rectangle(frame, lastHorizonPoints[i]-offset,
		    lastHorizonPoints[i]+offset,
		    vectorColor);
      cv::line(frame, lastHorizonPoints[i], horizonPoints[i], vectorColor);
    }
  }

  for (int i=0; i < odomPoints.size(); ++i) {
    if (odomInliers.at<uchar>(i)) {
      cv::rectangle(frame, lastOdomPoints[i]-offset,
		    lastOdomPoints[i]+offset,
		    vectorColor);
      cv::line(frame, lastOdomPoints[i], odomPoints[i], vectorColor);
    }
  }

  if (publishFlow) {
    sensor_msgs::ImagePtr msg =
      cv_bridge::CvImage(std_msgs::Header(), "bgr8", frame).toImageMsg();
    imagePub.publish(msg);
  }
}

void processImage(const sensor_msgs::Image::ConstPtr& msg) {
  cv::Mat frame = cv_bridge::toCvShare(msg, "bgr8")->image;

  if (frameCount == 0) {
    makeMasks(frame);
  }

  ++frameCount;
  ROS_INFO("Processing frame %ld", frameCount);

  cv::Mat gray;
  cv::cvtColor(frame, gray, cv::COLOR_BGR2GRAY);

  std::vector<cv::KeyPoint> keypoints;
  std::vector<cv::Point2f> horizonPoints;
  std::vector<cv::Point2f> odomPoints;

  horizonDetector->detect(gray, keypoints, horizonMask);
  cv::KeyPoint::convert(keypoints, lastHorizonPoints);

  odomDetector->detect(gray, keypoints, odomMask);
  cv::KeyPoint::convert(keypoints, lastOdomPoints);

  if (frameCount >= 2) {
    findFlow(frame, gray, horizonPoints, odomPoints);
  }

  lastGray = gray;

  lastHorizonPoints = horizonPoints;
  lastOdomPoints = odomPoints;
}

/**
 * Starts the ROS node.
 */
int main(int argc, char **argv) {
  ros::init(argc, argv, "video_odometry");
  ros::NodeHandle n("~");

  n.param("horizon_width", horizonWidth, 400);
  n.param("horizon_height", horizonHeight, 150);

  n.param("odom_width", odomWidth, 300);
  n.param("odom_height", odomHeight, 150);
  n.param("odom_margin", odomMargin, 50);

  n.param("horizon_features", horizonFeatures, 500);
  n.param("horizon_quality", horizonQuality, 0.01);
  n.param("horizon_min_distance", horizonMinDistance, 1);
  n.param("horizon_window_size", horizonWindowSize, 21);

  n.param("odom_features", odomFeatures, 500);
  n.param("odom_quality", odomQuality, 0.01);
  n.param("odom_min_distance", odomMinDistance, 1);
  n.param("odom_window_size", odomWindowSize, 21);

  n.param("heading_scaling", headingScaling, 0.002);
  n.param("odom_scaling", odomScaling, 0.003);

  n.param("publish_flow", publishFlow, false);

  ros::Subscriber imageSub = n.subscribe("/color/image_raw", 1000, processImage);

  ros::Publisher odomPub = n.advertise<nav_msgs::Odometry>("/odom", 1000);
  imagePub = n.advertise<sensor_msgs::Image>("/color/image_flow", 1000);

  horizonDetector =
    cv::GFTTDetector::create(horizonFeatures, horizonQuality,
			     horizonMinDistance, 3);
  odomDetector =
    cv::GFTTDetector::create(odomFeatures, odomQuality,
			     odomMinDistance, 3);
  frameCount = 0;

  ros::spin();

  return 0;
}
<|endoftext|>


<commit_before>/**
 * This file is part of the "FnordMetric" project
 *   Copyright (c) 2014 Paul Asmuth, Google Inc.
 *
 * FnordMetric is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License v3.0. You should have received a
 * copy of the GNU General Public License along with this program. If not, see
 * <http://www.gnu.org/licenses/>.
 */
#include "fnord-feeds/RemoteFeedReader.h"
#include "fnord/base/logging.h"
#include "fnord/json/JSONRPCCodec.h"
#include "fnord-feeds/FeedService.h"

namespace fnord {
namespace feeds {

RemoteFeedReader::RemoteFeedReader(
    RPCClient* rpc_client) :
    rpc_client_(rpc_client),
    time_backfill_fn_(nullptr) {}

void RemoteFeedReader::addSourceFeed(
    URI rpc_url,
    String feed_name,
    uint64_t initial_offset,
    size_t batch_size /* = kDefaultBatchSize */,
    size_t max_buffer_size /* = kDefaultMaxBufferSize */) {
  for (const auto& source : sources_) {
    if (source->feed_name == feed_name) {
      RAISEF(kIndexError, "feed '$0' already exists", feed_name);
    }
  }

  auto source = new SourceFeed();
  source->rpc_url = rpc_url;
  source->feed_name = feed_name;
  source->batch_size = batch_size;
  source->max_buffer_size = max_buffer_size;
  source->is_fetching = false;
  source->next_offset = initial_offset;
  source->consumed_offset = initial_offset;
  source->stream_time = 0;
  sources_.emplace_back(source);
}

Option<FeedEntry> RemoteFeedReader::fetchNextEntry() {
  ScopedLock<std::mutex> lk(mutex_);
  int idx = -1;
  uint64_t min_stream_time = std::numeric_limits<uint64_t>::max();
  Duration max_spread(3600 * kMicrosPerSecond);

  for (int i = 0; i < sources_.size(); ++i) {
    const auto& source = sources_[i];

    maybeFillBuffer(source.get());

    if (source->stream_time.unixMicros() < min_stream_time) {
      min_stream_time = source->stream_time.unixMicros();

      if (source->read_buffer.size() > 0) {
        idx = i;
      } else {
        idx = -1;
      }
    }
  }

  if (idx == -1) {
    min_stream_time += max_spread.microseconds();

    for (int i = 0; i < sources_.size(); ++i) {
      const auto& source = sources_[i];

      if (source->stream_time.unixMicros() < min_stream_time &&
          source->read_buffer.size() > 0) {
        min_stream_time = source->stream_time.unixMicros();
        idx = i;
      }
    }
  }

  if (idx < 0) {
    return None<FeedEntry>();
  } else {
    const auto& source = sources_[idx];
    auto entry = source->read_buffer.front();
    source->read_buffer.pop_front();

    if (entry.time > source->stream_time) {
      source->stream_time = entry.time;
    }

    source->consumed_offset = entry.next_offset;
    return Some(entry);
  }
}

void RemoteFeedReader::maybeFillBuffer(SourceFeed* source) {
  if (source->is_fetching ||
      source->read_buffer.size() >= source->max_buffer_size) {
    return;
  }

  source->is_fetching = true;

#ifndef NDEBUG
  fnord::logTrace(
      "fnord.feeds.remotefeedreader",
      "Fetching from feed\n    name=$0\n    url=$1\n    offset=$2",
      source->feed_name,
      source->rpc_url.toString(),
      source->next_offset);
#endif

  auto rpc = fnord::mkRPC<json::JSONRPCCodec>(
      &FeedService::fetch,
      source->feed_name,
      source->next_offset,
      (int) source->batch_size);

  rpc_client_->call(source->rpc_url, rpc.get());

  rpc->onSuccess([this, source] (const decltype(rpc)::ValueType& r) mutable {
    ScopedLock<std::mutex> lk(mutex_);

    for (const auto& e : r.result()) {
      auto entry = e;

      if (entry.time.unixMicros() == 0 && time_backfill_fn_) {
        entry.time = time_backfill_fn_(entry);
      }

      source->read_buffer.emplace_back(std::move(entry));
    }

    source->is_fetching = false;

    if (source->read_buffer.size() > 0) {
      source->next_offset = source->read_buffer.back().next_offset;
      lk.unlock();
      data_available_wakeup_.wakeup();
    }
  });

  rpc->onError([this, source] (const Status& status) {
    ScopedLock<std::mutex> lk(mutex_);
    source->is_fetching = false;
    lk.unlock();

    logError(
        "fnord.feeds.remotefeedreader",
        "Error while fetching from feed:\n" \
        "    feed=$1\n    url=$0\n    error=$2",
        source->rpc_url.toString(),
        source->feed_name,
        status);

    data_available_wakeup_.wakeup();
  });
}

void RemoteFeedReader::waitForNextEntry() {
  ScopedLock<std::mutex> lk(mutex_);
  bool is_data_available = false;

  for (const auto& source : sources_) {
    maybeFillBuffer(source.get());

    if (source->read_buffer.size() > 0) {
      is_data_available = true;
    }
  }

  /* fastpath if there is data available on any feed */
  if (is_data_available) {
    return;
  }

  auto wakeup_gen = data_available_wakeup_.generation();
  lk.unlock();

  /* wait until there is any data available */
  data_available_wakeup_.waitForWakeup(wakeup_gen);
}

Vector<Pair<String, uint64_t>> RemoteFeedReader::streamOffsets() const {
  ScopedLock<std::mutex> lk(mutex_);

  Vector<Pair<String, uint64_t>> offsets;
  for (const auto& source : sources_) {
    offsets.emplace_back(source->feed_name, source->consumed_offset);
  }

  return offsets;
}

Pair<DateTime, DateTime> RemoteFeedReader::watermarks() const {
  ScopedLock<std::mutex> lk(mutex_);

  if (sources_.size() == 0) {
    return std::make_pair(DateTime(0), DateTime(0));
  }

  uint64_t low = std::numeric_limits<uint64_t>::max();
  uint64_t high = 0;

  for (const auto& source : sources_) {
    if (source->stream_time.unixMicros() < low) {
      low = source->stream_time.unixMicros();
    }

    if (source->stream_time.unixMicros() > high) {
      high = source->stream_time.unixMicros();
    }
  }

  return std::make_pair(DateTime(low), DateTime(high));
}

DateTime RemoteFeedReader::lowWatermark() const {
  return watermarks().first;
}

DateTime RemoteFeedReader::highWatermark() const {
  return watermarks().second;
}

void RemoteFeedReader::setTimeBackfill(
    Function<DateTime (const FeedEntry& entry)> fn) {
  time_backfill_fn_ = fn;
}

void RemoteFeedReader::exportStats(
    const String& path_prefix /* = "/fnord/feeds/reader/" */,
    stats::StatsRepository* stats_repo /* = nullptr */) {
}

}
}
<commit_msg>RemoteFeedReader: unconditional time backfill<commit_after>/**
 * This file is part of the "FnordMetric" project
 *   Copyright (c) 2014 Paul Asmuth, Google Inc.
 *
 * FnordMetric is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License v3.0. You should have received a
 * copy of the GNU General Public License along with this program. If not, see
 * <http://www.gnu.org/licenses/>.
 */
#include "fnord-feeds/RemoteFeedReader.h"
#include "fnord/base/logging.h"
#include "fnord/json/JSONRPCCodec.h"
#include "fnord-feeds/FeedService.h"

namespace fnord {
namespace feeds {

RemoteFeedReader::RemoteFeedReader(
    RPCClient* rpc_client) :
    rpc_client_(rpc_client),
    time_backfill_fn_(nullptr) {}

void RemoteFeedReader::addSourceFeed(
    URI rpc_url,
    String feed_name,
    uint64_t initial_offset,
    size_t batch_size /* = kDefaultBatchSize */,
    size_t max_buffer_size /* = kDefaultMaxBufferSize */) {
  for (const auto& source : sources_) {
    if (source->feed_name == feed_name) {
      RAISEF(kIndexError, "feed '$0' already exists", feed_name);
    }
  }

  auto source = new SourceFeed();
  source->rpc_url = rpc_url;
  source->feed_name = feed_name;
  source->batch_size = batch_size;
  source->max_buffer_size = max_buffer_size;
  source->is_fetching = false;
  source->next_offset = initial_offset;
  source->consumed_offset = initial_offset;
  source->stream_time = 0;
  sources_.emplace_back(source);
}

Option<FeedEntry> RemoteFeedReader::fetchNextEntry() {
  ScopedLock<std::mutex> lk(mutex_);
  int idx = -1;
  uint64_t min_stream_time = std::numeric_limits<uint64_t>::max();
  Duration max_spread(3600 * kMicrosPerSecond);

  for (int i = 0; i < sources_.size(); ++i) {
    const auto& source = sources_[i];

    maybeFillBuffer(source.get());

    if (source->stream_time.unixMicros() < min_stream_time) {
      min_stream_time = source->stream_time.unixMicros();

      if (source->read_buffer.size() > 0) {
        idx = i;
      } else {
        idx = -1;
      }
    }
  }

  if (idx == -1) {
    min_stream_time += max_spread.microseconds();

    for (int i = 0; i < sources_.size(); ++i) {
      const auto& source = sources_[i];

      if (source->stream_time.unixMicros() < min_stream_time &&
          source->read_buffer.size() > 0) {
        min_stream_time = source->stream_time.unixMicros();
        idx = i;
      }
    }
  }

  if (idx < 0) {
    return None<FeedEntry>();
  } else {
    const auto& source = sources_[idx];
    auto entry = source->read_buffer.front();
    source->read_buffer.pop_front();

    if (entry.time > source->stream_time) {
      source->stream_time = entry.time;
    }

    source->consumed_offset = entry.next_offset;
    return Some(entry);
  }
}

void RemoteFeedReader::maybeFillBuffer(SourceFeed* source) {
  if (source->is_fetching ||
      source->read_buffer.size() >= source->max_buffer_size) {
    return;
  }

  source->is_fetching = true;

#ifndef NDEBUG
  fnord::logTrace(
      "fnord.feeds.remotefeedreader",
      "Fetching from feed\n    name=$0\n    url=$1\n    offset=$2",
      source->feed_name,
      source->rpc_url.toString(),
      source->next_offset);
#endif

  auto rpc = fnord::mkRPC<json::JSONRPCCodec>(
      &FeedService::fetch,
      source->feed_name,
      source->next_offset,
      (int) source->batch_size);

  rpc_client_->call(source->rpc_url, rpc.get());

  rpc->onSuccess([this, source] (const decltype(rpc)::ValueType& r) mutable {
    ScopedLock<std::mutex> lk(mutex_);

    for (const auto& e : r.result()) {
      auto entry = e;

      if (time_backfill_fn_) {
        entry.time = time_backfill_fn_(entry);
      }

      source->read_buffer.emplace_back(std::move(entry));
    }

    source->is_fetching = false;

    if (source->read_buffer.size() > 0) {
      source->next_offset = source->read_buffer.back().next_offset;
      lk.unlock();
      data_available_wakeup_.wakeup();
    }
  });

  rpc->onError([this, source] (const Status& status) {
    ScopedLock<std::mutex> lk(mutex_);
    source->is_fetching = false;
    lk.unlock();

    logError(
        "fnord.feeds.remotefeedreader",
        "Error while fetching from feed:\n" \
        "    feed=$1\n    url=$0\n    error=$2",
        source->rpc_url.toString(),
        source->feed_name,
        status);

    data_available_wakeup_.wakeup();
  });
}

void RemoteFeedReader::waitForNextEntry() {
  ScopedLock<std::mutex> lk(mutex_);
  bool is_data_available = false;

  for (const auto& source : sources_) {
    maybeFillBuffer(source.get());

    if (source->read_buffer.size() > 0) {
      is_data_available = true;
    }
  }

  /* fastpath if there is data available on any feed */
  if (is_data_available) {
    return;
  }

  auto wakeup_gen = data_available_wakeup_.generation();
  lk.unlock();

  /* wait until there is any data available */
  data_available_wakeup_.waitForWakeup(wakeup_gen);
}

Vector<Pair<String, uint64_t>> RemoteFeedReader::streamOffsets() const {
  ScopedLock<std::mutex> lk(mutex_);

  Vector<Pair<String, uint64_t>> offsets;
  for (const auto& source : sources_) {
    offsets.emplace_back(source->feed_name, source->consumed_offset);
  }

  return offsets;
}

Pair<DateTime, DateTime> RemoteFeedReader::watermarks() const {
  ScopedLock<std::mutex> lk(mutex_);

  if (sources_.size() == 0) {
    return std::make_pair(DateTime(0), DateTime(0));
  }

  uint64_t low = std::numeric_limits<uint64_t>::max();
  uint64_t high = 0;

  for (const auto& source : sources_) {
    if (source->stream_time.unixMicros() < low) {
      low = source->stream_time.unixMicros();
    }

    if (source->stream_time.unixMicros() > high) {
      high = source->stream_time.unixMicros();
    }
  }

  return std::make_pair(DateTime(low), DateTime(high));
}

DateTime RemoteFeedReader::lowWatermark() const {
  return watermarks().first;
}

DateTime RemoteFeedReader::highWatermark() const {
  return watermarks().second;
}

void RemoteFeedReader::setTimeBackfill(
    Function<DateTime (const FeedEntry& entry)> fn) {
  time_backfill_fn_ = fn;
}

void RemoteFeedReader::exportStats(
    const String& path_prefix /* = "/fnord/feeds/reader/" */,
    stats::StatsRepository* stats_repo /* = nullptr */) {
}

}
}
<|endoftext|>


<commit_before>//
// Created by Masashi Fujita on 2016/05/06.
//
// Copyright (c) 2016 Masashi Fujita.
//

#include <doctest/doctest.h>
#include <array>
#include <vector>
#include <string>

#include "chacha20.hpp"

extern "C" {
#include "ecrypt-sync.h"
}

namespace {
    std::vector<uint8_t>    encode (ECRYPT_ctx &ctx, const std::string &s) {
        std::vector<uint8_t>    result ;
        result.resize (s.size ()) ;
        ECRYPT_encrypt_bytes (&ctx, (const u8 *)s.data (), (u8 *)result.data (), s.size ()) ;
        return result ;
    }

    std::vector<uint8_t>    encode (ChaCha::State &state, const std::string &s) {
        std::vector<uint8_t>    result ;
        result.resize (s.size ()) ;
        ChaCha::apply (state, result.data (), s.data (), s.size ()) ;
        return result ;
    }

    std::vector<uint8_t>    encode (ChaCha::State &state, const std::string &s, size_t offset) {
        std::vector<uint8_t>    result ;
        result.resize (s.size ()) ;
        ChaCha::apply (state, result.data (), s.data (), s.size (), offset) ;
        return result ;
    }
}

TEST_CASE ("Test with small key") {
    SUBCASE ("with small key") {
        ECRYPT_ctx ctx;
        memset (&ctx, 0, sizeof (ctx));
        const std::string key { "0123456789abcdef" };
        REQUIRE (8 * key.size () == 128);
        ECRYPT_keysetup (&ctx, reinterpret_cast<const u8 *> (key.data ()), 128, 0);
        std::array<uint8_t, 8> iv;
        iv.fill (0);
        ECRYPT_ivsetup (&ctx, static_cast<const u8 *> (iv.data ()));
        ChaCha::State S { key.data (), key.size (), 0 };

        SUBCASE ("Compare states") {
            auto const        &state = S.state ();
            for (int_fast32_t i = 0 ; i < state.size () ; ++i) {
                CAPTURE (i);
                REQUIRE (state[i] == ctx.input[i]);
            }
        }
        SUBCASE ("Encrypt strings") {
            SUBCASE ("Encrypt \"A\"") {
                auto const &expected = encode (ctx, "A");
                auto const &actual   = encode (S, "A");
                REQUIRE (expected.size () == actual.size ());
                REQUIRE (expected == actual);
            }
            SUBCASE ("Encrypt \"The ninja warrior are the immortal murder machines.\"") {
                const std::string src { "The ninja warrior are the immortal murder machines." };
                auto const &expected = encode (ctx, src);
                auto const &actual   = encode (S, src);
                REQUIRE (expected.size () == actual.size ());
                REQUIRE (expected == actual);
            }
            SUBCASE ("Encrypt \"The ninja warrior are the immortal murder machines.\" (splitted)") {
                const std::string src { "The ninja warrior are the immortal murder machines." };
                auto              off = src.size () / 2;
                auto const &expected = encode (ctx, src);
                auto       &&a0      = encode (S, src.substr (0, off), 0);
                auto const &a1       = encode (S, src.substr (off), off);
                a0.insert (a0.end (), a1.cbegin (), a1.cend ());
                REQUIRE (expected.size () == a0.size ());
                REQUIRE (expected == a0);
            }
        }
    }
}

TEST_CASE ("Test with large key") {
    SUBCASE ("with large key") {
        ECRYPT_ctx  ctx ;
        memset (&ctx, 0, sizeof (ctx)) ;
        std::string key { "0123456789abcdef0123456789abcdef" };
        REQUIRE (8 * key.size () == 256) ;
        ECRYPT_keysetup (&ctx, reinterpret_cast<const u8 *> (key.data ()), 256, 0) ;
        std::array<uint8_t, 8>  iv ;
        iv.fill (0) ;
        ECRYPT_ivsetup (&ctx, static_cast<const u8 *> (iv.data ())) ;
        ChaCha::State   S { key.data (), key.size (), 0 } ;

        SUBCASE ("Compare states") {
            auto const &    state = S.state () ;
            for (int_fast32_t i = 0 ; i < state.size () ; ++i) {
                CAPTURE (i) ;
                REQUIRE (state [i] == ctx.input [i]) ;
            }
        }
        SUBCASE ("Encrypt strings") {
            SUBCASE ("Encrypt \"A\"") {
                auto const &    expected = encode (ctx, "A") ;
                auto const &    actual = encode (S, "A") ;
                REQUIRE (expected.size () == actual.size ()) ;
                REQUIRE (expected == actual) ;
            }
            SUBCASE ("Encrypt \"The ninja warrior are the immortal murder machines.\"") {
                const std::string   src { "The ninja warrior are the immortal murder machines." };
                auto const &    expected = encode (ctx, src) ;
                auto const &    actual = encode (S, src) ;
                REQUIRE (expected.size () == actual.size ()) ;
                REQUIRE (expected == actual) ;
            }
            SUBCASE ("Encrypt \"The ninja warrior are the immortal murder machines.\" (splitted)") {
                const std::string   src { "The ninja warrior are the immortal murder machines." };
                auto off = src.size () / 2 ;
                auto const &    expected = encode (ctx, src) ;
                auto &&    a0 = encode (S, src.substr (0, off), 0) ;
                auto const &    a1 = encode (S, src.substr (off), off) ;
                a0.insert (a0.end (), a1.cbegin (), a1.cend ()) ;
                REQUIRE (expected.size () == a0.size ()) ;
                REQUIRE (expected == a0) ;
            }
        }
    }
}
<commit_msg>test: Add property tests<commit_after>//
// Created by Masashi Fujita on 2016/05/06.
//
// Copyright (c) 2016 Masashi Fujita.
//

#include "chacha20.hpp"

#include "doctest-rapidcheck.hpp"

#include <array>
#include <vector>
#include <string>

#include <doctest/doctest.h>

extern "C" {
#include "ecrypt-sync.h"
}

namespace {
    std::vector<uint8_t>    encode (ECRYPT_ctx &ctx, const std::string &s) {
        std::vector<uint8_t>    result ;
        result.resize (s.size ()) ;
        ECRYPT_encrypt_bytes (&ctx, (const u8 *)s.data (), (u8 *)result.data (), s.size ()) ;
        return result ;
    }

    std::vector<uint8_t>    encode (ChaCha::State &state, const std::string &s) {
        std::vector<uint8_t>    result ;
        result.resize (s.size ()) ;
        ChaCha::apply (state, result.data (), s.data (), s.size ()) ;
        return result ;
    }

    std::vector<uint8_t>    encode (ChaCha::State &state, const std::string &s, size_t offset) {
        std::vector<uint8_t>    result ;
        result.resize (s.size ()) ;
        ChaCha::apply (state, result.data (), s.data (), s.size (), offset) ;
        return result ;
    }
    template <typename It_>
        std::string concat (It_ b, It_ e) {
            size_t sz = 0;
            for (auto it = b; it != e; ++it) {
                sz += it->size ();
            }
            std::string result;
            result.reserve (sz);
            for (auto it = b; it != e; ++it) {
                result += *it;
            }
            return result;
        }
}

TEST_CASE ("Test with small key") {
    SUBCASE ("with small key") {
        ECRYPT_ctx ctx;
        memset (&ctx, 0, sizeof (ctx));
        const std::string key { "0123456789abcdef" };
        REQUIRE (8 * key.size () == 128);
        ECRYPT_keysetup (&ctx, reinterpret_cast<const u8 *> (key.data ()), 128, 0);
        std::array<uint8_t, 8> iv;
        iv.fill (0);
        ECRYPT_ivsetup (&ctx, static_cast<const u8 *> (iv.data ()));
        ChaCha::State S { key.data (), key.size (), 0 };

        SUBCASE ("Compare states") {
            auto const        &state = S.state ();
            for (int_fast32_t i = 0 ; i < state.size () ; ++i) {
                CAPTURE (i);
                REQUIRE (state[i] == ctx.input[i]);
            }
        }
        SUBCASE ("Encrypt strings") {
            SUBCASE ("Encrypt \"A\"") {
                auto const &expected = encode (ctx, "A");
                auto const &actual   = encode (S, "A");
                REQUIRE (expected.size () == actual.size ());
                REQUIRE (expected == actual);
            }
            SUBCASE ("Encrypt \"The ninja warrior are the immortal murder machines.\"") {
                const std::string src { "The ninja warrior are the immortal murder machines." };
                auto const &expected = encode (ctx, src);
                auto const &actual   = encode (S, src);
                REQUIRE (expected.size () == actual.size ());
                REQUIRE (expected == actual);
            }
            SUBCASE ("Encrypt \"The ninja warrior are the immortal murder machines.\" (splitted)") {
                const std::string src { "The ninja warrior are the immortal murder machines." };
                auto              off = src.size () / 2;
                auto const &expected = encode (ctx, src);
                auto       &&a0      = encode (S, src.substr (0, off), 0);
                auto const &a1       = encode (S, src.substr (off), off);
                a0.insert (a0.end (), a1.cbegin (), a1.cend ());
                REQUIRE (expected.size () == a0.size ());
                REQUIRE (expected == a0);
            }
        }
    }
}

TEST_CASE ("Test with large key") {
    SUBCASE ("with large key") {
        ECRYPT_ctx  ctx ;
        memset (&ctx, 0, sizeof (ctx)) ;
        std::string key { "0123456789abcdef0123456789abcdef" };
        REQUIRE (8 * key.size () == 256) ;
        ECRYPT_keysetup (&ctx, reinterpret_cast<const u8 *> (key.data ()), 256, 0) ;
        std::array<uint8_t, 8>  iv ;
        iv.fill (0) ;
        ECRYPT_ivsetup (&ctx, static_cast<const u8 *> (iv.data ())) ;
        ChaCha::State   S { key.data (), key.size (), 0 } ;

        SUBCASE ("Compare states") {
            auto const &    state = S.state () ;
            for (int_fast32_t i = 0 ; i < state.size () ; ++i) {
                CAPTURE (i) ;
                REQUIRE (state [i] == ctx.input [i]) ;
            }
        }
        SUBCASE ("Encrypt strings") {
            SUBCASE ("Encrypt \"A\"") {
                auto const &    expected = encode (ctx, "A") ;
                auto const &    actual = encode (S, "A") ;
                REQUIRE (expected.size () == actual.size ()) ;
                REQUIRE (expected == actual) ;
            }
            SUBCASE ("Encrypt \"The ninja warrior are the immortal murder machines.\"") {
                const std::string   src { "The ninja warrior are the immortal murder machines." };
                auto const &    expected = encode (ctx, src) ;
                auto const &    actual = encode (S, src) ;
                REQUIRE (expected.size () == actual.size ()) ;
                REQUIRE (expected == actual) ;
            }
            SUBCASE ("Encrypt \"The ninja warrior are the immortal murder machines.\" (splitted)") {
                const std::string   src { "The ninja warrior are the immortal murder machines." };
                auto off = src.size () / 2 ;
                auto const &    expected = encode (ctx, src) ;
                auto &&    a0 = encode (S, src.substr (0, off), 0) ;
                auto const &    a1 = encode (S, src.substr (off), off) ;
                a0.insert (a0.end (), a1.cbegin (), a1.cend ()) ;
                REQUIRE (expected.size () == a0.size ()) ;
                REQUIRE (expected == a0) ;
            }
        }
    }
}

TEST_CASE ("property") {
    rc::prop("roundtrip", [](){
        auto const key_size = *rc::gen::element(16, 32);
        auto const &key = *rc::gen::container<std::vector<char>> (key_size, rc::gen::arbitrary<char>());
        auto const &plain = *rc::gen::scale(128, rc::gen::arbitrary<std::string>());

        RC_ASSERT (key.size () == 16 || key.size () == 32);
        ECRYPT_ctx  ctx ;
        memset (&ctx, 0, sizeof (ctx)) ;

        ECRYPT_keysetup (&ctx, reinterpret_cast<const u8 *> (key.data ()), 8u * key.size (), 0) ;
        std::array<uint8_t, 8>  iv ;
        iv.fill (0) ;
        ECRYPT_ivsetup (&ctx, static_cast<const u8 *> (iv.data ())) ;
        ChaCha::State   S { key.data (), key.size (), 0 } ;

        auto const &    state = S.state () ;
        for (int_fast32_t i = 0 ; i < state.size () ; ++i) {
            RC_ASSERT (state [i] == ctx.input [i]) ;
        }
        auto const &expected = encode(ctx, plain);
        auto const &actual = encode(S, plain);
        RC_ASSERT (std::equal(expected.begin (), expected.end (), actual.begin(), actual.end(),
                              [](auto a, auto b) { return a == b; }));
    });
    rc::prop ("splitted inputs", [](){
        auto const    key_size = *rc::gen::element (16, 32).as ("key_size");
        auto const    &key     = *rc::gen::container<std::vector<char>> (key_size, rc::gen::arbitrary<char> ()).as ("key");
        ChaCha::State S { key.data (), key.size () };
        auto const    &inputs  = *rc::gen::container<std::vector<std::string>> (rc::gen::string<std::string> ()).as ("inputs");
        auto const &plain = concat (inputs.begin (), inputs.end ());
        std::string expected;
        expected.reserve (plain.size ());
        {
            auto const &e = encode (S, plain);
            std::copy (e.begin (), e.end (), std::back_inserter (expected));
        }
        std::string actual;
        actual.reserve (plain.size ());
        size_t          off = 0;
        for (auto const &s : inputs) {
            auto const &e = encode (S, s, off);
            off += s.size ();

            std::copy (e.begin (), e.end (), std::back_inserter (actual));
        }
        RC_TAG(inputs.size (), plain.size ());
        RC_ASSERT (expected.size () == actual.size ());
        RC_ASSERT (expected == actual);
    });
}
<|endoftext|>