<commit_before>#include "lexer.hpp"
#include "../symbol-pool.hpp"
#include "../parser/parser.hpp"

namespace Mirb
{
	void Lexer::white()
	{
		input++;

		while(input.in(1, 9) || input.in(11, 12) || input.in(14, 32))
			input++;

		restep(true);
	}

	void Lexer::unknown()
	{
		input++;

		while(jump_table[input] == &Lexer::unknown)
			input++;

		lexeme.stop = &input;
		lexeme.type = Lexeme::NONE;

		if (lexeme.length() == 1)
			parser.report(lexeme.dup(memory_pool), "Invalid character '" + lexeme.string() + "'");
		else
			parser.report(lexeme.dup(memory_pool), "Invalid characters '" + lexeme.string() + "'");

		restep();
	}

	bool Lexer::process_null(const char_t *input, bool expected)
	{
		bool result = (size_t)(input - input_str) >= length;

		if(!result && !expected)
			report_null();

		return result;
	}

	void Lexer::null()
	{
		if(process_null(&input, true))
		{
			lexeme.stop = &input;
			lexeme.type = Lexeme::END;
		}
		else
			unknown();
	}

	void Lexer::newline()
	{
		input++;
		lexeme.line++;
		lexeme.line_start = &input;

		lexeme.start = &input;
		lexeme.stop = &input;
		lexeme.type = Lexeme::LINE;
	}

	void Lexer::carrige_return()
	{
		input++;
		lexeme.line++;

		if(input == '\n')
			input++;

		lexeme.line_start = &input;

		lexeme.start = &input;
		lexeme.stop = &input;
		lexeme.type = Lexeme::LINE;
	}

	void Lexer::eol()
	{
		while(input != '\n' && input != '\r' && input != 0)
			input++;
	}

	void Lexer::curly_open()
	{
		input++;

		lexeme.curlies.push(nullptr);

		lexeme.stop = &input;
		lexeme.type = Lexeme::CURLY_OPEN;
	}

	void Lexer::exclamation()
	{
		input++;

		switch(input)
		{
			case '=':
			{
				input++;
				lexeme.type = Lexeme::NO_EQUALITY;

				break;
			}

			case '~':
			{
				input++;
				lexeme.type = Lexeme::NOT_MATCHES;

				break;
			}

			default:
			{
				lexeme.type = Lexeme::LOGICAL_NOT;

				break;
			}
		}

		lexeme.stop = &input;
	}

	void Lexer::colon()
	{
		input++;

		if(input == ':')
		{
			input++;

			lexeme.type = Lexeme::SCOPE;
		}
		else
		{
			if(is_start_ident(input))
			{
				skip_ident();

				lexeme.stop = &input;
				lexeme.type = Lexeme::SYMBOL;
				lexeme.symbol = symbol_pool.get(lexeme);
			}
			else
				lexeme.type = Lexeme::COLON;
		}

		lexeme.stop = &input;
	}

	void Lexer::assign_equal()
	{
		input++;

		switch(input)
		{
			case '=':
			{
				input++;

				if(input == '=')
				{
					input++;

					lexeme.type = Lexeme::CASE_EQUALITY;
				}
				else
					lexeme.type = Lexeme::EQUALITY;

				break;
			}

			case '>':
			{
				input++;
				lexeme.type = Lexeme::ASSOC;

				break;
			}

			case '~':
			{
				input++;
				lexeme.type = Lexeme::MATCHES;

				break;
			}

			default:
			{
				lexeme.type = Lexeme::ASSIGN;

				break;
			}
		}

		lexeme.stop = &input;
	}

	void Lexer::compare()
	{
		input++;

		switch(input)
		{
			case '<':
				input++;
				if(input == '=')
				{
					input++;
					lexeme.stop = &input;
					lexeme.type = Lexeme::ASSIGN_LEFT_SHIFT;
				}
				else
				{
					lexeme.stop = &input;
					lexeme.type = Lexeme::LEFT_SHIFT;
				}
				break;

			case '=':
				input++;
				if(input == '>')
				{
					input++;
					lexeme.stop = &input;
					lexeme.type = Lexeme::COMPARE;
				}
				else
				{
					lexeme.stop = &input;
					lexeme.type = Lexeme::LESS_OR_EQUAL;
				}
				break;

			default:
				lexeme.stop = &input;
				lexeme.type = Lexeme::LESS;
		}
	}

	void Lexer::comment()
	{
		eol();
		restep();
	}

	bool Lexer::is_start_ident(char_t c)
	{
		return Input::char_in(c, 'a', 'z') || Input::char_in(c, 'A', 'Z') || c == '_';
	}

	bool Lexer::is_ident(char_t c)
	{
		return is_start_ident(c)  || Input::char_in(c, '0', '9');
	}

	void Lexer::skip_ident()
	{
		while(is_ident(input))
			input++;
	}

	void Lexer::ivar()
	{
		input++;

		if(is_ident(input))
		{
			input++;

			bool error = !is_start_ident(input);

			skip_ident();

			lexeme.stop = &input;
			lexeme.type = Lexeme::IVAR;
			lexeme.symbol = symbol_pool.get(lexeme);

			if(error)
				parser.report(lexeme.dup(memory_pool), "Instance variable names cannot start with a number");
		}
		else if(input == '@')
		{
			input++;

			if(is_ident(input))
			{
				input++;

				bool error = !is_start_ident(input);

				skip_ident();

				lexeme.stop = &input;
				lexeme.type = Lexeme::CVAR;
				lexeme.symbol = symbol_pool.get(lexeme);

				if(error)
					parser.report(lexeme.dup(memory_pool), "Class variable names cannot start with a number");
			}
			else
			{
				lexeme.stop = &input;
				lexeme.type = Lexeme::CVAR;
				lexeme.symbol = symbol_pool.get(lexeme);

				parser.report(lexeme.dup(memory_pool), "Expected a class variable name");
			}
		}
		else
		{
			lexeme.stop = &input;
			lexeme.type = Lexeme::IVAR;
			lexeme.symbol = symbol_pool.get(lexeme);

			parser.report(lexeme.dup(memory_pool), "Expected a instance variable name");
		}
	}

	void Lexer::global()
	{
		input++;

		if(!is_ident(input))
		{
			lexeme.stop = &input;
			lexeme.type = Lexeme::GLOBAL;
			lexeme.symbol = symbol_pool.get(lexeme);

			parser.report(lexeme.dup(memory_pool), "Expected a global variable name");

			return;
		}

		bool error = !is_start_ident(input);

		skip_ident();

		lexeme.stop = &input;
		lexeme.type = Lexeme::GLOBAL;
		lexeme.symbol = symbol_pool.get(lexeme);

		if(error)
			parser.report(lexeme.dup(memory_pool), "Global variable names cannot start with a number");
	}

	void Lexer::add()
	{
		input++;

		if(input == '=')
		{
			input++;
			lexeme.type = Lexeme::ASSIGN_ADD;
		}
		else if(input == '@')
		{
			input++;
			lexeme.type = Lexeme::UNARY_ADD;
		}
		else
		{
			lexeme.type = Lexeme::ADD;
		}

		lexeme.stop = &input;
	}

	void Lexer::sub()
	{
		input++;

		if(input == '=')
		{
			input++;
			lexeme.type = Lexeme::ASSIGN_SUB;
		}
		else if(input == '@')
		{
			input++;
			lexeme.type = Lexeme::UNARY_SUB;
		}
		else
		{
			lexeme.type = Lexeme::SUB;
		}

		lexeme.stop = &input;
	}

	void Lexer::ident()
	{
		input++;

		skip_ident();

		switch(input)
		{
			case '?':
			case '!':
			{
				input++;

				lexeme.type = Lexeme::EXT_IDENT;

				break;
			}

			default:
			{
				lexeme.type = Lexeme::IDENT;

				break;
			}
		}

		lexeme.stop = &input;
		lexeme.symbol = symbol_pool.get(lexeme);

		if(lexeme.allow_keywords)
		{
			Lexeme::Type keyword = keywords.mapping.try_get(lexeme.symbol, [] { return Lexeme::NONE; });

			if(keyword != Lexeme::NONE)
				lexeme.type = keyword;
		}
	}
};
<commit_msg>Fixed a bug.<commit_after>#include "lexer.hpp"
#include "../symbol-pool.hpp"
#include "../parser/parser.hpp"

namespace Mirb
{
	void Lexer::white()
	{
		input++;

		while(input.in(1, 9) || input.in(11, 12) || input.in(14, 32))
			input++;

		restep(true);
	}

	void Lexer::unknown()
	{
		input++;

		while(jump_table[input] == &Lexer::unknown)
			input++;

		lexeme.stop = &input;
		lexeme.type = Lexeme::NONE;

		if (lexeme.length() == 1)
			parser.report(lexeme.dup(memory_pool), "Invalid character '" + lexeme.string() + "'");
		else
			parser.report(lexeme.dup(memory_pool), "Invalid characters '" + lexeme.string() + "'");

		restep();
	}

	bool Lexer::process_null(const char_t *input, bool expected)
	{
		bool result = (size_t)(input - input_str) >= length;

		if(!result && !expected)
			report_null();

		return result;
	}

	void Lexer::null()
	{
		if(process_null(&input, true))
		{
			lexeme.stop = &input;
			lexeme.type = Lexeme::END;
		}
		else
			unknown();
	}

	void Lexer::newline()
	{
		input++;
		lexeme.line++;
		lexeme.line_start = &input;

		lexeme.start = &input;
		lexeme.stop = &input;
		lexeme.type = Lexeme::LINE;
	}

	void Lexer::carrige_return()
	{
		input++;
		lexeme.line++;

		if(input == '\n')
			input++;

		lexeme.line_start = &input;

		lexeme.start = &input;
		lexeme.stop = &input;
		lexeme.type = Lexeme::LINE;
	}

	void Lexer::eol()
	{
		while(input != '\n' && input != '\r' && input != 0)
			input++;
	}

	void Lexer::curly_open()
	{
		input++;

		lexeme.curlies.push(nullptr);

		lexeme.stop = &input;
		lexeme.type = Lexeme::CURLY_OPEN;
	}

	void Lexer::exclamation()
	{
		input++;

		switch(input)
		{
			case '=':
			{
				input++;
				lexeme.type = Lexeme::NO_EQUALITY;

				break;
			}

			case '~':
			{
				input++;
				lexeme.type = Lexeme::NOT_MATCHES;

				break;
			}

			default:
			{
				lexeme.type = Lexeme::LOGICAL_NOT;

				break;
			}
		}

		lexeme.stop = &input;
	}

	void Lexer::colon()
	{
		input++;

		if(input == ':')
		{
			input++;

			lexeme.type = Lexeme::SCOPE;
		}
		else
		{
			if(is_start_ident(input))
			{
				skip_ident();

				lexeme.stop = &input;
				lexeme.type = Lexeme::SYMBOL;
				lexeme.symbol = symbol_pool.get(lexeme);
			}
			else
				lexeme.type = Lexeme::COLON;
		}

		lexeme.stop = &input;
	}

	void Lexer::assign_equal()
	{
		input++;

		switch(input)
		{
			case '=':
			{
				input++;

				if(input == '=')
				{
					input++;

					lexeme.type = Lexeme::CASE_EQUALITY;
				}
				else
					lexeme.type = Lexeme::EQUALITY;

				break;
			}

			case '>':
			{
				input++;
				lexeme.type = Lexeme::ASSOC;

				break;
			}

			case '~':
			{
				input++;
				lexeme.type = Lexeme::MATCHES;

				break;
			}

			default:
			{
				lexeme.type = Lexeme::ASSIGN;

				break;
			}
		}

		lexeme.stop = &input;
	}

