uuid.cc #1

  • //
  • guest/
  • perforce_software/
  • p4/
  • 2014-1/
  • support/
  • uuid.cc
  • View
  • Commits
  • Open Download .zip Download (7 KB)
// -*- mode: C++; tab-width: 8; -*-
// vi:ts=8 sw=4 noexpandtab autoindent

/**
 * uuid.cc
 *
 * Description:
 *	A simple implementation of UUIDs, inspired by an old
 *	UUID implementation by Kenneth Laskoski, which is
 *	licensed under the Boost Software Licence, Version 1.0.
 *	No parts of Boost are used.
 *
 *	This version supports only NIL and RFC 4122 random UUIDs,
 *	and does NOT use a strong random number generator.  It is,
 *	however, sufficient for our purposes (generating server ids).
 *	The current (v1.47.0) boost implementation provides a strong
 *	PRNG (Mersenne Twister [mt19937]), but also uses iostreams,
 *	which adds about 500KB to our object size.  This version adds
 *	only 4KB.
 *
 *	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
 *
 *	See:
 *	    http://en.wikipedia.org/wiki/Uuid
 *	    http://www.boost.org/doc/libs/1_47_0/libs/uuid/uuid.html
 *
 * Copyright 2011 Perforce Software.  All rights reserved.
 *
 * This file is part of Perforce - the FAST SCM System.
 */

#ifdef OS_NT
#  define _CRT_RAND_S
    // define before <stdhdrs.h>/<stdlib.h> to get rand_s()
#endif // OS_NT
#include <stdhdrs.h>
#include <strbuf.h>
#include <time.h>
#include <error.h>
#include <pid.h>

#include "datetime.h"
#include "uuid.h"

#ifdef OS_NT
#  define SRANDOM(x)	::srand(x)
#  define RANDOM()	::rand()
#else
#  define SRANDOM(x)	::srandom(x)
#  define RANDOM()	::random()
#endif // OS_NT

/*
 * initialize our pseudo-random number generator
 */
static void
InitPRNG()
{
	DateTimeHighPrecision	dthp;
	dthp.Now();

	SRANDOM( (dthp.Seconds() * dthp.Nanos()) ^ Pid().GetID() );
}

/*
 * Convenient wrapper for getting a random int
 * after the PRNG has been initialized (if needed).
 * Bury some of the OS-dependent cruft here.
 */
static unsigned int
GetRandomInt()
{
#if defined( OS_NT ) && !defined( OS_MINGW )
	    /*
	     * NB: rand_s() appeared in VS 2005 and is supported only from XP onwards.
	     *     On Windows, the seed used by rand() is stored in TLS and so needs
	     *     to be initialized in each thread.
	     *     rand_s() doesn't use a user-provided seed, and yields a better
	     *     random distribution, so we'll use it instead.
	     */
	    unsigned int	val;
	    ::rand_s( &val );
	    return val;
#else
	    return RANDOM();
#endif // OS_NT
}

/*
 * fill buffer with random bytes
 */
static void
Randomize(
	unsigned char	*buffer,
	unsigned int	count)
{
#if defined( OS_MINGW )
	InitPRNG();
#endif

	for( int i = 0; i < count; ++i )
	{
	    buffer[i] = GetRandomInt() & 0xFF;
	}
}

/*
 * Orthodox Canonical Form (OCF) methods
 */


/**
 * default ctor
 * - generate a Version 4 (Random) UUID
 */
UUID::UUID()
{
#ifndef OS_NT
	// on Windows we use rand_s(), which doesn't use a seed
	MT_STATIC bool	inited = false;

	if( !inited )
	{
	    // initialize the PRNG
	    InitPRNG();
	    inited = true;
	}
#endif // OS_NT

	Randomize( begin(), kDataSize );

	// set variant
	// should be 0b10xxxxxx
	m_uuid[8] &= 0xBF;   // 0b10111111
	m_uuid[8] |= 0x80;   // 0b10000000

	// set version
	// should be 0b0100xxxx
	m_uuid[6] &= 0x4F;   // 0b01001111
	m_uuid[6] |= 0x40;   // 0b01000000
} // default ctor

/**
 * initialize with copies of the given byte value
 * - don't use unless you know what you're doing
 * - mostly useful if you want a NIL UUID.
 */
UUID::UUID(
	int	val)
{
	for( int i = 0; i < kDataSize; ++i )
	    m_uuid[i] = val;
}

/**
 * copy ctor
 */
UUID::UUID(
	const UUID	&rhs)
{
	for( int i = 0; i < kDataSize; ++i )
	    m_uuid[i] = rhs.m_uuid[i];
} // copy ctor

/**
 * dtor
 */
UUID::~UUID()
{
} // dtor

/**
 * assignment op
 */
const UUID	&
UUID::operator=(
	const UUID	&rhs)
{
	if( this != &rhs ) {
	    for( int i = 0; i < kDataSize; ++i )
		m_uuid[i] = rhs.m_uuid[i];
	}

	return *this;
} // op=

