<commit_before>/*********************************************************************
 *
 * Condor ClassAd library
 * Copyright (C) 1990-2003, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI and Rajesh Raman.
 *
 * This source code is covered by the Condor Public License, which can
 * be found in the accompanying LICENSE file, or online at
 * www.condorproject.org.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * AND THE UNIVERSITY OF WISCONSIN-MADISON "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, OF SATISFACTORY QUALITY, AND FITNESS
 * FOR A PARTICULAR PURPOSE OR USE ARE DISCLAIMED. THE COPYRIGHT
 * HOLDERS AND CONTRIBUTORS AND THE UNIVERSITY OF WISCONSIN-MADISON
 * MAKE NO MAKE NO REPRESENTATION THAT THE SOFTWARE, MODIFICATIONS,
 * ENHANCEMENTS OR DERIVATIVE WORKS THEREOF, WILL NOT INFRINGE ANY
 * PATENT, COPYRIGHT, TRADEMARK, TRADE SECRET OR OTHER PROPRIETARY
 * RIGHT.
 *
 *********************************************************************/

#include "common.h"
#include "xmlLexer.h"
#include "lexer.h"
#include "util.h"

using namespace std;

BEGIN_NAMESPACE( classad )

// Note that these must be in the same order as the enum TagID
struct xml_tag_mapping tag_mappings[] =
{
	{"classads",        XMLLexer::tagID_ClassAds},
	{"c",               XMLLexer::tagID_ClassAd},
	{"a",               XMLLexer::tagID_Attribute},
	{"i",               XMLLexer::tagID_Integer},
	{"r",              XMLLexer::tagID_Real},
	{"s",               XMLLexer::tagID_String},
	{"b",               XMLLexer::tagID_Bool},
	{"un",              XMLLexer::tagID_Undefined},
	{"er",              XMLLexer::tagID_Error},
	{"at",               XMLLexer::tagID_AbsoluteTime},
	{"rt",               XMLLexer::tagID_RelativeTime},
	{"l",               XMLLexer::tagID_List},
	{"e",               XMLLexer::tagID_Expr},
	{"?xml",            XMLLexer::tagID_XML},
	{"?xml-stylesheet", XMLLexer::tagID_XMLStylesheet},
	{"DOCTYPE",         XMLLexer::tagID_Doctype}
};

struct entity
{
	char *name;
	char *replacement_text;
	int length;
};

#define NUMBER_OF_ENTITIES (sizeof(entities) / sizeof(struct entity))
struct entity entities[] =
{
	{"&",  "&",  5},
	{"<",   "<",  4},
	{">",   ">",  4}
};

XMLLexer::Token::
Token()
{
	return;
}

XMLLexer::Token::
~Token()
{
	return;
}

void XMLLexer::Token::
ClearToken(void)
{
	token_type = tokenType_Invalid;
	tag_type   = tagType_Invalid;
	tag_id     = tagID_NoTag;
	text       = "";
	attributes.clear();
	return;
}

void XMLLexer::Token::
DumpToken(void)
{
	if (token_type == tokenType_Tag) {
		printf("TAG: \"%s\", ", text.c_str());
		printf("  Type: ");
		switch (tag_type) {
		case tagType_Start:   printf("start, ");   break;
		case tagType_End:     printf("end, ");     break;
		case tagType_Empty:   printf("empty, ");   break;
		case tagType_Invalid: printf("invalid, "); break;
		default: break;
		}
		printf("  ID: ");
		switch (tag_id) {
		case tagID_ClassAds:  printf("classads\n"); break;
		case tagID_ClassAd:   printf("classad\n"); break;
		case tagID_Attribute: printf("attribute\n"); break;
		case tagID_Integer:    printf("integer\n"); break;
		case tagID_Real:    printf("real\n"); break;
		case tagID_String:    printf("string\n"); break;
		case tagID_Bool:      printf("bool\n"); break;
		case tagID_Undefined: printf("undefined\n"); break;
		case tagID_Error:     printf("error\n"); break;
		case tagID_AbsoluteTime:      printf("absolutetime\n"); break;
		case tagID_RelativeTime:      printf("relativetime\n"); break;
		case tagID_List:      printf("list\n"); break;
		case tagID_Expr:      printf("expr\n"); break;
		case tagID_XML:       printf("?xml\n"); break;
		case tagID_XMLStylesheet: printf("?xmlStyleSheet\n"); break;
		case tagID_Doctype:   printf("DOCTYPE\n"); break;
		case tagID_NoTag:     printf("notag\n"); break;
		}
		XMLAttributesIterator  iter;

		for (iter = attributes.begin(); iter != attributes.end(); iter++) {
			printf("  Attribute: %s = \"%s\"\n",
				   iter->first.c_str(), iter->second.c_str());
		}
	} else if (token_type == tokenType_Text) {
		printf("TEXT: \"%s\"\n", text.c_str());
	} else {
		printf("Invalid token.\n");
	}
	return;
}

XMLLexer::
XMLLexer ()
{
	lexer_source = NULL;
	return;
}

XMLLexer::
~XMLLexer ()
{
	return;
}

void XMLLexer::
SetLexerSource(LexerSource *source)
{
	lexer_source = source;

	return;
}

bool XMLLexer::
ConsumeToken(Token *token)
{
	bool have_token;

	if (!token_is_valid) {
		have_token = PeekToken(token);
	} else {
		have_token = true;
		if (token != NULL) {
			*token = current_token;
		}
	}
	token_is_valid = false;

    /*
    if (have_token) {
        printf("Consuming token: ");
        current_token.DumpToken();
    }
    */

	return have_token;
}

bool XMLLexer::
PeekToken(Token *token)
{
	bool have_token;

	if (token_is_valid) {
		have_token = true;
	} else {
		have_token = GrabToken();
	}

	if (have_token) {
		if (token != NULL) {
			*token = current_token;
		}
		token_is_valid = true;
        /*
        printf("Peeking token: ");
        current_token.DumpToken();
        */
	}
	return have_token;
}

bool XMLLexer::
GrabToken(void)
{
	bool have_token;
	bool have_character;
	int  character;

	current_token.ClearToken();

	// Find first non-whitespace character, to decide if we
	// are getting a tag or some text. If we get text, we preserve
	// whitespace, because we keep whitespace in strings.
	have_character = false;
	while (!lexer_source->AtEnd()) {
		character = lexer_source->ReadCharacter();
		if (!isspace(character)) {
			have_character = true;
			break;
		}
	}

	if (have_character) {
		if (character == '<') {
			have_token = GrabTag();
		} else {
			lexer_source->UnreadCharacter();
			have_token = GrabText();
		}
	} else {
		have_token = false;
	}

	//current_token.DumpToken();

	return have_token;
}

bool XMLLexer::
GrabTag(void)
{
	bool    have_token;
	int     character;
	string  complete_tag; // the tag and its attributes

	current_token.token_type = tokenType_Tag;
	complete_tag = "";
	character = -1;

	// Skip whitespace
	while (!lexer_source->AtEnd()) {
		character = lexer_source->ReadCharacter();
		if (!isspace(character)) {
			complete_tag += character;
			break;
		}
	}

	// Read the rest of the tag, a character at a time
	while (!lexer_source->AtEnd()) {
		character = lexer_source->ReadCharacter();
		if (character != '>') {
			complete_tag += character;
		} else {
			break;
		}
	}

	if (character != '>') {
		// We have an unclosed tag. This will not do.
		have_token = false;
	} else {
		BreakdownTag(complete_tag.c_str());
		have_token = true;
	}
	return true;
}

void XMLLexer::
BreakdownTag(const char *complete_tag)
{

	int length, i;
	int start, count;

	length = strlen(complete_tag);

	// Skip whitespace
	for (i = 0; i < length && isspace(complete_tag[i]); i++) {
		;
	}

	// Is it a begin or end tag?
	if (complete_tag[i] == '/') {
		current_token.tag_type = tagType_End;
		i++;
	} else if (complete_tag[length-1] == '/') {
		current_token.tag_type = tagType_Empty;
		length--; // skip the / in the processing that follows.
	} else {
		current_token.tag_type = tagType_Start;
	}

	// Now pull out the tag name
	current_token.text = "";
	start = i;
	count = 0;
	while (i < length && i != '>' && !isspace(complete_tag[i])) {
		//current_token.text += complete_tag[i];
		i++;
		count++;
	}
	// With gcc-2.x's STL, this is faster than using a bunch of += statements.
	current_token.text.assign(complete_tag+start, count);

	// Figure out which tag it is
	current_token.tag_id = tagID_NoTag;
	for (unsigned int x = 0; x < NUMBER_OF_TAG_MAPPINGS; x++) {
		if (!strcmp(current_token.text.c_str(), tag_mappings[x].tag_name)) {
			current_token.tag_id = tag_mappings[x].id;
			break;
		}
	}

	// If we're not at the end, we probably have attributes, so let's
	// pull them out. (We might just have whitespace though.)
	while (i < length) {
		string name, value;

		name  = "";
		value = "";

		// Skip whitespace
		while (i < length && isspace(complete_tag[i])) {
			i++;
		}

		// Now take text up to a whitespace or equal sign. This is the name
		start = i;
		count = 0;
		while (i < length
			   && !isspace(complete_tag[i])
			   && complete_tag[i] != '=') {
			//name += complete_tag[i];
			i++;
			count++;
		}
		// With gcc-2.x's STL, this is faster than using a bunch of += statements.
		name.assign(complete_tag+start, count);

		// Now skip whitespace and equal signs
		// Note that this allows some technically illegal things
		// like " == = = =", but who really cares?
		while (i < length
			   && (isspace(complete_tag[i]) || complete_tag[i] == '=')) {
			i++;
		}

		i++; // go past 1st \"

		// Now pick out the value
		char oldCh = 0;
		// consume the string literal; read upto " ignoring \"
		while (    (i<length)
				&& (    complete_tag[i] != '\"'
					 || ( complete_tag[i] == '\"' && oldCh == '\\' ) ) ) {
			oldCh = complete_tag[i];
			value += complete_tag[i];
			i++;
		}
		// scan string for &...; & replace them with their corresponding entities
		for (unsigned int k=0; k< value.length(); k++) {
			if (value[k] == '&') { // create substring
				int index = k-1;
				string str;
				do {
					index++;
					str += value[index];
				} while(value[index] != ';');
				for (unsigned int i = 0; i < NUMBER_OF_ENTITIES; i++){
					if (!strcmp(str.c_str(), entities[i].name)) {
						value.replace(k, str.length(), entities[i].replacement_text);
					}
				}
			}
		}
		bool validStr = true;
		value += " ";
		convert_escapes(value, validStr);
		if(!validStr) {  // contains a \0 escape char
			current_token.tag_type = tagType_Invalid;
		}
		else if (name.size() > 0 && value.size() > 0) {
			current_token.attributes[name] = value;
		}
	}
	return;
}

bool XMLLexer::
GrabText(void)
{
	bool have_token;
	bool have_nonspace;

	current_token.token_type = tokenType_Text;
	have_nonspace = false;
	current_token.text = "";

	while (!lexer_source->AtEnd()) {
		int character;

		character = lexer_source->ReadCharacter();
		if (character == '<') {
			lexer_source->UnreadCharacter();
			break;
		} else {
			if (character == '&') {

				have_nonspace = true;

				// Figure out if this is an entity. If it is, figure
				// out what character should actually be put into the text;
				// First, find the text of the entity: it's text up and including
				// a semicolon. But finding a space or another & will stop us.

				string entity_text;
				entity_text = character;
				while (!lexer_source->AtEnd()) {
					int ch = lexer_source->ReadCharacter();
					if (ch == ' ') {
						// drop the entire text, including the space into the token
						entity_text += ch;
						current_token.text += entity_text;
						break;
					} else if (ch == '&') {
						// drop the text before the & into the token.
						// put back the & to be found the next time around, so
						// we can look for another entity
						lexer_source->UnreadCharacter();
						current_token.text += entity_text;
						break;
					} else if (ch == ';') {
						bool replaced_entity = false;
						// Non-numeric entity, do a comparison
						entity_text += ch;
						for (unsigned int i = 0; i < NUMBER_OF_ENTITIES; i++){
						  if (!strcmp(entity_text.c_str(), entities[i].name)) {
						    current_token.text += entities[i].replacement_text;
						    replaced_entity = true;
						    break;
						  }
						}

						if (!replaced_entity) {
							current_token.text += entity_text;
						}
						break;
					} else {
						entity_text += ch;
					}
				}
			} else {
				current_token.text += character;
				if (!isspace(character)) {
					have_nonspace = true;
				}
			}
		}
	}

	if (!have_nonspace) {
		// We know that we're at the end of the buffer, because
		// otherwise GrabTag would have been called.
		have_token = false;
	} else {
		have_token = true;
	}
	return have_token;
}

