<commit_before>#ifndef __YUNI_PRIVATE_MEDIA_STREAM_HXX__
# define __YUNI_PRIVATE_MEDIA_STREAM_HXX__

#include "openal.h"
#include "../../core/math.h"

namespace Yuni
{
namespace Private
{
namespace Media
{

	template<StreamType TypeT>
	Stream<TypeT>::Stream(File* parent, AVFormatContext* format, AVCodecContext* codecCtx, uint index):
		pCodec(codecCtx),
		pFormat(format),
		pIndex(index),
		pALFormat(0),
		pSize(0),
		pCrtPts(AV_NOPTS_VALUE),
		pFrame(nullptr),
		pParent(parent)
	{
		if (!pParent)
		{
			pCodec = nullptr;
			return;
		}

		// Try to find the codec for the given codec ID, and open it
		AVCodec* codec = ::avcodec_find_decoder(pCodec->codec_id);
		# if LIBAVFORMAT_VERSION_MAJOR < 53
		if (!codec or ::avcodec_open(pCodec, codec) < 0)
		# else
		if (!codec or ::avcodec_open2(pCodec, codec, NULL) < 0)
		# endif // LIBAVFORMAT_VERSION_MAJOR < 53
		{
			pCodec = nullptr;
			return;
		}

		if (IsAudio)
			pALFormat = Private::Media::OpenAL::GetFormat(16, pCodec->channels);
	}

	template<StreamType TypeT>
	Stream<TypeT>::~Stream()
	{
		if (pCodec and pCodec->codec)
		{
			//::avcodec_close(pCodec);
			pCodec = nullptr;
		}
		if (pFrame)
			::av_free(pFrame);
	}

	template<StreamType TypeT>
	AVPacket* Stream<TypeT>::nextPacket()
	{
		// If the queue is empty
		if (pPackets.empty())
		{
			if (!pParent)
				return nullptr;

			AVPacket* pkt = pParent->getNextPacket(this);
			if (!pkt)
				// No more packets
				return nullptr;
		}

		// Get the first packet in queue
		AVPacket* pkt = pPackets.front();
		pPackets.pop_front();
		return pkt;
	}

	template<StreamType TypeT>
	uint Stream<TypeT>::readFrame()
	{
		// Frame allocation
		if (!pFrame)
		{
			if (!(pFrame = ::avcodec_alloc_frame()))
			{
				std::cerr << "Error allocating a frame for audio decoding !" << std::endl;
				return 0;
			}
		}
		else
		{
			// Should not happen, but this is a security.
			::avcodec_get_frame_defaults(pFrame);
		}

		int bytesRead = 0;
		int frameFinished = 0;
		AVPacket* packet = nullptr;

		while (not frameFinished)
		{
			// Get next packet
			packet = nextPacket();
			if (!packet)
				return 0;

			// VIDEO
			if (IsVideo)
			{
				pCrtPts = 0;
				// Decode the packet
				#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(52,30,0)
				if ((bytesRead = ::avcodec_decode_video2(pCodec, pFrame, &frameFinished, packet)) < 0)
				#else
				if ((bytesRead = ::avcodec_decode_video(pCodec, pFrame, &frameFinished, packet->data, packet->size)) < 0)
				#endif
				{
					std::cerr << "Error while decoding video !" << std::endl;
					continue;
					// Do not do anything here, just act normally and try to recover from the error
				}

				// If the frame is finished (should be in one shot)
				if (frameFinished)
				{
					if ((uint64)AV_NOPTS_VALUE == (uint64)packet->dts and pFrame->opaque
						&& (uint64)AV_NOPTS_VALUE != *(uint64*)pFrame->opaque)
						pCrtPts = *(uint64*)pFrame->opaque;
					else if ((uint64)AV_NOPTS_VALUE != (uint64)packet->dts)
						pCrtPts = packet->dts;
					else
						pCrtPts = 0.0;
					// pCrtPts = ::av_frame_get_best_effort_timestamp(pFrame);
					float timeRatio = ::av_q2d(pFormat->streams[pIndex]->time_base);
					pCrtPts *= timeRatio;
					pCrtPts -= (pFormat->streams[pIndex]->start_time * timeRatio);
					break;
				}
			}
			// AUDIO
			else
			{
				// Decode the packet
				#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(52,30,0)
				if ((bytesRead = ::avcodec_decode_audio4(pCodec, pFrame, &frameFinished, packet)) < 0)
				#else
				if ((bytesRead = ::avcodec_decode_audio3(pCodec, pFrame, &frameFinished, packet->data, packet->size)) < 0)
				#endif
				{
					std::cerr << "Error while decoding audio !" << std::endl;
					// Do not do anything here, just act normally and try to recover from the error
				}

				// If the frame is finished (should be in one shot)
				if (frameFinished)
					break;
			}

			// Free packet before looping
			::av_free_packet(packet);
			delete packet;
			packet = nullptr;
		}

		// Free packet before quitting
		if (packet)
		{
			::av_free_packet(packet);
			delete packet;
		}

		return bytesRead;
	}

	template<StreamType TypeT>
	inline Frame::Ptr Stream<TypeT>::nextFrame()
	{
		//YUNI_STATIC_ASSERT(IsVideo, nextFrameNotAccessibleInAudio);
		if (IsVideo)
		{
			readFrame();
		}
		else // IsAudio
		{
			//readAudioFrame();
			readFrame();
		}

		static uint i = 1u;
		// TODO : Give the real frame index
		Frame* frame = new Frame(i++, pCrtPts);
		// Our Frame object takes custody of the AVFrame
		// and will take care of its deletion
		frame->setData(pFrame);
		// Reset the current frame
		pFrame = nullptr;
		return frame;
	}

	template<StreamType TypeT>
	inline void Stream<TypeT>::rewind()
	{
		if (pFrame)
			::av_free(pFrame);
		::av_seek_frame(pFormat, pIndex, 0, 0);
	}

	template<StreamType TypeT>
	inline uint Stream<TypeT>::index() const
	{
		return pIndex;
	}

	template<StreamType TypeT>
	inline uint Stream<TypeT>::duration() const
	{
		assert(pParent);
		return pParent->duration();
	}

	template<StreamType TypeT>
	inline uint Stream<TypeT>::width() const
	{
		YUNI_STATIC_ASSERT(IsVideo, NotAccessibleInAudio);
		return pCodec->width;
	}

	template<StreamType TypeT>
	inline uint Stream<TypeT>::height() const
	{
		YUNI_STATIC_ASSERT(IsVideo, NotAccessibleInAudio);
		return pCodec->height;
	}

	template<StreamType TypeT>
	inline uint Stream<TypeT>::depth() const
	{
		YUNI_STATIC_ASSERT(IsVideo, NotAccessibleInAudio);
		return ::av_get_bits_per_pixel(&::av_pix_fmt_descriptors[pCodec->pix_fmt]);
	}

	template<StreamType TypeT>
	inline float Stream<TypeT>::fps() const
	{
		YUNI_STATIC_ASSERT(IsVideo, NotAccessibleInAudio);
		assert(pCodec);
		assert(pFormat);
		assert(pFormat->streams[pIndex]);

		auto* avStream = pFormat->streams[pIndex];

		float den = avStream->avg_frame_rate.den;
		float variable = 0.0f;
		if (den > 0.0f) // avoid divide by 0
			variable = ::av_q2d(avStream->avg_frame_rate);

		den = avStream->time_base.num;
		float constant = 0.0f;
		if (den > 0.0f) // avoid divide by 0
			constant = pCodec->ticks_per_frame * avStream->time_base.den / den;

		return Math::Max(variable, constant);
	}

	template<StreamType TypeT>
	inline uint Stream<TypeT>::rate() const
	{
		YUNI_STATIC_ASSERT(IsAudio, NotAccessibleInVideo);
		assert(pCodec);
		return (pCodec->channels > 0) ? (pCodec->sample_rate / pCodec->channels) : 0;
	}

	template<StreamType TypeT>
	inline uint Stream<TypeT>::channels() const
	{
		YUNI_STATIC_ASSERT(IsAudio, NotAccessibleInVideo);
		assert(pCodec);
		return pCodec->channels;
	}

	template<StreamType TypeT>
	inline uint Stream<TypeT>::bits() const
	{
		YUNI_STATIC_ASSERT(IsAudio, NotAccessibleInVideo);
		assert(pCodec);
		// Internal FFMpeg format is always 16 bits
		return 16;
	}

	template<StreamType TypeT>
	inline StreamType Stream<TypeT>::type() const
	{
		return TypeT;
	}

} // namespace Media
} // namespace Private
} // namespace Yuni

#endif // __YUNI_PRIVATE_MEDIA_STREAM_HXX__
<commit_msg>Media : Fixed deprecated calls in newer versions of ffmpeg.<commit_after>#ifndef __YUNI_PRIVATE_MEDIA_STREAM_HXX__
# define __YUNI_PRIVATE_MEDIA_STREAM_HXX__

#include "openal.h"
#include "../../core/math.h"

namespace Yuni
{
namespace Private
{
namespace Media
{

	template<StreamType TypeT>
	Stream<TypeT>::Stream(File* parent, AVFormatContext* format, AVCodecContext* codecCtx, uint index):
		pCodec(codecCtx),
		pFormat(format),
		pIndex(index),
		pALFormat(0),
		pSize(0),
		pCrtPts(AV_NOPTS_VALUE),
		pFrame(nullptr),
		pParent(parent)
	{
		if (!pParent)
		{
			pCodec = nullptr;
			return;
		}

		// Try to find the codec for the given codec ID, and open it
		AVCodec* codec = ::avcodec_find_decoder(pCodec->codec_id);
		# if LIBAVFORMAT_VERSION_MAJOR < 53
		if (!codec or ::avcodec_open(pCodec, codec) < 0)
		# else
		if (!codec or ::avcodec_open2(pCodec, codec, NULL) < 0)
		# endif // LIBAVFORMAT_VERSION_MAJOR < 53
		{
			pCodec = nullptr;
			return;
		}

		if (IsAudio)
			pALFormat = Private::Media::OpenAL::GetFormat(16, pCodec->channels);
	}

	template<StreamType TypeT>
	Stream<TypeT>::~Stream()
	{
		if (pCodec and pCodec->codec)
		{
			//::avcodec_close(pCodec);
			pCodec = nullptr;
		}
		if (pFrame)
			::av_free(pFrame);
	}

	template<StreamType TypeT>
	AVPacket* Stream<TypeT>::nextPacket()
	{
		// If the queue is empty
		if (pPackets.empty())
		{
			if (!pParent)
				return nullptr;

			AVPacket* pkt = pParent->getNextPacket(this);
			if (!pkt)
				// No more packets
				return nullptr;
		}

		// Get the first packet in queue
		AVPacket* pkt = pPackets.front();
		pPackets.pop_front();
		return pkt;
	}

