<commit_before>#ifndef LUABIND_SCOPE_HPP_INCLUDED
#define LUABIND_SCOPE_HPP_INCLUDED

#include <vector>
#include <string>
#include <stack>

#include <luabind/detail/ref.hpp>

namespace luabind
{
	namespace detail
	{
		struct scoped_sequence;

		struct scoped_object
		{
			scoped_object()
			{
			}

			virtual ~scoped_object() {}

			virtual void commit(lua_State*) const = 0;
			virtual scoped_object* clone() const = 0;

			scoped_sequence operator,(const scoped_object& rhs) const;
		};

		struct scoped_sequence : scoped_object
		{
			scoped_sequence(scoped_object* a, scoped_object* b)
			{
				this->objects.push_back(a);
				this->objects.push_back(b);
			}

			scoped_sequence() {}

			virtual ~scoped_sequence()
			{
					for (std::vector<scoped_object*>
							::const_iterator i = this->objects.begin()
												; i != this->objects.end()
												; ++i)
					{
						scoped_object* ptr = *i;
						delete ptr;
					}
			}

			scoped_sequence& operator,(const scoped_object& rhs)
			{
				objects.push_back(rhs.clone());
				return *this;
			}

			virtual scoped_object* clone() const
			{
				scoped_sequence* copy = new scoped_sequence();
				copy->objects.swap(this->objects);
				return copy;
			}

			virtual void commit(lua_State* L) const
			{
				for (std::vector<scoped_object*>
							::const_iterator i = this->objects.begin()
												; i != this->objects.end()
												; ++i)
				{
					scoped_object* ptr = *i;
					ptr->commit(L);
				}
			}

			mutable std::vector<scoped_object*> objects;
		};

		inline scoped_sequence scoped_object::operator,(const scoped_object& rhs) const
		{
			return scoped_sequence(this->clone(), rhs.clone());
		}
	}
/*
	def(..),
	def(..)

	function_obj1, function_obj2
	function_obj1.push_back(function_obj2.doclone());
*/
	struct scope_stack
	{
		std::stack<int> scopes;

		static int gc(lua_State* L)
		{
			scope_stack* scopes = static_cast<scope_stack*>(
				lua_touserdata(L, -1)
			);

			assert(scopes->scopes.size() == 1);

			int top = scopes->scopes.top();
			scopes->scopes.pop();

			detail::unref(L, top);

			scopes->~scope_stack();

			return 0;
		}

		static int top(lua_State* L)
		{
			lua_pushstring(L, "__luabind_scope_stack");
			lua_gettable(L, LUA_REGISTRYINDEX);

			scope_stack* scopes = static_cast<scope_stack*>(
				lua_touserdata(L, -1)
			);

			lua_pop(L, 1);

			return scopes->scopes.top();
		}

		static void push(lua_State* L)
		{
			lua_pushstring(L, "__luabind_scope_stack");
			lua_gettable(L, LUA_REGISTRYINDEX);

			scope_stack* scopes = static_cast<scope_stack*>(
				lua_touserdata(L, -1)
			);

			lua_pop(L, 1);

			return scopes->scopes.push(detail::ref(L));
		}

		static void pop(lua_State* L)
		{
			lua_pushstring(L, "__luabind_scope_stack");
			lua_gettable(L, LUA_REGISTRYINDEX);

			scope_stack* scopes = static_cast<scope_stack*>(
				lua_touserdata(L, -1)
			);

			lua_pop(L, 1);

			int n = scopes->scopes.top();
			scopes->scopes.pop();
			detail::unref(L, n);
		}
	};

	class scope : public detail::scoped_object
	{
	public:

		static void init(lua_State* L)
		{
			lua_pushstring(L, "__luabind_scope_stack");
			lua_gettable(L, LUA_REGISTRYINDEX);

			scope_stack* scopes = static_cast<scope_stack*>(
				lua_touserdata(L, -1)
			);

			lua_pop(L, 1);

			if (scopes == 0)
			{
				scopes = static_cast<scope_stack*>(
					lua_newuserdata(L, sizeof(scope_stack))
				);

				lua_pushstring(L, "__luabind_scope_stack");
				lua_pushvalue(L, -2);
				lua_settable(L, LUA_REGISTRYINDEX);

				new (scopes) scope_stack();

				lua_pushvalue(L, LUA_GLOBALSINDEX);
				scopes->scopes.push(detail::ref(L));

				lua_newtable(L);
				lua_pushstring(L, "__gc");
				lua_pushcclosure(L, scope_stack::gc, 0);
				lua_settable(L, -3);
				lua_setmetatable(L, -2);

				lua_pop(L, 1);
			}
		}

		scope(lua_State* L, const char* name)
			: m_state(L)
			, m_name(name)
		{
			init(L);
		}

		scope(const char* name)
			: m_state(0)
			, m_name(name)
		{
		}

		virtual ~scope()
		{
			for (std::vector<scoped_object*>
					::const_iterator i = m_children.begin()
										; i != m_children.end()
										; ++i)
			{
				scoped_object* ptr = *i;
				delete ptr;
			}
		}

		scope& operator[](const detail::scoped_object& x)
		{
			m_children.push_back(x.clone());

			if (m_state)
			{
				this->commit(m_state);

				for (std::vector<scoped_object*>
						::const_iterator i = m_children.begin()
											; i != m_children.end()
											; ++i)
				{
					scoped_object* ptr = *i;
					delete ptr;
				}

				m_children.clear();
			}

			return *this;
		}

		virtual detail::scoped_object* clone() const
		{
			std::vector<detail::scoped_object*> tmp;
			tmp.swap(this->m_children);
			scope* copy = new scope(*this);
			copy->m_children.swap(tmp);
			return copy;
		}

		virtual void commit(lua_State* L) const
		{
			init(L);

			detail::getref(L, scope_stack::top(L)); // get current scope
			lua_pushstring(L, m_name.c_str());
			lua_gettable(L, -2);
			lua_remove(L, -2); // remove scope

			if (lua_isnil(L, -1))
			{
				lua_pop(L, 1);

				lua_newtable(L);
				detail::getref(L, scope_stack::top(L));
				lua_pushstring(L, m_name.c_str());
				lua_pushvalue(L, -3);
				lua_settable(L, -3);
				lua_pop(L, 1);
			}

			scope_stack::push(L);

			for (std::vector<detail::scoped_object*>
						::const_iterator i = m_children.begin()
	 										; i != m_children.end()
											; ++i)
			{
				scoped_object* ptr = *i;
				ptr->commit(L);
			}

			scope_stack::pop(L);
		}

	private:

		mutable std::vector<scoped_object*> m_children;
		lua_State* m_state;
		std::string m_name;
	};

	typedef scope namespace_;
}
/*
scope dwarf("dwarf");

dwarf
[
	def(..),
	def(..),

	class<vec3>("vec3")
		.def(constructor<float,float,float>())
		.def(self + self)
		.def(self - self)
		.def(self == self)
		,

	def("getDirection", &get_direction),
	def("getPosition", &get_position)
];

dwarf
[
	def(..)
];

dwarf.commit(L);

*/
#endif

<commit_msg>*** empty log message ***<commit_after>// Copyright (c) 2003 Daniel Wallin and Arvid Norberg

// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
// OR OTHER DEALINGS IN THE SOFTWARE.

#ifndef LUABIND_SCOPE_HPP_INCLUDED
#define LUABIND_SCOPE_HPP_INCLUDED

#include <vector>
#include <string>
#include <stack>

#include <luabind/detail/ref.hpp>

namespace luabind
{
	namespace detail
	{
		struct scoped_sequence;

		struct scoped_object
		{
			scoped_object()
			{
			}

			virtual ~scoped_object() {}

			virtual void commit(lua_State*) const = 0;
			virtual scoped_object* clone() const = 0;

			scoped_sequence operator,(const scoped_object& rhs) const;
		};

		struct scoped_sequence : scoped_object
		{
			scoped_sequence(scoped_object* a, scoped_object* b)
			{
				this->objects.push_back(a);
				this->objects.push_back(b);
			}

			scoped_sequence() {}

			virtual ~scoped_sequence()
			{
					for (std::vector<scoped_object*>
							::const_iterator i = this->objects.begin()
												; i != this->objects.end()
												; ++i)
					{
						scoped_object* ptr = *i;
						delete ptr;
					}
			}

			scoped_sequence& operator,(const scoped_object& rhs)
			{
				objects.push_back(rhs.clone());
				return *this;
			}

			virtual scoped_object* clone() const
			{
				scoped_sequence* copy = new scoped_sequence();
				copy->objects.swap(this->objects);
				return copy;
			}

			virtual void commit(lua_State* L) const
			{
				for (std::vector<scoped_object*>
							::const_iterator i = this->objects.begin()
												; i != this->objects.end()
												; ++i)
				{
					scoped_object* ptr = *i;
					ptr->commit(L);
				}
			}

			mutable std::vector<scoped_object*> objects;
		};

		inline scoped_sequence scoped_object::operator,(const scoped_object& rhs) const
		{
			return scoped_sequence(this->clone(), rhs.clone());
		}
	}

	struct scope_stack
	{
		std::stack<int> scopes;

		static int gc(lua_State* L)
		{
			scope_stack* scopes = static_cast<scope_stack*>(
				lua_touserdata(L, -1)
			);

			assert(scopes->scopes.size() == 1);

			int top = scopes->scopes.top();
			scopes->scopes.pop();

			detail::unref(L, top);

			scopes->~scope_stack();

			return 0;
		}

		static int top(lua_State* L)
		{
			lua_pushstring(L, "__luabind_scope_stack");
			lua_gettable(L, LUA_REGISTRYINDEX);

			scope_stack* scopes = static_cast<scope_stack*>(
				lua_touserdata(L, -1)
			);

			lua_pop(L, 1);

			return scopes->scopes.top();
		}

		static void push(lua_State* L)
		{
			lua_pushstring(L, "__luabind_scope_stack");
			lua_gettable(L, LUA_REGISTRYINDEX);

			scope_stack* scopes = static_cast<scope_stack*>(
				lua_touserdata(L, -1)
			);

			lua_pop(L, 1);

			return scopes->scopes.push(detail::ref(L));
		}

		static void pop(lua_State* L)
		{
			lua_pushstring(L, "__luabind_scope_stack");
			lua_gettable(L, LUA_REGISTRYINDEX);

			scope_stack* scopes = static_cast<scope_stack*>(
				lua_touserdata(L, -1)
			);

			lua_pop(L, 1);

			int n = scopes->scopes.top();
			scopes->scopes.pop();
			detail::unref(L, n);
		}
	};

	class scope : public detail::scoped_object
	{
	public:

