<commit_before>//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Wed Nov 30 01:02:57 PST 2016
// Last Modified: Fri Dec  2 03:45:34 PST 2016
// Filename:      tool-filter.cpp
// URL:           https://github.com/craigsapp/minHumdrum/blob/master/src/tool-filter.cpp
// Syntax:        C++11
// vim:           syntax=cpp ts=3 noexpandtab nowrap
//
// Description:   Example of extracting a 2D pitch grid from
//                a score for dissonance analysis.
//

#include "tool-filter.h"

// tools which filter can process:
#include "tool-autobeam.h"
#include "tool-autostem.h"
#include "tool-extract.h"
#include "tool-recip.h"
#include "tool-satb2gs.h"
#include "tool-metlev.h"
#include "tool-transpose.h"
#include "HumRegex.h"

#include <algorithm>
#include <cmath>

using namespace std;

namespace hum {

// START_MERGE

#define RUNTOOL(NAME, INFILE, COMMAND, STATUS)     \
	Tool_##NAME *tool = new Tool_##NAME;            \
	tool->process(COMMAND);                         \
	tool->run(INFILE);                              \
	if (tool->hasError()) {                         \
		status = false;                              \
		tool->getError(cerr);                        \
		delete tool;                                 \
		break;                                       \
	} else if (tool->hasHumdrumText()) {            \
		INFILE.readString(tool->getHumdrumText());   \
	}                                               \
	delete tool;

////////////////////////////////
//
// Tool_filter::Tool_filter -- Set the recognized options for the tool.
//

Tool_filter::Tool_filter(void) {
	define("debug=b", "print debug statement");
}

/////////////////////////////////
//
// Tool_filter::run -- Primary interfaces to the tool.
//

bool Tool_filter::run(const string& indata, ostream& out) {
	HumdrumFile infile(indata);
	bool status = run(infile);
	if (hasAnyText()) {
		getAllText(out);
	} else {
		out << infile;
	}
	return status;
}

bool Tool_filter::run(HumdrumFile& infile, ostream& out) {
	int status = run(infile);
	if (hasAnyText()) {
		getAllText(out);
	} else {
		out << infile;
	}
	return status;
}

//
// In-place processing of file:
//

bool Tool_filter::run(HumdrumFile& infile) {
	initialize(infile);

	bool status = true;
	vector<pair<string, string> > commands;
	getCommandList(commands, infile);
	for (int i=0; i<(int)commands.size(); i++) {
		if (commands[i].first == "autobeam") {
			RUNTOOL(autobeam, infile, commands[i].second, status);
		} else if (commands[i].first == "autostem") {
			RUNTOOL(autostem, infile, commands[i].second, status);
		} else if (commands[i].first == "extract") {
			RUNTOOL(extract, infile, commands[i].second, status);
		} else if (commands[i].first == "metlev") {
			RUNTOOL(metlev, infile, commands[i].second, status);
		} else if (commands[i].first == "satb2gs") {
			RUNTOOL(satb2gs, infile, commands[i].second, status);
		} else if (commands[i].first == "recip") {
			RUNTOOL(recip, infile, commands[i].second, status);
		} else if (commands[i].first == "transpose") {
			RUNTOOL(transpose, infile, commands[i].second, status);
		}
	}

	// Re-load the text for each line from their tokens in case any
	// updates are needed from token changes.
	infile.createLinesFromTokens();
	return status;
}

//////////////////////////////
//
// Tool_filter::getCommandList --
//

void Tool_filter::getCommandList(vector<pair<string, string> >& commands,
		HumdrumFile& infile) {

	vector<HumdrumLine*> refs = infile.getReferenceRecords();
	pair<string, string> entry;
	string tag = "filter";
	vector<string> clist;
	HumRegex hre;
   if (m_variant.size() > 0) {
		tag += "-";
		tag += m_variant;
	}
	for (int i=0; i<(int)refs.size(); i++) {
		if (refs[i]->getReferenceKey() != tag) {
			continue;
		}
		string command = refs[i]->getReferenceValue();
		hre.split(clist, command, "\\s*\\|\\s*");
		for (int j=0; j<(int)clist.size(); j++) {
			if (hre.search(clist[j], "^\\s*([^\\s]+)")) {
				entry.first  = hre.getMatch(1);
				entry.second = clist[j];
				commands.push_back(entry);
			}
		}
	}
}

//////////////////////////////
//
// Tool_filter::initialize -- extract time signature lines for
//    each **kern spine in file.
//

void Tool_filter::initialize(HumdrumFile& infile) {
	m_debugQ = getBoolean("debug");
}

// END_MERGE

} // end namespace hum

<commit_msg>Added myank to filter.<commit_after>//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Wed Nov 30 01:02:57 PST 2016
// Last Modified: Fri Dec  2 03:45:34 PST 2016
// Filename:      tool-filter.cpp
// URL:           https://github.com/craigsapp/minHumdrum/blob/master/src/tool-filter.cpp
// Syntax:        C++11
// vim:           syntax=cpp ts=3 noexpandtab nowrap
//
// Description:   Example of extracting a 2D pitch grid from
//                a score for dissonance analysis.
//

#include "tool-filter.h"

// tools which filter can process:
#include "tool-autobeam.h"
#include "tool-autostem.h"
#include "tool-extract.h"
#include "tool-recip.h"
#include "tool-satb2gs.h"
#include "tool-metlev.h"
#include "tool-myank.h"
#include "tool-transpose.h"
#include "HumRegex.h"

#include <algorithm>
#include <cmath>

using namespace std;

namespace hum {

// START_MERGE

#define RUNTOOL(NAME, INFILE, COMMAND, STATUS)     \
	Tool_##NAME *tool = new Tool_##NAME;            \
	tool->process(COMMAND);                         \
	tool->run(INFILE);                              \
	if (tool->hasError()) {                         \
		status = false;                              \
		tool->getError(cerr);                        \
		delete tool;                                 \
		break;                                       \
	} else if (tool->hasHumdrumText()) {            \
		INFILE.readString(tool->getHumdrumText());   \
	}                                               \
	delete tool;

////////////////////////////////
//
// Tool_filter::Tool_filter -- Set the recognized options for the tool.
//

Tool_filter::Tool_filter(void) {
	define("debug=b", "print debug statement");
}

/////////////////////////////////
//
// Tool_filter::run -- Primary interfaces to the tool.
//

bool Tool_filter::run(const string& indata, ostream& out) {
	HumdrumFile infile(indata);
	bool status = run(infile);
	if (hasAnyText()) {
		getAllText(out);
	} else {
		out << infile;
	}
	return status;
}

bool Tool_filter::run(HumdrumFile& infile, ostream& out) {
	int status = run(infile);
	if (hasAnyText()) {
		getAllText(out);
	} else {
		out << infile;
	}
	return status;
}

//
// In-place processing of file:
//

bool Tool_filter::run(HumdrumFile& infile) {
	initialize(infile);

	bool status = true;
	vector<pair<string, string> > commands;
	getCommandList(commands, infile);
	for (int i=0; i<(int)commands.size(); i++) {
		if (commands[i].first == "autobeam") {
			RUNTOOL(autobeam, infile, commands[i].second, status);
		} else if (commands[i].first == "autostem") {
			RUNTOOL(autostem, infile, commands[i].second, status);
		} else if (commands[i].first == "extract") {
			RUNTOOL(extract, infile, commands[i].second, status);
		} else if (commands[i].first == "metlev") {
			RUNTOOL(metlev, infile, commands[i].second, status);
		} else if (commands[i].first == "satb2gs") {
			RUNTOOL(satb2gs, infile, commands[i].second, status);
		} else if (commands[i].first == "recip") {
			RUNTOOL(recip, infile, commands[i].second, status);
		} else if (commands[i].first == "transpose") {
			RUNTOOL(transpose, infile, commands[i].second, status);
		} else if (commands[i].first == "myank") {
			RUNTOOL(myank, infile, commands[i].second, status);
		}
	}

	// Re-load the text for each line from their tokens in case any
	// updates are needed from token changes.
	infile.createLinesFromTokens();
	return status;
}

//////////////////////////////
//
// Tool_filter::getCommandList --
//

void Tool_filter::getCommandList(vector<pair<string, string> >& commands,
		HumdrumFile& infile) {

	vector<HumdrumLine*> refs = infile.getReferenceRecords();
	pair<string, string> entry;
	string tag = "filter";
	vector<string> clist;
	HumRegex hre;
   if (m_variant.size() > 0) {
		tag += "-";
		tag += m_variant;
	}
	for (int i=0; i<(int)refs.size(); i++) {
		if (refs[i]->getReferenceKey() != tag) {
			continue;
		}
		string command = refs[i]->getReferenceValue();
		hre.split(clist, command, "\\s*\\|\\s*");
		for (int j=0; j<(int)clist.size(); j++) {
			if (hre.search(clist[j], "^\\s*([^\\s]+)")) {
				entry.first  = hre.getMatch(1);
				entry.second = clist[j];
				commands.push_back(entry);
			}
		}
	}
}

//////////////////////////////
//
// Tool_filter::initialize -- extract time signature lines for
//    each **kern spine in file.
//

void Tool_filter::initialize(HumdrumFile& infile) {
	m_debugQ = getBoolean("debug");
}

// END_MERGE

} // end namespace hum

<|endoftext|>


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

#include <iostream>
#include <fstream>

#include "game.hpp"

using namespace pg;

int
main(int argc, char **argv)
{
    Game game;
    if (argc >= 2) {
        std::ifstream file(argv[1]);
        game.parse_pgsolver(file, false);
        file.close();
    } else {
        game.parse_pgsolver(std::cin);
    }

    if (argc >= 3) {
        std::ofstream file(argv[2]);
        game.write_dot(file);
        file.close();
    } else {
        game.write_dot(std::cout);
    }
    return 0;
}
<commit_msg>Ensure dotty doesn't apply optimizations<commit_after>/*
 * Copyright 2017-2018 Tom van Dijk, Johannes Kepler University Linz
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <iostream>
#include <fstream>

#include "game.hpp"

using namespace pg;

int
main(int argc, char **argv)
{
    Game game;
    if (argc >= 2) {
        std::ifstream file(argv[1]);
        game.parse_pgsolver(file, false);
        file.close();
    } else {
        game.parse_pgsolver(std::cin, false);
    }

    if (argc >= 3) {
        std::ofstream file(argv[2]);
        game.write_dot(file);
        file.close();
    } else {
        game.write_dot(std::cout);
    }
    return 0;
}
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2016 Couchbase, Inc
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

#include <stdexcept>

#include "polyfill.h"
#include "trace_buffer.h"

TraceBufferChunk::TraceBufferChunk(size_t _generation, size_t _buffer_index)
    : thread_id(std::this_thread::get_id()),
      generation(_generation),
      buffer_index(_buffer_index),
      next_free(0) {
}

bool TraceBufferChunk::isFull() const {
    return next_free == chunk.max_size();
}

size_t TraceBufferChunk::count() const {
    return next_free;
}

size_t TraceBufferChunk::getIndex() const {
    return buffer_index;
}

size_t TraceBufferChunk::getGeneration() const {
    return generation;
}

void TraceBufferChunk::addEvent(TraceEvent&& event) {
    if(isFull()) {
        throw std::out_of_range("All events in chunk have been used");
    }
    chunk[next_free++] = event;
}

TraceEvent& TraceBufferChunk::addEvent() {
    if(isFull()) {
        throw std::out_of_range("All events in chunk have been used");
    }
    return chunk[next_free++];
}