	template<StreamType TypeT>
	uint Stream<TypeT>::readFrame()
	{
		// Frame allocation
		if (!pFrame)
		{
			#if LIBAVUTIL_VERSION_INT > AV_VERSION_INT(52, 20, 100)
			if (!(pFrame = ::av_frame_alloc()))
			#else
			if (!(pFrame = ::avcodec_alloc_frame()))
			#endif
			{
				std::cerr << "Error allocating a frame for audio decoding !" << std::endl;
				return 0;
			}
		}
		else
		{
			// Should not happen, but this is a security.
			#if LIBAVUTIL_VERSION_INT > AV_VERSION_INT(52, 20, 100)
			::av_frame_unref(pFrame);
			#else
			::avcodec_get_frame_defaults(pFrame);
			#endif
		}

		int bytesRead = 0;
		int frameFinished = 0;
		AVPacket* packet = nullptr;

		while (not frameFinished)
		{
			// Get next packet
			packet = nextPacket();
			if (!packet)
				return 0;

			// VIDEO
			if (IsVideo)
			{
				pCrtPts = 0;
				// Decode the packet
				#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(52,30,0)
				if ((bytesRead = ::avcodec_decode_video2(pCodec, pFrame, &frameFinished, packet)) < 0)
				#else
				if ((bytesRead = ::avcodec_decode_video(pCodec, pFrame, &frameFinished, packet->data, packet->size)) < 0)
				#endif
				{
					std::cerr << "Error while decoding video !" << std::endl;
					continue;
					// Do not do anything here, just act normally and try to recover from the error
				}

				// If the frame is finished (should be in one shot)
				if (frameFinished)
				{
					if ((uint64)AV_NOPTS_VALUE == (uint64)packet->dts and pFrame->opaque
						&& (uint64)AV_NOPTS_VALUE != *(uint64*)pFrame->opaque)
						pCrtPts = *(uint64*)pFrame->opaque;
					else if ((uint64)AV_NOPTS_VALUE != (uint64)packet->dts)
						pCrtPts = packet->dts;
					else
						pCrtPts = 0.0;
					// pCrtPts = ::av_frame_get_best_effort_timestamp(pFrame);
					float timeRatio = ::av_q2d(pFormat->streams[pIndex]->time_base);
					pCrtPts *= timeRatio;
					pCrtPts -= (pFormat->streams[pIndex]->start_time * timeRatio);
					break;
				}
			}
			// AUDIO
			else
			{
				// Decode the packet
				#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(52,30,0)
				if ((bytesRead = ::avcodec_decode_audio4(pCodec, pFrame, &frameFinished, packet)) < 0)
				#else
				if ((bytesRead = ::avcodec_decode_audio3(pCodec, pFrame, &frameFinished, packet->data, packet->size)) < 0)
				#endif
				{
					std::cerr << "Error while decoding audio !" << std::endl;
					// Do not do anything here, just act normally and try to recover from the error
				}

				// If the frame is finished (should be in one shot)
				if (frameFinished)
					break;
			}

			// Free packet before looping
			::av_free_packet(packet);
			delete packet;
			packet = nullptr;
		}

		// Free packet before quitting
		if (packet)
		{
			::av_free_packet(packet);
			delete packet;
		}

		return bytesRead;
	}

	template<StreamType TypeT>
	inline Frame::Ptr Stream<TypeT>::nextFrame()
	{
		//YUNI_STATIC_ASSERT(IsVideo, nextFrameNotAccessibleInAudio);
		if (IsVideo)
		{
			readFrame();
		}
		else // IsAudio
		{
			//readAudioFrame();
			readFrame();
		}

		static uint i = 1u;
		// TODO : Give the real frame index
		Frame* frame = new Frame(i++, pCrtPts);
		// Our Frame object takes custody of the AVFrame
		// and will take care of its deletion
		frame->setData(pFrame);
		// Reset the current frame
		pFrame = nullptr;
		return frame;
	}

	template<StreamType TypeT>
	inline void Stream<TypeT>::rewind()
	{
		if (pFrame)
			::av_free(pFrame);
		::av_seek_frame(pFormat, pIndex, 0, 0);
	}

	template<StreamType TypeT>
	inline uint Stream<TypeT>::index() const
	{
		return pIndex;
	}

	template<StreamType TypeT>
	inline uint Stream<TypeT>::duration() const
	{
		assert(pParent);
		return pParent->duration();
	}

	template<StreamType TypeT>
	inline uint Stream<TypeT>::width() const
	{
		YUNI_STATIC_ASSERT(IsVideo, NotAccessibleInAudio);
		return pCodec->width;
	}

	template<StreamType TypeT>
	inline uint Stream<TypeT>::height() const
	{
		YUNI_STATIC_ASSERT(IsVideo, NotAccessibleInAudio);
		return pCodec->height;
	}

	template<StreamType TypeT>
	inline uint Stream<TypeT>::depth() const
	{
		YUNI_STATIC_ASSERT(IsVideo, NotAccessibleInAudio);
		#if LIBAVCODEC_VERSION_INT > AV_VERSION_INT(52,30,0)
		return ::av_get_bits_per_pixel(::av_pix_fmt_desc_get(pCodec->pix_fmt));
		#else
		return ::av_get_bits_per_pixel(&::av_pix_fmt_descriptors[pCodec->pix_fmt]);
		#endif
	}

	template<StreamType TypeT>
	inline float Stream<TypeT>::fps() const
	{
		YUNI_STATIC_ASSERT(IsVideo, NotAccessibleInAudio);
		assert(pCodec);
		assert(pFormat);
		assert(pFormat->streams[pIndex]);

		auto* avStream = pFormat->streams[pIndex];

		float den = avStream->avg_frame_rate.den;
		float variable = 0.0f;
		if (den > 0.0f) // avoid divide by 0
			variable = ::av_q2d(avStream->avg_frame_rate);

		den = avStream->time_base.num;
		float constant = 0.0f;
		if (den > 0.0f) // avoid divide by 0
			constant = pCodec->ticks_per_frame * avStream->time_base.den / den;

		return Math::Max(variable, constant);
	}

	template<StreamType TypeT>
	inline uint Stream<TypeT>::rate() const
	{
		YUNI_STATIC_ASSERT(IsAudio, NotAccessibleInVideo);
		assert(pCodec);
		return (pCodec->channels > 0) ? (pCodec->sample_rate / pCodec->channels) : 0;
	}

	template<StreamType TypeT>
	inline uint Stream<TypeT>::channels() const
	{
		YUNI_STATIC_ASSERT(IsAudio, NotAccessibleInVideo);
		assert(pCodec);
		return pCodec->channels;
	}

	template<StreamType TypeT>
	inline uint Stream<TypeT>::bits() const
	{
		YUNI_STATIC_ASSERT(IsAudio, NotAccessibleInVideo);
		assert(pCodec);
		// Internal FFMpeg format is always 16 bits
		return 16;
	}

	template<StreamType TypeT>
	inline StreamType Stream<TypeT>::type() const
	{
		return TypeT;
	}

} // namespace Media
} // namespace Private
} // namespace Yuni

#endif // __YUNI_PRIVATE_MEDIA_STREAM_HXX__
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the V4VM module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include <QString>
#include "debugging.h"
#include <qmljs_environment.h>
#include <qmljs_objects.h>
#include <qv4ecmaobjects_p.h>
#include "qv4mm.h"

namespace QQmlJS {
namespace VM {

DiagnosticMessage::DiagnosticMessage()
    : offset(0)
    , length(0)
    , startLine(0)
    , startColumn(0)
    , type(0)
    , next(0)
{}

DiagnosticMessage::~DiagnosticMessage()
{
    delete next;
}

String *DiagnosticMessage::buildFullMessage(ExecutionContext *ctx) const
{
    QString msg;
    if (!fileName.isEmpty())
        msg = fileName + QLatin1Char(':');
    msg += QString::number(startLine) + QLatin1Char(':') + QString::number(startColumn) + QLatin1String(": ");
    if (type == QQmlJS::VM::DiagnosticMessage::Error)
        msg += QLatin1String("error");
    else
        msg += QLatin1String("warning");
    msg += ": " + message;

    return ctx->engine->newString(msg);
}

bool ExecutionContext::hasBinding(String *name) const
{
    if (!function)
        return false;

    for (unsigned int i = 0; i < function->varCount; ++i) {
        if (__qmljs_string_equal(function->varList[i], name))
            return true;
    }
    for (unsigned int i = 0; i < function->formalParameterCount; ++i) {
        if (__qmljs_string_equal(function->formalParameterList[i], name))
            return true;
    }
    if (activation)
        return activation->__hasProperty__(this, name);
    return false;
}

void ExecutionContext::createMutableBinding(String *name, bool deletable)
{
    if (!activation)
        activation = engine->newActivationObject();

    if (activation->__hasProperty__(this, name))
        return;
    PropertyDescriptor desc;
    desc.value = Value::undefinedValue();
    desc.type = PropertyDescriptor::Data;
    desc.configurable = deletable ? PropertyDescriptor::Set : PropertyDescriptor::Unset;
    desc.writable = PropertyDescriptor::Set;
    desc.enumberable = PropertyDescriptor::Set;
    activation->__defineOwnProperty__(this, name, &desc);
}

bool ExecutionContext::setMutableBinding(ExecutionContext *scope, String *name, Value value)
{
    // ### throw if scope->strict is true, and it would change an immutable binding
    for (unsigned int i = 0; i < variableCount(); ++i) {
        if (__qmljs_string_equal(variables()[i], name)) {
            locals[i] = value;
            return true;
        }
    }
    for (unsigned int i = 0; i < formalCount(); ++i) {
        if (__qmljs_string_equal(formals()[i], name)) {
            arguments[i] = value;
            return true;
        }
    }
    if (activation && activation->__hasProperty__(scope, name)) {
        activation->__put__(scope, name, value);
        return true;
    }

    return false;
}

Value ExecutionContext::getBindingValue(ExecutionContext *scope, String *name, bool strict) const
{
    Q_UNUSED(strict);
    assert(function);

    for (unsigned int i = 0; i < variableCount(); ++i) {
        if (__qmljs_string_equal(variables()[i], name))
            return locals[i];
    }
    for (unsigned int i = 0; i < formalCount(); ++i) {
        if (__qmljs_string_equal(formals()[i], name))
            return arguments[i];
    }
    if (activation && activation->__hasProperty__(this, name))
        return activation->__get__(scope, name);
    assert(false);
}

bool ExecutionContext::deleteBinding(ExecutionContext *scope, String *name)
{
    if (activation)
        activation->__delete__(scope, name);

    if (scope->strictMode)
        __qmljs_throw_type_error(scope);
    return false;
}

void ExecutionContext::pushWithObject(Object *with)
{
    With *w = new With;
    w->next = withObject;
    w->object = with;
    withObject = w;
}

void ExecutionContext::popWithObject()
{
    assert(withObject);

    With *w = withObject;
    withObject = w->next;
    delete w;
}

ExecutionContext *ExecutionContext::outer() const
{
    return function ? function->scope : 0;
}

String **ExecutionContext::formals() const
{
    return function ? function->formalParameterList : 0;
}

unsigned int ExecutionContext::formalCount() const
{
    return function ? function->formalParameterCount : 0;
}

String **ExecutionContext::variables() const
{
    return function ? function->varList : 0;
}

unsigned int ExecutionContext::variableCount() const
{
    return function ? function->varCount : 0;
}

void ExecutionContext::init(ExecutionEngine *eng)
{
    engine = eng;
    parent = 0;
    thisObject = eng->globalObject;

    function = 0;
    arguments = 0;
    argumentCount = 0;
    locals = 0;
    strictMode = false;
    activation = 0;
    withObject = 0;

    eng->exception = Value::undefinedValue();
}

bool ExecutionContext::deleteProperty(String *name)
{
    for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) {
        if (ctx->withObject) {
            ExecutionContext::With *w = ctx->withObject;
            while (w) {
                if (w->object->__hasProperty__(this, name))
                    w->object->__delete__(this, name);
                w = w->next;
            }
        }
        if (ctx->activation) {
            if (ctx->activation->__hasProperty__(this, name))
                ctx->activation->__delete__(this, name);
        }
    }
    if (strictMode)
        throwSyntaxError(0);
    return true;
}

void ExecutionContext::setProperty(String *name, Value value)
{
    for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) {
        if (ctx->withObject) {
            With *w = ctx->withObject;
            while (w) {
                if (w->object->__hasProperty__(ctx, name)) {
                    w->object->__put__(ctx, name, value);
                    return;
                }
                w = w->next;
            }
        }
        if (ctx->setMutableBinding(this, name, value))
            return;
    }
    if (strictMode)
        throwReferenceError(Value::fromString(name));
    engine->globalObject.objectValue()->__put__(this, name, value);
}