		static void init(lua_State* L)
		{
			lua_pushstring(L, "__luabind_scope_stack");
			lua_gettable(L, LUA_REGISTRYINDEX);

			scope_stack* scopes = static_cast<scope_stack*>(
				lua_touserdata(L, -1)
			);

			lua_pop(L, 1);

			if (scopes == 0)
			{
				scopes = static_cast<scope_stack*>(
					lua_newuserdata(L, sizeof(scope_stack))
				);

				lua_pushstring(L, "__luabind_scope_stack");
				lua_pushvalue(L, -2);
				lua_settable(L, LUA_REGISTRYINDEX);

				new (scopes) scope_stack();

				lua_pushvalue(L, LUA_GLOBALSINDEX);
				scopes->scopes.push(detail::ref(L));

				lua_newtable(L);
				lua_pushstring(L, "__gc");
				lua_pushcclosure(L, scope_stack::gc, 0);
				lua_settable(L, -3);
				lua_setmetatable(L, -2);

				lua_pop(L, 1);
			}
		}

		scope(lua_State* L, const char* name)
			: m_state(L)
			, m_name(name)
		{
			init(L);
		}

		scope(const char* name)
			: m_state(0)
			, m_name(name)
		{
		}

		virtual ~scope()
		{
			for (std::vector<scoped_object*>
					::const_iterator i = m_children.begin()
										; i != m_children.end()
										; ++i)
			{
				scoped_object* ptr = *i;
				delete ptr;
			}
		}

		scope& operator[](const detail::scoped_object& x)
		{
			m_children.push_back(x.clone());

			if (m_state)
			{
				this->commit(m_state);

				for (std::vector<scoped_object*>
						::const_iterator i = m_children.begin()
											; i != m_children.end()
											; ++i)
				{
					scoped_object* ptr = *i;
					delete ptr;
				}

				m_children.clear();
			}

			return *this;
		}

		virtual detail::scoped_object* clone() const
		{
			std::vector<detail::scoped_object*> tmp;
			tmp.swap(this->m_children);
			scope* copy = new scope(*this);
			copy->m_children.swap(tmp);
			return copy;
		}

		virtual void commit(lua_State* L) const
		{
			init(L);

			detail::getref(L, scope_stack::top(L)); // get current scope
			lua_pushstring(L, m_name.c_str());
			lua_gettable(L, -2);
			lua_remove(L, -2); // remove scope

			if (lua_isnil(L, -1))
			{
				lua_pop(L, 1);

				lua_newtable(L);
				detail::getref(L, scope_stack::top(L));
				lua_pushstring(L, m_name.c_str());
				lua_pushvalue(L, -3);
				lua_settable(L, -3);
				lua_pop(L, 1);
			}

			scope_stack::push(L);

			for (std::vector<detail::scoped_object*>
						::const_iterator i = m_children.begin()
	 										; i != m_children.end()
											; ++i)
			{
				scoped_object* ptr = *i;
				ptr->commit(L);
			}

			scope_stack::pop(L);
		}

	private:

		mutable std::vector<scoped_object*> m_children;
		lua_State* m_state;
		std::string m_name;
	};

	typedef scope namespace_;
}

#endif // LUABIND_SCOPE_HPP_INCLUDED

<|endoftext|>


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

#include "GrBackendSurface.h"
#include "GrClip.h"
#include "GrContext.h"
#include "GrContextPriv.h"
#include "GrRenderTargetContext.h"
#include "GrTexture.h"
#include "GrTextureAdjuster.h"
#include "SkBitmapCache.h"
#include "SkImage_Gpu.h"
#include "SkImage_GpuBase.h"
#include "SkReadPixelsRec.h"

SkImage_GpuBase::SkImage_GpuBase(sk_sp<GrContext> context, int width, int height, uint32_t uniqueID,
                                 SkAlphaType at, SkBudgeted budgeted, sk_sp<SkColorSpace> cs)
        : INHERITED(width, height, uniqueID)
        , fContext(std::move(context))
        , fAlphaType(at)
        , fBudgeted(budgeted)
        , fColorSpace(std::move(cs)) {}

SkImage_GpuBase::~SkImage_GpuBase() {}

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

bool SkImage_GpuBase::ValidateBackendTexture(GrContext* ctx, const GrBackendTexture& tex,
                                             GrPixelConfig* config, SkColorType ct, SkAlphaType at,
                                             sk_sp<SkColorSpace> cs) {
    if (!tex.isValid()) {
        return false;
    }
    // TODO: Create a SkImageColorInfo struct for color, alpha, and color space so we don't need to
    // create a fake image info here.
    SkImageInfo info = SkImageInfo::Make(1, 1, ct, at, cs);
    if (!SkImageInfoIsValid(info)) {
        return false;
    }

    return ctx->contextPriv().caps()->validateBackendTexture(tex, ct, config);
}

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

bool SkImage_GpuBase::getROPixels(SkBitmap* dst, SkColorSpace*, CachingHint chint) const {
    if (!fContext->contextPriv().resourceProvider()) {
        // DDL TODO: buffer up the readback so it occurs when the DDL is drawn?
        return false;
    }

    // The SkColorSpace parameter "dstColorSpace" is really just a hint about how/where the bitmap
    // will be used. The client doesn't expect that we convert to that color space, it's intended
    // for codec-backed images, to drive our decoding heuristic. In theory we *could* read directly
    // into that color space (to save the client some effort in whatever they're about to do), but
    // that would make our use of the bitmap cache incorrect (or much less efficient, assuming we
    // rolled the dstColorSpace into the key).
    const auto desc = SkBitmapCacheDesc::Make(this);
    if (SkBitmapCache::Find(desc, dst)) {
        SkASSERT(dst->isImmutable());
        SkASSERT(dst->getPixels());
        return true;
    }

    SkBitmapCache::RecPtr rec = nullptr;
    SkPixmap pmap;
    if (kAllow_CachingHint == chint) {
        rec = SkBitmapCache::Alloc(desc, this->onImageInfo(), &pmap);
        if (!rec) {
            return false;
        }
    } else {
        if (!dst->tryAllocPixels(this->onImageInfo()) || !dst->peekPixels(&pmap)) {
            return false;
        }
    }

    sk_sp<GrSurfaceContext> sContext = fContext->contextPriv().makeWrappedSurfaceContext(
        this->asTextureProxyRef(),
        fColorSpace);
    if (!sContext) {
        return false;
    }

    if (!sContext->readPixels(pmap.info(), pmap.writable_addr(), pmap.rowBytes(), 0, 0)) {
        return false;
    }

    if (rec) {
        SkBitmapCache::Add(std::move(rec), dst);
        this->notifyAddedToRasterCache();
    }
    return true;
}

sk_sp<SkImage> SkImage_GpuBase::onMakeSubset(const SkIRect& subset) const {
    sk_sp<GrSurfaceProxy> proxy = this->asTextureProxyRef();

    GrSurfaceDesc desc;
    desc.fWidth = subset.width();
    desc.fHeight = subset.height();
    desc.fConfig = proxy->config();

    sk_sp<GrSurfaceContext> sContext(fContext->contextPriv().makeDeferredSurfaceContext(
        desc, proxy->origin(), GrMipMapped::kNo, SkBackingFit::kExact, fBudgeted));
    if (!sContext) {
        return nullptr;
    }

    if (!sContext->copy(proxy.get(), subset, SkIPoint::Make(0, 0))) {
        return nullptr;
    }

    // MDB: this call is okay bc we know 'sContext' was kExact
    return sk_make_sp<SkImage_Gpu>(fContext, kNeedNewImageUniqueID,
                                   fAlphaType, sContext->asTextureProxyRef(),
                                   fColorSpace, fBudgeted);
}

static void apply_premul(const SkImageInfo& info, void* pixels, size_t rowBytes) {
    switch (info.colorType()) {
    case kRGBA_8888_SkColorType:
    case kBGRA_8888_SkColorType:
        break;
    default:
        return; // nothing to do
    }

    // SkColor is not necesarily RGBA or BGRA, but it is one of them on little-endian,
    // and in either case, the alpha-byte is always in the same place, so we can safely call
    // SkPreMultiplyColor()
    //
    SkColor* row = (SkColor*)pixels;
    for (int y = 0; y < info.height(); ++y) {
        for (int x = 0; x < info.width(); ++x) {
            row[x] = SkPreMultiplyColor(row[x]);
        }
        row = (SkColor*)((char*)(row)+rowBytes);
    }
}

bool SkImage_GpuBase::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB,
                                   int srcX, int srcY, CachingHint) const {
    if (!fContext->contextPriv().resourceProvider()) {
        // DDL TODO: buffer up the readback so it occurs when the DDL is drawn?
        return false;
    }

    if (!SkImageInfoValidConversion(dstInfo, this->onImageInfo())) {
        return false;
    }

    SkReadPixelsRec rec(dstInfo, dstPixels, dstRB, srcX, srcY);
    if (!rec.trim(this->width(), this->height())) {
        return false;
    }

    // TODO: this seems to duplicate code in GrTextureContext::onReadPixels and
    // GrRenderTargetContext::onReadPixels
    uint32_t flags = 0;
    if (kUnpremul_SkAlphaType == rec.fInfo.alphaType() && kPremul_SkAlphaType == fAlphaType) {
        // let the GPU perform this transformation for us
        flags = GrContextPriv::kUnpremul_PixelOpsFlag;
    }

    sk_sp<GrSurfaceContext> sContext = fContext->contextPriv().makeWrappedSurfaceContext(
        this->asTextureProxyRef(), fColorSpace);
    if (!sContext) {
        return false;
    }

    if (!sContext->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY, flags)) {
        return false;
    }

    // do we have to manually fix-up the alpha channel?
    //      src         dst
    //      unpremul    premul      fix manually
    //      premul      unpremul    done by kUnpremul_PixelOpsFlag
    // all other combos need to change.
    //
    // Should this be handled by Ganesh? todo:?
    //
    if (kPremul_SkAlphaType == rec.fInfo.alphaType() && kUnpremul_SkAlphaType == fAlphaType) {
        apply_premul(rec.fInfo, rec.fPixels, rec.fRowBytes);
    }
    return true;
}

sk_sp<GrTextureProxy> SkImage_GpuBase::asTextureProxyRef(GrContext* context,
                                                         const GrSamplerState& params,
                                                         SkColorSpace* dstColorSpace,
                                                         sk_sp<SkColorSpace>* texColorSpace,
                                                         SkScalar scaleAdjust[2]) const {
    if (context->uniqueID() != fContext->uniqueID()) {
        SkASSERT(0);
        return nullptr;
    }

    GrTextureAdjuster adjuster(fContext.get(), this->asTextureProxyRef(), fAlphaType,
                               this->uniqueID(), fColorSpace.get());
    return adjuster.refTextureProxyForParams(params, dstColorSpace, texColorSpace, scaleAdjust);
}