const TraceEvent& TraceBufferChunk::operator[] (const int index) const {
    return chunk[index];
}

/**
 * TraceBuffer implementation that stores events in a fixed-size
 * vector of unique pointers to BufferChunks.
 */
class FixedTraceBuffer : public TraceBuffer {

public:
    FixedTraceBuffer(size_t generation_, size_t buffer_size_)
        : generation(generation_),
          buffer_size(buffer_size_) {
        buffer.reserve(buffer_size);
    }

    ~FixedTraceBuffer() override {

    }

    std::unique_ptr<TraceBufferChunk> getChunk() override {
        if(isFull()) {
            throw std::out_of_range("The TraceBuffer is full");
        }

        auto index(buffer.size());
        buffer.push_back(nullptr);
        return make_unique<TraceBufferChunk>(generation, index);
    }

    void returnChunk(chunk_ptr&& chunk) override {
        if(chunk->getGeneration() != generation) {
            throw std::invalid_argument("Chunk is not from this buffer");
        }

        buffer.at(chunk->getIndex()) = std::move(chunk);
    }

    bool isFull() const override {
        return buffer.size() >= buffer_size;
    }

    size_t getGeneration() const override {
        return generation;
    }

    TraceEventIterator begin() const override {
        std::cerr << buffer.size() << std::endl;
        return TraceEventIterator(buffer.begin());
    }

    TraceEventIterator end() const override {
        return TraceEventIterator(buffer.end());
    }

protected:
    std::vector<chunk_ptr> buffer;
    size_t generation;
    size_t buffer_size;
};

std::unique_ptr<TraceBuffer> make_fixed_buffer(size_t generation,
                                               size_t buffer_size) {
    return make_unique<FixedTraceBuffer>(generation, buffer_size);
}
<commit_msg>Remove debug logging in trace_buffer<commit_after>/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2016 Couchbase, Inc
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

#include <stdexcept>

#include "polyfill.h"
#include "trace_buffer.h"

TraceBufferChunk::TraceBufferChunk(size_t _generation, size_t _buffer_index)
    : thread_id(std::this_thread::get_id()),
      generation(_generation),
      buffer_index(_buffer_index),
      next_free(0) {
}

bool TraceBufferChunk::isFull() const {
    return next_free == chunk.max_size();
}

size_t TraceBufferChunk::count() const {
    return next_free;
}

size_t TraceBufferChunk::getIndex() const {
    return buffer_index;
}

size_t TraceBufferChunk::getGeneration() const {
    return generation;
}

void TraceBufferChunk::addEvent(TraceEvent&& event) {
    if(isFull()) {
        throw std::out_of_range("All events in chunk have been used");
    }
    chunk[next_free++] = event;
}

TraceEvent& TraceBufferChunk::addEvent() {
    if(isFull()) {
        throw std::out_of_range("All events in chunk have been used");
    }
    return chunk[next_free++];
}

const TraceEvent& TraceBufferChunk::operator[] (const int index) const {
    return chunk[index];
}

/**
 * TraceBuffer implementation that stores events in a fixed-size
 * vector of unique pointers to BufferChunks.
 */
class FixedTraceBuffer : public TraceBuffer {

public:
    FixedTraceBuffer(size_t generation_, size_t buffer_size_)
        : generation(generation_),
          buffer_size(buffer_size_) {
        buffer.reserve(buffer_size);
    }

    ~FixedTraceBuffer() override {

    }

    std::unique_ptr<TraceBufferChunk> getChunk() override {
        if(isFull()) {
            throw std::out_of_range("The TraceBuffer is full");
        }

        auto index(buffer.size());
        buffer.push_back(nullptr);
        return make_unique<TraceBufferChunk>(generation, index);
    }

    void returnChunk(chunk_ptr&& chunk) override {
        if(chunk->getGeneration() != generation) {
            throw std::invalid_argument("Chunk is not from this buffer");
        }

        buffer.at(chunk->getIndex()) = std::move(chunk);
    }

    bool isFull() const override {
        return buffer.size() >= buffer_size;
    }

    size_t getGeneration() const override {
        return generation;
    }

    TraceEventIterator begin() const override {
        return TraceEventIterator(buffer.begin());
    }

    TraceEventIterator end() const override {
        return TraceEventIterator(buffer.end());
    }

protected:
    std::vector<chunk_ptr> buffer;
    size_t generation;
    size_t buffer_size;
};

std::unique_ptr<TraceBuffer> make_fixed_buffer(size_t generation,
                                               size_t buffer_size) {
    return make_unique<FixedTraceBuffer>(generation, buffer_size);
}
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2013 Couchbase, Inc
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

#include "config.h"

#include "ep_engine.h"
#include "upr-stream.h"

UprConsumer::UprConsumer(EventuallyPersistentEngine &e, const void *cookie,
                         const std::string &n)
    : Consumer(e), opaqueCounter(0) {
    conn_ = new Connection(this, cookie, n);
    conn_->setSupportAck(true);
    conn_->setLogHeader("UPR (Consumer) " + conn_->getName() + " -");
    setReserved(false);
}

ENGINE_ERROR_CODE UprConsumer::addStream(uint32_t opaque, uint16_t vbucket,
                                         uint32_t flags) {
    LockHolder lh(streamMutex);
    RCPtr<VBucket> vb = engine_.getVBucket(vbucket);
    if (!vb) {
        return ENGINE_NOT_MY_VBUCKET;
    }

    uint64_t start_seqno = vb->getHighSeqno();
    uint64_t end_seqno = std::numeric_limits<uint64_t>::max();
    uint64_t vbucket_uuid = 0;
    uint64_t high_seqno = start_seqno;
    uint32_t new_opaque = ++opaqueCounter;

    std::map<uint16_t, PassiveStream*>::iterator itr = streams_.find(vbucket);
    if (itr != streams_.end() && itr->second->isActive()) {
        return ENGINE_KEY_EEXISTS;
    } else if (itr != streams_.end()) {
        delete itr->second;
        streams_.erase(itr);
    }

    streams_[vbucket] = new PassiveStream(conn_->name, flags, new_opaque,
                                          vbucket, start_seqno, end_seqno,
                                          vbucket_uuid, high_seqno);
    opaqueMap_[new_opaque] = std::make_pair(opaque, vbucket);
    return ENGINE_SUCCESS;
}

ENGINE_ERROR_CODE UprConsumer::closeStream(uint32_t opaque, uint16_t vbucket) {
    LockHolder lh(streamMutex);

    opaque_map::iterator oitr = opaqueMap_.find(opaque);
    if (oitr != opaqueMap_.end()) {
        opaqueMap_.erase(oitr);
    }

    std::map<uint16_t, PassiveStream*>::iterator itr;
    if ((itr = streams_.find(vbucket)) == streams_.end()) {
        return ENGINE_KEY_ENOENT;
    }

    if (itr->second->getOpaque() != opaque) {
        return ENGINE_KEY_EEXISTS;
    }

    itr->second->setDead();
    return ENGINE_SUCCESS;
}

ENGINE_ERROR_CODE UprConsumer::streamEnd(uint32_t opaque, uint16_t vbucket,
                                         uint32_t flags) {
    if (closeStream(opaque, vbucket) == ENGINE_SUCCESS) {
        LOG(EXTENSION_LOG_WARNING, "%s end stream received with reason %d",
            logHeader(), flags);
    } else {
        LOG(EXTENSION_LOG_WARNING, "%s end stream received but vbucket %d with"
            " opaque %d does not exist", logHeader(), vbucket, opaque);
    }
    return ENGINE_SUCCESS;
}

ENGINE_ERROR_CODE UprConsumer::mutation(uint32_t opaque, const void* key,
                                        uint16_t nkey, const void* value,
                                        uint32_t nvalue, uint64_t cas,
                                        uint16_t vbucket, uint32_t flags,
                                        uint8_t datatype, uint32_t locktime,
                                        uint64_t bySeqno, uint64_t revSeqno,
                                        uint32_t exptime, uint8_t nru,
                                        const void* meta, uint16_t nmeta) {
    ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;

    if (!isValidOpaque(opaque, vbucket)) {
        return ENGINE_FAILED;
    }

    std::string key_str(static_cast<const char*>(key), nkey);
    value_t vblob(Blob::New(static_cast<const char*>(value), nvalue,
                                                     NULL, 0));
    Item *item = new Item(key_str, flags, exptime, vblob, cas, bySeqno,
                          vbucket, revSeqno);

    if (isBackfillPhase(vbucket)) {
        ret = engine_.getEpStore()->addTAPBackfillItem(*item, meta, nru);
    } else {
        ret = engine_.getEpStore()->setWithMeta(*item, 0, conn_->cookie, true,
                                                 true, nru);
    }
    return ret;
}

ENGINE_ERROR_CODE UprConsumer::deletion(uint32_t opaque, const void* key,
                                        uint16_t nkey, uint64_t cas,
                                        uint16_t vbucket, uint64_t bySeqno,
                                        uint64_t revSeqno, const void* meta,
                                        uint16_t nmeta) {
    ENGINE_ERROR_CODE ret;

    if (!isValidOpaque(opaque, vbucket)) {
        return ENGINE_FAILED;
    }

    uint64_t delCas = 0;
    ItemMetaData itemMeta(cas, revSeqno, 0, 0);
    std::string key_str((const char*)key, nkey);
    ret = engine_.getEpStore()->deleteWithMeta(key_str, &delCas, vbucket,
                                               conn_->cookie, true, &itemMeta,
                                               isBackfillPhase(vbucket));

    if (ret == ENGINE_KEY_ENOENT) {
        ret = ENGINE_SUCCESS;
    }

    return ENGINE_SUCCESS;
}

ENGINE_ERROR_CODE UprConsumer::expiration(uint32_t opaque, const void* key,
                                          uint16_t nkey, uint64_t cas,
                                          uint16_t vbucket, uint64_t bySeqno,
                                          uint64_t revSeqno, const void* meta,
                                          uint16_t nmeta) {
    return ENGINE_ENOTSUP;
}

ENGINE_ERROR_CODE UprConsumer::snapshotMarker(uint32_t opaque,
                                              uint16_t vbucket) {
    (void) opaque;
    (void) vbucket;
    return ENGINE_SUCCESS;
}

ENGINE_ERROR_CODE UprConsumer::flush(uint32_t opaque, uint16_t vbucket) {
    (void) opaque;
    (void) vbucket;
    return ENGINE_ENOTSUP;
}

ENGINE_ERROR_CODE UprConsumer::setVBucketState(uint32_t opaque,
                                               uint16_t vbucket,
                                               vbucket_state_t state) {
    if (isValidOpaque(opaque, vbucket)) {
        return Consumer::setVBucketState(opaque, vbucket, state);
    } else {
        LOG(EXTENSION_LOG_WARNING, "Invalid opaque value for UPR-CONSUMER");
        return ENGINE_FAILED;
    }
}

ENGINE_ERROR_CODE UprConsumer::step(struct upr_message_producers* producers) {
    UprResponse *resp = getNextItem();

    if (resp == NULL) {
        return ENGINE_SUCCESS; // Change to tmpfail once mcd layer is fixed
    }

    switch (resp->getEvent()) {
        case UPR_ADD_STREAM:
        {
            AddStreamResponse *as = static_cast<AddStreamResponse*>(resp);
            producers->add_stream_rsp(conn_->cookie, as->getOpaque(),
                                      as->getStreamOpaque(), as->getStatus());
            break;
        }
        case UPR_STREAM_REQ:
        {
            StreamRequest *sr = static_cast<StreamRequest*> (resp);
            producers->stream_req(conn_->cookie, sr->getOpaque(),
                                  sr->getVBucket(), sr->getFlags(),
                                  sr->getStartSeqno(), sr->getEndSeqno(),
                                  sr->getVBucketUUID(), sr->getHighSeqno());
            break;
        }
        default:
            LOG(EXTENSION_LOG_WARNING, "Unknown consumer event, "
                "disconnecting");
            return ENGINE_DISCONNECT;
    }

    delete resp;
    return ENGINE_SUCCESS;
}