Value ExecutionContext::getProperty(String *name)
{
    if (name == engine->id_this)
        return thisObject;

    for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) {
        if (ctx->withObject) {
            With *w = ctx->withObject;
            while (w) {
                if (w->object->__hasProperty__(ctx, name))
                    return w->object->__get__(ctx, name);
                w = w->next;
            }
        }

        for (unsigned int i = 0; i < ctx->variableCount(); ++i)
            if (__qmljs_string_equal(ctx->variables()[i], name))
                return ctx->locals[i];
        for (unsigned int i = 0; i < ctx->formalCount(); ++i)
            if (__qmljs_string_equal(ctx->formals()[i], name))
                return ctx->arguments[i];
        if (ctx->activation && ctx->activation->__hasProperty__(ctx, name))
            return ctx->activation->__get__(ctx, name);
        if (name->isEqualTo(ctx->engine->id_arguments)) {
            Value arguments = Value::fromObject(new (engine->memoryManager) ArgumentsObject(this));
            createMutableBinding(ctx->engine->id_arguments, false);
            setMutableBinding(this, ctx->engine->id_arguments, arguments);
            return arguments;
        }
    }
    throwReferenceError(Value::fromString(name));
    return Value::undefinedValue();
}

Value ExecutionContext::getPropertyNoThrow(String *name)
{
    if (name == engine->id_this)
        return thisObject;

    for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) {
        if (ctx->withObject) {
            With *w = ctx->withObject;
            while (w) {
                if (w->object->__hasProperty__(ctx, name))
                    return w->object->__get__(ctx, name);
                w = w->next;
            }
        }

        for (unsigned int i = 0; i < ctx->variableCount(); ++i)
            if (__qmljs_string_equal(ctx->variables()[i], name))
                return ctx->locals[i];
        for (unsigned int i = 0; i < ctx->formalCount(); ++i)
            if (__qmljs_string_equal(ctx->formals()[i], name))
                return ctx->arguments[i];
        if (ctx->activation && ctx->activation->__hasProperty__(ctx, name))
            return ctx->activation->__get__(ctx, name);
        if (name->isEqualTo(ctx->engine->id_arguments)) {
            Value arguments = Value::fromObject(new (engine->memoryManager) ArgumentsObject(this));
            createMutableBinding(ctx->engine->id_arguments, false);
            setMutableBinding(this, ctx->engine->id_arguments, arguments);
            return arguments;
        }
    }
    return Value::undefinedValue();
}

void ExecutionContext::inplaceBitOp(Value value, String *name, BinOp op)
{
    for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) {
        if (ctx->activation) {
            if (ctx->activation->inplaceBinOp(value, name, op, this))
                return;
        }
    }
    throwReferenceError(Value::fromString(name));
}

void ExecutionContext::throwError(Value value)
{
    __qmljs_builtin_throw(value, this);
}

void ExecutionContext::throwError(const QString &message)
{
    Value v = Value::fromString(this, message);
    throwError(Value::fromObject(engine->newErrorObject(v)));
}

void ExecutionContext::throwSyntaxError(DiagnosticMessage *message)
{
    throwError(Value::fromObject(engine->newSyntaxErrorObject(this, message)));
}

void ExecutionContext::throwTypeError()
{
    throwError(Value::fromObject(engine->newTypeErrorObject(this, QStringLiteral("Type error"))));
}

void ExecutionContext::throwUnimplemented(const QString &message)
{
    Value v = Value::fromString(this, QStringLiteral("Unimplemented ") + message);
    throwError(Value::fromObject(engine->newErrorObject(v)));
}

void ExecutionContext::throwReferenceError(Value value)
{
    String *s = value.toString(this);
    QString msg = s->toQString() + QStringLiteral(" is not defined");
    throwError(Value::fromObject(engine->newReferenceErrorObject(this, msg)));
}

void ExecutionContext::initCallContext(ExecutionContext *parent, const Value that, FunctionObject *f, Value *args, unsigned argc)
{
    MemoryManager::GCBlocker blockGC(parent->engine->memoryManager);

    engine = parent->engine;
    this->parent = parent;

    function = f;
    strictMode = f->strictMode;

    thisObject = that;
    if (!strictMode && !thisObject.isObject()) {
        if (thisObject.isUndefined() || thisObject.isNull())
            thisObject = engine->globalObject;
        else
            thisObject = thisObject.toObject(this);
    }

    arguments = args;
    argumentCount = argc;
    if (function->needsActivation || argc < function->formalParameterCount){
        arguments = new Value[qMax(argc, function->formalParameterCount)];
        if (argc)
            std::copy(args, args + argc, arguments);
        if (argc < function->formalParameterCount)
            std::fill(arguments + argc, arguments + function->formalParameterCount, Value::undefinedValue());
    }
    locals = function->varCount ? new Value[function->varCount] : 0;
    if (function->varCount)
        std::fill(locals, locals + function->varCount, Value::undefinedValue());

    if (function->needsActivation)
        activation = engine->newActivationObject();
    else
        activation = 0;

    withObject = 0;

    if (engine->debugger)
        engine->debugger->aboutToCall(f, this);
}

void ExecutionContext::leaveCallContext()
{
    // ## Should rather be handled by a the activation object having a ref to the environment
    if (activation) {
        delete[] locals;
        locals = 0;
    }
    parent = 0;

    if (engine->debugger)
        engine->debugger->justLeft(this);
}

void ExecutionContext::wireUpPrototype()
{
    assert(thisObject.isObject());

    Value proto = function->__get__(this, engine->id_prototype);
    if (proto.isObject())
        thisObject.objectValue()->prototype = proto.objectValue();
    else
        thisObject.objectValue()->prototype = engine->objectPrototype;
}

} // namespace VM
} // namespace QQmlJS
<commit_msg>Don't allow this as LHS operand<commit_after>/****************************************************************************
**
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the V4VM module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include <QString>
#include "debugging.h"
#include <qmljs_environment.h>
#include <qmljs_objects.h>
#include <qv4ecmaobjects_p.h>
#include "qv4mm.h"

namespace QQmlJS {
namespace VM {

DiagnosticMessage::DiagnosticMessage()
    : offset(0)
    , length(0)
    , startLine(0)
    , startColumn(0)
    , type(0)
    , next(0)
{}

DiagnosticMessage::~DiagnosticMessage()
{
    delete next;
}

String *DiagnosticMessage::buildFullMessage(ExecutionContext *ctx) const
{
    QString msg;
    if (!fileName.isEmpty())
        msg = fileName + QLatin1Char(':');
    msg += QString::number(startLine) + QLatin1Char(':') + QString::number(startColumn) + QLatin1String(": ");
    if (type == QQmlJS::VM::DiagnosticMessage::Error)
        msg += QLatin1String("error");
    else
        msg += QLatin1String("warning");
    msg += ": " + message;

    return ctx->engine->newString(msg);
}

bool ExecutionContext::hasBinding(String *name) const
{
    if (!function)
        return false;

    for (unsigned int i = 0; i < function->varCount; ++i) {
        if (__qmljs_string_equal(function->varList[i], name))
            return true;
    }
    for (unsigned int i = 0; i < function->formalParameterCount; ++i) {
        if (__qmljs_string_equal(function->formalParameterList[i], name))
            return true;
    }
    if (activation)
        return activation->__hasProperty__(this, name);
    return false;
}

void ExecutionContext::createMutableBinding(String *name, bool deletable)
{
    if (!activation)
        activation = engine->newActivationObject();

    if (activation->__hasProperty__(this, name))
        return;
    PropertyDescriptor desc;
    desc.value = Value::undefinedValue();
    desc.type = PropertyDescriptor::Data;
    desc.configurable = deletable ? PropertyDescriptor::Set : PropertyDescriptor::Unset;
    desc.writable = PropertyDescriptor::Set;
    desc.enumberable = PropertyDescriptor::Set;
    activation->__defineOwnProperty__(this, name, &desc);
}

bool ExecutionContext::setMutableBinding(ExecutionContext *scope, String *name, Value value)
{
    // ### throw if scope->strict is true, and it would change an immutable binding
    for (unsigned int i = 0; i < variableCount(); ++i) {
        if (__qmljs_string_equal(variables()[i], name)) {
            locals[i] = value;
            return true;
        }
    }
    for (unsigned int i = 0; i < formalCount(); ++i) {
        if (__qmljs_string_equal(formals()[i], name)) {
            arguments[i] = value;
            return true;
        }
    }
    if (activation && activation->__hasProperty__(scope, name)) {
        activation->__put__(scope, name, value);
        return true;
    }

    return false;
}

Value ExecutionContext::getBindingValue(ExecutionContext *scope, String *name, bool strict) const
{
    Q_UNUSED(strict);
    assert(function);

    for (unsigned int i = 0; i < variableCount(); ++i) {
        if (__qmljs_string_equal(variables()[i], name))
            return locals[i];
    }
    for (unsigned int i = 0; i < formalCount(); ++i) {
        if (__qmljs_string_equal(formals()[i], name))
            return arguments[i];
    }
    if (activation && activation->__hasProperty__(this, name))
        return activation->__get__(scope, name);
    assert(false);
}

bool ExecutionContext::deleteBinding(ExecutionContext *scope, String *name)
{
    if (activation)
        activation->__delete__(scope, name);

    if (scope->strictMode)
        __qmljs_throw_type_error(scope);
    return false;
}

void ExecutionContext::pushWithObject(Object *with)
{
    With *w = new With;
    w->next = withObject;
    w->object = with;
    withObject = w;
}

void ExecutionContext::popWithObject()
{
    assert(withObject);

    With *w = withObject;
    withObject = w->next;
    delete w;
}

ExecutionContext *ExecutionContext::outer() const
{
    return function ? function->scope : 0;
}

String **ExecutionContext::formals() const
{
    return function ? function->formalParameterList : 0;
}

unsigned int ExecutionContext::formalCount() const
{
    return function ? function->formalParameterCount : 0;
}

String **ExecutionContext::variables() const
{
    return function ? function->varList : 0;
}

unsigned int ExecutionContext::variableCount() const
{
    return function ? function->varCount : 0;
}

void ExecutionContext::init(ExecutionEngine *eng)
{
    engine = eng;
    parent = 0;
    thisObject = eng->globalObject;

    function = 0;
    arguments = 0;
    argumentCount = 0;
    locals = 0;
    strictMode = false;
    activation = 0;
    withObject = 0;

    eng->exception = Value::undefinedValue();
}

bool ExecutionContext::deleteProperty(String *name)
{
    for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) {
        if (ctx->withObject) {
            ExecutionContext::With *w = ctx->withObject;
            while (w) {
                if (w->object->__hasProperty__(this, name))
                    w->object->__delete__(this, name);
                w = w->next;
            }
        }
        if (ctx->activation) {
            if (ctx->activation->__hasProperty__(this, name))
                ctx->activation->__delete__(this, name);
        }
    }
    if (strictMode)
        throwSyntaxError(0);
    return true;
}

void ExecutionContext::setProperty(String *name, Value value)
{
    for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) {
        if (ctx->withObject) {
            With *w = ctx->withObject;
            while (w) {
                if (w->object->__hasProperty__(ctx, name)) {
                    w->object->__put__(ctx, name, value);
                    return;
                }
                w = w->next;
            }
        }
        if (ctx->setMutableBinding(this, name, value))
            return;
    }
    if (strictMode || name == engine->id_this)
        throwReferenceError(Value::fromString(name));
    engine->globalObject.objectValue()->__put__(this, name, value);
}