	void Lexer::compare()
	{
		input++;

		switch(input)
		{
			case '<':
				input++;
				if(input == '=')
				{
					input++;
					lexeme.stop = &input;
					lexeme.type = Lexeme::ASSIGN_LEFT_SHIFT;
				}
				else
				{
					lexeme.stop = &input;
					lexeme.type = Lexeme::LEFT_SHIFT;
				}
				break;

			case '=':
				input++;
				if(input == '>')
				{
					input++;
					lexeme.stop = &input;
					lexeme.type = Lexeme::COMPARE;
				}
				else
				{
					lexeme.stop = &input;
					lexeme.type = Lexeme::LESS_OR_EQUAL;
				}
				break;

			default:
				lexeme.stop = &input;
				lexeme.type = Lexeme::LESS;
		}
	}

	void Lexer::comment()
	{
		eol();
		restep();
	}

	bool Lexer::is_start_ident(char_t c)
	{
		return Input::char_in(c, 'a', 'z') || Input::char_in(c, 'A', 'Z') || c == '_';
	}

	bool Lexer::is_ident(char_t c)
	{
		return is_start_ident(c)  || Input::char_in(c, '0', '9');
	}

	void Lexer::skip_ident()
	{
		while(is_ident(input))
			input++;
	}

	void Lexer::ivar()
	{
		input++;

		if(is_ident(input))
		{
			bool error = !is_start_ident(input);

			input++;

			skip_ident();

			lexeme.stop = &input;
			lexeme.type = Lexeme::IVAR;
			lexeme.symbol = symbol_pool.get(lexeme);

			if(error)
				parser.report(lexeme.dup(memory_pool), "Instance variable names cannot start with a number");
		}
		else if(input == '@')
		{
			input++;

			if(is_ident(input))
			{
				bool error = !is_start_ident(input);

				input++;

				skip_ident();

				lexeme.stop = &input;
				lexeme.type = Lexeme::CVAR;
				lexeme.symbol = symbol_pool.get(lexeme);

				if(error)
					parser.report(lexeme.dup(memory_pool), "Class variable names cannot start with a number");
			}
			else
			{
				lexeme.stop = &input;
				lexeme.type = Lexeme::CVAR;
				lexeme.symbol = symbol_pool.get(lexeme);

				parser.report(lexeme.dup(memory_pool), "Expected a class variable name");
			}
		}
		else
		{
			lexeme.stop = &input;
			lexeme.type = Lexeme::IVAR;
			lexeme.symbol = symbol_pool.get(lexeme);

			parser.report(lexeme.dup(memory_pool), "Expected a instance variable name");
		}
	}

	void Lexer::global()
	{
		input++;

		if(!is_ident(input))
		{
			lexeme.stop = &input;
			lexeme.type = Lexeme::GLOBAL;
			lexeme.symbol = symbol_pool.get(lexeme);

			parser.report(lexeme.dup(memory_pool), "Expected a global variable name");

			return;
		}

		bool error = !is_start_ident(input);

		skip_ident();

		lexeme.stop = &input;
		lexeme.type = Lexeme::GLOBAL;
		lexeme.symbol = symbol_pool.get(lexeme);

		if(error)
			parser.report(lexeme.dup(memory_pool), "Global variable names cannot start with a number");
	}

	void Lexer::add()
	{
		input++;

		if(input == '=')
		{
			input++;
			lexeme.type = Lexeme::ASSIGN_ADD;
		}
		else if(input == '@')
		{
			input++;
			lexeme.type = Lexeme::UNARY_ADD;
		}
		else
		{
			lexeme.type = Lexeme::ADD;
		}

		lexeme.stop = &input;
	}

	void Lexer::sub()
	{
		input++;

		if(input == '=')
		{
			input++;
			lexeme.type = Lexeme::ASSIGN_SUB;
		}
		else if(input == '@')
		{
			input++;
			lexeme.type = Lexeme::UNARY_SUB;
		}
		else
		{
			lexeme.type = Lexeme::SUB;
		}

		lexeme.stop = &input;
	}

	void Lexer::ident()
	{
		input++;

		skip_ident();

		switch(input)
		{
			case '?':
			case '!':
			{
				input++;

				lexeme.type = Lexeme::EXT_IDENT;

				break;
			}

			default:
			{
				lexeme.type = Lexeme::IDENT;

				break;
			}
		}

		lexeme.stop = &input;
		lexeme.symbol = symbol_pool.get(lexeme);

		if(lexeme.allow_keywords)
		{
			Lexeme::Type keyword = keywords.mapping.try_get(lexeme.symbol, [] { return Lexeme::NONE; });

			if(keyword != Lexeme::NONE)
				lexeme.type = keyword;
		}
	}
};
<|endoftext|>


<commit_before>#include "logMsg/logMsg.h"                          // LM_W

#include "au/mutex/TokenTaker.h"

#include "engine/DiskOperation.h"                   // engine::DiskOperation
#include "engine/DiskManager.h"                     // notification_disk_operation_request_response
#include "engine/Engine.h"                          // engine::Engine
#include "engine/Notification.h"                    // engine::Notification
#include "engine/MemoryManager.h"                   // engine::MemoryManager
#include "engine/ProcessManager.h"

#include "samson/common/MessagesOperations.h"
#include "samson/common/MemoryTags.h"               // MemoryBlocks
#include "samson/common/SamsonSetup.h"              // samson::SamsonSetup
#include "samson/common/KVHeader.h"

#include "samson/module/KVFormat.h"
#include "samson/module/ModulesManager.h"           // ModulesManager

#include "PopQueueTask.h"                           // samson::stream::PopQueueTask
#include "BlockManager.h"                           // BlockManager
#include "BlockList.h"                              // BlockList

#include "Block.h"

#include "BlockLookupList.h" // Own interface

namespace samson {
    namespace stream
    {

        BlockLookupList::BlockLookupList( Block* block )
        {
            // Vector of KVInfo per hash-group
            KVInfo* kvInfoV  = createKVInfoVector(block->getData(), &error);
            if( !kvInfoV )
            {
                head = NULL;
                hashInfo = NULL;
                return;
            }

            LM_T(LmtRest, ("Creating lookup list"));

            // Store format locally
            kvFormat = block->getKVFormat();

            // semTake();
            head = (BlockLookupRecord*) calloc(block->header->info.kvs, sizeof(BlockLookupRecord));
            size = block->header->info.kvs;

            if (head == NULL)
                LM_X(1,("Error allocating lookupList.head of %d bytes", block->header->info.kvs * sizeof(BlockLookupRecord)));

            hashInfo = (BlockHashLookupRecord*) calloc(KVFILE_NUM_HASHGROUPS, sizeof(BlockHashLookupRecord));
            if (hashInfo == NULL)
                LM_X(1,("Error allocating lookupList.hashInfo of %d bytes", KVFILE_NUM_HASHGROUPS * sizeof(BlockHashLookupRecord)));

            LM_T(LmtRest, ("Created a lookup list for %d records", block->header->info.kvs));

            unsigned int  hashIx;
            unsigned int  kvIx;
            unsigned int  offset              = 0;
            unsigned int  noOfKvs             = 0;
            char*         kvsStart            = block->getData() + sizeof(KVHeader);
            Data*         keyData             = ModulesManager::shared()->getData(kvFormat.keyFormat);
            Data*         valueData           = ModulesManager::shared()->getData(kvFormat.valueFormat);
            DataInstance* keyDataInstance     = (DataInstance*) keyData->getInstance();
            DataInstance* valueDataInstance   = (DataInstance*) valueData->getInstance();
            int           maxEntries          = 0;
            int           maxEntryHashGroup   = -1;

            for (hashIx = 0; hashIx < KVFILE_NUM_HASHGROUPS; hashIx++)
            {
                if (kvInfoV[hashIx].kvs != 0)
                {
                    int entries = kvInfoV[hashIx].kvs;

                    if (entries > maxEntries)
                    {
                        maxEntries        = entries;
                        maxEntryHashGroup = hashIx;
                    }

                    LM_T(LmtRest, ("setting hashInfo[%d]: %d-%d (%d entries in hashgroup - max entries is %d)", hashIx, noOfKvs, noOfKvs + kvInfoV[hashIx].kvs, entries, maxEntries));
                }

                hashInfo[hashIx].startIx = noOfKvs;
                hashInfo[hashIx].endIx   = noOfKvs + kvInfoV[hashIx].kvs;

                for (kvIx = 0; kvIx < kvInfoV[hashIx].kvs; kvIx++)
                {
                    int keySize   = keyDataInstance->parse(kvsStart + offset);
                    int valueSize = valueDataInstance->parse(kvsStart + offset + keySize);

                    head[noOfKvs].keyP = kvsStart + offset;
                    head[noOfKvs].keyS = keySize;

                    offset += (keySize + valueSize);
                    ++noOfKvs;
                }
            }
            LM_T(LmtRest, ("Hash Group %d has %d entries", maxEntryHashGroup, maxEntries));

            // int wordIx = 0;
            // for (size_t ix = lookupList.hashInfo[maxEntryHashGroup].startIx; ix <= lookupList.hashInfo[maxEntryHashGroup].endIx; ix++)
            // {
            //     samson::system::String string;
            //     string->parse(lookupList.head[ix].keyP);
            //
            //     LM_T(LmtHash, ("HG %d, Word %02d: '%s'", maxEntryHashGroup, wordIx, string->value.c_str()));
            //     ++wordIx;
            // }

            LM_T(LmtRest, ("lookup list created"));
            // semGive();

            if( kvInfoV )
                free(kvInfoV);
        }

        BlockLookupList::~BlockLookupList()
        {
            if (head != NULL)
                free(head);
            if (hashInfo != NULL)
                free(hashInfo);
        }

        int compare_binary_keys( char* key , size_t key_size , char* key2 , size_t key_size2 )
        {

            if( key_size < key_size2 )
                return -1;

            if( key_size > key_size2 )
                return 1;

            for (int i = 0 ; i < key_size ; i++ )
            {

                if( key[i] < key2[i] )
                    return -1;

                if( key[i] > key2[i] )
                    return 1;
            }
            return 0;
        }