ENGINE_ERROR_CODE UprConsumer::handleResponse(
                                        protocol_binary_response_header *resp) {
    uint8_t opcode = resp->response.opcode;
    if (opcode == PROTOCOL_BINARY_CMD_UPR_STREAM_REQ) {
        protocol_binary_response_upr_stream_req* pkt =
            reinterpret_cast<protocol_binary_response_upr_stream_req*>(resp);

        uint16_t status = ntohs(pkt->message.header.response.status);
        uint32_t opaque = pkt->message.header.response.opaque;
        uint64_t bodylen = ntohl(pkt->message.header.response.bodylen);
        uint8_t* body = pkt->bytes + sizeof(protocol_binary_response_header);

        if (status == ENGINE_ROLLBACK) {
            assert(bodylen == sizeof(uint64_t));
            uint64_t rollbackSeqno = 0;
            memcpy(&rollbackSeqno, body, sizeof(uint64_t));
            rollbackSeqno = ntohll(rollbackSeqno);
            return ENGINE_ENOTSUP;
        }

        streamAccepted(opaque, status, body, bodylen);
        return ENGINE_SUCCESS;
    }

    LOG(EXTENSION_LOG_WARNING, "%s Trying to handle an unknown response %d, "
        "disconnecting", logHeader(), opcode);

    return ENGINE_DISCONNECT;
}

void UprConsumer::addStats(ADD_STAT add_stat, const void *c) {
    ConnHandler::addStats(add_stat, c);

    LockHolder lh(streamMutex);
    std::map<uint16_t, PassiveStream*>::iterator itr;
    for (itr = streams_.begin(); itr != streams_.end(); ++itr) {
        itr->second->addStats(add_stat, c);
    }
}

UprResponse* UprConsumer::getNextItem() {
    LockHolder lh(streamMutex);
    std::map<uint16_t, PassiveStream*>::iterator itr = streams_.begin();
    for (; itr != streams_.end(); ++itr) {
        UprResponse* op = itr->second->next();

        if (!op) {
            continue;
        }
        switch (op->getEvent()) {
            case UPR_STREAM_REQ:
            case UPR_ADD_STREAM:
                break;
            default:
                LOG(EXTENSION_LOG_WARNING, "%s Consumer is attempting to write"
                    " an unexpected event %d", logHeader(), op->getEvent());
                abort();
        }
        return op;
    }
    return NULL;
}

void UprConsumer::streamAccepted(uint32_t opaque, uint16_t status, uint8_t* body,
                                 uint32_t bodylen) {
    LockHolder lh(streamMutex);

    opaque_map::iterator oitr = opaqueMap_.find(opaque);
    if (oitr != opaqueMap_.end()) {
        uint32_t add_opaque = oitr->second.first;
        uint16_t vbucket = oitr->second.second;
        std::map<uint16_t, PassiveStream*>::iterator sitr = streams_.find(vbucket);
        if (sitr != streams_.end() && sitr->second->getOpaque() == opaque &&
            sitr->second->getState() == STREAM_PENDING) {
            if (status == ENGINE_SUCCESS) {
                RCPtr<VBucket> vb = engine_.getVBucket(vbucket);
                vb->failovers->replaceFailoverLog(body, bodylen);
                EventuallyPersistentStore* st = engine_.getEpStore();
                st->scheduleVBSnapshot(Priority::VBucketPersistHighPriority,
                                st->getVBuckets().getShard(vbucket)->getId());
            }
            sitr->second->acceptStream(status, add_opaque);
        } else {
            LOG(EXTENSION_LOG_WARNING, "%s Trying to add stream, but none "
                "exists (opaque: %d, add_opaque: %d)", logHeader(), opaque,
                add_opaque);
        }
        opaqueMap_.erase(opaque);
    } else {
        LOG(EXTENSION_LOG_WARNING, "%s No opaque for add stream response",
            logHeader());
    }
}

bool UprConsumer::isValidOpaque(uint32_t opaque, uint16_t vbucket) {
    LockHolder lh(streamMutex);
    std::map<uint16_t, PassiveStream*>::iterator itr = streams_.find(vbucket);
    return itr != streams_.end() && itr->second->getOpaque() == opaque;
}
<commit_msg>MB-9892: Add correct vb uuid and high seqno to consumer stream req<commit_after>/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2013 Couchbase, Inc
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

#include "config.h"

#include "ep_engine.h"
#include "upr-stream.h"

UprConsumer::UprConsumer(EventuallyPersistentEngine &e, const void *cookie,
                         const std::string &n)
    : Consumer(e), opaqueCounter(0) {
    conn_ = new Connection(this, cookie, n);
    conn_->setSupportAck(true);
    conn_->setLogHeader("UPR (Consumer) " + conn_->getName() + " -");
    setReserved(false);
}

ENGINE_ERROR_CODE UprConsumer::addStream(uint32_t opaque, uint16_t vbucket,
                                         uint32_t flags) {
    LockHolder lh(streamMutex);
    RCPtr<VBucket> vb = engine_.getVBucket(vbucket);
    if (!vb) {
        return ENGINE_NOT_MY_VBUCKET;
    }

    failover_entry_t entry = vb->failovers->getLatestEntry();
    uint64_t start_seqno = vb->getHighSeqno();
    uint64_t end_seqno = std::numeric_limits<uint64_t>::max();
    uint64_t vbucket_uuid = entry.vb_uuid;
    uint64_t high_seqno = entry.by_seqno;
    uint32_t new_opaque = ++opaqueCounter;

    std::map<uint16_t, PassiveStream*>::iterator itr = streams_.find(vbucket);
    if (itr != streams_.end() && itr->second->isActive()) {
        return ENGINE_KEY_EEXISTS;
    } else if (itr != streams_.end()) {
        delete itr->second;
        streams_.erase(itr);
    }

    streams_[vbucket] = new PassiveStream(conn_->name, flags, new_opaque,
                                          vbucket, start_seqno, end_seqno,
                                          vbucket_uuid, high_seqno);
    opaqueMap_[new_opaque] = std::make_pair(opaque, vbucket);
    return ENGINE_SUCCESS;
}

ENGINE_ERROR_CODE UprConsumer::closeStream(uint32_t opaque, uint16_t vbucket) {
    LockHolder lh(streamMutex);

    opaque_map::iterator oitr = opaqueMap_.find(opaque);
    if (oitr != opaqueMap_.end()) {
        opaqueMap_.erase(oitr);
    }

    std::map<uint16_t, PassiveStream*>::iterator itr;
    if ((itr = streams_.find(vbucket)) == streams_.end()) {
        return ENGINE_KEY_ENOENT;
    }

    if (itr->second->getOpaque() != opaque) {
        return ENGINE_KEY_EEXISTS;
    }

    itr->second->setDead();
    return ENGINE_SUCCESS;
}

ENGINE_ERROR_CODE UprConsumer::streamEnd(uint32_t opaque, uint16_t vbucket,
                                         uint32_t flags) {
    if (closeStream(opaque, vbucket) == ENGINE_SUCCESS) {
        LOG(EXTENSION_LOG_WARNING, "%s end stream received with reason %d",
            logHeader(), flags);
    } else {
        LOG(EXTENSION_LOG_WARNING, "%s end stream received but vbucket %d with"
            " opaque %d does not exist", logHeader(), vbucket, opaque);
    }
    return ENGINE_SUCCESS;
}

ENGINE_ERROR_CODE UprConsumer::mutation(uint32_t opaque, const void* key,
                                        uint16_t nkey, const void* value,
                                        uint32_t nvalue, uint64_t cas,
                                        uint16_t vbucket, uint32_t flags,
                                        uint8_t datatype, uint32_t locktime,
                                        uint64_t bySeqno, uint64_t revSeqno,
                                        uint32_t exptime, uint8_t nru,
                                        const void* meta, uint16_t nmeta) {
    ENGINE_ERROR_CODE ret = ENGINE_SUCCESS;

    if (!isValidOpaque(opaque, vbucket)) {
        return ENGINE_FAILED;
    }

    std::string key_str(static_cast<const char*>(key), nkey);
    value_t vblob(Blob::New(static_cast<const char*>(value), nvalue,
                                                     NULL, 0));
    Item *item = new Item(key_str, flags, exptime, vblob, cas, bySeqno,
                          vbucket, revSeqno);

    if (isBackfillPhase(vbucket)) {
        ret = engine_.getEpStore()->addTAPBackfillItem(*item, meta, nru);
    } else {
        ret = engine_.getEpStore()->setWithMeta(*item, 0, conn_->cookie, true,
                                                 true, nru);
    }
    return ret;
}

ENGINE_ERROR_CODE UprConsumer::deletion(uint32_t opaque, const void* key,
                                        uint16_t nkey, uint64_t cas,
                                        uint16_t vbucket, uint64_t bySeqno,
                                        uint64_t revSeqno, const void* meta,
                                        uint16_t nmeta) {
    ENGINE_ERROR_CODE ret;

    if (!isValidOpaque(opaque, vbucket)) {
        return ENGINE_FAILED;
    }

    uint64_t delCas = 0;
    ItemMetaData itemMeta(cas, revSeqno, 0, 0);
    std::string key_str((const char*)key, nkey);
    ret = engine_.getEpStore()->deleteWithMeta(key_str, &delCas, vbucket,
                                               conn_->cookie, true, &itemMeta,
                                               isBackfillPhase(vbucket));

    if (ret == ENGINE_KEY_ENOENT) {
        ret = ENGINE_SUCCESS;
    }

    return ENGINE_SUCCESS;
}

ENGINE_ERROR_CODE UprConsumer::expiration(uint32_t opaque, const void* key,
                                          uint16_t nkey, uint64_t cas,
                                          uint16_t vbucket, uint64_t bySeqno,
                                          uint64_t revSeqno, const void* meta,
                                          uint16_t nmeta) {
    return ENGINE_ENOTSUP;
}

ENGINE_ERROR_CODE UprConsumer::snapshotMarker(uint32_t opaque,
                                              uint16_t vbucket) {
    (void) opaque;
    (void) vbucket;
    return ENGINE_SUCCESS;
}

ENGINE_ERROR_CODE UprConsumer::flush(uint32_t opaque, uint16_t vbucket) {
    (void) opaque;
    (void) vbucket;
    return ENGINE_ENOTSUP;
}

ENGINE_ERROR_CODE UprConsumer::setVBucketState(uint32_t opaque,
                                               uint16_t vbucket,
                                               vbucket_state_t state) {
    if (isValidOpaque(opaque, vbucket)) {
        return Consumer::setVBucketState(opaque, vbucket, state);
    } else {
        LOG(EXTENSION_LOG_WARNING, "Invalid opaque value for UPR-CONSUMER");
        return ENGINE_FAILED;
    }
}

