<commit_before>#include <cassert>
#include <sstream>

#include <iostream>

#include <SQLiteDatabase.h>

using std::size_t;
using std::string;
using std::stringstream;

using namespace SoundCity;

SQLiteDatabase::SQLiteDatabase(const string &file)
: filename(file), dbConnection(nullptr){

}

SQLiteDatabase::~SQLiteDatabase() {
  if (dbConnection)
  {
    sqlite3_close(dbConnection);
  }
}

int SQLiteDatabase::initialization()
{
  // @see http://www.sqlite.org/c3ref/open.html
  int status = sqlite3_open_v2(
    filename.c_str(),
    &dbConnection,
    SQLITE_OPEN_READONLY,
    nullptr
  );

  if(status != SQLITE_OK)
  {
    std::cout << status << ": " << sqlite3_errstr(status) << std::endl;
    assert(status == SQLITE_OK);
  }

  return 1;
}

enum RowIndex {
  TRACK_ID,
  TRACK_TITLE,
  TRACK_DURATION,
  CONTEXT_POPULARITY,
  CONTEXT_ARTIST_ID,
  CONTEXT_ARTIST_NAME,
  CONTEXT_ARTIST_FAMILIARITY,
  CONTEXT_ARTIST_POPULARITY,
  CONTEXT_ALBUM_ID,
  CONTEXT_ALBUM_TITLE,
  CONTEXT_ALBUM_RELEASE,
  CONTEXT_ALBUM_ARTIST_ID,
  CONTEXT_ALBUM_ARTIST_NAME,
  CONTEXT_ALBUM_ARTIST_FAMILIARITY,
  CONTEXT_ALBUM_ARTIST_POPULARITY,
  SIGNAL_RHYTHM,
  SIGNAL_ENERGY,
  SIGNAL_TONALITY
};

TrackPool SQLiteDatabase::select(const OptionList &options, size_t size)
{
  stringstream buffer;
  buffer << u8"SELECT ";
    // Track
    buffer << u8"tracks.id, ";
    buffer << u8"tracks.title, ";
    buffer << u8"tracks.duration, ";
      // Context
      buffer << u8"tracks.popularity, ";
        // Artist
        buffer << u8"artists.id, ";
        buffer << u8"artists.name, ";
        buffer << u8"artists.familiarity, ";
        buffer << u8"artists.popularity, ";
        // Album
        buffer << u8"albums.id, ";
        buffer << u8"albums.title, ";
        buffer << u8"albums.release, ";
          // Album Artist
          buffer << u8"album_artists.id, ";
          buffer << u8"album_artists.name, ";
          buffer << u8"album_artists.familiarity, ";
          buffer << u8"album_artists.popularity, ";
      // Signal
      buffer << u8"tracks.rhythm, tracks.energy, 0.0 ",
  buffer << u8" ";
  buffer << u8"FROM tracks ";
  buffer << u8"INNER JOIN albums ";
  buffer << u8"ON tracks.album_id = albums.id ";
  buffer << u8"INNER JOIN artists ";
  buffer << u8"ON tracks.artist_id = artists.id ";
  buffer << u8"INNER JOIN artists AS album_artists ";
  buffer << u8"ON albums.artist_id = album_artists.id ";

  stringstream whereBuffer;
  bool firstWhere = true;

  // Year filtering
  if (options.getStartYear() > 0 && options.getEndYear() > 0)
  {
    if (firstWhere)
      firstWhere = false;
    else
      whereBuffer << "AND ";

    whereBuffer << u8"albums.release BETWEEN "
                << options.getStartYear()
                << u8" AND "
                << options.getEndYear()
                << u8" ";
  }

  string whereRequest = whereBuffer.str();
  std::cout << whereBuffer.str() << std::endl;
  if (!whereRequest.empty()) buffer << u8"WHERE " << whereRequest;
  buffer << u8"ORDER BY random() ";
  buffer << u8"LIMIT " << size << u8";";

  string request = buffer.str();

  // Request compilation
  sqlite3_stmt *preparedRequest = nullptr;
  int status = sqlite3_prepare_v2(
    dbConnection,
    request.c_str(),
    request.size(),
    &preparedRequest,
    nullptr
  );

  if(status != SQLITE_OK)
  {
    std::cout << status << ": " <<sqlite3_errstr(status) << std::endl;
    assert(status == SQLITE_OK);
  }

  TrackPool pool(size);

  // Contruction de la pool de pistes
  while ((status = sqlite3_step(preparedRequest)) != SQLITE_DONE)
  {
    //status = sqlite3_step(preparedRequest);

    assert(status == SQLITE_ROW);

    Track track(
      sqlite3_column_int(preparedRequest, TRACK_ID),
      reinterpret_cast<char const *>(
        sqlite3_column_text(preparedRequest, TRACK_TITLE)
      ),
      SignalData(
        sqlite3_column_double(preparedRequest, SIGNAL_RHYTHM),
        sqlite3_column_double(preparedRequest, SIGNAL_ENERGY),
        sqlite3_column_double(preparedRequest, SIGNAL_TONALITY)
      ),
      ContextData(
        Artist(
          sqlite3_column_int(preparedRequest, CONTEXT_ARTIST_ID),
          reinterpret_cast<char const *>(
            sqlite3_column_text(preparedRequest, CONTEXT_ARTIST_NAME)
          ),
          //"",
          sqlite3_column_double(preparedRequest, CONTEXT_ARTIST_FAMILIARITY),
          sqlite3_column_double(preparedRequest, CONTEXT_ARTIST_POPULARITY)
        ),
        Album(
          sqlite3_column_int(preparedRequest, CONTEXT_ALBUM_ID),
          reinterpret_cast<char const *>(
            sqlite3_column_text(preparedRequest, CONTEXT_ALBUM_TITLE)
          ),
          reinterpret_cast<char const *>(
            sqlite3_column_text(preparedRequest, CONTEXT_ALBUM_ARTIST_NAME)
          ),
          sqlite3_column_int(preparedRequest, CONTEXT_ALBUM_RELEASE)
        ),
        sqlite3_column_double(preparedRequest, CONTEXT_POPULARITY)
      )
    );

    pool.insert(track);
  }

  sqlite3_free(preparedRequest);

  return pool;
}
<commit_msg>correction des fuites mémoires<commit_after>#include <cassert>
#include <sstream>

#include <iostream>

#include <SQLiteDatabase.h>

using std::size_t;
using std::string;
using std::stringstream;

using namespace SoundCity;

SQLiteDatabase::SQLiteDatabase(const string &file)
: filename(file), dbConnection(nullptr){

}

SQLiteDatabase::~SQLiteDatabase() {
  if (dbConnection)
  {
    sqlite3_close(dbConnection);
  }
}

int SQLiteDatabase::initialization()
{
  // @see http://www.sqlite.org/c3ref/open.html
  int status = sqlite3_open_v2(
    filename.c_str(),
    &dbConnection,
    SQLITE_OPEN_READONLY,
    nullptr
  );

  if(status != SQLITE_OK)
  {
    std::cout << status << ": " << sqlite3_errstr(status) << std::endl;
    assert(status == SQLITE_OK);
  }

  return 1;
}

enum RowIndex {
  TRACK_ID,
  TRACK_TITLE,
  TRACK_DURATION,
  CONTEXT_POPULARITY,
  CONTEXT_ARTIST_ID,
  CONTEXT_ARTIST_NAME,
  CONTEXT_ARTIST_FAMILIARITY,
  CONTEXT_ARTIST_POPULARITY,
  CONTEXT_ALBUM_ID,
  CONTEXT_ALBUM_TITLE,
  CONTEXT_ALBUM_RELEASE,
  CONTEXT_ALBUM_ARTIST_ID,
  CONTEXT_ALBUM_ARTIST_NAME,
  CONTEXT_ALBUM_ARTIST_FAMILIARITY,
  CONTEXT_ALBUM_ARTIST_POPULARITY,
  SIGNAL_RHYTHM,
  SIGNAL_ENERGY,
  SIGNAL_TONALITY
};

TrackPool SQLiteDatabase::select(const OptionList &options, size_t size)
{
  stringstream buffer;
  buffer << u8"SELECT ";
    // Track
    buffer << u8"tracks.id, ";
    buffer << u8"tracks.title, ";
    buffer << u8"tracks.duration, ";
      // Context
      buffer << u8"tracks.popularity, ";
        // Artist
        buffer << u8"artists.id, ";
        buffer << u8"artists.name, ";
        buffer << u8"artists.familiarity, ";
        buffer << u8"artists.popularity, ";
        // Album
        buffer << u8"albums.id, ";
        buffer << u8"albums.title, ";
        buffer << u8"albums.release, ";
          // Album Artist
          buffer << u8"album_artists.id, ";
          buffer << u8"album_artists.name, ";
          buffer << u8"album_artists.familiarity, ";
          buffer << u8"album_artists.popularity, ";
      // Signal
      buffer << u8"tracks.rhythm, tracks.energy, 0.0 ",
  buffer << u8" ";
  buffer << u8"FROM tracks ";
  buffer << u8"INNER JOIN albums ";
  buffer << u8"ON tracks.album_id = albums.id ";
  buffer << u8"INNER JOIN artists ";
  buffer << u8"ON tracks.artist_id = artists.id ";
  buffer << u8"INNER JOIN artists AS album_artists ";
  buffer << u8"ON albums.artist_id = album_artists.id ";

  stringstream whereBuffer;
  bool firstWhere = true;

  // Year filtering
  if (options.getStartYear() > 0 && options.getEndYear() > 0)
  {
    if (firstWhere)
      firstWhere = false;
    else
      whereBuffer << "AND ";

    whereBuffer << u8"albums.release BETWEEN "
                << options.getStartYear()
                << u8" AND "
                << options.getEndYear()
                << u8" ";
  }

  string whereRequest = whereBuffer.str();
  std::cout << whereBuffer.str() << std::endl;
  if (!whereRequest.empty()) buffer << u8"WHERE " << whereRequest;
  buffer << u8"ORDER BY random() ";
  buffer << u8"LIMIT " << size << u8";";

  string request = buffer.str();

  // Request compilation
  sqlite3_stmt *preparedRequest = nullptr;
  int status = sqlite3_prepare_v2(
    dbConnection,
    request.c_str(),
    request.size(),
    &preparedRequest,
    nullptr
  );

  if(status != SQLITE_OK)
  {
    std::cout << status << ": " <<sqlite3_errstr(status) << std::endl;
    assert(status == SQLITE_OK);
  }

  TrackPool pool(size);

  // Contruction de la pool de pistes
  while ((status = sqlite3_step(preparedRequest)) != SQLITE_DONE)
  {
    //status = sqlite3_step(preparedRequest);

    assert(status == SQLITE_ROW);

    Track track(
      sqlite3_column_int(preparedRequest, TRACK_ID),
      reinterpret_cast<char const *>(
        sqlite3_column_text(preparedRequest, TRACK_TITLE)
      ),
      SignalData(
        sqlite3_column_double(preparedRequest, SIGNAL_RHYTHM),
        sqlite3_column_double(preparedRequest, SIGNAL_ENERGY),
        sqlite3_column_double(preparedRequest, SIGNAL_TONALITY)
      ),
      ContextData(
        Artist(
          sqlite3_column_int(preparedRequest, CONTEXT_ARTIST_ID),
          reinterpret_cast<char const *>(
            sqlite3_column_text(preparedRequest, CONTEXT_ARTIST_NAME)
          ),
          //"",
          sqlite3_column_double(preparedRequest, CONTEXT_ARTIST_FAMILIARITY),
          sqlite3_column_double(preparedRequest, CONTEXT_ARTIST_POPULARITY)
        ),
        Album(
          sqlite3_column_int(preparedRequest, CONTEXT_ALBUM_ID),
          reinterpret_cast<char const *>(
            sqlite3_column_text(preparedRequest, CONTEXT_ALBUM_TITLE)
          ),
          reinterpret_cast<char const *>(
            sqlite3_column_text(preparedRequest, CONTEXT_ALBUM_ARTIST_NAME)
          ),
          sqlite3_column_int(preparedRequest, CONTEXT_ALBUM_RELEASE)
        ),
        sqlite3_column_double(preparedRequest, CONTEXT_POPULARITY)
      )
    );

    pool.insert(track);
  }

  sqlite3_finalize(preparedRequest);

  return pool;
}
<|endoftext|>


<commit_before>#include "stdafx.h"
#include "simulator.h"
#include "mesh.h"
#include "intergrator.h"
#include "mecMaterial.h"
#include "memory.h"

