uuid
uuid库是一个小的实用工具,可以表示和生成UUID。
UUID是Universally Unique Identifier的缩写,另一个别名是GUID,它是一个128位的数字(16字节),不需要有一个中央认证机构就可以创建全球唯一的标识符。例如"E4A0D7CE-9E6D-4E74-9E6D-7E749E6D7E74"就是一个UUID。
UUID可以用在很多地方,比如用于数据库记录的RowID、标识某个系统的用户、标识网络传输消息等……只要我们想唯一地标识一个实体,就可以使用UUID。
uuid位于名字空间boost::uuids,但它没有一个集中的头文件,而是把功能分散在了若干小文件中,因此为了使用uuid组件,需要包含数个头文件,即:
//uuids.hpp可以自定义一个头文件,包含uuid的所有声明头文件
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>
using namespace boost::uuids;
类摘要
class uuid
{
public:
static size_type static_size(); //长度,返回16
size_type size() const;
uint8_t data[static_size()]; //内部实现
iterator begin();
iterator end();
bool is_nil() const;
enum variant_type {
variant_ncs, //Ncs backward compatibility
variant_rfc_4122, //defined in REC 4122 document
variant_microsoft, //Microsoft corporation backward compatibility
variant_future //future definition
);
variant_type variant() const;
enum version_type {
version_unknown = -1,
version_time_based = -1,
version_dce_security = 2,
version_name_based_md5 = 3,
version_random_number_based = 4,
version_name_based_shal = 5
);
version_type version() const;
void swap(uuid& rhs);
};
在以上列出的成员函数之外,uuid还全面支持比较操作和流输入/输出。两个uuid 值的比较是基于字典序的,通常的比较操作使用的是memcmp,但针对x86做了特殊的优化以加快速度。
用法
uuid是一个很小的类,目标是尽量简单,便于高效率地操作。因此它特意被设计为没有构造函数,可以像POD数据类型一样使用。
uuid内部使用一个16字节的数组data作为UUID值的存储,这个数组是public 的,因此可以任意访问,比如拷贝或者赋值。基于 data数组,uuid 提供了begin()和end()的迭代器支持,可以像一个容器一样操作UUID值的每个字节。成员函数size()和静态成员函数static_size()可以获得UUID的长度,是一个固定值,总返回16。因此,某种程度上可以把uuid看做是一个容量固定为16、元素类型为unsigned char的容器。
示范uuid容器用法的代码如下:
#include "uuids.hpp" //自定义头文件
using namespace boost::uuids; //名字空间
int main()
{
uuid u; //声明一个uuid对象
assert(uuid::static_size() == 16); //uuid的长度总是16
assert(u.size() == 16);
vector<unsigned char> v(16, 7); //一个vector对象
std::copy(v.begin(), v.end(), u.begin()); //使用标准复制算法
assert(u.data[0] == u.data[1] //数组方式访问
&& u.data[15] == 7);
//流输出:07070707-0707-0707-07-0707070707
cout << u << endl;
std::fill_n(u.data + 10, 6, 8); //标准算法fill_n直接操作数组
//流输出:07070707-0707-0707-07-0808080808
cout << u << endl;
}
uuid 内部定义的枚举类型variant_type标识了UUID的变体号,表示了UUID的布局类型,成员函数variant()可以获得这个UUID的变体号。
UUID 的生成有不同的算法,这些算法使用枚举version_type来标识,version()函数可以获得UUID的算法版本。uuid类可以识别现有的五种生成算法,分别是:
1)基于时间和 MAC的算法(version_time_based);
2)分布计算环境算法(dce_security);
3)MD5摘要算法(version_name_based_md5);
4)随机数算法(version_random_number_based);
5)SHA1摘要算法(version_name_based_sha1)。
在数量庞大的UUID中有一个特殊的全零值nil,它表示一个无效的UUID,成员函数is_nil()可以检测uuid是否是nil。
示范uuid用于表示UUID用法的代码如下:
uuid u;
std::fill_n(u.begin(), u.size(), 0xab); //直接填入0xab
assert(!u.is_nil()); //不是无效UUID
assert(u.variant() == u.variant_rfc_4122); //4122变体类型
assert(u.version() == u.version_unknown); //生成算法未知
cout << u << endl; //一串ab值
std::memset(u.data, 0, 16); //可以使用memset操纵数组
assert(u.is_nil()); //此时是一个无效的UUID
uuid还支持各种比较操作:
uuid u1, u2; //两个uuid变量
std::fill_n(u1.begin(), u1.size(), 0xab); //全0xab
std::fill_n(u2.begin(), u2.size(), 0x10); //全0x10
assert(u1 != u2 && u1 > u2); //u1>u2
u2.data[0] = 0xff; //u2的第一个字节改为0xff
assert(u1 < u2); //此时u1<u2
std::memset(u1.data, 0, 16); //两个uuid都z
std::memset(u2.data, 0, 16);
assert(u1 == u2); //两者相等
生成器
使用uuid提供的数组和迭代器接口我们可以直接写出任意的UUID值,但因为不使用规定的算法,手工创建的UUID值很容易重复。这种方式只适合于从其他地方获得的原始UUID值导入到uuid对象中,如果要创建属于自己的UUID,我们需要使用UUID 生成器。
uuid库提供了四种生成器,分别是Nil生成器、字符串生成器、名字生成器和随机生成器。它们都是函数对象,重载了operator(),可以直接调用生成uuid对象。
Nil生成器
Nil生成器是最简单的UUID生成器,只能生成一个无效的u0ID值(即全零的U0ID),它的存在只是为了方便地表示无效UUID。
Nil生成器的类名是nil_generator,另外有一个内联函数nil_uuid(),相当于直接调用了Nil生成器。
示范Ni1生成器用法的代码如下:
uuid u = nil_generator()();
assert (u.is_nil());
u = nil_uuid();
assert(u.is_nil());
注意:代码的第一行,在 nil_generator类名后面出现了两对圆括号,不熟悉函数对象的读者可能会不太理解。它的语义解析是:第一对圆括号与nil_generator结合,结果是调用nil_generator 的构造函数,生成一个临时对象,然后第二对圆括号是nil_generator对象的 operator()操作符重载,就像是一个函数调用,产生了一个niluuid对象。
字符串生成器
字符串生成器string_generator可以从一个字符串创建出uuid对象,字符串可以是c字符串、string、wstring,或者是一对迭代器指定的字符序列的区间。
string_generator对字符串的格式有严格的要求,有两种格式是可接受的。一种是没有连字符的全十六进制数字,另一种是使用连字符,但必须符合UUID的定义,在第5、7、9、11字节前使用连字符,其他位置出现连字符都不允许。UUID字符串也可以使用花括号括起来,除了花括号不能出现十六进制数以外的任何字符,否则会抛出runtime_error异常。
示范stririg_generator用法的代码如下:
string_generator sgen; //声明字符串生成器对象
uuid u1 = sgen("0123456789abcdef0123456789abcdef");
cout << u1 << endl;
uuid u2 = sgen("01234567-89ab-cdef-0123-456789abcdef");
cout << u2 << endl;
uuid u3 = sgen(L"{01234567-89ab-cdef-0123-456789abcdef}");
cout << u3 << endl;
名字生成器
名字生成器name_generator使用基于名字的SHA1摘要算法,它需要先指定一个基准UUID,然后使用字符串名字派生出基于这个UUID的一系列UUID。名字生成器的典型的应用场景是为一个组织内的所有成员创建UUID标识,只要基准UUID不变,那么相同的名字总会产生相同的UUID。
uuid www_xxx_com = string_generator() //首先生成一个组织的UUID
("{0123456789abcdef0123456789abcdef}");
name_generator ngen(www_xxx_com); //构造名字生成器
uuid u1 = ngen("mario"); //为名字mario生成UUID
assert(!u1.is_nil() && //version是shal算法
u1.version() == uuid::version_name_base_shal);
uuid u2 = ngen("link"); //为名字link生成UUID
cout << u2 << endl;
随机生成器
随机生成器采用随机数生成0UID,uuid库使用Boost库的另一个组件random 作为随机数的发生源,它可以产生高质量的伪随机数,保证生成的随机UUID 不会重复。
随机生成器basic_random_generator是一个模板类,可以用模板参数指定要使用的随机数发生器,具体的随机数类可以参考random库。为了方便使用,uuid定义了一个常用的生成器random_generator,它使用mt19937作为随机数发生器。
random_generator rgen; //随机生成器
uuid u = rgen();
assert(u.version() == uuid::version_random_number_based);
cout << u << endl;
增强的uuid类
uuid类为了追求效率而没有提供构造函数,要生成一个U0ID值必须要使用生成器。但有的时候这个操作步骤显得有些麻烦,因此我们可以从uuid类派生一个可以自动产生UUID值的增强类,以简化UUID的使用。
下面的代码实现了这个增强类uuid_t,它是uuid的子类,因而具有uuid的全部能力。它在内部定义了两个生成器的静态成员函数,分别用来产生随机UUID 和字符串 UUID,对应地也提供了两种重载形式的构造函数。对于Nil生成器,uuid_t使用带int参数的构造函数来调用实现,而名字生成器则使用了接受uuid和字符串参数的构造函数。
uuid_t还实现了两个类型转换操作符重载,可以隐式地转换成uuid对象,方便被应用在其他使用uuid类型的场景。
using namespace boost::uuids;
class uuid_t : public uuid
{
private:
static random_generator& rand_uuid() //随机uuid生成器
{
static random_generator gen;
return gen;
}
static string_generator& string_uuid() //字符串uuid生成器
{
static string_generator gen;
return gen;
}
public:
uuid_t() : uuid(rand_uuid()()){
} //默认构造函数,生成随机UUID
uuid_t(int) : uuid (nil_uuid()){
} //0值的UUID构造函数
uuid_t(const char* str) : //字符串构造函数
uuid(string_uuid ( )(str)) {
}
uuid_t(const uuid& u, const char* str): //名字生成器构造函数
uuid (name_generator (uy(str)){
}
explicit uuid_t(const uuids u) : uuid (u){
}//拷贝构造函数
operator uuid() //转换到uuid类型
{
return static_cast<uuid&> (*this);
}
operator uuid() const //常函数,转换到const uuid类型
{
return static_cast< const uuid&> (*this) ;
}
};
由于uuid_t类封装了uuid的所有生成器,故它比uuid用起来更加方便容易,例如:
uuid_t u0 = 0; //0值的UUID
assert(u0.is_nil());
uuid_t u1,u2; //创建两个随机UUID的值
cout << u1 << endl;
cout << u2 << endl;
uuid_t u3("{01234567-89ab-cdef-0123-456789abcdef)"}; //字符串构造
cout << u3 << endl;
cout << uuid_t(u3, "test name gen") << endl; //通过名字构造
转换字符串
uuid可以使用字符串生成器从字符串生成,相应地也提供了转换成字符的操作,使用两个自由函数:
std::string to_string(uuid const& u); //窄字符串转换
std::wstring to_wstring(uuid const& u); //宽字符串转换
它们的用法如下:
uuid u = string_generator() //字符串生成器
("01234567-89ab-cdef-0123-456789abcdef");
string str = to_string(u); //生成字符串表示
cout << str << endl;
另一个Boost库组件lexical_cast也可以非常方便地实现字符串与uuid的双向转换(得益于uuid的流输入/输出功能):
#include <boost/lexical_cast.hpp> //lexical_cast头文件
using namespace boost;
int main()
{
uuid u = lexical_cast<uuid> //字符串转换到uuid
("01234567-89ab-cdef-0123-456789abcdef");
cout << u << endl;
string str = lexical_cast<string>(u); //uuid转换到字符串
cout << str << endl;
}
lexical_cast的字符串转换uuid的用法很类似字符串生成器string_generator,但功能要弱很多,因为lexical_cast的转换功能是基于uuid的流输入能力的,因此只能接受连字符格式的字符串,而且不能有花括号。
SHA1摘要算法
uuid的名字生成器使用了SHA1摘要算法,这是一个很重要的密码学算法,可以将任意长度的文本压缩成一个只有20字节(160位)的独一无二的摘要,被广泛地应用于防篡改、身份鉴定等安全认证领域。
shal算法位于名字空间 boost::uuids::detail,使用时需要包含头文件<boost/uuid/shal.hpp>,它的类摘要如下。
class shal
{
public:
typedef unsigned int (&digest_type)[5];
shal();
void reset();
void process_byte(unsigned char byte) ;
void process_block(void const* bytes_begin, void const* bytes_end);
void process_bytes(void const* buffer, size_t byte_count);
void get_digest(digest_type digest);
);
sha1类很容易使用,使用成员函数process_byte()、process_block()和process_bytes()以不同的方式把数据“喂”给sha1对象,当输入所有数据后用get_digest()获得计算出的摘要值。
示范shal用法的代码如下:
using namespace boost::uuids::detail;
int main()
{
sha1 sha; //sha1对象
const char* szMsg = "a short message"; //用于摘要的消息
sha.process_byte(0x10); //处理一个字节
sha.process_bytes(szMsg, strlen(szMsg)); //处理多个字节
sha.process_block(szMsg, szMsg + strlen(szMsg));
unsigned int digest[5]; //摘要的返回值
sha.get_digest(digest); //获得摘要
for (int i = 0; i < 5; ++i)
{
cout << hex << digest[i]; //16进制输出
}
}
代码示例
#include <iostream>
using namespace std;
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>
using namespace boost::uuids;
//
void case1()
{
uuid u;
assert(uuid::static_size() == 16);
assert(u.size() == 16);
vector<unsigned char> v(16, 7);
std::copy(v.begin(), v.end(), u.begin());
assert(u.data[0] == u.data[1]
&& u.data[15] == 7);
cout << u << endl;
std::fill_n(u.data + 10, 6, 8);
cout << u << endl;
}
//
void case2()
{
uuid u;
std::fill_n(u.begin(), u.size(), 0xab);
assert(!u.is_nil());
assert(u.variant() == u.variant_rfc_4122);
assert(u.version() == u.version_unknown);
cout << u << endl;
std::memset(u.data, 0, 16);
assert(u.is_nil());
uuid u1, u2;
std::fill_n(u1.begin(), u1.size(), 0xab);
std::fill_n(u2.begin(), u2.size(), 0x10);
assert(u1 != u2 && u1 > u2);
u2.data[0] = 0xff;
assert(u1 < u2);
std::memset(u1.data, 0, 16);
std::memset(u2.data, 0, 16);
assert(u1 == u2);
}
//
void case3()
{
uuid u = nil_generator()();
assert(u.is_nil());
u = nil_uuid();
assert(u.is_nil());
string_generator sgen;
uuid u1 = sgen("0123456789abcdef0123456789abcdef");
cout << u1 << endl;
uuid u2 = sgen("01234567-89ab-cdef-0123-456789abcdef");
cout << u2 << endl;
uuid u3 = sgen(L"{01234567-89ab-cdef-0123-456789abcdef}");
cout << u3 << endl;
}
//
void case4()
{
uuid www_xxx_com = string_generator()
("{0123456789abcdef0123456789abcdef}");
name_generator ngen(www_xxx_com);
uuid u1 = ngen("mario");
assert(!u1.is_nil() &&
u1.version() == uuid::version_name_based_sha1);
uuid u2 = ngen("link");
cout << u2 << endl;
random_generator rgen;
uuid u = rgen();
assert(u.version() == uuid::version_random_number_based);
cout << u << endl;
}
//
class uuid_t : public uuid
{
private:
static random_generator& rand_uuid()
{
static random_generator gen;
return gen;
}
static string_generator& string_uuid()
{
static string_generator gen;
return gen;
}
public:
uuid_t() : uuid(rand_uuid()()) {
}
uuid_t(int) : uuid(nil_uuid()) {
}
uuid_t(const char* str) : uuid(string_uuid()(str)) {
}
uuid_t(const uuid& u, const char* str) :
uuid(name_generator(u)(str)) {
}
explicit uuid_t(const uuid& u) : uuid(u) {
}
operator uuid()
{
return static_cast<uuid&>(*this);
}
operator uuid() const
{
return static_cast<const uuid&>(*this);
}
};
void case5()
{
uuid_t u0 = 0;
assert(u0.is_nil());
uuid_t u1, u2;
cout << u1 << endl;
cout << u2 << endl;
uuid_t u3("{01234567-89ab-cdef-0123-456789abcdef}");
cout << u3 << endl;
cout << uuid_t(u3, "test name gen") << endl;
}
//
#include <boost/lexical_cast.hpp>
using namespace boost;
void case6()
{
uuid u = string_generator()
("01234567-89ab-cdef-0123-456789abcdef");
string str = to_string(u);
cout << str << endl;
{
uuid u = lexical_cast<uuid>
("01234567-89ab-cdef-0123-456789abcdef");
cout << u << endl;
string str = lexical_cast<string>(u);
cout << str << endl;
}
}
//
#include <boost/version.hpp>
#if BOOST_VERSION <= 106400
#include <boost/uuid/sha1.hpp>
#else
#include <boost/uuid/detail/sha1.hpp>
#endif
using namespace boost::uuids::detail;
void case7()
{
sha1 sha;
const char* szMsg = "a short message";
sha.process_byte(0x10);
sha.process_bytes(szMsg, strlen(szMsg));
sha.process_block(szMsg, szMsg + strlen(szMsg));
unsigned int digest[5];
sha.get_digest(digest);
for (int i = 0; i < 5; ++i)
{
cout << hex << digest[i];
}
}
//
int main()
{
case1();
case2();
case3();
case4();
case5();
case6();
case7();
}