ENGINE_ERROR_CODE UprConsumer::step(struct upr_message_producers* producers) {
    UprResponse *resp = getNextItem();

    if (resp == NULL) {
        return ENGINE_SUCCESS; // Change to tmpfail once mcd layer is fixed
    }

    switch (resp->getEvent()) {
        case UPR_ADD_STREAM:
        {
            AddStreamResponse *as = static_cast<AddStreamResponse*>(resp);
            producers->add_stream_rsp(conn_->cookie, as->getOpaque(),
                                      as->getStreamOpaque(), as->getStatus());
            break;
        }
        case UPR_STREAM_REQ:
        {
            StreamRequest *sr = static_cast<StreamRequest*> (resp);
            producers->stream_req(conn_->cookie, sr->getOpaque(),
                                  sr->getVBucket(), sr->getFlags(),
                                  sr->getStartSeqno(), sr->getEndSeqno(),
                                  sr->getVBucketUUID(), sr->getHighSeqno());
            break;
        }
        default:
            LOG(EXTENSION_LOG_WARNING, "Unknown consumer event, "
                "disconnecting");
            return ENGINE_DISCONNECT;
    }

    delete resp;
    return ENGINE_SUCCESS;
}

ENGINE_ERROR_CODE UprConsumer::handleResponse(
                                        protocol_binary_response_header *resp) {
    uint8_t opcode = resp->response.opcode;
    if (opcode == PROTOCOL_BINARY_CMD_UPR_STREAM_REQ) {
        protocol_binary_response_upr_stream_req* pkt =
            reinterpret_cast<protocol_binary_response_upr_stream_req*>(resp);

        uint16_t status = ntohs(pkt->message.header.response.status);
        uint32_t opaque = pkt->message.header.response.opaque;
        uint64_t bodylen = ntohl(pkt->message.header.response.bodylen);
        uint8_t* body = pkt->bytes + sizeof(protocol_binary_response_header);

        if (status == ENGINE_ROLLBACK) {
            assert(bodylen == sizeof(uint64_t));
            uint64_t rollbackSeqno = 0;
            memcpy(&rollbackSeqno, body, sizeof(uint64_t));
            rollbackSeqno = ntohll(rollbackSeqno);
            return ENGINE_ENOTSUP;
        }

        streamAccepted(opaque, status, body, bodylen);
        return ENGINE_SUCCESS;
    }

    LOG(EXTENSION_LOG_WARNING, "%s Trying to handle an unknown response %d, "
        "disconnecting", logHeader(), opcode);

    return ENGINE_DISCONNECT;
}

void UprConsumer::addStats(ADD_STAT add_stat, const void *c) {
    ConnHandler::addStats(add_stat, c);

    LockHolder lh(streamMutex);
    std::map<uint16_t, PassiveStream*>::iterator itr;
    for (itr = streams_.begin(); itr != streams_.end(); ++itr) {
        itr->second->addStats(add_stat, c);
    }
}

UprResponse* UprConsumer::getNextItem() {
    LockHolder lh(streamMutex);
    std::map<uint16_t, PassiveStream*>::iterator itr = streams_.begin();
    for (; itr != streams_.end(); ++itr) {
        UprResponse* op = itr->second->next();

        if (!op) {
            continue;
        }
        switch (op->getEvent()) {
            case UPR_STREAM_REQ:
            case UPR_ADD_STREAM:
                break;
            default:
                LOG(EXTENSION_LOG_WARNING, "%s Consumer is attempting to write"
                    " an unexpected event %d", logHeader(), op->getEvent());
                abort();
        }
        return op;
    }
    return NULL;
}

void UprConsumer::streamAccepted(uint32_t opaque, uint16_t status, uint8_t* body,
                                 uint32_t bodylen) {
    LockHolder lh(streamMutex);

    opaque_map::iterator oitr = opaqueMap_.find(opaque);
    if (oitr != opaqueMap_.end()) {
        uint32_t add_opaque = oitr->second.first;
        uint16_t vbucket = oitr->second.second;
        std::map<uint16_t, PassiveStream*>::iterator sitr = streams_.find(vbucket);
        if (sitr != streams_.end() && sitr->second->getOpaque() == opaque &&
            sitr->second->getState() == STREAM_PENDING) {
            if (status == ENGINE_SUCCESS) {
                RCPtr<VBucket> vb = engine_.getVBucket(vbucket);
                vb->failovers->replaceFailoverLog(body, bodylen);
                EventuallyPersistentStore* st = engine_.getEpStore();
                st->scheduleVBSnapshot(Priority::VBucketPersistHighPriority,
                                st->getVBuckets().getShard(vbucket)->getId());
            }
            sitr->second->acceptStream(status, add_opaque);
        } else {
            LOG(EXTENSION_LOG_WARNING, "%s Trying to add stream, but none "
                "exists (opaque: %d, add_opaque: %d)", logHeader(), opaque,
                add_opaque);
        }
        opaqueMap_.erase(opaque);
    } else {
        LOG(EXTENSION_LOG_WARNING, "%s No opaque for add stream response",
            logHeader());
    }
}

bool UprConsumer::isValidOpaque(uint32_t opaque, uint16_t vbucket) {
    LockHolder lh(streamMutex);
    std::map<uint16_t, PassiveStream*>::iterator itr = streams_.find(vbucket);
    return itr != streams_.end() && itr->second->getOpaque() == opaque;
}
<|endoftext|>


<commit_before>#include "qtfirebaseremoteconfig.h"

namespace remote_config = ::firebase::remote_config;

QtFirebaseRemoteConfig *QtFirebaseRemoteConfig::self = 0;

QtFirebaseRemoteConfig::QtFirebaseRemoteConfig(QObject *parent) :
    QObject(parent),
    _ready(false),
    _initializing(false),
    _cacheExpirationTime(firebase::remote_config::kDefaultCacheExpiration*1000), // milliseconds
    __appId(nullptr)
{
    __QTFIREBASE_ID = QString().sprintf("%8p", this);

    if(self == 0)
    {
        self = this;
        qDebug() << self << "::QtFirebaseRemoteConfig" << "singleton";
    }

    #if defined(Q_OS_ANDROID)
    if (GooglePlayServices::available()) {
        qDebug() << this << " Google Play Services is available, now init remote_config" ;

        //Call init outside of constructor, otherwise signal readyChanged not emited
        QTimer::singleShot(500, this, SLOT(delayedInit()));
        connect(qFirebase,&QtFirebase::futureEvent, this, &QtFirebaseRemoteConfig::onFutureEvent);
    } else {
        qDebug() << this << " Google Play Services is NOT available, CANNOT use remote_config" ;
    }
    #else
    //Call init outside of constructor, otherwise signal readyChanged not emited
    QTimer::singleShot(500, this, SLOT(delayedInit()));
    connect(qFirebase,&QtFirebase::futureEvent, this, &QtFirebaseRemoteConfig::onFutureEvent);
    #endif
}

QtFirebaseRemoteConfig::~QtFirebaseRemoteConfig() {
    if(_ready)
        remote_config::Terminate();
}

bool QtFirebaseRemoteConfig::checkInstance(const char *function)
{
    bool b = (QtFirebaseRemoteConfig::self != 0);
    if(!b) qWarning("QtFirebaseRemoteConfig::%s:", function);
    return b;
}

void QtFirebaseRemoteConfig::addParameterInternal(const QString &name, const QVariant &defaultValue)
{
    _parameters[name] = defaultValue;
}

void QtFirebaseRemoteConfig::delayedInit()
{
    if(qFirebase->ready())
    {
        qDebug() << this << "::delayedInit : QtFirebase is ready, calling init" ;
        init();
    }
    else
    {
        qDebug() << this << "::delayedInit : QtFirebase not ready, connecting to its readyChanged signal" ;
        connect(qFirebase,&QtFirebase::readyChanged, this, &QtFirebaseRemoteConfig::init);
        qFirebase->requestInit();
    }
}

QVariant QtFirebaseRemoteConfig::getParameterValue(const QString &name) const
{
    if(_parameters.contains(name))
    {
        return _parameters[name];
    }
    return _parameters[name];
}

bool QtFirebaseRemoteConfig::ready()
{
    return _ready;
}

void QtFirebaseRemoteConfig::setReady(bool ready)
{
    qDebug() << this << "::setReady before:" << _ready << "now:" << ready;
    if (_ready != ready) {
        _ready = ready;
        emit readyChanged();
    }
}

QVariantMap QtFirebaseRemoteConfig::parameters() const
{
    return _parameters;
}

void QtFirebaseRemoteConfig::setParameters(const QVariantMap &map)
{
    _parameters = map;
    emit parametersChanged();
}

long long QtFirebaseRemoteConfig::cacheExpirationTime() const
{
    return _cacheExpirationTime;
}

void QtFirebaseRemoteConfig::setCacheExpirationTime(long long timeMs)
{
    _cacheExpirationTime = timeMs;
    emit cacheExpirationTimeChanged();
}

void QtFirebaseRemoteConfig::addParameter(const QString &name, long long defaultValue)
{
    addParameterInternal(name, defaultValue);
}

void QtFirebaseRemoteConfig::addParameter(const QString &name, double defaultValue)
{
    addParameterInternal(name, defaultValue);
}

void QtFirebaseRemoteConfig::addParameter(const QString &name, const QString &defaultValue)
{
    addParameterInternal(name, defaultValue);
}

void QtFirebaseRemoteConfig::addParameter(const QString &name, bool defaultValue)
{
    addParameterInternal(name, defaultValue);
}

void QtFirebaseRemoteConfig::init()
{
    qDebug() << self << "::init" << "called";
    if(!qFirebase->ready()) {
        qDebug() << self << "::init" << "base not ready";
        return;
    }

    if(!_ready && !_initializing) {
        _initializing = true;

        ::firebase::ModuleInitializer initializer;
        auto future = initializer.Initialize(qFirebase->firebaseApp(), nullptr, [](::firebase::App* app, void*) {
            qDebug() << self << "::init" << "try to initialize Remote Config";
            return ::firebase::remote_config::Initialize(*app);
        });

        qFirebase->addFuture(__QTFIREBASE_ID + QStringLiteral(".config.init"), future);
    }
}

void QtFirebaseRemoteConfig::onFutureEvent(QString eventId, firebase::FutureBase future)
{
    if(!eventId.startsWith(__QTFIREBASE_ID))
        return;

    qDebug() << self << "::onFutureEvent" << eventId;
    if(eventId == __QTFIREBASE_ID + QStringLiteral(".config.fetch"))
        onFutureEventFetch(future);
    else if( eventId == __QTFIREBASE_ID + QStringLiteral(".config.init") )
        onFutureEventInit(future);
}

void QtFirebaseRemoteConfig::onFutureEventInit(firebase::FutureBase &future)
{
    if (future.error() != firebase::kFutureStatusComplete) {
        qDebug() << this << "::onFutureEvent" << "initializing failed." << "ERROR: Action failed with error code and message: " << future.error() << future.error_message();
        _initializing = false;
        return;
    }

    qDebug() << this << "::onFutureEvent initialized ok";
    _initializing = false;
    setReady(true);
}