namespace ODER{
	Simulator::Simulator(const Reference<Mesh> m, const Reference<NodeIndexer>& nodeIndexer, Intergrator *intergrate)
		:mesh(m), indexer(nodeIndexer), intergrator(intergrate){
		rawDisplacements = allocAligned<double>(indexer->getMatrixOrder(mesh));
	};

	Simulator::Simulator(Simulator &&simulator){
		mesh = simulator.mesh;
		indexer = simulator.indexer;
		intergrator = simulator.intergrator;
		rawDisplacements = simulator.rawDisplacements;

		simulator.intergrator = NULL;
		simulator.rawDisplacements = NULL;
	}
	Simulator& Simulator::operator=(Simulator &&simulator){
		std::swap(mesh, simulator.mesh);
		std::swap(indexer, simulator.indexer);
		std::swap(intergrator, simulator.intergrator);
		std::swap(rawDisplacements, simulator.rawDisplacements);
		return *this;
	}

	void Simulator::getVertexPositions(Vector *vertices) const{
		const int* constrainIndices = NULL;
		int constrainSize = indexer->getConstrainIndices(&constrainIndices);
		intergrator->getRawDisplacements(rawDisplacements);

		int constrainIndex = 0;
		int displacementIndex = 0;
		int vertCount = mesh->getNodeCount();
		for (int vertIndex = 0; vertIndex < vertCount; vertIndex++){
			for (int axis = 0; axis < 3; axis++){
				if (constrainIndex < constrainSize && (3 * vertIndex + axis) == constrainIndices[constrainIndex]){
					vertices[vertIndex][axis] = mesh->getVertex(vertIndex)[axis];
					constrainIndex++;
				}
				else
					vertices[vertIndex][axis] = mesh->getVertex(vertIndex)[axis] + float(rawDisplacements[displacementIndex++]);
			}
		}
	}
	void Simulator::runTimeSteps(int stepCount){
		for (int step = 0; step < stepCount; step++)
			intergrator->runOneTimeStep();
	}
	Simulator::~Simulator(){
		if (intergrator)
		    delete intergrator;
		if (rawDisplacements)
		    freeAligned(rawDisplacements);
	}
}<commit_msg>simulator hotfix<commit_after>#include "stdafx.h"
#include "simulator.h"
#include "mesh.h"
#include "intergrator.h"
#include "mecMaterial.h"
#include "memory.h"

namespace ODER{
	Simulator::Simulator(const Reference<Mesh> m, const Reference<NodeIndexer>& nodeIndexer, Intergrator *intergrate)
		:mesh(m), indexer(nodeIndexer), intergrator(intergrate){
		rawDisplacements = allocAligned<double>(indexer->getMatrixOrder(mesh));
	};

	Simulator::Simulator(Simulator &&simulator){
		mesh = std::move(simulator.mesh);
		indexer = std::move(simulator.indexer);
		intergrator = simulator.intergrator;
		rawDisplacements = simulator.rawDisplacements;

		simulator.intergrator = NULL;
		simulator.rawDisplacements = NULL;
	}
	Simulator& Simulator::operator=(Simulator &&simulator){
		std::swap(mesh, simulator.mesh);
		std::swap(indexer, simulator.indexer);
		std::swap(intergrator, simulator.intergrator);
		std::swap(rawDisplacements, simulator.rawDisplacements);
		return *this;
	}

	void Simulator::getVertexPositions(Vector *vertices) const{
		const int* constrainIndices = NULL;
		int constrainSize = indexer->getConstrainIndices(&constrainIndices);
		intergrator->getRawDisplacements(rawDisplacements);

		int constrainIndex = 0;
		int displacementIndex = 0;
		int vertCount = mesh->getNodeCount();
		for (int vertIndex = 0; vertIndex < vertCount; vertIndex++){
			for (int axis = 0; axis < 3; axis++){
				if (constrainIndex < constrainSize && (3 * vertIndex + axis) == constrainIndices[constrainIndex]){
					vertices[vertIndex][axis] = mesh->getVertex(vertIndex)[axis];
					constrainIndex++;
				}
				else
					vertices[vertIndex][axis] = mesh->getVertex(vertIndex)[axis] + float(rawDisplacements[displacementIndex++]);
			}
		}
	}
	void Simulator::runTimeSteps(int stepCount){
		for (int step = 0; step < stepCount; step++)
			intergrator->runOneTimeStep();
	}
	Simulator::~Simulator(){
		if (intergrator)
		    delete intergrator;
		if (rawDisplacements)
		    freeAligned(rawDisplacements);
	}
}<|endoftext|>


<commit_before>/*
 * Copyright 2015 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "gm/gm.h"
#include "include/core/SkBlendMode.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkColor.h"
#include "include/core/SkColorFilter.h"
#include "include/core/SkFilterQuality.h"
#include "include/core/SkImage.h"
#include "include/core/SkImageFilter.h"
#include "include/core/SkImageInfo.h"
#include "include/core/SkMatrix.h"
#include "include/core/SkPaint.h"
#include "include/core/SkRRect.h"
#include "include/core/SkRect.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkScalar.h"
#include "include/core/SkSurface.h"
#include "include/core/SkTypes.h"
#include "include/effects/SkColorMatrix.h"
#include "include/effects/SkImageFilters.h"
#include "tools/Resources.h"
#include "tools/ToolUtils.h"

#include <utility>

/**
 *  Test drawing a primitive w/ an imagefilter (in this case, just matrix w/ identity) to see
 *  that we apply the xfermode *after* the image has been created and filtered, and not during
 *  the creation step (i.e. before it is filtered).
 *
 *  see https://bug.skia.org/3741
 */
static void do_draw(SkCanvas* canvas, SkBlendMode mode, sk_sp<SkImageFilter> imf) {
        SkAutoCanvasRestore acr(canvas, true);
        canvas->clipRect(SkRect::MakeWH(220, 220));

        // want to force a layer, so modes like DstIn can combine meaningfully, but the final
        // image can still be shown against our default (opaque) background. non-opaque GMs
        // are a lot more trouble to compare/triage.
        canvas->saveLayer(nullptr, nullptr);
        canvas->drawColor(SK_ColorGREEN);

        SkPaint paint;
        paint.setAntiAlias(true);

        SkRect r0 = SkRect::MakeXYWH(10, 60, 200, 100);
        SkRect r1 = SkRect::MakeXYWH(60, 10, 100, 200);

        paint.setColor(SK_ColorRED);
        canvas->drawOval(r0, paint);

        paint.setColor(0x660000FF);
        paint.setImageFilter(std::move(imf));
        paint.setBlendMode(mode);
        canvas->drawOval(r1, paint);
}

DEF_SIMPLE_GM(imagefilters_xfermodes, canvas, 480, 480) {
        canvas->translate(10, 10);

        // just need an imagefilter to trigger the code-path (which creates a tmp layer)
        sk_sp<SkImageFilter> imf(SkImageFilters::MatrixTransform(SkMatrix::I(),
                                                                 kNone_SkFilterQuality,
                                                                 nullptr));

        const SkBlendMode modes[] = {
            SkBlendMode::kSrcATop, SkBlendMode::kDstIn
        };

        for (size_t i = 0; i < SK_ARRAY_COUNT(modes); ++i) {
            canvas->save();
            do_draw(canvas, modes[i], nullptr);
            canvas->translate(240, 0);
            do_draw(canvas, modes[i], imf);
            canvas->restore();

            canvas->translate(0, 240);
        }
}

static sk_sp<SkImage> make_image(SkCanvas* canvas) {
    const SkImageInfo info = SkImageInfo::MakeS32(100, 100, kPremul_SkAlphaType);
    auto              surface(ToolUtils::makeSurface(canvas, info));
    surface->getCanvas()->drawRect(SkRect::MakeXYWH(25, 25, 50, 50), SkPaint());
    return surface->makeImageSnapshot();
}