END_NAMESPACE // classad
<commit_msg>Fixed some uninitalized values. A couple of them seemed okay, but valgrind complained. But one of them definitely needed fixed. Whatever.<commit_after>/*********************************************************************
 *
 * Condor ClassAd library
 * Copyright (C) 1990-2003, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI and Rajesh Raman.
 *
 * This source code is covered by the Condor Public License, which can
 * be found in the accompanying LICENSE file, or online at
 * www.condorproject.org.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * AND THE UNIVERSITY OF WISCONSIN-MADISON "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, OF SATISFACTORY QUALITY, AND FITNESS
 * FOR A PARTICULAR PURPOSE OR USE ARE DISCLAIMED. THE COPYRIGHT
 * HOLDERS AND CONTRIBUTORS AND THE UNIVERSITY OF WISCONSIN-MADISON
 * MAKE NO MAKE NO REPRESENTATION THAT THE SOFTWARE, MODIFICATIONS,
 * ENHANCEMENTS OR DERIVATIVE WORKS THEREOF, WILL NOT INFRINGE ANY
 * PATENT, COPYRIGHT, TRADEMARK, TRADE SECRET OR OTHER PROPRIETARY
 * RIGHT.
 *
 *********************************************************************/

#include "common.h"
#include "xmlLexer.h"
#include "lexer.h"
#include "util.h"

using namespace std;

BEGIN_NAMESPACE( classad )

// Note that these must be in the same order as the enum TagID
struct xml_tag_mapping tag_mappings[] =
{
	{"classads",        XMLLexer::tagID_ClassAds},
	{"c",               XMLLexer::tagID_ClassAd},
	{"a",               XMLLexer::tagID_Attribute},
	{"i",               XMLLexer::tagID_Integer},
	{"r",              XMLLexer::tagID_Real},
	{"s",               XMLLexer::tagID_String},
	{"b",               XMLLexer::tagID_Bool},
	{"un",              XMLLexer::tagID_Undefined},
	{"er",              XMLLexer::tagID_Error},
	{"at",               XMLLexer::tagID_AbsoluteTime},
	{"rt",               XMLLexer::tagID_RelativeTime},
	{"l",               XMLLexer::tagID_List},
	{"e",               XMLLexer::tagID_Expr},
	{"?xml",            XMLLexer::tagID_XML},
	{"?xml-stylesheet", XMLLexer::tagID_XMLStylesheet},
	{"DOCTYPE",         XMLLexer::tagID_Doctype}
};

struct entity
{
	char *name;
	char *replacement_text;
	int length;
};

#define NUMBER_OF_ENTITIES (sizeof(entities) / sizeof(struct entity))
struct entity entities[] =
{
	{"&",  "&",  5},
	{"<",   "<",  4},
	{">",   ">",  4}
};

XMLLexer::Token::
Token()
{
	return;
}

XMLLexer::Token::
~Token()
{
	return;
}

void XMLLexer::Token::
ClearToken(void)
{
	token_type = tokenType_Invalid;
	tag_type   = tagType_Invalid;
	tag_id     = tagID_NoTag;
	text       = "";
	attributes.clear();
	return;
}

void XMLLexer::Token::
DumpToken(void)
{
	if (token_type == tokenType_Tag) {
		printf("TAG: \"%s\", ", text.c_str());
		printf("  Type: ");
		switch (tag_type) {
		case tagType_Start:   printf("start, ");   break;
		case tagType_End:     printf("end, ");     break;
		case tagType_Empty:   printf("empty, ");   break;
		case tagType_Invalid: printf("invalid, "); break;
		default: break;
		}
		printf("  ID: ");
		switch (tag_id) {
		case tagID_ClassAds:  printf("classads\n"); break;
		case tagID_ClassAd:   printf("classad\n"); break;
		case tagID_Attribute: printf("attribute\n"); break;
		case tagID_Integer:    printf("integer\n"); break;
		case tagID_Real:    printf("real\n"); break;
		case tagID_String:    printf("string\n"); break;
		case tagID_Bool:      printf("bool\n"); break;
		case tagID_Undefined: printf("undefined\n"); break;
		case tagID_Error:     printf("error\n"); break;
		case tagID_AbsoluteTime:      printf("absolutetime\n"); break;
		case tagID_RelativeTime:      printf("relativetime\n"); break;
		case tagID_List:      printf("list\n"); break;
		case tagID_Expr:      printf("expr\n"); break;
		case tagID_XML:       printf("?xml\n"); break;
		case tagID_XMLStylesheet: printf("?xmlStyleSheet\n"); break;
		case tagID_Doctype:   printf("DOCTYPE\n"); break;
		case tagID_NoTag:     printf("notag\n"); break;
		}
		XMLAttributesIterator  iter;

		for (iter = attributes.begin(); iter != attributes.end(); iter++) {
			printf("  Attribute: %s = \"%s\"\n",
				   iter->first.c_str(), iter->second.c_str());
		}
	} else if (token_type == tokenType_Text) {
		printf("TEXT: \"%s\"\n", text.c_str());
	} else {
		printf("Invalid token.\n");
	}
	return;
}

XMLLexer::
XMLLexer ()
{
	lexer_source = NULL;
    token_is_valid = false;
	return;
}

XMLLexer::
~XMLLexer ()
{
	return;
}

void XMLLexer::
SetLexerSource(LexerSource *source)
{
	lexer_source = source;

	return;
}

bool XMLLexer::
ConsumeToken(Token *token)
{
	bool have_token;

	if (!token_is_valid) {
		have_token = PeekToken(token);
	} else {
		have_token = true;
		if (token != NULL) {
			*token = current_token;
		}
	}
	token_is_valid = false;

    /*
    if (have_token) {
        printf("Consuming token: ");
        current_token.DumpToken();
    }
    */

	return have_token;
}

bool XMLLexer::
PeekToken(Token *token)
{
	bool have_token;

	if (token_is_valid) {
		have_token = true;
	} else {
		have_token = GrabToken();
	}

	if (have_token) {
		if (token != NULL) {
			*token = current_token;
		}
		token_is_valid = true;
        /*
        printf("Peeking token: ");
        current_token.DumpToken();
        */
	}
	return have_token;
}

bool XMLLexer::
GrabToken(void)
{
	bool have_token;
	bool have_character;
	int  character;

	current_token.ClearToken();
	have_character = false;
    have_token     = false;
    character      = -1;
	// Find first non-whitespace character, to decide if we
	// are getting a tag or some text. If we get text, we preserve
	// whitespace, because we keep whitespace in strings.
	while (!lexer_source->AtEnd()) {
		character = lexer_source->ReadCharacter();
		if (!isspace(character)) {
			have_character = true;
			break;
		}
	}

	if (have_character) {
		if (character == '<') {
			have_token = GrabTag();
		} else {
			lexer_source->UnreadCharacter();
			have_token = GrabText();
		}
	} else {
		have_token = false;
	}

	//current_token.DumpToken();

	return have_token;
}

bool XMLLexer::
GrabTag(void)
{
	bool    have_token;
	int     character;
	string  complete_tag; // the tag and its attributes

	current_token.token_type = tokenType_Tag;
	complete_tag = "";
	character = -1;

	// Skip whitespace
	while (!lexer_source->AtEnd()) {
		character = lexer_source->ReadCharacter();
		if (!isspace(character)) {
			complete_tag += character;
			break;
		}
	}

	// Read the rest of the tag, a character at a time
	while (!lexer_source->AtEnd()) {
		character = lexer_source->ReadCharacter();
		if (character != '>') {
			complete_tag += character;
		} else {
			break;
		}
	}

	if (character != '>') {
		// We have an unclosed tag. This will not do.
		have_token = false;
	} else {
		BreakdownTag(complete_tag.c_str());
		have_token = true;
	}
	return true;
}

void XMLLexer::
BreakdownTag(const char *complete_tag)
{

	int length, i;
	int start, count;

	length = strlen(complete_tag);

	// Skip whitespace
	for (i = 0; i < length && isspace(complete_tag[i]); i++) {
		;
	}

	// Is it a begin or end tag?
	if (complete_tag[i] == '/') {
		current_token.tag_type = tagType_End;
		i++;
	} else if (complete_tag[length-1] == '/') {
		current_token.tag_type = tagType_Empty;
		length--; // skip the / in the processing that follows.
	} else {
		current_token.tag_type = tagType_Start;
	}

	// Now pull out the tag name
	current_token.text = "";
	start = i;
	count = 0;
	while (i < length && i != '>' && !isspace(complete_tag[i])) {
		//current_token.text += complete_tag[i];
		i++;
		count++;
	}
	// With gcc-2.x's STL, this is faster than using a bunch of += statements.
	current_token.text.assign(complete_tag+start, count);

	// Figure out which tag it is
	current_token.tag_id = tagID_NoTag;
	for (unsigned int x = 0; x < NUMBER_OF_TAG_MAPPINGS; x++) {
		if (!strcmp(current_token.text.c_str(), tag_mappings[x].tag_name)) {
			current_token.tag_id = tag_mappings[x].id;
			break;
		}
	}

	// If we're not at the end, we probably have attributes, so let's
	// pull them out. (We might just have whitespace though.)
	while (i < length) {
		string name, value;

		name  = "";
		value = "";

		// Skip whitespace
		while (i < length && isspace(complete_tag[i])) {
			i++;
		}

		// Now take text up to a whitespace or equal sign. This is the name
		start = i;
		count = 0;
		while (i < length
			   && !isspace(complete_tag[i])
			   && complete_tag[i] != '=') {
			//name += complete_tag[i];
			i++;
			count++;
		}
		// With gcc-2.x's STL, this is faster than using a bunch of += statements.
		name.assign(complete_tag+start, count);

		// Now skip whitespace and equal signs
		// Note that this allows some technically illegal things
		// like " == = = =", but who really cares?
		while (i < length
			   && (isspace(complete_tag[i]) || complete_tag[i] == '=')) {
			i++;
		}

		i++; // go past 1st \"

		// Now pick out the value
		char oldCh = 0;
		// consume the string literal; read upto " ignoring \"
		while (    (i<length)
				&& (    complete_tag[i] != '\"'
					 || ( complete_tag[i] == '\"' && oldCh == '\\' ) ) ) {
			oldCh = complete_tag[i];
			value += complete_tag[i];
			i++;
		}
		// scan string for &...; & replace them with their corresponding entities
		for (unsigned int k=0; k< value.length(); k++) {
			if (value[k] == '&') { // create substring
				int index = k-1;
				string str;
				do {
					index++;
					str += value[index];
				} while(value[index] != ';');
				for (unsigned int i = 0; i < NUMBER_OF_ENTITIES; i++){
					if (!strcmp(str.c_str(), entities[i].name)) {
						value.replace(k, str.length(), entities[i].replacement_text);
					}
				}
			}
		}
		bool validStr = true;
		value += " ";
		convert_escapes(value, validStr);
		if(!validStr) {  // contains a \0 escape char
			current_token.tag_type = tagType_Invalid;
		}
		else if (name.size() > 0 && value.size() > 0) {
			current_token.attributes[name] = value;
		}
	}
	return;
}

