Boost Development Guide-4.10uuid

uuid

The uuid library is a small utility that can represent and generate UUIDs.

UUID is the abbreviation of Universally Unique Identifier. Another alias is GUID, which is a 128-bit number (16 bytes) that can create a globally unique identifier without a central certification authority. For example, “E4A0D7CE-9E6D-4E74-9E6D-7E749E6D7E74” is a UUID.

UUID can be used in many places, such as RowID for database records, identifying users of a certain system, identifying network transmission messages, etc… As long as we want to uniquely identify an entity, we can use UUID.

uuid is located in the namespace boost::uuids, but it does not have a centralized header file. Instead, the functions are scattered in several small files. Therefore, in order to use the uuid component, you need to include several header files, namely:

//uuids.hpp can customize a header file that contains all declaration header files of uuid
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>
using namespace boost::uuids;

Class summary

class uuid
{<!-- -->
public:
    static size_type static_size(); //Length, return 16
    size_type size() const;
    uint8_t data[static_size()]; //Internal implementation
    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 & amp; rhs);
};

In addition to the member functions listed above, uuid also fully supports comparison operations and stream input/output. The comparison of two uuid values is based on lexicographic order. The usual comparison operation uses memcmp, but special optimizations are made for x86 to speed up the speed.

Usage

uuid is a very small class, the goal is to be as simple as possible and easy to operate efficiently. Therefore it is deliberately designed to have no constructor and can be used like the POD data type.

Uuid internally uses a 16-byte array data as the storage of UUID values. This array is public, so it can be accessed at will, such as copying or assignment. Based on the data array, uuid provides iterator support for begin() and end(), which can operate each byte of the UUID value like a container. The member function size() and the static member function static_size() can obtain the length of the UUID, which is a fixed value and always returns 16. Therefore, to a certain extent, uuid can be regarded as a container with a fixed capacity of 16 and an element type of unsigned char.

The code demonstrating the usage of uuid container is as follows:

#include "uuids.hpp" //Customized header file
using namespace boost::uuids; //namespace
int main()
{<!-- -->
uuid u; //Declare a uuid object
assert(uuid::static_size() == 16); //The length of uuid is always 16
assert(u.size() == 16);

vector<unsigned char> v(16, 7); //A vector object
std::copy(v.begin(), v.end(), u.begin()); //Use standard copy algorithm
assert(u.data[0] == u.data[1] //array access
& amp; & amp; u.data[15] == 7);
    //Stream output: 07070707-0707-0707-07-0707070707
cout << u << endl;
std::fill_n(u.data + 10, 6, 8); //The standard algorithm fill_n directly operates the array
    //Stream output: 07070707-0707-0707-07-0808080808
cout << u << endl;
}

The internally defined enumeration type variant_type of uuid identifies the variant number of UUID and represents the layout type of UUID. The member function variant() can obtain the variant number of this UUID.

There are different algorithms for UUID generation. These algorithms are identified by the enumeration version_type. The version() function can obtain the algorithm version of the UUID. The uuid class can identify the five existing generation algorithms, which are:
1) Algorithm based on time and MAC (version_time_based);
2) Distributed computing environment algorithm (dce_security);
3) MD5 digest algorithm (version_name_based_md5);
4) Random number algorithm (version_random_number_based);
5) SHA1 digest algorithm (version_name_based_sha1).

Among the huge number of UUIDs, there is a special all-zero value nil, which represents an invalid UUID. The member function is_nil() can detect whether the uuid is nil.

The code that demonstrates uuid usage is as follows:

uuid u;

std::fill_n(u.begin(), u.size(), 0xab); //Fill in 0xab directly
assert(!u.is_nil()); //Not an invalid UUID
assert(u.variant() == u.variant_rfc_4122); //4122 variant type
assert(u.version() == u.version_unknown); //The generation algorithm is unknown
cout << u << endl; //A string of ab values

std::memset(u.data, 0, 16); //You can use memset to manipulate arrays
assert(u.is_nil()); //This is an invalid UUID

uuid also supports various comparison operations:

uuid u1, u2; //Two uuid variables

std::fill_n(u1.begin(), u1.size(), 0xab); //All 0xab
std::fill_n(u2.begin(), u2.size(), 0x10); //All 0x10
assert(u1 != u2 & amp; & amp; u1 > u2); //u1>u2

u2.data[0] = 0xff; //The first byte of u2 is changed to 0xff
assert(u1 < u2); //u1<u2 at this time