Value ExecutionContext::getProperty(String *name)
{
    if (name == engine->id_this)
        return thisObject;

    for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) {
        if (ctx->withObject) {
            With *w = ctx->withObject;
            while (w) {
                if (w->object->__hasProperty__(ctx, name))
                    return w->object->__get__(ctx, name);
                w = w->next;
            }
        }

        for (unsigned int i = 0; i < ctx->variableCount(); ++i)
            if (__qmljs_string_equal(ctx->variables()[i], name))
                return ctx->locals[i];
        for (unsigned int i = 0; i < ctx->formalCount(); ++i)
            if (__qmljs_string_equal(ctx->formals()[i], name))
                return ctx->arguments[i];
        if (ctx->activation && ctx->activation->__hasProperty__(ctx, name))
            return ctx->activation->__get__(ctx, name);
        if (name->isEqualTo(ctx->engine->id_arguments)) {
            Value arguments = Value::fromObject(new (engine->memoryManager) ArgumentsObject(this));
            createMutableBinding(ctx->engine->id_arguments, false);
            setMutableBinding(this, ctx->engine->id_arguments, arguments);
            return arguments;
        }
    }
    throwReferenceError(Value::fromString(name));
    return Value::undefinedValue();
}

Value ExecutionContext::getPropertyNoThrow(String *name)
{
    if (name == engine->id_this)
        return thisObject;

    for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) {
        if (ctx->withObject) {
            With *w = ctx->withObject;
            while (w) {
                if (w->object->__hasProperty__(ctx, name))
                    return w->object->__get__(ctx, name);
                w = w->next;
            }
        }

        for (unsigned int i = 0; i < ctx->variableCount(); ++i)
            if (__qmljs_string_equal(ctx->variables()[i], name))
                return ctx->locals[i];
        for (unsigned int i = 0; i < ctx->formalCount(); ++i)
            if (__qmljs_string_equal(ctx->formals()[i], name))
                return ctx->arguments[i];
        if (ctx->activation && ctx->activation->__hasProperty__(ctx, name))
            return ctx->activation->__get__(ctx, name);
        if (name->isEqualTo(ctx->engine->id_arguments)) {
            Value arguments = Value::fromObject(new (engine->memoryManager) ArgumentsObject(this));
            createMutableBinding(ctx->engine->id_arguments, false);
            setMutableBinding(this, ctx->engine->id_arguments, arguments);
            return arguments;
        }
    }
    return Value::undefinedValue();
}

void ExecutionContext::inplaceBitOp(Value value, String *name, BinOp op)
{
    for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) {
        if (ctx->activation) {
            if (ctx->activation->inplaceBinOp(value, name, op, this))
                return;
        }
    }
    throwReferenceError(Value::fromString(name));
}

void ExecutionContext::throwError(Value value)
{
    __qmljs_builtin_throw(value, this);
}

void ExecutionContext::throwError(const QString &message)
{
    Value v = Value::fromString(this, message);
    throwError(Value::fromObject(engine->newErrorObject(v)));
}

void ExecutionContext::throwSyntaxError(DiagnosticMessage *message)
{
    throwError(Value::fromObject(engine->newSyntaxErrorObject(this, message)));
}

void ExecutionContext::throwTypeError()
{
    throwError(Value::fromObject(engine->newTypeErrorObject(this, QStringLiteral("Type error"))));
}

void ExecutionContext::throwUnimplemented(const QString &message)
{
    Value v = Value::fromString(this, QStringLiteral("Unimplemented ") + message);
    throwError(Value::fromObject(engine->newErrorObject(v)));
}

void ExecutionContext::throwReferenceError(Value value)
{
    String *s = value.toString(this);
    QString msg = s->toQString() + QStringLiteral(" is not defined");
    throwError(Value::fromObject(engine->newReferenceErrorObject(this, msg)));
}

void ExecutionContext::initCallContext(ExecutionContext *parent, const Value that, FunctionObject *f, Value *args, unsigned argc)
{
    MemoryManager::GCBlocker blockGC(parent->engine->memoryManager);

    engine = parent->engine;
    this->parent = parent;

    function = f;
    strictMode = f->strictMode;

    thisObject = that;
    if (!strictMode && !thisObject.isObject()) {
        if (thisObject.isUndefined() || thisObject.isNull())
            thisObject = engine->globalObject;
        else
            thisObject = thisObject.toObject(this);
    }

    arguments = args;
    argumentCount = argc;
    if (function->needsActivation || argc < function->formalParameterCount){
        arguments = new Value[qMax(argc, function->formalParameterCount)];
        if (argc)
            std::copy(args, args + argc, arguments);
        if (argc < function->formalParameterCount)
            std::fill(arguments + argc, arguments + function->formalParameterCount, Value::undefinedValue());
    }
    locals = function->varCount ? new Value[function->varCount] : 0;
    if (function->varCount)
        std::fill(locals, locals + function->varCount, Value::undefinedValue());

    if (function->needsActivation)
        activation = engine->newActivationObject();
    else
        activation = 0;

    withObject = 0;

    if (engine->debugger)
        engine->debugger->aboutToCall(f, this);
}

void ExecutionContext::leaveCallContext()
{
    // ## Should rather be handled by a the activation object having a ref to the environment
    if (activation) {
        delete[] locals;
        locals = 0;
    }
    parent = 0;

    if (engine->debugger)
        engine->debugger->justLeft(this);
}

void ExecutionContext::wireUpPrototype()
{
    assert(thisObject.isObject());

    Value proto = function->__get__(this, engine->id_prototype);
    if (proto.isObject())
        thisObject.objectValue()->prototype = proto.objectValue();
    else
        thisObject.objectValue()->prototype = engine->objectPrototype;
}

} // namespace VM
} // namespace QQmlJS
<|endoftext|>


<commit_before>#include "module.h"

#include "resolve.h"
#include "environment.h"

#include <QDebug>
#include <QFile>
#include <QFileInfo>
#include <QJSValueIterator>
#include <QQmlEngine>

static QJSValue bindMethod(const QJSValue &obj, const char *name)
{
    auto method = obj.property(name);
    return method.property("bind").callWithInstance(method, {obj});
}

Module::Module(Environment *environment, const QString &fileName, QObject *parent) :
    QObject(parent),
    mEnvironment(environment),
    mExports(environment->engine()->newObject()),
    mFileName(fileName)
{
}

Module::~Module()
{
}

void Module::setExports(const QJSValue &exports)
{
    mExports = exports;
}

QString Module::resolveRequire(const QString &path) const
{
    auto file = resolve::require(path, mFileName, mEnvironment->builtins().keys());
    if (file.isEmpty()) {
        // TODO: throw errors (impossible with Qt public API only?)
        qWarning() << "cannot load module" << path << "from" << mFileName;
    }
    return file;
}

QJSValue Module::require(const QString &path)
{
    auto fileName = resolveRequire(path);
    if (fileName.isEmpty()) {
        return QJSValue();
    }

    auto module = mEnvironment->modules()->value(fileName, nullptr);

    if (!module) {
        module = new Module(mEnvironment, fileName, this);

        module->mParentModule = this;
        mChildModules << module;

        module->load();
    }

    return module->exports();
}

void Module::load()
{
    QFile file(mFileName);

    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qWarning() << "cannot open file: " << mFileName;
        return;
    }

    auto header =  "(function (module, exports, require, __filename, __dirname, process, Buffer) {";
    auto content = QString::fromUtf8(file.readAll());
    auto footer = "})";

    auto scope = mEnvironment->engine()->evaluate(header + content + footer, mFileName, 0);

    auto module = mEnvironment->engine()->newQObject(this);
    auto require = bindMethod(module, "require");

    auto fileName = mFileName;
    auto dirName = QFileInfo(fileName).absolutePath();

    auto process = mEnvironment->builtins()["__process"];
    auto bufferClass = mEnvironment->builtins()["__Buffer"];

    mEnvironment->modules()->insert(mFileName, this);

    scope.call({module, exports(), require, fileName, dirName, process, bufferClass});
}
<commit_msg>Linebreak after module header and before footer<commit_after>#include "module.h"

#include "resolve.h"
#include "environment.h"

#include <QDebug>
#include <QFile>
#include <QFileInfo>
#include <QJSValueIterator>
#include <QQmlEngine>

static QJSValue bindMethod(const QJSValue &obj, const char *name)
{
    auto method = obj.property(name);
    return method.property("bind").callWithInstance(method, {obj});
}

Module::Module(Environment *environment, const QString &fileName, QObject *parent) :
    QObject(parent),
    mEnvironment(environment),
    mExports(environment->engine()->newObject()),
    mFileName(fileName)
{
}

Module::~Module()
{
}

void Module::setExports(const QJSValue &exports)
{
    mExports = exports;
}

QString Module::resolveRequire(const QString &path) const
{
    auto file = resolve::require(path, mFileName, mEnvironment->builtins().keys());
    if (file.isEmpty()) {
        // TODO: throw errors (impossible with Qt public API only?)
        qWarning() << "cannot load module" << path << "from" << mFileName;
    }
    return file;
}

QJSValue Module::require(const QString &path)
{
    auto fileName = resolveRequire(path);
    if (fileName.isEmpty()) {
        return QJSValue();
    }

    auto module = mEnvironment->modules()->value(fileName, nullptr);

    if (!module) {
        module = new Module(mEnvironment, fileName, this);

        module->mParentModule = this;
        mChildModules << module;

        module->load();
    }

    return module->exports();
}

void Module::load()
{
    QFile file(mFileName);

    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qWarning() << "cannot open file: " << mFileName;
        return;
    }

    auto header =  "(function (module, exports, require, __filename, __dirname, process, Buffer) {\n";
    auto content = QString::fromUtf8(file.readAll());
    auto footer = "\n})";

    auto scope = mEnvironment->engine()->evaluate(header + content + footer, mFileName, 0);

    auto module = mEnvironment->engine()->newQObject(this);
    auto require = bindMethod(module, "require");

    auto fileName = mFileName;
    auto dirName = QFileInfo(fileName).absolutePath();

    auto process = mEnvironment->builtins()["__process"];
    auto bufferClass = mEnvironment->builtins()["__Buffer"];

    mEnvironment->modules()->insert(mFileName, this);

    scope.call({module, exports(), require, fileName, dirName, process, bufferClass});
}
<|endoftext|>


<commit_before>/****************************************************************************

 This file is part of the GLC-lib library.
 Copyright (C) 2005-2008 Laurent Ribon (laumaya@users.sourceforge.net)
 Version 2.0.0, packaged on July 2010.

 http://glc-lib.sourceforge.net

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

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

 You should have received a copy of the GNU Lesser General Public License
 along with GLC-lib; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

 *****************************************************************************/

#include "glc_cachemanager.h"
#include <QtDebug>