bool XMLLexer::
GrabText(void)
{
	bool have_token;
	bool have_nonspace;

	current_token.token_type = tokenType_Text;
	have_nonspace = false;
	current_token.text = "";

	while (!lexer_source->AtEnd()) {
		int character;

		character = lexer_source->ReadCharacter();
		if (character == '<') {
			lexer_source->UnreadCharacter();
			break;
		} else {
			if (character == '&') {

				have_nonspace = true;

				// Figure out if this is an entity. If it is, figure
				// out what character should actually be put into the text;
				// First, find the text of the entity: it's text up and including
				// a semicolon. But finding a space or another & will stop us.

				string entity_text;
				entity_text = character;
				while (!lexer_source->AtEnd()) {
					int ch = lexer_source->ReadCharacter();
					if (ch == ' ') {
						// drop the entire text, including the space into the token
						entity_text += ch;
						current_token.text += entity_text;
						break;
					} else if (ch == '&') {
						// drop the text before the & into the token.
						// put back the & to be found the next time around, so
						// we can look for another entity
						lexer_source->UnreadCharacter();
						current_token.text += entity_text;
						break;
					} else if (ch == ';') {
						bool replaced_entity = false;
						// Non-numeric entity, do a comparison
						entity_text += ch;
						for (unsigned int i = 0; i < NUMBER_OF_ENTITIES; i++){
						  if (!strcmp(entity_text.c_str(), entities[i].name)) {
						    current_token.text += entities[i].replacement_text;
						    replaced_entity = true;
						    break;
						  }
						}

						if (!replaced_entity) {
							current_token.text += entity_text;
						}
						break;
					} else {
						entity_text += ch;
					}
				}
			} else {
				current_token.text += character;
				if (!isspace(character)) {
					have_nonspace = true;
				}
			}
		}
	}

	if (!have_nonspace) {
		// We know that we're at the end of the buffer, because
		// otherwise GrabTag would have been called.
		have_token = false;
	} else {
		have_token = true;
	}
	return have_token;
}

END_NAMESPACE // classad
<|endoftext|>


<commit_before>/***************************************************************
 *
 * Copyright (C) 1990-2019, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 *
 * 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 "condor_common.h"
#include "condor_config.h"
#include "condor_attributes.h"
#include "qmgmt.h"
#include "scheduler.h"
#include "condor_qmgr.h"
#include "jobsets.h"
#include "classad_merge.h"

void
JobSets::setNextSetId(int id)
{
	if (id <= 0) {
		dprintf(D_ALWAYS /* | D_BACKTRACE*/, "JobSet rejecting attempt to setNextSetId(%d)\n", id);
		return;
	}
	next_setid_num = id;
	dprintf(D_FULLDEBUG, "JobSet setNextSetId restored to id %d\n", id);
}

void
JobSets::reconfig()
{

}

// add this jobset to the map of JobSets
bool
JobSets::restoreJobSet(JobQueueJobSet *setAd)
{
	setAd->PopulateFromAd(); // pull fields from the classad

	std::string name;
	if ( ! setAd->LookupString(ATTR_JOB_SET_NAME, name) || setAd->Jobset() <= 0 || !setAd->ownerinfo)
	{
		dprintf(D_ALWAYS, "ERROR: restoreJobSet - log has malformed JobSet %d key=(%d,%d)\n",
			setAd->Jobset(), setAd->jid.cluster, setAd->jid.proc);
		return false;
	}

	// Add into our per-user name to id map, making sure it is not already there
	std::string alias = makeAlias(name, setAd->ownerinfo->name);
	unsigned int &setId = mapAliasToId[alias];
	if (setId) {
		dprintf(D_ALWAYS, "ERROR: restoreJobSet - log has duplicate JobSet names (%s,%d) for set %d for user %s\n",
			name.c_str(), setId, setAd->set_id, setAd->ownerinfo->Name());
		return false;
	}

	setId = setAd->Jobset();
	return true;
}

// load cached fields in the JobQueueJobSet from the classad and/or key
void JobQueueJobSet::PopulateFromAd()
{
	ASSERT(this->Jobset() == qkey2_to_JOBSETID(jid.proc));
	this->LookupInteger(ATTR_TOTAL_COMPLETED_JOBS, jobStatusAggregates.JobsCompleted);
	this->LookupInteger("Scheduler" ATTR_TOTAL_COMPLETED_JOBS, jobStatusAggregates.SchedulerJobsCompleted);
	this->LookupInteger(ATTR_TOTAL_REMOVED_JOBS, jobStatusAggregates.JobsRemoved);
	this->LookupInteger("Scheduler" ATTR_TOTAL_REMOVED_JOBS, jobStatusAggregates.SchedulerJobsRemoved);
}

JobQueueJobSet* JobSets::getSet(JobQueueJob & job)
{
	int setId = job.set_id;
	if (setId == -1) {
		// job is not in a set, bail out quickly now before ad lookups etc
		return nullptr;
	}
	return GetJobSetAd(setId);
}

// lookup or allocate a setid, for use by the qmgmt commit transaction code
int JobSets::getOrCreateSet(const std::string & setName, const std::string & user, std::vector<unsigned int> & new_ids)
{
	std::string alias = makeAlias(setName, user);
	auto it = mapAliasToId.find(alias);
	if (it != mapAliasToId.end()) return it->second;

	// Create a new set with a new set id!
	int setId = next_setid_num;
	SetSecureAttributeInt(0, 0, ATTR_NEXT_JOBSET_NUM, ++next_setid_num);
	JobSetCreate(setId, setName.c_str(), user.c_str());
	new_ids.push_back(setId);
	mapAliasToId[alias] = setId;
	return setId;
}

bool JobSets::removeSet(JobQueueJobSet* & jobset)
{
	std::string name, alias;
	jobset->LookupString(ATTR_JOB_SET_NAME, name);
	if (name.empty() || ! jobset->ownerinfo) {
		// we don't know the alias, so remove all elements that match the set id
		dprintf(D_ALWAYS, "Removing JobSet id=%d (%zu refs)\n", jobset->Jobset(), jobset->member_count);
		for (auto it = mapAliasToId.begin(); it != mapAliasToId.end(); /* it updated inside loop */) {
			if (it->second == jobset->Jobset()) { it = mapAliasToId.erase(it); }
			else { ++it; }
		}
	} else {
		alias = makeAlias(name, jobset->ownerinfo->name);
		dprintf(D_ALWAYS, "Removing JobSet %s id=%d (%zu refs)\n", alias.c_str(), jobset->Jobset(), jobset->member_count);
		mapAliasToId.erase(alias);
	}
	bool rval = JobSetDestroy(jobset->Jobset());
	if ( ! rval) {
		dprintf(D_ALWAYS, "WARNING: unable to remove from log JobSet id=%d\n", jobset->Jobset());
	}
	jobset = nullptr;
	return rval;
}

// called by InitJobQueue if jobsets are enabled
// to add this job to the jobset member count and aggregates
//
bool JobSets::addJobToSet(JobQueueJob & job, std::vector<unsigned int> & new_ids)
{
	if (job.IsJob() || job.IsCluster()) {
		// if the job has a set_id of 0, that means "look it up, creating the set if needed"
		if ( ! job.set_id) {
			std::string setName;
			if (!job.LookupString(ATTR_JOB_SET_NAME, setName)) {
				// uncomment to use clusterid as default setname
				// setName = std::to_string(job.jid.cluster);
			}
			if (setName.empty()) {
				// remember this job is not in a set for fast bailout next time
				job.set_id = -1;
				return false;
			}
			job.set_id = getOrCreateSet(setName, job.ownerinfo->name, new_ids);
		}

		JobQueueJobSet * set = getSet(job);
		if (!set) {
			// nothing to do, this job is not a member of a set
			if (job.set_id > 0) {
				// hmm. job has a setid, but that set does not exist
			}
			return false;
		}
		set->member_count += 1;
		set->Assign(ATTR_REF_COUNT, set->member_count);
		if (job.IsJob()) {
			IncrementLiveJobCounter(set->jobStatusAggregates, job.Universe(), job.Status(), 1);
		}
		return true;
	}
	return false;
}

void JobSets::status_change(JobQueueJob & job, int new_status)
{
	if ( ! job.IsJob())
		return;

	JobQueueJobSet * jobset = getSet(job);
	if (jobset) {
		IncrementLiveJobCounter(jobset->jobStatusAggregates, job.Universe(), job.Status(), -1);
		IncrementLiveJobCounter(jobset->jobStatusAggregates, job.Universe(), new_status, 1);

		if (IsDebugCatAndVerbosity(D_JOB | D_VERBOSE)) {
			ClassAd s;
			jobset->jobStatusAggregates.publish(s, "Live");
			MergeClassAds(&s, jobset, true);
			std::string name;
			if ( ! jobset->LookupString(ATTR_JOB_SET_NAME, name)) { name = std::to_string(jobset->Jobset()); }
			dprintf(D_JOB | D_VERBOSE, "Updating JobSet %s (id=%u) after status change in job %d.%d to be:\n",
				name.c_str(), jobset->Jobset(), job.jid.cluster, job.jid.proc);
			dPrintAd(D_JOB, s);
		}
	}
}

bool JobSets::removeJobFromSet(JobQueueJob & job)
{
	if (job.set_id <= 0) return true;

	JobQueueJobSet* jobset = getSet(job);
	if ( ! jobset) {
		// if job is not in a set, we don't necessarily create one ;)
		return true;
	}

	job.set_id = -1;

	// update historical counters now to disk, as this job is leaving
	// We need a separate count of jobs that are no longer in the set
	// This is used to initialized the
	if (jobset) {
		if (job.IsJob() && job.Status() >= REMOVED && job.Status() <= COMPLETED) {
			static const char * const attrs[]{
				ATTR_TOTAL_REMOVED_JOBS, "Scheduler" ATTR_TOTAL_REMOVED_JOBS,
				ATTR_TOTAL_COMPLETED_JOBS, "Scheduler" ATTR_TOTAL_COMPLETED_JOBS,
			};
			int ix = 2 * (job.Status() - REMOVED) + (job.Universe() == CONDOR_UNIVERSE_SCHEDULER) ? 1 : 0;
			const char * attr = attrs[ix];
			int val = 0;
			jobset->LookupInteger(attr, val);
			val++;
			SetSecureAttributeInt(jobset->jid.cluster, jobset->jid.proc, attr, val);
		}

		if (jobset->member_count > 0) {
			jobset->member_count -= 1;
			jobset->Assign(ATTR_REF_COUNT, jobset->member_count);
		}

		// Check if time to destroy this set...
		garbageCollectSet(jobset);
	}

	return true;
}

bool JobSets::garbageCollectSet(JobQueueJobSet* & jobset)
{
	// TODO: implement other garbage collection policies
	if (jobset &&
		jobset->member_count == 0 &&
		jobset->garbagePolicy == JobQueueJobSet::garbagePolicyEnum::immediateAfterEmpty)
	{
		removeSet(jobset);
		return true;
	}
	return false;
}

<commit_msg>HTCONDOR-899 fixup dprintf args to remove warnings<commit_after>/***************************************************************
 *
 * Copyright (C) 1990-2019, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 *
 * 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 "condor_common.h"
#include "condor_config.h"
#include "condor_attributes.h"
#include "qmgmt.h"
#include "scheduler.h"
#include "condor_qmgr.h"
#include "jobsets.h"
#include "classad_merge.h"

void
JobSets::setNextSetId(int id)
{
	if (id <= 0) {
		dprintf(D_ALWAYS /* | D_BACKTRACE*/, "JobSet rejecting attempt to setNextSetId(%d)\n", id);
		return;
	}
	next_setid_num = id;
	dprintf(D_FULLDEBUG, "JobSet setNextSetId restored to id %d\n", id);
}

void
JobSets::reconfig()
{

}

// add this jobset to the map of JobSets
bool
JobSets::restoreJobSet(JobQueueJobSet *setAd)
{
	setAd->PopulateFromAd(); // pull fields from the classad

	std::string name;
	if ( ! setAd->LookupString(ATTR_JOB_SET_NAME, name) || setAd->Jobset() <= 0 || !setAd->ownerinfo)
	{
		dprintf(D_ALWAYS, "ERROR: restoreJobSet - log has malformed JobSet %d key=(%d,%d)\n",
			setAd->Jobset(), setAd->jid.cluster, setAd->jid.proc);
		return false;
	}

	// Add into our per-user name to id map, making sure it is not already there
	std::string alias = makeAlias(name, setAd->ownerinfo->name);
	unsigned int &setId = mapAliasToId[alias];
	if (setId) {
		dprintf(D_ALWAYS, "ERROR: restoreJobSet - log has duplicate JobSet names (%s,%d) for set %d for user %s\n",
			name.c_str(), setId, setAd->set_id, setAd->ownerinfo->Name());
		return false;
	}

	setId = setAd->Jobset();
	return true;
}