GrBackendTexture SkImage_GpuBase::onGetBackendTexture(bool flushPendingGrContextIO,
                                                      GrSurfaceOrigin* origin) const {
    sk_sp<GrTextureProxy> proxy = this->asTextureProxyRef();
    SkASSERT(proxy);

    if (!fContext->contextPriv().resourceProvider() && !proxy->isInstantiated()) {
        // This image was created with a DDL context and cannot be instantiated.
        return GrBackendTexture();
}

    if (!proxy->instantiate(fContext->contextPriv().resourceProvider())) {
        return GrBackendTexture(); // invalid
    }

    GrTexture* texture = proxy->peekTexture();

    if (texture) {
        if (flushPendingGrContextIO) {
            fContext->contextPriv().prepareSurfaceForExternalIO(proxy.get());
        }
        if (origin) {
            *origin = proxy->origin();
        }
        return texture->getBackendTexture();
    }
    return GrBackendTexture(); // invalid
}

GrTexture* SkImage_GpuBase::onGetTexture() const {
    GrTextureProxy* proxy = this->peekProxy();
    if (!proxy) {
        return nullptr;
    }

    sk_sp<GrTextureProxy> proxyRef = this->asTextureProxyRef();
    if (!fContext->contextPriv().resourceProvider() && !proxyRef->isInstantiated()) {
        // This image was created with a DDL context and cannot be instantiated.
        return nullptr;
    }

    if (!proxy->instantiate(fContext->contextPriv().resourceProvider())) {
        return nullptr;
    }

    return proxy->peekTexture();
}

sk_sp<SkImage> SkImage_GpuBase::onMakeColorSpace(sk_sp<SkColorSpace> target) const {
    auto xform = GrColorSpaceXformEffect::Make(fColorSpace.get(), fAlphaType,
                                               target.get(),      fAlphaType);
    if (!xform) {
        return sk_ref_sp(const_cast<SkImage_GpuBase*>(this));
    }

    sk_sp<GrTextureProxy> proxy = this->asTextureProxyRef();

    sk_sp<GrRenderTargetContext> renderTargetContext(
        fContext->contextPriv().makeDeferredRenderTargetContext(
            SkBackingFit::kExact, this->width(), this->height(), proxy->config(), nullptr));
    if (!renderTargetContext) {
        return nullptr;
    }

    GrPaint paint;
    paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
    paint.addColorTextureProcessor(std::move(proxy), SkMatrix::I());
    paint.addColorFragmentProcessor(std::move(xform));

    const SkRect rect = SkRect::MakeIWH(this->width(), this->height());

    renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), rect);

    if (!renderTargetContext->asTextureProxy()) {
        return nullptr;
    }

    // MDB: this call is okay bc we know 'renderTargetContext' was exact
    return sk_make_sp<SkImage_Gpu>(fContext, kNeedNewImageUniqueID,
                                   fAlphaType, renderTargetContext->asTextureProxyRef(),
                                   std::move(target), fBudgeted);
}

bool SkImage_GpuBase::onIsValid(GrContext* context) const {
    // The base class has already checked that context isn't abandoned (if it's not nullptr)
    if (fContext->abandoned()) {
        return false;
    }

    if (context && context != fContext.get()) {
        return false;
    }

    return true;
}

