/* Copyright 2012. Bloomberg Finance L.P.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:  The above
 * copyright notice and this permission notice shall be included in all copies
 * or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */
// blpapi_name.h                                                      -*-C++-*-
#ifndef INCLUDED_BLPAPI_NAME
#define INCLUDED_BLPAPI_NAME

//@PURPOSE: Provide a representation of strings for use as container keys.
//
//@CLASSES:
// blpapi::Name: constant string in an efficient form for use as container keys
//
//@DESCRIPTION: This component implements a representation of a string which is
// efficient for use as a key in a container (constant-time hashing and
// ordering operations using the standard "intern string" technique).

#include <blpapi_defs.h>
#include <blpapi_types.h>

#include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif

BLPAPI_EXPORT
blpapi_Name_t *blpapi_Name_create(const char *nameString);

BLPAPI_EXPORT
void blpapi_Name_destroy(blpapi_Name_t *name);

BLPAPI_EXPORT
blpapi_Name_t *blpapi_Name_duplicate(const blpapi_Name_t *src);

BLPAPI_EXPORT
int blpapi_Name_equalsStr(const blpapi_Name_t *name, const char *string);

BLPAPI_EXPORT
const char *blpapi_Name_string(const blpapi_Name_t *name);

BLPAPI_EXPORT
size_t blpapi_Name_length(const blpapi_Name_t *name);

BLPAPI_EXPORT
blpapi_Name_t *blpapi_Name_findName(const char *nameString);

#ifdef __cplusplus
}

#include <cstring> // for strcmp
#include <iostream>
#include <utility> // for swap

namespace BloombergLP {
namespace blpapi {
// ==========
// class Name
// ==========

class Name {
    // 'Name' represents a string in a form which provides O(1) hashing
    // and comparison, independent of the string length.
    //
    // 'Name' objects are used to identify and access the classes which define
    // a schema - 'SchemaTypeDefinition', 'SchemaElementDefinition',
    // 'Constant', and 'ConstantList'.  They are also used to access the values
    // in 'Element' objects and 'Message' objects.
    //
    // The 'Name' class is an efficient substitute for a string when used as a
    // key, providing constant-time hashing and comparison.  Two 'Name'
    // objects constructed from strings for which 'strcmp()' would return 0
    // will always compare equal.
    //
    // The ordering of 'Name' objects (as defined by 'operator<(Name,Name)') is
    // consistent during a particular instance of a single application.
    // However, the ordering is not lexical and is not necessarily consistent
    // with the ordering of the same 'Name' objects in any other process.
    //
    // !'Name'! !objects! !should! !be! !initialized! !once! !and! !then!
    // !reused!.  Creating a 'Name' object from a 'const char*' involves
    // a search in a container requiring multiple string comparison operations.
    //
    // Note: Each 'Name' instance refers to an entry in a global static table.
    // 'Name' instances for identical strings will refer to the same data.
    // There is no provision for removing entries from the static table so
    // 'Name' objects should be used only when the set of input strings is
    // bounded.
    //
    // For example, creating a 'Name' for every possible field name and type in
    // a data model is reasonable (in fact, the API will do this whenever it
    // receives schema information).  Converting sequence numbers from incoming
    // messages to strings and creating a 'Name' from each one of those
    // strings, however, will cause the static table to grow in an unbounded
    // manner, and is tantamount to a memory leak.

    blpapi_Name_t *d_impl_p;

  public:
    // CLASS METHODS

    static Name findName(const char *nameString);
    // If a 'Name' already exists which matches the specified
    // 'nameString', then return a copy of that 'Name'; otherwise return a
    // 'Name' which will compare equal to a 'Name' created using the
    // default constructor. The behavior is undefined if 'nameString' does
    // not point to a null-terminated string.

    static bool hasName(const char *nameString);
    // Return 'true' if a 'Name' has been created which matches the
    // specified 'nameString'; otherwise return 'false'. The behavior is
    // undefined if 'nameString' does not point to a null-terminated
    // string.

    Name();
    // Construct an uninitialized 'Name'. An uninitialized 'Name' can be
    // assigned to, destroyed, or tested for equality. The behavior for all
    // other operations is undefined.

    Name(blpapi_Name_t *handle);