// load cached fields in the JobQueueJobSet from the classad and/or key
void JobQueueJobSet::PopulateFromAd()
{
	ASSERT(this->Jobset() == qkey2_to_JOBSETID(jid.proc));
	this->LookupInteger(ATTR_TOTAL_COMPLETED_JOBS, jobStatusAggregates.JobsCompleted);
	this->LookupInteger("Scheduler" ATTR_TOTAL_COMPLETED_JOBS, jobStatusAggregates.SchedulerJobsCompleted);
	this->LookupInteger(ATTR_TOTAL_REMOVED_JOBS, jobStatusAggregates.JobsRemoved);
	this->LookupInteger("Scheduler" ATTR_TOTAL_REMOVED_JOBS, jobStatusAggregates.SchedulerJobsRemoved);
}

JobQueueJobSet* JobSets::getSet(JobQueueJob & job)
{
	int setId = job.set_id;
	if (setId == -1) {
		// job is not in a set, bail out quickly now before ad lookups etc
		return nullptr;
	}
	return GetJobSetAd(setId);
}

// lookup or allocate a setid, for use by the qmgmt commit transaction code
int JobSets::getOrCreateSet(const std::string & setName, const std::string & user, std::vector<unsigned int> & new_ids)
{
	std::string alias = makeAlias(setName, user);
	auto it = mapAliasToId.find(alias);
	if (it != mapAliasToId.end()) return it->second;

	// Create a new set with a new set id!
	int setId = next_setid_num;
	SetSecureAttributeInt(0, 0, ATTR_NEXT_JOBSET_NUM, ++next_setid_num);
	JobSetCreate(setId, setName.c_str(), user.c_str());
	new_ids.push_back(setId);
	mapAliasToId[alias] = setId;
	return setId;
}

bool JobSets::removeSet(JobQueueJobSet* & jobset)
{
	std::string name, alias;
	jobset->LookupString(ATTR_JOB_SET_NAME, name);
	if (name.empty() || ! jobset->ownerinfo) {
		// we don't know the alias, so remove all elements that match the set id
		dprintf(D_ALWAYS, "Removing JobSet id=%d (%u refs)\n", jobset->Jobset(), jobset->member_count);
		for (auto it = mapAliasToId.begin(); it != mapAliasToId.end(); /* it updated inside loop */) {
			if (it->second == jobset->Jobset()) { it = mapAliasToId.erase(it); }
			else { ++it; }
		}
	} else {
		alias = makeAlias(name, jobset->ownerinfo->name);
		dprintf(D_ALWAYS, "Removing JobSet %s id=%d (%u refs)\n", alias.c_str(), jobset->Jobset(), jobset->member_count);
		mapAliasToId.erase(alias);
	}
	bool rval = JobSetDestroy(jobset->Jobset());
	if ( ! rval) {
		dprintf(D_ALWAYS, "WARNING: unable to remove from log JobSet id=%d\n", jobset->Jobset());
	}
	jobset = nullptr;
	return rval;
}

// called by InitJobQueue if jobsets are enabled
// to add this job to the jobset member count and aggregates
//
bool JobSets::addJobToSet(JobQueueJob & job, std::vector<unsigned int> & new_ids)
{
	if (job.IsJob() || job.IsCluster()) {
		// if the job has a set_id of 0, that means "look it up, creating the set if needed"
		if ( ! job.set_id) {
			std::string setName;
			if (!job.LookupString(ATTR_JOB_SET_NAME, setName)) {
				// uncomment to use clusterid as default setname
				// setName = std::to_string(job.jid.cluster);
			}
			if (setName.empty()) {
				// remember this job is not in a set for fast bailout next time
				job.set_id = -1;
				return false;
			}
			job.set_id = getOrCreateSet(setName, job.ownerinfo->name, new_ids);
		}

		JobQueueJobSet * set = getSet(job);
		if (!set) {
			// nothing to do, this job is not a member of a set
			if (job.set_id > 0) {
				// hmm. job has a setid, but that set does not exist
			}
			return false;
		}
		set->member_count += 1;
		set->Assign(ATTR_REF_COUNT, set->member_count);
		if (job.IsJob()) {
			IncrementLiveJobCounter(set->jobStatusAggregates, job.Universe(), job.Status(), 1);
		}
		return true;
	}
	return false;
}

void JobSets::status_change(JobQueueJob & job, int new_status)
{
	if ( ! job.IsJob())
		return;

	JobQueueJobSet * jobset = getSet(job);
	if (jobset) {
		IncrementLiveJobCounter(jobset->jobStatusAggregates, job.Universe(), job.Status(), -1);
		IncrementLiveJobCounter(jobset->jobStatusAggregates, job.Universe(), new_status, 1);

		if (IsDebugCatAndVerbosity(D_JOB | D_VERBOSE)) {
			ClassAd s;
			jobset->jobStatusAggregates.publish(s, "Live");
			MergeClassAds(&s, jobset, true);
			std::string name;
			if ( ! jobset->LookupString(ATTR_JOB_SET_NAME, name)) { name = std::to_string(jobset->Jobset()); }
			dprintf(D_JOB | D_VERBOSE, "Updating JobSet %s (id=%u) after status change in job %d.%d to be:\n",
				name.c_str(), jobset->Jobset(), job.jid.cluster, job.jid.proc);
			dPrintAd(D_JOB, s);
		}
	}
}

bool JobSets::removeJobFromSet(JobQueueJob & job)
{
	if (job.set_id <= 0) return true;

	JobQueueJobSet* jobset = getSet(job);
	if ( ! jobset) {
		// if job is not in a set, we don't necessarily create one ;)
		return true;
	}

	job.set_id = -1;

	// update historical counters now to disk, as this job is leaving
	// We need a separate count of jobs that are no longer in the set
	// This is used to initialized the
	if (jobset) {
		if (job.IsJob() && job.Status() >= REMOVED && job.Status() <= COMPLETED) {
			static const char * const attrs[]{
				ATTR_TOTAL_REMOVED_JOBS, "Scheduler" ATTR_TOTAL_REMOVED_JOBS,
				ATTR_TOTAL_COMPLETED_JOBS, "Scheduler" ATTR_TOTAL_COMPLETED_JOBS,
			};
			int ix = 2 * (job.Status() - REMOVED) + (job.Universe() == CONDOR_UNIVERSE_SCHEDULER) ? 1 : 0;
			const char * attr = attrs[ix];
			int val = 0;
			jobset->LookupInteger(attr, val);
			val++;
			SetSecureAttributeInt(jobset->jid.cluster, jobset->jid.proc, attr, val);
		}

		if (jobset->member_count > 0) {
			jobset->member_count -= 1;
			jobset->Assign(ATTR_REF_COUNT, jobset->member_count);
		}

		// Check if time to destroy this set...
		garbageCollectSet(jobset);
	}

	return true;
}

bool JobSets::garbageCollectSet(JobQueueJobSet* & jobset)
{
	// TODO: implement other garbage collection policies
	if (jobset &&
		jobset->member_count == 0 &&
		jobset->garbagePolicy == JobQueueJobSet::garbagePolicyEnum::immediateAfterEmpty)
	{
		removeSet(jobset);
		return true;
	}
	return false;
}

<|endoftext|>


<commit_before>#include "IPAddress.h"

IPAddress::IPAddress() {
    this->init(IPV4);
}

IPAddress::IPAddress(const int version) {
    this->init(version);
}

IPAddress::IPAddress(const int version, const char* address) {
    string saddress(address);
    this->init(version);
    this->data_from_string(address);
}

IPAddress::IPAddress(const int version, const string &address) {
    this->init(version);
    this->data_from_string(address);
}

/**
 * Create IPAddress based on an IPv4 address, converting to network
 * byte-order for storing internally.
 *
 * data: IPv4 address in host byte-order
 */
IPAddress::IPAddress(const uint32_t data) {
    this->init(IPV4);
    uint32_t moddata = htonl(data);
    memcpy(this->data, &moddata, this->length);
}

IPAddress::IPAddress(const IPAddress &other) {
    this->init(other.getVersion());
    other.toArray(this->data);
}

IPAddress::IPAddress(const int version, const uint8_t* data) {
    this->init(version);
    memcpy(this->data, data, this->length);
}

IPAddress::IPAddress(const struct in_addr *data) {
    this->init(IPV4);
    memcpy(this->data, data, this->length);
}

IPAddress::IPAddress(const struct in6_addr* data) {
    this->init(IPV6);
    memcpy(this->data, data, this->length);
}

IPAddress::IPAddress(const int version, int prefix_len) {
    this->init(version);

    for (size_t i = 0; i < this->length; i++) {
        if (prefix_len >= 8){
            this->data[i] = 0xff;
            prefix_len -= 8;
        } else if (prefix_len == 0) {
            this->data[i] = 0;
        } else {
            this->data[i] = ((1 << prefix_len) - 1 ) << (8 - prefix_len);
            prefix_len = 0;
        }
    }
}

IPAddress::~IPAddress() {
    delete this->data;
}

IPAddress& IPAddress::operator=(const IPAddress &other) {
    if (this != &other) {
        delete data;
        this->init(other.getVersion());
        other.toArray(this->data);
    }
    return *this;
}

bool IPAddress::operator==(const IPAddress &other) const {
    return (this->getVersion() == other.getVersion() and
        (memcmp(other.data, this->data, this->length) == 0));
}

/**
 * Returns the in_addr (or in6_addr) structure for this IPAddress
 *
 * The caller is responsible for deleting the returned struct.
 */
void* IPAddress::toInAddr() const {
    void* n;
    if (this->version == IPV4) {
        n = new in_addr;
        memcpy(&((in_addr*) n)->s_addr, this->data, this->length);
    }
    else if (this->version == IPV6) {
        n = new in6_addr;
        memcpy(((in6_addr*) n)->s6_addr, this->data, this->length);
    }
    return n;
}

void IPAddress::toArray(uint8_t* array) const {
    memcpy(array, this->data, this->length);
}

/**
 * Return the IPv4 address in host byte-order
 *
 * Returns 0 on failure (not IPv4 address).
 */
uint32_t IPAddress::toUint32() const {
    if (this->version == IPV4) {
        return ntohl(((in_addr*) this->toInAddr())->s_addr);
    }
    else {
        return 0;
    }
}

string IPAddress::toString() const {
    char* dst = NULL;
    void* n = this->toInAddr();
    if (this->version == IPV4) {
        dst = new char[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, n, dst, INET_ADDRSTRLEN);
        delete (in_addr*) n;
    }
    else if (this->version == IPV6) {
        dst = new char[INET6_ADDRSTRLEN];
        inet_ntop(AF_INET6, n, dst, INET6_ADDRSTRLEN);
        delete (in6_addr*) n;
    }
    string result = string(dst != NULL? dst : "");
    delete dst;
    return result;
}

int IPAddress::toPrefixLen() const {
	int n = 0;

    // Count the number of set bits starting from the MSB.
    for (size_t i = 0; i < this->length; i++){
        if (data[i] == 0xff){
            n += 8;
        } else {
            for (int mask_bit = 1 << 7; data[i] & mask_bit; mask_bit >>= 1) {
                n++;
            }
            break;
        }
    }

	return n;
}

int IPAddress::toCIDRMask() const {
    return this->toPrefixLen();
}

int IPAddress::getVersion() const {
    return this->version;
}

size_t IPAddress::getLength() const {
    return this->length;
}

void IPAddress::init(const int version) {
    this->version = version;
    if (this->version == IPV4) {
        this->length = 4;
    } else if (this->version == IPV6) {
        this->length = 16;
    } else {
        throw "Constructing IPAddress with invalid version!";
    }
    this->data = new uint8_t[this->length];
}

void IPAddress::data_from_string(const string &address) {
    if (this->version == IPV4) {
        struct in_addr n;
        inet_pton(AF_INET, address.c_str(), &n);
        memcpy(this->data, &n.s_addr, 4);
    }
    else if (this->version == IPV6) {
        struct in6_addr n6;
        inet_pton(AF_INET6, address.c_str(), &n6);
        memcpy(this->data, &n6.s6_addr, 16);
    }
}
<commit_msg>Add null-checks to IPAddress constructors<commit_after>#include "IPAddress.h"

IPAddress::IPAddress() {
    this->init(IPV4);
}

IPAddress::IPAddress(const int version) {
    this->init(version);
}

IPAddress::IPAddress(const int version, const char* address) {
    if (address == NULL) {
        throw "Invalid IPAddress string!";
    }
    string saddress(address);
    this->init(version);
    this->data_from_string(address);
}