/////////////////////////////////////////////////////////////////////////////////////////////////
sk_sp<GrTexture> SkPromiseImageHelper::getTexture(GrResourceProvider* resourceProvider,
                                                  GrPixelConfig config) {
    // Releases the promise helper if there are no outstanding hard refs. This means that we
    // don't have any ReleaseProcs waiting to be called so we will need to do a fulfill.
    if (fReleaseHelper && fReleaseHelper->weak_expired()) {
        this->resetReleaseHelper();
    }

    sk_sp<GrTexture> tex;
    if (!fReleaseHelper) {
        fFulfillProc(fContext, &fBackendTex);
        fBackendTex.fConfig = config;
        if (!fBackendTex.isValid()) {
            // Even though the GrBackendTexture is not valid, we must call the release
            // proc to keep our contract of always calling Fulfill and Release in pairs.
            fReleaseProc(fContext);
            return sk_sp<GrTexture>();
        }

        tex = resourceProvider->wrapBackendTexture(fBackendTex, kBorrow_GrWrapOwnership);
        if (!tex) {
            // Even though the GrBackendTexture is not valid, we must call the release
            // proc to keep our contract of always calling Fulfill and Release in pairs.
            fReleaseProc(fContext);
            return sk_sp<GrTexture>();
        }
        fReleaseHelper = new SkPromiseReleaseProcHelper(fReleaseProc, fContext, fDoneHelper);
        // Take a weak ref
        fReleaseHelper->weak_ref();
    } else {
        SkASSERT(fBackendTex.isValid());
        tex = resourceProvider->wrapBackendTexture(fBackendTex, kBorrow_GrWrapOwnership);
        if (!tex) {
            // We weren't able to make a texture here, but since we are in this branch
            // of the calls (promiseHelper.fReleaseHelper is valid) there is already a
            // texture out there which will call the release proc so we don't need to
            // call it here.
            return sk_sp<GrTexture>();
        }

        SkAssertResult(fReleaseHelper->try_ref());
    }
    SkASSERT(tex);
    // Pass the hard ref off to the texture
    tex->setRelease(sk_sp<GrReleaseProcHelper>(fReleaseHelper));
    return tex;
}
<commit_msg>Fix bug with makeColorSpace of GPU images with non-renderable configs<commit_after>/*
 * Copyright 2018 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "GrBackendSurface.h"
#include "GrClip.h"
#include "GrContext.h"
#include "GrContextPriv.h"
#include "GrRenderTargetContext.h"
#include "GrTexture.h"
#include "GrTextureAdjuster.h"
#include "SkBitmapCache.h"
#include "SkImage_Gpu.h"
#include "SkImage_GpuBase.h"
#include "SkReadPixelsRec.h"

SkImage_GpuBase::SkImage_GpuBase(sk_sp<GrContext> context, int width, int height, uint32_t uniqueID,
                                 SkAlphaType at, SkBudgeted budgeted, sk_sp<SkColorSpace> cs)
        : INHERITED(width, height, uniqueID)
        , fContext(std::move(context))
        , fAlphaType(at)
        , fBudgeted(budgeted)
        , fColorSpace(std::move(cs)) {}

SkImage_GpuBase::~SkImage_GpuBase() {}

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

bool SkImage_GpuBase::ValidateBackendTexture(GrContext* ctx, const GrBackendTexture& tex,
                                             GrPixelConfig* config, SkColorType ct, SkAlphaType at,
                                             sk_sp<SkColorSpace> cs) {
    if (!tex.isValid()) {
        return false;
    }
    // TODO: Create a SkImageColorInfo struct for color, alpha, and color space so we don't need to
    // create a fake image info here.
    SkImageInfo info = SkImageInfo::Make(1, 1, ct, at, cs);
    if (!SkImageInfoIsValid(info)) {
        return false;
    }

    return ctx->contextPriv().caps()->validateBackendTexture(tex, ct, config);
}

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

bool SkImage_GpuBase::getROPixels(SkBitmap* dst, SkColorSpace*, CachingHint chint) const {
    if (!fContext->contextPriv().resourceProvider()) {
        // DDL TODO: buffer up the readback so it occurs when the DDL is drawn?
        return false;
    }

    // The SkColorSpace parameter "dstColorSpace" is really just a hint about how/where the bitmap
    // will be used. The client doesn't expect that we convert to that color space, it's intended
    // for codec-backed images, to drive our decoding heuristic. In theory we *could* read directly
    // into that color space (to save the client some effort in whatever they're about to do), but
    // that would make our use of the bitmap cache incorrect (or much less efficient, assuming we
    // rolled the dstColorSpace into the key).
    const auto desc = SkBitmapCacheDesc::Make(this);
    if (SkBitmapCache::Find(desc, dst)) {
        SkASSERT(dst->isImmutable());
        SkASSERT(dst->getPixels());
        return true;
    }

    SkBitmapCache::RecPtr rec = nullptr;
    SkPixmap pmap;
    if (kAllow_CachingHint == chint) {
        rec = SkBitmapCache::Alloc(desc, this->onImageInfo(), &pmap);
        if (!rec) {
            return false;
        }
    } else {
        if (!dst->tryAllocPixels(this->onImageInfo()) || !dst->peekPixels(&pmap)) {
            return false;
        }
    }

    sk_sp<GrSurfaceContext> sContext = fContext->contextPriv().makeWrappedSurfaceContext(
        this->asTextureProxyRef(),
        fColorSpace);
    if (!sContext) {
        return false;
    }

    if (!sContext->readPixels(pmap.info(), pmap.writable_addr(), pmap.rowBytes(), 0, 0)) {
        return false;
    }

    if (rec) {
        SkBitmapCache::Add(std::move(rec), dst);
        this->notifyAddedToRasterCache();
    }
    return true;
}

sk_sp<SkImage> SkImage_GpuBase::onMakeSubset(const SkIRect& subset) const {
    sk_sp<GrSurfaceProxy> proxy = this->asTextureProxyRef();

    GrSurfaceDesc desc;
    desc.fWidth = subset.width();
    desc.fHeight = subset.height();
    desc.fConfig = proxy->config();

    sk_sp<GrSurfaceContext> sContext(fContext->contextPriv().makeDeferredSurfaceContext(
        desc, proxy->origin(), GrMipMapped::kNo, SkBackingFit::kExact, fBudgeted));
    if (!sContext) {
        return nullptr;
    }

    if (!sContext->copy(proxy.get(), subset, SkIPoint::Make(0, 0))) {
        return nullptr;
    }

    // MDB: this call is okay bc we know 'sContext' was kExact
    return sk_make_sp<SkImage_Gpu>(fContext, kNeedNewImageUniqueID,
                                   fAlphaType, sContext->asTextureProxyRef(),
                                   fColorSpace, fBudgeted);
}

static void apply_premul(const SkImageInfo& info, void* pixels, size_t rowBytes) {
    switch (info.colorType()) {
    case kRGBA_8888_SkColorType:
    case kBGRA_8888_SkColorType:
        break;
    default:
        return; // nothing to do
    }

    // SkColor is not necesarily RGBA or BGRA, but it is one of them on little-endian,
    // and in either case, the alpha-byte is always in the same place, so we can safely call
    // SkPreMultiplyColor()
    //
    SkColor* row = (SkColor*)pixels;
    for (int y = 0; y < info.height(); ++y) {
        for (int x = 0; x < info.width(); ++x) {
            row[x] = SkPreMultiplyColor(row[x]);
        }
        row = (SkColor*)((char*)(row)+rowBytes);
    }
}

bool SkImage_GpuBase::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB,
                                   int srcX, int srcY, CachingHint) const {
    if (!fContext->contextPriv().resourceProvider()) {
        // DDL TODO: buffer up the readback so it occurs when the DDL is drawn?
        return false;
    }

    if (!SkImageInfoValidConversion(dstInfo, this->onImageInfo())) {
        return false;
    }

    SkReadPixelsRec rec(dstInfo, dstPixels, dstRB, srcX, srcY);
    if (!rec.trim(this->width(), this->height())) {
        return false;
    }

    // TODO: this seems to duplicate code in GrTextureContext::onReadPixels and
    // GrRenderTargetContext::onReadPixels
    uint32_t flags = 0;
    if (kUnpremul_SkAlphaType == rec.fInfo.alphaType() && kPremul_SkAlphaType == fAlphaType) {
        // let the GPU perform this transformation for us
        flags = GrContextPriv::kUnpremul_PixelOpsFlag;
    }

    sk_sp<GrSurfaceContext> sContext = fContext->contextPriv().makeWrappedSurfaceContext(
        this->asTextureProxyRef(), fColorSpace);
    if (!sContext) {
        return false;
    }

    if (!sContext->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY, flags)) {
        return false;
    }

    // do we have to manually fix-up the alpha channel?
    //      src         dst
    //      unpremul    premul      fix manually
    //      premul      unpremul    done by kUnpremul_PixelOpsFlag
    // all other combos need to change.
    //
    // Should this be handled by Ganesh? todo:?
    //
    if (kPremul_SkAlphaType == rec.fInfo.alphaType() && kUnpremul_SkAlphaType == fAlphaType) {
        apply_premul(rec.fInfo, rec.fPixels, rec.fRowBytes);
    }
    return true;
}

sk_sp<GrTextureProxy> SkImage_GpuBase::asTextureProxyRef(GrContext* context,
                                                         const GrSamplerState& params,
                                                         SkColorSpace* dstColorSpace,
                                                         sk_sp<SkColorSpace>* texColorSpace,
                                                         SkScalar scaleAdjust[2]) const {
    if (context->uniqueID() != fContext->uniqueID()) {
        SkASSERT(0);
        return nullptr;
    }

    GrTextureAdjuster adjuster(fContext.get(), this->asTextureProxyRef(), fAlphaType,
                               this->uniqueID(), fColorSpace.get());
    return adjuster.refTextureProxyForParams(params, dstColorSpace, texColorSpace, scaleAdjust);
}

GrBackendTexture SkImage_GpuBase::onGetBackendTexture(bool flushPendingGrContextIO,
                                                      GrSurfaceOrigin* origin) const {
    sk_sp<GrTextureProxy> proxy = this->asTextureProxyRef();
    SkASSERT(proxy);

    if (!fContext->contextPriv().resourceProvider() && !proxy->isInstantiated()) {
        // This image was created with a DDL context and cannot be instantiated.
        return GrBackendTexture();
}

    if (!proxy->instantiate(fContext->contextPriv().resourceProvider())) {
        return GrBackendTexture(); // invalid
    }

    GrTexture* texture = proxy->peekTexture();

    if (texture) {
        if (flushPendingGrContextIO) {
            fContext->contextPriv().prepareSurfaceForExternalIO(proxy.get());
        }
        if (origin) {
            *origin = proxy->origin();
        }
        return texture->getBackendTexture();
    }
    return GrBackendTexture(); // invalid
}

GrTexture* SkImage_GpuBase::onGetTexture() const {
    GrTextureProxy* proxy = this->peekProxy();
    if (!proxy) {
        return nullptr;
    }

    sk_sp<GrTextureProxy> proxyRef = this->asTextureProxyRef();
    if (!fContext->contextPriv().resourceProvider() && !proxyRef->isInstantiated()) {
        // This image was created with a DDL context and cannot be instantiated.
        return nullptr;
    }

    if (!proxy->instantiate(fContext->contextPriv().resourceProvider())) {
        return nullptr;
    }

    return proxy->peekTexture();
}

sk_sp<SkImage> SkImage_GpuBase::onMakeColorSpace(sk_sp<SkColorSpace> target) const {
    auto xform = GrColorSpaceXformEffect::Make(fColorSpace.get(), fAlphaType,
                                               target.get(),      fAlphaType);
    if (!xform) {
        return sk_ref_sp(const_cast<SkImage_GpuBase*>(this));
    }

    sk_sp<GrTextureProxy> proxy = this->asTextureProxyRef();

    sk_sp<GrRenderTargetContext> renderTargetContext(
        fContext->contextPriv().makeDeferredRenderTargetContextWithFallback(
            SkBackingFit::kExact, this->width(), this->height(), proxy->config(), nullptr));
    if (!renderTargetContext) {
        return nullptr;
    }

    GrPaint paint;
    paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
    paint.addColorTextureProcessor(std::move(proxy), SkMatrix::I());
    paint.addColorFragmentProcessor(std::move(xform));

    const SkRect rect = SkRect::MakeIWH(this->width(), this->height());

    renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), rect);

    if (!renderTargetContext->asTextureProxy()) {
        return nullptr;
    }

    // MDB: this call is okay bc we know 'renderTargetContext' was exact
    return sk_make_sp<SkImage_Gpu>(fContext, kNeedNewImageUniqueID,
                                   fAlphaType, renderTargetContext->asTextureProxyRef(),
                                   std::move(target), fBudgeted);
}

bool SkImage_GpuBase::onIsValid(GrContext* context) const {
    // The base class has already checked that context isn't abandoned (if it's not nullptr)
    if (fContext->abandoned()) {
        return false;
    }

    if (context && context != fContext.get()) {
        return false;
    }

    return true;
}

/////////////////////////////////////////////////////////////////////////////////////////////////
sk_sp<GrTexture> SkPromiseImageHelper::getTexture(GrResourceProvider* resourceProvider,
                                                  GrPixelConfig config) {
    // Releases the promise helper if there are no outstanding hard refs. This means that we
    // don't have any ReleaseProcs waiting to be called so we will need to do a fulfill.
    if (fReleaseHelper && fReleaseHelper->weak_expired()) {
        this->resetReleaseHelper();
    }

    sk_sp<GrTexture> tex;
    if (!fReleaseHelper) {
        fFulfillProc(fContext, &fBackendTex);
        fBackendTex.fConfig = config;
        if (!fBackendTex.isValid()) {
            // Even though the GrBackendTexture is not valid, we must call the release
            // proc to keep our contract of always calling Fulfill and Release in pairs.
            fReleaseProc(fContext);
            return sk_sp<GrTexture>();
        }

        tex = resourceProvider->wrapBackendTexture(fBackendTex, kBorrow_GrWrapOwnership);
        if (!tex) {
            // Even though the GrBackendTexture is not valid, we must call the release
            // proc to keep our contract of always calling Fulfill and Release in pairs.
            fReleaseProc(fContext);
            return sk_sp<GrTexture>();
        }
        fReleaseHelper = new SkPromiseReleaseProcHelper(fReleaseProc, fContext, fDoneHelper);
        // Take a weak ref
        fReleaseHelper->weak_ref();
    } else {
        SkASSERT(fBackendTex.isValid());
        tex = resourceProvider->wrapBackendTexture(fBackendTex, kBorrow_GrWrapOwnership);
        if (!tex) {
            // We weren't able to make a texture here, but since we are in this branch
            // of the calls (promiseHelper.fReleaseHelper is valid) there is already a
            // texture out there which will call the release proc so we don't need to
            // call it here.
            return sk_sp<GrTexture>();
        }

        SkAssertResult(fReleaseHelper->try_ref());
    }
    SkASSERT(tex);
    // Pass the hard ref off to the texture
    tex->setRelease(sk_sp<GrReleaseProcHelper>(fReleaseHelper));
    return tex;
}
<|endoftext|>


<commit_before>#include "Trie.hpp"
#include "Stack2.hpp"
#include "Pair.hpp"
#include <string.h>
#include <iostream>

TrieNode::TrieNode(): parent_(0), value_(0), key_(0)
{
  memset(index_, 0, sizeof(TrieNode*)*53);
}

bool TrieNode::operator<(const TrieNode & b) const
{
  return (b.value_ < this->value_);
}

std::ostream& TrieNode::toStream(std::ostream& oOut)
{
    oOut << key_;
    oOut << ":";
    oOut << value_;
    return oOut;
}

std::ostream& operator<<(std::ostream& oOut, TrieNode & iObj) { return iObj.toStream(oOut); }

Trie::Trie(): size_(0) {}

void Trie::insert(const char * iWord)
{
  TrieNode * p = &root_;
  while (*iWord!=0)
  {
    char i = hash(*iWord);
    if ( p->index_[i] )
    {
      p = p->index_[i];
    }
    else
    {
      TrieNode * node = new TrieNode();
      node->key_ = *iWord;
      node->parent_ = p;
      p->index_[i] = node;
      p = node;
      ++size_;
    }
    ++iWord;
  }
  if (p->value_==0)
    list_.pushBack(p);
  ++(p->value_);
}

void Trie::frequencies(int iTop) const
{
  printf("\tTrie_size= %i\n", size_);
  printf("\tMemory_space= %i\n", size_*sizeof(TrieNode));
  list_.sort2();
  //std::cout << list_ << std::endl;

  LinkedListIterator<TrieNode*> it = list_.getIterator();
  int i = 0;

  for (; i<iTop && !it.end(); ++i, ++it)
  {
    const TrieNode * p = *it;
    char s[64];
    memset(s,0,64);
    char * q = s;
    int freq = p->value_;

    while (p!=&root_)
    {
      *q=p->key_;
      ++q;
      p = p->parent_;
    }
    // we have to reverse the string before printing it
    reverse_string(s, get_string_size(s));
    printf("\t%i\t%s\n", freq, s);
  }

}

char Trie::hash(char c)
{
  if (c>64 && c<91)
    return c-65;
  else if (c>96 && c<123)
    return c-71;
  return 52;
}

void Trie::reverse_string(char * s, int n) const
{
  char * e = s+n-1;
  char t;

  for (; s<e; ++s, --e)
  {
    t = *s;
    *s = *e;
    *e = t;
  }

  return;
}

int Trie::get_string_size(char * s) const
{
  char * p = s;
  while(*p)
    ++p;
  return (p-s);
}

int Trie::get_tree_high() const
{
  typedef Pair<const TrieNode*, int> Dub;

  Stack2< Dub > stack;
  int max = 0;
  int cur = 0;
  const TrieNode* p = 0;

  stack.push( Dub(&root_,cur) );

  while( !stack.empty() )
  {
    Dub d = stack.getTop();
    stack.pop();
    p = d.first();
    cur = d.second();

    if (cur>max)
    {
      max=cur;
    }

    for (int i=52; i>=0; --i)
    {
      if ( p->index_[i] )
      {
        stack.push( Dub(p->index_[i], cur+1) );
      }
    }
  }

  return max;
}

<commit_msg>Added cstdio include<commit_after>#include "Trie.hpp"
#include "Stack2.hpp"
#include "Pair.hpp"
#include <cstring>
#include <cstdio>
#include <iostream>

TrieNode::TrieNode(): parent_(0), value_(0), key_(0)
{
  memset(index_, 0, sizeof(TrieNode*)*53);
}

bool TrieNode::operator<(const TrieNode & b) const
{
  return (b.value_ < this->value_);
}

std::ostream& TrieNode::toStream(std::ostream& oOut)
{
    oOut << key_;
    oOut << ":";
    oOut << value_;
    return oOut;
}

std::ostream& operator<<(std::ostream& oOut, TrieNode & iObj) { return iObj.toStream(oOut); }

Trie::Trie(): size_(0) {}

void Trie::insert(const char * iWord)
{
  TrieNode * p = &root_;
  while (*iWord!=0)
  {
    char i = hash(*iWord);
    if ( p->index_[i] )
    {
      p = p->index_[i];
    }
    else
    {
      TrieNode * node = new TrieNode();
      node->key_ = *iWord;
      node->parent_ = p;
      p->index_[i] = node;
      p = node;
      ++size_;
    }
    ++iWord;
  }
  if (p->value_==0)
    list_.pushBack(p);
  ++(p->value_);
}

void Trie::frequencies(int iTop) const
{
  printf("\tTrie_size= %i\n", size_);
  printf("\tMemory_space= %i\n", size_*sizeof(TrieNode));
  list_.sort2();
  //std::cout << list_ << std::endl;

  LinkedListIterator<TrieNode*> it = list_.getIterator();
  int i = 0;

  for (; i<iTop && !it.end(); ++i, ++it)
  {
    const TrieNode * p = *it;
    char s[64];
    memset(s,0,64);
    char * q = s;
    int freq = p->value_;

    while (p!=&root_)
    {
      *q=p->key_;
      ++q;
      p = p->parent_;
    }
    // we have to reverse the string before printing it
    reverse_string(s, get_string_size(s));
    printf("\t%i\t%s\n", freq, s);
  }

}

char Trie::hash(char c)
{
  if (c>64 && c<91)
    return c-65;
  else if (c>96 && c<123)
    return c-71;
  return 52;
}

void Trie::reverse_string(char * s, int n) const
{
  char * e = s+n-1;
  char t;

  for (; s<e; ++s, --e)
  {
    t = *s;
    *s = *e;
    *e = t;
  }

  return;
}

int Trie::get_string_size(char * s) const
{
  char * p = s;
  while(*p)
    ++p;
  return (p-s);
}

int Trie::get_tree_high() const
{
  typedef Pair<const TrieNode*, int> Dub;

  Stack2< Dub > stack;
  int max = 0;
  int cur = 0;
  const TrieNode* p = 0;

  stack.push( Dub(&root_,cur) );

  while( !stack.empty() )
  {
    Dub d = stack.getTop();
    stack.pop();
    p = d.first();
    cur = d.second();

    if (cur>max)
    {
      max=cur;
    }

    for (int i=52; i>=0; --i)
    {
      if ( p->index_[i] )
      {
        stack.push( Dub(p->index_[i], cur+1) );
      }
    }
  }

  return max;
}

<|endoftext|>


<commit_before>/*
Copyright © 2003 Richard Lärkäng
Copyright © 2003 Jason Kivlighn <jkivlighn@gmail.com>

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

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

You should have received a copy of the GNU Lesser General Public
License along with this library.  If not, see <http://www.gnu.org/licenses/>
*/