void QtFirebaseRemoteConfig::onFutureEventFetch(firebase::FutureBase &future)
{
    if(future.status() != firebase::kFutureStatusComplete)
    {
        qDebug() << this << "::onFutureEvent initializing failed." << "ERROR: Action failed with error code and message: " << future.error() << future.error_message();
        _initializing = false;
        return;
    }
    qDebug() << this << "::onFutureEvent initialized ok";
    _initializing = false;

    bool fetchActivated = remote_config::ActivateFetched();
    //On first run even if we have activateResult failed we still can get cached values
    qDebug() << self << QString(QStringLiteral("ActivateFetched %1")).arg(fetchActivated ? QStringLiteral("succeeded") : QStringLiteral("failed"));

    const remote_config::ConfigInfo& info = remote_config::GetInfo();

    qDebug() << self << QString(QStringLiteral("Info last_fetch_time_ms=%1 fetch_status=%2 failure_reason=%3"))
                .arg(QString::number(info.fetch_time))
                .arg(info.last_fetch_status)
                .arg(info.last_fetch_failure_reason);

    if(info.last_fetch_status == remote_config::kLastFetchStatusSuccess)
    {
        QVariantMap updatedParameters;
        for(QVariantMap::const_iterator it = _parameters.begin(); it!=_parameters.end();++it)
        {
            const QVariant& value = it.value();

            Q_ASSERT(value.type() == QVariant::Bool ||
                     value.type() == QVariant::LongLong ||
                     value.type() == QVariant::Int ||
                     value.type() == QVariant::Double ||
                     value.type() == QVariant::String);

            if(value.type() == QVariant::Bool)
            {
                updatedParameters[it.key()] = remote_config::GetBoolean(it.key().toUtf8().constData());
            }
            else if(value.type() == QVariant::LongLong)
            {
                updatedParameters[it.key()] = remote_config::GetLong(it.key().toUtf8().constData());
            }
            else if(value.type() == QVariant::Int)
            {
                updatedParameters[it.key()] = remote_config::GetLong(it.key().toUtf8().constData());
            }
            else if(value.type() == QVariant::Double)
            {
                updatedParameters[it.key()] = remote_config::GetDouble(it.key().toUtf8().constData());
            }
            else if(value.type() == QVariant::String)
            {
                std::string result = remote_config::GetString(it.key().toUtf8().constData());
                updatedParameters[it.key()] = QString(QString::fromUtf8(result.c_str()));

                //Code for data type
                /*std::vector<unsigned char> out = remote_config::GetData(it.key().toUtf8().constData());
                QByteArray data;
                for (size_t i = 0; i < out.size(); ++i)
                {
                    data.append(out[i]);
                }
                updatedParameters[it.key()] = QString(data);*/
            }
        }

        //SDK code to print out the keys
        /*std::vector<std::string> keys = remote_config::GetKeys();
        qDebug() << "QtFirebaseRemoteConfig GetKeys:";
        for (auto s = keys.begin(); s != keys.end(); ++s)
        {
            qDebug() << s->c_str();
        }
        keys = remote_config::GetKeysByPrefix("TestD");
        printf("GetKeysByPrefix(\"TestD\"):");
        for (auto s = keys.begin(); s != keys.end(); ++s) {
          printf("  %s", s->c_str());
        }*/

        setParameters(updatedParameters);
    }
    else
    {
        if(info.last_fetch_failure_reason == remote_config::kFetchFailureReasonInvalid)
        {
            emit error(FetchFailureReasonInvalid, QStringLiteral("The fetch has not yet failed."));
        }
        else if(info.last_fetch_failure_reason == remote_config::kFetchFailureReasonThrottled)
        {
            emit error(FetchFailureReasonThrottled, QStringLiteral("Throttled by the server. You are sending too many fetch requests in too short a time."));
        }
        else
        {
            emit error(FetchFailureReasonError, QStringLiteral("Failure reason is unknown"));
        }
    }
    future.Release();
}

void QtFirebaseRemoteConfig::fetch()
{
    fetch(_cacheExpirationTime<1000 ? 0 : _cacheExpirationTime/1000);
}

void QtFirebaseRemoteConfig::fetch(long long cacheExpirationInSeconds)
{
    if(_parameters.size() == 0)
    {
        qDebug() << self << "::fetch not started, parameters were not initialized";
        return;
    }
    qDebug() << self <<"::fetch with expirationtime" << cacheExpirationInSeconds << "seconds";

    QVariantMap filteredMap;
    for(QVariantMap::const_iterator it = _parameters.begin(); it!=_parameters.end();++it)
    {
        const QVariant& value = it.value();
        if(value.type() == QVariant::Bool ||
                value.type() == QVariant::LongLong ||
                value.type() == QVariant::Int ||
                value.type() == QVariant::Double ||
                value.type() == QVariant::String)
        {
            filteredMap[it.key()] = value;
        }
        else
        {
            qWarning() << self << "Data type:" << value.typeName() << " not supported";
        }
    }

    // From <memory> include
    std::unique_ptr<remote_config::ConfigKeyValueVariant[]> defaults(
                new remote_config::ConfigKeyValueVariant[filteredMap.size()]);

    __defaultsByteArrayList.clear();
    uint index = 0;
    for(QVariantMap::const_iterator it = filteredMap.begin(); it!=filteredMap.end();++it)
    {
        const QVariant& value = it.value();

        __defaultsByteArrayList.insert(index,QByteArray(it.key().toUtf8().data()));
        if(value.type() == QVariant::Bool)
        {
            defaults[index] = remote_config::ConfigKeyValueVariant{__defaultsByteArrayList.at(index).constData(),
                    value.toBool()};
        }
        else if(value.type() == QVariant::LongLong)
        {
            defaults[index] = remote_config::ConfigKeyValueVariant{__defaultsByteArrayList.at(index).constData(),
                    value.toLongLong()};
        }
        else if(value.type() == QVariant::Int)
        {
            defaults[index] = remote_config::ConfigKeyValueVariant{__defaultsByteArrayList.at(index).constData(),
                    value.toInt()};
        }
        else if(value.type() == QVariant::Double)
        {
            defaults[index] = remote_config::ConfigKeyValueVariant{__defaultsByteArrayList.at(index).constData(),
                    value.toDouble()};
        }

        else if(value.type() == QVariant::String)
        {
            defaults[index] = remote_config::ConfigKeyValueVariant{__defaultsByteArrayList.at(index).constData(),
                    value.toString().toUtf8().constData()};

            //Code for data type
            /*QByteArray data = value.toString().toUtf8();
            defaults[cnt] = remote_config::ConfigKeyValueVariant{
                                it.key().toUtf8().constData(),
                                firebase::Variant::FromMutableBlob(data.constData(), data.size())};*/
        }

        index++;
    }

    remote_config::SetDefaults(defaults.get(), filteredMap.size());

    /*remote_config::SetConfigSetting(remote_config::kConfigSettingDeveloperMode, "1");
    if ((*remote_config::GetConfigSetting(remote_config::kConfigSettingDeveloperMode)
                .c_str()) != '1') {
        qDebug()<<"Failed to enable developer mode";
    }*/

    qDebug() << self << "::fetch" << "run fetching...";
    auto future = remote_config::Fetch(cacheExpirationInSeconds);
    qFirebase->addFuture(__QTFIREBASE_ID + QStringLiteral(".config.fetch"), future);
}

void QtFirebaseRemoteConfig::fetchNow()
{
    fetch(0);
}
<commit_msg>Fix RemoteConfig life-cycle re-initialization bug. This should close #42<commit_after>#include "qtfirebaseremoteconfig.h"

namespace remote_config = ::firebase::remote_config;

QtFirebaseRemoteConfig *QtFirebaseRemoteConfig::self = 0;

QtFirebaseRemoteConfig::QtFirebaseRemoteConfig(QObject *parent) :
    QObject(parent),
    _ready(false),
    _initializing(false),
    _cacheExpirationTime(firebase::remote_config::kDefaultCacheExpiration*1000), // milliseconds
    __appId(nullptr)
{
    __QTFIREBASE_ID = QString().sprintf("%8p", this);

    if(self == 0)
    {
        self = this;
        qDebug() << self << "::QtFirebaseRemoteConfig" << "singleton";
    }

    #if defined(Q_OS_ANDROID)
    if (GooglePlayServices::available()) {
        qDebug() << this << " Google Play Services is available, now init remote_config" ;

        //Call init outside of constructor, otherwise signal readyChanged not emited
        QTimer::singleShot(500, this, SLOT(delayedInit()));
        connect(qFirebase,&QtFirebase::futureEvent, this, &QtFirebaseRemoteConfig::onFutureEvent);
    } else {
        qDebug() << this << " Google Play Services is NOT available, CANNOT use remote_config" ;
    }
    #else
    //Call init outside of constructor, otherwise signal readyChanged not emited
    QTimer::singleShot(500, this, SLOT(delayedInit()));
    connect(qFirebase,&QtFirebase::futureEvent, this, &QtFirebaseRemoteConfig::onFutureEvent);
    #endif
}

QtFirebaseRemoteConfig::~QtFirebaseRemoteConfig() {
    if(_ready)
        remote_config::Terminate();
}

bool QtFirebaseRemoteConfig::checkInstance(const char *function)
{
    bool b = (QtFirebaseRemoteConfig::self != 0);
    if(!b) qWarning("QtFirebaseRemoteConfig::%s:", function);
    return b;
}

void QtFirebaseRemoteConfig::addParameterInternal(const QString &name, const QVariant &defaultValue)
{
    _parameters[name] = defaultValue;
}

void QtFirebaseRemoteConfig::delayedInit()
{
    if(qFirebase->ready())
    {
        qDebug() << this << "::delayedInit : QtFirebase is ready, calling init" ;
        init();
    }
    else
    {
        qDebug() << this << "::delayedInit : QtFirebase not ready, connecting to its readyChanged signal" ;
        connect(qFirebase,&QtFirebase::readyChanged, this, &QtFirebaseRemoteConfig::init);
        qFirebase->requestInit();
    }
}

QVariant QtFirebaseRemoteConfig::getParameterValue(const QString &name) const
{
    if(_parameters.contains(name))
    {
        return _parameters[name];
    }
    return _parameters[name];
}

bool QtFirebaseRemoteConfig::ready()
{
    return _ready;
}

void QtFirebaseRemoteConfig::setReady(bool ready)
{
    qDebug() << this << "::setReady before:" << _ready << "now:" << ready;
    if (_ready != ready) {
        _ready = ready;
        emit readyChanged();
    }
}

QVariantMap QtFirebaseRemoteConfig::parameters() const
{
    return _parameters;
}

void QtFirebaseRemoteConfig::setParameters(const QVariantMap &map)
{
    _parameters = map;
    emit parametersChanged();
}

long long QtFirebaseRemoteConfig::cacheExpirationTime() const
{
    return _cacheExpirationTime;
}

void QtFirebaseRemoteConfig::setCacheExpirationTime(long long timeMs)
{
    _cacheExpirationTime = timeMs;
    emit cacheExpirationTimeChanged();
}

void QtFirebaseRemoteConfig::addParameter(const QString &name, long long defaultValue)
{
    addParameterInternal(name, defaultValue);
}

void QtFirebaseRemoteConfig::addParameter(const QString &name, double defaultValue)
{
    addParameterInternal(name, defaultValue);
}

void QtFirebaseRemoteConfig::addParameter(const QString &name, const QString &defaultValue)
{
    addParameterInternal(name, defaultValue);
}

void QtFirebaseRemoteConfig::addParameter(const QString &name, bool defaultValue)
{
    addParameterInternal(name, defaultValue);
}

void QtFirebaseRemoteConfig::init()
{
    qDebug() << this << "::init" << "called";
    if(!qFirebase->ready()) {
        qDebug() << this << "::init" << "base not ready";
        return;
    }

    if(!_ready && !_initializing) {
        _initializing = true;

        ::firebase::ModuleInitializer initializer;
        auto future = initializer.Initialize(qFirebase->firebaseApp(), nullptr, [](::firebase::App* app, void*) {
            // NOTE only write debug output here when developing
            // Causes crash on re-initialization (probably the "self" reference. And "this" can't be used in a lambda)
            //qDebug() << self << "::init" << "try to initialize Remote Config";
            return ::firebase::remote_config::Initialize(*app);
        });

        qFirebase->addFuture(__QTFIREBASE_ID + QStringLiteral(".config.init"), future);
    }
}