GLC_CacheManager::GLC_CacheManager(const QString& path)
: m_Dir()
, m_UseCompression(true)
, m_CompressionLevel(-1)
{
	if (! path.isEmpty())
	{
		QFileInfo pathInfo(path);
		if (pathInfo.isDir() && pathInfo.isReadable())
		{
			m_Dir.setPath(path);
		}
	}
}

// Copy constructor
GLC_CacheManager::GLC_CacheManager(const GLC_CacheManager& cacheManager)
:m_Dir(cacheManager.m_Dir)
, m_UseCompression(cacheManager.m_UseCompression)
, m_CompressionLevel(cacheManager.m_CompressionLevel)
{

}

// Assignement operator
GLC_CacheManager& GLC_CacheManager::operator=(const GLC_CacheManager& cacheManager)
{
	m_Dir= cacheManager.m_Dir;
	m_UseCompression= cacheManager.m_UseCompression;
	m_CompressionLevel= cacheManager.m_CompressionLevel;

	return *this;
}

GLC_CacheManager::~GLC_CacheManager()
{

}

//////////////////////////////////////////////////////////////////////
// Get Functions
//////////////////////////////////////////////////////////////////////

// Return true if the cache is is readable
bool GLC_CacheManager::isReadable() const
{
	bool isReadable= true;
	isReadable= isReadable && m_Dir.exists();
	QFileInfo dirInfo(m_Dir.absolutePath());
	isReadable= isReadable && dirInfo.isReadable();

	return isReadable;

}

// Return true if the cache is writable
bool GLC_CacheManager::isWritable() const
{
	bool isWritable= true;
	isWritable= isWritable && m_Dir.exists();
	QFileInfo dirInfo(m_Dir.absolutePath());
	isWritable= isWritable && dirInfo.isWritable();

	return isWritable;
}

// Return True if the specified file is cashed in the specified context
bool GLC_CacheManager::isCashed(const QString& context, const QString& fileName) const
{
	if (! isReadable()) return false;

	QFileInfo fileInfo(m_Dir.absolutePath() + QDir::separator() + context + QDir::separator() + fileName + '.' + GLC_BSRep::suffix());
	return fileInfo.exists();
}

// Return True if the cached file is usable
bool GLC_CacheManager::isUsable(const QDateTime& timeStamp, const QString& context, const QString& fileName) const
{
	bool result= isCashed(context, fileName);

	if (result)
	{
		QFileInfo cacheFileInfo(m_Dir.absolutePath() + QDir::separator() + context + QDir::separator() + fileName+ '.' + GLC_BSRep::suffix());
		//result= result && (timeStamp == cacheFileInfo.lastModified());
		result= result && cacheFileInfo.isReadable();
		if (result)
		{
			GLC_BSRep binaryRep;
			binaryRep.setAbsoluteFileName(cacheFileInfo.absoluteFilePath());
			result= result && binaryRep.repIsUpToDate(timeStamp);
		}
	}

	return result;
}

// Return the binary serialized representation of the specified file
GLC_BSRep GLC_CacheManager::binary3DRep(const QString& context, const QString& fileName) const
{
	const QString absoluteFileName(m_Dir.absolutePath() + QDir::separator() + context + QDir::separator() + fileName + '.' + GLC_BSRep::suffix());
	GLC_BSRep binaryRep(absoluteFileName);

	return binaryRep;
}

// Add the specified file in the cache
bool GLC_CacheManager::addToCache(const QString& context, const GLC_3DRep& rep)
{
	Q_ASSERT(!rep.fileName().isEmpty());
	bool addedToCache= isWritable();
	if (addedToCache)
	{
		QFileInfo contextCacheInfo(m_Dir.absolutePath() + QDir::separator() + context);
		if (! contextCacheInfo.exists())
		{
			addedToCache= m_Dir.mkdir(context);
		}
		if (addedToCache)
		{
			QString repFileName= rep.fileName();
			if (glc::isArchiveString(repFileName))
			{
				repFileName= glc::archiveEntryFileName(repFileName);
			}
			else
			{
				repFileName= QFileInfo(repFileName).fileName();
			}
			const QString binaryFileName= contextCacheInfo.filePath() + QDir::separator() + repFileName;
			GLC_BSRep binariRep(binaryFileName, m_UseCompression);
			binariRep.setCompressionLevel(m_CompressionLevel);
			addedToCache= binariRep.save(rep);
		}
	}

	return addedToCache;
}

//////////////////////////////////////////////////////////////////////
//Set Functions
//////////////////////////////////////////////////////////////////////

// Set the cache file path
bool GLC_CacheManager::setCachePath(const QString& path)
{
	QFileInfo pathInfo(path);
	bool result= pathInfo.isDir();
	result= result && pathInfo.isReadable();

	if (result)
	{
		m_Dir.setPath(path);
	}
	return result;
}

<commit_msg>refactoring<commit_after>/****************************************************************************

 This file is part of the GLC-lib library.
 Copyright (C) 2005-2008 Laurent Ribon (laumaya@users.sourceforge.net)
 Version 2.0.0, packaged on July 2010.

 http://glc-lib.sourceforge.net

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

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

 You should have received a copy of the GNU Lesser General Public License
 along with GLC-lib; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

 *****************************************************************************/

#include "glc_cachemanager.h"
#include <QtDebug>

GLC_CacheManager::GLC_CacheManager(const QString& path)
: m_Dir()
, m_UseCompression(true)
, m_CompressionLevel(-1)
{
	if (! path.isEmpty())
	{
		QFileInfo pathInfo(path);
		if (pathInfo.isDir() && pathInfo.isReadable())
		{
			m_Dir.setPath(path);
		}
	}
}

// Copy constructor
GLC_CacheManager::GLC_CacheManager(const GLC_CacheManager& cacheManager)
:m_Dir(cacheManager.m_Dir)
, m_UseCompression(cacheManager.m_UseCompression)
, m_CompressionLevel(cacheManager.m_CompressionLevel)
{

}

// Assignement operator
GLC_CacheManager& GLC_CacheManager::operator=(const GLC_CacheManager& cacheManager)
{
	m_Dir= cacheManager.m_Dir;
	m_UseCompression= cacheManager.m_UseCompression;
	m_CompressionLevel= cacheManager.m_CompressionLevel;

	return *this;
}

GLC_CacheManager::~GLC_CacheManager()
{

}

//////////////////////////////////////////////////////////////////////
// Get Functions
//////////////////////////////////////////////////////////////////////

// Return true if the cache is is readable
bool GLC_CacheManager::isReadable() const
{
	bool isReadable= true;
	isReadable= isReadable && m_Dir.exists();
	QFileInfo dirInfo(m_Dir.absolutePath());
	isReadable= isReadable && dirInfo.isReadable();

	return isReadable;

}

// Return true if the cache is writable
bool GLC_CacheManager::isWritable() const
{
	bool isWritable= true;
	isWritable= isWritable && m_Dir.exists();
	QFileInfo dirInfo(m_Dir.absolutePath());
	isWritable= isWritable && dirInfo.isWritable();

	return isWritable;
}

// Return True if the specified file is cashed in the specified context
bool GLC_CacheManager::isCashed(const QString& context, const QString& fileName) const
{
	if (! isReadable()) return false;

	QFileInfo fileInfo(m_Dir.absolutePath() + QDir::separator() + context + QDir::separator() + fileName + '.' + GLC_BSRep::suffix());
	return fileInfo.exists();
}

// Return True if the cached file is usable
bool GLC_CacheManager::isUsable(const QDateTime& timeStamp, const QString& context, const QString& fileName) const
{
	bool result= isCashed(context, fileName);

	if (result)
	{
		QFileInfo cacheFileInfo(m_Dir.absolutePath() + QDir::separator() + context + QDir::separator() + fileName+ '.' + GLC_BSRep::suffix());
		//result= result && (timeStamp == cacheFileInfo.lastModified());
		result= result && cacheFileInfo.isReadable();
		if (result)
		{
			GLC_BSRep binaryRep;
			binaryRep.setAbsoluteFileName(cacheFileInfo.absoluteFilePath());
			result= result && binaryRep.isUsable(timeStamp);
		}
	}

	return result;
}

// Return the binary serialized representation of the specified file
GLC_BSRep GLC_CacheManager::binary3DRep(const QString& context, const QString& fileName) const
{
	const QString absoluteFileName(m_Dir.absolutePath() + QDir::separator() + context + QDir::separator() + fileName + '.' + GLC_BSRep::suffix());
	GLC_BSRep binaryRep(absoluteFileName);

	return binaryRep;
}

// Add the specified file in the cache
bool GLC_CacheManager::addToCache(const QString& context, const GLC_3DRep& rep)
{
	Q_ASSERT(!rep.fileName().isEmpty());
	bool addedToCache= isWritable();
	if (addedToCache)
	{
		QFileInfo contextCacheInfo(m_Dir.absolutePath() + QDir::separator() + context);
		if (! contextCacheInfo.exists())
		{
			addedToCache= m_Dir.mkdir(context);
		}
		if (addedToCache)
		{
			QString repFileName= rep.fileName();
			if (glc::isArchiveString(repFileName))
			{
				repFileName= glc::archiveEntryFileName(repFileName);
			}
			else
			{
				repFileName= QFileInfo(repFileName).fileName();
			}
			const QString binaryFileName= contextCacheInfo.filePath() + QDir::separator() + repFileName;
			GLC_BSRep binariRep(binaryFileName, m_UseCompression);
			binariRep.setCompressionLevel(m_CompressionLevel);
			addedToCache= binariRep.save(rep);
		}
	}

	return addedToCache;
}

//////////////////////////////////////////////////////////////////////
//Set Functions
//////////////////////////////////////////////////////////////////////

// Set the cache file path
bool GLC_CacheManager::setCachePath(const QString& path)
{
	QFileInfo pathInfo(path);
	bool result= pathInfo.isDir();
	result= result && pathInfo.isReadable();

	if (result)
	{
		m_Dir.setPath(path);
	}
	return result;
}

<|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.h"
#include "SkCanvas.h"
#include "SkRRect.h"
#include "SkPath.h"

class ScaledStrokesGM : public skiagm::GM {
public:
    ScaledStrokesGM() {}

protected:

    SkString onShortName() override {
        return SkString("scaledstrokes");
    }

    SkISize onISize() override {
        return SkISize::Make(640, 320);
    }

    static void draw_path(SkScalar size, SkCanvas* canvas, SkPaint paint) {
        SkScalar c = 0.551915024494f * size;
        SkPath path;
        path.moveTo(0.0f, size);
        path.cubicTo(c, size, size, c, size, 0.0f);
        path.cubicTo(size, -c, c, -size, 0.0f, -size);
        path.cubicTo(-c, -size, -size, -c, -size, 0.0f);
        path.cubicTo(-size, c, -c, size, 0.0f, size);
        canvas->drawPath(path, paint);
    }