        std::string BlockLookupList::lookup( const char* key, std::string outputFormat)
        {
            int            hashGroup;
            int            keySize;
            int            testKeySize;
            char           keyName[1024];
            Data*          keyData             = ModulesManager::shared()->getData(kvFormat.keyFormat);
            DataInstance*  keyDataInstance     = (DataInstance*) keyData->getInstance();
            int            compare;

            keyDataInstance->setFromString(key);

            keySize      = keyDataInstance->serialize(keyName);
            hashGroup    = keyDataInstance->hash(KVFILE_NUM_HASHGROUPS);
            LM_T(LmtRest, ("looking up key '%s'(keyDataInstance:name:%s, val:%s) in hashgroup:%d", key, keyDataInstance->getName().c_str(), keyDataInstance->str().c_str(), hashGroup));

            int startIx  = hashInfo[hashGroup].startIx;
            int endIx    = hashInfo[hashGroup].endIx;
            int testIx   = (endIx - startIx) / 2 + startIx;

            while (true)
            {
                LM_T(LmtRest, ("looking up key '%s' - comparing with ix %d (from ix %d to %d)", key, testIx, startIx, endIx));
                // Andreu: We compare key and values only looking at the binary representation
                //compare = keyDataInstance->serial_compare(keyName, head[testIx].keyP);
                compare = compare_binary_keys(keyName, keySize, head[testIx].keyP , head[testIx].keyS );

                if (compare == 0)
                {
                    testKeySize = keyDataInstance->parse( head[testIx].keyP );

                    Data*          valueData          = ModulesManager::shared()->getData(kvFormat.valueFormat);
                    char*          valueP             = (char*) ((size_t) head[testIx].keyP + testKeySize);
                    DataInstance*  valueDataInstance  = (DataInstance*) valueData->getInstance();

                    valueDataInstance->parse(valueP);

                    LM_T(LmtRest, ("Match key '%s' - at ix %d (testKeySize:%d, name:%s, path:%s)", key, testIx, testKeySize, valueDataInstance->getName().c_str(), valueDataInstance->getType()));

                    if (outputFormat == "xml")
                    {
                        std::ostringstream output;
                        output << "<key>\n";
                        output << keyDataInstance->strXML();
                        output << "</key>\n";
                        output << "<value>\n";
                        output << valueDataInstance->strXML();
                        output << "</value>\n";
                        return output.str();
                    }
                    if (outputFormat == "html")
                    {
                        std::ostringstream output;
                        //output << "
key
\n";
                        output << keyDataInstance->strHTML("key", 1);
                        //output << "
value
\n";
                        output << valueDataInstance->strHTML("value", 1);
                        return output.str();
                    }
                    if (outputFormat == "thtml")
                    {
                        std::ostringstream output;

                        output << keyDataInstance->strHTMLTable("key");

                        output << valueDataInstance->strHTMLTable("value");

                        return output.str();
                    }
                    else
                    {
                        std::ostringstream output;
                        output << "{ \"key\" : ";
                        output << keyDataInstance->strJSON();
                        output << " \"value\" : ";
                        output << valueDataInstance->strJSON();
                        output << " } ";
                        return output.str();
                    }
                }

                if (compare < 0) // keyName < testKey => Go to the left - to 'smaller' key names
                    endIx = testIx - 1;
                else
                    startIx = testIx + 1;

                testIx = (endIx - startIx) / 2 + startIx;

                if (startIx > endIx) // Not found
                {
                    if (outputFormat == "xml")
                        return au::xml_simple("error", au::str("Key %s not found" , key));
                    else
                        return std::string("  \"error\" : \"") + au::str("Key %s not found\"\r\n" , key);
                }
            }
        }

    }
}

        <commit_msg>Fix broken compulaton in linux<commit_after>#include "logMsg/logMsg.h"                          // LM_W

#include "au/mutex/TokenTaker.h"

#include "engine/DiskOperation.h"                   // engine::DiskOperation
#include "engine/DiskManager.h"                     // notification_disk_operation_request_response
#include "engine/Engine.h"                          // engine::Engine
#include "engine/Notification.h"                    // engine::Notification
#include "engine/MemoryManager.h"                   // engine::MemoryManager
#include "engine/ProcessManager.h"

#include "samson/common/MessagesOperations.h"
#include "samson/common/MemoryTags.h"               // MemoryBlocks
#include "samson/common/SamsonSetup.h"              // samson::SamsonSetup
#include "samson/common/KVHeader.h"

#include "samson/module/KVFormat.h"
#include "samson/module/ModulesManager.h"           // ModulesManager

#include "PopQueueTask.h"                           // samson::stream::PopQueueTask
#include "BlockManager.h"                           // BlockManager
#include "BlockList.h"                              // BlockList

#include "Block.h"

#include "BlockLookupList.h" // Own interface

namespace samson {
    namespace stream
    {

        BlockLookupList::BlockLookupList( Block* block )
        {
            // Vector of KVInfo per hash-group
            KVInfo* kvInfoV  = createKVInfoVector(block->getData(), &error);
            if( !kvInfoV )
            {
                head = NULL;
                hashInfo = NULL;
                return;
            }

            LM_T(LmtRest, ("Creating lookup list"));

            // Store format locally
            kvFormat = block->getKVFormat();

            // semTake();
            head = (BlockLookupRecord*) calloc(block->header->info.kvs, sizeof(BlockLookupRecord));
            size = block->header->info.kvs;

            if (head == NULL)
                LM_X(1,("Error allocating lookupList.head of %d bytes", block->header->info.kvs * sizeof(BlockLookupRecord)));

            hashInfo = (BlockHashLookupRecord*) calloc(KVFILE_NUM_HASHGROUPS, sizeof(BlockHashLookupRecord));
            if (hashInfo == NULL)
                LM_X(1,("Error allocating lookupList.hashInfo of %d bytes", KVFILE_NUM_HASHGROUPS * sizeof(BlockHashLookupRecord)));

            LM_T(LmtRest, ("Created a lookup list for %d records", block->header->info.kvs));

            unsigned int  hashIx;
            unsigned int  kvIx;
            unsigned int  offset              = 0;
            unsigned int  noOfKvs             = 0;
            char*         kvsStart            = block->getData() + sizeof(KVHeader);
            Data*         keyData             = ModulesManager::shared()->getData(kvFormat.keyFormat);
            Data*         valueData           = ModulesManager::shared()->getData(kvFormat.valueFormat);
            DataInstance* keyDataInstance     = (DataInstance*) keyData->getInstance();
            DataInstance* valueDataInstance   = (DataInstance*) valueData->getInstance();
            int           maxEntries          = 0;
            int           maxEntryHashGroup   = -1;

            for (hashIx = 0; hashIx < KVFILE_NUM_HASHGROUPS; hashIx++)
            {
                if (kvInfoV[hashIx].kvs != 0)
                {
                    int entries = kvInfoV[hashIx].kvs;

                    if (entries > maxEntries)
                    {
                        maxEntries        = entries;
                        maxEntryHashGroup = hashIx;
                    }

                    LM_T(LmtRest, ("setting hashInfo[%d]: %d-%d (%d entries in hashgroup - max entries is %d)", hashIx, noOfKvs, noOfKvs + kvInfoV[hashIx].kvs, entries, maxEntries));
                }

                hashInfo[hashIx].startIx = noOfKvs;
                hashInfo[hashIx].endIx   = noOfKvs + kvInfoV[hashIx].kvs;

                for (kvIx = 0; kvIx < kvInfoV[hashIx].kvs; kvIx++)
                {
                    int keySize   = keyDataInstance->parse(kvsStart + offset);
                    int valueSize = valueDataInstance->parse(kvsStart + offset + keySize);

                    head[noOfKvs].keyP = kvsStart + offset;
                    head[noOfKvs].keyS = keySize;

                    offset += (keySize + valueSize);
                    ++noOfKvs;
                }
            }
            LM_T(LmtRest, ("Hash Group %d has %d entries", maxEntryHashGroup, maxEntries));

            // int wordIx = 0;
            // for (size_t ix = lookupList.hashInfo[maxEntryHashGroup].startIx; ix <= lookupList.hashInfo[maxEntryHashGroup].endIx; ix++)
            // {
            //     samson::system::String string;
            //     string->parse(lookupList.head[ix].keyP);
            //
            //     LM_T(LmtHash, ("HG %d, Word %02d: '%s'", maxEntryHashGroup, wordIx, string->value.c_str()));
            //     ++wordIx;
            // }

            LM_T(LmtRest, ("lookup list created"));
            // semGive();

            if( kvInfoV )
                free(kvInfoV);
        }

        BlockLookupList::~BlockLookupList()
        {
            if (head != NULL)
                free(head);
            if (hashInfo != NULL)
                free(hashInfo);
        }

        int compare_binary_keys( char* key , size_t key_size , char* key2 , size_t key_size2 )
        {

            if( key_size < key_size2 )
                return -1;

            if( key_size > key_size2 )
                return 1;

            for (size_t i = 0 ; i < key_size ; i++ )
            {

                if( key[i] < key2[i] )
                    return -1;

                if( key[i] > key2[i] )
                    return 1;
            }
            return 0;
        }

        std::string BlockLookupList::lookup( const char* key, std::string outputFormat)
        {
            int            hashGroup;
            int            keySize;
            int            testKeySize;
            char           keyName[1024];
            Data*          keyData             = ModulesManager::shared()->getData(kvFormat.keyFormat);
            DataInstance*  keyDataInstance     = (DataInstance*) keyData->getInstance();
            int            compare;

            keyDataInstance->setFromString(key);

            keySize      = keyDataInstance->serialize(keyName);
            hashGroup    = keyDataInstance->hash(KVFILE_NUM_HASHGROUPS);
            LM_T(LmtRest, ("looking up key '%s'(keyDataInstance:name:%s, val:%s) in hashgroup:%d", key, keyDataInstance->getName().c_str(), keyDataInstance->str().c_str(), hashGroup));

            int startIx  = hashInfo[hashGroup].startIx;
            int endIx    = hashInfo[hashGroup].endIx;
            int testIx   = (endIx - startIx) / 2 + startIx;

            while (true)
            {
                LM_T(LmtRest, ("looking up key '%s' - comparing with ix %d (from ix %d to %d)", key, testIx, startIx, endIx));
                // Andreu: We compare key and values only looking at the binary representation
                //compare = keyDataInstance->serial_compare(keyName, head[testIx].keyP);
                compare = compare_binary_keys(keyName, keySize, head[testIx].keyP , head[testIx].keyS );

                if (compare == 0)
                {
                    testKeySize = keyDataInstance->parse( head[testIx].keyP );

                    Data*          valueData          = ModulesManager::shared()->getData(kvFormat.valueFormat);
                    char*          valueP             = (char*) ((size_t) head[testIx].keyP + testKeySize);
                    DataInstance*  valueDataInstance  = (DataInstance*) valueData->getInstance();

                    valueDataInstance->parse(valueP);

                    LM_T(LmtRest, ("Match key '%s' - at ix %d (testKeySize:%d, name:%s, path:%s)", key, testIx, testKeySize, valueDataInstance->getName().c_str(), valueDataInstance->getType()));

                    if (outputFormat == "xml")
                    {
                        std::ostringstream output;
                        output << "<key>\n";
                        output << keyDataInstance->strXML();
                        output << "</key>\n";
                        output << "<value>\n";
                        output << valueDataInstance->strXML();
                        output << "</value>\n";
                        return output.str();
                    }
                    if (outputFormat == "html")
                    {
                        std::ostringstream output;
                        //output << "
key
\n";
                        output << keyDataInstance->strHTML("key", 1);
                        //output << "
value
\n";
                        output << valueDataInstance->strHTML("value", 1);
                        return output.str();
                    }
                    if (outputFormat == "thtml")
                    {
                        std::ostringstream output;

                        output << keyDataInstance->strHTMLTable("key");

                        output << valueDataInstance->strHTMLTable("value");

                        return output.str();
                    }
                    else
                    {
                        std::ostringstream output;
                        output << "{ \"key\" : ";
                        output << keyDataInstance->strJSON();
                        output << " \"value\" : ";
                        output << valueDataInstance->strJSON();
                        output << " } ";
                        return output.str();
                    }
                }

                if (compare < 0) // keyName < testKey => Go to the left - to 'smaller' key names
                    endIx = testIx - 1;
                else
                    startIx = testIx + 1;

                testIx = (endIx - startIx) / 2 + startIx;

                if (startIx > endIx) // Not found
                {
                    if (outputFormat == "xml")
                        return au::xml_simple("error", au::str("Key %s not found" , key));
                    else
                        return std::string("  \"error\" : \"") + au::str("Key %s not found\"\r\n" , key);
                }
            }
        }

    }
}

<|endoftext|>


<commit_before>
#include "StaticFileServing.h"

#include <QStringBuilder>
#include <QUrlQuery>
#include <QList>
#include <QPair>
#include <QFile>
#include <QFileInfo>
#include <QResource>