/**
 * op==
 */
bool
UUID::operator==(
	const UUID	&rhs) const
{
	if( this == &rhs ) {
	    return true;
	}

	for( int i = 0; i < kDataSize; ++i )
	{
	    if( m_uuid[i] != rhs.m_uuid[i] )
	    	return false;
	}

	return true;
} // op==

/**
 * op!=
 */
bool
UUID::operator!=(
	const UUID	&rhs) const
{
	return !(*this == rhs);
} // op!=


// accessors

bool
UUID::IsNil() const
{
	for( int i = 0; i < kDataSize; ++i )
	{
	    if( m_uuid[i] )
	    	return false;
	}

	return true;
}

/*
 * From wikipedia:
 *    In the canonical representation, xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx,
 *    the most significant bits of N indicates the variant (depending on the
 *    variant; one, two or three bits are used). The variant covered by the
 *    UUID specification is indicated by the two most significant bits of N
 *    being 1 0 (i.e. the hexadecimal N will always be 8, 9, a, or b).
 *
 *    In the variant covered by the UUID specification, there are five versions.
 *    For this variant, the four bits of M indicates the UUID version
 *    (i.e. the hexadecimal M will either be 1, 2, 3, 4, or 5)
 *
 * NB: The underlying implementation supports only the random version (4) of the
 * normal variant and the NIL variant (all bits 0).
 */

/**
 * VariantType
 * - Return the UUID variant of this UUID.
 * - Returns either 0x80 (RFC 4122)
 *   or 0 (NIL)
 *   - NIL UUIDs return 0, otherwise
 *     high 2 bits are always 10, per wikipedia note above.
 */
unsigned int
UUID::VariantType() const
{
    	return m_uuid[8] & 0xC0;
}

/*
 * VersionType
 * - Return the type of the UUID:
 *    Version 1 (MAC address)
 *    Version 2 (DCE Security)
 *    Version 3 (MD5 hash)
 *    Version 4 (random)
 *    Version 5 (SHA-1 hash)
 * - Returns either 0x40 (random)
 *   or 0 (NIL)
 */
unsigned int
UUID::VersionType() const
{
    	return m_uuid[6] & 0xF0;
}

/**
 * Data
 * - return the raw underlying UUID bytes
 */
void
UUID::Data(DataType &data) const
{
	::memcpy(data, begin(), sizeof(data));
}

/**
 * Size
 * - return the data size of the underlying boost uuid object
 * [static]
 */
unsigned int
UUID::SizeofData()
{
	return kDataSize;
}

// mutators

// swap the underlying uuid data bytes with rhs
void
UUID::Swap(UUID	&rhs)
{
    	DataType	tmp;

	::memcpy(&tmp, begin(), kDataSize);
	::memcpy(begin(), rhs.begin(), kDataSize);
	::memcpy(rhs.begin(), &tmp, kDataSize);
}

/*
 * Other methods
 */


/**
 * NibbleToHexChar
 * - helper routine to convert a nibble value to its
 *   hexadecimal character representation.
 * - assumes ASCII-compatible character set (ie, not EBCDIC).
 */
static char
NibbleToHexChar(unsigned int	val)
{
	if( val < 10 )
	{
	    return val + '0';
	}
	return val - 10 + 'A';
}

/**
 * ToStrBuf
 * - Set "buf" to the formatted hexadecimal character representation of this UUID.
 */
StrPtr
UUID::ToStrBuf(StrBuf	&buf) const
{
	// standard format is 8-4-4-4-12 (32 digits plus 4 dashes)

	// efficiently ensure that we have enough room
	buf.Clear();
	buf.Alloc( kStringSize );
	buf.Clear();

	for( const_iterator	iter = begin();
	    iter != end();
	    ++iter )
	{
	    switch( iter - begin() )
	    {
	    // note: 2 hex digits per iteration
	    case 4:	// fall through
	    case 6:	// fall through
	    case 8:	// fall through
	    case 10:	// fall through
	        buf.Append( "-" );
	        break;
	    default:
	        break;	// do nothing but keep some compilers happy
	    }

	    unsigned int	val = static_cast<unsigned int>(*iter);
	    unsigned int	hi = (val >> 4) & 0xF;
	    unsigned int	lo = val & 0xF;
	    char		hexstr[] = { NibbleToHexChar(hi), NibbleToHexChar(lo), '\0' };
	    buf.Append( hexstr );
	}

	return buf;
} // ToStrBuf
# Change User Description Committed
#1 15902 Matt Attaway A second renaming that I will not obliterate as a badge of shame
//guest/perforce_software/p4/2014_1/support/uuid.cc
#1 15901 Matt Attaway Clean up code to fit modern Workshop naming standards
//guest/perforce_software/p4/2014.1/support/uuid.cc
#1 12188 Matt Attaway Move 'main' p4 into a release specific directory in prep for new releases
//guest/perforce_software/p4/support/uuid.cc
#1 9129 Matt Attaway Initial commit of the 2014.1 p4/p4api source code