void QtFirebaseRemoteConfig::onFutureEvent(QString eventId, firebase::FutureBase future)
{
    if(!eventId.startsWith(__QTFIREBASE_ID))
        return;

    qDebug() << this << "::onFutureEvent" << eventId;
    if(eventId == __QTFIREBASE_ID + QStringLiteral(".config.fetch"))
        onFutureEventFetch(future);
    else if( eventId == __QTFIREBASE_ID + QStringLiteral(".config.init") )
        onFutureEventInit(future);
}

void QtFirebaseRemoteConfig::onFutureEventInit(firebase::FutureBase &future)
{
    if (future.error() != firebase::kFutureStatusComplete) {
        qDebug() << this << "::onFutureEvent" << "initializing failed." << "ERROR: Action failed with error code and message: " << future.error() << future.error_message();
        _initializing = false;
        return;
    }

    qDebug() << this << "::onFutureEvent initialized ok";
    _initializing = false;
    setReady(true);
}

void QtFirebaseRemoteConfig::onFutureEventFetch(firebase::FutureBase &future)
{
    if(future.status() != firebase::kFutureStatusComplete)
    {
        qDebug() << this << "::onFutureEvent initializing failed." << "ERROR: Action failed with error code and message: " << future.error() << future.error_message();
        _initializing = false;
        return;
    }
    qDebug() << this << "::onFutureEvent initialized ok";
    _initializing = false;

    bool fetchActivated = remote_config::ActivateFetched();
    //On first run even if we have activateResult failed we still can get cached values
    qDebug() << this << QString(QStringLiteral("ActivateFetched %1")).arg(fetchActivated ? QStringLiteral("succeeded") : QStringLiteral("failed"));

    const remote_config::ConfigInfo& info = remote_config::GetInfo();

    qDebug() << this << QString(QStringLiteral("Info last_fetch_time_ms=%1 fetch_status=%2 failure_reason=%3"))
                .arg(QString::number(info.fetch_time))
                .arg(info.last_fetch_status)
                .arg(info.last_fetch_failure_reason);

    if(info.last_fetch_status == remote_config::kLastFetchStatusSuccess)
    {
        QVariantMap updatedParameters;
        for(QVariantMap::const_iterator it = _parameters.begin(); it!=_parameters.end();++it)
        {
            const QVariant& value = it.value();

            Q_ASSERT(value.type() == QVariant::Bool ||
                     value.type() == QVariant::LongLong ||
                     value.type() == QVariant::Int ||
                     value.type() == QVariant::Double ||
                     value.type() == QVariant::String);

            if(value.type() == QVariant::Bool)
            {
                updatedParameters[it.key()] = remote_config::GetBoolean(it.key().toUtf8().constData());
            }
            else if(value.type() == QVariant::LongLong)
            {
                updatedParameters[it.key()] = remote_config::GetLong(it.key().toUtf8().constData());
            }
            else if(value.type() == QVariant::Int)
            {
                updatedParameters[it.key()] = remote_config::GetLong(it.key().toUtf8().constData());
            }
            else if(value.type() == QVariant::Double)
            {
                updatedParameters[it.key()] = remote_config::GetDouble(it.key().toUtf8().constData());
            }
            else if(value.type() == QVariant::String)
            {
                std::string result = remote_config::GetString(it.key().toUtf8().constData());
                updatedParameters[it.key()] = QString(QString::fromUtf8(result.c_str()));

                //Code for data type
                /*std::vector<unsigned char> out = remote_config::GetData(it.key().toUtf8().constData());
                QByteArray data;
                for (size_t i = 0; i < out.size(); ++i)
                {
                    data.append(out[i]);
                }
                updatedParameters[it.key()] = QString(data);*/
            }
        }

        //SDK code to print out the keys
        /*std::vector<std::string> keys = remote_config::GetKeys();
        qDebug() << "QtFirebaseRemoteConfig GetKeys:";
        for (auto s = keys.begin(); s != keys.end(); ++s)
        {
            qDebug() << s->c_str();
        }
        keys = remote_config::GetKeysByPrefix("TestD");
        printf("GetKeysByPrefix(\"TestD\"):");
        for (auto s = keys.begin(); s != keys.end(); ++s) {
          printf("  %s", s->c_str());
        }*/

        setParameters(updatedParameters);
    }
    else
    {
        if(info.last_fetch_failure_reason == remote_config::kFetchFailureReasonInvalid)
        {
            emit error(FetchFailureReasonInvalid, QStringLiteral("The fetch has not yet failed."));
        }
        else if(info.last_fetch_failure_reason == remote_config::kFetchFailureReasonThrottled)
        {
            emit error(FetchFailureReasonThrottled, QStringLiteral("Throttled by the server. You are sending too many fetch requests in too short a time."));
        }
        else
        {
            emit error(FetchFailureReasonError, QStringLiteral("Failure reason is unknown"));
        }
    }
    future.Release();
}

void QtFirebaseRemoteConfig::fetch()
{
    fetch(_cacheExpirationTime<1000 ? 0 : _cacheExpirationTime/1000);
}

void QtFirebaseRemoteConfig::fetch(long long cacheExpirationInSeconds)
{
    if(_parameters.size() == 0)
    {
        qDebug() << this << "::fetch not started, parameters were not initialized";
        return;
    }
    qDebug() << this <<"::fetch with expirationtime" << cacheExpirationInSeconds << "seconds";

    QVariantMap filteredMap;
    for(QVariantMap::const_iterator it = _parameters.begin(); it!=_parameters.end();++it)
    {
        const QVariant& value = it.value();
        if(value.type() == QVariant::Bool ||
                value.type() == QVariant::LongLong ||
                value.type() == QVariant::Int ||
                value.type() == QVariant::Double ||
                value.type() == QVariant::String)
        {
            filteredMap[it.key()] = value;
        }
        else
        {
            qWarning() << this << "Data type:" << value.typeName() << " not supported";
        }
    }

    // From <memory> include
    std::unique_ptr<remote_config::ConfigKeyValueVariant[]> defaults(
                new remote_config::ConfigKeyValueVariant[filteredMap.size()]);

    __defaultsByteArrayList.clear();
    uint index = 0;
    for(QVariantMap::const_iterator it = filteredMap.begin(); it!=filteredMap.end();++it)
    {
        const QVariant& value = it.value();

        __defaultsByteArrayList.insert(index,QByteArray(it.key().toUtf8().data()));
        if(value.type() == QVariant::Bool)
        {
            defaults[index] = remote_config::ConfigKeyValueVariant{__defaultsByteArrayList.at(index).constData(),
                    value.toBool()};
        }
        else if(value.type() == QVariant::LongLong)
        {
            defaults[index] = remote_config::ConfigKeyValueVariant{__defaultsByteArrayList.at(index).constData(),
                    value.toLongLong()};
        }
        else if(value.type() == QVariant::Int)
        {
            defaults[index] = remote_config::ConfigKeyValueVariant{__defaultsByteArrayList.at(index).constData(),
                    value.toInt()};
        }
        else if(value.type() == QVariant::Double)
        {
            defaults[index] = remote_config::ConfigKeyValueVariant{__defaultsByteArrayList.at(index).constData(),
                    value.toDouble()};
        }

        else if(value.type() == QVariant::String)
        {
            defaults[index] = remote_config::ConfigKeyValueVariant{__defaultsByteArrayList.at(index).constData(),
                    value.toString().toUtf8().constData()};

            //Code for data type
            /*QByteArray data = value.toString().toUtf8();
            defaults[cnt] = remote_config::ConfigKeyValueVariant{
                                it.key().toUtf8().constData(),
                                firebase::Variant::FromMutableBlob(data.constData(), data.size())};*/
        }

        index++;
    }

    remote_config::SetDefaults(defaults.get(), filteredMap.size());

    /*remote_config::SetConfigSetting(remote_config::kConfigSettingDeveloperMode, "1");
    if ((*remote_config::GetConfigSetting(remote_config::kConfigSettingDeveloperMode)
                .c_str()) != '1') {
        qDebug()<<"Failed to enable developer mode";
    }*/

    qDebug() << this << "::fetch" << "run fetching...";
    auto future = remote_config::Fetch(cacheExpirationInSeconds);
    qFirebase->addFuture(__QTFIREBASE_ID + QStringLiteral(".config.fetch"), future);
}

void QtFirebaseRemoteConfig::fetchNow()
{
    fetch(0);
}
<|endoftext|>


<commit_before>#include "qtfirebaseremoteconfig.h"

namespace remote_config = ::firebase::remote_config;

QtFirebaseRemoteConfig *QtFirebaseRemoteConfig::self = 0;

QtFirebaseRemoteConfig::QtFirebaseRemoteConfig(QObject *parent) :
    QObject(parent),
    _ready(false),
    _initializing(false),
    _cacheExpirationTime(firebase::remote_config::kDefaultCacheExpiration*1000),//milliseconds
    __appId(nullptr)
{
    __QTFIREBASE_ID = QString().sprintf("%8p", this);

    if(self == 0)
    {
        self = this;
        qDebug() << self << "::QtFirebaseRemoteConfig" << "singleton";
    }

    #if defined(Q_OS_ANDROID)
    if (PlatformUtils::googleServicesAvailable()) {
        qDebug() << this << " Google Services is available, now init remote_config" ;

        //Call init outside of constructor, otherwise signal readyChanged not emited
        QTimer::singleShot(500, this, SLOT(delayedInit()));
        connect(qFirebase,&QtFirebase::futureEvent, this, &QtFirebaseRemoteConfig::onFutureEvent);
    } else {
        qDebug() << this << " Google Services is NOT available, CANNOT use remote_config" ;
    }
    #else
    //Call init outside of constructor, otherwise signal readyChanged not emited
    QTimer::singleShot(500, this, SLOT(delayedInit()));
    connect(qFirebase,&QtFirebase::futureEvent, this, &QtFirebaseRemoteConfig::onFutureEvent);
    #endif
}

bool QtFirebaseRemoteConfig::checkInstance(const char *function)
{
    bool b = (QtFirebaseRemoteConfig::self != 0);
    if(!b) qWarning("QtFirebaseRemoteConfig::%s:", function);
    return b;
}

void QtFirebaseRemoteConfig::addParameterInternal(const QString &name, const QVariant &defaultValue)
{
    _parameters[name] = defaultValue;
}

void QtFirebaseRemoteConfig::delayedInit()
{
    if(qFirebase->ready())
    {
        qDebug() << this << "::delayedInit : QtFirebase is ready, calling init" ;
        init();
    }
    else
    {
        qDebug() << this << "::delayedInit : QtFirebase not ready, connecting to its readyChanged signal" ;
        connect(qFirebase,&QtFirebase::readyChanged, this, &QtFirebaseRemoteConfig::init);
        qFirebase->requestInit();
    }
}

QVariant QtFirebaseRemoteConfig::getParameterValue(const QString &name) const
{
    if(_parameters.contains(name))
    {
        return _parameters[name];
    }
    return _parameters[name];
}

bool QtFirebaseRemoteConfig::ready()
{
    return _ready;
}

void QtFirebaseRemoteConfig::setReady(bool ready)
{
    qDebug() << this << "::setReady before:" << _ready << "now:" << ready;
    if (_ready != ready) {
        _ready = ready;
        emit readyChanged();
    }
}

QVariantMap QtFirebaseRemoteConfig::parameters() const
{
    return _parameters;
}

void QtFirebaseRemoteConfig::setParameters(const QVariantMap &map)
{
    _parameters = map;
    emit parametersChanged();
}