std::memset(u1.data, 0, 16); //Both uuid are z
std::memset(u2.data, 0, 16);
assert(u1 == u2); //Both are equal

Generator

We can directly write any UUID value using the array and iterator interface provided by uuid, but because the prescribed algorithm is not used, manually created UUID values are easy to repeat. This method is only suitable for importing original UUID values obtained from other places into uuid objects. If you want to create your own UUID, we need to use a UUID generator.

The uuid library provides four generators, namely Nil generator, string generator, name generator and random generator. They are all function objects, overloaded with operator(), and can be directly called to generate uuid objects.

Nil generator

The Nil generator is the simplest UUID generator and can only generate an invalid u0ID value (that is, a U0ID with all zeros). It exists only to conveniently represent invalid UUIDs.

The class name of the Nil generator is nil_generator, and there is an inline function nil_uuid(), which is equivalent to directly calling the Nil generator.

The code that demonstrates the use of the Ni1 generator is as follows:

uuid u = nil_generator()();
assert (u.is_nil());
u = nil_uuid();
assert(u.is_nil());

Note: In the first line of the code, two pairs of parentheses appear after the nil_generator class name. Readers who are not familiar with function objects may not understand it. Its semantic analysis is: the first pair of parentheses is combined with nil_generator, the result is to call the constructor of nil_generator, generating a temporary object, and then the second pair of parentheses is the operator() operator overload of the nil_generator object, just like a function call , resulting in a niluuid object.

String generator

The string generator string_generator can create a uuid object from a string. The string can be a c string, string, wstring, or a range of character sequences specified by a pair of iterators.

string_generator has strict requirements on the format of strings, and two formats are acceptable. One is a full hexadecimal number without hyphens, and the other is using hyphens, but it must comply with the definition of UUID. Hyphens are used before bytes 5, 7, 9, and 11, and hyphens appear in other positions. Neither is allowed. UUID strings can also be enclosed in curly braces. Except that curly braces cannot contain any characters other than hexadecimal numbers, otherwise a runtime_error exception will be thrown.

The code demonstrating the usage of strrig_generator is as follows:

string_generator sgen; //Declare string generator object

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

The name generator name_generator uses the name-based SHA1 digest algorithm. It needs to specify a base UUID first, and then use the string name to derive a series of UUIDs based on this UUID. A typical application scenario of the name generator is to create UUID identifiers for all members of an organization. As long as the base UUID remains unchanged, the same name will always generate the same UUID.

uuid www_xxx_com = string_generator() //First generate an organization's UUID
  ("{0123456789abcdef0123456789abcdef}");
name_generator ngen(www_xxx_com); //Construct a name generator

uuid u1 = ngen("mario"); //Generate UUID for the name mario
assert(!u1.is_nil() & amp; & amp; //version is shal algorithm
   u1.version() == uuid::version_name_base_shal);

uuid u2 = ngen("link"); //Generate UUID for the name link
cout << u2 << endl;

Random generator

The random generator uses random numbers to generate 0UIDs. The uuid library uses random, another component of the Boost library, as the source of random numbers. It can generate high-quality pseudo-random numbers and ensure that the generated random UUIDs will not be repeated.

The random generator basic_random_generator is a template class. You can use template parameters to specify the random number generator to be used. For specific random number classes, please refer to the random library. For ease of use, uuid defines a commonly used generator random_generator, which uses mt19937 as a random number generator.

random_generator rgen; //random generator

uuid u = rgen();
assert(u.version() == uuid::version_random_number_based);
cout << u << endl;

Enhanced uuid class

The uuid class does not provide a constructor in order to pursue efficiency. To generate a U0ID value, a generator must be used. But sometimes this operation step seems a bit cumbersome, so we can derive an enhanced class from the uuid class that can automatically generate UUID values to simplify the use of UUID.

The following code implements this enhanced class uuid_t, which is a subclass of uuid and therefore has all the capabilities of uuid. It defines two static member functions of the generator internally, which are used to generate random UUID and string UUID respectively. Correspondingly, it also provides two overloaded forms of constructors. For the Nil generator, uuid_t uses a constructor with int parameters to call the implementation, while the name generator uses a constructor that accepts uuid and string parameters.

uuid_t also implements two type conversion operator overloads, which can be implicitly converted into uuid objects to facilitate application in other scenarios using uuid types.

using namespace boost::uuids;