IPAddress::IPAddress(const int version, const string &address) {
    this->init(version);
    this->data_from_string(address);
}

/**
 * Create IPAddress based on an IPv4 address, converting to network
 * byte-order for storing internally.
 *
 * data: IPv4 address in host byte-order
 */
IPAddress::IPAddress(const uint32_t data) {
    this->init(IPV4);
    uint32_t moddata = htonl(data);
    memcpy(this->data, &moddata, this->length);
}

IPAddress::IPAddress(const IPAddress &other) {
    this->init(other.getVersion());
    other.toArray(this->data);
}

IPAddress::IPAddress(const int version, const uint8_t* data) {
    if (data == NULL) {
        throw "Invalid IPAddress data!";
    }
    this->init(version);
    memcpy(this->data, data, this->length);
}

IPAddress::IPAddress(const struct in_addr *data) {
    if (data == NULL) {
        throw "Invalid IPAddress data!";
    }
    this->init(IPV4);
    memcpy(this->data, data, this->length);
}

IPAddress::IPAddress(const struct in6_addr* data) {
    if (data == NULL) {
        throw "Invalid IPAddress data!";
    }
    this->init(IPV6);
    memcpy(this->data, data, this->length);
}

IPAddress::IPAddress(const int version, int prefix_len) {
    this->init(version);

    for (size_t i = 0; i < this->length; i++) {
        if (prefix_len >= 8){
            this->data[i] = 0xff;
            prefix_len -= 8;
        } else if (prefix_len == 0) {
            this->data[i] = 0;
        } else {
            this->data[i] = ((1 << prefix_len) - 1 ) << (8 - prefix_len);
            prefix_len = 0;
        }
    }
}

IPAddress::~IPAddress() {
    delete this->data;
}

IPAddress& IPAddress::operator=(const IPAddress &other) {
    if (this != &other) {
        delete data;
        this->init(other.getVersion());
        other.toArray(this->data);
    }
    return *this;
}

bool IPAddress::operator==(const IPAddress &other) const {
    return (this->getVersion() == other.getVersion() and
        (memcmp(other.data, this->data, this->length) == 0));
}

/**
 * Returns the in_addr (or in6_addr) structure for this IPAddress
 *
 * The caller is responsible for deleting the returned struct.
 */
void* IPAddress::toInAddr() const {
    void* n;
    if (this->version == IPV4) {
        n = new in_addr;
        memcpy(&((in_addr*) n)->s_addr, this->data, this->length);
    }
    else if (this->version == IPV6) {
        n = new in6_addr;
        memcpy(((in6_addr*) n)->s6_addr, this->data, this->length);
    }
    return n;
}

void IPAddress::toArray(uint8_t* array) const {
    memcpy(array, this->data, this->length);
}

/**
 * Return the IPv4 address in host byte-order
 *
 * Returns 0 on failure (not IPv4 address).
 */
uint32_t IPAddress::toUint32() const {
    if (this->version == IPV4) {
        return ntohl(((in_addr*) this->toInAddr())->s_addr);
    }
    else {
        return 0;
    }
}

string IPAddress::toString() const {
    char* dst = NULL;
    void* n = this->toInAddr();
    if (this->version == IPV4) {
        dst = new char[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, n, dst, INET_ADDRSTRLEN);
        delete (in_addr*) n;
    }
    else if (this->version == IPV6) {
        dst = new char[INET6_ADDRSTRLEN];
        inet_ntop(AF_INET6, n, dst, INET6_ADDRSTRLEN);
        delete (in6_addr*) n;
    }
    string result = string(dst != NULL? dst : "");
    delete dst;
    return result;
}

int IPAddress::toPrefixLen() const {
	int n = 0;

    // Count the number of set bits starting from the MSB.
    for (size_t i = 0; i < this->length; i++){
        if (data[i] == 0xff){
            n += 8;
        } else {
            for (int mask_bit = 1 << 7; data[i] & mask_bit; mask_bit >>= 1) {
                n++;
            }
            break;
        }
    }

	return n;
}

int IPAddress::toCIDRMask() const {
    return this->toPrefixLen();
}

int IPAddress::getVersion() const {
    return this->version;
}

size_t IPAddress::getLength() const {
    return this->length;
}

void IPAddress::init(const int version) {
    this->version = version;
    if (this->version == IPV4) {
        this->length = 4;
    } else if (this->version == IPV6) {
        this->length = 16;
    } else {
        throw "Constructing IPAddress with invalid version!";
    }
    this->data = new uint8_t[this->length];
}

void IPAddress::data_from_string(const string &address) {
    if (this->version == IPV4) {
        struct in_addr n;
        inet_pton(AF_INET, address.c_str(), &n);
        memcpy(this->data, &n.s_addr, 4);
    }
    else if (this->version == IPV6) {
        struct in6_addr n6;
        inet_pton(AF_INET6, address.c_str(), &n6);
        memcpy(this->data, &n6.s6_addr, 16);
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright 2011-2012 Gregory Banks
 *
 * 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.
 */
#ifndef __np_spiegel_mapping_hxx__
#define __np_spiegel_mapping_hxx__ 1

#include "np/spiegel/common.hxx"

namespace np {
namespace spiegel {

struct mapping_t : public np::util::zalloc
{
    mapping_t()
     :  offset_(0), size_(0), map_(0)
    {}
    mapping_t(unsigned long off, unsigned long sz, void *m)
     :  offset_(off), size_(sz), map_(m)
    {}
    ~mapping_t() {}

    unsigned long get_offset() const { return offset_; }
    void set_offset(unsigned long off) { offset_ = off; }

    unsigned long get_size() const { return size_; }
    void set_size(unsigned long sz) { size_ = sz; }

    void set_range(unsigned long off, unsigned long sz)
    {
	offset_ = off;
	size_ = sz;
    }

    bool is_mapped() const { return !!map_; }
    void set_map(void *m) { map_ = m; }

    unsigned long get_end() const { return offset_ + size_; }
    void set_end(unsigned long e) { size_ = e - offset_; }

    bool contains(const mapping_t &m) const
    {
	return (offset_ <= m.offset_ &&
		get_end() >= m.get_end());
    }
    bool contains(void *p) const
    {
	return (map_ <= p &&
		(void *)((char *)map_ + size_) >= p);
    }
    void map_from(const mapping_t &m)
    {
	map_ = (void *)((char *)m.map_ + offset_ - m.offset_);
    }

    int mmap(int fd, bool rw);
    int munmap();

    void expand_to_pages();

    static int compare_by_offset(const void *v1, const void *v2);

protected:
    unsigned long offset_;
    unsigned long size_;
    void *map_;
};

// close namespaces
}; };

#endif // __np_spiegel_mapping_hxx__
<commit_msg>Re-implement mapping_t::get_map()<commit_after>/*
 * Copyright 2011-2012 Gregory Banks
 *
 * 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.
 */
#ifndef __np_spiegel_mapping_hxx__
#define __np_spiegel_mapping_hxx__ 1

#include "np/spiegel/common.hxx"

namespace np {
namespace spiegel {

struct mapping_t : public np::util::zalloc
{
    mapping_t()
     :  offset_(0), size_(0), map_(0)
    {}
    mapping_t(unsigned long off, unsigned long sz, void *m)
     :  offset_(off), size_(sz), map_(m)
    {}
    ~mapping_t() {}

    unsigned long get_offset() const { return offset_; }
    void set_offset(unsigned long off) { offset_ = off; }

    unsigned long get_size() const { return size_; }
    void set_size(unsigned long sz) { size_ = sz; }

    void set_range(unsigned long off, unsigned long sz)
    {
	offset_ = off;
	size_ = sz;
    }

    bool is_mapped() const { return !!map_; }
    void set_map(void *m) { map_ = m; }
    void *get_map() const { return map_; }

    unsigned long get_end() const { return offset_ + size_; }
    void set_end(unsigned long e) { size_ = e - offset_; }

    bool contains(const mapping_t &m) const
    {
	return (offset_ <= m.offset_ &&
		get_end() >= m.get_end());
    }
    bool contains(void *p) const
    {
	return (map_ <= p &&
		(void *)((char *)map_ + size_) >= p);
    }
    void map_from(const mapping_t &m)
    {
	map_ = (void *)((char *)m.map_ + offset_ - m.offset_);
    }

    int mmap(int fd, bool rw);
    int munmap();

    void expand_to_pages();

    static int compare_by_offset(const void *v1, const void *v2);

protected:
    unsigned long offset_;
    unsigned long size_;
    void *map_;
};

// close namespaces
}; };

#endif // __np_spiegel_mapping_hxx__
<|endoftext|>


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

#include "SkThreadedBMPDevice.h"

#include "SkPath.h"
#include "SkSpecialImage.h"
#include "SkTaskGroup.h"
#include "SkVertices.h"

// Calling init(j, k) would initialize the j-th element on k-th thread. It returns false if it's
// already initiailized.
bool SkThreadedBMPDevice::DrawQueue::initColumn(int column, int thread) {
    return fElements[column].tryInitOnce(&fThreadAllocs[thread]);
}

// Calling work(i, j, k) would draw j-th element the i-th tile on k-th thead. If the element still
// needs to be initialized, drawFn will return false without drawing.
bool SkThreadedBMPDevice::DrawQueue::work2D(int row, int column, int thread) {
    return fElements[column].tryDraw(fDevice->fTileBounds[row], &fThreadAllocs[thread]);
}

void SkThreadedBMPDevice::DrawQueue::reset() {
    if (fTasks) {
        fTasks->finish();
    }

    fThreadAllocs.reset(fDevice->fThreadCnt);
    fSize = 0;

    // using TaskGroup2D = SkSpinningTaskGroup2D;
    using TaskGroup2D = SkFlexibleTaskGroup2D;

    fTasks.reset(new TaskGroup2D(this, fDevice->fTileCnt, fDevice->fExecutor,
                                 fDevice->fThreadCnt));
    fTasks->start();
}

SkThreadedBMPDevice::SkThreadedBMPDevice(const SkBitmap& bitmap,
                                         int tiles,
                                         int threads,
                                         SkExecutor* executor)
        : INHERITED(bitmap)
        , fTileCnt(tiles)
        , fThreadCnt(threads <= 0 ? tiles : threads)
        , fQueue(this)
{
    if (executor == nullptr) {
        fInternalExecutor = SkExecutor::MakeFIFOThreadPool(fThreadCnt);
        executor = fInternalExecutor.get();
    }
    fExecutor = executor;

    // Tiling using stripes for now; we'll explore better tiling in the future.
    int h = (bitmap.height() + fTileCnt - 1) / SkTMax(fTileCnt, 1);
    int w = bitmap.width();
    int top = 0;
    for(int tid = 0; tid < fTileCnt; ++tid, top += h) {
        fTileBounds.push_back(SkIRect::MakeLTRB(0, top, w, top + h));
    }
    fQueue.reset();
}

void SkThreadedBMPDevice::flush() {
    fQueue.reset();
    fAlloc.reset();
}

SkThreadedBMPDevice::DrawState::DrawState(SkThreadedBMPDevice* dev) {
    // we need fDst to be set, and if we're actually drawing, to dirty the genID
    if (!dev->accessPixels(&fDst)) {
        // NoDrawDevice uses us (why?) so we have to catch this case w/ no pixels
        fDst.reset(dev->imageInfo(), nullptr, 0);
    }
    fMatrix = dev->ctm();
    fMatrix.getType(); // make it thread safe
    fRC = dev->fRCStack.rc();
}

SkDraw SkThreadedBMPDevice::DrawState::getDraw() const {
    SkDraw draw;
    draw.fDst = fDst;
    draw.fMatrix = &fMatrix;
    draw.fRC = &fRC;
    return draw;
}

SkThreadedBMPDevice::TileDraw::TileDraw(const DrawState& ds, const SkIRect& tileBounds)
        : fTileRC(ds.fRC) {
    fDst = ds.fDst;
    fMatrix = &ds.fMatrix;
    fTileRC.op(tileBounds, SkRegion::kIntersect_Op);
    fRC = &fTileRC;
}

static inline SkRect get_fast_bounds(const SkRect& r, const SkPaint& p) {
    SkRect result;
    if (p.canComputeFastBounds()) {
        result = p.computeFastBounds(r, &result);
    } else {
        result = SkRectPriv::MakeLargest();
    }
    return result;
}

void SkThreadedBMPDevice::drawPaint(const SkPaint& paint) {
    SkRect drawBounds = SkRectPriv::MakeLargest();
    fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
        TileDraw(ds, tileBounds).drawPaint(paint);
    });
}