long long QtFirebaseRemoteConfig::cacheExpirationTime() const
{
    return _cacheExpirationTime;
}

void QtFirebaseRemoteConfig::setCacheExpirationTime(long long timeMs)
{
    _cacheExpirationTime = timeMs;
    emit cacheExpirationTimeChanged();
}

void QtFirebaseRemoteConfig::addParameter(const QString &name, long long defaultValue)
{
    addParameterInternal(name, defaultValue);
}

void QtFirebaseRemoteConfig::addParameter(const QString &name, double defaultValue)
{
    addParameterInternal(name, defaultValue);
}

void QtFirebaseRemoteConfig::addParameter(const QString &name, const QString &defaultValue)
{
    addParameterInternal(name, defaultValue);
}

void QtFirebaseRemoteConfig::addParameter(const QString &name, bool defaultValue)
{
    addParameterInternal(name, defaultValue);
}

void QtFirebaseRemoteConfig::init()
{
    qDebug() << self << "::init" << "called";
    if(!qFirebase->ready()) {
        qDebug() << self << "::init" << "base not ready";
        return;
    }

    if(!_ready && !_initializing) {
        _initializing = true;

        ::firebase::ModuleInitializer initializer;
        auto future = initializer.Initialize(qFirebase->firebaseApp(), nullptr, [](::firebase::App* app, void*) {
            qDebug() << self << "::init" << "try to initialize Remote Config";
            return ::firebase::remote_config::Initialize(*app);
        });

        qFirebase->addFuture(__QTFIREBASE_ID + QStringLiteral(".config.init"), future);
    }
}

void QtFirebaseRemoteConfig::onFutureEvent(QString eventId, firebase::FutureBase future)
{
    if(!eventId.startsWith(__QTFIREBASE_ID))
        return;

    qDebug() << self << "::onFutureEvent" << eventId;
    if(eventId == __QTFIREBASE_ID + QStringLiteral(".config.fetch"))
        onFutureEventFetch(future);
    else if( eventId == __QTFIREBASE_ID + QStringLiteral(".config.init") )
        onFutureEventInit(future);
}

void QtFirebaseRemoteConfig::onFutureEventInit(firebase::FutureBase &future)
{
    if (future.error() != firebase::kFutureStatusComplete) {
        qDebug() << this << "::onFutureEvent" << "initializing failed." << "ERROR: Action failed with error code and message: " << future.error() << future.error_message();
        _initializing = false;
        return;
    }

    qDebug() << this << "::onFutureEvent initialized ok";
    _initializing = false;
    setReady(true);
}

void QtFirebaseRemoteConfig::onFutureEventFetch(firebase::FutureBase &future)
{
    if(future.status() != firebase::kFutureStatusComplete)
    {
        qDebug() << this << "::onFutureEvent initializing failed." << "ERROR: Action failed with error code and message: " << future.error() << future.error_message();
        _initializing = false;
        return;
    }
    qDebug() << this << "::onFutureEvent initialized ok";
    _initializing = false;

    bool fetchActivated = remote_config::ActivateFetched();
    //On first run even if we have activateResult failed we still can get cached values
    qDebug() << self << QString(QStringLiteral("ActivateFetched %1")).arg(fetchActivated ? QStringLiteral("succeeded") : QStringLiteral("failed"));

    const remote_config::ConfigInfo& info = remote_config::GetInfo();

    qDebug() << self << QString(QStringLiteral("Info last_fetch_time_ms=%1 fetch_status=%2 failure_reason=%3"))
                .arg(QString::number(info.fetch_time))
                .arg(info.last_fetch_status)
                .arg(info.last_fetch_failure_reason);

    if(info.last_fetch_status == remote_config::kLastFetchStatusSuccess)
    {
        QVariantMap updatedParameters;
        for(QVariantMap::const_iterator it = _parameters.begin(); it!=_parameters.end();++it)
        {
            const QVariant& value = it.value();

            Q_ASSERT(value.type() == QVariant::Bool ||
                     value.type() == QVariant::LongLong ||
                     value.type() == QVariant::Int ||
                     value.type() == QVariant::Double ||
                     value.type() == QVariant::String);

            if(value.type() == QVariant::Bool)
            {
                updatedParameters[it.key()] = remote_config::GetBoolean(it.key().toUtf8().constData());
            }
            else if(value.type() == QVariant::LongLong)
            {
                updatedParameters[it.key()] = remote_config::GetLong(it.key().toUtf8().constData());
            }
            else if(value.type() == QVariant::Int)
            {
                updatedParameters[it.key()] = remote_config::GetLong(it.key().toUtf8().constData());
            }
            else if(value.type() == QVariant::Double)
            {
                updatedParameters[it.key()] = remote_config::GetDouble(it.key().toUtf8().constData());
            }
            else if(value.type() == QVariant::String)
            {
                std::string result = remote_config::GetString(it.key().toUtf8().constData());
                updatedParameters[it.key()] = QString(QString::fromUtf8(result.c_str()));

                //Code for data type
                /*std::vector<unsigned char> out = remote_config::GetData(it.key().toUtf8().constData());
                QByteArray data;
                for (size_t i = 0; i < out.size(); ++i)
                {
                    data.append(out[i]);
                }
                updatedParameters[it.key()] = QString(data);*/
            }
        }

        //SDK code to print out the keys
        /*std::vector<std::string> keys = remote_config::GetKeys();
        qDebug() << "QtFirebaseRemoteConfig GetKeys:";
        for (auto s = keys.begin(); s != keys.end(); ++s)
        {
            qDebug() << s->c_str();
        }
        keys = remote_config::GetKeysByPrefix("TestD");
        printf("GetKeysByPrefix(\"TestD\"):");
        for (auto s = keys.begin(); s != keys.end(); ++s) {
          printf("  %s", s->c_str());
        }*/

        setParameters(updatedParameters);
    }
    else
    {
        if(info.last_fetch_failure_reason == remote_config::kFetchFailureReasonInvalid)
        {
            emit error(kFetchFailureReasonInvalid, QStringLiteral("The fetch has not yet failed."));
        }
        else if(info.last_fetch_failure_reason == remote_config::kFetchFailureReasonThrottled)
        {
            emit error(kFetchFailureReasonThrottled, QStringLiteral("Throttled by the server. You are sending too many fetch requests in too short a time."));
        }
        else
        {
            emit error(kFetchFailureReasonError, QStringLiteral("Failure reason is unknown"));
        }
    }
    future.Release();
}

void QtFirebaseRemoteConfig::fetch()
{
    fetch(_cacheExpirationTime<1000 ? 0 : _cacheExpirationTime/1000);
}

void QtFirebaseRemoteConfig::fetch(long long cacheExpirationInSeconds)
{
    if(_parameters.size() == 0)
    {
        qDebug() << self << "::fetch not started, parameters were not initialized";
        return;
    }
    qDebug() << self <<"::fetch with expirationtime" << cacheExpirationInSeconds << "seconds";

    QVariantMap filteredMap;
    for(QVariantMap::const_iterator it = _parameters.begin(); it!=_parameters.end();++it)
    {
        const QVariant& value = it.value();
        if(value.type() == QVariant::Bool ||
                value.type() == QVariant::LongLong ||
                value.type() == QVariant::Int ||
                value.type() == QVariant::Double ||
                value.type() == QVariant::String)
        {
            filteredMap[it.key()] = value;
        }
        else
        {
            qWarning() << self << "Data type:" << value.typeName() << " not supported";
        }
    }

    std::unique_ptr<remote_config::ConfigKeyValueVariant[]> defaults(
                new remote_config::ConfigKeyValueVariant[filteredMap.size()]);

    __defaultsByteArrayList.clear();
    uint index = 0;
    for(QVariantMap::const_iterator it = filteredMap.begin(); it!=filteredMap.end();++it)
    {
        const QVariant& value = it.value();

        __defaultsByteArrayList.insert(index,QByteArray(it.key().toUtf8().data()));
        if(value.type() == QVariant::Bool)
        {
            defaults[index] = remote_config::ConfigKeyValueVariant{__defaultsByteArrayList.at(index).constData(),
                    value.toBool()};
        }
        else if(value.type() == QVariant::LongLong)
        {
            defaults[index] = remote_config::ConfigKeyValueVariant{__defaultsByteArrayList.at(index).constData(),
                    value.toLongLong()};
        }
        else if(value.type() == QVariant::Int)
        {
            defaults[index] = remote_config::ConfigKeyValueVariant{__defaultsByteArrayList.at(index).constData(),
                    value.toInt()};
        }
        else if(value.type() == QVariant::Double)
        {
            defaults[index] = remote_config::ConfigKeyValueVariant{__defaultsByteArrayList.at(index).constData(),
                    value.toDouble()};
        }

        else if(value.type() == QVariant::String)
        {
            defaults[index] = remote_config::ConfigKeyValueVariant{__defaultsByteArrayList.at(index).constData(),
                    value.toString().toUtf8().constData()};

            //Code for data type
            /*QByteArray data = value.toString().toUtf8();
            defaults[cnt] = remote_config::ConfigKeyValueVariant{
                                it.key().toUtf8().constData(),
                                firebase::Variant::FromMutableBlob(data.constData(), data.size())};*/
        }

        index++;
    }

    // NOTE crash on iOS with Firebase 4.1.0
    remote_config::SetDefaults(defaults.get(), filteredMap.size());

    /*remote_config::SetConfigSetting(remote_config::kConfigSettingDeveloperMode, "1");
    if ((*remote_config::GetConfigSetting(remote_config::kConfigSettingDeveloperMode)
                .c_str()) != '1') {
        qDebug()<<"Failed to enable developer mode";
    }*/

    qDebug() << self << "::fetch" << "run fetching...";
    auto future = remote_config::Fetch(cacheExpirationInSeconds);
    qFirebase->addFuture(__QTFIREBASE_ID + QStringLiteral(".config.fetch"), future);
}

void QtFirebaseRemoteConfig::fetchNow()
{
    fetch(0);
}
<commit_msg>Fixed crash in RemoteConfig<commit_after>#include "qtfirebaseremoteconfig.h"

namespace remote_config = ::firebase::remote_config;

QtFirebaseRemoteConfig *QtFirebaseRemoteConfig::self = 0;

QtFirebaseRemoteConfig::QtFirebaseRemoteConfig(QObject *parent) :
    QObject(parent),
    _ready(false),
    _initializing(false),
    _cacheExpirationTime(firebase::remote_config::kDefaultCacheExpiration*1000),//milliseconds
    __appId(nullptr)
{
    __QTFIREBASE_ID = QString().sprintf("%8p", this);

    if(self == 0)
    {
        self = this;
        qDebug() << self << "::QtFirebaseRemoteConfig" << "singleton";
    }

    #if defined(Q_OS_ANDROID)
    if (PlatformUtils::googleServicesAvailable()) {
        qDebug() << this << " Google Services is available, now init remote_config" ;

        //Call init outside of constructor, otherwise signal readyChanged not emited
        QTimer::singleShot(500, this, SLOT(delayedInit()));
        connect(qFirebase,&QtFirebase::futureEvent, this, &QtFirebaseRemoteConfig::onFutureEvent);
    } else {
        qDebug() << this << " Google Services is NOT available, CANNOT use remote_config" ;
    }
    #else
    //Call init outside of constructor, otherwise signal readyChanged not emited
    QTimer::singleShot(500, this, SLOT(delayedInit()));
    connect(qFirebase,&QtFirebase::futureEvent, this, &QtFirebaseRemoteConfig::onFutureEvent);
    #endif
}