    void onDraw(SkCanvas* canvas) override {
        SkPaint paint;
        SkPath path;
        paint.setStyle(SkPaint::Style::kStroke_Style);
        canvas->translate(5.0f, 5.0f);
        const SkScalar size = 60.0f;
        for (int i = 0; i < 2; i++) {
            paint.setAntiAlias(i == 1);
            canvas->save();
            for (int j = 0; j < 4; j++) {
                SkScalar scale = 4.0f - j;
                paint.setStrokeWidth(4.0f / scale);
                canvas->save();
                canvas->translate(size / 2.0f, size / 2.0f);
                canvas->scale(scale, scale);
                draw_path(size / 2.0f / scale, canvas, paint);
                canvas->restore();

                canvas->save();
                canvas->translate(size / 2.0f, 80.0f + size / 2.0f);
                canvas->scale(scale, scale);
                canvas->drawCircle(0.0f, 0.0f, size / 2.0f / scale, paint);
                canvas->restore();

                canvas->save();
                canvas->translate(0.0f, 160.0f);
                canvas->scale(scale, scale);
                canvas->drawRect(SkRect::MakeXYWH(0.0f, 0.0f, size / scale, size / scale), paint);
                canvas->restore();

                canvas->save();
                canvas->translate(0.0f, 240.0f);
                canvas->scale(scale, scale);
                canvas->drawLine(0.0f, 0.0f, size / scale, size / scale, paint);
                canvas->restore();

                canvas->translate(80.0f, 0.0f);
            }
            canvas->restore();
        }

    }

private:
    typedef GM INHERITED;
};

DEF_GM( return new ScaledStrokesGM; )
<commit_msg>Fix scaledstrokes GM<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.h"
#include "SkCanvas.h"
#include "SkRRect.h"
#include "SkPath.h"

class ScaledStrokesGM : public skiagm::GM {
public:
    ScaledStrokesGM() {}

protected:

    SkString onShortName() override {
        return SkString("scaledstrokes");
    }

    SkISize onISize() override {
        return SkISize::Make(640, 320);
    }

    static void draw_path(SkScalar size, SkCanvas* canvas, SkPaint paint) {
        SkScalar c = 0.551915024494f * size;
        SkPath path;
        path.moveTo(0.0f, size);
        path.cubicTo(c, size, size, c, size, 0.0f);
        path.cubicTo(size, -c, c, -size, 0.0f, -size);
        path.cubicTo(-c, -size, -size, -c, -size, 0.0f);
        path.cubicTo(-size, c, -c, size, 0.0f, size);
        canvas->drawPath(path, paint);
    }

    void onDraw(SkCanvas* canvas) override {
        SkPaint paint;
        SkPath path;
        paint.setStyle(SkPaint::Style::kStroke_Style);
        canvas->translate(5.0f, 5.0f);
        const SkScalar size = 60.0f;
        for (int i = 0; i < 2; i++) {
            paint.setAntiAlias(i == 1);
            for (int j = 0; j < 4; j++) {
                SkScalar scale = 4.0f - j;
                paint.setStrokeWidth(4.0f / scale);
                canvas->save();
                canvas->translate(size / 2.0f, size / 2.0f);
                canvas->scale(scale, scale);
                draw_path(size / 2.0f / scale, canvas, paint);
                canvas->restore();

                canvas->save();
                canvas->translate(size / 2.0f, 80.0f + size / 2.0f);
                canvas->scale(scale, scale);
                canvas->drawCircle(0.0f, 0.0f, size / 2.0f / scale, paint);
                canvas->restore();

                canvas->save();
                canvas->translate(0.0f, 160.0f);
                canvas->scale(scale, scale);
                canvas->drawRect(SkRect::MakeXYWH(0.0f, 0.0f, size / scale, size / scale), paint);
                canvas->restore();

                canvas->save();
                canvas->translate(0.0f, 240.0f);
                canvas->scale(scale, scale);
                canvas->drawLine(0.0f, 0.0f, size / scale, size / scale, paint);
                canvas->restore();

                canvas->translate(80.0f, 0.0f);
            }
        }

    }

private:
    typedef GM INHERITED;
};

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


<commit_before>#include <gmock/gmock.h>
#include <gtest/gtest.h>

#include <rapidjson/document.h>

#include <blackhole/attributes.hpp>
#include <blackhole/cpp17/string_view.hpp>
#include <blackhole/extensions/writer.hpp>
#include <blackhole/formatter/json.hpp>
#include <blackhole/record.hpp>

namespace blackhole {
namespace testing {
namespace formatter {

using ::blackhole::formatter::json_t;
using ::blackhole::formatter::routing_t;

TEST(json_t, FormatMessage) {
    json_t formatter;

    const string_view message("value");
    const attribute_pack pack;
    record_t record(0, message, pack);
    writer_t writer;
    formatter.format(record, writer);

    rapidjson::Document doc;
    doc.Parse<0>(writer.result().to_string().c_str());
    ASSERT_TRUE(doc.HasMember("message"));
    ASSERT_TRUE(doc["message"].IsString());
    EXPECT_STREQ("value", doc["message"].GetString());
}

TEST(json_t, FormatSeverity) {
    json_t formatter;

    const string_view message("value");
    const attribute_pack pack;
    record_t record(4, message, pack);
    writer_t writer;
    formatter.format(record, writer);

    rapidjson::Document doc;
    doc.Parse<0>(writer.result().to_string().c_str());

    ASSERT_TRUE(doc.HasMember("severity"));
    ASSERT_TRUE(doc["severity"].IsInt());
    EXPECT_EQ(4, doc["severity"].GetInt());
}

TEST(json_t, FormatTimestamp) {
    json_t formatter;

    const string_view message("value");
    const attribute_pack pack;
    record_t record(4, message, pack);
    record.activate();
    writer_t writer;
    formatter.format(record, writer);

    rapidjson::Document doc;
    doc.Parse<0>(writer.result().to_string().c_str());

    ASSERT_TRUE(doc.HasMember("timestamp"));
    ASSERT_TRUE(doc["timestamp"].IsUint64());
    EXPECT_TRUE(doc["timestamp"].GetUint64() > 0);
}

TEST(json_t, FormatAttribute) {
    json_t formatter;

    const string_view message("value");
    const attribute_list attributes{{"counter", 42}};
    const attribute_pack pack{attributes};
    record_t record(0, message, pack);
    writer_t writer;
    formatter.format(record, writer);

    rapidjson::Document doc;
    doc.Parse<0>(writer.result().to_string().c_str());
    ASSERT_TRUE(doc.HasMember("message"));
    ASSERT_TRUE(doc["message"].IsString());
    EXPECT_STREQ("value", doc["message"].GetString());

    ASSERT_TRUE(doc.HasMember("counter"));
    ASSERT_TRUE(doc["counter"].IsInt());
    EXPECT_EQ(42, doc["counter"].GetInt());
}

TEST(json_t, FormatAttributeNull) {
    json_t formatter;

    const string_view message("value");
    const attribute_list attributes{{"endpoint", nullptr}};
    const attribute_pack pack{attributes};
    record_t record(0, message, pack);
    writer_t writer;
    formatter.format(record, writer);

    rapidjson::Document doc;
    doc.Parse<0>(writer.result().to_string().c_str());
    ASSERT_TRUE(doc.HasMember("endpoint"));
    ASSERT_TRUE(doc["endpoint"].IsNull());
}

TEST(json_t, FormatAttributeBool) {
    json_t formatter;

    const string_view message("value");
    const attribute_list attributes{{"available", true}};
    const attribute_pack pack{attributes};
    record_t record(0, message, pack);
    writer_t writer;
    formatter.format(record, writer);

    rapidjson::Document doc;
    doc.Parse<0>(writer.result().to_string().c_str());
    ASSERT_TRUE(doc.HasMember("available"));
    ASSERT_TRUE(doc["available"].IsBool());
    EXPECT_TRUE(doc["available"].GetBool());
}

TEST(json_t, FormatAttributeDouble) {
    json_t formatter;

    const string_view message("value");
    const attribute_list attributes{{"pi", 3.1415}};
    const attribute_pack pack{attributes};
    record_t record(0, message, pack);
    writer_t writer;
    formatter.format(record, writer);

    rapidjson::Document doc;
    doc.Parse<0>(writer.result().to_string().c_str());
    ASSERT_TRUE(doc.HasMember("pi"));
    ASSERT_TRUE(doc["pi"].IsDouble());
    EXPECT_DOUBLE_EQ(3.1415, doc["pi"].GetDouble());
}

TEST(json_t, FormatAttributeString) {
    json_t formatter;

    const string_view message("value");
    const attribute_list attributes{{"endpoint", "127.0.0.1:8080"}};
    const attribute_pack pack{attributes};
    record_t record(0, message, pack);
    writer_t writer;
    formatter.format(record, writer);

    rapidjson::Document doc;
    doc.Parse<0>(writer.result().to_string().c_str());
    ASSERT_TRUE(doc.HasMember("message"));
    ASSERT_TRUE(doc["message"].IsString());
    EXPECT_STREQ("value", doc["message"].GetString());

    ASSERT_TRUE(doc.HasMember("endpoint"));
    ASSERT_TRUE(doc["endpoint"].IsString());
    EXPECT_STREQ("127.0.0.1:8080", doc["endpoint"].GetString());
}

TEST(json_t, FormatMessageWithRouting) {
    json_t formatter(routing_t().spec("/fields", {"message"}));

    const string_view message("value");
    const attribute_pack pack;
    record_t record(0, message, pack);
    writer_t writer;
    formatter.format(record, writer);

    rapidjson::Document doc;
    doc.Parse<0>(writer.result().to_string().c_str());
    ASSERT_TRUE(doc.HasMember("fields"));
    ASSERT_TRUE(doc["fields"].HasMember("message"));
    ASSERT_TRUE(doc["fields"]["message"].IsString());
    EXPECT_STREQ("value", doc["fields"]["message"].GetString());
}

TEST(json_t, FormatAttributeStringWithRouting) {
    json_t formatter(routing_t().spec("/fields", {"endpoint"}));

    const string_view message("value");
    const attribute_list attributes{{"endpoint", "127.0.0.1:8080"}};
    const attribute_pack pack{attributes};
    record_t record(0, message, pack);
    writer_t writer;
    formatter.format(record, writer);

    rapidjson::Document doc;
    doc.Parse<0>(writer.result().to_string().c_str());
    ASSERT_TRUE(doc.HasMember("fields"));
    ASSERT_TRUE(doc["fields"].HasMember("endpoint"));
    ASSERT_TRUE(doc["fields"]["endpoint"].IsString());
    EXPECT_STREQ("127.0.0.1:8080", doc["fields"]["endpoint"].GetString());
}

}  // namespace formatter
}  // namespace testing
}  // namespace blackhole
<commit_msg>chore(tests): check nested routing<commit_after>#include <gmock/gmock.h>
#include <gtest/gtest.h>

#include <rapidjson/document.h>

#include <blackhole/attributes.hpp>
#include <blackhole/cpp17/string_view.hpp>
#include <blackhole/extensions/writer.hpp>
#include <blackhole/formatter/json.hpp>
#include <blackhole/record.hpp>