#include "mx2importer.h"

#include <klocale.h>
#include <kdebug.h>

#include <QFile>
#include <QStringList>
#include <QTextStream>

#include "datablocks/recipe.h"

MX2Importer::MX2Importer()
{}

void MX2Importer::parseFile( const QString& filename )
{
	QFile file( filename );
	kDebug() << "loading file: " << filename ;
	if ( file.open( QIODevice::ReadOnly ) ) {
		kDebug() << "file opened" ;
		QDomDocument doc;

		//hopefully a temporary hack, since MasterCook creates invalid xml declarations
		QTextStream stream( &file );
		QString all_data = stream.readAll();
		if ( all_data.startsWith( "<?xml" ) )
			all_data.remove( 0, all_data.indexOf( "?>" ) + 2 );

		QString error;
		int line;
		int column;
		if ( !doc.setContent( all_data, &error, &line, &column ) ) {
			kDebug() << QString( "error: \"%1\" at line %2, column %3" ).arg( error ).arg( line ).arg( column ) ;
			setErrorMsg( i18n( "\"%1\" at line %2, column %3.  This may not be a *.mx2 file.", error, line, column ) );
			return ;
		}

		QDomElement mx2 = doc.documentElement();

		// TODO Check if there are changes between versions
		if ( mx2.tagName() != "mx2" /*|| mx2.attribute("source") != "MasterCook 5.0"*/ ) {
			setErrorMsg( i18n( "This file does not appear to be a *.mx2 file" ) );
			return ;
		}

		QDomNodeList l = mx2.childNodes();

		for ( int i = 0; i < l.count(); i++ ) {
			QDomElement el = l.item( i ).toElement();

			if ( el.tagName() == "RcpE" ) {
				Recipe recipe;
				recipe.title = el.attribute( "name" );

				Element author( el.attribute( "author" ) );
				recipe.authorList.append( author );

				readRecipe( el.childNodes(), &recipe );
				add
					( recipe );
			}
		}
	}
	else
		setErrorMsg( i18n( "Unable to open file." ) );
}

MX2Importer::~MX2Importer()
{
}

void MX2Importer::readRecipe( const QDomNodeList& l, Recipe *recipe )
{
	for ( int i = 0; i < l.count(); i++ ) {
		QDomElement el = l.item( i ).toElement();

		QString tagName = el.tagName();
		if ( tagName == "Serv" ) {
			recipe->yield.amount = el.attribute( "qty" ).toInt();
			recipe->yield.type = i18n("servings");
		}
		else if ( tagName == "PrpT" )
			recipe->prepTime = QTime::fromString( el.attribute( "elapsed" ), "h:mm" );
		else if ( tagName == "CatS" ) {
			QDomNodeList categories = el.childNodes();
			for ( int j = 0; j < categories.count(); j++ ) {
				QDomElement c = categories.item( j ).toElement();
				if ( c.tagName() == "CatT" ) {
					if ( c.text().length() > 0 ) {
						Element cat( c.text().trimmed() );
						recipe->categoryList.append( cat );
					}
				}
			}
		}
		else if ( tagName == "IngR" ) {
			Ingredient new_ing( el.attribute( "name" ),
			                    el.attribute( "qty" ).toDouble(),
			                    Unit( el.attribute( "unit" ), el.attribute( "qty" ).toDouble() ) );
			if ( el.hasChildNodes() ) {
				QDomNodeList iChilds = el.childNodes();
				for ( int j = 0; j < iChilds.count(); j++ ) {
					QDomElement iChild = iChilds.item( j ).toElement();
					if ( iChild.tagName() == "IPrp" )
						new_ing.prepMethodList.append( Element(iChild.text().trimmed()) );
					else if ( iChild.tagName() == "INtI" )
						; // TODO: What does it mean?... ingredient nutrient info?
				}
			}
			recipe->ingList.append( new_ing );
		}
		else if ( tagName == "DirS" ) {
			QStringList directions;
			QDomNodeList dirs = el.childNodes();
			for ( int j = 0; j < dirs.count(); j++ ) {
				QDomElement dir = dirs.item( j ).toElement();
				if ( dir.tagName() == "DirT" )
					directions.append( dir.text().trimmed() );
			}
			QString directionsText;

			// TODO This is copied from RecipeML, maybe a QStringList
			//	for directions in Recipe instead?
			if ( directions.count() > 1 ) {
				for ( int i = 1; i <= directions.count(); i++ ) {
					if ( i != 1 ) {
						directionsText += "\n\n";
					}

					QString sWith = QString( "%1. " ).arg( i );
					QString text = directions[ i - 1 ];
					if ( !text.trimmed().startsWith( sWith ) )
						directionsText += sWith;
					directionsText += text;
				}
			}
			else
				directionsText = directions[ 0 ];

			recipe->instructions = directionsText;
		}
		else if ( tagName == "SrvI" ) {
			// Don't know what to do with it, for now add it to directions
			// btw lets hope this is read after the directions
			recipe->instructions += "\n\n" + el.text().trimmed();
		}
		else if ( tagName == "Note" ) {
			// Don't know what to do with it, for now add it to directions
			// btw lets hope this is read after the directions
			recipe->instructions += "\n\n" + el.text().trimmed();
		}
		else if ( tagName == "Nutr" ) {
			//example: <Nutr>Per Serving (excluding unknown items): 51 Calories; 6g Fat (99.5% calories from fat); trace Protein; trace Carbohydrate; 0g Dietary Fiber; 16mg Cholesterol; 137mg Sodium.  Exchanges: 1 Fat.</Nutr>
			// Don't know what to do with it, for now add it to directions
			// btw lets hope this is read after the directions
			recipe->instructions += "\n\n" + el.text().trimmed();
		}
		/* tags to check for (example follows:
		<Srce>SARA'S SECRETS with Sara Moulton - (Show # SS-1B43) - from the TV FOOD NETWORK</Srce>
		<AltS label="Formatted for MC7" source="07-11-2003  by Joe Comiskey - Mad's Recipe Emporium"/>
		*/
		// TODO Have i missed some tag?
	}
}

<commit_msg>Read ranges and fractions from mx2 files.<commit_after>/*
Copyright © 2003 Richard Lärkäng
Copyright © 2003 Jason Kivlighn <jkivlighn@gmail.com>

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

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

You should have received a copy of the GNU Lesser General Public
License along with this library.  If not, see <http://www.gnu.org/licenses/>
*/

#include "mx2importer.h"

#include <klocale.h>
#include <kdebug.h>

#include <QFile>
#include <QStringList>
#include <QTextStream>

#include "datablocks/mixednumber.h"
#include "datablocks/recipe.h"

MX2Importer::MX2Importer()
{}

void MX2Importer::parseFile( const QString& filename )
{
	QFile file( filename );
	kDebug() << "loading file: " << filename ;
	if ( file.open( QIODevice::ReadOnly ) ) {
		kDebug() << "file opened" ;
		QDomDocument doc;

		//hopefully a temporary hack, since MasterCook creates invalid xml declarations
		QTextStream stream( &file );
		QString all_data = stream.readAll();
		if ( all_data.startsWith( "<?xml" ) )
			all_data.remove( 0, all_data.indexOf( "?>" ) + 2 );

		QString error;
		int line;
		int column;
		if ( !doc.setContent( all_data, &error, &line, &column ) ) {
			kDebug() << QString( "error: \"%1\" at line %2, column %3" ).arg( error ).arg( line ).arg( column ) ;
			setErrorMsg( i18n( "\"%1\" at line %2, column %3.  This may not be a *.mx2 file.", error, line, column ) );
			return ;
		}

		QDomElement mx2 = doc.documentElement();

		// TODO Check if there are changes between versions
		if ( mx2.tagName() != "mx2" /*|| mx2.attribute("source") != "MasterCook 5.0"*/ ) {
			setErrorMsg( i18n( "This file does not appear to be a *.mx2 file" ) );
			return ;
		}

		QDomNodeList l = mx2.childNodes();

		for ( int i = 0; i < l.count(); i++ ) {
			QDomElement el = l.item( i ).toElement();

			if ( el.tagName() == "RcpE" ) {
				Recipe recipe;
				recipe.title = el.attribute( "name" );

				Element author( el.attribute( "author" ) );
				recipe.authorList.append( author );

				readRecipe( el.childNodes(), &recipe );
				add
					( recipe );
			}
		}
	}
	else
		setErrorMsg( i18n( "Unable to open file." ) );
}