class uuid_t : public uuid
{<!-- -->
private:
   static random_generator & amp; rand_uuid() //random uuid generator
   {<!-- -->
       static random_generator gen;
       returngen;
   }
   static string_generator & string_uuid() //String uuid generator
   {<!-- -->
       static string_generator gen;
       returngen;
   }
public:
   uuid_t() : uuid(rand_uuid()()){<!-- -->} //Default constructor, generates a random UUID
   uuid_t(int) : uuid (nil_uuid()){<!-- -->} //UUID constructor with 0 value
   
   uuid_t(const char* str): //String constructor
      uuid(string_uuid ( )(str)) {<!-- -->}
   
   uuid_t(const uuid & amp; u, const char* str): //Name generator constructor
      uuid (name_generator (uy(str)){<!-- --> }
   
   explicit uuid_t(const uuids u) : uuid (u){<!-- --> }//Copy constructor

   operator uuid() //Convert to uuid type
   {<!-- -->
        return static_cast<uuid & amp;> (*this);
   }
   operator uuid() const //Constant function, converted to const uuid type
   {<!-- -->
        return static_cast< const uuid &> (*this);
   }
};

Since the uuid_t class encapsulates all generators of uuid, it is more convenient and easier to use than uuid, for example:

uuid_t u0 = 0; //UUID with 0 value
assert(u0.is_nil());

uuid_t u1,u2; //Create two random UUID values
cout << u1 << endl;
cout << u2 << endl;

uuid_t u3("{01234567-89ab-cdef-0123-456789abcdef)"}; //String construction
cout << u3 << endl;

cout << uuid_t(u3, "test name gen") << endl; //Constructed by name

Convert string

Uuid can be generated from a string using the string generator, which accordingly provides conversion to characters using two free functions:

std::string to_string(uuid const & amp; u); //Narrow string conversion
std::wstring to_wstring(uuid const & amp; u); //Wide string conversion

They are used as follows:

uuid u = string_generator() //String generator
("01234567-89ab-cdef-0123-456789abcdef");

string str = to_string(u); //Generate string representation
cout << str << endl;

Another Boost library component, lexical_cast, can also easily achieve bidirectional conversion between strings and uuid (thanks to uuid’s stream input/output function):

#include <boost/lexical_cast.hpp> //lexical_cast header file
using namespace boost;

int main()
{<!-- -->
uuid u = lexical_cast<uuid> //Convert string to uuid
("01234567-89ab-cdef-0123-456789abcdef");
cout << u << endl;
string str = lexical_cast<string>(u); //Convert uuid to string
cout << str << endl;
}

The usage of lexical_cast’s string conversion uuid is very similar to the string generator string_generator, but its function is much weaker. Because the conversion function of lexical_cast is based on the stream input capability of uuid, it can only accept strings in hyphen format, and cannot have curly braces.

SHA1 digest algorithm

The uuid name generator uses the SHA1 digest algorithm, which is a very important cryptographic algorithm that can compress text of any length into a unique digest of only 20 bytes (160 bits) and is widely used to prevent tampering. , identity authentication and other security authentication fields.

The shal algorithm is located in the namespace boost::uuids::detail. When using it, you need to include the header file . Its class summary is as follows.

class shal
{<!-- -->
public:
   typedef unsigned int ( & amp;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);
);

The sha1 class is easy to use. Use the member functions process_byte(), process_block() and process_bytes() to “feed” data to the sha1 object in different ways. When all the data is input, use get_digest() to obtain the calculated digest value.

The code demonstrating shal usage is as follows:

using namespace boost::uuids::detail;

int main()
{<!-- -->
sha1 sha; //sha1 object

const char* szMsg = "a short message"; //Message for summary
sha.process_byte(0x10); //Process a byte
sha.process_bytes(szMsg, strlen(szMsg)); //Process multiple bytes
sha.process_block(szMsg, szMsg + strlen(szMsg));

unsigned int digest[5]; //Return value of digest
sha.get_digest(digest); //Get the digest
for (int i = 0; i < 5; + + i)
{<!-- -->
cout << hex << digest[i]; //Hexadecimal output
}
}

Code example

#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]
& amp; & amp; 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 & amp; & amp; 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() & amp; & amp;
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;
returngen;
}
static string_generator & string_uuid()
{<!-- -->
static string_generator gen;
returngen;
}

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 & amp; u, const char* str):
uuid(name_generator(u)(str)) {<!-- -->}
explicit uuid_t(const uuid & amp; u) : uuid(u) {<!-- -->}

operator uuid()
{<!-- -->
return static_cast<uuid & amp;>(*this);
}
operator uuid() const
{<!-- -->
return static_cast<const uuid & amp;>(*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();
}