void SkThreadedBMPDevice::drawPoints(SkCanvas::PointMode mode, size_t count,
        const SkPoint pts[], const SkPaint& paint) {
    SkPoint* clonedPts = this->cloneArray(pts, count);
    SkRect drawBounds = SkRectPriv::MakeLargest(); // TODO tighter drawBounds
    fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
        TileDraw(ds, tileBounds).drawPoints(mode, count, clonedPts, paint, nullptr);
    });
}

void SkThreadedBMPDevice::drawRect(const SkRect& r, const SkPaint& paint) {
    SkRect drawBounds = get_fast_bounds(r, paint);
    fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
        TileDraw(ds, tileBounds).drawRect(r, paint);
    });
}

void SkThreadedBMPDevice::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
#ifdef SK_IGNORE_BLURRED_RRECT_OPT
    SkPath  path;

    path.addRRect(rrect);
    // call the VIRTUAL version, so any subclasses who do handle drawPath aren't
    // required to override drawRRect.
    this->drawPath(path, paint, nullptr, false);
#else
    SkRect drawBounds = get_fast_bounds(rrect.getBounds(), paint);
    fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
        TileDraw(ds, tileBounds).drawRRect(rrect, paint);
    });
#endif
}

void SkThreadedBMPDevice::drawPath(const SkPath& path, const SkPaint& paint,
        const SkMatrix* prePathMatrix, bool pathIsMutable) {
    SkRect drawBounds = path.isInverseFillType() ? SkRectPriv::MakeLargest()
                                                 : get_fast_bounds(path.getBounds(), paint);
    // when path is small, init-once has too much overhead; init-once also can't handle mask filter
    if (path.countVerbs() < 4 || paint.getMaskFilter()) {
        fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds) {
            TileDraw(ds, tileBounds).drawPath(path, paint, prePathMatrix, false);
        });
    } else {
        fQueue.push(drawBounds, [=](SkArenaAlloc* alloc, DrawElement* elem) {
            SkInitOnceData data = {alloc, elem};
            elem->getDraw().drawPath(path, paint, prePathMatrix, false, false, nullptr, &data);
        });
    }
}

SkBitmap SkThreadedBMPDevice::snapBitmap(const SkBitmap& bitmap) {
    // We can't use bitmap.isImmutable() because it could be temporarily immutable
    // TODO(liyuqian): use genID to reduce the copy frequency
    SkBitmap snap;
    snap.allocPixels(bitmap.info());
    bitmap.readPixels(snap.pixmap());
    return snap;
}

void SkThreadedBMPDevice::drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
                                     const SkRect* dstOrNull, const SkPaint& paint) {
    SkRect drawBounds;
    SkRect* clonedDstOrNull = nullptr;
    if (dstOrNull == nullptr) {
        drawBounds = SkRect::MakeWH(bitmap.width(), bitmap.height());
        matrix.mapRect(&drawBounds);
    } else {
        drawBounds = *dstOrNull;
        clonedDstOrNull = fAlloc.make<SkRect>(*dstOrNull);
    }

    SkBitmap snap = this->snapBitmap(bitmap);
    fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
        SkBitmap local = snap; // bitmap is not thread safe; copy a local one.
        TileDraw(ds, tileBounds).drawBitmap(local, matrix, clonedDstOrNull, paint);
    });
}

void SkThreadedBMPDevice::drawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
        const SkRect& dst, const SkPaint& paint, SkCanvas::SrcRectConstraint constraint) {
    // SkBitmapDevice::drawBitmapRect may use shader and drawRect. In that case, we need to snap
    // the bitmap here because we won't go into SkThreadedBMPDevice::drawBitmap.
    SkBitmap snap = this->snapBitmap(bitmap);
    this->SkBitmapDevice::drawBitmapRect(snap, src, dst, paint, constraint);
}

void SkThreadedBMPDevice::drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& paint) {
    SkRect drawBounds = SkRect::MakeXYWH(x, y, bitmap.width(), bitmap.height());
    SkBitmap snap = this->snapBitmap(bitmap);
    fQueue.push<false>(drawBounds, [=](SkArenaAlloc*, const DrawState& ds,
                                       const SkIRect& tileBounds){
        SkBitmap local = snap; // bitmap is not thread safe; copy a local one.
        TileDraw(ds, tileBounds).drawSprite(local, x, y, paint);
    });
}

void SkThreadedBMPDevice::drawPosText(const void* text, size_t len, const SkScalar xpos[],
        int scalarsPerPos, const SkPoint& offset, const SkPaint& paint) {
    char* clonedText = this->cloneArray((const char*)text, len);
    SkScalar* clonedXpos = this->cloneArray(xpos, paint.countText(text, len) * scalarsPerPos);
    SkRect drawBounds = SkRectPriv::MakeLargest(); // TODO tighter drawBounds
    SkSurfaceProps prop(SkBitmapDeviceFilteredSurfaceProps(fBitmap, paint, this->surfaceProps())());
    fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
        TileDraw(ds, tileBounds).drawPosText(clonedText, len, clonedXpos, scalarsPerPos, offset,
                                             paint, &prop);
    });
}

void SkThreadedBMPDevice::drawVertices(const SkVertices* vertices, const SkMatrix* bones,
                                       int boneCount, SkBlendMode bmode, const SkPaint& paint) {
    const sk_sp<SkVertices> verts = sk_ref_sp(vertices);  // retain vertices until flush
    SkRect drawBounds = SkRectPriv::MakeLargest(); // TODO tighter drawBounds

    // Make the bone matrices thread-safe.
    for (int i = 0; i < boneCount; i ++) {
        bones[i].getType();
    }

    fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
        TileDraw(ds, tileBounds).drawVertices(verts->mode(), verts->vertexCount(),
                                              verts->positions(), verts->texCoords(),
                                              verts->colors(), verts->boneIndices(),
                                              verts->boneWeights(), bmode, verts->indices(),
                                              verts->indexCount(), paint, bones, boneCount);
    });
}

sk_sp<SkSpecialImage> SkThreadedBMPDevice::snapSpecial() {
    this->flush();
    return this->makeSpecial(fBitmap);
}
<commit_msg>fixed ASAN dm errors<commit_after>/*
 * Copyright 2017 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkThreadedBMPDevice.h"

#include "SkPath.h"
#include "SkSpecialImage.h"
#include "SkTaskGroup.h"
#include "SkVertices.h"

// Calling init(j, k) would initialize the j-th element on k-th thread. It returns false if it's
// already initiailized.
bool SkThreadedBMPDevice::DrawQueue::initColumn(int column, int thread) {
    return fElements[column].tryInitOnce(&fThreadAllocs[thread]);
}

// Calling work(i, j, k) would draw j-th element the i-th tile on k-th thead. If the element still
// needs to be initialized, drawFn will return false without drawing.
bool SkThreadedBMPDevice::DrawQueue::work2D(int row, int column, int thread) {
    return fElements[column].tryDraw(fDevice->fTileBounds[row], &fThreadAllocs[thread]);
}

void SkThreadedBMPDevice::DrawQueue::reset() {
    if (fTasks) {
        fTasks->finish();
    }

    fThreadAllocs.reset(fDevice->fThreadCnt);
    fSize = 0;

    // using TaskGroup2D = SkSpinningTaskGroup2D;
    using TaskGroup2D = SkFlexibleTaskGroup2D;

    fTasks.reset(new TaskGroup2D(this, fDevice->fTileCnt, fDevice->fExecutor,
                                 fDevice->fThreadCnt));
    fTasks->start();
}

SkThreadedBMPDevice::SkThreadedBMPDevice(const SkBitmap& bitmap,
                                         int tiles,
                                         int threads,
                                         SkExecutor* executor)
        : INHERITED(bitmap)
        , fTileCnt(tiles)
        , fThreadCnt(threads <= 0 ? tiles : threads)
        , fQueue(this)
{
    if (executor == nullptr) {
        fInternalExecutor = SkExecutor::MakeFIFOThreadPool(fThreadCnt);
        executor = fInternalExecutor.get();
    }
    fExecutor = executor;

    // Tiling using stripes for now; we'll explore better tiling in the future.
    int h = (bitmap.height() + fTileCnt - 1) / SkTMax(fTileCnt, 1);
    int w = bitmap.width();
    int top = 0;
    for(int tid = 0; tid < fTileCnt; ++tid, top += h) {
        fTileBounds.push_back(SkIRect::MakeLTRB(0, top, w, top + h));
    }
    fQueue.reset();
}

void SkThreadedBMPDevice::flush() {
    fQueue.reset();
    fAlloc.reset();
}

SkThreadedBMPDevice::DrawState::DrawState(SkThreadedBMPDevice* dev) {
    // we need fDst to be set, and if we're actually drawing, to dirty the genID
    if (!dev->accessPixels(&fDst)) {
        // NoDrawDevice uses us (why?) so we have to catch this case w/ no pixels
        fDst.reset(dev->imageInfo(), nullptr, 0);
    }
    fMatrix = dev->ctm();
    fMatrix.getType(); // make it thread safe
    fRC = dev->fRCStack.rc();
}

SkDraw SkThreadedBMPDevice::DrawState::getDraw() const {
    SkDraw draw;
    draw.fDst = fDst;
    draw.fMatrix = &fMatrix;
    draw.fRC = &fRC;
    return draw;
}

SkThreadedBMPDevice::TileDraw::TileDraw(const DrawState& ds, const SkIRect& tileBounds)
        : fTileRC(ds.fRC) {
    fDst = ds.fDst;
    fMatrix = &ds.fMatrix;
    fTileRC.op(tileBounds, SkRegion::kIntersect_Op);
    fRC = &fTileRC;
}

static inline SkRect get_fast_bounds(const SkRect& r, const SkPaint& p) {
    SkRect result;
    if (p.canComputeFastBounds()) {
        result = p.computeFastBounds(r, &result);
    } else {
        result = SkRectPriv::MakeLargest();
    }
    return result;
}

void SkThreadedBMPDevice::drawPaint(const SkPaint& paint) {
    SkRect drawBounds = SkRectPriv::MakeLargest();
    fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
        TileDraw(ds, tileBounds).drawPaint(paint);
    });
}

void SkThreadedBMPDevice::drawPoints(SkCanvas::PointMode mode, size_t count,
        const SkPoint pts[], const SkPaint& paint) {
    SkPoint* clonedPts = this->cloneArray(pts, count);
    SkRect drawBounds = SkRectPriv::MakeLargest(); // TODO tighter drawBounds
    fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
        TileDraw(ds, tileBounds).drawPoints(mode, count, clonedPts, paint, nullptr);
    });
}

void SkThreadedBMPDevice::drawRect(const SkRect& r, const SkPaint& paint) {
    SkRect drawBounds = get_fast_bounds(r, paint);
    fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
        TileDraw(ds, tileBounds).drawRect(r, paint);
    });
}

void SkThreadedBMPDevice::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
#ifdef SK_IGNORE_BLURRED_RRECT_OPT
    SkPath  path;

    path.addRRect(rrect);
    // call the VIRTUAL version, so any subclasses who do handle drawPath aren't
    // required to override drawRRect.
    this->drawPath(path, paint, nullptr, false);
#else
    SkRect drawBounds = get_fast_bounds(rrect.getBounds(), paint);
    fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
        TileDraw(ds, tileBounds).drawRRect(rrect, paint);
    });
#endif
}

void SkThreadedBMPDevice::drawPath(const SkPath& path, const SkPaint& paint,
        const SkMatrix* prePathMatrix, bool pathIsMutable) {
    SkRect drawBounds = path.isInverseFillType() ? SkRectPriv::MakeLargest()
                                                 : get_fast_bounds(path.getBounds(), paint);
    // when path is small, init-once has too much overhead; init-once also can't handle mask filter
    if (path.countVerbs() < 4 || paint.getMaskFilter()) {
        fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds) {
            TileDraw(ds, tileBounds).drawPath(path, paint, prePathMatrix, false);
        });
    } else {
        fQueue.push(drawBounds, [=](SkArenaAlloc* alloc, DrawElement* elem) {
            SkInitOnceData data = {alloc, elem};
            elem->getDraw().drawPath(path, paint, prePathMatrix, false, false, nullptr, &data);
        });
    }
}

SkBitmap SkThreadedBMPDevice::snapBitmap(const SkBitmap& bitmap) {
    // We can't use bitmap.isImmutable() because it could be temporarily immutable
    // TODO(liyuqian): use genID to reduce the copy frequency
    SkBitmap snap;
    snap.allocPixels(bitmap.info());
    bitmap.readPixels(snap.pixmap());
    return snap;
}

void SkThreadedBMPDevice::drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
                                     const SkRect* dstOrNull, const SkPaint& paint) {
    SkRect drawBounds;
    SkRect* clonedDstOrNull = nullptr;
    if (dstOrNull == nullptr) {
        drawBounds = SkRect::MakeWH(bitmap.width(), bitmap.height());
        matrix.mapRect(&drawBounds);
    } else {
        drawBounds = *dstOrNull;
        clonedDstOrNull = fAlloc.make<SkRect>(*dstOrNull);
    }

    SkBitmap snap = this->snapBitmap(bitmap);
    fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
        SkBitmap local = snap; // bitmap is not thread safe; copy a local one.
        TileDraw(ds, tileBounds).drawBitmap(local, matrix, clonedDstOrNull, paint);
    });
}

void SkThreadedBMPDevice::drawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
        const SkRect& dst, const SkPaint& paint, SkCanvas::SrcRectConstraint constraint) {
    // SkBitmapDevice::drawBitmapRect may use shader and drawRect. In that case, we need to snap
    // the bitmap here because we won't go into SkThreadedBMPDevice::drawBitmap.
    SkBitmap snap = this->snapBitmap(bitmap);
    this->SkBitmapDevice::drawBitmapRect(snap, src, dst, paint, constraint);
}

void SkThreadedBMPDevice::drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& paint) {
    SkRect drawBounds = SkRect::MakeXYWH(x, y, bitmap.width(), bitmap.height());
    SkBitmap snap = this->snapBitmap(bitmap);
    fQueue.push<false>(drawBounds, [=](SkArenaAlloc*, const DrawState& ds,
                                       const SkIRect& tileBounds){
        SkBitmap local = snap; // bitmap is not thread safe; copy a local one.
        TileDraw(ds, tileBounds).drawSprite(local, x, y, paint);
    });
}

void SkThreadedBMPDevice::drawPosText(const void* text, size_t len, const SkScalar xpos[],
        int scalarsPerPos, const SkPoint& offset, const SkPaint& paint) {
    char* clonedText = this->cloneArray((const char*)text, len);
    SkScalar* clonedXpos = this->cloneArray(xpos, paint.countText(text, len) * scalarsPerPos);
    SkRect drawBounds = SkRectPriv::MakeLargest(); // TODO tighter drawBounds
    SkSurfaceProps prop(SkBitmapDeviceFilteredSurfaceProps(fBitmap, paint, this->surfaceProps())());
    fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
        TileDraw(ds, tileBounds).drawPosText(clonedText, len, clonedXpos, scalarsPerPos, offset,
                                             paint, &prop);
    });
}

void SkThreadedBMPDevice::drawVertices(const SkVertices* vertices, const SkMatrix* bones,
                                       int boneCount, SkBlendMode bmode, const SkPaint& paint) {
    const sk_sp<SkVertices> verts = sk_ref_sp(vertices);  // retain vertices until flush
    SkRect drawBounds = SkRectPriv::MakeLargest(); // TODO tighter drawBounds

    // Make a copy of the bone matrices.
    SkMatrix* clonedBones = this->cloneArray(bones, boneCount);

    // Make the bone matrices thread-safe.
    for (int i = 0; i < boneCount; i ++) {
        clonedBones[i].getType();
    }

    fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){
        TileDraw(ds, tileBounds).drawVertices(verts->mode(), verts->vertexCount(),
                                              verts->positions(), verts->texCoords(),
                                              verts->colors(), verts->boneIndices(),
                                              verts->boneWeights(), bmode, verts->indices(),
                                              verts->indexCount(), paint, clonedBones, boneCount);
    });
}

sk_sp<SkSpecialImage> SkThreadedBMPDevice::snapSpecial() {
    this->flush();
    return this->makeSpecial(fBitmap);
}
<|endoftext|>


<commit_before>/***************************************************************************
*   Copyright © 2003 Unai Garro <ugarro@gmail.com>                        *
*   Copyright © 2003 Cyril Bosselut <bosselut@b1project.com>              *
*   Copyright © 2003 Jason Kivlighn <jkivlighn@gmail.com>                 *
*                                                                         *
*   This program is free software; you can redistribute it and/or modify  *
*   it under the terms of the GNU General Public License as published by  *
*   the Free Software Foundation; either version 2 of the License, or     *
*   (at your option) any later version.                                   *
***************************************************************************/