StaticFileServing::StaticFileServing (Hyperion *hyperion, QString baseUrl, quint16 port, QObject * parent)
	:  QObject   (parent)
	, _hyperion(hyperion)
	, _baseUrl (baseUrl)
	, _cgi(hyperion, baseUrl, this)
	, _log(Logger::getInstance("WEBSERVER"))
{
	Q_INIT_RESOURCE(WebConfig);

	_mimeDb = new QMimeDatabase;

	_server = new QtHttpServer (this);
	_server->setServerName (QStringLiteral ("Hyperion WebConfig"));

	connect (_server, &QtHttpServer::started,           this, &StaticFileServing::onServerStarted);
	connect (_server, &QtHttpServer::stopped,           this, &StaticFileServing::onServerStopped);
	connect (_server, &QtHttpServer::error,             this, &StaticFileServing::onServerError);
	connect (_server, &QtHttpServer::requestNeedsReply, this, &StaticFileServing::onRequestNeedsReply);

	_server->start (port);

}

StaticFileServing::~StaticFileServing ()
{
	_server->stop ();
}

void StaticFileServing::onServerStarted (quint16 port)
{
	Info(_log, "started on port %d name \"%s\"", port ,_server->getServerName().toStdString().c_str());
}

void StaticFileServing::onServerStopped () {
	Info(_log, "stopped %s", _server->getServerName().toStdString().c_str());
}

void StaticFileServing::onServerError (QString msg)
{
	Error(_log, "%s", msg.toStdString().c_str());
}

static inline void printErrorToReply (QtHttpReply * reply, QString errorMessage)
{
	reply->addHeader ("Content-Type", QByteArrayLiteral ("text/plain"));
	reply->appendRawData (errorMessage.toLocal8Bit ());
}

void StaticFileServing::onRequestNeedsReply (QtHttpRequest * request, QtHttpReply * reply)
{
	QString command = request->getCommand ();
	if (command == QStringLiteral ("GET"))
	{
		QString path = request->getUrl ().path ();
		QStringList uri_parts = path.split('/', QString::SkipEmptyParts);

		// special uri handling for server commands
		if ( ! uri_parts.empty() && uri_parts.at(0) == "cgi"  )
		{
			uri_parts.removeAt(0);
			try
			{
				_cgi.exec(uri_parts, request, reply);
			}
			catch(...)
			{
				printErrorToReply (reply, "script failed (" % path % ")");
			}
			return;
		}
		Q_INIT_RESOURCE(WebConfig);

		QFileInfo info(_baseUrl % "/" % path);
		if ( path == "/" || path.isEmpty() || ! info.exists() )
		{
			path = "index.html";
		}
		else if (info.isDir() && path.endsWith("/") )
		{
			path += "index.html";
		}
		else if (info.isDir() && ! path.endsWith("/") )
		{
			path += "/index.html";
		}

		// get static files
		QFile file(_baseUrl % "/" % path);
		if (file.exists())
		{
			QMimeType mime = _mimeDb->mimeTypeForFile (file.fileName ());
			if (file.open (QFile::ReadOnly)) {
				QByteArray data = file.readAll ();
				reply->addHeader ("Content-Type", mime.name ().toLocal8Bit ());
				reply->appendRawData (data);
				file.close ();
			}
			else
			{
				printErrorToReply (reply, "Requested file " % path % " couldn't be open for reading !");
			}
		}
		else
		{
			printErrorToReply (reply, "Requested file " % path % " couldn't be found !");
		}
	}
	else
	{
		printErrorToReply (reply, "Unhandled HTTP/1.1 method " % command % " on static file server !");
	}
}