MX2Importer::~MX2Importer()
{
}

void MX2Importer::readRecipe( const QDomNodeList& l, Recipe *recipe )
{
	for ( int i = 0; i < l.count(); i++ ) {
		QDomElement el = l.item( i ).toElement();

		QString tagName = el.tagName();
		if ( tagName == "Serv" ) {
			recipe->yield.amount = el.attribute( "qty" ).toInt();
			recipe->yield.type = i18n("servings");
		}
		else if ( tagName == "PrpT" )
			recipe->prepTime = QTime::fromString( el.attribute( "elapsed" ), "h:mm" );
		else if ( tagName == "CatS" ) {
			QDomNodeList categories = el.childNodes();
			for ( int j = 0; j < categories.count(); j++ ) {
				QDomElement c = categories.item( j ).toElement();
				if ( c.tagName() == "CatT" ) {
					if ( c.text().length() > 0 ) {
						Element cat( c.text().trimmed() );
						recipe->categoryList.append( cat );
					}
				}
			}
		}
		else if ( tagName == "IngR" ) {
			double quantity1=0, quantity2=0, offset=0;
			bool ok;
			QStringList qtyStrList = el.attribute( "qty" ).split( " " );
			if ( !qtyStrList.isEmpty() ) {
				quantity1 = MixedNumber::fromString( qtyStrList.first(), &ok, false ).toDouble();
				if ( !ok )
					quantity1 = 0;
				if ( qtyStrList.constBegin() != qtyStrList.constEnd() )
					quantity2 = MixedNumber::fromString( qtyStrList.last(), &ok, false ).toDouble();
						if ( !ok )
							quantity2 = 0;
			}
			offset = quantity2 - quantity1;
			Ingredient new_ing( el.attribute( "name" ),
			                    quantity1,
			                    Unit( el.attribute( "unit" ), quantity1 ) );
			new_ing.amount_offset = offset;
			if ( el.hasChildNodes() ) {
				QDomNodeList iChilds = el.childNodes();
				for ( int j = 0; j < iChilds.count(); j++ ) {
					QDomElement iChild = iChilds.item( j ).toElement();
					if ( iChild.tagName() == "IPrp" )
						new_ing.prepMethodList.append( Element(iChild.text().trimmed()) );
					else if ( iChild.tagName() == "INtI" )
						; // TODO: What does it mean?... ingredient nutrient info?
				}
			}
			recipe->ingList.append( new_ing );
		}
		else if ( tagName == "DirS" ) {
			QStringList directions;
			QDomNodeList dirs = el.childNodes();
			for ( int j = 0; j < dirs.count(); j++ ) {
				QDomElement dir = dirs.item( j ).toElement();
				if ( dir.tagName() == "DirT" )
					directions.append( dir.text().trimmed() );
			}
			QString directionsText;

			// TODO This is copied from RecipeML, maybe a QStringList
			//	for directions in Recipe instead?
			if ( directions.count() > 1 ) {
				for ( int i = 1; i <= directions.count(); i++ ) {
					if ( i != 1 ) {
						directionsText += "\n\n";
					}

					QString sWith = QString( "%1. " ).arg( i );
					QString text = directions[ i - 1 ];
					if ( !text.trimmed().startsWith( sWith ) )
						directionsText += sWith;
					directionsText += text;
				}
			}
			else
				directionsText = directions[ 0 ];

			recipe->instructions = directionsText;
		}
		else if ( tagName == "SrvI" ) {
			// Don't know what to do with it, for now add it to directions
			// btw lets hope this is read after the directions
			recipe->instructions += "\n\n" + el.text().trimmed();
		}
		else if ( tagName == "Note" ) {
			// Don't know what to do with it, for now add it to directions
			// btw lets hope this is read after the directions
			recipe->instructions += "\n\n" + el.text().trimmed();
		}
		else if ( tagName == "Nutr" ) {
			//example: <Nutr>Per Serving (excluding unknown items): 51 Calories; 6g Fat (99.5% calories from fat); trace Protein; trace Carbohydrate; 0g Dietary Fiber; 16mg Cholesterol; 137mg Sodium.  Exchanges: 1 Fat.</Nutr>
			// Don't know what to do with it, for now add it to directions
			// btw lets hope this is read after the directions
			recipe->instructions += "\n\n" + el.text().trimmed();
		}
		/* tags to check for (example follows:
		<Srce>SARA'S SECRETS with Sara Moulton - (Show # SS-1B43) - from the TV FOOD NETWORK</Srce>
		<AltS label="Formatted for MC7" source="07-11-2003  by Joe Comiskey - Mad's Recipe Emporium"/>
		*/
		// TODO Have i missed some tag?
	}
}

<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Jan Steemann
////////////////////////////////////////////////////////////////////////////////

#include "ServerJob.h"

#include "Basics/MutexLocker.h"
#include "Cluster/ClusterInfo.h"
#include "Cluster/HeartbeatThread.h"
#include "Dispatcher/DispatcherQueue.h"
#include "Logger/Logger.h"
#include "RestServer/DatabaseFeature.h"
#include "V8/v8-utils.h"
#include "V8Server/V8Context.h"
#include "V8Server/V8DealerFeature.h"
#include "VocBase/server.h"
#include "VocBase/vocbase.h"

#include <iostream>

using namespace arangodb;
using namespace arangodb::application_features;
using namespace arangodb::rest;

static arangodb::Mutex ExecutorLock;

////////////////////////////////////////////////////////////////////////////////
/// @brief constructs a new db server job
////////////////////////////////////////////////////////////////////////////////

ServerJob::ServerJob(HeartbeatThread* heartbeat)
    : Job("HttpServerJob"),
      _heartbeat(heartbeat),
      _shutdown(0),
      _abandon(false) {}

////////////////////////////////////////////////////////////////////////////////
/// @brief destructs a db server job
////////////////////////////////////////////////////////////////////////////////

ServerJob::~ServerJob() {}

void ServerJob::work() {
  LOG(TRACE) << "starting plan update handler";

  if (_shutdown != 0) {
    return;
  }

  _heartbeat->setReady();

  bool result;

  {
    // only one plan change at a time
    MUTEX_LOCKER(mutexLocker, ExecutorLock);

    result = execute();
  }

  _heartbeat->removeDispatchedJob(result);
}

bool ServerJob::cancel() { return false; }