namespace blackhole {
namespace testing {
namespace formatter {

using ::blackhole::formatter::json_t;
using ::blackhole::formatter::routing_t;

TEST(json_t, FormatMessage) {
    json_t formatter;

    const string_view message("value");
    const attribute_pack pack;
    record_t record(0, message, pack);
    writer_t writer;
    formatter.format(record, writer);

    rapidjson::Document doc;
    doc.Parse<0>(writer.result().to_string().c_str());
    ASSERT_TRUE(doc.HasMember("message"));
    ASSERT_TRUE(doc["message"].IsString());
    EXPECT_STREQ("value", doc["message"].GetString());
}

TEST(json_t, FormatSeverity) {
    json_t formatter;

    const string_view message("value");
    const attribute_pack pack;
    record_t record(4, message, pack);
    writer_t writer;
    formatter.format(record, writer);

    rapidjson::Document doc;
    doc.Parse<0>(writer.result().to_string().c_str());

    ASSERT_TRUE(doc.HasMember("severity"));
    ASSERT_TRUE(doc["severity"].IsInt());
    EXPECT_EQ(4, doc["severity"].GetInt());
}

TEST(json_t, FormatTimestamp) {
    json_t formatter;

    const string_view message("value");
    const attribute_pack pack;
    record_t record(4, message, pack);
    record.activate();
    writer_t writer;
    formatter.format(record, writer);

    rapidjson::Document doc;
    doc.Parse<0>(writer.result().to_string().c_str());

    ASSERT_TRUE(doc.HasMember("timestamp"));
    ASSERT_TRUE(doc["timestamp"].IsUint64());
    EXPECT_TRUE(doc["timestamp"].GetUint64() > 0);
}

TEST(json_t, FormatAttribute) {
    json_t formatter;

    const string_view message("value");
    const attribute_list attributes{{"counter", 42}};
    const attribute_pack pack{attributes};
    record_t record(0, message, pack);
    writer_t writer;
    formatter.format(record, writer);

    rapidjson::Document doc;
    doc.Parse<0>(writer.result().to_string().c_str());
    ASSERT_TRUE(doc.HasMember("message"));
    ASSERT_TRUE(doc["message"].IsString());
    EXPECT_STREQ("value", doc["message"].GetString());

    ASSERT_TRUE(doc.HasMember("counter"));
    ASSERT_TRUE(doc["counter"].IsInt());
    EXPECT_EQ(42, doc["counter"].GetInt());
}

TEST(json_t, FormatAttributeNull) {
    json_t formatter;

    const string_view message("value");
    const attribute_list attributes{{"endpoint", nullptr}};
    const attribute_pack pack{attributes};
    record_t record(0, message, pack);
    writer_t writer;
    formatter.format(record, writer);

    rapidjson::Document doc;
    doc.Parse<0>(writer.result().to_string().c_str());
    ASSERT_TRUE(doc.HasMember("endpoint"));
    ASSERT_TRUE(doc["endpoint"].IsNull());
}

TEST(json_t, FormatAttributeBool) {
    json_t formatter;

    const string_view message("value");
    const attribute_list attributes{{"available", true}};
    const attribute_pack pack{attributes};
    record_t record(0, message, pack);
    writer_t writer;
    formatter.format(record, writer);

    rapidjson::Document doc;
    doc.Parse<0>(writer.result().to_string().c_str());
    ASSERT_TRUE(doc.HasMember("available"));
    ASSERT_TRUE(doc["available"].IsBool());
    EXPECT_TRUE(doc["available"].GetBool());
}

TEST(json_t, FormatAttributeDouble) {
    json_t formatter;

    const string_view message("value");
    const attribute_list attributes{{"pi", 3.1415}};
    const attribute_pack pack{attributes};
    record_t record(0, message, pack);
    writer_t writer;
    formatter.format(record, writer);

    rapidjson::Document doc;
    doc.Parse<0>(writer.result().to_string().c_str());
    ASSERT_TRUE(doc.HasMember("pi"));
    ASSERT_TRUE(doc["pi"].IsDouble());
    EXPECT_DOUBLE_EQ(3.1415, doc["pi"].GetDouble());
}

TEST(json_t, FormatAttributeString) {
    json_t formatter;

    const string_view message("value");
    const attribute_list attributes{{"endpoint", "127.0.0.1:8080"}};
    const attribute_pack pack{attributes};
    record_t record(0, message, pack);
    writer_t writer;
    formatter.format(record, writer);

    rapidjson::Document doc;
    doc.Parse<0>(writer.result().to_string().c_str());
    ASSERT_TRUE(doc.HasMember("message"));
    ASSERT_TRUE(doc["message"].IsString());
    EXPECT_STREQ("value", doc["message"].GetString());

    ASSERT_TRUE(doc.HasMember("endpoint"));
    ASSERT_TRUE(doc["endpoint"].IsString());
    EXPECT_STREQ("127.0.0.1:8080", doc["endpoint"].GetString());
}

TEST(json_t, FormatMessageWithRouting) {
    json_t formatter(routing_t().spec("/fields", {"message"}));

    const string_view message("value");
    const attribute_pack pack;
    record_t record(0, message, pack);
    writer_t writer;
    formatter.format(record, writer);

    rapidjson::Document doc;
    doc.Parse<0>(writer.result().to_string().c_str());
    ASSERT_TRUE(doc.HasMember("fields"));
    ASSERT_TRUE(doc["fields"].HasMember("message"));
    ASSERT_TRUE(doc["fields"]["message"].IsString());
    EXPECT_STREQ("value", doc["fields"]["message"].GetString());
}

TEST(json_t, FormatAttributeStringWithRouting) {
    json_t formatter(routing_t().spec("/fields", {"endpoint"}));

    const string_view message("value");
    const attribute_list attributes{{"endpoint", "127.0.0.1:8080"}};
    const attribute_pack pack{attributes};
    record_t record(0, message, pack);
    writer_t writer;
    formatter.format(record, writer);

    rapidjson::Document doc;
    doc.Parse<0>(writer.result().to_string().c_str());
    ASSERT_TRUE(doc.HasMember("fields"));
    ASSERT_TRUE(doc["fields"].HasMember("endpoint"));
    ASSERT_TRUE(doc["fields"]["endpoint"].IsString());
    EXPECT_STREQ("127.0.0.1:8080", doc["fields"]["endpoint"].GetString());
}

TEST(json_t, FormatAttributeStringWithNestedRouting) {
    json_t formatter(routing_t().spec("/fields/external", {"endpoint"}));

    const string_view message("value");
    const attribute_list attributes{{"endpoint", "127.0.0.1:8080"}};
    const attribute_pack pack{attributes};
    record_t record(0, message, pack);
    writer_t writer;
    formatter.format(record, writer);

    rapidjson::Document doc;
    doc.Parse<0>(writer.result().to_string().c_str());
    ASSERT_TRUE(doc.HasMember("fields"));
    ASSERT_TRUE(doc["fields"].HasMember("external"));
    ASSERT_TRUE(doc["fields"]["external"].IsObject());
    ASSERT_TRUE(doc["fields"]["external"].HasMember("endpoint"));
    ASSERT_TRUE(doc["fields"]["external"]["endpoint"].IsString());
    EXPECT_STREQ("127.0.0.1:8080", doc["fields"]["external"]["endpoint"].GetString());
}

}  // namespace formatter
}  // namespace testing
}  // namespace blackhole
<|endoftext|>


<commit_before>
#ifndef __BTREE_KEY_VALUE_STORE_HPP__
#define __BTREE_KEY_VALUE_STORE_HPP__

#include "config/alloc.hpp"
#include "buffer_cache/buffer_cache.hpp"
#include "btree/node.hpp"   // For btree_superblock_t
#include "btree/fsm.hpp"
#include "utils.hpp"
#include "containers/intrusive_list.hpp"
#include "concurrency/access.hpp"

/* When the serializer starts up, it will create an initial superblock and initialize it to zero.
This isn't quite the behavior we want. The job of initialize_superblock_fsm is to initialize the
superblock to contain NULL_BLOCK_ID rather than zero as the root node. */

class initialize_superblock_fsm_t :
    private block_available_callback_t,
    private transaction_begin_callback_t,
    private transaction_commit_callback_t,
    public alloc_mixin_t<tls_small_obj_alloc_accessor<alloc_t>, initialize_superblock_fsm_t>{

public:
    initialize_superblock_fsm_t(cache_t *cache)
        : state(state_unstarted), cache(cache), sb_buf(NULL), txn(NULL)
        {}
    ~initialize_superblock_fsm_t() {
        assert(state == state_unstarted || state == state_done);
    }

    struct callback_t {
        virtual void on_initialize_superblock() = 0;
    };

    bool initialize_superblock_if_necessary(callback_t *cb) {
        assert(state == state_unstarted);
        state = state_begin_transaction;
        callback = NULL;
        if (next_initialize_superblock_step()) {
            return true;
        } else {
            callback = cb;
            return false;
        }
    }

private:
    enum state_t {
        state_unstarted,
        state_begin_transaction,
        state_beginning_transaction,
        state_acquire_superblock,
        state_acquiring_superblock,
        state_make_change,
        state_commit_transaction,
        state_committing_transaction,
        state_finish,
        state_done
    } state;

    cache_t *cache;
    buf_t *sb_buf;
    transaction_t *txn;
    callback_t *callback;

    bool next_initialize_superblock_step() {

        if (state == state_begin_transaction) {
            txn = cache->begin_transaction(rwi_write, this);
            if (txn) {
                state = state_acquire_superblock;
            } else {
                state = state_beginning_transaction;
                return false;
            }
        }

        if (state == state_acquire_superblock) {
            sb_buf = txn->acquire(SUPERBLOCK_ID, rwi_write, this);
            if (sb_buf) {
                state = state_make_change;
            } else {
                state = state_acquiring_superblock;
                return false;
            }
        }

        if (state == state_make_change) {
            btree_superblock_t *sb = (btree_superblock_t*)(sb_buf->get_data_write());
            // The serializer will init the superblock to zeroes if the database is newly created.
            if (!sb->database_exists) {
                sb->database_exists = 1;
                sb->root_block = NULL_BLOCK_ID;
            }
            sb_buf->release();
            state = state_commit_transaction;
        }

        if (state == state_commit_transaction) {
            if (txn->commit(this)) {
                state = state_finish;
            } else {
                state = state_committing_transaction;
                return false;
            }
        }

        if (state == state_finish) {
            state = state_done;
            if (callback) callback->on_initialize_superblock();
            return true;
        }

        fail("Unexpected state");
    }

    void on_txn_begin(transaction_t *t) {
        assert(state == state_beginning_transaction);
        txn = t;
        state = state_acquire_superblock;
        next_initialize_superblock_step();
    }

    void on_txn_commit(transaction_t *t) {
        assert(state == state_committing_transaction);
        state = state_finish;
        next_initialize_superblock_step();
    }

    void on_block_available(buf_t *buf) {
        assert(state == state_acquiring_superblock);
        sb_buf = buf;
        state = state_make_change;
        next_initialize_superblock_step();
    }
};

/* btree_key_value_store_t is a thin wrapper around cache_t that handles initializing the buffer
cache for the purpose of storing a btree. */

