OverviewFilesCommitsIssuesDocsReleasesPeers

opencog-atomspace / opencog / atoms / base / Node.cc

186 lines · cc ref:
/*
 * opencog/atoms/base/Node.cc
 *
 * Copyright (C) 2008-2010 OpenCog Foundation
 * Copyright (C) 2002-2007 Novamente LLC
 * All Rights Reserved
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <iomanip>

#include <opencog/atoms/atom_types/NameServer.h>

#include "Node.h"

using namespace opencog;

// #define CHECK_UTF8
#ifdef CHECK_UTF8

#define MASK(mask,val) (((*ptr) & mask) == val)

static const char * first_invalid_utf8(const char* str)
{
	const char* ptr = str;
	while (*ptr)
	{
		if (MASK(0x80, 0x0)) {
			ptr++; continue;
		}
		if (MASK(0xe0, 0xc0)) {
			ptr++; if (not MASK(0xc0, 0x80)) return ptr;
			ptr++; continue;
		}
		if (MASK(0xf0, 0xe0)) {
			ptr++; if (not MASK(0xc0, 0x80)) return ptr;
			ptr++; if (not MASK(0xc0, 0x80)) return ptr;
			ptr++; continue;
		}
		if (MASK(0xf8, 0xf0)) {
			ptr++; if (not MASK(0xc0, 0x80)) return ptr;
			ptr++; if (not MASK(0xc0, 0x80)) return ptr;
			ptr++; if (not MASK(0xc0, 0x80)) return ptr;
			ptr++; continue;
		}
		return ptr;
	}
	return 0;
}
#endif // CHECK_UTF8

void Node::init()
{
    if (not nameserver().isA(_type, NODE))
        throw InvalidParamException(TRACE_INFO,
            "Node - Invalid node type '%d' %s.",
            _type, nameserver().getTypeName(_type).c_str());

#ifdef CHECK_UTF8
    const char *np = _name.c_str();
    const char *bad = first_invalid_utf8(np);
    if (0 != bad)
        throw InvalidParamException(TRACE_INFO,
            "Node - Invalid UTF8 at pos=%ld in node name %s.",
            bad-np, np);
#endif // CHECK_UTF8
}

/// Return a universally-unique string for each distinct node.
/// It needs to be fast (because it is used in performance-critical
/// code paths), it needs to be human-readable, and it must not have
/// any trailing newlines.
std::string Node::to_short_string(const std::string& indent) const
{
    size_t len = _name.length();
    std::string answer;
    answer.reserve(2*len);
    answer = indent + '(' + nameserver().getTypeShortName(_type) + " \"";
    for (unsigned int i=0; i < len; i++)
    {
        if ('"' == _name[i] or '\\' == _name[i])
        {
            answer += '\\';
            answer += _name[i];
        }
        else if ((unsigned char) _name[i] < 0x20)
        {
            // Characters that control printing.
            if ('\a' == _name[i]) answer += "\a";
            else if ('\b' == _name[i]) answer += "\\b";
            else if ('\t' == _name[i]) answer += "\\t";
            else if ('\n' == _name[i]) answer += "\\n";
            else if ('\v' == _name[i]) answer += "\\v";
            else if ('\f' == _name[i]) answer += "\\f";
            else if ('\r' == _name[i]) answer += "\\r";
            else answer += _name[i];
        }
        else
            answer += _name[i];
    }
    answer += '\"';

    answer += ')';
    return answer;
}

/// Return a universally-unique string for each distinct node.
/// It needs to be fast (because it is used in performance-critical
/// code paths), it needs to escape embedded quotes, and it must
/// not have any trailing newlines.
///
std::string Node::to_string_esc() const
{
    std::stringstream ss;

    ss << "(" << nameserver().getTypeName(_type) << " "
       << std::quoted(_name) << ")";

    return ss.str();
}

std::string Node::to_string(const std::string& indent) const
{
    return to_short_string(indent) + " ; " + id_to_string();
}

bool Node::operator==(const Atom& other) const
{
    // If other points to this, then have equality.
    if (this == &other) return true;

    // Rule out obvious mis-matches, based on the hash.
    if (get_hash() != other.get_hash()) return false;

    if (get_type() != other.get_type()) return false;
    return get_name() == other.get_name();
}

bool Node::operator<(const Atom& other) const
{
    if (this == &other) return false;

    ContentHash cht = get_hash();
    ContentHash cho = other.get_hash();
    if (cht != cho) return cht < cho;

    // We get to here only if the hashes are equal. This is
    // extremely unlikely; it requires a 64-bit hash collision.
    // Compare the contents directly, for this case.
    if (get_type() != other.get_type())
        return get_type() < other.get_type();

    return get_name() < other.get_name();
}

ContentHash Node::compute_hash() const
{
	ContentHash hsh = std::hash<std::string>()(get_name());

	// 1<<43 - 369 is a prime number.
	// The nameserver().getTypeHash() returns hash of the type string name,
	// and is thus independent of other types in the tree.
	hsh += (hsh<<5) + ((1ULL<<43)-369) * nameserver().getTypeHash(get_type());

	// Nodes will never have the MSB set.
	ContentHash mask = ~(((ContentHash) 1ULL) << (8*sizeof(ContentHash) - 1));
	hsh &= mask;

	if (Handle::INVALID_HASH == hsh) hsh -= 1;
	return hsh;
}