    Name(const Name& original);
    // Create a 'Name' object having the same value as the specified
    // 'original'.

    explicit Name(const char *nameString);
    // Construct a 'Name' from the specified 'nameString'. The behavior is
    // undefined unless 'nameString' is a null-terminated string. Note that
    // any null-terminated string can be specified, including an empty
    // string. Note also that constructing a 'Name' from a 'const char *'
    // is a relatively expensive operation. If a 'Name' will be used
    // repeatedly it is preferable to create it once and re-use (or copy)
    // the object.

    ~Name();
    // Destroy this object.

    // MANIPULATORS

    Name& operator=(const Name& rhs);
    // Assign to this object the value of the specified 'rhs', and return a
    // reference to this modifiable object.

    // ACCESSORS

    const char *string() const;
    // Return a pointer to the null-terminated string value of this 'Name'.
    // The pointer returned will be valid at least until main() exits.

    size_t length() const;
    // Return the length of the string value of this 'Name',
    // (excluding a terminating null). Note that 'name.length()' is
    // logically equivalent to 'strlen(name.string())', however the former
    // is potentially more efficient.

    size_t hash() const;
    // Return an integral value such that for two 'Name' objects 'a' and
    // 'b', if 'a == b' then 'a.hash() == b.hash()', and if 'a != b' then
    // it is unlikely that 'a.hash() == b.hash()'.