class btree_key_value_store_t :
    private cache_t::ready_callback_t,
    private initialize_superblock_fsm_t::callback_t,
    private cache_t::shutdown_callback_t
{

public:
    btree_key_value_store_t(
        char *filename,
        size_t block_size,
        size_t max_size,
        bool wait_for_flush,
        unsigned int flush_timer_ms,
        unsigned int flush_threshold_percent)
        : state(state_unstarted),
          cache(filename, block_size, max_size, wait_for_flush, flush_timer_ms, flush_threshold_percent)
        { }

public:
    struct ready_callback_t {
        virtual void on_store_ready() = 0;
    };
    bool start(ready_callback_t *cb) {
        assert(state == state_unstarted);
        state = state_starting_up_start_cache;
        ready_callback = NULL;
        if (next_starting_up_step()) {
            return true;
        } else {
            ready_callback = cb;
            return false;
        }
    }
private:
    bool next_starting_up_step() {

        if (state == state_starting_up_start_cache) {
            if (cache.start(this)) {
                state = state_starting_up_initialize_superblock;
            } else {
                state = state_starting_up_waiting_for_cache;
                return false;
            }
        }

        if (state == state_starting_up_initialize_superblock) {
            sb_fsm = new initialize_superblock_fsm_t(&cache);
            if (sb_fsm->initialize_superblock_if_necessary(this)) {
                state = state_starting_up_finish;
            } else {
                state = state_starting_up_waiting_for_superblock;
                return false;
            }
        }

        if (state == state_starting_up_finish) {

            delete sb_fsm;

            state = state_ready;
            if (ready_callback) ready_callback->on_store_ready();

            std::vector<btree_fsm_t*, gnew_alloc<btree_fsm_t *> >::iterator it;
            for (it = fsms_waiting_for_ready.begin(); it != fsms_waiting_for_ready.end(); it ++) {
                btree_fsm_t *fsm = *it;
                bool done = (fsm->do_transition(NULL) == btree_fsm_t::transition_complete);
                if (done && fsm->on_complete) {
                    fsm->on_complete(fsm);
                }
            }
            fsms_waiting_for_ready.clear();

            return true;
        }

        fail("Unexpected state");
    }
    void on_cache_ready() {
        assert(state == state_starting_up_waiting_for_cache);
        state = state_starting_up_initialize_superblock;
        next_starting_up_step();
    }
    void on_initialize_superblock() {
        assert(state == state_starting_up_waiting_for_superblock);
        state = state_starting_up_finish;
        next_starting_up_step();
    }
    ready_callback_t *ready_callback;

    // TODO It's kind of hacky to queue requests here. Should we queue them on the worker instead or
    // perhaps refuse to accept connections until all the key-value stores are ready?
    std::vector<btree_fsm_t*, gnew_alloc<btree_fsm_t *> > fsms_waiting_for_ready;

public:
    bool run_fsm(btree_fsm_t *fsm, btree_fsm_t::on_complete_t cb) {

        // The btree is constructed with no cache; here we must assign it its proper cache
        assert(!fsm->cache);
        fsm->cache = &cache;

        fsm->on_complete = cb;
        if (state == state_ready) {
            return (fsm->do_transition(NULL) == btree_fsm_t::transition_complete);
        } else {
            fsms_waiting_for_ready.push_back(fsm);
            return false;
        }
    }

public:
    struct shutdown_callback_t {
        virtual void on_store_shutdown() = 0;
    };
    bool shutdown(shutdown_callback_t *cb) {
        assert(state == state_ready);
        if (cache.shutdown(this)) {
            state = state_shut_down;
            return true;
        } else {
            state = state_shutting_down;
            shutdown_callback = cb;
            return false;
        }
    }
private:
    void on_cache_shutdown() {
        state = state_shut_down;
        if (shutdown_callback) shutdown_callback->on_store_shutdown();
    }
    shutdown_callback_t *shutdown_callback;

private:
    enum state_t {
        state_unstarted,

        state_starting_up_start_cache,
        state_starting_up_waiting_for_cache,
        state_starting_up_initialize_superblock,
        state_starting_up_waiting_for_superblock,
        state_starting_up_finish,

        state_ready,

        state_shutting_down,

        state_shut_down
    } state;

    initialize_superblock_fsm_t *sb_fsm;

    cache_t cache;
};

typedef btree_key_value_store_t store_t;

#endif /* __BTREE_KEY_VALUE_STORE_HPP__ */
<commit_msg>Fixing key value store to properly use get_data_read/get_data_write<commit_after>
#ifndef __BTREE_KEY_VALUE_STORE_HPP__
#define __BTREE_KEY_VALUE_STORE_HPP__

#include "config/alloc.hpp"
#include "buffer_cache/buffer_cache.hpp"
#include "btree/node.hpp"   // For btree_superblock_t
#include "btree/fsm.hpp"
#include "utils.hpp"
#include "containers/intrusive_list.hpp"
#include "concurrency/access.hpp"

/* When the serializer starts up, it will create an initial superblock and initialize it to zero.
This isn't quite the behavior we want. The job of initialize_superblock_fsm is to initialize the
superblock to contain NULL_BLOCK_ID rather than zero as the root node. */

class initialize_superblock_fsm_t :
    private block_available_callback_t,
    private transaction_begin_callback_t,
    private transaction_commit_callback_t,
    public alloc_mixin_t<tls_small_obj_alloc_accessor<alloc_t>, initialize_superblock_fsm_t>{

public:
    initialize_superblock_fsm_t(cache_t *cache)
        : state(state_unstarted), cache(cache), sb_buf(NULL), txn(NULL)
        {}
    ~initialize_superblock_fsm_t() {
        assert(state == state_unstarted || state == state_done);
    }

    struct callback_t {
        virtual void on_initialize_superblock() = 0;
    };

    bool initialize_superblock_if_necessary(callback_t *cb) {
        assert(state == state_unstarted);
        state = state_begin_transaction;
        callback = NULL;
        if (next_initialize_superblock_step()) {
            return true;
        } else {
            callback = cb;
            return false;
        }
    }

private:
    enum state_t {
        state_unstarted,
        state_begin_transaction,
        state_beginning_transaction,
        state_acquire_superblock,
        state_acquiring_superblock,
        state_make_change,
        state_commit_transaction,
        state_committing_transaction,
        state_finish,
        state_done
    } state;

    cache_t *cache;
    buf_t *sb_buf;
    transaction_t *txn;
    callback_t *callback;

    bool next_initialize_superblock_step() {

        if (state == state_begin_transaction) {
            txn = cache->begin_transaction(rwi_write, this);
            if (txn) {
                state = state_acquire_superblock;
            } else {
                state = state_beginning_transaction;
                return false;
            }
        }

        if (state == state_acquire_superblock) {
            sb_buf = txn->acquire(SUPERBLOCK_ID, rwi_write, this);
            if (sb_buf) {
                state = state_make_change;
            } else {
                state = state_acquiring_superblock;
                return false;
            }
        }

        if (state == state_make_change) {
            const btree_superblock_t *sb = (const btree_superblock_t*)(sb_buf->get_data_read());
            // The serializer will init the superblock to zeroes if the database is newly created.
            if (!sb->database_exists) {
                btree_superblock_t *sb = (btree_superblock_t*)(sb_buf->get_data_write());
                sb->database_exists = 1;
                sb->root_block = NULL_BLOCK_ID;
            }
            sb_buf->release();
            state = state_commit_transaction;
        }

        if (state == state_commit_transaction) {
            if (txn->commit(this)) {
                state = state_finish;
            } else {
                state = state_committing_transaction;
                return false;
            }
        }

        if (state == state_finish) {
            state = state_done;
            if (callback) callback->on_initialize_superblock();
            return true;
        }

        fail("Unexpected state");
    }

    void on_txn_begin(transaction_t *t) {
        assert(state == state_beginning_transaction);
        txn = t;
        state = state_acquire_superblock;
        next_initialize_superblock_step();
    }

    void on_txn_commit(transaction_t *t) {
        assert(state == state_committing_transaction);
        state = state_finish;
        next_initialize_superblock_step();
    }

    void on_block_available(buf_t *buf) {
        assert(state == state_acquiring_superblock);
        sb_buf = buf;
        state = state_make_change;
        next_initialize_superblock_step();
    }
};

/* btree_key_value_store_t is a thin wrapper around cache_t that handles initializing the buffer
cache for the purpose of storing a btree. */

class btree_key_value_store_t :
    private cache_t::ready_callback_t,
    private initialize_superblock_fsm_t::callback_t,
    private cache_t::shutdown_callback_t
{

public:
    btree_key_value_store_t(
        char *filename,
        size_t block_size,
        size_t max_size,
        bool wait_for_flush,
        unsigned int flush_timer_ms,
        unsigned int flush_threshold_percent)
        : state(state_unstarted),
          cache(filename, block_size, max_size, wait_for_flush, flush_timer_ms, flush_threshold_percent)
        { }

public:
    struct ready_callback_t {
        virtual void on_store_ready() = 0;
    };
    bool start(ready_callback_t *cb) {
        assert(state == state_unstarted);
        state = state_starting_up_start_cache;
        ready_callback = NULL;
        if (next_starting_up_step()) {
            return true;
        } else {
            ready_callback = cb;
            return false;
        }
    }
private:
    bool next_starting_up_step() {

        if (state == state_starting_up_start_cache) {
            if (cache.start(this)) {
                state = state_starting_up_initialize_superblock;
            } else {
                state = state_starting_up_waiting_for_cache;
                return false;
            }
        }

        if (state == state_starting_up_initialize_superblock) {
            sb_fsm = new initialize_superblock_fsm_t(&cache);
            if (sb_fsm->initialize_superblock_if_necessary(this)) {
                state = state_starting_up_finish;
            } else {
                state = state_starting_up_waiting_for_superblock;
                return false;
            }
        }

        if (state == state_starting_up_finish) {

            delete sb_fsm;

            state = state_ready;
            if (ready_callback) ready_callback->on_store_ready();

            std::vector<btree_fsm_t*, gnew_alloc<btree_fsm_t *> >::iterator it;
            for (it = fsms_waiting_for_ready.begin(); it != fsms_waiting_for_ready.end(); it ++) {
                btree_fsm_t *fsm = *it;
                bool done = (fsm->do_transition(NULL) == btree_fsm_t::transition_complete);
                if (done && fsm->on_complete) {
                    fsm->on_complete(fsm);
                }
            }
            fsms_waiting_for_ready.clear();

            return true;
        }

        fail("Unexpected state");
    }
    void on_cache_ready() {
        assert(state == state_starting_up_waiting_for_cache);
        state = state_starting_up_initialize_superblock;
        next_starting_up_step();
    }
    void on_initialize_superblock() {
        assert(state == state_starting_up_waiting_for_superblock);
        state = state_starting_up_finish;
        next_starting_up_step();
    }
    ready_callback_t *ready_callback;

    // TODO It's kind of hacky to queue requests here. Should we queue them on the worker instead or
    // perhaps refuse to accept connections until all the key-value stores are ready?
    std::vector<btree_fsm_t*, gnew_alloc<btree_fsm_t *> > fsms_waiting_for_ready;

public:
    bool run_fsm(btree_fsm_t *fsm, btree_fsm_t::on_complete_t cb) {

        // The btree is constructed with no cache; here we must assign it its proper cache
        assert(!fsm->cache);
        fsm->cache = &cache;

        fsm->on_complete = cb;
        if (state == state_ready) {
            return (fsm->do_transition(NULL) == btree_fsm_t::transition_complete);
        } else {
            fsms_waiting_for_ready.push_back(fsm);
            return false;
        }
    }

public:
    struct shutdown_callback_t {
        virtual void on_store_shutdown() = 0;
    };
    bool shutdown(shutdown_callback_t *cb) {
        assert(state == state_ready);
        if (cache.shutdown(this)) {
            state = state_shut_down;
            return true;
        } else {
            state = state_shutting_down;
            shutdown_callback = cb;
            return false;
        }
    }
private:
    void on_cache_shutdown() {
        state = state_shut_down;
        if (shutdown_callback) shutdown_callback->on_store_shutdown();
    }
    shutdown_callback_t *shutdown_callback;

private:
    enum state_t {
        state_unstarted,

        state_starting_up_start_cache,
        state_starting_up_waiting_for_cache,
        state_starting_up_initialize_superblock,
        state_starting_up_waiting_for_superblock,
        state_starting_up_finish,

        state_ready,

        state_shutting_down,

        state_shut_down
    } state;

    initialize_superblock_fsm_t *sb_fsm;

    cache_t cache;
};

typedef btree_key_value_store_t store_t;

#endif /* __BTREE_KEY_VALUE_STORE_HPP__ */
<|endoftext|>