#include "selectunitdialog.h"

#include <kconfig.h>
#include <kglobal.h>
#include <klocale.h>
#include <kvbox.h>
#include <QVBoxLayout>

SelectUnitDialog::SelectUnitDialog( QWidget* parent, const UnitList &unitList, OptionFlag showEmpty )
		: KDialog( parent), m_showEmpty(showEmpty)
{
	setButtons(  KDialog::Ok | KDialog::Cancel );
	setDefaultButton( KDialog::Ok );
	setCaption(i18nc( "@title:window", "Choose Unit" ) );
	setModal( true );
	KVBox *page = new KVBox(this );
	setMainWidget( page );

	box = new Q3GroupBox( page );
	box->setTitle( i18nc( "@title:group", "Choose Unit" ) );
	box->setColumnLayout( 0, Qt::Vertical );
	QVBoxLayout *boxLayout = new QVBoxLayout( box->layout() );

	unitChooseView = new K3ListView( box );

	KConfigGroup config( KGlobal::config(), "Advanced" );
	bool show_id = config.readEntry( "ShowID", false );
	unitChooseView->addColumn( "Id" , show_id ? -1 : 0 );

	unitChooseView->addColumn( i18nc( "@title:column", "Unit" ) );
	unitChooseView->setSorting(1);
	unitChooseView->setGeometry( QRect( 5, 30, 180, 250 ) );
	unitChooseView->setAllColumnsShowFocus( true );
	boxLayout->addWidget( unitChooseView );

	resize( QSize( 200, 350 ) );

	loadUnits( unitList );
}

SelectUnitDialog::~SelectUnitDialog()
{}

int SelectUnitDialog::unitID( void )
{

	Q3ListViewItem * it;
	if ( ( it = unitChooseView->selectedItem() ) ) {
		return ( it->text( 0 ).toInt() );
	}
	else
		return ( -1 );
}

void SelectUnitDialog::loadUnits( const UnitList &unitList )
{
	for ( UnitList::const_iterator unit_it = unitList.begin(); unit_it != unitList.end(); ++unit_it ) {
		QString unitName = ( *unit_it ).name;
		if ( unitName.isEmpty() ) {
			if ( m_showEmpty == ShowEmptyUnit )
				unitName = " "+i18nc("@item", "-No unit-");
			else
				continue;
		}

		( void ) new Q3ListViewItem( unitChooseView, QString::number( ( *unit_it ).id ), unitName );
	}
}

<commit_msg>Fix Krazy warnings: doublequote_chars - src/dialogs/selectunitdialog.cpp<commit_after>/***************************************************************************
*   Copyright © 2003 Unai Garro <ugarro@gmail.com>                        *
*   Copyright © 2003 Cyril Bosselut <bosselut@b1project.com>              *
*   Copyright © 2003 Jason Kivlighn <jkivlighn@gmail.com>                 *
*                                                                         *
*   This program is free software; you can redistribute it and/or modify  *
*   it under the terms of the GNU General Public License as published by  *
*   the Free Software Foundation; either version 2 of the License, or     *
*   (at your option) any later version.                                   *
***************************************************************************/

#include "selectunitdialog.h"

#include <kconfig.h>
#include <kglobal.h>
#include <klocale.h>
#include <kvbox.h>
#include <QVBoxLayout>

SelectUnitDialog::SelectUnitDialog( QWidget* parent, const UnitList &unitList, OptionFlag showEmpty )
		: KDialog( parent), m_showEmpty(showEmpty)
{
	setButtons(  KDialog::Ok | KDialog::Cancel );
	setDefaultButton( KDialog::Ok );
	setCaption(i18nc( "@title:window", "Choose Unit" ) );
	setModal( true );
	KVBox *page = new KVBox(this );
	setMainWidget( page );

	box = new Q3GroupBox( page );
	box->setTitle( i18nc( "@title:group", "Choose Unit" ) );
	box->setColumnLayout( 0, Qt::Vertical );
	QVBoxLayout *boxLayout = new QVBoxLayout( box->layout() );

	unitChooseView = new K3ListView( box );

	KConfigGroup config( KGlobal::config(), "Advanced" );
	bool show_id = config.readEntry( "ShowID", false );
	unitChooseView->addColumn( "Id" , show_id ? -1 : 0 );

	unitChooseView->addColumn( i18nc( "@title:column", "Unit" ) );
	unitChooseView->setSorting(1);
	unitChooseView->setGeometry( QRect( 5, 30, 180, 250 ) );
	unitChooseView->setAllColumnsShowFocus( true );
	boxLayout->addWidget( unitChooseView );

	resize( QSize( 200, 350 ) );

	loadUnits( unitList );
}

SelectUnitDialog::~SelectUnitDialog()
{}

int SelectUnitDialog::unitID( void )
{

	Q3ListViewItem * it;
	if ( ( it = unitChooseView->selectedItem() ) ) {
		return ( it->text( 0 ).toInt() );
	}
	else
		return ( -1 );
}

void SelectUnitDialog::loadUnits( const UnitList &unitList )
{
	for ( UnitList::const_iterator unit_it = unitList.begin(); unit_it != unitList.end(); ++unit_it ) {
		QString unitName = ( *unit_it ).name;
		if ( unitName.isEmpty() ) {
			if ( m_showEmpty == ShowEmptyUnit )
				unitName = ' '+i18nc("@item", "-No unit-");
			else
				continue;
		}

		( void ) new Q3ListViewItem( unitChooseView, QString::number( ( *unit_it ).id ), unitName );
	}
}

<|endoftext|>


<commit_before>/* dlvhex -- Answer-Set Programming with external interfaces.
 * Copyright (C) 2005, 2006, 2007 Roman Schindlauer
 * Copyright (C) 2006, 2007, 2008, 2009, 2010 Thomas Krennwallner
 * Copyright (C) 2009, 2010 Peter Schüller
 *
 * This file is part of dlvhex.
 *
 * dlvhex is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * dlvhex is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with dlvhex; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA.
 */

/**
 * @file EvalHeuristicEasy.cpp
 * @author Peter Schüller
 *
 * @brief Implementation of a nontrivial but simple evaluation heuristic.
 */

#include "dlvhex/EvalHeuristicEasy.hpp"
#include "dlvhex/Logger.hpp"

#include <boost/unordered_map.hpp>
#include <boost/graph/topological_sort.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/graph/properties.hpp>

DLVHEX_NAMESPACE_BEGIN

EvalHeuristicEasy::EvalHeuristicEasy(EvalGraphBuilder& builder):
  Base(builder)
{
}

EvalHeuristicEasy::~EvalHeuristicEasy()
{
}

typedef ComponentGraph::Component Component;
typedef ComponentGraph::ComponentIterator ComponentIterator;
typedef std::vector<Component> ComponentContainer;