    blpapi_Name_t *impl() const;
};

// FREE OPERATORS
bool operator==(const Name& lhs, const Name& rhs);
// Return true if the specified 'lhs' and 'rhs' name objects have
// the same value, and false otherwise. Two 'Name' objects 'a' and 'b' have
// the same value if and only if 'strcmp(a.string(), b.string()) == 0'.

bool operator!=(const Name& lhs, const Name& rhs);
// Return false if the specified 'lhs' and 'rhs' name objects have the same
// value, and true otherwise. Two 'Name' objects 'a' and 'b' have the same
// value if and only if 'strcmp(a.string(), b.string()) == 0'.  Note that
// 'lhs != rhs' is equivalent to '!(lhs==rhs)'.

bool operator==(const Name& lhs, const char *rhs);
// Return true if the specified 'lhs' and 'rhs' have the same value, and
// false otherwise. A 'Name' object 'a' and a null-terminated string 'b'
// have the same value if and only if 'strcmp(a.string(), b) == 0'. The
// behavior is undefined unless 'rhs' is a null-terminated string.

bool operator!=(const Name& lhs, const char *rhs);
// Return false if the specified 'lhs' and 'rhs' have the same value, and
// true otherwise. A 'Name' object 'a' and a null-terminated string 'b'
// have the same value if and only if 'strcmp(a.string(), b) == 0'. The
// behavior is undefined unless 'rhs' is a null-terminated string.

bool operator==(const char *lhs, const Name& rhs);
// Return true if the specified 'lhs' and 'rhs' have the same value, and
// false otherwise. A 'Name' object 'a' and a null-terminated string 'b'
// have the same value if and only if 'strcmp(a.string(), b) == 0'. The
// behavior is undefined unless 'lhs' is a null-terminated string.

bool operator!=(const char *lhs, const Name& rhs);
// Return false if the specified 'lhs' and 'rhs' have the same value, and
// true otherwise. A 'Name' object 'a' and a null-terminated string 'b'
// have the same value if and only if 'strcmp(a.string(), b) == 0'. The
// behavior is undefined unless 'lhs' is a null-terminated string.

bool operator<(const Name& lhs, const Name& rhs);
// Return 'true' if the specified 'lhs' is ordered before the specified
// 'rhs', and 'false' otherwise. The ordering used is stable within the
// lifetime of a single process and is compatible with
// 'operator==(const Name&, const Name&)', however this order is neither
// guaranteed to be consistent across different processes (including
// repeated runs of the same process), nor guaranteed to be lexical (i.e.
// compatible with 'strcmp').

bool operator<=(const Name& lhs, const Name& rhs);
// Return 'false' if the specified 'rhs' is ordered before the specified
// 'lhs', and 'true' otherwise. The ordering used is stable within the
// lifetime of a single process and is compatible with
// 'operator==(const Name&, const Name&)', however this order is neither
// guaranteed to be consistent across different processes (including
// repeated runs of the same process), nor guaranteed to be lexical (i.e.
// compatible with 'strcmp').

bool operator>(const Name& lhs, const Name& rhs);
// Return 'true' if the specified 'rhs' is ordered before the specified
// 'lhs', and 'false' otherwise. The ordering used is stable within the
// lifetime of a single process and is compatible with
// 'operator==(const Name&, const Name&)', however this order is neither
// guaranteed to be consistent across different processes (including
// repeated runs of the same process), nor guaranteed to be lexical (i.e.
// compatible with 'strcmp').

bool operator>=(const Name& lhs, const Name& rhs);
// Return 'false' if the specified 'lhs' is ordered before the specified
// 'rhs', and 'true' otherwise. The ordering used is stable within the
// lifetime of a single process and is compatible with
// 'operator==(const Name&, const Name&)', however this order is neither
// guaranteed to be consistent across different processes (including
// repeated runs of the same process), nor guaranteed to be lexical (i.e.
// compatible with 'strcmp').

std::ostream& operator<<(std::ostream& stream, const Name& name);
// Write the value of the specified 'name' object to the specified output
// 'stream', and return a reference to 'stream'.  Note that this
// human-readable format is not fully specified and can change without
// notice.

//=============================================================================
//                           INLINE FUNCTION DEFINITIONS
//=============================================================================

// ----------
// class Name
// ----------

inline Name::Name(blpapi_Name_t *handle)
    : d_impl_p(handle)
{
}

inline Name::Name()
    : d_impl_p(0)
{
}

inline Name::Name(const Name& original)
    : d_impl_p(blpapi_Name_duplicate(original.d_impl_p))
{
}

inline Name::Name(const char *nameString)
{
    d_impl_p = blpapi_Name_create(nameString);
}

inline Name::~Name()
{
    if (d_impl_p) {
        blpapi_Name_destroy(d_impl_p);
    }
}

inline Name& Name::operator=(const Name& rhs)
{
    using std::swap;

    Name tmp(rhs);
    swap(tmp.d_impl_p, d_impl_p);
    return *this;
}

inline const char *Name::string() const
{
    return blpapi_Name_string(d_impl_p);
}

inline size_t Name::length() const { return blpapi_Name_length(d_impl_p); }

inline blpapi_Name_t *Name::impl() const { return d_impl_p; }

inline Name Name::findName(const char *nameString)
{
    return Name(blpapi_Name_findName(nameString));
}

inline bool Name::hasName(const char *nameString)
{
    return blpapi_Name_findName(nameString) ? true : false;
}

inline size_t Name::hash() const { return reinterpret_cast<size_t>(impl()); }

} // close namespace blpapi

inline bool blpapi::operator==(const Name& lhs, const Name& rhs)
{
    return (lhs.impl() == rhs.impl());
}

inline bool blpapi::operator!=(const Name& lhs, const Name& rhs)
{
    return !(lhs == rhs);
}

inline bool blpapi::operator==(const Name& lhs, const char *rhs)
{
    return blpapi_Name_equalsStr(lhs.impl(), rhs) != 0;
}

inline bool blpapi::operator!=(const Name& lhs, const char *rhs)
{
    return !(lhs == rhs);
}

inline bool blpapi::operator==(const char *lhs, const Name& rhs)
{
    return rhs == lhs;
}

inline bool blpapi::operator!=(const char *lhs, const Name& rhs)
{
    return !(rhs == lhs);
}

inline bool blpapi::operator<(const Name& lhs, const Name& rhs)
{
    return lhs.impl() < rhs.impl();
}

inline bool blpapi::operator<=(const Name& lhs, const Name& rhs)
{
    return !(rhs < lhs);
}

inline bool blpapi::operator>(const Name& lhs, const Name& rhs)
{
    return rhs < lhs;
}

inline bool blpapi::operator>=(const Name& lhs, const Name& rhs)
{
    return !(lhs < rhs);
}

inline std::ostream& blpapi::operator<<(std::ostream& stream, const Name& name)
{
    return stream << name.string();
}

} // close namespace BloombergLP

#endif // __cplusplus

#endif // #ifndef INCLUDED_BLPAPI_NAME