<commit_msg>Added avahi/bonjour advertisement to webconfig (#258)<commit_after>
#include "StaticFileServing.h"

#include <QStringBuilder>
#include <QUrlQuery>
#include <QList>
#include <QPair>
#include <QFile>
#include <QFileInfo>
#include <QResource>
#include <QHostInfo>
#include <bonjour/bonjourserviceregister.h>
#include <bonjour/bonjourrecord.h>

StaticFileServing::StaticFileServing (Hyperion *hyperion, QString baseUrl, quint16 port, QObject * parent)
	:  QObject   (parent)
	, _hyperion(hyperion)
	, _baseUrl (baseUrl)
	, _cgi(hyperion, baseUrl, this)
	, _log(Logger::getInstance("WEBSERVER"))
{
	Q_INIT_RESOURCE(WebConfig);

	_mimeDb = new QMimeDatabase;

	_server = new QtHttpServer (this);
	_server->setServerName (QStringLiteral ("Hyperion WebConfig"));

	connect (_server, &QtHttpServer::started,           this, &StaticFileServing::onServerStarted);
	connect (_server, &QtHttpServer::stopped,           this, &StaticFileServing::onServerStopped);
	connect (_server, &QtHttpServer::error,             this, &StaticFileServing::onServerError);
	connect (_server, &QtHttpServer::requestNeedsReply, this, &StaticFileServing::onRequestNeedsReply);

	_server->start (port);

}

StaticFileServing::~StaticFileServing ()
{
	_server->stop ();
}

void StaticFileServing::onServerStarted (quint16 port)
{
	Info(_log, "started on port %d name \"%s\"", port ,_server->getServerName().toStdString().c_str());

        const std::string mDNSDescr = ( _server->getServerName().toStdString()
                                        + "@" +
                                        QHostInfo::localHostName().toStdString()
                                        );

	BonjourServiceRegister *bonjourRegister_http = new BonjourServiceRegister();
	bonjourRegister_http->registerService(
		BonjourRecord(mDNSDescr.c_str(), "_http._tcp", QString()),
		port
		);
	Debug(_log, "Web Config mDNS responder started");
}

void StaticFileServing::onServerStopped () {
	Info(_log, "stopped %s", _server->getServerName().toStdString().c_str());
}

void StaticFileServing::onServerError (QString msg)
{
	Error(_log, "%s", msg.toStdString().c_str());
}

static inline void printErrorToReply (QtHttpReply * reply, QString errorMessage)
{
	reply->addHeader ("Content-Type", QByteArrayLiteral ("text/plain"));
	reply->appendRawData (errorMessage.toLocal8Bit ());
}

void StaticFileServing::onRequestNeedsReply (QtHttpRequest * request, QtHttpReply * reply)
{
	QString command = request->getCommand ();
	if (command == QStringLiteral ("GET"))
	{
		QString path = request->getUrl ().path ();
		QStringList uri_parts = path.split('/', QString::SkipEmptyParts);

		// special uri handling for server commands
		if ( ! uri_parts.empty() && uri_parts.at(0) == "cgi"  )
		{
			uri_parts.removeAt(0);
			try
			{
				_cgi.exec(uri_parts, request, reply);
			}
			catch(...)
			{
				printErrorToReply (reply, "script failed (" % path % ")");
			}
			return;
		}
		Q_INIT_RESOURCE(WebConfig);

		QFileInfo info(_baseUrl % "/" % path);
		if ( path == "/" || path.isEmpty() || ! info.exists() )
		{
			path = "index.html";
		}
		else if (info.isDir() && path.endsWith("/") )
		{
			path += "index.html";
		}
		else if (info.isDir() && ! path.endsWith("/") )
		{
			path += "/index.html";
		}

		// get static files
		QFile file(_baseUrl % "/" % path);
		if (file.exists())
		{
			QMimeType mime = _mimeDb->mimeTypeForFile (file.fileName ());
			if (file.open (QFile::ReadOnly)) {
				QByteArray data = file.readAll ();
				reply->addHeader ("Content-Type", mime.name ().toLocal8Bit ());
				reply->appendRawData (data);
				file.close ();
			}
			else
			{
				printErrorToReply (reply, "Requested file " % path % " couldn't be open for reading !");
			}
		}
		else
		{
			printErrorToReply (reply, "Requested file " % path % " couldn't be found !");
		}
	}
	else
	{
		printErrorToReply (reply, "Unhandled HTTP/1.1 method " % command % " on static file server !");
	}
}

<|endoftext|>


<commit_before>#include <lightex/html_converter/html_visitor.h>

#include <cctype>
#include <list>
#include <iomanip>
#include <iostream>
#include <sstream>

namespace lightex {
namespace html_converter {
namespace {

std::string FormatText(const std::string& unformatted) {
  std::ostringstream buffer;

  bool previous_is_space = false;
  for (char c : unformatted) {
    if (std::isspace(c)) {
      if (!previous_is_space) {
        buffer << ' ';
      }
      previous_is_space = true;
      continue;
    }

    buffer << c;
    previous_is_space = false;
  }

  return buffer.str();
}

std::string EscapeStringForHtml(const std::string& unescaped) {
  std::ostringstream buffer;

  for (char c : unescaped) {
    switch (c) {
      case '&':
        buffer << "&";
        break;

      case '\"':
        buffer << """;
        break;

      case '\'':
        buffer << "'";
        break;

      case '<':
        buffer << "<";
        break;

      case '>':
        buffer << ">";
        break;

      default:
        buffer << c;
    }
  }

  return buffer.str();
}

std::string EscapeStringForJs(const std::string& unescaped) {
  std::ostringstream buffer;

  for (char c : unescaped) {
    if (c == '"' || c == '\\' || ('\x00' <= c && c <= '\x1f')) {
      buffer << "\\u" << std::hex << std::setw(4) << std::setfill('0') << int{c};
    } else {
      buffer << c;
    }
  }

  return buffer.str();
}

std::string RenderMathFormula(const std::string& math_text, bool is_inlined, int* math_text_span_num) {
  std::string span_id = "mathTextSpan" + std::to_string(++(*math_text_span_num));
  std::string display_mode = is_inlined ? "false" : "true";

  std::ostringstream buffer;
  buffer << "<span id=\"" << span_id << "\">";

  buffer << "";

  return buffer.str();
}

template <typename MacroDefinition>
const MacroDefinition* GetMacroDefinition(const std::list<MacroDefinition>& macro_definitions,
                                          const std::string& name) {
  for (auto it = macro_definitions.rbegin(); it != macro_definitions.rend(); ++it) {
    if (it->name == name) {
      return &(*it);
    }
  }

  return nullptr;
}
}  // namespace

Result Result::Failure(const std::string& error_message) {
  return {false, "", "", error_message, false};
}

Result Result::Success(const std::string& escaped, const std::string& unescaped, bool breaks_paragraph) {
  return {true, escaped, unescaped, "", breaks_paragraph};
}

Result HtmlVisitor::operator()(const ast::Program& program) {
  return JoinNodeResults(program.nodes);
}

Result HtmlVisitor::operator()(const ast::PlainText& plain_text) {
  return Result::Success(EscapeStringForHtml(FormatText(plain_text.text)), plain_text.text);
}

Result HtmlVisitor::operator()(const ast::Paragraph& paragraph) {
  Result result = JoinNodeResults(paragraph.nodes);
  if (!result.is_successful) {
    return result;
  }

  if (active_environment_definitions_num_ == 0 && !result.breaks_paragraph) {
    result.escaped = "
" + result.escaped + "
";
    result.unescaped = "
" + result.unescaped + "
";
  }

  return result;
}

Result HtmlVisitor::operator()(const ast::ParagraphBreaker& paragraph_breaker) {
  return Result::Success("", "");
}

Result HtmlVisitor::operator()(const ast::Argument& argument) {
  return JoinNodeResults(argument.nodes);
}

Result HtmlVisitor::operator()(const ast::ArgumentRef& argument_ref) {
  const Result* argument = GetArgumentByReference(argument_ref.argument_id - 1, false);
  if (!argument) {
    return Result::Failure("Invalid argument reference.");
  }

  return *argument;
}

Result HtmlVisitor::operator()(const ast::OuterArgumentRef& outer_argument_ref) {
  const Result* argument = GetArgumentByReference(outer_argument_ref.argument_id - 1, true);
  if (!argument) {
    return Result::Failure("Invalid outer argument reference.");
  }

  return *argument;
}

Result HtmlVisitor::operator()(const ast::InlinedMathText& math_text) {
  std::string render_result = RenderMathFormula(math_text.text, true, &math_text_span_num_);
  return Result::Success(render_result, render_result);
}

Result HtmlVisitor::operator()(const ast::MathText& math_text) {
  std::string render_result = RenderMathFormula(math_text.text, false, &math_text_span_num_);
  return Result::Success(render_result, render_result);
}

Result HtmlVisitor::operator()(const ast::CommandMacro& command_macro) {
  if (command_macro.arguments_num.get_value_or(0) < command_macro.default_arguments.size()) {
    return Result::Failure("Invalid number of arguments for command macro " + command_macro.name + ".");
  }

  defined_command_macros_.push_back(command_macro);
  return Result::Success("", "");
}

Result HtmlVisitor::operator()(const ast::EnvironmentMacro& environment_macro) {
  if (environment_macro.arguments_num.get_value_or(0) < environment_macro.default_arguments.size()) {
    return Result::Failure("Invalid number of arguments for environment macro " + environment_macro.name + ".");
  }

  defined_environment_macros_.push_back(environment_macro);
  return Result::Success("", "");
}

Result HtmlVisitor::operator()(const ast::Command& command) {
  const ast::CommandMacro* command_macro_ptr = GetDefinedCommandMacro(command.name);
  if (!command_macro_ptr) {
    return Result::Failure("Command macro " + command.name + " is not defined yet.");
  }

  std::vector<Result> args;
  Result intermediate_result = PrepareMacroArguments(command, *command_macro_ptr, &args);
  if (!intermediate_result.is_successful) {
    return intermediate_result;
  }

  arguments_stack_.push_back(std::move(args));
  intermediate_result = (*this)(command_macro_ptr->body);
  arguments_stack_.pop_back();

  return intermediate_result;
}

Result HtmlVisitor::operator()(const ast::UnescapedCommand& unescaped_command) {
  Result result = (*this)(unescaped_command.body);

  if (result.is_successful) {
    result.escaped = result.unescaped;
  }

  return result;
}

Result HtmlVisitor::operator()(const ast::NparagraphCommand& nparagraph_command) {
  Result result = (*this)(nparagraph_command.body);

  result.breaks_paragraph = true;
  return result;
}

Result HtmlVisitor::operator()(const ast::Environment& environment) {
  if (environment.name != environment.end_name) {
    return Result::Failure("Environment name doesn't match the end name: " + environment.name + " != " +
                           environment.end_name);
  }

  const ast::EnvironmentMacro* environment_macro_ptr = GetDefinedEnvironmentMacro(environment.name);
  if (!environment_macro_ptr) {
    return Result::Failure("Environment macro " + environment.name + " is not defined yet.");
  }

  std::vector<Result> args;
  Result intermediate_result = PrepareMacroArguments(environment, *environment_macro_ptr, &args);
  if (!intermediate_result.is_successful) {
    return intermediate_result;
  }

  std::size_t cached_defined_command_macros_num = defined_command_macros_.size();

  const auto rollback = [&]() {
    while (cached_defined_command_macros_num < defined_command_macros_.size()) {
      defined_command_macros_.pop_back();
    }

    arguments_stack_.pop_back();
  };

  arguments_stack_.push_back(std::move(args));

  std::string escaped;
  std::string unescaped;
  bool breaks_paragraph;

  active_environment_definitions_num_ += 1;
  intermediate_result = (*this)(environment_macro_ptr->pre_program);
  active_environment_definitions_num_ -= 1;
  if (intermediate_result.is_successful) {
    escaped += intermediate_result.escaped;
    unescaped += intermediate_result.unescaped;
  } else {
    rollback();
    return intermediate_result;
  }

  intermediate_result = (*this)(environment.program);
  if (intermediate_result.is_successful) {
    escaped += intermediate_result.escaped;
    unescaped += intermediate_result.unescaped;
    breaks_paragraph = intermediate_result.breaks_paragraph;
  } else {
    rollback();
    return intermediate_result;
  }

  active_environment_definitions_num_ += 1;
  intermediate_result = (*this)(environment_macro_ptr->post_program);
  active_environment_definitions_num_ -= 1;
  if (intermediate_result.is_successful) {
    escaped += intermediate_result.escaped;
    unescaped += intermediate_result.unescaped;
  } else {
    rollback();
    return intermediate_result;
  }

  rollback();
  return Result::Success(escaped, unescaped, breaks_paragraph);
}

Result HtmlVisitor::operator()(const ast::VerbatimEnvironment& verbatim_environment) {
  std::string html_text = "
" + verbatim_environment.content + "
";

  return Result::Success(html_text, html_text);
}

template <typename Node>
Result HtmlVisitor::JoinNodeResults(const std::list<Node>& nodes) {
  std::ostringstream escaped;
  std::ostringstream unescaped;

  bool breaks_paragraph = false;
  for (const auto& node : nodes) {
    Result child_result = boost::apply_visitor(*this, node);
    if (!child_result.is_successful) {
      return child_result;
    }

    escaped << child_result.escaped;
    unescaped << child_result.unescaped;
    breaks_paragraph |= child_result.breaks_paragraph;
  }

  return Result::Success(escaped.str(), unescaped.str(), breaks_paragraph);
};

template <typename Macro, typename MacroDefinition>
Result HtmlVisitor::PrepareMacroArguments(const Macro& macro,
                                          const MacroDefinition& macro_definition,
                                          std::vector<Result>* output_args) {
  if (!output_args) {
    return Result::Failure("No place for preparing macro arguments is provided.");
  }

  int default_args_num = macro_definition.default_arguments.size();
  int redefined_default_args_num = macro.default_arguments.size();
  if (redefined_default_args_num > default_args_num) {
    return Result::Failure("Macro " + macro.name + " has more default arguments than it's been defined. " +
                           "Expected " + std::to_string(default_args_num) + ", got " +
                           std::to_string(redefined_default_args_num) + ".");
  }

  int args_num = macro_definition.default_arguments.size() + macro.arguments.size();
  int expected_args_num = macro_definition.arguments_num.get_value_or(0);
  if (args_num != expected_args_num) {
    return Result::Failure("Macro " + macro.name + " has invalid number of arguments. " + "Expected " +
                           std::to_string(expected_args_num) + ", got " + std::to_string(args_num) + ".");
  }

  const auto get_argument = [&](int i) {
    const auto list_at = [](const std::list<ast::Argument>& arguments, int index) {
      return *std::next(arguments.begin(), index);
    };

    if (i < redefined_default_args_num) {
      return list_at(macro.default_arguments, i);
    } else if (i < default_args_num) {
      return list_at(macro_definition.default_arguments, i);
    } else {
      return list_at(macro.arguments, i - default_args_num);
    }
  };

  output_args->resize(args_num);
  for (int i = 0; i < args_num; ++i) {
    Result child_result = (*this)(get_argument(i));
    if (!child_result.is_successful) {
      return child_result;
    }

    output_args->at(i) = std::move(child_result);
  }

  return Result::Success("", "");
}

const ast::CommandMacro* HtmlVisitor::GetDefinedCommandMacro(const std::string& name) const {
  return GetMacroDefinition(defined_command_macros_, name);
}

const ast::EnvironmentMacro* HtmlVisitor::GetDefinedEnvironmentMacro(const std::string& name) const {
  return GetMacroDefinition(defined_environment_macros_, name);
}

const Result* HtmlVisitor::GetArgumentByReference(int index, bool is_outer) const {
  int arguments_stack_index = static_cast<int>(arguments_stack_.size()) - (is_outer ? 2 : 1);
  if (index < 0 || arguments_stack_index < 0) {
    return nullptr;
  }
  const auto& arguments = arguments_stack_[arguments_stack_index];

  if (arguments.size() <= index) {
    return nullptr;
  }

  return &arguments[index];
}
}  // namespace html_converter
}  // namespace lightex
<commit_msg>Do not create a paragraph for an empty text.<commit_after>#include <lightex/html_converter/html_visitor.h>

#include <cctype>
#include <list>
#include <iomanip>
#include <iostream>
#include <sstream>

namespace lightex {
namespace html_converter {
namespace {

std::string FormatText(const std::string& unformatted) {
  std::ostringstream buffer;

  bool previous_is_space = false;
  for (char c : unformatted) {
    if (std::isspace(c)) {
      if (!previous_is_space) {
        buffer << ' ';
      }
      previous_is_space = true;
      continue;
    }

    buffer << c;
    previous_is_space = false;
  }

  return buffer.str();
}

std::string EscapeStringForHtml(const std::string& unescaped) {
  std::ostringstream buffer;

  for (char c : unescaped) {
    switch (c) {
      case '&':
        buffer << "&";
        break;

      case '\"':
        buffer << """;
        break;

      case '\'':
        buffer << "'";
        break;

      case '<':
        buffer << "<";
        break;

      case '>':
        buffer << ">";
        break;

      default:
        buffer << c;
    }
  }

  return buffer.str();
}

std::string EscapeStringForJs(const std::string& unescaped) {
  std::ostringstream buffer;

  for (char c : unescaped) {
    if (c == '"' || c == '\\' || ('\x00' <= c && c <= '\x1f')) {
      buffer << "\\u" << std::hex << std::setw(4) << std::setfill('0') << int{c};
    } else {
      buffer << c;
    }
  }

  return buffer.str();
}

std::string RenderMathFormula(const std::string& math_text, bool is_inlined, int* math_text_span_num) {
  std::string span_id = "mathTextSpan" + std::to_string(++(*math_text_span_num));
  std::string display_mode = is_inlined ? "false" : "true";

  std::ostringstream buffer;
  buffer << "<span id=\"" << span_id << "\">";

  buffer << "";

  return buffer.str();
}

template <typename MacroDefinition>
const MacroDefinition* GetMacroDefinition(const std::list<MacroDefinition>& macro_definitions,
                                          const std::string& name) {
  for (auto it = macro_definitions.rbegin(); it != macro_definitions.rend(); ++it) {
    if (it->name == name) {
      return &(*it);
    }
  }

  return nullptr;
}
}  // namespace

Result Result::Failure(const std::string& error_message) {
  return {false, "", "", error_message, false};
}

Result Result::Success(const std::string& escaped, const std::string& unescaped, bool breaks_paragraph) {
  return {true, escaped, unescaped, "", breaks_paragraph};
}

Result HtmlVisitor::operator()(const ast::Program& program) {
  return JoinNodeResults(program.nodes);
}

Result HtmlVisitor::operator()(const ast::PlainText& plain_text) {
  return Result::Success(EscapeStringForHtml(FormatText(plain_text.text)), plain_text.text);
}

Result HtmlVisitor::operator()(const ast::Paragraph& paragraph) {
  Result result = JoinNodeResults(paragraph.nodes);
  if (!result.is_successful) {
    return result;
  }

  result.escaped = FormatText(result.escaped);
  if (result.escaped.empty() || result.escaped == " ") {
    result.escaped = "";
    return result;
  }

  if (active_environment_definitions_num_ == 0 && !result.breaks_paragraph) {
    result.escaped = "
" + result.escaped + "
";
    result.unescaped = "
" + result.unescaped + "
";
  }

  return result;
}

Result HtmlVisitor::operator()(const ast::ParagraphBreaker& paragraph_breaker) {
  return Result::Success("", "");
}

Result HtmlVisitor::operator()(const ast::Argument& argument) {
  return JoinNodeResults(argument.nodes);
}

Result HtmlVisitor::operator()(const ast::ArgumentRef& argument_ref) {
  const Result* argument = GetArgumentByReference(argument_ref.argument_id - 1, false);
  if (!argument) {
    return Result::Failure("Invalid argument reference.");
  }

  return *argument;
}

Result HtmlVisitor::operator()(const ast::OuterArgumentRef& outer_argument_ref) {
  const Result* argument = GetArgumentByReference(outer_argument_ref.argument_id - 1, true);
  if (!argument) {
    return Result::Failure("Invalid outer argument reference.");
  }

  return *argument;
}

Result HtmlVisitor::operator()(const ast::InlinedMathText& math_text) {
  std::string render_result = RenderMathFormula(math_text.text, true, &math_text_span_num_);
  return Result::Success(render_result, render_result);
}

Result HtmlVisitor::operator()(const ast::MathText& math_text) {
  std::string render_result = RenderMathFormula(math_text.text, false, &math_text_span_num_);
  return Result::Success(render_result, render_result);
}

Result HtmlVisitor::operator()(const ast::CommandMacro& command_macro) {
  if (command_macro.arguments_num.get_value_or(0) < command_macro.default_arguments.size()) {
    return Result::Failure("Invalid number of arguments for command macro " + command_macro.name + ".");
  }

  defined_command_macros_.push_back(command_macro);
  return Result::Success("", "");
}

Result HtmlVisitor::operator()(const ast::EnvironmentMacro& environment_macro) {
  if (environment_macro.arguments_num.get_value_or(0) < environment_macro.default_arguments.size()) {
    return Result::Failure("Invalid number of arguments for environment macro " + environment_macro.name + ".");
  }

  defined_environment_macros_.push_back(environment_macro);
  return Result::Success("", "");
}

Result HtmlVisitor::operator()(const ast::Command& command) {
  const ast::CommandMacro* command_macro_ptr = GetDefinedCommandMacro(command.name);
  if (!command_macro_ptr) {
    return Result::Failure("Command macro " + command.name + " is not defined yet.");
  }

  std::vector<Result> args;
  Result intermediate_result = PrepareMacroArguments(command, *command_macro_ptr, &args);
  if (!intermediate_result.is_successful) {
    return intermediate_result;
  }

  arguments_stack_.push_back(std::move(args));
  intermediate_result = (*this)(command_macro_ptr->body);
  arguments_stack_.pop_back();

  return intermediate_result;
}

Result HtmlVisitor::operator()(const ast::UnescapedCommand& unescaped_command) {
  Result result = (*this)(unescaped_command.body);

  if (result.is_successful) {
    result.escaped = result.unescaped;
  }

  return result;
}

Result HtmlVisitor::operator()(const ast::NparagraphCommand& nparagraph_command) {
  Result result = (*this)(nparagraph_command.body);

  result.breaks_paragraph = true;
  return result;
}

Result HtmlVisitor::operator()(const ast::Environment& environment) {
  if (environment.name != environment.end_name) {
    return Result::Failure("Environment name doesn't match the end name: " + environment.name + " != " +
                           environment.end_name);
  }

  const ast::EnvironmentMacro* environment_macro_ptr = GetDefinedEnvironmentMacro(environment.name);
  if (!environment_macro_ptr) {
    return Result::Failure("Environment macro " + environment.name + " is not defined yet.");
  }

  std::vector<Result> args;
  Result intermediate_result = PrepareMacroArguments(environment, *environment_macro_ptr, &args);
  if (!intermediate_result.is_successful) {
    return intermediate_result;
  }

  std::size_t cached_defined_command_macros_num = defined_command_macros_.size();

  const auto rollback = [&]() {
    while (cached_defined_command_macros_num < defined_command_macros_.size()) {
      defined_command_macros_.pop_back();
    }

    arguments_stack_.pop_back();
  };

  arguments_stack_.push_back(std::move(args));

  std::string escaped;
  std::string unescaped;
  bool breaks_paragraph;

  active_environment_definitions_num_ += 1;
  intermediate_result = (*this)(environment_macro_ptr->pre_program);
  active_environment_definitions_num_ -= 1;
  if (intermediate_result.is_successful) {
    escaped += intermediate_result.escaped;
    unescaped += intermediate_result.unescaped;
  } else {
    rollback();
    return intermediate_result;
  }

  intermediate_result = (*this)(environment.program);
  if (intermediate_result.is_successful) {
    escaped += intermediate_result.escaped;
    unescaped += intermediate_result.unescaped;
    breaks_paragraph = intermediate_result.breaks_paragraph;
  } else {
    rollback();
    return intermediate_result;
  }

  active_environment_definitions_num_ += 1;
  intermediate_result = (*this)(environment_macro_ptr->post_program);
  active_environment_definitions_num_ -= 1;
  if (intermediate_result.is_successful) {
    escaped += intermediate_result.escaped;
    unescaped += intermediate_result.unescaped;
  } else {
    rollback();
    return intermediate_result;
  }

  rollback();
  return Result::Success(escaped, unescaped, breaks_paragraph);
}

Result HtmlVisitor::operator()(const ast::VerbatimEnvironment& verbatim_environment) {
  std::string html_text = "
" + verbatim_environment.content + "
";

  return Result::Success(html_text, html_text);
}

template <typename Node>
Result HtmlVisitor::JoinNodeResults(const std::list<Node>& nodes) {
  std::ostringstream escaped;
  std::ostringstream unescaped;

  bool breaks_paragraph = false;
  for (const auto& node : nodes) {
    Result child_result = boost::apply_visitor(*this, node);
    if (!child_result.is_successful) {
      return child_result;
    }

    escaped << child_result.escaped;
    unescaped << child_result.unescaped;
    breaks_paragraph |= child_result.breaks_paragraph;
  }

  return Result::Success(escaped.str(), unescaped.str(), breaks_paragraph);
};

template <typename Macro, typename MacroDefinition>
Result HtmlVisitor::PrepareMacroArguments(const Macro& macro,
                                          const MacroDefinition& macro_definition,
                                          std::vector<Result>* output_args) {
  if (!output_args) {
    return Result::Failure("No place for preparing macro arguments is provided.");
  }

  int default_args_num = macro_definition.default_arguments.size();
  int redefined_default_args_num = macro.default_arguments.size();
  if (redefined_default_args_num > default_args_num) {
    return Result::Failure("Macro " + macro.name + " has more default arguments than it's been defined. " +
                           "Expected " + std::to_string(default_args_num) + ", got " +
                           std::to_string(redefined_default_args_num) + ".");
  }

  int args_num = macro_definition.default_arguments.size() + macro.arguments.size();
  int expected_args_num = macro_definition.arguments_num.get_value_or(0);
  if (args_num != expected_args_num) {
    return Result::Failure("Macro " + macro.name + " has invalid number of arguments. " + "Expected " +
                           std::to_string(expected_args_num) + ", got " + std::to_string(args_num) + ".");
  }

  const auto get_argument = [&](int i) {
    const auto list_at = [](const std::list<ast::Argument>& arguments, int index) {
      return *std::next(arguments.begin(), index);
    };

    if (i < redefined_default_args_num) {
      return list_at(macro.default_arguments, i);
    } else if (i < default_args_num) {
      return list_at(macro_definition.default_arguments, i);
    } else {
      return list_at(macro.arguments, i - default_args_num);
    }
  };

  output_args->resize(args_num);
  for (int i = 0; i < args_num; ++i) {
    Result child_result = (*this)(get_argument(i));
    if (!child_result.is_successful) {
      return child_result;
    }

    output_args->at(i) = std::move(child_result);
  }

  return Result::Success("", "");
}

const ast::CommandMacro* HtmlVisitor::GetDefinedCommandMacro(const std::string& name) const {
  return GetMacroDefinition(defined_command_macros_, name);
}

const ast::EnvironmentMacro* HtmlVisitor::GetDefinedEnvironmentMacro(const std::string& name) const {
  return GetMacroDefinition(defined_environment_macros_, name);
}

const Result* HtmlVisitor::GetArgumentByReference(int index, bool is_outer) const {
  int arguments_stack_index = static_cast<int>(arguments_stack_.size()) - (is_outer ? 2 : 1);
  if (index < 0 || arguments_stack_index < 0) {
    return nullptr;
  }
  const auto& arguments = arguments_stack_[arguments_stack_index];

  if (arguments.size() <= index) {
    return nullptr;
  }

  return &arguments[index];
}
}  // namespace html_converter
}  // namespace lightex
<|endoftext|>


<commit_before>#include "condor_common.h"

#include <algorithm>
#include "ranger.h"

///// ranger insert/erase/find implementation /////

ranger::iterator ranger::insert(ranger::range r)
{
    // lower_bound here will coalesce an adjacent disjoint range;
    // can use upper_bound instead to avoid this and leave them fractured
    iterator it_start = lower_bound(r._start);
    iterator it = it_start;
    while (it != end() && it->_start <= r._end)  // '<' for no coalesce
        ++it;

    iterator it_end = it;
    if (it_start == it_end)
        return forest.insert(it_end, r);

    if (it_start->contains(r))
        return it_start;

    iterator it_back = --it;
    range rr_new = { std::min(it_start->_start, r._start),
                     std::max(it_back->_end, r._end) };

    // avoid erase+insert if only expanding a range
    if (it_start == it_back) {
        it_start->_start = rr_new._start;
        it_start->_end = rr_new._end;
        return it_start;
    }

    iterator hint = forest.erase(it_start, it_end);

    return forest.insert(hint, rr_new);
}

static
bool shrink_single_edge(const ranger::range &rm, const ranger::range &at)
{
    bool erase_start = rm._start <= at._start;
    bool erase_back  = at._end   <= rm._end;
    if (erase_start ^ erase_back) {
        if (erase_start)
            at._start = rm._end;
        else
            at._end = rm._start;
        return true;
    }
    return false;
}

ranger::iterator ranger::erase(ranger::range r)
{
    iterator it_start = upper_bound(r._start);
    iterator it = it_start;
    while (it != end() && it->_start < r._end)
        ++it;

    iterator it_end = it;
    if (it_start == it_end)
        return it_start;

    iterator it_back = --it;

    // avoid erase+insert if only shrinking an edge of a range
    if (it_start == it_back && shrink_single_edge(r, *it_start))
        return it_start;

    range rr_start = *it_start;
    range rr_back  = *it_back;

    iterator hint = forest.erase(it_start, it_end);
    if (rr_start._start < r._start) {
        hint = forest.insert(hint, range(rr_start._start, r._start));
        ++hint;  // advance to erasure gap
    }

    if (r._end < rr_back._end)
        hint = forest.insert(hint, range(r._end, rr_back._end));

    return hint;
}

std::pair<ranger::iterator, bool>
ranger::find(value_type x) const
{
    iterator it = upper_bound(x);
    return {it, it != end() && it->_start <= x};
}

bool ranger::contains(value_type x) const
{
    return find(x).second;
}

ranger::ranger(const std::initializer_list<ranger::range> &il)
{
    for (const range &rr : il)
        insert(rr);
}

ranger::iterator ranger::lower_bound(value_type x) const
{
    return forest.lower_bound(x);
}

ranger::iterator ranger::upper_bound(value_type x) const
{
    return forest.upper_bound(x);
}

///// ranger::elements::iterator implementation /////

/* This allows:
 *
 *      for (int i : myranger.get_elements())
 *          process_int(i);
 *
 *
 * Instead of the still-straightforward:
 *
 *      for (auto &rr : myranger)
 *          for (int i : rr)
 *              process_int(i);
 */

void ranger::elements::iterator::mk_valid()
{
    if (!rit_valid) {
        rit = sit->begin();
        rit_valid = true;
    }
}

ranger::value_type ranger::elements::iterator::operator*()
{
    mk_valid();
    return *rit;
}

ranger::elements::iterator &ranger::elements::iterator::operator++()
{
    mk_valid();
    if (++rit == sit->end()) {
        ++sit;
        rit_valid = false;
    }
    return *this;
}

ranger::elements::iterator &ranger::elements::iterator::operator--()
{
    mk_valid();
    if (rit == sit->begin()) {
        --sit;
        rit = sit->end();
        --rit;
    }
    return *this;
}

bool ranger::elements::iterator::operator==(iterator &it)
{
    if (sit != it.sit)
        return false;
    if (!rit_valid && !it.rit_valid)
        return true;
    // at this point neither sit nor it.sit points to the end() of its set,
    // thus it's OK to call mk_valid() on each, which may dereference *sit
    mk_valid();
    it.mk_valid();
    return rit == it.rit;
}

bool ranger::elements::iterator::operator!=(iterator &it)
{
    return !(*this == it);
}

///// ranger persist/load implementation /////

#include <stdlib.h> // strtol
#include <stdio.h>  // sprintf
#include <string>

/*  persist / load ranger objects
 *
 *  The serialized format is one or more sub-ranges, separated by semicolons,
 *  where each sub-range is either N-M (for inclusive N..M) or N for a single
 *  integer.  Eg, "2", "5-10", "4;7;10-20;44;50-60"
 */

static
void persist_range_single(std::string &s, const ranger::range &rr)
{
    char buf[64];
    int n;
    if (rr._start == rr._end - 1)
        n = sprintf(buf, "%d;", rr._start);
    else
        n = sprintf(buf, "%d-%d;", rr._start, rr._end - 1);
    s.append(buf, n);
}

void persist(std::string &s, const ranger &r)
{
    s.clear();
    if (r.empty())
        return;

    for (auto &rr : r)
        persist_range_single(s, rr);

    s.erase(s.size() - 1);
}

void persist_range(std::string &s, const ranger &r, const ranger::range &rr)
{
    s.clear();
    if (r.empty())
        return;

    auto rit = r.find(rr._start).first;
    for (; rit != r.end() && rit->_start < rr._end; ++rit) {
        ranger::range rr_new = { std::max(rit->_start, rr._start),
                                 std::min(rit->_end,   rr._end) };
        persist_range_single(s, rr_new);
    }

    s.erase(s.size() - 1);
}

void persist_slice(std::string &s, const ranger &r, int start, int back)
{
    persist_range(s, r, {start, back+1});
}

// return 0 on success, (-1 - (position in string)) on parse failure
int load(ranger &r, const char *s)
{
    const char *sstart = s;
    while (*s) {
        char *sp;
        int start = strtol(s, &sp, 10);
        int back;
        if (s == sp)
            // no int parsed is ok as long as we're at the end
            return *s ? -1 - int(s - sstart) : 0;
        s = sp;
        if (*sp == '-') {
            s++;
            back = strtol(s, &sp, 10);
            if (s == sp)
                // a number should have followed '-'
                return -1 - int(s - sstart);
            s = sp;
        } else {
            back = start;
        }
        if (*s == ';')
            s++;
        else if (*s)
            // expected either ';' or end of string
            return -1 - int(s - sstart);
        r.insert({start, back + 1});
    }
    return 0;
}

<commit_msg>specialize lower/upper_bound on forest type<commit_after>#include "condor_common.h"

#include <algorithm>
#include "ranger.h"

///// ranger insert/erase/find implementation /////

ranger::iterator ranger::insert(ranger::range r)
{
    // lower_bound here will coalesce an adjacent disjoint range;
    // can use upper_bound instead to avoid this and leave them fractured
    iterator it_start = lower_bound(r._start);
    iterator it = it_start;
    while (it != end() && it->_start <= r._end)  // '<' for no coalesce
        ++it;

    iterator it_end = it;
    if (it_start == it_end)
        return forest.insert(it_end, r);

    if (it_start->contains(r))
        return it_start;

    iterator it_back = --it;
    range rr_new = { std::min(it_start->_start, r._start),
                     std::max(it_back->_end, r._end) };

    // avoid erase+insert if only expanding a range
    if (it_start == it_back) {
        it_start->_start = rr_new._start;
        it_start->_end = rr_new._end;
        return it_start;
    }

    iterator hint = forest.erase(it_start, it_end);

    return forest.insert(hint, rr_new);
}

static
bool shrink_single_edge(const ranger::range &rm, const ranger::range &at)
{
    bool erase_start = rm._start <= at._start;
    bool erase_back  = at._end   <= rm._end;
    if (erase_start ^ erase_back) {
        if (erase_start)
            at._start = rm._end;
        else
            at._end = rm._start;
        return true;
    }
    return false;
}

ranger::iterator ranger::erase(ranger::range r)
{
    iterator it_start = upper_bound(r._start);
    iterator it = it_start;
    while (it != end() && it->_start < r._end)
        ++it;

    iterator it_end = it;
    if (it_start == it_end)
        return it_start;

    iterator it_back = --it;

    // avoid erase+insert if only shrinking an edge of a range
    if (it_start == it_back && shrink_single_edge(r, *it_start))
        return it_start;

    range rr_start = *it_start;
    range rr_back  = *it_back;

    iterator hint = forest.erase(it_start, it_end);
    if (rr_start._start < r._start) {
        hint = forest.insert(hint, range(rr_start._start, r._start));
        ++hint;  // advance to erasure gap
    }

    if (r._end < rr_back._end)
        hint = forest.insert(hint, range(r._end, rr_back._end));

    return hint;
}

std::pair<ranger::iterator, bool>
ranger::find(value_type x) const
{
    iterator it = upper_bound(x);
    return {it, it != end() && it->_start <= x};
}

bool ranger::contains(value_type x) const
{
    return find(x).second;
}

ranger::ranger(const std::initializer_list<ranger::range> &il)
{
    for (const range &rr : il)
        insert(rr);
}

// specialize lower_bound / upper_bound based on forest type
static inline std::set<ranger::range>::const_iterator
lower_bounder(const std::set<ranger::range> &f, ranger::range rr)
{
    return f.lower_bound(rr);
}

static inline std::set<ranger::range>::const_iterator
upper_bounder(const std::set<ranger::range> &f, ranger::range rr)
{
    return f.upper_bound(rr);
}

static inline std::vector<ranger::range>::const_iterator
lower_bounder(const std::vector<ranger::range> &f, ranger::range rr)
{
    return std::lower_bound(f.begin(), f.end(), rr);
}

static inline std::vector<ranger::range>::const_iterator
upper_bounder(const std::vector<ranger::range> &f, ranger::range rr)
{
    return std::upper_bound(f.begin(), f.end(), rr);
}

ranger::iterator ranger::lower_bound(value_type x) const
{
    return lower_bounder(forest, x);
}

ranger::iterator ranger::upper_bound(value_type x) const
{
    return upper_bounder(forest, x);
}

///// ranger::elements::iterator implementation /////

/* This allows:
 *
 *      for (int i : myranger.get_elements())
 *          process_int(i);
 *
 *
 * Instead of the still-straightforward:
 *
 *      for (auto &rr : myranger)
 *          for (int i : rr)
 *              process_int(i);
 */

void ranger::elements::iterator::mk_valid()
{
    if (!rit_valid) {
        rit = sit->begin();
        rit_valid = true;
    }
}

ranger::value_type ranger::elements::iterator::operator*()
{
    mk_valid();
    return *rit;
}

ranger::elements::iterator &ranger::elements::iterator::operator++()
{
    mk_valid();
    if (++rit == sit->end()) {
        ++sit;
        rit_valid = false;
    }
    return *this;
}

ranger::elements::iterator &ranger::elements::iterator::operator--()
{
    mk_valid();
    if (rit == sit->begin()) {
        --sit;
        rit = sit->end();
        --rit;
    }
    return *this;
}

bool ranger::elements::iterator::operator==(iterator &it)
{
    if (sit != it.sit)
        return false;
    if (!rit_valid && !it.rit_valid)
        return true;
    // at this point neither sit nor it.sit points to the end() of its set,
    // thus it's OK to call mk_valid() on each, which may dereference *sit
    mk_valid();
    it.mk_valid();
    return rit == it.rit;
}

bool ranger::elements::iterator::operator!=(iterator &it)
{
    return !(*this == it);
}

///// ranger persist/load implementation /////

#include <stdlib.h> // strtol
#include <stdio.h>  // sprintf
#include <string>

/*  persist / load ranger objects
 *
 *  The serialized format is one or more sub-ranges, separated by semicolons,
 *  where each sub-range is either N-M (for inclusive N..M) or N for a single
 *  integer.  Eg, "2", "5-10", "4;7;10-20;44;50-60"
 */

static
void persist_range_single(std::string &s, const ranger::range &rr)
{
    char buf[64];
    int n;
    if (rr._start == rr._end - 1)
        n = sprintf(buf, "%d;", rr._start);
    else
        n = sprintf(buf, "%d-%d;", rr._start, rr._end - 1);
    s.append(buf, n);
}

void persist(std::string &s, const ranger &r)
{
    s.clear();
    if (r.empty())
        return;

    for (auto &rr : r)
        persist_range_single(s, rr);

    s.erase(s.size() - 1);
}

void persist_range(std::string &s, const ranger &r, const ranger::range &rr)
{
    s.clear();
    if (r.empty())
        return;

    auto rit = r.find(rr._start).first;
    for (; rit != r.end() && rit->_start < rr._end; ++rit) {
        ranger::range rr_new = { std::max(rit->_start, rr._start),
                                 std::min(rit->_end,   rr._end) };
        persist_range_single(s, rr_new);
    }

    s.erase(s.size() - 1);
}

void persist_slice(std::string &s, const ranger &r, int start, int back)
{
    persist_range(s, r, {start, back+1});
}

// return 0 on success, (-1 - (position in string)) on parse failure
int load(ranger &r, const char *s)
{
    const char *sstart = s;
    while (*s) {
        char *sp;
        int start = strtol(s, &sp, 10);
        int back;
        if (s == sp)
            // no int parsed is ok as long as we're at the end
            return *s ? -1 - int(s - sstart) : 0;
        s = sp;
        if (*sp == '-') {
            s++;
            back = strtol(s, &sp, 10);
            if (s == sp)
                // a number should have followed '-'
                return -1 - int(s - sstart);
            s = sp;
        } else {
            back = start;
        }
        if (*s == ';')
            s++;
        else if (*s)
            // expected either ';' or end of string
            return -1 - int(s - sstart);
        r.insert({start, back + 1});
    }
    return 0;
}

<|endoftext|>


<commit_before>/**
 * @file    TimerScheduler.cpp
 * @author  Longwei Lai<lailongwei@126.com>
 * @date    2013/12/01
 * @version 1.0
 *
 * @brief
 */

#include "llbc/common/Export.h"
#include "llbc/common/BeforeIncl.h"

#include "llbc/core/os/OS_Time.h"

#include "llbc/core/timer/BaseTimer.h"
#include "llbc/core/timer/TimerData.h"

#include "llbc/core/timer/TimerScheduler.h"

__LLBC_INTERNAL_NS_BEGIN

static LLBC_NS LLBC_TimerScheduler *__g_entryThreadTimerScheduler = NULL;

__LLBC_INTERNAL_NS_END

__LLBC_NS_BEGIN

LLBC_TimerScheduler::LLBC_TimerScheduler()
: _maxTimerId(0)
, _enabled(true)
, _destroyed(false)
{
}

LLBC_TimerScheduler::~LLBC_TimerScheduler()
{
    _destroyed = true;

    size_t size = _heap.GetSize();
    const _Heap::Container &elems = _heap.GetData();
    for (size_t i = 1; i <= size; i++)
    {
        LLBC_TimerData *data = const_cast<LLBC_TimerData *>(elems[i]);
        if (data->validate)
        {
            data->validate = false;
            data->timer->SetScheduling(false);

            data->timer->OnCancel();
        }
    }

    for (size_t i = 1; i < size; i++)
    {
        delete const_cast<LLBC_TimerData *>(elems[i]);
    }
}

void LLBC_TimerScheduler::CreateEntryThreadScheduler()
{
    LLBC_TimerScheduler *&scheduler =
        LLBC_INTERNAL_NS __g_entryThreadTimerScheduler;
    if (!scheduler)
    {
        scheduler = new LLBC_TimerScheduler;
    }
}

void LLBC_TimerScheduler::DestroyEntryThreadScheduler()
{
    LLBC_XDelete(LLBC_INTERNAL_NS __g_entryThreadTimerScheduler);
}

LLBC_TimerScheduler::_This *LLBC_TimerScheduler::GetEntryThreadScheduler()
{
    return LLBC_INTERNAL_NS __g_entryThreadTimerScheduler;
}

LLBC_TimerScheduler::_This *LLBC_TimerScheduler::GetCurrentThreadScheduler()
{
    __LLBC_LibTls *tls = __LLBC_GetLibTls();
    return reinterpret_cast<_This *>(tls->coreTls.timerScheduler);
}

void LLBC_TimerScheduler::Update()
{
    if (!_enabled)
        return;

    LLBC_TimerData *data;
    uint64 now = LLBC_GetMilliSeconds();

    while (_heap.FindTop(data) == LLBC_OK)
    {
        if (now < data->handle)
            break;

        _heap.DeleteTop();

        if (!data->validate)
        {
            _idxMap.erase(data->timerId);
            delete data;
            continue;
        }

        data->validate = false;
        data->timer->SetScheduling(false);

        bool reSchedule = false;
#if LLBC_CFG_CORE_TIMER_STRICT_SCHEDULE
        uint64 pseudoNow = now;
        while (pseudoNow >= data->handle)
#endif // LLBC_CFG_CORE_TIMER_STRICT_SCHEDULE
        {
            ++ data->repeatTimes;
            if (!(reSchedule = data->timer->OnTimeout()))
                break;

            if (data->timer->IsScheduling())
            {
                reSchedule = false;
                break;
            }

            if (data->period == 0)
                break;

#if LLBC_CFG_CORE_TIMER_STRICT_SCHEDULE
            if (UNLIKELY(pseudoNow < data->period))
                break;

            pseudoNow -= data->period;
#endif // LLBC_CFG_CORE_TIMER_STRICT_SCHEDULE
        }

        if (reSchedule)
        {
            uint64 delay = (data->period != 0) ? (now - data->handle) % data->period : 0;

            data->validate = true;
            data->timer->SetScheduling(true);
            data->handle = now + data->period - delay;

            _heap.Insert(data);
        }
        else
        {
            _idxMap.erase(data->timerId);
            delete data;
        }
    }
}

bool LLBC_TimerScheduler::IsEnabled() const
{
    return _enabled;
}

void LLBC_TimerScheduler::SetEnabled(bool enabled)
{
    _enabled = enabled;
}

bool LLBC_TimerScheduler::IsDstroyed() const
{
    return _destroyed;
}

int LLBC_TimerScheduler::Schedule(LLBC_BaseTimer *timer)
{
    LLBC_TimerData *data = new LLBC_TimerData;
    data->handle = LLBC_GetMilliSeconds() + timer->GetDueTime();
    data->timerId = ++ _maxTimerId;
    data->dueTime = timer->GetDueTime();
    data->period = timer->GetPeriod();
    data->repeatTimes = 0;
    data->timer = timer;
    data->validate = true;

    timer->SetTimerId(_maxTimerId);
    timer->SetScheduling(true);

    _heap.Insert(data);
    _idxMap.insert(std::make_pair(_maxTimerId, data));

    return LLBC_OK;
}

int LLBC_TimerScheduler::Cancel(LLBC_BaseTimer *timer)
{
    _IdxMap::iterator iter = _idxMap.find(timer->GetTimerId());
    if (iter == _idxMap.end())
    {
        LLBC_SetLastError(LLBC_ERROR_NOT_FOUND);
        return LLBC_FAILED;
    }

    LLBC_TimerData *data = iter->second;
    ASSERT(data->timer == timer &&
        "Timer manager internal error, LLBC_TimerData::timer != argument: timer!");

    _idxMap.erase(data->timerId);

    data->validate = false;
    data->timer->SetScheduling(false);
    data->timer->OnCancel();

    return LLBC_OK;
}

void LLBC_TimerScheduler::CancelAll()
{
    if (_destroyed)
        return;

    std::vector<LLBC_TimerId> timerIds;
    for (_IdxMap::iterator iter = _idxMap.begin();
         iter != _idxMap.end();
         iter++)
    {
        LLBC_TimerData *data = iter->second;
        if (data->validate)
            timerIds.push_back(data->timerId);
    }

    for (size_t i = 0; i < timerIds.size(); i++)
    {
        _IdxMap::iterator iter = _idxMap.find(timerIds[i]);
        if (iter != _idxMap.end())
        {
            LLBC_TimerData *data = iter->second;
            if (UNLIKELY(!data->validate))
                return;

            data->timer->Cancel();
        }
    }
}

__LLBC_NS_END

#include "llbc/common/AfterIncl.h"
<commit_msg>Fix TimerScheduler schedule in Strict-Mode error<commit_after>/**
 * @file    TimerScheduler.cpp
 * @author  Longwei Lai<lailongwei@126.com>
 * @date    2013/12/01
 * @version 1.0
 *
 * @brief
 */

#include "llbc/common/Export.h"
#include "llbc/common/BeforeIncl.h"

#include "llbc/core/os/OS_Time.h"

#include "llbc/core/timer/BaseTimer.h"
#include "llbc/core/timer/TimerData.h"

#include "llbc/core/timer/TimerScheduler.h"

__LLBC_INTERNAL_NS_BEGIN

static LLBC_NS LLBC_TimerScheduler *__g_entryThreadTimerScheduler = NULL;

__LLBC_INTERNAL_NS_END

__LLBC_NS_BEGIN

LLBC_TimerScheduler::LLBC_TimerScheduler()
: _maxTimerId(0)
, _enabled(true)
, _destroyed(false)
{
}

LLBC_TimerScheduler::~LLBC_TimerScheduler()
{
    _destroyed = true;

    size_t size = _heap.GetSize();
    const _Heap::Container &elems = _heap.GetData();
    for (size_t i = 1; i <= size; i++)
    {
        LLBC_TimerData *data = const_cast<LLBC_TimerData *>(elems[i]);
        if (data->validate)
        {
            data->validate = false;
            data->timer->SetScheduling(false);

            data->timer->OnCancel();
        }
    }

    for (size_t i = 1; i < size; i++)
    {
        delete const_cast<LLBC_TimerData *>(elems[i]);
    }
}

void LLBC_TimerScheduler::CreateEntryThreadScheduler()
{
    LLBC_TimerScheduler *&scheduler =
        LLBC_INTERNAL_NS __g_entryThreadTimerScheduler;
    if (!scheduler)
    {
        scheduler = new LLBC_TimerScheduler;
    }
}

void LLBC_TimerScheduler::DestroyEntryThreadScheduler()
{
    LLBC_XDelete(LLBC_INTERNAL_NS __g_entryThreadTimerScheduler);
}

LLBC_TimerScheduler::_This *LLBC_TimerScheduler::GetEntryThreadScheduler()
{
    return LLBC_INTERNAL_NS __g_entryThreadTimerScheduler;
}

LLBC_TimerScheduler::_This *LLBC_TimerScheduler::GetCurrentThreadScheduler()
{
    __LLBC_LibTls *tls = __LLBC_GetLibTls();
    return reinterpret_cast<_This *>(tls->coreTls.timerScheduler);
}

void LLBC_TimerScheduler::Update()
{
    if (!_enabled)
        return;

    LLBC_TimerData *data;
    uint64 now = LLBC_GetMilliSeconds();

    while (_heap.FindTop(data) == LLBC_OK)
    {
        if (now < data->handle)
            break;

        _heap.DeleteTop();

        if (!data->validate)
        {
            _idxMap.erase(data->timerId);
            delete data;
            continue;
        }

        data->validate = false;
        data->timer->SetScheduling(false);

        bool reSchedule = false;
#if LLBC_CFG_CORE_TIMER_STRICT_SCHEDULE
        uint64 pseudoNow = now;
        while (pseudoNow >= data->handle)
#endif // LLBC_CFG_CORE_TIMER_STRICT_SCHEDULE
        {
            ++ data->repeatTimes;
            reSchedule = data->timer->OnTimeout();
#if LLBC_CFG_CORE_TIMER_STRICT_SCHEDULE
            if (!reSchedule)
                break;
#endif // !LLBC_CFG_CORE_TIMER_STRICT_SCHEDULE

            if (data->timer->IsScheduling())
            {
                reSchedule = false;
#if LLBC_CFG_CORE_TIMER_STRICT_SCHEDULE
                break;
#endif // LLBC_CFG_CORE_TIMER_STRICT_SCHEDULE
            }

#if LLBC_CFG_CORE_TIMER_STRICT_SCHEDULE
            if (data->period == 0)
                break;

            if (UNLIKELY(pseudoNow < data->period))
                break;

            pseudoNow -= data->period;
#endif // LLBC_CFG_CORE_TIMER_STRICT_SCHEDULE
        }

        if (reSchedule)
        {
            uint64 delay = (data->period != 0) ? (now - data->handle) % data->period : 0;

            data->validate = true;
            data->timer->SetScheduling(true);
            data->handle = now + data->period - delay;

            _heap.Insert(data);
        }
        else
        {
            _idxMap.erase(data->timerId);
            delete data;
        }
    }
}

bool LLBC_TimerScheduler::IsEnabled() const
{
    return _enabled;
}

void LLBC_TimerScheduler::SetEnabled(bool enabled)
{
    _enabled = enabled;
}

bool LLBC_TimerScheduler::IsDstroyed() const
{
    return _destroyed;
}

int LLBC_TimerScheduler::Schedule(LLBC_BaseTimer *timer)
{
    LLBC_TimerData *data = new LLBC_TimerData;
    data->handle = LLBC_GetMilliSeconds() + timer->GetDueTime();
    data->timerId = ++ _maxTimerId;
    data->dueTime = timer->GetDueTime();
    data->period = timer->GetPeriod();
    data->repeatTimes = 0;
    data->timer = timer;
    data->validate = true;

    timer->SetTimerId(_maxTimerId);
    timer->SetScheduling(true);

    _heap.Insert(data);
    _idxMap.insert(std::make_pair(_maxTimerId, data));

    return LLBC_OK;
}

int LLBC_TimerScheduler::Cancel(LLBC_BaseTimer *timer)
{
    _IdxMap::iterator iter = _idxMap.find(timer->GetTimerId());
    if (iter == _idxMap.end())
    {
        LLBC_SetLastError(LLBC_ERROR_NOT_FOUND);
        return LLBC_FAILED;
    }

    LLBC_TimerData *data = iter->second;
    ASSERT(data->timer == timer &&
        "Timer manager internal error, LLBC_TimerData::timer != argument: timer!");

    _idxMap.erase(data->timerId);

    data->validate = false;
    data->timer->SetScheduling(false);
    data->timer->OnCancel();

    return LLBC_OK;
}

void LLBC_TimerScheduler::CancelAll()
{
    if (_destroyed)
        return;

    std::vector<LLBC_TimerId> timerIds;
    for (_IdxMap::iterator iter = _idxMap.begin();
         iter != _idxMap.end();
         iter++)
    {
        LLBC_TimerData *data = iter->second;
        if (data->validate)
            timerIds.push_back(data->timerId);
    }

    for (size_t i = 0; i < timerIds.size(); i++)
    {
        _IdxMap::iterator iter = _idxMap.find(timerIds[i]);
        if (iter != _idxMap.end())
        {
            LLBC_TimerData *data = iter->second;
            if (UNLIKELY(!data->validate))
                return;

            data->timer->Cancel();
        }
    }
}

__LLBC_NS_END

#include "llbc/common/AfterIncl.h"
<|endoftext|>