guid.hpp

Go to the documentation of this file.
00001 /******************************************************************************
00002  * $Id: guid.hpp 813 2008-07-25 21:53:52Z mloskot $
00003  *
00004  * Project:  libLAS - http://liblas.org - A BSD library for LAS format data.
00005  * Purpose:  GUID implementation
00006  * Author:   Andy Tompkins (modified by Mateusz Loskot)
00007  *
00008  * This file has been stolen from the Boost Vault and
00009  * modified for libLAS purposes. Here is the original code posted:
00010  * http://lists.boost.org/boost-users/2007/04/27397.php
00011  * 
00012  * (C) Copyright 2006 Andy Tompkins.
00013  * (C) Copyright 2008 Mateusz Loskot, mateusz@loskot.net.
00014  * Distributed under the Boost  Software License, Version 1.0.
00015  * (See accompanying file LICENSE_1_0.txt or copy at
00016  * http://www.boost.org/LICENSE_1_0.txt)
00017  * 
00018  * Revision History
00019  * 06 Feb 2006 - Initial Revision
00020  * 09 Nov 2006 - fixed variant and version bits for v4 guids
00021  * 13 Nov 2006 - added serialization
00022  * 17 Nov 2006 - added name-based guid creation
00023  * 20 Nov 2006 - add fixes for gcc (from Tim Blechmann)
00024  * 07 Mar 2007 - converted to header only
00025  * 20 Jan 2008 - removed dependency of Boost and modified for libLAS (by Mateusz Loskot)
00026  ******************************************************************************
00027  *
00028  * All rights reserved.
00029  * 
00030  * Redistribution and use in source and binary forms, with or without 
00031  * modification, are permitted provided that the following 
00032  * conditions are met:
00033  * 
00034  *     * Redistributions of source code must retain the above copyright 
00035  *       notice, this list of conditions and the following disclaimer.
00036  *     * Redistributions in binary form must reproduce the above copyright 
00037  *       notice, this list of conditions and the following disclaimer in 
00038  *       the documentation and/or other materials provided 
00039  *       with the distribution.
00040  *     * Neither the name of the Martin Isenburg or Iowa Department 
00041  *       of Natural Resources nor the names of its contributors may be 
00042  *       used to endorse or promote products derived from this software 
00043  *       without specific prior written permission.
00044  * 
00045  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
00046  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
00047  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
00048  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
00049  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
00050  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
00051  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 
00052  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
00053  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
00054  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 
00055  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
00056  * OF SUCH DAMAGE.
00057  ****************************************************************************/
00058
00059 #ifndef LIBLAS_GUID_HPP_INCLUDED
00060 #define LIBLAS_GUID_HPP_INCLUDED
00061 
00062 #include <liblas/cstdint.hpp>
00063 #include <liblas/detail/sha1.hpp>
00064 #include <liblas/detail/utility.hpp>
00065 #include <iosfwd>
00066 #include <iomanip>
00067 #include <algorithm>
00068 #include <limits>
00069 #include <stdexcept>
00070 #include <sstream>
00071 #include <string>
00072 #include <cstdlib>
00073 #include <cstring>
00074 #include <ctime>
00075 #include <cassert>
00076
00077 namespace liblas {
00078
00088 class guid
00089 {
00090 public:
00091
00095     guid() /* throw() */
00096     {
00097         std::fill(data_, data_ + static_size, 0);
00098     }
00099
00104     explicit guid(char const* const str)
00105     {
00106         if (0 == str)
00107             throw_invalid_argument();
00108         construct(std::string(str));
00109     }
00110
00115     template <typename ch, typename char_traits, typename alloc>
00116     explicit guid(std::basic_string<ch, char_traits, alloc> const& str)
00117     {
00118         construct(str);
00119     }
00120
00128     guid(liblas::uint32_t const& d1, liblas::uint16_t const& d2, liblas::uint16_t const& d3, liblas::uint8_t const (&d4)[8])
00129     {
00130         construct(d1, d2, d3, d4);
00131     }
00132
00135     guid(guid const& rhs) /* throw() */
00136     {
00137         std::copy(rhs.data_, rhs.data_ + static_size, data_);
00138     }
00139
00142     ~guid() /* throw() */
00143     {}
00144
00147     guid& operator=(guid const& rhs) /* throw() */
00148     {
00149         if (&rhs != this)
00150         {
00151             std::copy(rhs.data_, rhs.data_ + static_size, data_);
00152         }
00153         return *this;
00154     }
00155
00158     bool operator==(guid const& rhs) const /* throw() */
00159     {
00160         return std::equal(data_, data_ + static_size, rhs.data_);
00161     }
00162
00165     bool operator!=(guid const& rhs) const /* throw() */
00166     {
00167         return (!(*this == rhs));
00168     }
00169
00175     bool operator<(guid const& rhs) const /* throw() */
00176     {
00177         return std::lexicographical_compare(data_, data_ + static_size, rhs.data_, rhs.data_ + static_size);
00178     }
00179
00185     bool operator>(guid const& rhs) const /* throw() */
00186     {
00187         return std::lexicographical_compare(rhs.data_, rhs.data_ + static_size, data_, data_ + static_size);
00188     }
00189
00193     bool operator<=(guid const& rhs) const /* throw() */
00194     {
00195         return (*this == rhs) || (*this < rhs);
00196     }
00197
00201     bool operator>=(guid const& rhs) const /* throw() */
00202     {
00203         return (*this == rhs) || (*this > rhs);
00204     }
00205
00210     bool is_null() const /* throw() */
00211     {
00212         return ((*this) == null());
00213     }
00214
00220     std::string to_string() const
00221     {
00222         return to_basic_string<std::string::value_type, std::string::traits_type, std::string::allocator_type>();
00223     }
00224
00228     template <typename ch, typename char_traits, typename alloc>
00229     std::basic_string<ch, char_traits, alloc> to_basic_string() const
00230     {
00231         std::basic_string<ch, char_traits, alloc> s;
00232         std::basic_stringstream<ch, char_traits, alloc> ss;
00233         liblas::guid const& g = *this;
00234         ss << g;
00235         if (!ss || !(ss >> s))
00236         {
00237             throw std::runtime_error("failed to convert guid to string");
00238         }
00239
00240         assert(!s.empty());
00241         return s;
00242     }
00243
00247     size_t byte_count() const /* throw() */
00248     {
00249         return static_size;
00250     }
00251
00252
00255     template <typename ByteOutputIterator>
00256     void output_bytes(ByteOutputIterator out) const
00257     {
00258         std::copy(data_, data_ + static_size, out);
00259     }
00260
00267     void output_data(liblas::uint32_t& d1, liblas::uint16_t& d2, liblas::uint16_t& d3, liblas::uint8_t (&d4)[8]) const
00268     {
00269         d1 = d2 = d3 = 0;
00270         std::size_t pos = 0;
00271         int const charbit = std::numeric_limits<liblas::uint8_t>::digits;
00272
00273         for (; pos < 4; ++pos)
00274         {
00275
00276             d1 <<= charbit;
00277             d1 |= static_cast<unsigned char>(data_[pos]);
00278         }
00279
00280         for (; pos < 6; ++pos)
00281         {
00282             d2 <<= charbit;
00283             d2 |= static_cast<unsigned char>(data_[pos]);
00284         }
00285
00286         for (; pos < 8; ++pos)
00287         {
00288             d3 <<= charbit;
00289             d3 |= static_cast<unsigned char>(data_[pos]);
00290         }
00291
00292         for (std::size_t j = 0; j < sizeof(d4); ++j)
00293         {
00294             d4[j] = data_[j + 8];
00295         }
00296     }
00297
00301     static guid const& null() /* throw() */
00302     {
00303         static const guid n;
00304         return n;
00305     }
00306
00310     static guid create()
00311     {
00312         return create_random_based();
00313     }
00314
00317     static guid create(guid const& namespace_guid, char const* name, int name_length)
00318     {
00319         return create_name_based(namespace_guid, name, name_length);
00320     }
00321
00324     static inline bool get_showbraces(std::ios_base & iosbase)
00325     {
00326         return (iosbase.iword(get_showbraces_index()) != 0);
00327     }
00328
00331     static inline void set_showbraces(std::ios_base & iosbase, bool showbraces)
00332     {
00333         iosbase.iword(get_showbraces_index()) = showbraces;
00334     }
00335
00338     static inline std::ios_base& showbraces(std::ios_base& iosbase)
00339     {
00340         set_showbraces(iosbase, true);
00341         return iosbase;
00342     }
00343     static inline std::ios_base& noshowbraces(std::ios_base& iosbase)
00344     {
00345         set_showbraces(iosbase, false);
00346         return iosbase;
00347     }
00348
00350     friend std::ostream& operator<<(std::ostream& os, guid const& g);
00351
00353     friend std::istream& operator>>(std::istream& is, guid &g);
00354
00355 private:
00356
00357     void throw_invalid_argument() const
00358     {
00359         throw std::invalid_argument("invalid guid string");
00360     }
00361
00362     template <typename ch, typename char_traits, typename alloc>
00363     void construct(std::basic_string<ch, char_traits, alloc> const& str)
00364     {
00365         std::basic_stringstream <ch, char_traits, alloc > ss;
00366         if (!(ss << str) || !(ss >> *this))
00367         {
00368             throw_invalid_argument();
00369         }
00370     }
00371
00372     void construct(liblas::uint32_t const& d1, liblas::uint16_t const& d2, liblas::uint16_t const& d3, liblas::uint8_t const (&d4)[8])
00373     {
00374         std::ostringstream ss;
00375         ss.flags(std::ios::hex);
00376         ss.fill('0');
00377
00378         ss.width(8);
00379         ss << d1;
00380         ss << '-';
00381
00382         ss.width(4);
00383         ss << d2;
00384         ss << '-';
00385
00386         ss.width(4);
00387         ss << d3;
00388         ss << '-';
00389
00390         for (std::size_t i = 0; i < sizeof(d4); ++i)
00391         {
00392             ss.width(2);
00393             ss << static_cast<liblas::uint32_t>(d4[i]);
00394             if (1 == i)
00395                 ss << '-';
00396         }
00397
00398         construct(ss.str());
00399     }
00400
00401     //random number based
00402     static guid create_random_based() // throw()
00403     {
00404         guid result;
00405         static bool init_rand = true;
00406         if (init_rand)
00407         {
00408             std::srand(static_cast<unsigned int>(std::time(0)));
00409             init_rand = false;
00410         }
00411
00412         for (size_t i = 0; i < static_size; i++)
00413         {
00414             result.data_[i] = detail::generate_random_byte<liblas::uint8_t>();
00415         }
00416
00417         // set variant
00418         // should be 0b10xxxxxx
00419         result.data_[8] &= 0xBF;
00420         result.data_[8] |= 0x80;
00421
00422        // set version
00423         // should be 0b0100xxxx
00424         result.data_[6] &= 0x4F; //0b01001111
00425         result.data_[6] |= 0x40; //0b01000000
00426
00427         return result;
00428     }
00429
00430     // name based
00431     static guid create_name_based(guid const& namespace_guid, char const* name, int name_length)
00432     {
00433         using liblas::uint8_t;
00434
00435         detail::SHA1 sha1;
00436         sha1.Input(namespace_guid.data_, namespace_guid.static_size);
00437         sha1.Input(name, name_length);
00438         unsigned int digest[5];
00439
00440         if (sha1.Result(digest) == false)
00441         {
00442             throw std::runtime_error("create error");
00443         }
00444
00445         guid result;
00446         for (int i = 0; i < 4; ++i)
00447         {
00448
00449             result.data_[i*4+0] = static_cast<uint8_t>((digest[i] >> 24) & 0xFF);
00450             result.data_[i*4+1] = static_cast<uint8_t>((digest[i] >> 16) & 0xFF);
00451             result.data_[i*4+2] = static_cast<uint8_t>((digest[i] >> 8) & 0xFF);
00452             result.data_[i*4+3] = static_cast<uint8_t>((digest[i] >> 0) & 0xFF);
00453         }
00454
00455         // set variant
00456         // should be 0b10xxxxxx
00457         result.data_[8] &= 0xBF;
00458         result.data_[8] |= 0x80;
00459
00460         // set version
00461         // should be 0b0101xxxx
00462         result.data_[6] &= 0x5F; //0b01011111
00463         result.data_[6] |= 0x50; //0b01010000
00464
00465         return result;
00466     }
00467
00468     static int get_showbraces_index()
00469     {
00470         static int index = std::ios_base::xalloc();
00471         return index;
00472     }
00473
00474 private:
00475
00476     static const std::size_t static_size = 16;
00477     liblas::uint8_t data_[static_size];
00478 };
00479
00480 inline std::ostream& operator<<(std::ostream& os, guid const& g)
00481 {
00482     using namespace std;
00483
00484     // TODO: If optional support of Boost is added,
00485     // use Boost I/O State Savers for safe RAII.
00486     std::ios_base::fmtflags flags_saver(os.flags());
00487     std::streamsize width_saver(os.width());
00488     std::ostream::char_type fill_saver(os.fill());
00489
00490     const std::ostream::sentry ok(os);
00491     if (ok)
00492     {
00493         bool showbraces = guid::get_showbraces(os);
00494         if (showbraces)
00495         {
00496             os << '{';
00497         }
00498         os << hex;
00499         os.fill('0');
00500         for (size_t i = 0; i < g.static_size; ++i)
00501         {
00502             os.width(2);
00503             os << static_cast<unsigned int>(g.data_[i]);
00504             if (i == 3 || i == 5 || i == 7 || i == 9)
00505             {
00506                 os << '-';
00507             }
00508         }
00509         if (showbraces)
00510         {
00511             os << '}';
00512         }
00513     }
00514
00515     os.flags(flags_saver);
00516     os.width(width_saver);
00517     os.fill(fill_saver);
00518
00519     return os;
00520 }
00521
00522 inline std::istream& operator>>(std::istream& is, guid &g)
00523 {
00524     using namespace std;
00525
00526     typedef std::istream::char_type char_type;
00527     guid temp_guid;
00528     const std::istream::sentry ok(is);
00529     if (ok)
00530     {
00531         char_type c;
00532         c = static_cast<char_type>(is.peek());
00533         bool bHaveBraces = false;
00534         if (c == '{')
00535         {
00536             bHaveBraces = true;
00537             is >> c; // read brace
00538         }
00539
00540         for (size_t i = 0; i < temp_guid.static_size && is; ++i)
00541         {
00542             std::stringstream ss;
00543
00544             // read 2 characters into stringstream
00545             is >> c; ss << c;
00546             is >> c; ss << c;
00547
00548             // extract 2 characters from stringstream as a hex number
00549             unsigned int val = 0;
00550             ss >> hex >> val;
00551             if (!ss)
00552             {
00553                 is.setstate(ios_base::failbit);
00554             }
00555
00556             // check that val is within valid range
00557             if (val > 255)
00558             {
00559                 is.setstate(ios_base::badbit);
00560             }
00561
00562             temp_guid.data_[i] = static_cast<liblas::uint8_t>(val);
00563
00564             if (is)
00565             {
00566                 if (i == 3 || i == 5 || i == 7 || i == 9)
00567                 {
00568                     is >> c;
00569                     if (c != '-')
00570                         is.setstate(ios_base::failbit);
00571                 }
00572             }
00573         }
00574
00575         if (bHaveBraces && is)
00576         {
00577             is >> c;
00578             if (c != '}')
00579                 is.setstate(ios_base::failbit);
00580         }
00581
00582         if (is)
00583         {
00584             g = temp_guid;
00585         }
00586     }
00587
00588     return is;
00589 }
00590
00591 } //namespace liblas
00592
00593 #endif // LIBLAS_GUID_HPP_INCLUDED