Boost开发指南-4.10uuid

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();
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_36314864/article/details/132537480