// Compare blurs when we're tightly clipped (fast) and not as tightly (slower)
//
// Expect the two to draw the same (modulo the extra border of pixels when the clip is larger)
//
DEF_SIMPLE_GM(fast_slow_blurimagefilter, canvas, 620, 260) {
    sk_sp<SkImage> image(make_image(canvas));
    const SkRect r = SkRect::MakeIWH(image->width(), image->height());

    canvas->translate(10, 10);
    for (SkScalar sigma = 8; sigma <= 128; sigma *= 2) {
        SkPaint paint;
        paint.setImageFilter(SkImageFilters::Blur(sigma, sigma, nullptr));

        canvas->save();
        // we outset the clip by 1, to fall out of the fast-case in drawImage
        // i.e. the clip is larger than the image
        for (SkScalar outset = 0; outset <= 1; ++outset) {
            canvas->save();
            canvas->clipRect(r.makeOutset(outset, outset));
            canvas->drawImage(image, 0, 0, &paint);
            canvas->restore();
            canvas->translate(0, r.height() + 20);
        }
        canvas->restore();
        canvas->translate(r.width() + 20, 0);
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////

static void draw_set(SkCanvas* canvas, sk_sp<SkImageFilter> filters[], int count) {
    const SkRect r = SkRect::MakeXYWH(30, 30, 200, 200);
    const SkScalar offset = 250;
    SkScalar dx = 0, dy = 0;

    for (int i = 0; i < count; ++i) {
        canvas->save();
        SkRRect rr = SkRRect::MakeRectXY(r.makeOffset(dx, dy), 20, 20);
        canvas->clipRRect(rr, true);
        canvas->saveLayer({ &rr.getBounds(), nullptr, filters[i].get(), nullptr, nullptr, 0 });
        canvas->drawColor(0x40FFFFFF);
        canvas->restore();
        canvas->restore();

        if (0 == dx) {
            dx = offset;
        } else {
            dx = 0;
            dy = offset;
        }
    }
}

DEF_SIMPLE_GM(savelayer_with_backdrop, canvas, 830, 550) {
    SkColorMatrix cm;
    cm.setSaturation(10);
    sk_sp<SkColorFilter> cf(SkColorFilters::Matrix(cm));
    const SkScalar kernel[] = { 4, 0, 4, 0, -15, 0, 4, 0, 4 };
    sk_sp<SkImageFilter> filters[] = {
        SkImageFilters::Blur(10, 10, nullptr),
        SkImageFilters::Dilate(8, 8, nullptr),
        SkImageFilters::MatrixConvolution({ 3, 3 }, kernel, 1, 0, { 0, 0 },
                                          SkTileMode::kDecal, true, nullptr),
        SkImageFilters::ColorFilter(std::move(cf), nullptr),
    };

    const struct {
        SkScalar    fSx, fSy, fTx, fTy;
    } xforms[] = {
        { 1, 1, 0, 0 },
        { 0.5f, 0.5f, 530, 0 },
        { 0.25f, 0.25f, 530, 275 },
        { 0.125f, 0.125f, 530, 420 },
    };

    SkPaint paint;
    paint.setFilterQuality(kMedium_SkFilterQuality);
    sk_sp<SkImage> image(GetResourceAsImage("images/mandrill_512.png"));

    canvas->translate(20, 20);
    for (const auto& xform : xforms) {
        canvas->save();
        canvas->translate(xform.fTx, xform.fTy);
        canvas->scale(xform.fSx, xform.fSy);
        canvas->drawImage(image, 0, 0, &paint);
        draw_set(canvas, filters, SK_ARRAY_COUNT(filters));
        canvas->restore();
    }
}
<commit_msg>Roll external/skia 5f152f07455c..52a6a40535b0 (1 commits)<commit_after>/*
 * Copyright 2015 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "gm/gm.h"
#include "include/core/SkBlendMode.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkColor.h"
#include "include/core/SkColorFilter.h"
#include "include/core/SkFilterQuality.h"
#include "include/core/SkImage.h"
#include "include/core/SkImageFilter.h"
#include "include/core/SkImageInfo.h"
#include "include/core/SkMatrix.h"
#include "include/core/SkPaint.h"
#include "include/core/SkRRect.h"
#include "include/core/SkRect.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkScalar.h"
#include "include/core/SkSurface.h"
#include "include/core/SkTypes.h"
#include "include/effects/SkColorMatrix.h"
#include "include/effects/SkImageFilters.h"
#include "tools/Resources.h"
#include "tools/ToolUtils.h"

#include <utility>

/**
 *  Test drawing a primitive w/ an imagefilter (in this case, just matrix w/ identity) to see
 *  that we apply the xfermode *after* the image has been created and filtered, and not during
 *  the creation step (i.e. before it is filtered).
 *
 *  see https://bug.skia.org/3741
 */
static void do_draw(SkCanvas* canvas, SkBlendMode mode, sk_sp<SkImageFilter> imf) {
        SkAutoCanvasRestore acr(canvas, true);
        canvas->clipRect(SkRect::MakeWH(220, 220));

        // want to force a layer, so modes like DstIn can combine meaningfully, but the final
        // image can still be shown against our default (opaque) background. non-opaque GMs
        // are a lot more trouble to compare/triage.
        canvas->saveLayer(nullptr, nullptr);
        canvas->drawColor(SK_ColorGREEN);

        SkPaint paint;
        paint.setAntiAlias(true);

        SkRect r0 = SkRect::MakeXYWH(10, 60, 200, 100);
        SkRect r1 = SkRect::MakeXYWH(60, 10, 100, 200);

        paint.setColor(SK_ColorRED);
        canvas->drawOval(r0, paint);

        paint.setColor(0x660000FF);
        paint.setImageFilter(std::move(imf));
        paint.setBlendMode(mode);
        canvas->drawOval(r1, paint);
}

DEF_SIMPLE_GM(imagefilters_xfermodes, canvas, 480, 480) {
        canvas->translate(10, 10);

        // just need an imagefilter to trigger the code-path (which creates a tmp layer)
        sk_sp<SkImageFilter> imf(SkImageFilters::MatrixTransform(SkMatrix::I(),
                                                                 kNone_SkFilterQuality,
                                                                 nullptr));

        const SkBlendMode modes[] = {
            SkBlendMode::kSrcATop, SkBlendMode::kDstIn
        };

        for (size_t i = 0; i < SK_ARRAY_COUNT(modes); ++i) {
            canvas->save();
            do_draw(canvas, modes[i], nullptr);
            canvas->translate(240, 0);
            do_draw(canvas, modes[i], imf);
            canvas->restore();

            canvas->translate(0, 240);
        }
}

static sk_sp<SkImage> make_image(SkCanvas* canvas) {
    const SkImageInfo info = SkImageInfo::MakeS32(100, 100, kPremul_SkAlphaType);
    auto              surface(ToolUtils::makeSurface(canvas, info));
    surface->getCanvas()->drawRect(SkRect::MakeXYWH(25, 25, 50, 50), SkPaint());
    return surface->makeImageSnapshot();
}

// Compare blurs when we're tightly clipped (fast) and not as tightly (slower)
//
// Expect the two to draw the same (modulo the extra border of pixels when the clip is larger)
//
DEF_SIMPLE_GM(fast_slow_blurimagefilter, canvas, 620, 260) {
    sk_sp<SkImage> image(make_image(canvas));
    const SkRect r = SkRect::MakeIWH(image->width(), image->height());

    canvas->translate(10, 10);
    for (SkScalar sigma = 8; sigma <= 128; sigma *= 2) {
        SkPaint paint;
        paint.setImageFilter(SkImageFilters::Blur(sigma, sigma, nullptr));

        canvas->save();
        // we outset the clip by 1, to fall out of the fast-case in drawImage
        // i.e. the clip is larger than the image
        for (SkScalar outset = 0; outset <= 1; ++outset) {
            canvas->save();
            canvas->clipRect(r.makeOutset(outset, outset));
            canvas->drawImage(image, 0, 0, &paint);
            canvas->restore();
            canvas->translate(0, r.height() + 20);
        }
        canvas->restore();
        canvas->translate(r.width() + 20, 0);
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////

static void draw_set(SkCanvas* canvas, sk_sp<SkImageFilter> filters[], int count) {
    const SkRect r = SkRect::MakeXYWH(30, 30, 200, 200);
    const SkScalar offset = 250;
    SkScalar dx = 0, dy = 0;

    for (int i = 0; i < count; ++i) {
        canvas->save();
        SkRRect rr = SkRRect::MakeRectXY(r.makeOffset(dx, dy), 20, 20);
        canvas->clipRRect(rr, true);
        canvas->saveLayer({ &rr.getBounds(), nullptr, filters[i].get(), nullptr, nullptr, 0 });
        canvas->drawColor(0x40FFFFFF);
        canvas->restore();
        canvas->restore();

        if (0 == dx) {
            dx = offset;
        } else {
            dx = 0;
            dy = offset;
        }
    }
}

class SaveLayerWithBackdropGM : public skiagm::GM {
protected:
    bool runAsBench() const override { return true; }
    SkString onShortName() override { return SkString("savelayer_with_backdrop"); }
    SkISize onISize() override { return SkISize::Make(830, 550); }

    void onDraw(SkCanvas* canvas) override {
        SkColorMatrix cm;
        cm.setSaturation(10);
        sk_sp<SkColorFilter> cf(SkColorFilters::Matrix(cm));
        const SkScalar kernel[] = { 4, 0, 4, 0, -15, 0, 4, 0, 4 };
        sk_sp<SkImageFilter> filters[] = {
            SkImageFilters::Blur(10, 10, nullptr),
            SkImageFilters::Dilate(8, 8, nullptr),
            SkImageFilters::MatrixConvolution({ 3, 3 }, kernel, 1, 0, { 0, 0 },
                                              SkTileMode::kDecal, true, nullptr),
            SkImageFilters::ColorFilter(std::move(cf), nullptr),
        };

        const struct {
            SkScalar    fSx, fSy, fTx, fTy;
        } xforms[] = {
            { 1, 1, 0, 0 },
            { 0.5f, 0.5f, 530, 0 },
            { 0.25f, 0.25f, 530, 275 },
            { 0.125f, 0.125f, 530, 420 },
        };

        SkPaint paint;
        paint.setFilterQuality(kMedium_SkFilterQuality);
        sk_sp<SkImage> image(GetResourceAsImage("images/mandrill_512.png"));

        canvas->translate(20, 20);
        for (const auto& xform : xforms) {
            canvas->save();
            canvas->translate(xform.fTx, xform.fTy);
            canvas->scale(xform.fSx, xform.fSy);
            canvas->drawImage(image, 0, 0, &paint);
            draw_set(canvas, filters, SK_ARRAY_COUNT(filters));
            canvas->restore();
        }
    }
};

DEF_GM(return new SaveLayerWithBackdropGM();)
<|endoftext|>


<commit_before>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/vespalib/testkit/test_kit.h>
#include <vespa/eval/eval/operation.h>
#include <vespa/eval/eval/fast_value.h>
#include <vespa/eval/eval/tensor_function.h>
#include <vespa/eval/eval/test/eval_fixture.h>
#include <vespa/eval/eval/test/gen_spec.h>
#include <vespa/eval/instruction/dense_xw_product_function.h>

#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/util/stash.h>

using namespace vespalib;
using namespace vespalib::eval;
using namespace vespalib::eval::test;
using namespace vespalib::eval::tensor_function;

const ValueBuilderFactory &prod_factory = FastValueBuilderFactory::get();

struct First {
    bool value;
    explicit First(bool value_in) : value(value_in) {}
    operator bool() const { return value; }
};

GenSpec::seq_t my_vec_seq = [] (size_t i) noexcept { return (3.0 + i) * 7.0; };
GenSpec::seq_t my_mat_seq = [] (size_t i) noexcept { return (5.0 + i) * 43.0; };

void add_vector(EvalFixture::ParamRepo &repo, const char *d1, size_t s1) {
    auto name = make_string("%s%zu", d1, s1);
    auto layout = GenSpec().idx(d1, s1).seq(my_vec_seq);
    repo.add(name, layout);
    repo.add(name + "f", layout.cells_float());
}

void add_matrix(EvalFixture::ParamRepo &repo, const char *d1, size_t s1, const char *d2, size_t s2) {
    auto name = make_string("%s%zu%s%zu", d1, s1, d2, s2);
    auto layout = GenSpec().idx(d1, s1).idx(d2, s2).seq(my_mat_seq);
    repo.add(name, layout);
    repo.add(name + "f", layout.cells_float());
}

EvalFixture::ParamRepo make_params() {
    EvalFixture::ParamRepo repo;
    add_vector(repo, "y",  1);
    add_vector(repo, "y",  3);
    add_vector(repo, "y",  5);
    add_vector(repo, "y", 16);
    add_matrix(repo, "x",  1, "y",  1);
    add_matrix(repo, "y",  1, "z",  1);
    add_matrix(repo, "x",  2, "y",  3);
    add_matrix(repo, "y",  3, "z",  2);
    add_matrix(repo, "x",  2, "z",  3);
    add_matrix(repo, "x",  8, "y",  5);
    add_matrix(repo, "y",  5, "z",  8);
    add_matrix(repo, "x",  5, "y", 16);
    add_matrix(repo, "y", 16, "z",  5);
    return repo;
}
EvalFixture::ParamRepo param_repo = make_params();

void verify_optimized(const vespalib::string &expr, size_t vec_size, size_t res_size, bool happy) {
    EvalFixture slow_fixture(prod_factory, expr, param_repo, false);
    EvalFixture fixture(prod_factory, expr, param_repo, true);
    EXPECT_EQUAL(fixture.result(), EvalFixture::ref(expr, param_repo));
    EXPECT_EQUAL(fixture.result(), slow_fixture.result());
    auto info = fixture.find_all<DenseXWProductFunction>();
    ASSERT_EQUAL(info.size(), 1u);
    EXPECT_TRUE(info[0]->result_is_mutable());
    EXPECT_EQUAL(info[0]->vector_size(), vec_size);
    EXPECT_EQUAL(info[0]->result_size(), res_size);
    EXPECT_EQUAL(info[0]->common_inner(), happy);
}

vespalib::string make_expr(const vespalib::string &a, const vespalib::string &b, const vespalib::string &common,
                           bool float_a, bool float_b)
{
    return make_string("reduce(%s%s*%s%s,sum,%s)", a.c_str(), float_a ? "f" : "", b.c_str(), float_b ? "f" : "", common.c_str());
}

void verify_optimized_multi(const vespalib::string &a, const vespalib::string &b, const vespalib::string &common,
                            size_t vec_size, size_t res_size, bool happy, First first = First(true))
{
    for (bool float_a: {false, true}) {
        for (bool float_b: {false, true}) {
            auto expr = make_expr(a, b, common, float_a, float_b);
            TEST_STATE(expr.c_str());
            TEST_DO(verify_optimized(expr, vec_size, res_size, happy));
        }
    }
    if (first) {
        TEST_DO(verify_optimized_multi(b, a, common, vec_size, res_size, happy, First(false)));
    }
}

void verify_not_optimized(const vespalib::string &expr) {
    EvalFixture slow_fixture(prod_factory, expr, param_repo, false);
    EvalFixture fixture(prod_factory, expr, param_repo, true);
    EXPECT_EQUAL(fixture.result(), EvalFixture::ref(expr, param_repo));
    EXPECT_EQUAL(fixture.result(), slow_fixture.result());
    auto info = fixture.find_all<DenseXWProductFunction>();
    EXPECT_TRUE(info.empty());
}

TEST("require that xw product gives same results as reference join/reduce") {
    // 1 -> 1 happy/unhappy
    TEST_DO(verify_optimized_multi("y1", "x1y1", "y", 1, 1, true));
    TEST_DO(verify_optimized_multi("y1", "y1z1", "y", 1, 1, false));
    // 3 -> 2 happy/unhappy
    TEST_DO(verify_optimized_multi("y3", "x2y3", "y", 3, 2, true));
    TEST_DO(verify_optimized_multi("y3", "y3z2", "y", 3, 2, false));
    // 5 -> 8 happy/unhappy
    TEST_DO(verify_optimized_multi("y5", "x8y5", "y", 5, 8, true));
    TEST_DO(verify_optimized_multi("y5", "y5z8", "y", 5, 8, false));
    // 16 -> 5 happy/unhappy
    TEST_DO(verify_optimized_multi("y16", "x5y16", "y", 16, 5, true));
    TEST_DO(verify_optimized_multi("y16", "y16z5", "y", 16, 5, false));
}

TEST("require that various variants of xw product can be optimized") {
    TEST_DO(verify_optimized("reduce(join(y3,x2y3,f(x,y)(x*y)),sum,y)", 3, 2, true));
}

TEST("require that expressions similar to xw product are not optimized") {
    TEST_DO(verify_not_optimized("reduce(y3*x2y3,sum,x)"));
    TEST_DO(verify_not_optimized("reduce(y3*x2y3,prod,y)"));
    TEST_DO(verify_not_optimized("reduce(y3*x2y3,sum)"));
    TEST_DO(verify_not_optimized("reduce(join(y3,x2y3,f(x,y)(y*x)),sum,y)"));
    TEST_DO(verify_not_optimized("reduce(join(y3,x2y3,f(x,y)(x+y)),sum,y)"));
    TEST_DO(verify_not_optimized("reduce(join(y3,x2y3,f(x,y)(x*x)),sum,y)"));
    TEST_DO(verify_not_optimized("reduce(join(y3,x2y3,f(x,y)(y*y)),sum,y)"));
    TEST_DO(verify_not_optimized("reduce(join(y3,x2y3,f(x,y)(y*x*1)),sum,y)"));
    TEST_DO(verify_not_optimized("reduce(y3*x2z3,sum,y)"));
    TEST_DO(verify_not_optimized("reduce(y3*x2z3,sum,z)"));
}

TEST("require that xw product can be debug dumped") {
    EvalFixture fixture(prod_factory, "reduce(y5*x8y5,sum,y)", param_repo, true);
    auto info = fixture.find_all<DenseXWProductFunction>();
    ASSERT_EQUAL(info.size(), 1u);
    EXPECT_TRUE(info[0]->result_is_mutable());
    fprintf(stderr, "%s\n", info[0]->as_string().c_str());
}

TEST_MAIN() { TEST_RUN_ALL(); }
<commit_msg>test with all cell types<commit_after>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/vespalib/testkit/test_kit.h>
#include <vespa/eval/eval/operation.h>
#include <vespa/eval/eval/fast_value.h>
#include <vespa/eval/eval/tensor_function.h>
#include <vespa/eval/eval/test/eval_fixture.h>
#include <vespa/eval/eval/test/gen_spec.h>
#include <vespa/eval/instruction/dense_xw_product_function.h>

#include <vespa/vespalib/util/stringfmt.h>
#include <vespa/vespalib/util/stash.h>

using namespace vespalib;
using namespace vespalib::eval;
using namespace vespalib::eval::test;
using namespace vespalib::eval::tensor_function;

const ValueBuilderFactory &prod_factory = FastValueBuilderFactory::get();

GenSpec::seq_t lhs_seq = [] (size_t i) noexcept { return (3.0 + i) * 7.0; };
GenSpec::seq_t rhs_seq = [] (size_t i) noexcept { return (5.0 + i) * 43.0; };

struct FunInfo {
    using LookFor = DenseXWProductFunction;
    size_t vec_size;
    size_t res_size;
    bool happy;
    bool check(const LookFor &fun) const {
        return ((fun.result_is_mutable()) &&
                (fun.vector_size() == vec_size) &&
                (fun.result_size() == res_size) &&
                (fun.common_inner() == happy));
    }
};

void verify(const vespalib::string &expr, const std::vector<FunInfo> &fun_info, const std::vector<CellType> &with_cell_types) {
    auto fun = Function::parse(expr);
    ASSERT_EQUAL(fun->num_params(), 2u);
    vespalib::string lhs_name = fun->param_name(0);
    vespalib::string rhs_name = fun->param_name(1);
    const auto lhs_spec = GenSpec::from_desc(lhs_name);
    const auto rhs_spec = GenSpec::from_desc(rhs_name);
    for (CellType lhs_ct: with_cell_types) {
        for (CellType rhs_ct: with_cell_types) {
            EvalFixture::ParamRepo param_repo;
            param_repo.add(lhs_name, lhs_spec.cpy().cells(lhs_ct).seq(lhs_seq));
            param_repo.add(rhs_name, rhs_spec.cpy().cells(rhs_ct).seq(rhs_seq));
            EvalFixture slow_fixture(prod_factory, expr, param_repo, false);
            EvalFixture fixture(prod_factory, expr, param_repo, true);
            EXPECT_EQUAL(fixture.result(), EvalFixture::ref(expr, param_repo));
            EXPECT_EQUAL(fixture.result(), slow_fixture.result());
            auto info = fixture.find_all<FunInfo::LookFor>();
            ASSERT_EQUAL(info.size(), fun_info.size());
            for (size_t i = 0; i < fun_info.size(); ++i) {
                EXPECT_TRUE(fun_info[i].check(*info[i]));
            }
        }
    }
}

void verify_not_optimized(const vespalib::string &expr) {
    return verify(expr, {}, {CellType::FLOAT});
}

void verify_optimized(const vespalib::string &expr, size_t vec_size, size_t res_size, bool happy) {
    return verify(expr, {{vec_size, res_size, happy}}, CellTypeUtils::list_types());
}

vespalib::string make_expr(const vespalib::string &a, const vespalib::string &b, const vespalib::string &common) {
    return make_string("reduce(%s*%s,sum,%s)", a.c_str(), b.c_str(), common.c_str());
}

void verify_optimized_multi(const vespalib::string &a, const vespalib::string &b, const vespalib::string &common,
                            size_t vec_size, size_t res_size, bool happy)
{
    {
        auto expr = make_expr(a, b, common);
        TEST_STATE(expr.c_str());
        TEST_DO(verify_optimized(expr, vec_size, res_size, happy));
    }
    {
        auto expr = make_expr(b, a, common);
        TEST_STATE(expr.c_str());
        TEST_DO(verify_optimized(expr, vec_size, res_size, happy));
    }
}

TEST("require that xw product gives same results as reference join/reduce") {
    // 1 -> 1 happy/unhappy
    TEST_DO(verify_optimized_multi("y1", "x1y1", "y", 1, 1, true));
    TEST_DO(verify_optimized_multi("y1", "y1z1", "y", 1, 1, false));
    // 3 -> 2 happy/unhappy
    TEST_DO(verify_optimized_multi("y3", "x2y3", "y", 3, 2, true));
    TEST_DO(verify_optimized_multi("y3", "y3z2", "y", 3, 2, false));
    // 5 -> 8 happy/unhappy
    TEST_DO(verify_optimized_multi("y5", "x8y5", "y", 5, 8, true));
    TEST_DO(verify_optimized_multi("y5", "y5z8", "y", 5, 8, false));
    // 16 -> 5 happy/unhappy
    TEST_DO(verify_optimized_multi("y16", "x5y16", "y", 16, 5, true));
    TEST_DO(verify_optimized_multi("y16", "y16z5", "y", 16, 5, false));
}

TEST("require that various variants of xw product can be optimized") {
    TEST_DO(verify_optimized("reduce(join(y3,x2y3,f(x,y)(x*y)),sum,y)", 3, 2, true));
}

TEST("require that expressions similar to xw product are not optimized") {
    TEST_DO(verify_not_optimized("reduce(y3*x2y3,sum,x)"));
    TEST_DO(verify_not_optimized("reduce(y3*x2y3,prod,y)"));
    TEST_DO(verify_not_optimized("reduce(y3*x2y3,sum)"));
    TEST_DO(verify_not_optimized("reduce(join(y3,x2y3,f(x,y)(y*x)),sum,y)"));
    TEST_DO(verify_not_optimized("reduce(join(y3,x2y3,f(x,y)(x+y)),sum,y)"));
    TEST_DO(verify_not_optimized("reduce(join(y3,x2y3,f(x,y)(x*x)),sum,y)"));
    TEST_DO(verify_not_optimized("reduce(join(y3,x2y3,f(x,y)(y*y)),sum,y)"));
    TEST_DO(verify_not_optimized("reduce(join(y3,x2y3,f(x,y)(y*x*1)),sum,y)"));
    TEST_DO(verify_not_optimized("reduce(y3*x2z3,sum,y)"));
    TEST_DO(verify_not_optimized("reduce(y3*x2z3,sum,z)"));
}

TEST("require that xw product can be debug dumped") {
    EvalFixture::ParamRepo param_repo;
    param_repo.add("y5", GenSpec::from_desc("y5"));
    param_repo.add("x8y5", GenSpec::from_desc("x8y5"));
    EvalFixture fixture(prod_factory, "reduce(y5*x8y5,sum,y)", param_repo, true);
    auto info = fixture.find_all<DenseXWProductFunction>();
    ASSERT_EQUAL(info.size(), 1u);
    EXPECT_TRUE(info[0]->result_is_mutable());
    fprintf(stderr, "%s\n", info[0]->as_string().c_str());
}

TEST_MAIN() { TEST_RUN_ALL(); }
<|endoftext|>


<commit_before>#include "DactQueryModel.hh"
#include <AlpinoCorpus/CorpusReader.hh>
#include <QtDebug>

DactQueryModel::DactQueryModel(CorpusPtr corpus, QObject *parent)
:
d_corpus(corpus),
QAbstractListModel(parent)
{
    QObject::connect(&d_entryMap, SIGNAL(entryFound(QString)),
        this, SLOT(mapperEntryFound(QString)));

    QObject::connect(&d_mapper, SIGNAL(started(int)),
        this, SLOT(mapperStarted(int)));

    QObject::connect(&d_mapper, SIGNAL(progress(int, int)),
        this, SLOT(mapperProgressed(int, int)));

    QObject::connect(&d_mapper, SIGNAL(stopped(int, int)),
        this, SLOT(mapperStopped(int, int)));
}

DactQueryModel::~DactQueryModel()
{
	cancelQuery();
}

int DactQueryModel::rowCount(QModelIndex const &index) const
{
    return d_results.size();
}

QVariant DactQueryModel::data(QModelIndex const &index, int role) const
{
    return (index.isValid()
            && index.column() == 0
            && (index.row() < d_results.size() && index.row() >= 0)
            && (role == Qt::DisplayRole || role == Qt::UserRole))
        ? d_results.at(index.row())
        : QVariant();
}

QVariant DactQueryModel::headerData(int column, Qt::Orientation orientation, int role) const
{
    return (column == 0
            && orientation == Qt::Horizontal
            && role == Qt::DisplayRole)
        ? tr("File")
        : QVariant();
}

void DactQueryModel::mapperEntryFound(QString entry)
{
    int row = d_results.size();
    d_results.append(entry);

    emit dataChanged(index(row, 0), index(row + 1, 0));
}

void DactQueryModel::mapperStarted(int totalEntries)
{
    emit queryStarted(totalEntries);
}

void DactQueryModel::mapperProgressed(int n, int totalEntries)
{
    emit queryProgressed(n, totalEntries);
}

void DactQueryModel::mapperStopped(int n, int totalEntries)
{
    emit queryStopped(n, totalEntries);
}

void DactQueryModel::runQuery(QString const &query)
{
    cancelQuery(); // just in case

    int size = d_results.size();
    d_results.clear();

    emit dataChanged(index(0, 0), index(size, 0));

    if (!query.isEmpty())
        getEntriesWithQuery(query);
    else
        getEntries();
}

void DactQueryModel::cancelQuery()
{
    if (d_mapper.isRunning())
    {
        d_mapper.cancel();
        d_mapper.wait();
    }
}

void DactQueryModel::getEntries()
{
    // @TODO make this async
    for (alpinocorpus::CorpusReader::EntryIterator itr(d_corpus->begin()), end(d_corpus->end());
         itr != end; itr++)
        mapperEntryFound(*itr);
}

void DactQueryModel::getEntriesWithQuery(QString const &query)
{
    d_mapper.start(d_corpus.data(), query, &d_entryMap);
}<commit_msg>Queries to dbxml are now run through dbxml. This is fast.<commit_after>#include <QDebug>
#include <QtConcurrentRun>

#include <algorithm>

#include <alpinocorpus/Error.hh>
#include "DactQueryModel.hh"

DactQueryModel::DactQueryModel(CorpusPtr corpus, QObject *parent)
:
d_corpus(corpus),
QAbstractListModel(parent)
{
    QObject::connect(&d_entryMap, SIGNAL(entryFound(QString)),
        this, SLOT(mapperEntryFound(QString)));

    QObject::connect(&d_mapper, SIGNAL(started(int)),
        this, SLOT(mapperStarted(int)));

    QObject::connect(&d_mapper, SIGNAL(progress(int, int)),
        this, SLOT(mapperProgressed(int, int)));

    QObject::connect(&d_mapper, SIGNAL(stopped(int, int)),
        this, SLOT(mapperStopped(int, int)));
}

DactQueryModel::~DactQueryModel()
{
	cancelQuery();
}

int DactQueryModel::rowCount(QModelIndex const &index) const
{
    return d_results.size();
}

QVariant DactQueryModel::data(QModelIndex const &index, int role) const
{
    return (index.isValid()
            && index.column() == 0
            && (index.row() < d_results.size() && index.row() >= 0)
            && (role == Qt::DisplayRole || role == Qt::UserRole))
        ? d_results.at(index.row())
        : QVariant();
}

QVariant DactQueryModel::headerData(int column, Qt::Orientation orientation, int role) const
{
    return (column == 0
            && orientation == Qt::Horizontal
            && role == Qt::DisplayRole)
        ? tr("File")
        : QVariant();
}

void DactQueryModel::mapperEntryFound(QString entry)
{
    if (d_results.contains(entry))
        return;

    int row = d_results.size();
    d_results.append(entry);

    emit dataChanged(index(row, 0), index(row + 1, 0));
}

void DactQueryModel::mapperStarted(int totalEntries)
{
    emit queryStarted(totalEntries);
}

void DactQueryModel::mapperProgressed(int n, int totalEntries)
{
    emit queryProgressed(n, totalEntries);
}

void DactQueryModel::mapperStopped(int n, int totalEntries)
{
    emit queryStopped(n, totalEntries);
}

void DactQueryModel::runQuery(QString const &query)
{
    cancelQuery(); // just in case

    int size = d_results.size();
    d_results.clear();

    emit dataChanged(index(0, 0), index(size, 0));

    if (!query.isEmpty())
        QtConcurrent::run(this, &DactQueryModel::getEntriesWithQuery, query);
    else
        QtConcurrent::run(this, &DactQueryModel::getEntries);
}

void DactQueryModel::cancelQuery()
{
    // @TODO this does not cancel the other methods
    if (d_mapper.isRunning())
    {
        d_mapper.cancel();
        d_mapper.wait();
    }
}

// run async
void DactQueryModel::getEntries()
{
    // @TODO make this stoppable
    for (alpinocorpus::CorpusReader::EntryIterator itr(d_corpus->begin()), end(d_corpus->end());
         itr != end; itr++)
        mapperEntryFound(*itr);
}

// run async
void DactQueryModel::getEntriesWithQuery(QString const &query)
{
	try {
        mapperStarted(0); // we don't know how many entries will be found

		std::for_each(
		    d_corpus->query(alpinocorpus::CorpusReader::XPATH, query),
			d_corpus->end(),
			std::bind1st(std::mem_fun(&DactQueryModel::mapperEntryFound), this)
		);

        mapperStopped(d_results.size(), d_results.size());
	} catch (alpinocorpus::NotImplemented const &e) {
		d_mapper.start(d_corpus.data(), query, &d_entryMap);
	} catch (alpinocorpus::Error const &e) {
		qDebug() << "Error performing query: " << e.what();
	}
}<|endoftext|>


<commit_before>// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2005, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// ---
// Author: Sanjay Ghemawat
//         Chris Demetriou (refactoring)
//
// Profile current program by sampling stack-trace every so often

#include "config.h"
#include "getpc.h"      // should be first to get the _GNU_SOURCE dfn
#include <signal.h>
#include <assert.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>  // for getpid()
#endif
#if defined(HAVE_SYS_UCONTEXT_H)
#include <sys/ucontext.h>
#elif defined(HAVE_UCONTEXT_H)
#include <ucontext.h>
#elif defined(HAVE_CYGWIN_SIGNAL_H)
#include <cygwin/signal.h>
typedef ucontext ucontext_t;
#else
typedef int ucontext_t;   // just to quiet the compiler, mostly
#endif
#include <sys/time.h>
#include <string>
#include <gperftools/profiler.h>
#include <gperftools/stacktrace.h>
#include "base/commandlineflags.h"
#include "base/logging.h"
#include "base/googleinit.h"
#include "base/spinlock.h"
#include "base/sysinfo.h"             /* for GetUniquePathFromEnv, etc */
#include "profiledata.h"
#include "profile-handler.h"
#ifdef HAVE_CONFLICT_SIGNAL_H
#include "conflict-signal.h"          /* used on msvc machines */
#endif

using std::string;

DEFINE_bool(cpu_profiler_unittest,
            EnvToBool("PERFTOOLS_UNITTEST", true),
            "Determines whether or not we are running under the \
             control of a unit test. This allows us to include or \
			 exclude certain behaviours.");

// Collects up all profile data. This is a singleton, which is
// initialized by a constructor at startup. If no cpu profiler
// signal is specified then the profiler lifecycle is either
// manaully controlled via the API or attached to the scope of
// the singleton (program scope). Otherwise the cpu toggle is
// used to allow for user selectable control via signal generation.
// This is very useful for profiling a daemon process without
// having to start and stop the daemon or having to modify the
// source code to use the cpu profiler API.
class CpuProfiler {
 public:
  CpuProfiler();
  ~CpuProfiler();

  // Start profiler to write profile info into fname
  bool Start(const char* fname, const ProfilerOptions* options);

  // Stop profiling and write the data to disk.
  void Stop();

  // Write the data to disk (and continue profiling).
  void FlushTable();

  bool Enabled();

  void GetCurrentState(ProfilerState* state);

  static CpuProfiler instance_;

 private:
  // This lock implements the locking requirements described in the ProfileData
  // documentation, specifically:
  //
  // lock_ is held all over all collector_ method calls except for the 'Add'
  // call made from the signal handler, to protect against concurrent use of
  // collector_'s control routines. Code other than signal handler must
  // unregister the signal handler before calling any collector_ method.
  // 'Add' method in the collector is protected by a guarantee from
  // ProfileHandle that only one instance of prof_handler can run at a time.
  SpinLock      lock_;
  ProfileData   collector_;

  // Filter function and its argument, if any.  (NULL means include all
  // samples).  Set at start, read-only while running.  Written while holding
  // lock_, read and executed in the context of SIGPROF interrupt.
  int           (*filter_)(void*);
  void*         filter_arg_;

  // Opaque token returned by the profile handler. To be used when calling
  // ProfileHandlerUnregisterCallback.
  ProfileHandlerToken* prof_handler_token_;

  // Sets up a callback to receive SIGPROF interrupt.
  void EnableHandler();

  // Disables receiving SIGPROF interrupt.
  void DisableHandler();

  // Signal handler that records the interrupted pc in the profile data.
  static void prof_handler(int sig, siginfo_t*, void* signal_ucontext,
                           void* cpu_profiler);
};

// Signal handler that is registered when a user selectable signal
// number is defined in the environment variable CPUPROFILESIGNAL.
static void CpuProfilerSwitch(int signal_number)
{
    bool static started = false;
	static unsigned profile_count = 0;
    static char base_profile_name[1024] = "\0";

	if (base_profile_name[0] == '\0') {
    	if (!GetUniquePathFromEnv("CPUPROFILE", base_profile_name)) {
        	RAW_LOG(FATAL,"Cpu profiler switch is registered but no CPUPROFILE is defined");
        	return;
    	}
	}
    if (!started)
    {
    	char full_profile_name[1024];

		snprintf(full_profile_name, sizeof(full_profile_name), "%s.%u",
                 base_profile_name, profile_count++);

        if(!ProfilerStart(full_profile_name))
        {
            RAW_LOG(FATAL, "Can't turn on cpu profiling for '%s': %s\n",
                    full_profile_name, strerror(errno));
        }
    }
    else
    {
        ProfilerStop();
    }
    started = !started;
}

// Profile data structure singleton: Constructor will check to see if
// profiling should be enabled.  Destructor will write profile data
// out to disk.
CpuProfiler CpuProfiler::instance_;

// Initialize profiling: activated if getenv("CPUPROFILE") exists.
CpuProfiler::CpuProfiler()
    : prof_handler_token_(NULL) {
  // TODO(cgd) Move this code *out* of the CpuProfile constructor into a
  // separate object responsible for initialization. With ProfileHandler there
  // is no need to limit the number of profilers.
  if (getenv("CPUPROFILE") == NULL) {
    if (!FLAGS_cpu_profiler_unittest) {
      RAW_LOG(WARNING, "CPU profiler linked but no valid CPUPROFILE environment variable found\n");
    }
    return;
  }

  // We don't enable profiling if setuid -- it's a security risk
#ifdef HAVE_GETEUID
  if (getuid() != geteuid()) {
    if (!FLAGS_cpu_profiler_unittest) {
      RAW_LOG(WARNING, "Cannot perform CPU profiling when running with setuid\n");
    }
    return;
  }
#endif

  char *signal_number_str = getenv("CPUPROFILESIGNAL");
  if (signal_number_str != NULL) {
    long int signal_number = strtol(signal_number_str, NULL, 10);
    if (signal_number >= 1 && signal_number <= 64) {
      intptr_t old_signal_handler = reinterpret_cast<intptr_t>(signal(signal_number, CpuProfilerSwitch));
      if (old_signal_handler == 0) {
        RAW_LOG(INFO,"Using signal %d as cpu profiling switch", signal_number);
      } else {
        RAW_LOG(FATAL, "Signal %d already in use\n", signal_number);
      }
    } else {
      RAW_LOG(FATAL, "Signal number %s is invalid\n", signal_number_str);
    }
  } else {
    char fname[PATH_MAX];
    if (!GetUniquePathFromEnv("CPUPROFILE", fname)) {
      if (!FLAGS_cpu_profiler_unittest) {
        RAW_LOG(WARNING, "CPU profiler linked but no valid CPUPROFILE environment variable found\n");
      }
      return;
	}

    if (!Start(fname, NULL)) {
      RAW_LOG(FATAL, "Can't turn on cpu profiling for '%s': %s\n",
              fname, strerror(errno));
    }
  }
}

bool CpuProfiler::Start(const char* fname, const ProfilerOptions* options) {
  SpinLockHolder cl(&lock_);

  if (collector_.enabled()) {
    return false;
  }

  ProfileHandlerState prof_handler_state;
  ProfileHandlerGetState(&prof_handler_state);

  ProfileData::Options collector_options;
  collector_options.set_frequency(prof_handler_state.frequency);
  if (!collector_.Start(fname, collector_options)) {
    return false;
  }

  filter_ = NULL;
  if (options != NULL && options->filter_in_thread != NULL) {
    filter_ = options->filter_in_thread;
    filter_arg_ = options->filter_in_thread_arg;
  }

  // Setup handler for SIGPROF interrupts
  EnableHandler();

  return true;
}

CpuProfiler::~CpuProfiler() {
  Stop();
}

// Stop profiling and write out any collected profile data
void CpuProfiler::Stop() {
  SpinLockHolder cl(&lock_);

  if (!collector_.enabled()) {
    return;
  }

  // Unregister prof_handler to stop receiving SIGPROF interrupts before
  // stopping the collector.
  DisableHandler();

  // DisableHandler waits for the currently running callback to complete and
  // guarantees no future invocations. It is safe to stop the collector.
  collector_.Stop();
}

void CpuProfiler::FlushTable() {
  SpinLockHolder cl(&lock_);

  if (!collector_.enabled()) {
    return;
  }

  // Unregister prof_handler to stop receiving SIGPROF interrupts before
  // flushing the profile data.
  DisableHandler();

  // DisableHandler waits for the currently running callback to complete and
  // guarantees no future invocations. It is safe to flush the profile data.
  collector_.FlushTable();

  EnableHandler();
}

bool CpuProfiler::Enabled() {
  SpinLockHolder cl(&lock_);
  return collector_.enabled();
}

void CpuProfiler::GetCurrentState(ProfilerState* state) {
  ProfileData::State collector_state;
  {
    SpinLockHolder cl(&lock_);
    collector_.GetCurrentState(&collector_state);
  }

  state->enabled = collector_state.enabled;
  state->start_time = static_cast<time_t>(collector_state.start_time);
  state->samples_gathered = collector_state.samples_gathered;
  int buf_size = sizeof(state->profile_name);
  strncpy(state->profile_name, collector_state.profile_name, buf_size);
  state->profile_name[buf_size-1] = '\0';
}

void CpuProfiler::EnableHandler() {
  RAW_CHECK(prof_handler_token_ == NULL, "SIGPROF handler already registered");
  prof_handler_token_ = ProfileHandlerRegisterCallback(prof_handler, this);
  RAW_CHECK(prof_handler_token_ != NULL, "Failed to set up SIGPROF handler");
}

void CpuProfiler::DisableHandler() {
  RAW_CHECK(prof_handler_token_ != NULL, "SIGPROF handler is not registered");
  ProfileHandlerUnregisterCallback(prof_handler_token_);
  prof_handler_token_ = NULL;
}

// Signal handler that records the pc in the profile-data structure. We do no
// synchronization here.  profile-handler.cc guarantees that at most one
// instance of prof_handler() will run at a time. All other routines that
// access the data touched by prof_handler() disable this signal handler before
// accessing the data and therefore cannot execute concurrently with
// prof_handler().
void CpuProfiler::prof_handler(int sig, siginfo_t*, void* signal_ucontext,
                               void* cpu_profiler) {
  CpuProfiler* instance = static_cast<CpuProfiler*>(cpu_profiler);

  if (instance->filter_ == NULL ||
      (*instance->filter_)(instance->filter_arg_)) {
    void* stack[ProfileData::kMaxStackDepth];

    // Under frame-pointer-based unwinding at least on x86, the
    // top-most active routine doesn't show up as a normal frame, but
    // as the "pc" value in the signal handler context.
    stack[0] = GetPC(*reinterpret_cast<ucontext_t*>(signal_ucontext));

    // We skip the top three stack trace entries (this function,
    // SignalHandler::SignalHandler and one signal handler frame)
    // since they are artifacts of profiling and should not be
    // measured.  Other profiling related frames may be removed by
    // "pprof" at analysis time.  Instead of skipping the top frames,
    // we could skip nothing, but that would increase the profile size
    // unnecessarily.
    int depth = GetStackTraceWithContext(stack + 1, arraysize(stack) - 1,
                                         3, signal_ucontext);

    void **used_stack;
    if (depth > 0 && stack[1] == stack[0]) {
      // in case of non-frame-pointer-based unwinding we will get
      // duplicate of PC in stack[1], which we don't want
      used_stack = stack + 1;
    } else {
      used_stack = stack;
      depth++;  // To account for pc value in stack[0];
    }

    instance->collector_.Add(depth, used_stack);
  }
}

#if !(defined(__CYGWIN__) || defined(__CYGWIN32__))

extern "C" PERFTOOLS_DLL_DECL void ProfilerRegisterThread() {
  ProfileHandlerRegisterThread();
}

extern "C" PERFTOOLS_DLL_DECL void ProfilerFlush() {
  CpuProfiler::instance_.FlushTable();
}

extern "C" PERFTOOLS_DLL_DECL int ProfilingIsEnabledForAllThreads() {
  return CpuProfiler::instance_.Enabled();
}

extern "C" PERFTOOLS_DLL_DECL int ProfilerStart(const char* fname) {
  return CpuProfiler::instance_.Start(fname, NULL);
}

extern "C" PERFTOOLS_DLL_DECL int ProfilerStartWithOptions(
    const char *fname, const ProfilerOptions *options) {
  return CpuProfiler::instance_.Start(fname, options);
}

extern "C" PERFTOOLS_DLL_DECL void ProfilerStop() {
  CpuProfiler::instance_.Stop();
}

extern "C" PERFTOOLS_DLL_DECL void ProfilerGetCurrentState(
    ProfilerState* state) {
  CpuProfiler::instance_.GetCurrentState(state);
}

#else  // OS_CYGWIN

// ITIMER_PROF doesn't work under cygwin.  ITIMER_REAL is available, but doesn't
// work as well for profiling, and also interferes with alarm().  Because of
// these issues, unless a specific need is identified, profiler support is
// disabled under Cygwin.
extern "C" void ProfilerRegisterThread() { }
extern "C" void ProfilerFlush() { }
extern "C" int ProfilingIsEnabledForAllThreads() { return 0; }
extern "C" int ProfilerStart(const char* fname) { return 0; }
extern "C" int ProfilerStartWithOptions(const char *fname,
                                        const ProfilerOptions *options) {
  return 0;
}
extern "C" void ProfilerStop() { }
extern "C" void ProfilerGetCurrentState(ProfilerState* state) {
  memset(state, 0, sizeof(*state));
}

#endif  // OS_CYGWIN

// DEPRECATED routines
extern "C" PERFTOOLS_DLL_DECL void ProfilerEnable() { }
extern "C" PERFTOOLS_DLL_DECL void ProfilerDisable() { }
<commit_msg>Format and fix out of bound access in CpuProfilerSwitch<commit_after>// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2005, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// ---
// Author: Sanjay Ghemawat
//         Chris Demetriou (refactoring)
//
// Profile current program by sampling stack-trace every so often

#include "config.h"
#include "getpc.h"      // should be first to get the _GNU_SOURCE dfn
#include <signal.h>
#include <assert.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>  // for getpid()
#endif
#if defined(HAVE_SYS_UCONTEXT_H)
#include <sys/ucontext.h>
#elif defined(HAVE_UCONTEXT_H)
#include <ucontext.h>
#elif defined(HAVE_CYGWIN_SIGNAL_H)
#include <cygwin/signal.h>
typedef ucontext ucontext_t;
#else
typedef int ucontext_t;   // just to quiet the compiler, mostly
#endif
#include <sys/time.h>
#include <string>
#include <gperftools/profiler.h>
#include <gperftools/stacktrace.h>
#include "base/commandlineflags.h"
#include "base/logging.h"
#include "base/googleinit.h"
#include "base/spinlock.h"
#include "base/sysinfo.h"             /* for GetUniquePathFromEnv, etc */
#include "profiledata.h"
#include "profile-handler.h"
#ifdef HAVE_CONFLICT_SIGNAL_H
#include "conflict-signal.h"          /* used on msvc machines */
#endif

using std::string;

DEFINE_bool(cpu_profiler_unittest,
            EnvToBool("PERFTOOLS_UNITTEST", true),
            "Determines whether or not we are running under the \
             control of a unit test. This allows us to include or \
			 exclude certain behaviours.");

// Collects up all profile data. This is a singleton, which is
// initialized by a constructor at startup. If no cpu profiler
// signal is specified then the profiler lifecycle is either
// manaully controlled via the API or attached to the scope of
// the singleton (program scope). Otherwise the cpu toggle is
// used to allow for user selectable control via signal generation.
// This is very useful for profiling a daemon process without
// having to start and stop the daemon or having to modify the
// source code to use the cpu profiler API.
class CpuProfiler {
 public:
  CpuProfiler();
  ~CpuProfiler();

  // Start profiler to write profile info into fname
  bool Start(const char* fname, const ProfilerOptions* options);

  // Stop profiling and write the data to disk.
  void Stop();

  // Write the data to disk (and continue profiling).
  void FlushTable();

  bool Enabled();

  void GetCurrentState(ProfilerState* state);

  static CpuProfiler instance_;

 private:
  // This lock implements the locking requirements described in the ProfileData
  // documentation, specifically:
  //
  // lock_ is held all over all collector_ method calls except for the 'Add'
  // call made from the signal handler, to protect against concurrent use of
  // collector_'s control routines. Code other than signal handler must
  // unregister the signal handler before calling any collector_ method.
  // 'Add' method in the collector is protected by a guarantee from
  // ProfileHandle that only one instance of prof_handler can run at a time.
  SpinLock      lock_;
  ProfileData   collector_;

  // Filter function and its argument, if any.  (NULL means include all
  // samples).  Set at start, read-only while running.  Written while holding
  // lock_, read and executed in the context of SIGPROF interrupt.
  int           (*filter_)(void*);
  void*         filter_arg_;

  // Opaque token returned by the profile handler. To be used when calling
  // ProfileHandlerUnregisterCallback.
  ProfileHandlerToken* prof_handler_token_;

  // Sets up a callback to receive SIGPROF interrupt.
  void EnableHandler();

  // Disables receiving SIGPROF interrupt.
  void DisableHandler();

  // Signal handler that records the interrupted pc in the profile data.
  static void prof_handler(int sig, siginfo_t*, void* signal_ucontext,
                           void* cpu_profiler);
};

// Signal handler that is registered when a user selectable signal
// number is defined in the environment variable CPUPROFILESIGNAL.
static void CpuProfilerSwitch(int signal_number)
{
  static unsigned profile_count;
  static char base_profile_name[PATH_MAX];
  static bool started = false;

  if (base_profile_name[0] == '\0') {
    if (!GetUniquePathFromEnv("CPUPROFILE", base_profile_name)) {
      RAW_LOG(FATAL,"Cpu profiler switch is registered but no CPUPROFILE is defined");
      return;
    }
  }

  if (!started) {
    char full_profile_name[PATH_MAX + 16];

    snprintf(full_profile_name, sizeof(full_profile_name), "%s.%u",
             base_profile_name, profile_count++);

    if(!ProfilerStart(full_profile_name)) {
      RAW_LOG(FATAL, "Can't turn on cpu profiling for '%s': %s\n",
              full_profile_name, strerror(errno));
    }
  } else {
    ProfilerStop();
  }
  started = !started;
}

// Profile data structure singleton: Constructor will check to see if
// profiling should be enabled.  Destructor will write profile data
// out to disk.
CpuProfiler CpuProfiler::instance_;

// Initialize profiling: activated if getenv("CPUPROFILE") exists.
CpuProfiler::CpuProfiler()
    : prof_handler_token_(NULL) {
  // TODO(cgd) Move this code *out* of the CpuProfile constructor into a
  // separate object responsible for initialization. With ProfileHandler there
  // is no need to limit the number of profilers.
  if (getenv("CPUPROFILE") == NULL) {
    if (!FLAGS_cpu_profiler_unittest) {
      RAW_LOG(WARNING, "CPU profiler linked but no valid CPUPROFILE environment variable found\n");
    }
    return;
  }

  // We don't enable profiling if setuid -- it's a security risk
#ifdef HAVE_GETEUID
  if (getuid() != geteuid()) {
    if (!FLAGS_cpu_profiler_unittest) {
      RAW_LOG(WARNING, "Cannot perform CPU profiling when running with setuid\n");
    }
    return;
  }
#endif

  char *signal_number_str = getenv("CPUPROFILESIGNAL");
  if (signal_number_str != NULL) {
    long int signal_number = strtol(signal_number_str, NULL, 10);
    if (signal_number >= 1 && signal_number <= 64) {
      intptr_t old_signal_handler = reinterpret_cast<intptr_t>(signal(signal_number, CpuProfilerSwitch));
      if (old_signal_handler == 0) {
        RAW_LOG(INFO,"Using signal %d as cpu profiling switch", signal_number);
      } else {
        RAW_LOG(FATAL, "Signal %d already in use\n", signal_number);
      }
    } else {
      RAW_LOG(FATAL, "Signal number %s is invalid\n", signal_number_str);
    }
  } else {
    char fname[PATH_MAX];
    if (!GetUniquePathFromEnv("CPUPROFILE", fname)) {
      if (!FLAGS_cpu_profiler_unittest) {
        RAW_LOG(WARNING, "CPU profiler linked but no valid CPUPROFILE environment variable found\n");
      }
      return;
	}

    if (!Start(fname, NULL)) {
      RAW_LOG(FATAL, "Can't turn on cpu profiling for '%s': %s\n",
              fname, strerror(errno));
    }
  }
}

bool CpuProfiler::Start(const char* fname, const ProfilerOptions* options) {
  SpinLockHolder cl(&lock_);

  if (collector_.enabled()) {
    return false;
  }

  ProfileHandlerState prof_handler_state;
  ProfileHandlerGetState(&prof_handler_state);

  ProfileData::Options collector_options;
  collector_options.set_frequency(prof_handler_state.frequency);
  if (!collector_.Start(fname, collector_options)) {
    return false;
  }

  filter_ = NULL;
  if (options != NULL && options->filter_in_thread != NULL) {
    filter_ = options->filter_in_thread;
    filter_arg_ = options->filter_in_thread_arg;
  }

  // Setup handler for SIGPROF interrupts
  EnableHandler();

  return true;
}

CpuProfiler::~CpuProfiler() {
  Stop();
}

// Stop profiling and write out any collected profile data
void CpuProfiler::Stop() {
  SpinLockHolder cl(&lock_);

  if (!collector_.enabled()) {
    return;
  }

  // Unregister prof_handler to stop receiving SIGPROF interrupts before
  // stopping the collector.
  DisableHandler();

  // DisableHandler waits for the currently running callback to complete and
  // guarantees no future invocations. It is safe to stop the collector.
  collector_.Stop();
}

void CpuProfiler::FlushTable() {
  SpinLockHolder cl(&lock_);

  if (!collector_.enabled()) {
    return;
  }

  // Unregister prof_handler to stop receiving SIGPROF interrupts before
  // flushing the profile data.
  DisableHandler();

  // DisableHandler waits for the currently running callback to complete and
  // guarantees no future invocations. It is safe to flush the profile data.
  collector_.FlushTable();

  EnableHandler();
}

bool CpuProfiler::Enabled() {
  SpinLockHolder cl(&lock_);
  return collector_.enabled();
}

void CpuProfiler::GetCurrentState(ProfilerState* state) {
  ProfileData::State collector_state;
  {
    SpinLockHolder cl(&lock_);
    collector_.GetCurrentState(&collector_state);
  }

  state->enabled = collector_state.enabled;
  state->start_time = static_cast<time_t>(collector_state.start_time);
  state->samples_gathered = collector_state.samples_gathered;
  int buf_size = sizeof(state->profile_name);
  strncpy(state->profile_name, collector_state.profile_name, buf_size);
  state->profile_name[buf_size-1] = '\0';
}

void CpuProfiler::EnableHandler() {
  RAW_CHECK(prof_handler_token_ == NULL, "SIGPROF handler already registered");
  prof_handler_token_ = ProfileHandlerRegisterCallback(prof_handler, this);
  RAW_CHECK(prof_handler_token_ != NULL, "Failed to set up SIGPROF handler");
}

void CpuProfiler::DisableHandler() {
  RAW_CHECK(prof_handler_token_ != NULL, "SIGPROF handler is not registered");
  ProfileHandlerUnregisterCallback(prof_handler_token_);
  prof_handler_token_ = NULL;
}

// Signal handler that records the pc in the profile-data structure. We do no
// synchronization here.  profile-handler.cc guarantees that at most one
// instance of prof_handler() will run at a time. All other routines that
// access the data touched by prof_handler() disable this signal handler before
// accessing the data and therefore cannot execute concurrently with
// prof_handler().
void CpuProfiler::prof_handler(int sig, siginfo_t*, void* signal_ucontext,
                               void* cpu_profiler) {
  CpuProfiler* instance = static_cast<CpuProfiler*>(cpu_profiler);

  if (instance->filter_ == NULL ||
      (*instance->filter_)(instance->filter_arg_)) {
    void* stack[ProfileData::kMaxStackDepth];

    // Under frame-pointer-based unwinding at least on x86, the
    // top-most active routine doesn't show up as a normal frame, but
    // as the "pc" value in the signal handler context.
    stack[0] = GetPC(*reinterpret_cast<ucontext_t*>(signal_ucontext));

    // We skip the top three stack trace entries (this function,
    // SignalHandler::SignalHandler and one signal handler frame)
    // since they are artifacts of profiling and should not be
    // measured.  Other profiling related frames may be removed by
    // "pprof" at analysis time.  Instead of skipping the top frames,
    // we could skip nothing, but that would increase the profile size
    // unnecessarily.
    int depth = GetStackTraceWithContext(stack + 1, arraysize(stack) - 1,
                                         3, signal_ucontext);

    void **used_stack;
    if (depth > 0 && stack[1] == stack[0]) {
      // in case of non-frame-pointer-based unwinding we will get
      // duplicate of PC in stack[1], which we don't want
      used_stack = stack + 1;
    } else {
      used_stack = stack;
      depth++;  // To account for pc value in stack[0];
    }

    instance->collector_.Add(depth, used_stack);
  }
}

#if !(defined(__CYGWIN__) || defined(__CYGWIN32__))

extern "C" PERFTOOLS_DLL_DECL void ProfilerRegisterThread() {
  ProfileHandlerRegisterThread();
}

extern "C" PERFTOOLS_DLL_DECL void ProfilerFlush() {
  CpuProfiler::instance_.FlushTable();
}

extern "C" PERFTOOLS_DLL_DECL int ProfilingIsEnabledForAllThreads() {
  return CpuProfiler::instance_.Enabled();
}

extern "C" PERFTOOLS_DLL_DECL int ProfilerStart(const char* fname) {
  return CpuProfiler::instance_.Start(fname, NULL);
}

extern "C" PERFTOOLS_DLL_DECL int ProfilerStartWithOptions(
    const char *fname, const ProfilerOptions *options) {
  return CpuProfiler::instance_.Start(fname, options);
}

extern "C" PERFTOOLS_DLL_DECL void ProfilerStop() {
  CpuProfiler::instance_.Stop();
}

extern "C" PERFTOOLS_DLL_DECL void ProfilerGetCurrentState(
    ProfilerState* state) {
  CpuProfiler::instance_.GetCurrentState(state);
}

#else  // OS_CYGWIN

// ITIMER_PROF doesn't work under cygwin.  ITIMER_REAL is available, but doesn't
// work as well for profiling, and also interferes with alarm().  Because of
// these issues, unless a specific need is identified, profiler support is
// disabled under Cygwin.
extern "C" void ProfilerRegisterThread() { }
extern "C" void ProfilerFlush() { }
extern "C" int ProfilingIsEnabledForAllThreads() { return 0; }
extern "C" int ProfilerStart(const char* fname) { return 0; }
extern "C" int ProfilerStartWithOptions(const char *fname,
                                        const ProfilerOptions *options) {
  return 0;
}
extern "C" void ProfilerStop() { }
extern "C" void ProfilerGetCurrentState(ProfilerState* state) {
  memset(state, 0, sizeof(*state));
}

#endif  // OS_CYGWIN

// DEPRECATED routines
extern "C" PERFTOOLS_DLL_DECL void ProfilerEnable() { }
extern "C" PERFTOOLS_DLL_DECL void ProfilerDisable() { }
<|endoftext|>


<commit_before>#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MAIN
#define BOOST_TEST_MODULE parse JSON

#include <arpa/inet.h>
#include <boost/test/unit_test.hpp>
#include <sys/uio.h>

#include <cstring>

#include "cJSON.h"
#include "parse.h"
#include "state.h"

static char wrong_json[] =   "{\"id\": 7384,\"method\": add\",\"params\":{\"path\": \"foo/bar/state\",\"value\": 123}}";
static char json_no_method[] = "{\"id\": 7384,\"meth\": \"add\",\"params\":{\"path\": \"foo/bar/state\",\"value\": 123}}";
static char json_no_string_method[] = "{\"id\": 7384,\"method\": 123,\"params\":{\"path\": \"foo/bar/state\",\"value\": 123}}";
static char json_two_method[] = "[{\"id\": 7384,\"method\": \"add\",\"params\":{\"path\": \"foo/bar/state\",\"value\": 123}}, {\"id\": 7384,\"method\": \"add\",\"params\":{\"path\": \"foo/state\",\"value\": 321}}]";
static char json_unsupported_method[] = "{\"id\": 7384,\"method\": \"horst\",\"params\":{\"path\": \"foo/bar/state\",\"value\": 123}}";
static char add_without_path[] = "{\"id\": 7384,\"method\": \"add\",\"params\":{\"value\": 123}}";
static char remove_without_path[] = "{\"id\": 7384,\"method\": \"remove\",\"params\":{\"value\": 123}}";
static char fetch_without_id[] = "{\"id\": 7384,\"method\": \"fetch\",\"params\":{\"path\": {\"startsWith\": \"person\"}}}";
static char correct_fetch[] = "{\"id\": 7384,\"method\": \"fetch\",\"params\":{\"id\": \"123456\",\"path\": {\"startsWith\": \"person\"}}}";
static char path_no_string[] = "{\"id\": 7384,\"method\": \"add\",\"params\":{\"path\": 123,\"value\": 123}}";
static char no_value[] = "{\"id\": 7384,\"method\": \"add\",\"params\":{\"path\": \"foo/bar/state\"}}";
static char no_params[] = "{\"id\": 7384,\"method\": \"add\"}";

static const int ADD_WITHOUT_PATH = 1;
static const int PATH_NO_STRING = 2;
static const int NO_VALUE = 3;
static const int NO_PARAMS = 4;
static const int UNSUPPORTED_METHOD = 5;
static const int REMOVE_WITHOUT_PATH = 6;
static const int FETCH_WITHOUT_ID = 7;
static const int CORRECT_FETCH = 7;

static char readback_buffer[10000];
static char *readback_buffer_ptr = readback_buffer;

extern "C" {

	int fake_read(int fd, void *buf, size_t count)
	{
		return 0;
	}

	int fake_send(int fd, void *buf, size_t count, int flags)
	{
		return 0;
	}

	static int copy_iov(const struct iovec *iov, int iovcnt)
	{
		int count = 0;
		for (int i = 0; i < iovcnt; ++i) {
			memcpy(readback_buffer_ptr, iov[i].iov_base, iov[i].iov_len);
			readback_buffer_ptr += iov[i].iov_len;
			count += iov[i].iov_len;
		}
		return count;
	}

	int fake_writev(int fd, const struct iovec *iov, int iovcnt)
	{
		int count = copy_iov(iov, iovcnt);
		return count;
	}
}

struct F {
	F(int fd)
	{
		create_state_hashtable();
		p = alloc_peer(fd);

		readback_buffer_ptr = readback_buffer;
		std::memset(readback_buffer, 0x00, sizeof(readback_buffer));
	}
	~F()
	{
		free_peer(p);
		delete_state_hashtable();
	}

	struct peer *p;
};

static void check_invalid_message(const char *buffer, int code, const char *message_string)
{
	uint32_t len;
	const char *readback_ptr = buffer;
	memcpy(&len, readback_ptr, sizeof(len));
	len = ntohl(len);
	readback_ptr += sizeof(len);

	const char *end_parse;
	cJSON *root = cJSON_ParseWithOpts(readback_ptr, &end_parse, 0);
	BOOST_CHECK(root != NULL);

	uint32_t parsed_length = end_parse - readback_ptr;
	BOOST_CHECK(parsed_length == len);

	cJSON *error = cJSON_GetObjectItem(root, "error");
	BOOST_REQUIRE(error != NULL);

	cJSON *code_object = cJSON_GetObjectItem(error, "code");
	BOOST_REQUIRE(code_object != NULL);
	BOOST_CHECK(code_object->type == cJSON_Number);
	BOOST_CHECK(code_object->valueint == code);

	cJSON *message = cJSON_GetObjectItem(error, "message");
	BOOST_REQUIRE(message != NULL);
	BOOST_CHECK(message->type == cJSON_String);
	BOOST_CHECK(strcmp(message->valuestring, message_string) == 0);

	cJSON_Delete(root);
}

static void check_invalid_params_message(const char *buffer)
{
	check_invalid_message(buffer, -32602, "Invalid params");
}

static void check_method_not_found_message(const char *buffer)
{
	check_invalid_message(buffer, -32601, "Method not found");
}

static cJSON *create_correct_json()
{
	cJSON *root = cJSON_CreateObject();
	cJSON_AddNumberToObject(root, "id", 7384);
	cJSON_AddStringToObject(root, "method", "add");

	cJSON *params = cJSON_CreateObject();
	cJSON_AddStringToObject(params, "path", "/foo/bar/state/");
	cJSON_AddNumberToObject(params, "value", 123);
	cJSON_AddItemToObject(root, "params", params);
	return root;
}

static void check_invalid_request_message(const char *buffer)
{
	check_invalid_message(buffer, -32600, "Invalid Request");
}

BOOST_AUTO_TEST_CASE(parse_correct_json)
{
	F f(-1);
	cJSON *correct_json = create_correct_json();
	char *unformatted_json = cJSON_PrintUnformatted(correct_json);
	int ret = parse_message(unformatted_json, strlen(unformatted_json), f.p);
	cJSON_free(unformatted_json);
	cJSON_Delete(correct_json);
	BOOST_CHECK(ret == 0);
}

BOOST_AUTO_TEST_CASE(length_too_long)
{
	cJSON *correct_json = create_correct_json();
	char *unformatted_json = cJSON_PrintUnformatted(correct_json);
	int ret = parse_message(unformatted_json, strlen(unformatted_json) + 1, NULL);
	cJSON_free(unformatted_json);
	cJSON_Delete(correct_json);

	BOOST_CHECK(ret == -1);
}

BOOST_AUTO_TEST_CASE(length_too_short)
{
	cJSON *correct_json = create_correct_json();
	char *unformatted_json = cJSON_PrintUnformatted(correct_json);
	int ret = parse_message(unformatted_json, strlen(unformatted_json) - 1, NULL);
	cJSON_free(unformatted_json);
	cJSON_Delete(correct_json);

	BOOST_CHECK(ret == -1);
}

BOOST_AUTO_TEST_CASE(two_method)
{
	F f(-1);
	int ret = parse_message(json_two_method, strlen(json_two_method), f.p);
	BOOST_CHECK(ret == 0);
}

BOOST_AUTO_TEST_CASE(wrong_array)
{
	const int numbers[2] = {1,2};
	cJSON *root = cJSON_CreateIntArray(numbers, 2);
	char *unformatted_json = cJSON_PrintUnformatted(root);
	int ret = parse_message(unformatted_json, strlen(unformatted_json), NULL);
	cJSON_free(unformatted_json);
	cJSON_Delete(root);

	BOOST_CHECK(ret == -1);
}

BOOST_AUTO_TEST_CASE(add_without_path_test)
{
	F f(ADD_WITHOUT_PATH);
	int ret = parse_message(add_without_path, strlen(add_without_path), f.p);
	BOOST_CHECK(ret == 0);

	check_invalid_params_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(path_no_string_test)
{
	F f(PATH_NO_STRING);
	int ret = parse_message(path_no_string, strlen(path_no_string), f.p);
	BOOST_CHECK(ret == 0);

	check_invalid_params_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(no_value_test)
{
	F f(NO_VALUE);
	int ret = parse_message(no_value, strlen(no_value), f.p);
	BOOST_CHECK(ret == 0);

	check_invalid_params_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(no_params_test)
{
	F f(NO_PARAMS);
	int ret = parse_message(no_params, strlen(no_params), f.p);
	BOOST_CHECK(ret == 0);

	check_invalid_params_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(unsupported_method)
{
	F f(UNSUPPORTED_METHOD);
	int ret = parse_message(json_unsupported_method, strlen(json_unsupported_method), f.p);
	BOOST_CHECK(ret == 0);

	check_method_not_found_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(no_method)
{
	F f(UNSUPPORTED_METHOD);
	int ret = parse_message(json_no_method, strlen(json_no_method), f.p);
	BOOST_CHECK(ret == 0);

	check_invalid_request_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(no_string_method)
{
	F f(UNSUPPORTED_METHOD);
	int ret = parse_message(json_no_string_method, strlen(json_no_string_method), f.p);
	BOOST_CHECK(ret == 0);

	check_invalid_request_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(parse_wrong_json)
{
	int ret = parse_message(wrong_json, strlen(wrong_json), NULL);
	BOOST_CHECK(ret == -1);
}

BOOST_AUTO_TEST_CASE(remove_without_path_test)
{
	F f(REMOVE_WITHOUT_PATH);
	int ret = parse_message(remove_without_path, strlen(remove_without_path), f.p);
	BOOST_CHECK(ret == 0);

	check_invalid_params_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(fetch_without_id_test)
{
	F f(FETCH_WITHOUT_ID);
	int ret = parse_message(fetch_without_id, strlen(fetch_without_id), f.p);
	BOOST_CHECK(ret == 0);

	check_invalid_params_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(correct_fetch_test)
{
	F f(CORRECT_FETCH);
	int ret = parse_message(correct_fetch, strlen(correct_fetch), f.p);
	BOOST_CHECK(ret == 0);
}
<commit_msg>Use cJSON to create a message with two methods.<commit_after>#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MAIN
#define BOOST_TEST_MODULE parse JSON

#include <arpa/inet.h>
#include <boost/test/unit_test.hpp>
#include <sys/uio.h>

#include <cstring>

#include "cJSON.h"
#include "parse.h"
#include "state.h"

static char wrong_json[] =   "{\"id\": 7384,\"method\": add\",\"params\":{\"path\": \"foo/bar/state\",\"value\": 123}}";
static char json_no_method[] = "{\"id\": 7384,\"meth\": \"add\",\"params\":{\"path\": \"foo/bar/state\",\"value\": 123}}";
static char json_no_string_method[] = "{\"id\": 7384,\"method\": 123,\"params\":{\"path\": \"foo/bar/state\",\"value\": 123}}";
static char json_unsupported_method[] = "{\"id\": 7384,\"method\": \"horst\",\"params\":{\"path\": \"foo/bar/state\",\"value\": 123}}";
static char add_without_path[] = "{\"id\": 7384,\"method\": \"add\",\"params\":{\"value\": 123}}";
static char remove_without_path[] = "{\"id\": 7384,\"method\": \"remove\",\"params\":{\"value\": 123}}";
static char fetch_without_id[] = "{\"id\": 7384,\"method\": \"fetch\",\"params\":{\"path\": {\"startsWith\": \"person\"}}}";
static char correct_fetch[] = "{\"id\": 7384,\"method\": \"fetch\",\"params\":{\"id\": \"123456\",\"path\": {\"startsWith\": \"person\"}}}";
static char path_no_string[] = "{\"id\": 7384,\"method\": \"add\",\"params\":{\"path\": 123,\"value\": 123}}";
static char no_value[] = "{\"id\": 7384,\"method\": \"add\",\"params\":{\"path\": \"foo/bar/state\"}}";
static char no_params[] = "{\"id\": 7384,\"method\": \"add\"}";

static const int ADD_WITHOUT_PATH = 1;
static const int PATH_NO_STRING = 2;
static const int NO_VALUE = 3;
static const int NO_PARAMS = 4;
static const int UNSUPPORTED_METHOD = 5;
static const int REMOVE_WITHOUT_PATH = 6;
static const int FETCH_WITHOUT_ID = 7;
static const int CORRECT_FETCH = 7;

static char readback_buffer[10000];
static char *readback_buffer_ptr = readback_buffer;

extern "C" {

	int fake_read(int fd, void *buf, size_t count)
	{
		return 0;
	}

	int fake_send(int fd, void *buf, size_t count, int flags)
	{
		return 0;
	}

	static int copy_iov(const struct iovec *iov, int iovcnt)
	{
		int count = 0;
		for (int i = 0; i < iovcnt; ++i) {
			memcpy(readback_buffer_ptr, iov[i].iov_base, iov[i].iov_len);
			readback_buffer_ptr += iov[i].iov_len;
			count += iov[i].iov_len;
		}
		return count;
	}

	int fake_writev(int fd, const struct iovec *iov, int iovcnt)
	{
		int count = copy_iov(iov, iovcnt);
		return count;
	}
}

struct F {
	F(int fd)
	{
		create_state_hashtable();
		p = alloc_peer(fd);

		readback_buffer_ptr = readback_buffer;
		std::memset(readback_buffer, 0x00, sizeof(readback_buffer));
	}
	~F()
	{
		free_peer(p);
		delete_state_hashtable();
	}

	struct peer *p;
};

static void check_invalid_message(const char *buffer, int code, const char *message_string)
{
	uint32_t len;
	const char *readback_ptr = buffer;
	memcpy(&len, readback_ptr, sizeof(len));
	len = ntohl(len);
	readback_ptr += sizeof(len);

	const char *end_parse;
	cJSON *root = cJSON_ParseWithOpts(readback_ptr, &end_parse, 0);
	BOOST_CHECK(root != NULL);

	uint32_t parsed_length = end_parse - readback_ptr;
	BOOST_CHECK(parsed_length == len);

	cJSON *error = cJSON_GetObjectItem(root, "error");
	BOOST_REQUIRE(error != NULL);

	cJSON *code_object = cJSON_GetObjectItem(error, "code");
	BOOST_REQUIRE(code_object != NULL);
	BOOST_CHECK(code_object->type == cJSON_Number);
	BOOST_CHECK(code_object->valueint == code);

	cJSON *message = cJSON_GetObjectItem(error, "message");
	BOOST_REQUIRE(message != NULL);
	BOOST_CHECK(message->type == cJSON_String);
	BOOST_CHECK(strcmp(message->valuestring, message_string) == 0);

	cJSON_Delete(root);
}

static void check_invalid_params_message(const char *buffer)
{
	check_invalid_message(buffer, -32602, "Invalid params");
}

static void check_method_not_found_message(const char *buffer)
{
	check_invalid_message(buffer, -32601, "Method not found");
}

static cJSON *create_correct_json()
{
	cJSON *root = cJSON_CreateObject();
	cJSON_AddNumberToObject(root, "id", 7384);
	cJSON_AddStringToObject(root, "method", "add");

	cJSON *params = cJSON_CreateObject();
	cJSON_AddStringToObject(params, "path", "/foo/bar/state/");
	cJSON_AddNumberToObject(params, "value", 123);
	cJSON_AddItemToObject(root, "params", params);
	return root;
}

static cJSON *create_two_method_json()
{
	cJSON *array = cJSON_CreateArray();
	cJSON *method_1 = create_correct_json();
	cJSON *method_2 = create_correct_json();
	cJSON_AddItemToArray(array, method_1);
	cJSON_AddItemToArray(array, method_2);
	return array;
}

static void check_invalid_request_message(const char *buffer)
{
	check_invalid_message(buffer, -32600, "Invalid Request");
}

BOOST_AUTO_TEST_CASE(parse_correct_json)
{
	F f(-1);
	cJSON *correct_json = create_correct_json();
	char *unformatted_json = cJSON_PrintUnformatted(correct_json);
	int ret = parse_message(unformatted_json, strlen(unformatted_json), f.p);
	cJSON_free(unformatted_json);
	cJSON_Delete(correct_json);
	BOOST_CHECK(ret == 0);
}

BOOST_AUTO_TEST_CASE(length_too_long)
{
	cJSON *correct_json = create_correct_json();
	char *unformatted_json = cJSON_PrintUnformatted(correct_json);
	int ret = parse_message(unformatted_json, strlen(unformatted_json) + 1, NULL);
	cJSON_free(unformatted_json);
	cJSON_Delete(correct_json);

	BOOST_CHECK(ret == -1);
}

BOOST_AUTO_TEST_CASE(length_too_short)
{
	cJSON *correct_json = create_correct_json();
	char *unformatted_json = cJSON_PrintUnformatted(correct_json);
	int ret = parse_message(unformatted_json, strlen(unformatted_json) - 1, NULL);
	cJSON_free(unformatted_json);
	cJSON_Delete(correct_json);

	BOOST_CHECK(ret == -1);
}

BOOST_AUTO_TEST_CASE(two_method)
{
	F f(-1);
	cJSON *array = create_two_method_json();
	char *unformatted_json = cJSON_PrintUnformatted(array);
	int ret = parse_message(unformatted_json, strlen(unformatted_json), f.p);
	cJSON_free(unformatted_json);
	cJSON_Delete(array);
	BOOST_CHECK(ret == 0);
}

BOOST_AUTO_TEST_CASE(wrong_array)
{
	const int numbers[2] = {1,2};
	cJSON *root = cJSON_CreateIntArray(numbers, 2);
	char *unformatted_json = cJSON_PrintUnformatted(root);
	int ret = parse_message(unformatted_json, strlen(unformatted_json), NULL);
	cJSON_free(unformatted_json);
	cJSON_Delete(root);

	BOOST_CHECK(ret == -1);
}

BOOST_AUTO_TEST_CASE(add_without_path_test)
{
	F f(ADD_WITHOUT_PATH);
	int ret = parse_message(add_without_path, strlen(add_without_path), f.p);
	BOOST_CHECK(ret == 0);

	check_invalid_params_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(path_no_string_test)
{
	F f(PATH_NO_STRING);
	int ret = parse_message(path_no_string, strlen(path_no_string), f.p);
	BOOST_CHECK(ret == 0);

	check_invalid_params_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(no_value_test)
{
	F f(NO_VALUE);
	int ret = parse_message(no_value, strlen(no_value), f.p);
	BOOST_CHECK(ret == 0);

	check_invalid_params_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(no_params_test)
{
	F f(NO_PARAMS);
	int ret = parse_message(no_params, strlen(no_params), f.p);
	BOOST_CHECK(ret == 0);

	check_invalid_params_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(unsupported_method)
{
	F f(UNSUPPORTED_METHOD);
	int ret = parse_message(json_unsupported_method, strlen(json_unsupported_method), f.p);
	BOOST_CHECK(ret == 0);

	check_method_not_found_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(no_method)
{
	F f(UNSUPPORTED_METHOD);
	int ret = parse_message(json_no_method, strlen(json_no_method), f.p);
	BOOST_CHECK(ret == 0);

	check_invalid_request_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(no_string_method)
{
	F f(UNSUPPORTED_METHOD);
	int ret = parse_message(json_no_string_method, strlen(json_no_string_method), f.p);
	BOOST_CHECK(ret == 0);

	check_invalid_request_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(parse_wrong_json)
{
	int ret = parse_message(wrong_json, strlen(wrong_json), NULL);
	BOOST_CHECK(ret == -1);
}

BOOST_AUTO_TEST_CASE(remove_without_path_test)
{
	F f(REMOVE_WITHOUT_PATH);
	int ret = parse_message(remove_without_path, strlen(remove_without_path), f.p);
	BOOST_CHECK(ret == 0);

	check_invalid_params_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(fetch_without_id_test)
{
	F f(FETCH_WITHOUT_ID);
	int ret = parse_message(fetch_without_id, strlen(fetch_without_id), f.p);
	BOOST_CHECK(ret == 0);

	check_invalid_params_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(correct_fetch_test)
{
	F f(CORRECT_FETCH);
	int ret = parse_message(correct_fetch, strlen(correct_fetch), f.p);
	BOOST_CHECK(ret == 0);
}
<|endoftext|>