blob: 19bee0d0394e14b620915ec40f9c4eba48183df2 [file] [log] [blame]
/********************************************************************\
* uuid.h -- universally unique ID - as defined by ISO/IEC 9834-8 *
* *
* Copyright (C) 2008 Kenneth Laskoski *
* *
\********************************************************************/
/** @file uuid.h
@brief universally unique ID - as defined by ISO/IEC 9834-8:2005
@author Copyright (C) 2008 Kenneth Laskoski
based on work by
@author Copyright (C) 2006 Andy Tompkins
@author Copyright (C) 2000 Dave Peticolas <peticola@cs.ucdavis.edu>
@author Copyright (C) 1996, 1997, 1998 Theodore Ts'o
@author Copyright (C) 2004-2008 Ralf S. Engelschall <rse@engelschall.com>
Use, modification, and distribution are subject
to the Boost Software License, Version 1.0. (See accompanying file
LICENSE_1_0.txt or a copy at <http://www.boost.org/LICENSE_1_0.txt>.)
*/
#ifndef KL_UUID_H
#define KL_UUID_H
#include <istream>
#include <ostream>
#include <sstream>
#include "kashmir/randomstream.h"
#include <cstddef>
#include <stdexcept>
#include <algorithm>
#include "kashmir/iostate.h"
namespace kashmir {
namespace uuid {
/** @class uuid_t
@brief This class provides a C++ binding to the UUID type defined in
- ISO/IEC 9834-8:2005 | ITU-T Rec. X.667 - available at http://www.itu.int/ITU-T/studygroups/com17/oid.html
- IETF RFC 4122 - available at http://tools.ietf.org/html/rfc4122
These technically equivalent standards document the code below.
*/
class uuid_t
{
// an UUID is a string of 16 octets (128 bits)
typedef std::size_t size_type;
static const size_type size = 16;
static const size_type string_size = 36; // XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
// we use an unpacked representation, value_type may be larger than 8 bits,
// in which case every input operation must assert data[i] < 256 for i < 16
// note even char may be more than 8 bits in some particular platform
typedef unsigned char value_type;
value_type data[size];
// test for "nil" value
bool is_nil() const
{
for (size_type i = 0; i < size; ++i)
if (data[i])
return false;
return true;
}
public:
// default value is "nil"
uuid_t()
{
// std::fill(data, data+size, 0);
}
// destruction, copy and assignment
~uuid_t() {}
uuid_t(const uuid_t& rhs)
{
std::copy(rhs.data, rhs.data+size, data);
}
uuid_t& operator=(const uuid_t& rhs)
{
std::copy(rhs.data, rhs.data+size, data);
return *this;
}
// initialization from string literal
explicit uuid_t(const char* literal)
{
std::stringstream input(literal);
this->get(input);
}
// comparison operators define a total order
bool operator==(const uuid_t& rhs) const
{
return std::equal(data, data+size, rhs.data);
}
bool operator<(const uuid_t& rhs) const
{
return std::lexicographical_compare(data, data+size, rhs.data, rhs.data+size);
}
bool operator>(const uuid_t& rhs) const { return (rhs < *this); }
bool operator<=(const uuid_t& rhs) const { return !(rhs < *this); }
bool operator>=(const uuid_t& rhs) const { return !(*this < rhs); }
bool operator!=(const uuid_t& rhs) const { return !(*this == rhs); }
// some syntatic sugar using the is_nil method
bool operator!() const { return is_nil(); }
typedef bool (uuid_t::*bool_type)() const;
operator bool_type() const
{
return is_nil() ? 0 : &uuid_t::is_nil;
}
// insertion and extraction
template<class char_t, class char_traits>
std::basic_ostream<char_t, char_traits>& put(std::basic_ostream<char_t, char_traits>& os) const;
template<class char_t, class char_traits>
std::basic_istream<char_t, char_traits>& get(std::basic_istream<char_t, char_traits>& is);
// version 4 uuid extraction from a random stream
template<class user_impl>
user::randomstream<user_impl>& get(user::randomstream<user_impl>& is);
};
template<class char_t, class char_traits>
std::basic_ostream<char_t, char_traits>& uuid_t::put(std::basic_ostream<char_t, char_traits>& os) const
{
if (!os.good())
return os;
const typename std::basic_ostream<char_t, char_traits>::sentry ok(os);
if (ok)
{
ios_flags_saver flags(os);
basic_ios_fill_saver<char_t, char_traits> fill(os);
const std::streamsize width = os.width(0);
const std::streamsize mysize = string_size;
// right padding
if (flags.value() & (std::ios_base::right | std::ios_base::internal))
for (std::streamsize i = width; i > mysize; --i)
os << fill.value();
os << std::hex;
os.fill(os.widen('0'));
for (size_t i = 0; i < 16; ++i)
{
os.width(2);
os << static_cast<unsigned>(data[i]);
// if (i == 3 || i == 5 || i == 7 || i == 9)
// os << os.widen('-');
}
// left padding
if (flags.value() & std::ios_base::left)
for (std::streamsize i = width; i > mysize; --i)
os << fill.value();
}
return os;
}
template<class char_t, class char_traits>
std::basic_istream<char_t, char_traits>& uuid_t::get(std::basic_istream<char_t, char_traits>& is)
{
if (!is.good())
return is;
const typename std::basic_istream<char_t, char_traits>::sentry ok(is);
if (ok)
{
char_t hexdigits[16];
char_t* const npos = hexdigits+16;
typedef std::ctype<char_t> facet_t;
const facet_t& facet = std::use_facet<facet_t>(is.getloc());
const char* tmp = "0123456789abcdef";
facet.widen(tmp, tmp+16, hexdigits);
char_t c;
char_t* f;
for (size_t i = 0; i < size; ++i)
{
is >> c;
c = facet.tolower(c);
f = std::find(hexdigits, npos, c);
if (f == npos)
{
is.setstate(std::ios_base::failbit);
break;
}
data[i] = static_cast<value_type>(std::distance(hexdigits, f));
is >> c;
c = facet.tolower(c);
f = std::find(hexdigits, npos, c);
if (f == npos)
{
is.setstate(std::ios_base::failbit);
break;
}
data[i] <<= 4;
data[i] |= static_cast<value_type>(std::distance(hexdigits, f));
if (i == 3 || i == 5 || i == 7 || i == 9)
{
is >> c;
if (c != is.widen('-'))
{
is.setstate(std::ios_base::failbit);
break;
}
}
}
if (!is)
throw std::runtime_error("failed to extract valid uuid from stream.");
}
return is;
}
template<class user_impl>
user::randomstream<user_impl>& uuid_t::get(user::randomstream<user_impl>& is)
{
// get random bytes
char buffer[size];
is.read(buffer, size);
std::copy(buffer, buffer+size, data);
// this loop is necessary if uuid_t::value_type is larger than 8 bits,
// in order to maintain the invariant data[i] < 256 for i < 16
// note even char may be more than 8 bits in some particular platform
// for (size_t i = 0; i < size; ++i)
// data[i] &= 0xff;
// set variant
// should be 0b10xxxxxx
data[8] &= 0xbf; // 0b10111111
data[8] |= 0x80; // 0b10000000
// set version
// should be 0b0100xxxx
data[6] &= 0x4f; // 0b01001111
data[6] |= 0x40; // 0b01000000
return is;
}
template<class char_t, class char_traits>
inline std::basic_ostream<char_t, char_traits>& operator<<(std::basic_ostream<char_t, char_traits>& os, const uuid_t& uuid)
{
return uuid.put(os);
}
template<class char_t, class char_traits>
inline std::basic_istream<char_t, char_traits>& operator>>(std::basic_istream<char_t, char_traits>& is, uuid_t& uuid)
{
return uuid.get(is);
}
template<class user_impl>
inline user::randomstream<user_impl>& operator>>(user::randomstream<user_impl>& is, uuid_t& uuid)
{
return uuid.get(is);
}
} // namespace kashmir::uuid
using uuid::uuid_t;
} // namespace kashmir
#endif