void ServerJob::cleanup(DispatcherQueue* queue) {
  queue->removeJob(this);
  delete this;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief execute job
////////////////////////////////////////////////////////////////////////////////

bool ServerJob::execute() {
  // default to system database

  DatabaseFeature* database = dynamic_cast<DatabaseFeature*>(
    ApplicationServer::lookupFeature("Database"));

  TRI_vocbase_t* const vocbase = database->vocbase();

  if (vocbase == nullptr) {

    std::cout << "+++++++++++++ oops ++++++++++++++" << std::endl;
    // database is gone
    return false;
  }

  TRI_DEFER(TRI_ReleaseVocBase(vocbase));

  V8Context* context = V8DealerFeature::DEALER->enterContext(vocbase, true);

  if (context == nullptr) {
    return false;
  }

  bool ok = true;
  auto isolate = context->_isolate;

  try {
    v8::HandleScope scope(isolate);

    // execute script inside the context
    auto file = TRI_V8_ASCII_STRING("handle-plan-change");
    auto content =
        TRI_V8_ASCII_STRING("require('@arangodb/cluster').handlePlanChange();");
    v8::Handle<v8::Value> res = TRI_ExecuteJavaScriptString(
        isolate, isolate->GetCurrentContext(), content, file, false);
    if (res->IsBoolean() && res->IsTrue()) {
      LOG(ERR) << "An error occurred whilst executing the handlePlanChange in "
                  "JavaScript.";
      ok = false;  // The heartbeat thread will notice this!
    }
    // invalidate our local cache, even if an error occurred
    ClusterInfo::instance()->flush();
  } catch (...) {
  }

  V8DealerFeature::DEALER->exitContext(context);

  return ok;
}
<commit_msg>Use vocbase.<commit_after>////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Jan Steemann
////////////////////////////////////////////////////////////////////////////////

#include "ServerJob.h"

#include "Basics/MutexLocker.h"
#include "Cluster/ClusterInfo.h"
#include "Cluster/HeartbeatThread.h"
#include "Dispatcher/DispatcherQueue.h"
#include "Logger/Logger.h"
#include "RestServer/DatabaseFeature.h"
#include "V8/v8-utils.h"
#include "V8Server/V8Context.h"
#include "V8Server/V8DealerFeature.h"
#include "VocBase/server.h"
#include "VocBase/vocbase.h"

#include <iostream>

using namespace arangodb;
using namespace arangodb::application_features;
using namespace arangodb::rest;

static arangodb::Mutex ExecutorLock;

////////////////////////////////////////////////////////////////////////////////
/// @brief constructs a new db server job
////////////////////////////////////////////////////////////////////////////////

ServerJob::ServerJob(HeartbeatThread* heartbeat)
    : Job("HttpServerJob"),
      _heartbeat(heartbeat),
      _shutdown(0),
      _abandon(false) {}

////////////////////////////////////////////////////////////////////////////////
/// @brief destructs a db server job
////////////////////////////////////////////////////////////////////////////////

ServerJob::~ServerJob() {}

void ServerJob::work() {
  LOG(TRACE) << "starting plan update handler";

  if (_shutdown != 0) {
    return;
  }

  _heartbeat->setReady();

  bool result;

  {
    // only one plan change at a time
    MUTEX_LOCKER(mutexLocker, ExecutorLock);

    result = execute();
  }

  _heartbeat->removeDispatchedJob(result);
}

bool ServerJob::cancel() { return false; }

void ServerJob::cleanup(DispatcherQueue* queue) {
  queue->removeJob(this);
  delete this;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief execute job
////////////////////////////////////////////////////////////////////////////////

bool ServerJob::execute() {
  // default to system database

  DatabaseFeature* database = dynamic_cast<DatabaseFeature*>(
    ApplicationServer::lookupFeature("Database"));

  TRI_vocbase_t* const vocbase = database->vocbase();

  if (vocbase == nullptr) {

    std::cout << "+++++++++++++ oops ++++++++++++++" << std::endl;
    // database is gone
    return false;
  }

  TRI_UseVocBase(vocbase);
  TRI_DEFER(TRI_ReleaseVocBase(vocbase));

  V8Context* context = V8DealerFeature::DEALER->enterContext(vocbase, true);

  if (context == nullptr) {
    return false;
  }

  bool ok = true;
  auto isolate = context->_isolate;

  try {
    v8::HandleScope scope(isolate);

    // execute script inside the context
    auto file = TRI_V8_ASCII_STRING("handle-plan-change");
    auto content =
        TRI_V8_ASCII_STRING("require('@arangodb/cluster').handlePlanChange();");
    v8::Handle<v8::Value> res = TRI_ExecuteJavaScriptString(
        isolate, isolate->GetCurrentContext(), content, file, false);
    if (res->IsBoolean() && res->IsTrue()) {
      LOG(ERR) << "An error occurred whilst executing the handlePlanChange in "
                  "JavaScript.";
      ok = false;  // The heartbeat thread will notice this!
    }
    // invalidate our local cache, even if an error occurred
    ClusterInfo::instance()->flush();
  } catch (...) {
  }

  V8DealerFeature::DEALER->exitContext(context);

  return ok;
}
<|endoftext|>


<commit_before>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright (C) 2011 - 2015                                                  *
 * Dominik Charousset <dominik.charousset (at) haw-hamburg.de>                *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#include "caf/config.hpp"

// exclude this suite; seems to be too much to swallow for MSVC
#ifndef CAF_WINDOWS

#define CAF_SUITE typed_spawn
#include "caf/test/unit_test.hpp"

#include "caf/string_algorithms.hpp"

#include "caf/all.hpp"

using namespace std;
using namespace caf;

using passed_atom = caf::atom_constant<caf::atom("passed")>;

namespace {

// check invariants of type system
using dummy1 = typed_actor<reacts_to<int, int>,
                           replies_to<double>::with<double>>;

using dummy2 = dummy1::extend<reacts_to<ok_atom>>;

static_assert(std::is_convertible<dummy2, dummy1>::value,
              "handle not assignable to narrower definition");

static_assert(! std::is_convertible<dummy1, dummy2>::value,
              "handle is assignable to broader definition");

/******************************************************************************
 *            simple request/response test            *
 ******************************************************************************/

struct my_request {
  int a;
  int b;
};

using server_type = typed_actor<replies_to<my_request>::with<bool>>;

bool operator==(const my_request& lhs, const my_request& rhs) {
  return lhs.a == rhs.a && lhs.b == rhs.b;
}

server_type::behavior_type typed_server1() {
  return {
    [](const my_request& req) {
      return req.a == req.b;
    }
  };
}

server_type::behavior_type typed_server2(server_type::pointer) {
  return typed_server1();
}

class typed_server3 : public server_type::base {

public:

  typed_server3(const string& line, actor buddy) { send(buddy, line); }

  behavior_type make_behavior() override { return typed_server2(this); }

};

void client(event_based_actor* self, actor parent, server_type serv) {
  self->sync_send(serv, my_request{0, 0}).then(
    [=](bool val1) {
      CAF_CHECK_EQUAL(val1, true);
      self->sync_send(serv, my_request{10, 20}).then(
        [=](bool val2) {
          CAF_CHECK_EQUAL(val2, false);
          self->send(parent, passed_atom::value);
        }
      );
    }
  );
}

void test_typed_spawn(server_type ts) {
  scoped_actor self;
  self->send(ts, my_request{1, 2});
  self->receive(
    [](bool value) {
      CAF_CHECK_EQUAL(value, false);
    }
  );
  self->send(ts, my_request{42, 42});
  self->receive(
    [](bool value) {
      CAF_CHECK_EQUAL(value, true);
    }
  );
  self->sync_send(ts, my_request{10, 20}).await(
    [](bool value) {
      CAF_CHECK_EQUAL(value, false);
    }
  );
  self->sync_send(ts, my_request{0, 0}).await(
    [](bool value) {
      CAF_CHECK_EQUAL(value, true);
    }
  );
  self->spawn<monitored>(client, self, ts);
  self->receive(
    [](passed_atom) {
      CAF_MESSAGE("received `passed_atom`");
    }
  );
  self->receive(
    [](const down_msg& dmsg) {
      CAF_CHECK_EQUAL(dmsg.reason, exit_reason::normal);
    }
  );
  self->send_exit(ts, exit_reason::user_shutdown);
}

/******************************************************************************
 *      test skipping of messages intentionally + using become()      *
 ******************************************************************************/

struct get_state_msg {};

using event_testee_type = typed_actor<replies_to<get_state_msg>::with<string>,
                                      replies_to<string>::with<void>,
                                      replies_to<float>::with<void>,
                                      replies_to<int>::with<int>>;

class event_testee : public event_testee_type::base {

public:

  behavior_type wait4string() {
    return {on<get_state_msg>() >> [] { return "wait4string"; },
        on<string>() >> [=] { become(wait4int()); },
        (on<float>() || on<int>()) >> skip_message};
  }

  behavior_type wait4int() {
    return {
      on<get_state_msg>() >> [] { return "wait4int"; },
      on<int>() >> [=]()->int {become(wait4float());
        return 42;
      },
      (on<float>() || on<string>()) >> skip_message
    };
  }

  behavior_type wait4float() {
    return {
      on<get_state_msg>() >> [] {
        return "wait4float";
      },
      on<float>() >> [=] { become(wait4string()); },
      (on<string>() || on<int>()) >> skip_message};
  }

  behavior_type make_behavior() override {
    return wait4int();
  }

};

/******************************************************************************
 *             simple 'forwarding' chain              *
 ******************************************************************************/

using string_actor = typed_actor<replies_to<string>::with<string>>;

void simple_relay(string_actor::pointer self, string_actor master, bool leaf) {
  string_actor next =
    leaf ? spawn_typed(simple_relay, master, false) : master;
  self->link_to(next);
  self->become(
    [=](const string& str) {
      return self->sync_send(next, str).then(
        [](const string & answer)->string {
          return answer;
        }
      );
  });
}

string_actor::behavior_type simple_string_reverter() {
  return {
    [](const string& str) {
      return string{str.rbegin(), str.rend()};
    }
  };
}

/******************************************************************************
 *            sending typed actor handles             *
 ******************************************************************************/

using int_actor = typed_actor<replies_to<int>::with<int>>;

int_actor::behavior_type int_fun() {
  return {
    [](int i) { return i * i; }
  };
}

behavior foo(event_based_actor* self) {
  return {
    [=](int i, int_actor server) {
      return self->sync_send(server, i).then([=](int result) -> int {
        self->quit(exit_reason::normal);
        return result;
      });
    }
  };
}

int_actor::behavior_type int_fun2(int_actor::pointer self) {
  self->trap_exit(true);
  return {
    [=](int i) {
      self->monitor(self->current_sender());
      return i * i;
    },
    [=](const down_msg& dm) {
      CAF_CHECK_EQUAL(dm.reason, exit_reason::normal);
      self->quit();
    },
    [=](const exit_msg&) {
      CAF_TEST_ERROR("Unexpected message: "
                     << to_string(self->current_message()));
    }
  };
}

behavior foo2(event_based_actor* self) {
  return {
    [=](int i, int_actor server) {
      return self->sync_send(server, i).then([=](int result) -> int {
        self->quit(exit_reason::normal);
        return result;
      });
    }
  };
}

struct fixture {
  fixture() {
    announce<get_state_msg>("get_state_msg");
    announce<int_actor>("int_actor");
    announce<my_request>("my_request", &my_request::a, &my_request::b);
  }

  ~fixture() {
    await_all_actors_done();
    shutdown();
  }
};

} // namespace <anonymous>

CAF_TEST_FIXTURE_SCOPE(typed_spawn_tests, fixture)

/******************************************************************************
 *                             put it all together                            *
 ******************************************************************************/

CAF_TEST(typed_spawns) {
  // run test series with typed_server(1|2)
  test_typed_spawn(spawn_typed(typed_server1));
  await_all_actors_done();
  CAF_MESSAGE("finished test series with `typed_server1`");

  test_typed_spawn(spawn_typed(typed_server2));
  await_all_actors_done();
  CAF_MESSAGE("finished test series with `typed_server2`");
  {
    scoped_actor self;
    test_typed_spawn(spawn_typed<typed_server3>("hi there", self));
    self->receive(on("hi there") >> [] {
      CAF_MESSAGE("received \"hi there\"");
    });
  }
}

CAF_TEST(test_event_testee) {
  // run test series with event_testee
  scoped_actor self;
  auto et = self->spawn_typed<event_testee>();
  string result;
  self->send(et, 1);
  self->send(et, 2);
  self->send(et, 3);
  self->send(et, .1f);
  self->send(et, "hello event testee!");
  self->send(et, .2f);
  self->send(et, .3f);
  self->send(et, "hello again event testee!");
  self->send(et, "goodbye event testee!");
  typed_actor<replies_to<get_state_msg>::with<string>> sub_et = et;
  // $:: is the anonymous namespace
  set<string> iface{"caf::replies_to<get_state_msg>::with<@str>",
                    "caf::replies_to<@str>::with<void>",
                    "caf::replies_to<float>::with<void>",
                    "caf::replies_to<@i32>::with<@i32>"};
  CAF_CHECK_EQUAL(join(sub_et->message_types(), ","), join(iface, ","));
  self->send(sub_et, get_state_msg{});
  // we expect three 42s
  int i = 0;
  self->receive_for(i, 3)([](int value) { CAF_CHECK_EQUAL(value, 42); });
  self->receive(
    [&](const string& str) {
      result = str;
    },
    after(chrono::minutes(1)) >> [&] {
      CAF_TEST_ERROR("event_testee does not reply");
      throw runtime_error("event_testee does not reply");
    }
  );
  self->send_exit(et, exit_reason::user_shutdown);
  self->await_all_other_actors_done();
  CAF_CHECK_EQUAL(result, "wait4int");
}

CAF_TEST(test_simple_string_reverter) {
  // run test series with string reverter
  scoped_actor self;
  // actor-under-test
  auto aut = self->spawn_typed<monitored>(simple_relay,
                                          spawn_typed(simple_string_reverter),
                                          true);
  set<string> iface{"caf::replies_to<@str>::with<@str>"};
  CAF_CHECK(aut->message_types() == iface);
  self->sync_send(aut, "Hello World!").await([](const string& answer) {
    CAF_CHECK_EQUAL(answer, "!dlroW olleH");
  });
  anon_send_exit(aut, exit_reason::user_shutdown);
}

CAF_TEST(test_sending_typed_actors) {
  scoped_actor self;
  auto aut = spawn_typed(int_fun);
  self->send(spawn(foo), 10, aut);
  self->receive(
    [](int i) { CAF_CHECK_EQUAL(i, 100); }
  );
  self->send_exit(aut, exit_reason::user_shutdown);
}

CAF_TEST(test_sending_typed_actors_and_down_msg) {
  scoped_actor self;
  auto aut = spawn_typed(int_fun2);
  self->send(spawn(foo2), 10, aut);
  self->receive([](int i) { CAF_CHECK_EQUAL(i, 100); });
}

CAF_TEST_FIXTURE_SCOPE_END()

#endif // CAF_WINDOWS
<commit_msg>Add unit test for latest patch from ufownl<commit_after>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright (C) 2011 - 2015                                                  *
 * Dominik Charousset <dominik.charousset (at) haw-hamburg.de>                *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#include "caf/config.hpp"

// exclude this suite; seems to be too much to swallow for MSVC
#ifndef CAF_WINDOWS

#define CAF_SUITE typed_spawn
#include "caf/test/unit_test.hpp"

#include "caf/string_algorithms.hpp"

#include "caf/all.hpp"

using namespace std;
using namespace caf;

using passed_atom = caf::atom_constant<caf::atom("passed")>;

namespace {

// check invariants of type system
using dummy1 = typed_actor<reacts_to<int, int>,
                           replies_to<double>::with<double>>;

using dummy2 = dummy1::extend<reacts_to<ok_atom>>;

static_assert(std::is_convertible<dummy2, dummy1>::value,
              "handle not assignable to narrower definition");

static_assert(! std::is_convertible<dummy1, dummy2>::value,
              "handle is assignable to broader definition");

/******************************************************************************
 *            simple request/response test            *
 ******************************************************************************/

struct my_request {
  int a;
  int b;
};

using server_type = typed_actor<replies_to<my_request>::with<bool>>;

bool operator==(const my_request& lhs, const my_request& rhs) {
  return lhs.a == rhs.a && lhs.b == rhs.b;
}

server_type::behavior_type typed_server1() {
  return {
    [](const my_request& req) {
      return req.a == req.b;
    }
  };
}

server_type::behavior_type typed_server2(server_type::pointer) {
  return typed_server1();
}

class typed_server3 : public server_type::base {

public:

  typed_server3(const string& line, actor buddy) { send(buddy, line); }

  behavior_type make_behavior() override { return typed_server2(this); }

};

void client(event_based_actor* self, actor parent, server_type serv) {
  self->sync_send(serv, my_request{0, 0}).then(
    [=](bool val1) {
      CAF_CHECK_EQUAL(val1, true);
      self->sync_send(serv, my_request{10, 20}).then(
        [=](bool val2) {
          CAF_CHECK_EQUAL(val2, false);
          self->send(parent, passed_atom::value);
        }
      );
    }
  );
}

void test_typed_spawn(server_type ts) {
  scoped_actor self;
  self->send(ts, my_request{1, 2});
  self->receive(
    [](bool value) {
      CAF_CHECK_EQUAL(value, false);
    }
  );
  self->send(ts, my_request{42, 42});
  self->receive(
    [](bool value) {
      CAF_CHECK_EQUAL(value, true);
    }
  );
  self->sync_send(ts, my_request{10, 20}).await(
    [](bool value) {
      CAF_CHECK_EQUAL(value, false);
    }
  );
  self->sync_send(ts, my_request{0, 0}).await(
    [](bool value) {
      CAF_CHECK_EQUAL(value, true);
    }
  );
  self->spawn<monitored>(client, self, ts);
  self->receive(
    [](passed_atom) {
      CAF_MESSAGE("received `passed_atom`");
    }
  );
  self->receive(
    [](const down_msg& dmsg) {
      CAF_CHECK_EQUAL(dmsg.reason, exit_reason::normal);
    }
  );
  self->send_exit(ts, exit_reason::user_shutdown);
}

/******************************************************************************
 *      test skipping of messages intentionally + using become()      *
 ******************************************************************************/

struct get_state_msg {};

using event_testee_type = typed_actor<replies_to<get_state_msg>::with<string>,
                                      replies_to<string>::with<void>,
                                      replies_to<float>::with<void>,
                                      replies_to<int>::with<int>>;

class event_testee : public event_testee_type::base {

public:

  behavior_type wait4string() {
    return {on<get_state_msg>() >> [] { return "wait4string"; },
        on<string>() >> [=] { become(wait4int()); },
        (on<float>() || on<int>()) >> skip_message};
  }

  behavior_type wait4int() {
    return {
      on<get_state_msg>() >> [] { return "wait4int"; },
      on<int>() >> [=]()->int {become(wait4float());
        return 42;
      },
      (on<float>() || on<string>()) >> skip_message
    };
  }

  behavior_type wait4float() {
    return {
      on<get_state_msg>() >> [] {
        return "wait4float";
      },
      on<float>() >> [=] { become(wait4string()); },
      (on<string>() || on<int>()) >> skip_message};
  }

  behavior_type make_behavior() override {
    return wait4int();
  }

};

/******************************************************************************
 *             simple 'forwarding' chain              *
 ******************************************************************************/

using string_actor = typed_actor<replies_to<string>::with<string>>;

void simple_relay(string_actor::pointer self, string_actor master, bool leaf) {
  string_actor next =
    leaf ? spawn_typed(simple_relay, master, false) : master;
  self->link_to(next);
  self->become(
    [=](const string& str) {
      return self->sync_send(next, str).then(
        [](const string & answer)->string {
          return answer;
        }
      );
  });
}

string_actor::behavior_type simple_string_reverter() {
  return {
    [](const string& str) {
      return string{str.rbegin(), str.rend()};
    }
  };
}

/******************************************************************************
 *            sending typed actor handles             *
 ******************************************************************************/

using int_actor = typed_actor<replies_to<int>::with<int>>;

int_actor::behavior_type int_fun() {
  return {
    [](int i) { return i * i; }
  };
}

behavior foo(event_based_actor* self) {
  return {
    [=](int i, int_actor server) {
      return self->sync_send(server, i).then([=](int result) -> int {
        self->quit(exit_reason::normal);
        return result;
      });
    }
  };
}

int_actor::behavior_type int_fun2(int_actor::pointer self) {
  self->trap_exit(true);
  return {
    [=](int i) {
      self->monitor(self->current_sender());
      return i * i;
    },
    [=](const down_msg& dm) {
      CAF_CHECK_EQUAL(dm.reason, exit_reason::normal);
      self->quit();
    },
    [=](const exit_msg&) {
      CAF_TEST_ERROR("Unexpected message: "
                     << to_string(self->current_message()));
    }
  };
}

behavior foo2(event_based_actor* self) {
  return {
    [=](int i, int_actor server) {
      return self->sync_send(server, i).then([=](int result) -> int {
        self->quit(exit_reason::normal);
        return result;
      });
    }
  };
}

struct fixture {
  fixture() {
    announce<get_state_msg>("get_state_msg");
    announce<int_actor>("int_actor");
    announce<my_request>("my_request", &my_request::a, &my_request::b);
  }

  ~fixture() {
    await_all_actors_done();
    shutdown();
  }
};

} // namespace <anonymous>

CAF_TEST_FIXTURE_SCOPE(typed_spawn_tests, fixture)

/******************************************************************************
 *                             put it all together                            *
 ******************************************************************************/

CAF_TEST(typed_spawns) {
  // run test series with typed_server(1|2)
  test_typed_spawn(spawn_typed(typed_server1));
  await_all_actors_done();
  CAF_MESSAGE("finished test series with `typed_server1`");

  test_typed_spawn(spawn_typed(typed_server2));
  await_all_actors_done();
  CAF_MESSAGE("finished test series with `typed_server2`");
  {
    scoped_actor self;
    test_typed_spawn(spawn_typed<typed_server3>("hi there", self));
    self->receive(on("hi there") >> [] {
      CAF_MESSAGE("received \"hi there\"");
    });
  }
}

CAF_TEST(test_event_testee) {
  // run test series with event_testee
  scoped_actor self;
  auto et = self->spawn_typed<event_testee>();
  string result;
  self->send(et, 1);
  self->send(et, 2);
  self->send(et, 3);
  self->send(et, .1f);
  self->send(et, "hello event testee!");
  self->send(et, .2f);
  self->send(et, .3f);
  self->send(et, "hello again event testee!");
  self->send(et, "goodbye event testee!");
  typed_actor<replies_to<get_state_msg>::with<string>> sub_et = et;
  // $:: is the anonymous namespace
  set<string> iface{"caf::replies_to<get_state_msg>::with<@str>",
                    "caf::replies_to<@str>::with<void>",
                    "caf::replies_to<float>::with<void>",
                    "caf::replies_to<@i32>::with<@i32>"};
  CAF_CHECK_EQUAL(join(sub_et->message_types(), ","), join(iface, ","));
  self->send(sub_et, get_state_msg{});
  // we expect three 42s
  int i = 0;
  self->receive_for(i, 3)([](int value) { CAF_CHECK_EQUAL(value, 42); });
  self->receive(
    [&](const string& str) {
      result = str;
    },
    after(chrono::minutes(1)) >> [&] {
      CAF_TEST_ERROR("event_testee does not reply");
      throw runtime_error("event_testee does not reply");
    }
  );
  self->send_exit(et, exit_reason::user_shutdown);
  self->await_all_other_actors_done();
  CAF_CHECK_EQUAL(result, "wait4int");
}

CAF_TEST(test_simple_string_reverter) {
  // run test series with string reverter
  scoped_actor self;
  // actor-under-test
  auto aut = self->spawn_typed<monitored>(simple_relay,
                                          spawn_typed(simple_string_reverter),
                                          true);
  set<string> iface{"caf::replies_to<@str>::with<@str>"};
  CAF_CHECK(aut->message_types() == iface);
  self->sync_send(aut, "Hello World!").await([](const string& answer) {
    CAF_CHECK_EQUAL(answer, "!dlroW olleH");
  });
  anon_send_exit(aut, exit_reason::user_shutdown);
}

CAF_TEST(test_sending_typed_actors) {
  scoped_actor self;
  auto aut = spawn_typed(int_fun);
  self->send(spawn(foo), 10, aut);
  self->receive(
    [](int i) { CAF_CHECK_EQUAL(i, 100); }
  );
  self->send_exit(aut, exit_reason::user_shutdown);
}

CAF_TEST(test_sending_typed_actors_and_down_msg) {
  scoped_actor self;
  auto aut = spawn_typed(int_fun2);
  self->send(spawn(foo2), 10, aut);
  self->receive([](int i) { CAF_CHECK_EQUAL(i, 100); });
}

CAF_TEST(check_signature) {
  using foo_type = typed_actor<replies_to<put_atom>::
                               with_either<ok_atom>::or_else<error_atom>>;
  using foo_result_type = either<ok_atom>::or_else<error_atom>;
  using bar_type = typed_actor<reacts_to<ok_atom>, reacts_to<error_atom>>;
  auto foo_action = [](foo_type::pointer self) -> foo_type::behavior_type {
    return {
      [=] (put_atom) -> foo_result_type {
        self->quit();
        return {ok_atom::value};
      }
    };
  };
  auto bar_action = [=](bar_type::pointer self) -> bar_type::behavior_type {
    auto foo = self->spawn_typed<linked>(foo_action);
    self->send(foo, put_atom::value);
    return {
      [=](ok_atom) {
        self->quit();
      },
      [=](error_atom) {
        self->quit(exit_reason::user_defined);
      }
    };
  };
  scoped_actor self;
  self->spawn_typed<monitored>(bar_action);
  self->receive(
    [](const down_msg& dm) {
      CAF_CHECK_EQUAL(dm.reason, exit_reason::normal);
    }
  );
}

CAF_TEST_FIXTURE_SCOPE_END()

#endif // CAF_WINDOWS
<|endoftext|>