bool QtFirebaseRemoteConfig::checkInstance(const char *function)
{
    bool b = (QtFirebaseRemoteConfig::self != 0);
    if(!b) qWarning("QtFirebaseRemoteConfig::%s:", function);
    return b;
}

void QtFirebaseRemoteConfig::addParameterInternal(const QString &name, const QVariant &defaultValue)
{
    _parameters[name] = defaultValue;
}

void QtFirebaseRemoteConfig::delayedInit()
{
    if(qFirebase->ready())
    {
        qDebug() << this << "::delayedInit : QtFirebase is ready, calling init" ;
        init();
    }
    else
    {
        qDebug() << this << "::delayedInit : QtFirebase not ready, connecting to its readyChanged signal" ;
        connect(qFirebase,&QtFirebase::readyChanged, this, &QtFirebaseRemoteConfig::init);
        qFirebase->requestInit();
    }
}

QVariant QtFirebaseRemoteConfig::getParameterValue(const QString &name) const
{
    if(_parameters.contains(name))
    {
        return _parameters[name];
    }
    return _parameters[name];
}

bool QtFirebaseRemoteConfig::ready()
{
    return _ready;
}

void QtFirebaseRemoteConfig::setReady(bool ready)
{
    qDebug() << this << "::setReady before:" << _ready << "now:" << ready;
    if (_ready != ready) {
        _ready = ready;
        emit readyChanged();
    }
}

QVariantMap QtFirebaseRemoteConfig::parameters() const
{
    return _parameters;
}

void QtFirebaseRemoteConfig::setParameters(const QVariantMap &map)
{
    _parameters = map;
    emit parametersChanged();
}

long long QtFirebaseRemoteConfig::cacheExpirationTime() const
{
    return _cacheExpirationTime;
}

void QtFirebaseRemoteConfig::setCacheExpirationTime(long long timeMs)
{
    _cacheExpirationTime = timeMs;
    emit cacheExpirationTimeChanged();
}

void QtFirebaseRemoteConfig::addParameter(const QString &name, long long defaultValue)
{
    addParameterInternal(name, defaultValue);
}

void QtFirebaseRemoteConfig::addParameter(const QString &name, double defaultValue)
{
    addParameterInternal(name, defaultValue);
}

void QtFirebaseRemoteConfig::addParameter(const QString &name, const QString &defaultValue)
{
    addParameterInternal(name, defaultValue);
}

void QtFirebaseRemoteConfig::addParameter(const QString &name, bool defaultValue)
{
    addParameterInternal(name, defaultValue);
}

void QtFirebaseRemoteConfig::init()
{
    qDebug() << self << "::init" << "called";
    if(!qFirebase->ready()) {
        qDebug() << self << "::init" << "base not ready";
        return;
    }

    if(!_ready && !_initializing) {
        _initializing = true;

        ::firebase::ModuleInitializer initializer;
        auto future = initializer.Initialize(qFirebase->firebaseApp(), nullptr, [](::firebase::App* app, void*) {
            qDebug() << self << "::init" << "try to initialize Remote Config";
            return ::firebase::remote_config::Initialize(*app);
        });

        qFirebase->addFuture(__QTFIREBASE_ID + QStringLiteral(".config.init"), future);
    }
}

void QtFirebaseRemoteConfig::onFutureEvent(QString eventId, firebase::FutureBase future)
{
    if(!eventId.startsWith(__QTFIREBASE_ID))
        return;

    qDebug() << self << "::onFutureEvent" << eventId;
    if(eventId == __QTFIREBASE_ID + QStringLiteral(".config.fetch"))
        onFutureEventFetch(future);
    else if( eventId == __QTFIREBASE_ID + QStringLiteral(".config.init") )
        onFutureEventInit(future);
}

void QtFirebaseRemoteConfig::onFutureEventInit(firebase::FutureBase &future)
{
    if (future.error() != firebase::kFutureStatusComplete) {
        qDebug() << this << "::onFutureEvent" << "initializing failed." << "ERROR: Action failed with error code and message: " << future.error() << future.error_message();
        _initializing = false;
        return;
    }

    qDebug() << this << "::onFutureEvent initialized ok";
    _initializing = false;
    setReady(true);
}

void QtFirebaseRemoteConfig::onFutureEventFetch(firebase::FutureBase &future)
{
    if(future.status() != firebase::kFutureStatusComplete)
    {
        qDebug() << this << "::onFutureEvent initializing failed." << "ERROR: Action failed with error code and message: " << future.error() << future.error_message();
        _initializing = false;
        return;
    }
    qDebug() << this << "::onFutureEvent initialized ok";
    _initializing = false;

    bool fetchActivated = remote_config::ActivateFetched();
    //On first run even if we have activateResult failed we still can get cached values
    qDebug() << self << QString(QStringLiteral("ActivateFetched %1")).arg(fetchActivated ? QStringLiteral("succeeded") : QStringLiteral("failed"));

    const remote_config::ConfigInfo& info = remote_config::GetInfo();

    qDebug() << self << QString(QStringLiteral("Info last_fetch_time_ms=%1 fetch_status=%2 failure_reason=%3"))
                .arg(QString::number(info.fetch_time))
                .arg(info.last_fetch_status)
                .arg(info.last_fetch_failure_reason);

    if(info.last_fetch_status == remote_config::kLastFetchStatusSuccess)
    {
        QVariantMap updatedParameters;
        for(QVariantMap::const_iterator it = _parameters.begin(); it!=_parameters.end();++it)
        {
            const QVariant& value = it.value();

            Q_ASSERT(value.type() == QVariant::Bool ||
                     value.type() == QVariant::LongLong ||
                     value.type() == QVariant::Int ||
                     value.type() == QVariant::Double ||
                     value.type() == QVariant::String);

            if(value.type() == QVariant::Bool)
            {
                updatedParameters[it.key()] = remote_config::GetBoolean(it.key().toUtf8().constData());
            }
            else if(value.type() == QVariant::LongLong)
            {
                updatedParameters[it.key()] = remote_config::GetLong(it.key().toUtf8().constData());
            }
            else if(value.type() == QVariant::Int)
            {
                updatedParameters[it.key()] = remote_config::GetLong(it.key().toUtf8().constData());
            }
            else if(value.type() == QVariant::Double)
            {
                updatedParameters[it.key()] = remote_config::GetDouble(it.key().toUtf8().constData());
            }
            else if(value.type() == QVariant::String)
            {
                std::string result = remote_config::GetString(it.key().toUtf8().constData());
                updatedParameters[it.key()] = QString(QString::fromUtf8(result.c_str()));

                //Code for data type
                /*std::vector<unsigned char> out = remote_config::GetData(it.key().toUtf8().constData());
                QByteArray data;
                for (size_t i = 0; i < out.size(); ++i)
                {
                    data.append(out[i]);
                }
                updatedParameters[it.key()] = QString(data);*/
            }
        }

        //SDK code to print out the keys
        /*std::vector<std::string> keys = remote_config::GetKeys();
        qDebug() << "QtFirebaseRemoteConfig GetKeys:";
        for (auto s = keys.begin(); s != keys.end(); ++s)
        {
            qDebug() << s->c_str();
        }
        keys = remote_config::GetKeysByPrefix("TestD");
        printf("GetKeysByPrefix(\"TestD\"):");
        for (auto s = keys.begin(); s != keys.end(); ++s) {
          printf("  %s", s->c_str());
        }*/

        setParameters(updatedParameters);
    }
    else
    {
        if(info.last_fetch_failure_reason == remote_config::kFetchFailureReasonInvalid)
        {
            emit error(kFetchFailureReasonInvalid, QStringLiteral("The fetch has not yet failed."));
        }
        else if(info.last_fetch_failure_reason == remote_config::kFetchFailureReasonThrottled)
        {
            emit error(kFetchFailureReasonThrottled, QStringLiteral("Throttled by the server. You are sending too many fetch requests in too short a time."));
        }
        else
        {
            emit error(kFetchFailureReasonError, QStringLiteral("Failure reason is unknown"));
        }
    }
    future.Release();
}

void QtFirebaseRemoteConfig::fetch()
{
    fetch(_cacheExpirationTime<1000 ? 0 : _cacheExpirationTime/1000);
}

void QtFirebaseRemoteConfig::fetch(long long cacheExpirationInSeconds)
{
    if(_parameters.size() == 0)
    {
        qDebug() << self << "::fetch not started, parameters were not initialized";
        return;
    }
    qDebug() << self <<"::fetch with expirationtime" << cacheExpirationInSeconds << "seconds";

    QVariantMap filteredMap;
    for(QVariantMap::const_iterator it = _parameters.begin(); it!=_parameters.end();++it)
    {
        const QVariant& value = it.value();
        if(value.type() == QVariant::Bool ||
                value.type() == QVariant::LongLong ||
                value.type() == QVariant::Int ||
                value.type() == QVariant::Double ||
                value.type() == QVariant::String)
        {
            filteredMap[it.key()] = value;
        }
        else
        {
            qWarning() << self << "Data type:" << value.typeName() << " not supported";
        }
    }

    std::unique_ptr<remote_config::ConfigKeyValueVariant[]> defaults(
                new remote_config::ConfigKeyValueVariant[filteredMap.size()]);

    __defaultsByteArrayList.clear();
    uint index = 0;
    for(QVariantMap::const_iterator it = filteredMap.begin(); it!=filteredMap.end();++it)
    {
        const QVariant& value = it.value();

        __defaultsByteArrayList.insert(index,QByteArray(it.key().toUtf8().data()));
        if(value.type() == QVariant::Bool)
        {
            defaults[index] = remote_config::ConfigKeyValueVariant{__defaultsByteArrayList.at(index).constData(),
                    value.toBool()};
        }
        else if(value.type() == QVariant::LongLong)
        {
            defaults[index] = remote_config::ConfigKeyValueVariant{__defaultsByteArrayList.at(index).constData(),
                    value.toLongLong()};
        }
        else if(value.type() == QVariant::Int)
        {
            defaults[index] = remote_config::ConfigKeyValueVariant{__defaultsByteArrayList.at(index).constData(),
                    value.toInt()};
        }
        else if(value.type() == QVariant::Double)
        {
            defaults[index] = remote_config::ConfigKeyValueVariant{__defaultsByteArrayList.at(index).constData(),
                    value.toDouble()};
        }

        else if(value.type() == QVariant::String)
        {
            defaults[index] = remote_config::ConfigKeyValueVariant{__defaultsByteArrayList.at(index).constData(),
                    value.toString().toUtf8().constData()};

            //Code for data type
            /*QByteArray data = value.toString().toUtf8();
            defaults[cnt] = remote_config::ConfigKeyValueVariant{
                                it.key().toUtf8().constData(),
                                firebase::Variant::FromMutableBlob(data.constData(), data.size())};*/
        }

        index++;
    }

    remote_config::SetDefaults(defaults.get(), filteredMap.size());

    /*remote_config::SetConfigSetting(remote_config::kConfigSettingDeveloperMode, "1");
    if ((*remote_config::GetConfigSetting(remote_config::kConfigSettingDeveloperMode)
                .c_str()) != '1') {
        qDebug()<<"Failed to enable developer mode";
    }*/

    qDebug() << self << "::fetch" << "run fetching...";
    auto future = remote_config::Fetch(cacheExpirationInSeconds);
    qFirebase->addFuture(__QTFIREBASE_ID + QStringLiteral(".config.fetch"), future);
}

void QtFirebaseRemoteConfig::fetchNow()
{
    fetch(0);
}
<|endoftext|>