// trivial strategy:
// do a topological sort of the tree
// build eval units in that order
void EvalHeuristicEasy::build()
{
  ComponentGraph& compgraph = builder.getComponentGraph();
  typedef ComponentGraph::Component Component;
  typedef ComponentGraph::ComponentSet ComponentSet;

  //
  // forall external atoms:
  // merge with rules that contain them if they don't depend on something else
  // TODO relax that condition
  //
  {
    ComponentIterator cit;
    for(cit = compgraph.getComponents().first; // do not use boost::tie here! the container is modified in the loop!
        cit != compgraph.getComponents().second; ++cit)
    {
      Component comp = *cit;
      if( compgraph.propsOf(comp).outerEatoms.empty() )
        continue;

      LOG("checking whether to collapse external component " << comp);

      // get successors
      ComponentSet collapse;
      ComponentGraph::SuccessorIterator sit, sit_end;
      for(boost::tie(sit, sit_end) = compgraph.getProvides(comp);
          sit != sit_end;
          ++sit)
      {
        Component succ = compgraph.sourceOf(*sit);

        // skip successors with eatoms
        if( !compgraph.propsOf(succ).outerEatoms.empty() )
          continue;

        LOG("found successor " << succ);

        ComponentGraph::PredecessorIterator pit, pit_end;
        boost::tie(pit, pit_end) = compgraph.getDependencies(succ);
        bool good = true;
        assert(pit != pit_end);
        if( compgraph.targetOf(*pit) != comp )
        {
          LOG("successor bad as it depends on other node " << compgraph.targetOf(*pit));
          good = false;
        }
        pit++;
        if( pit != pit_end )
        {
          good = false;
          LOG("successor bad as it depends on more nodes");
        }
        if( good )
          collapse.insert(succ);
      }

      // collapse if not nonempty
      if( !collapse.empty() )
      {
        collapse.insert(comp);
        Component c = compgraph.collapseComponents(collapse);
        LOG("collapse of " << printrange(collapse) << " yielded new component " << c);

        // restart loop after collapse
        cit = compgraph.getComponents().first;
      }
    }
  }

  //
  // forall components with only inner rules:
  // if they contain a guess, merge with children that do not contain a guess and no eatoms and do not depend on anything else
  // TODO relax that condition
  //
  {
    ComponentIterator cit = compgraph.getComponents().first;
    do
    {
      Component comp = *cit;
      if( !compgraph.propsOf(comp).outerEatoms.empty() )
      {
        cit++;
        continue;
      }

      LOG("checking whether to collapse internal-only component " << comp);

      // get successors
      ComponentSet collapse;
      ComponentGraph::SuccessorIterator sit, sit_end;
      for(boost::tie(sit, sit_end) = compgraph.getProvides(comp);
          sit != sit_end;
          ++sit)
      {
        Component succ = compgraph.sourceOf(*sit);

        // skip successors with eatoms
        if( !compgraph.propsOf(succ).outerEatoms.empty() )
          continue;

        LOG("found successor " << succ);

        ComponentGraph::PredecessorIterator pit, pit_end;
        boost::tie(pit, pit_end) = compgraph.getDependencies(succ);
        bool good = true;
        assert(pit != pit_end);
        if( compgraph.targetOf(*pit) != comp )
        {
          LOG("successor bad as it depends on other node " << compgraph.targetOf(*pit));
          good = false;
        }
        pit++;
        if( pit != pit_end )
        {
          good = false;
          LOG("successor bad as it depends on more nodes");
        }
        if( good )
          collapse.insert(succ);
      }

      if( !collapse.empty() )
      {
        // collapse! (decreases graph size)
        collapse.insert(comp);
        assert(collapse.size() > 1);
        Component c = compgraph.collapseComponents(collapse);
        LOG("collapse of " << printrange(collapse) << " yielded new component " << c);

        // restart loop after collapse
        cit = compgraph.getComponents().first;
      }
      else
      {
        // advance
        ++cit;
      }
    }
    while(cit != compgraph.getComponents().second);
  }

  //
  // create eval units using topological sort
  //

  {
    // we need a hash map, as component graph is no graph with vecS-storage
    //
    typedef boost::unordered_map<Component, boost::default_color_type> CompColorHashMap;
    typedef boost::associative_property_map<CompColorHashMap> CompColorMap;
    CompColorHashMap ccWhiteHashMap;
    // fill white hash map
    ComponentIterator cit, cit_end;
    for(boost::tie(cit, cit_end) = compgraph.getComponents();
        cit != cit_end; ++cit)
    {
      //boost::put(ccWhiteHashMap, *cit, boost::white_color);
      ccWhiteHashMap[*cit] = boost::white_color;
    }
    CompColorHashMap ccHashMap(ccWhiteHashMap);

    //
    // do topological sort
    //
    ComponentContainer comps;
    std::back_insert_iterator<ComponentContainer> compinserter(comps);
    boost::topological_sort(
        compgraph.getInternalGraph(),
        compinserter,
        boost::color_map(CompColorMap(ccHashMap)));

    for(ComponentContainer::const_iterator it = comps.begin();
        it != comps.end(); ++it)
    {
      EvalGraphBuilder::EvalUnit u = builder.createEvalUnit(*it);
      LOG("component " << *it << " became eval unit " << u);
    }
  }
}

DLVHEX_NAMESPACE_END

// Local Variables:
// mode: C++
// End:
<commit_msg>improving easy eval heuristic<commit_after>/* dlvhex -- Answer-Set Programming with external interfaces.
 * Copyright (C) 2005, 2006, 2007 Roman Schindlauer
 * Copyright (C) 2006, 2007, 2008, 2009, 2010 Thomas Krennwallner
 * Copyright (C) 2009, 2010 Peter Schüller
 *
 * This file is part of dlvhex.
 *
 * dlvhex is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * dlvhex is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with dlvhex; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA.
 */

/**
 * @file EvalHeuristicEasy.cpp
 * @author Peter Schüller
 *
 * @brief Implementation of a nontrivial but simple evaluation heuristic.
 */

#include "dlvhex/EvalHeuristicEasy.hpp"
#include "dlvhex/Logger.hpp"

#include <boost/unordered_map.hpp>
#include <boost/graph/topological_sort.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/graph/properties.hpp>

DLVHEX_NAMESPACE_BEGIN

EvalHeuristicEasy::EvalHeuristicEasy(EvalGraphBuilder& builder):
  Base(builder)
{
}

EvalHeuristicEasy::~EvalHeuristicEasy()
{
}

typedef ComponentGraph::Component Component;
typedef ComponentGraph::ComponentIterator ComponentIterator;
typedef std::vector<Component> ComponentContainer;

template<typename ComponentGraph, typename Sequence>
void topologicalSortOfComponents(const ComponentGraph& compgraph, Sequence& comps)
{
  // we need a hash map, as component graph is no graph with vecS-storage
  //
  typedef boost::unordered_map<Component, boost::default_color_type> CompColorHashMap;
  typedef boost::associative_property_map<CompColorHashMap> CompColorMap;
  CompColorHashMap ccWhiteHashMap;
  // fill white hash map
  ComponentIterator cit, cit_end;
  for(boost::tie(cit, cit_end) = compgraph.getComponents();
      cit != cit_end; ++cit)
  {
    //boost::put(ccWhiteHashMap, *cit, boost::white_color);
    ccWhiteHashMap[*cit] = boost::white_color;
  }
  CompColorHashMap ccHashMap(ccWhiteHashMap);

  //
  // do topological sort
  //
  std::back_insert_iterator<Sequence> compinserter(comps);
  boost::topological_sort(
      compgraph.getInternalGraph(),
      compinserter,
      boost::color_map(CompColorMap(ccHashMap)));
}

void EvalHeuristicEasy::build()
{
  ComponentGraph& compgraph = builder.getComponentGraph();
  typedef ComponentGraph::Component Component;
  typedef ComponentGraph::ComponentSet ComponentSet;

  //
  // forall external components e:
  // merge with all rules that
  // * do not contain external atoms
  // * depend on e
  // * do not depend on something e does not (transitively) depend on
  //
  {
    //ComponentContainer sortedcomps;
    //topologicalSortOfComponents(compgraph, sortedcomponents);

    ComponentIterator cit;
    for(cit = compgraph.getComponents().first; // do not use boost::tie here! the container is modified in the loop!
        cit != compgraph.getComponents().second; ++cit)
    {
      Component comp = *cit;
      if( compgraph.propsOf(comp).outerEatoms.empty() )
        continue;

      LOG("checking whether to collapse external component " << comp);

      // get predecessors (we could do transitive closure here)
      ComponentSet preds;
      ComponentGraph::PredecessorIterator pit, pit_end;
      for(boost::tie(pit, pit_end) = compgraph.getDependencies(comp);
          pit != pit_end; ++pit)
      {
        preds.insert(compgraph.targetOf(*pit));
      }
      // insert this component
      preds.insert(comp);

      // get successors
      ComponentSet collapse;
      ComponentGraph::SuccessorIterator sit, sit_end;
      for(boost::tie(sit, sit_end) = compgraph.getProvides(comp);
          sit != sit_end; ++sit)
      {
        Component succ = compgraph.sourceOf(*sit);

        // skip successors with eatoms
        if( !compgraph.propsOf(succ).outerEatoms.empty() )
          continue;

        LOG("found successor " << succ);

        ComponentGraph::PredecessorIterator pit, pit_end;
        bool good = true;
        for(boost::tie(pit, pit_end) = compgraph.getDependencies(succ);
            pit != pit_end; ++pit)
        {
          Component dependson = compgraph.targetOf(*pit);
          if( preds.find(dependson) == preds.end() )
          {
            LOG("successor bad as it depends on other node " << dependson);
            good = false;
            break;
          }
        }
        if( good )
        {
          collapse.insert(succ);
          preds.insert(succ);
        }
      }

      // collapse if not nonempty
      if( !collapse.empty() )
      {
        collapse.insert(comp);
        Component c = compgraph.collapseComponents(collapse);
        LOG("collapse of " << printrange(collapse) << " yielded new component " << c);

        // restart loop after collapse
        cit = compgraph.getComponents().first;
      }
    }
  }

  //
  // forall components with only inner rules:
  // merge with children that are no eatoms and do not depend on anything else
  //
  {
    ComponentIterator cit = compgraph.getComponents().first;
    do
    {
      Component comp = *cit;
      if( !compgraph.propsOf(comp).outerEatoms.empty() )
      {
        cit++;
        continue;
      }

      LOG("checking whether to collapse internal-only component " << comp);

      // get successors
      ComponentSet collapse;
      ComponentGraph::SuccessorIterator sit, sit_end;
      for(boost::tie(sit, sit_end) = compgraph.getProvides(comp);
          sit != sit_end;
          ++sit)
      {
        Component succ = compgraph.sourceOf(*sit);

        // skip successors with eatoms
        if( !compgraph.propsOf(succ).outerEatoms.empty() )
          continue;

        LOG("found successor " << succ);

        ComponentGraph::PredecessorIterator pit, pit_end;
        boost::tie(pit, pit_end) = compgraph.getDependencies(succ);
        bool good = true;
        assert(pit != pit_end);
        if( compgraph.targetOf(*pit) != comp )
        {
          LOG("successor bad as it depends on other node " << compgraph.targetOf(*pit));
          good = false;
        }
        pit++;
        if( pit != pit_end )
        {
          good = false;
          LOG("successor bad as it depends on more nodes");
        }
        if( good )
          collapse.insert(succ);
      }

      if( !collapse.empty() )
      {
        // collapse! (decreases graph size)
        collapse.insert(comp);
        assert(collapse.size() > 1);
        Component c = compgraph.collapseComponents(collapse);
        LOG("collapse of " << printrange(collapse) << " yielded new component " << c);

        // restart loop after collapse
        cit = compgraph.getComponents().first;
      }
      else
      {
        // advance
        ++cit;
      }
    }
    while(cit != compgraph.getComponents().second);
  }

  //
  // forall components with only inner constraints:
  // merge with all other constraint-only components
  //
  {
    ComponentSet collapse;

    ComponentIterator cit;
    for(cit = compgraph.getComponents().first; // do not use boost::tie here! the container is modified in the loop!
        cit != compgraph.getComponents().second; ++cit)
    {
      Component comp = *cit;
      if( compgraph.propsOf(comp).outerEatoms.empty() &&
          compgraph.propsOf(comp).innerRules.empty() )
        collapse.insert(comp);
    }

    if( !collapse.empty() )
    {
      // collapse! (decreases graph size)
      LOG("collapsing constraint-only nodes " << printrange(collapse));
      Component c = compgraph.collapseComponents(collapse);
    }
  }

  //
  // create eval units using topological sort
  //
  ComponentContainer sortedcomps;
  topologicalSortOfComponents(compgraph, sortedcomps);
  for(ComponentContainer::const_iterator it = sortedcomps.begin();
      it != sortedcomps.end(); ++it)
  {
    EvalGraphBuilder::EvalUnit u = builder.createEvalUnit(*it);
    LOG("component " << *it << " became eval unit " << u);
  }
}

DLVHEX_NAMESPACE_END

// Local Variables:
// mode: C++
// End:
<|endoftext|>