BIND 10 trac749, updated. ecfc86a4afb1d26fcd48054b0406e153daf571ee [trac749] commit directory src/lib/util
BIND 10 source code commits
bind10-changes at lists.isc.org
Tue Apr 12 13:30:18 UTC 2011
The branch, trac749 has been updated
via ecfc86a4afb1d26fcd48054b0406e153daf571ee (commit)
from 36dd3d4b0bdd8c40695ac186955196bd268db586 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit ecfc86a4afb1d26fcd48054b0406e153daf571ee
Author: chenzhengzhang <jerry.zzpku at gmail.com>
Date: Tue Apr 12 21:29:23 2011 +0800
[trac749] commit directory src/lib/util
-----------------------------------------------------------------------
Summary of changes:
src/lib/util/Makefile.am | 27 ++
src/lib/util/README | 31 ++
.../util/compat/.deps/libutil_compat_la-base_n.Plo | 1 +
.../util/compat/.deps/libutil_compat_la-sha1.Plo | 1 +
src/lib/util/compat/base16_from_binary.h | 108 +++++
src/lib/util/compat/base32hex.h | 64 +++
src/lib/util/compat/base32hex_from_binary.h | 110 +++++
src/lib/util/compat/base64.h | 79 ++++
src/lib/util/compat/base_n.cc | 398 ++++++++++++++++
src/lib/util/compat/binary_from_base16.h | 120 +++++
src/lib/util/compat/binary_from_base32hex.h | 123 +++++
src/lib/util/compat/hex.h | 65 +++
src/lib/util/compat/sha1.cc | 484 ++++++++++++++++++++
src/lib/util/compat/sha1.h | 84 ++++
src/lib/util/compat/time_utilities.cc | 212 +++++++++
src/lib/util/compat/time_utilities.h | 147 ++++++
src/lib/util/filename.cc | 137 ++++++
src/lib/util/filename.h | 161 +++++++
src/lib/util/io/buffer.h | 448 ++++++++++++++++++
src/lib/util/io/io_utilities.h | 65 +++
src/lib/util/locks.h | 116 +++++
src/lib/util/lru_list.h | 260 +++++++++++
src/lib/util/random/qid_gen.cc | 57 +++
src/lib/util/random/qid_gen.h | 87 ++++
src/lib/util/random/random_number_generator.h | 208 +++++++++
src/lib/util/strutil.cc | 135 ++++++
src/lib/util/strutil.h | 145 ++++++
src/lib/util/tests/Makefile.am | 41 ++
src/lib/util/tests/base32hex_unittest.cc | 159 +++++++
src/lib/util/tests/base64_unittest.cc | 93 ++++
src/lib/util/tests/buffer_unittest.cc | 193 ++++++++
src/lib/util/tests/filename_unittest.cc | 179 ++++++++
src/lib/util/tests/hex_unittest.cc | 120 +++++
src/lib/util/tests/io_utilities_unittest.cc | 73 +++
src/lib/util/tests/lru_list_unittest.cc | 427 +++++++++++++++++
src/lib/util/tests/qid_gen_unittest.cc | 60 +++
.../util/tests/random_number_generator_unittest.cc | 311 +++++++++++++
src/lib/{log => util}/tests/run_unittests.cc | 0
src/lib/util/tests/sha1_unittest.cc | 107 +++++
src/lib/util/tests/strutil_unittest.cc | 214 +++++++++
src/lib/util/tests/time_utilities_unittest.cc | 163 +++++++
41 files changed, 6013 insertions(+), 0 deletions(-)
create mode 100644 src/lib/util/Makefile.am
create mode 100644 src/lib/util/README
create mode 100644 src/lib/util/compat/.deps/libutil_compat_la-base_n.Plo
create mode 100644 src/lib/util/compat/.deps/libutil_compat_la-sha1.Plo
create mode 100644 src/lib/util/compat/base16_from_binary.h
create mode 100644 src/lib/util/compat/base32hex.h
create mode 100644 src/lib/util/compat/base32hex_from_binary.h
create mode 100644 src/lib/util/compat/base64.h
create mode 100644 src/lib/util/compat/base_n.cc
create mode 100644 src/lib/util/compat/binary_from_base16.h
create mode 100644 src/lib/util/compat/binary_from_base32hex.h
create mode 100644 src/lib/util/compat/hex.h
create mode 100644 src/lib/util/compat/sha1.cc
create mode 100644 src/lib/util/compat/sha1.h
create mode 100644 src/lib/util/compat/time_utilities.cc
create mode 100644 src/lib/util/compat/time_utilities.h
create mode 100644 src/lib/util/filename.cc
create mode 100644 src/lib/util/filename.h
create mode 100644 src/lib/util/io/buffer.h
create mode 100644 src/lib/util/io/io_utilities.h
create mode 100644 src/lib/util/locks.h
create mode 100644 src/lib/util/lru_list.h
create mode 100644 src/lib/util/random/qid_gen.cc
create mode 100644 src/lib/util/random/qid_gen.h
create mode 100644 src/lib/util/random/random_number_generator.h
create mode 100644 src/lib/util/strutil.cc
create mode 100644 src/lib/util/strutil.h
create mode 100644 src/lib/util/tests/Makefile.am
create mode 100644 src/lib/util/tests/base32hex_unittest.cc
create mode 100644 src/lib/util/tests/base64_unittest.cc
create mode 100644 src/lib/util/tests/buffer_unittest.cc
create mode 100644 src/lib/util/tests/filename_unittest.cc
create mode 100644 src/lib/util/tests/hex_unittest.cc
create mode 100644 src/lib/util/tests/io_utilities_unittest.cc
create mode 100644 src/lib/util/tests/lru_list_unittest.cc
create mode 100644 src/lib/util/tests/qid_gen_unittest.cc
create mode 100644 src/lib/util/tests/random_number_generator_unittest.cc
copy src/lib/{log => util}/tests/run_unittests.cc (100%)
create mode 100644 src/lib/util/tests/sha1_unittest.cc
create mode 100644 src/lib/util/tests/strutil_unittest.cc
create mode 100644 src/lib/util/tests/time_utilities_unittest.cc
-----------------------------------------------------------------------
diff --git a/src/lib/util/Makefile.am b/src/lib/util/Makefile.am
new file mode 100644
index 0000000..46ec749
--- /dev/null
+++ b/src/lib/util/Makefile.am
@@ -0,0 +1,27 @@
+SUBDIRS = . tests
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/util -I$(top_builddir)/src/lib/util
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/exceptions -I$(top_builddir)/src/lib/exceptions
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+lib_LTLIBRARIES = libutil.la
+libutil_la_SOURCES = filename.h filename.cc
+libutil_la_SOURCES += locks.h lru_list.h
+libutil_la_SOURCES += strutil.h strutil.cc
+libutil_la_SOURCES += compat/base16_from_binary.h
+libutil_la_SOURCES += compat/base32hex.h compat/base64.h
+libutil_la_SOURCES += compat/base32hex_from_binary.h
+libutil_la_SOURCES += compat/base_n.cc compat/hex.h
+libutil_la_SOURCES += compat/binary_from_base32hex.h
+libutil_la_SOURCES += compat/binary_from_base16.h
+libutil_la_SOURCES += compat/sha1.h compat/sha1.cc
+libutil_la_SOURCES += compat/time_utilities.h
+libutil_la_SOURCES += compat/time_utilities.cc
+libutil_la_SOURCES += io/buffer.h io/io_utilities.h
+libutil_la_SOURCES += random/qid_gen.h random/qid_gen.cc
+libutil_la_SOURCES += random/random_number_generator.h
+
+libutil_la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
+CLEANFILES = *.gcno *.gcda
diff --git a/src/lib/util/README b/src/lib/util/README
new file mode 100644
index 0000000..a4c45fb
--- /dev/null
+++ b/src/lib/util/README
@@ -0,0 +1,31 @@
+This "util" directory is provided for utility header files and
+implementations.
+.
+
+The functionality provided in these tools is generally available in
+other external or perhaps system supplied libraries. The basic
+development policy of BIND 10 is to avoid "reinventing wheels" unless
+they belong to the exact technology area that BIND 10 targets (e.g.,
+DNS). However, libdns++ is a very core part of BIND 10, and is also
+intended to be used as a public library, so dependency from libdns++
+to external libraries should be minimized. The utilities in this
+directory are provided balancing two policies and as a kind of
+compromise.
+
+The header files in this directory are therefore not intended to be
+installed. Likewise, classes and public functions defined in this
+directory are not intended to be used outside libdns++, although we
+cannot prohibit it at the language level.
+
+They are not even expected to be used in other modules of BIND 10 than
+libdns++ based on the basic policy explained above. Other modules
+should only rely on the DNS specific interface that may internally
+rely on these utility interfaces, or should use external libraries if
+the other module really needs to use the utility feature directly.
+There seem to be some violations as of this writing, but we should
+eventually fix it. A notable example is the SHA1 interfaces. They
+are defined here in the context of NSEC3 processing, but, in fact,
+they are not even used from any of the other libdns++ classes or
+functions. The SHA1 related interfaces should be moved to the
+application that needs it or the application should only access it
+through DNS specific interfaces defined in libdns++.
diff --git a/src/lib/util/compat/.deps/libutil_compat_la-base_n.Plo b/src/lib/util/compat/.deps/libutil_compat_la-base_n.Plo
new file mode 100644
index 0000000..9ce06a8
--- /dev/null
+++ b/src/lib/util/compat/.deps/libutil_compat_la-base_n.Plo
@@ -0,0 +1 @@
+# dummy
diff --git a/src/lib/util/compat/.deps/libutil_compat_la-sha1.Plo b/src/lib/util/compat/.deps/libutil_compat_la-sha1.Plo
new file mode 100644
index 0000000..9ce06a8
--- /dev/null
+++ b/src/lib/util/compat/.deps/libutil_compat_la-sha1.Plo
@@ -0,0 +1 @@
+# dummy
diff --git a/src/lib/util/compat/base16_from_binary.h b/src/lib/util/compat/base16_from_binary.h
new file mode 100644
index 0000000..8606c61
--- /dev/null
+++ b/src/lib/util/compat/base16_from_binary.h
@@ -0,0 +1,108 @@
+#ifndef BOOST_ARCHIVE_ITERATORS_BASE16_FROM_BINARY_HPP
+#define BOOST_ARCHIVE_ITERATORS_BASE16_FROM_BINARY_HPP
+
+// MS compatible compilers support #pragma once
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+# pragma once
+#endif
+
+/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
+// base16_from_binary.h (derived from boost base64_from_binary.hpp)
+
+// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+// See http://www.boost.org for updates, documentation, and revision history.
+
+#include <cassert>
+
+#include <cstddef> // size_t
+#include <boost/config.hpp> // for BOOST_DEDUCED_TYPENAME
+#if defined(BOOST_NO_STDC_NAMESPACE)
+namespace std{
+ using ::size_t;
+} // namespace std
+#endif
+
+// See base32hex_from_binary.h for why we need base64_from...hpp here.
+#include <boost/archive/iterators/base64_from_binary.hpp>
+
+namespace boost {
+namespace archive {
+namespace iterators {
+
+/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
+// convert binary integers to base16 characters
+
+namespace detail {
+
+template<class CharType>
+struct from_4_bit {
+ typedef CharType result_type;
+ CharType operator()(CharType t) const{
+ const char * lookup_table =
+ "0123456789"
+ "ABCDEF";
+ assert(t < 16);
+ return (lookup_table[static_cast<size_t>(t)]);
+ }
+};
+
+} // namespace detail
+
+// note: what we would like to do is
+// template<class Base, class CharType = BOOST_DEDUCED_TYPENAME Base::value_type>
+// typedef transform_iterator<
+// from_4_bit<CharType>,
+// transform_width<Base, 4, sizeof(Base::value_type) * 8, CharType>
+// > base16_from_binary;
+// but C++ won't accept this. Rather than using a "type generator" and
+// using a different syntax, make a derivation which should be equivalent.
+//
+// Another issue addressed here is that the transform_iterator doesn't have
+// a templated constructor. This makes it incompatible with the dataflow
+// ideal. This is also addressed here.
+
+//template<class Base, class CharType = BOOST_DEDUCED_TYPENAME Base::value_type>
+template<
+ class Base,
+ class CharType = BOOST_DEDUCED_TYPENAME boost::iterator_value<Base>::type
+>
+class base16_from_binary :
+ public transform_iterator<
+ detail::from_4_bit<CharType>,
+ Base
+ >
+{
+ friend class boost::iterator_core_access;
+ typedef transform_iterator<
+ BOOST_DEDUCED_TYPENAME detail::from_4_bit<CharType>,
+ Base
+ > super_t;
+
+public:
+ // make composible buy using templated constructor
+ template<class T>
+ base16_from_binary(BOOST_PFTO_WRAPPER(T) start) :
+ super_t(
+ Base(BOOST_MAKE_PFTO_WRAPPER(static_cast<T>(start))),
+ detail::from_4_bit<CharType>()
+ )
+ {}
+ // intel 7.1 doesn't like default copy constructor
+ base16_from_binary(const base16_from_binary & rhs) :
+ super_t(
+ Base(rhs.base_reference()),
+ detail::from_4_bit<CharType>()
+ )
+ {}
+// base16_from_binary(){};
+};
+
+} // namespace iterators
+} // namespace archive
+} // namespace boost
+
+#endif // BOOST_ARCHIVE_ITERATORS_BASE16_FROM_BINARY_HPP
diff --git a/src/lib/util/compat/base32hex.h b/src/lib/util/compat/base32hex.h
new file mode 100644
index 0000000..d52679f
--- /dev/null
+++ b/src/lib/util/compat/base32hex.h
@@ -0,0 +1,64 @@
+// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __BASE32HEX_H
+#define __BASE32HEX_H 1
+
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+//
+// Note: this helper module isn't specific to the DNS protocol per se.
+// We should probably move this to somewhere else, possibly in some common
+// utility area.
+//
+
+namespace isc {
+namespace util {
+namespace compat {
+
+/// \brief Encode binary data in the base32hex format.
+///
+/// The underlying implementation is shared with \c encodeBase64, and all
+/// description except the format (base32hex) equally applies.
+///
+/// Note: the encoding format is base32hex, not base32.
+///
+/// \param binary A vector object storing the data to be encoded.
+/// \return A newly created string that stores base32hex encoded value for
+/// binary.
+std::string encodeBase32Hex(const std::vector<uint8_t>& binary);
+
+/// \brief Decode a text encoded in the base32hex format into the
+/// original %data.
+///
+/// The underlying implementation is shared with \c decodeBase64, and all
+/// description except the format (base32hex) equally applies.
+///
+/// Note: the encoding format is base32hex, not base32.
+///
+/// \param input A text encoded in the base32hex format.
+/// \param result A vector in which the decoded %data is to be stored.
+void decodeBase32Hex(const std::string& input, std::vector<uint8_t>& result);
+
+} // namespace compat
+} // namespace util
+} // namespace isc
+
+#endif // __BASE32HEX_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/util/compat/base32hex_from_binary.h b/src/lib/util/compat/base32hex_from_binary.h
new file mode 100644
index 0000000..5d16d04
--- /dev/null
+++ b/src/lib/util/compat/base32hex_from_binary.h
@@ -0,0 +1,110 @@
+#ifndef BOOST_ARCHIVE_ITERATORS_BASE32HEX_FROM_BINARY_HPP
+#define BOOST_ARCHIVE_ITERATORS_BASE32HEX_FROM_BINARY_HPP
+
+// MS compatible compilers support #pragma once
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+# pragma once
+#endif
+
+/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
+// base32hex_from_binary.h (derived from boost base64_from_binary.hpp)
+
+// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+// See http://www.boost.org for updates, documentation, and revision history.
+
+#include <cassert>
+
+#include <cstddef> // size_t
+#include <boost/config.hpp> // for BOOST_DEDUCED_TYPENAME
+#if defined(BOOST_NO_STDC_NAMESPACE)
+namespace std{
+ using ::size_t;
+} // namespace std
+#endif
+
+// We use the same boost header files used in "base64_from_". Since the
+// precise path to these headers may vary depending on the boost version we
+// simply include the base64 header here.
+#include <boost/archive/iterators/base64_from_binary.hpp>
+
+namespace boost {
+namespace archive {
+namespace iterators {
+
+/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
+// convert binary integers to base32hex characters
+
+namespace detail {
+
+template<class CharType>
+struct from_5_bit {
+ typedef CharType result_type;
+ CharType operator()(CharType t) const{
+ const char * lookup_table =
+ "0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUV";
+ assert(t < 32);
+ return (lookup_table[static_cast<size_t>(t)]);
+ }
+};
+
+} // namespace detail
+
+// note: what we would like to do is
+// template<class Base, class CharType = BOOST_DEDUCED_TYPENAME Base::value_type>
+// typedef transform_iterator<
+// from_5_bit<CharType>,
+// transform_width<Base, 5, sizeof(Base::value_type) * 8, CharType>
+// > base32hex_from_binary;
+// but C++ won't accept this. Rather than using a "type generator" and
+// using a different syntax, make a derivation which should be equivalent.
+//
+// Another issue addressed here is that the transform_iterator doesn't have
+// a templated constructor. This makes it incompatible with the dataflow
+// ideal. This is also addressed here.
+
+//template<class Base, class CharType = BOOST_DEDUCED_TYPENAME Base::value_type>
+template<
+ class Base,
+ class CharType = BOOST_DEDUCED_TYPENAME boost::iterator_value<Base>::type
+>
+class base32hex_from_binary :
+ public transform_iterator<
+ detail::from_5_bit<CharType>,
+ Base
+ >
+{
+ friend class boost::iterator_core_access;
+ typedef transform_iterator<
+ BOOST_DEDUCED_TYPENAME detail::from_5_bit<CharType>,
+ Base
+ > super_t;
+
+public:
+ // make composible buy using templated constructor
+ template<class T>
+ base32hex_from_binary(BOOST_PFTO_WRAPPER(T) start) :
+ super_t(
+ Base(BOOST_MAKE_PFTO_WRAPPER(static_cast<T>(start))),
+ detail::from_5_bit<CharType>()
+ )
+ {}
+ // intel 7.1 doesn't like default copy constructor
+ base32hex_from_binary(const base32hex_from_binary & rhs) :
+ super_t(
+ Base(rhs.base_reference()),
+ detail::from_5_bit<CharType>()
+ )
+ {}
+// base32hex_from_binary(){};
+};
+
+} // namespace iterators
+} // namespace archive
+} // namespace boost
+
+#endif // BOOST_ARCHIVE_ITERATORS_BASE32HEX_FROM_BINARY_HPP
diff --git a/src/lib/util/compat/base64.h b/src/lib/util/compat/base64.h
new file mode 100644
index 0000000..112ad23
--- /dev/null
+++ b/src/lib/util/compat/base64.h
@@ -0,0 +1,79 @@
+// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __BASE64_H
+#define __BASE64_H 1
+
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+//
+// Note: this helper module isn't specific to the DNS protocol per se.
+// We should probably move this to somewhere else, possibly in some common
+// utility area.
+//
+
+namespace isc {
+namespace util {
+namespace compat {
+
+/// \brief Encode binary data in the base64 format.
+///
+/// This function returns a new \c std::string object that stores a text
+/// encoded in the base64 format for the given \c binary %data.
+/// The resulting string will be a valid, canonical form of base64
+/// representation as specified in RFC4648.
+///
+/// If memory allocation for the returned string fails, a corresponding
+/// standard exception will be thrown. This function never throws exceptions
+/// otherwise.
+///
+/// \param binary A vector object storing the data to be encoded.
+/// \return A newly created string that stores base64 encoded value for binary.
+std::string encodeBase64(const std::vector<uint8_t>& binary);
+
+/// \brief Decode a text encoded in the base64 format into the original %data.
+///
+/// The \c input argument must be a valid string represented in the base64
+/// format as specified in RFC4648. Space characters (spaces, tabs, newlines)
+/// can be included in \c input and will be ignored. Without spaces, the
+/// length of string must be a multiple of 4 bytes with necessary paddings.
+/// Also it must be encoded using the canonical encoding (see RFC4648).
+/// If any of these conditions is not met, an exception of class
+/// \c isc::BadValue will be thrown.
+///
+/// If \c result doesn't have sufficient capacity to store all decoded %data
+/// and memory allocation fails, a corresponding standard exception will be
+/// thrown. If the caller knows the necessary length (which can in theory
+/// be calculated from the input string), this situation can be avoided by
+/// reserving sufficient space for \c result beforehand.
+///
+/// Any existing %data in \c result will be removed. This is the case in some
+/// of the cases where an exception is thrown; that is, this function only
+/// provides the basic exception guarantee.
+///
+/// \param input A text encoded in the base64 format.
+/// \param result A vector in which the decoded %data is to be stored.
+void decodeBase64(const std::string& input, std::vector<uint8_t>& result);
+
+} // namespace compat
+} // namespace util
+} // namespace isc
+
+#endif // __BASE64_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/util/compat/base_n.cc b/src/lib/util/compat/base_n.cc
new file mode 100644
index 0000000..e35c784
--- /dev/null
+++ b/src/lib/util/compat/base_n.cc
@@ -0,0 +1,398 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <stdint.h>
+#include <cassert>
+#include <iterator>
+#include <string>
+#include <vector>
+
+#include <boost/archive/iterators/base64_from_binary.hpp>
+#include <boost/archive/iterators/binary_from_base64.hpp>
+#include <boost/archive/iterators/transform_width.hpp>
+#include <boost/math/common_factor.hpp>
+
+#include <util/compat/base32hex_from_binary.h>
+#include <util/compat/binary_from_base32hex.h>
+#include <util/compat/base16_from_binary.h>
+#include <util/compat/binary_from_base16.h>
+#include <util/compat/base32hex.h>
+#include <util/compat/base64.h>
+
+#include <exceptions/exceptions.h>
+
+using namespace std;
+using namespace boost::archive::iterators;
+
+namespace isc {
+namespace util {
+namespace compat {
+
+// In the following anonymous namespace, we provide a generic framework
+// to encode/decode baseN format. We use the following tools:
+// - boost base64_from_binary/binary_from_base64: provide mapping table for
+// base64.
+// These classes take another iterator (Base) as a template argument, and
+// their dereference operator (operator*()) first retrieves an input value
+// from Base via Base::operator* and converts the value using their mapping
+// table. The converted value is returned as their own operator*.
+// - base{32hex,16}_from_binary/binary_from_base{32hex,16}: provide mapping
+// table for base32hex and base16. A straightforward variation of their
+// base64 counterparts.
+// - EncodeNormalizer/DecodeNormalizer: supplemental filter handling baseN
+// padding characters (=)
+// - boost transform_width: an iterator framework for handling data stream
+// per bit-group. It takes another iterator (Base) and output/input bit
+// numbers (BitsOut/BitsIn) template arguments. A transform_width object
+// internally maintains a bit stream, which can be retrieved per BitsOut
+// bits via its dereference operator (operator*()). It builds the stream
+// by internally iterating over the Base object via Base::operator++ and
+// Base::operator*, using the least BitsIn bits of the result of
+// Base::operator*. In our usage BitsIn for encoding and BitsOut for
+// decoding are always 8 (# of bits for one byte).
+//
+// Its dereference operator
+// retrieves BitsIn bits from the result of "*Base" (if necessary it
+// internally calls ++Base)
+//
+// A conceptual description of how the encoding and decoding work is as
+// follows:
+// Encoding:
+// input binary data => Normalizer (append sufficient number of 0 bits)
+// => transform_width (extract bit groups from the original
+// stream)
+// => baseXX_from_binary (convert each bit group to an
+// encoded byte using the mapping)
+// Decoding:
+// input baseXX text => Normalizer (convert '='s to the encoded characters
+// corresponding to 0, e.g. 'A's in base64)
+// => binary_from_baseXX (convert each encoded byte into
+// the original group bit)
+// => transform_width (build original byte stream by
+// concatenating the decoded bit
+// stream)
+//
+// Below, we define a set of templated classes to handle different parameters
+// for different encoding algorithms.
+namespace {
+// Common constants used for all baseN encoding.
+const char BASE_PADDING_CHAR = '=';
+const uint8_t BINARY_ZERO_CODE = 0;
+
+// EncodeNormalizer is an input iterator intended to be used as a filter
+// between the binary stream and baseXX_from_binary translator (via
+// transform_width). An EncodeNormalizer object is configured with two
+// iterators (base and base_end), specifying the head and end of the input
+// stream. It internally iterators over the original stream, and return
+// each byte of the stream intact via its dereference operator until it
+// reaches the end of the stream. After that the EncodeNormalizer object
+// will return 0 no matter how many times it is subsequently incremented.
+// This is necessary because the input binary stream may not contain
+// sufficient bits for a full encoded text while baseXX_from_binary expects
+// a sufficient length of input.
+// Note: this class is intended to be used within this implementation file,
+// and assumes "base < base_end" on construction without validating the
+// arguments. The behavior is undefined if this assumption doesn't hold.
+class EncodeNormalizer : public iterator<input_iterator_tag, uint8_t> {
+public:
+ EncodeNormalizer(const vector<uint8_t>::const_iterator& base,
+ const vector<uint8_t>::const_iterator& base_end) :
+ base_(base), base_end_(base_end), in_pad_(false)
+ {}
+ EncodeNormalizer& operator++() {
+ if (!in_pad_) {
+ ++base_;
+ }
+ if (base_ == base_end_) {
+ in_pad_ = true;
+ }
+ return (*this);
+ }
+ const uint8_t& operator*() const {
+ if (in_pad_) {
+ return (BINARY_ZERO_CODE);
+ } else {
+ return (*base_);
+ }
+ }
+ bool operator==(const EncodeNormalizer& other) const {
+ return (base_ == other.base_);
+ }
+private:
+ vector<uint8_t>::const_iterator base_;
+ const vector<uint8_t>::const_iterator base_end_;
+ bool in_pad_;
+};
+
+// DecodeNormalizer is an input iterator intended to be used as a filter
+// between the encoded baseX stream and binary_from_baseXX.
+// A DecodeNormalizer object is configured with three string iterators
+// (base, base_beinpad, and base_beginpad), specifying the head of the string,
+// the beginning position of baseX padding (when there's padding), and
+// end of the string, respectively. It internally iterators over the original
+// stream, and return each character of the encoded string via its dereference
+// operator until it reaches base_beginpad. After that the DecodeNormalizer
+// will return the encoding character corresponding to the all-0 value
+// (which is specified on construction via base_zero_code. see also
+// BaseZeroCode below). This translation is necessary because
+// binary_from_baseXX doesn't accept the padding character (i.e. '=').
+// Note: this class is intended to be used within this implementation file,
+// and for simplicity assumes "base < base_beginpad <= base_end" on
+// construction without validating the arguments. The behavior is undefined
+// if this assumption doesn't hold.
+class DecodeNormalizer : public iterator<input_iterator_tag, char> {
+public:
+ DecodeNormalizer(const char base_zero_code,
+ const string::const_iterator& base,
+ const string::const_iterator& base_beginpad,
+ const string::const_iterator& base_end) :
+ base_zero_code_(base_zero_code),
+ base_(base), base_beginpad_(base_beginpad), base_end_(base_end),
+ in_pad_(false)
+ {}
+ DecodeNormalizer& operator++() {
+ ++base_;
+ while (base_ != base_end_ && isspace(*base_)) {
+ ++base_;
+ }
+ if (base_ == base_beginpad_) {
+ in_pad_ = true;
+ }
+ return (*this);
+ }
+ const char& operator*() const {
+ if (in_pad_ && *base_ == BASE_PADDING_CHAR) {
+ return (base_zero_code_);
+ } else {
+ return (*base_);
+ }
+ }
+ bool operator==(const DecodeNormalizer& other) const {
+ return (base_ == other.base_);
+ }
+private:
+ const char base_zero_code_;
+ string::const_iterator base_;
+ const string::const_iterator base_beginpad_;
+ const string::const_iterator base_end_;
+ bool in_pad_;
+};
+
+// BitsPerChunk: number of bits to be converted using the baseN mapping table.
+// e.g. 6 for base64.
+// BaseZeroCode: the byte character that represents a value of 0 in
+// the corresponding encoding. e.g. 'A' for base64.
+// Encoder: baseX_from_binary<transform_width<EncodeNormalizer,
+// BitsPerChunk, 8> >
+// Decoder: transform_width<binary_from_baseX<DecodeNormalizer>,
+// 8, BitsPerChunk>
+template <int BitsPerChunk, char BaseZeroCode,
+ typename Encoder, typename Decoder>
+struct BaseNTransformer {
+ static string encode(const vector<uint8_t>& binary);
+ static void decode(const char* algorithm,
+ const string& base64, vector<uint8_t>& result);
+
+ // BITS_PER_GROUP is the number of bits for the smallest possible (non
+ // empty) bit string that can be converted to a valid baseN encoded text
+ // without padding. It's the least common multiple of 8 and BitsPerChunk,
+ // e.g. 24 for base64.
+ static const int BITS_PER_GROUP =
+ boost::math::static_lcm<BitsPerChunk, 8>::value;
+
+ // MAX_PADDING_CHARS is the maximum number of padding characters
+ // that can appear in a valid baseN encoded text.
+ // It's group_len - chars_for_byte, where group_len is the number of
+ // encoded characters to represent BITS_PER_GROUP bits, and
+ // chars_for_byte is the number of encoded character that is needed to
+ // represent a single byte, which is ceil(8 / BitsPerChunk).
+ // For example, for base64 we need two encoded characters to represent a
+ // byte, and each group consists of 4 encoded characters, so
+ // MAX_PADDING_CHARS is 4 - 2 = 2.
+ static const int MAX_PADDING_CHARS =
+ BITS_PER_GROUP / BitsPerChunk -
+ (8 / BitsPerChunk + ((8 % BitsPerChunk) == 0 ? 0 : 1));
+};
+
+template <int BitsPerChunk, char BaseZeroCode,
+ typename Encoder, typename Decoder>
+string
+BaseNTransformer<BitsPerChunk, BaseZeroCode, Encoder, Decoder>::encode(
+ const vector<uint8_t>& binary)
+{
+ // calculate the resulting length.
+ size_t bits = binary.size() * 8;
+ if (bits % BITS_PER_GROUP > 0) {
+ bits += (BITS_PER_GROUP - (bits % BITS_PER_GROUP));
+ }
+ const size_t len = bits / BitsPerChunk;
+
+ string result;
+ result.reserve(len);
+ result.assign(Encoder(EncodeNormalizer(binary.begin(), binary.end())),
+ Encoder(EncodeNormalizer(binary.end(), binary.end())));
+ assert(len >= result.length());
+ result.append(len - result.length(), BASE_PADDING_CHAR);
+ return (result);
+}
+
+template <int BitsPerChunk, char BaseZeroCode,
+ typename Encoder, typename Decoder>
+void
+BaseNTransformer<BitsPerChunk, BaseZeroCode, Encoder, Decoder>::decode(
+ const char* const algorithm,
+ const string& input,
+ vector<uint8_t>& result)
+{
+ // enumerate the number of trailing padding characters (=), ignoring
+ // white spaces. since baseN_from_binary doesn't accept padding,
+ // we handle it explicitly.
+ size_t padchars = 0;
+ string::const_reverse_iterator srit = input.rbegin();
+ string::const_reverse_iterator srit_end = input.rend();
+ while (srit != srit_end) {
+ char ch = *srit;
+ if (ch == BASE_PADDING_CHAR) {
+ if (++padchars > MAX_PADDING_CHARS) {
+ isc_throw(BadValue, "Too many " << algorithm
+ << " padding characters: " << input);
+ }
+ } else if (!isspace(ch)) {
+ break;
+ }
+ ++srit;
+ }
+ // then calculate the number of padding bits corresponding to the padding
+ // characters. In general, the padding bits consist of all-zero
+ // trailing bits of the last encoded character followed by zero bits
+ // represented by the padding characters:
+ // 1st pad 2nd pad 3rd pad...
+ // +++===== ======= ===... (+: from encoded chars, =: from pad chars)
+ // 0000...0 0......0 000...
+ // 0 7 8 15 16.... (bits)
+ // The number of bits for the '==...' part is padchars * BitsPerChunk.
+ // So the total number of padding bits is the smallest multiple of 8
+ // that is >= padchars * BitsPerChunk.
+ // (Below, note the common idiom of the bitwise AND with ~7. It clears the
+ // lowest three bits, so has the effect of rounding the result down to the
+ // nearest multiple of 8)
+ const size_t padbits = (padchars * BitsPerChunk + 7) & ~7;
+
+ // In some encoding algorithm, it could happen that a padding byte would
+ // contain a full set of encoded bits, which is not allowed by definition
+ // of padding. For example, if BitsPerChunk is 5, the following
+ // representation could happen:
+ // ++00000= (+: from encoded chars, 0: encoded char for '0', =: pad chars)
+ // 0 7 (bits)
+ // This must actually be encoded as follows:
+ // ++======
+ // 0 7 (bits)
+ // The following check rejects this type of invalid encoding.
+ if (padbits > BitsPerChunk * (padchars + 1)) {
+ isc_throw(BadValue, "Invalid " << algorithm << "padding: " << input);
+ }
+
+ // convert the number of bits in bytes for convenience.
+ const size_t padbytes = padbits / 8;
+
+ try {
+ result.assign(Decoder(DecodeNormalizer(BaseZeroCode, input.begin(),
+ srit.base(), input.end())),
+ Decoder(DecodeNormalizer(BaseZeroCode, input.end(),
+ input.end(), input.end())));
+ } catch (const dataflow_exception& ex) {
+ // convert any boost exceptions into our local one.
+ isc_throw(BadValue, ex.what());
+ }
+
+ // Confirm the original BaseX text is the canonical encoding of the
+ // data, that is, that the first byte of padding is indeed 0.
+ // (DecodeNormalizer and binary_from_baseXX ensure that the rest of the
+ // padding is all zero).
+ assert(result.size() >= padbytes);
+ if (padbytes > 0 && *(result.end() - padbytes) != 0) {
+ isc_throw(BadValue, "Non 0 bits included in " << algorithm
+ << " padding: " << input);
+ }
+
+ // strip the padded zero-bit fields
+ result.resize(result.size() - padbytes);
+}
+
+//
+// Instantiation for BASE-64
+//
+typedef
+base64_from_binary<transform_width<EncodeNormalizer, 6, 8> > base64_encoder;
+typedef
+transform_width<binary_from_base64<DecodeNormalizer>, 8, 6> base64_decoder;
+typedef BaseNTransformer<6, 'A', base64_encoder, base64_decoder>
+Base64Transformer;
+
+//
+// Instantiation for BASE-32HEX
+//
+typedef
+base32hex_from_binary<transform_width<EncodeNormalizer, 5, 8> >
+base32hex_encoder;
+typedef
+transform_width<binary_from_base32hex<DecodeNormalizer>, 8, 5>
+base32hex_decoder;
+typedef BaseNTransformer<5, '0', base32hex_encoder, base32hex_decoder>
+Base32HexTransformer;
+
+//
+// Instantiation for BASE-16 (HEX)
+//
+typedef
+base16_from_binary<transform_width<EncodeNormalizer, 4, 8> > base16_encoder;
+typedef
+transform_width<binary_from_base16<DecodeNormalizer>, 8, 4> base16_decoder;
+typedef BaseNTransformer<4, '0', base16_encoder, base16_decoder>
+Base16Transformer;
+}
+
+string
+encodeBase64(const vector<uint8_t>& binary) {
+ return (Base64Transformer::encode(binary));
+}
+
+void
+decodeBase64(const string& input, vector<uint8_t>& result) {
+ Base64Transformer::decode("base64", input, result);
+}
+
+string
+encodeBase32Hex(const vector<uint8_t>& binary) {
+ return (Base32HexTransformer::encode(binary));
+}
+
+void
+decodeBase32Hex(const string& input, vector<uint8_t>& result) {
+ Base32HexTransformer::decode("base32hex", input, result);
+}
+
+string
+encodeHex(const vector<uint8_t>& binary) {
+ return (Base16Transformer::encode(binary));
+}
+
+void
+decodeHex(const string& input, vector<uint8_t>& result) {
+ Base16Transformer::decode("base16", input, result);
+}
+
+} // namespace compat
+} // namespace util
+} // namespace isc
diff --git a/src/lib/util/compat/binary_from_base16.h b/src/lib/util/compat/binary_from_base16.h
new file mode 100644
index 0000000..50342f1
--- /dev/null
+++ b/src/lib/util/compat/binary_from_base16.h
@@ -0,0 +1,120 @@
+#ifndef BOOST_ARCHIVE_ITERATORS_BINARY_FROM_BASE16_HPP
+#define BOOST_ARCHIVE_ITERATORS_BINARY_FROM_BASE16_HPP
+
+// MS compatible compilers support #pragma once
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+# pragma once
+#endif
+
+/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
+// binary_from_base16.h (derived from boost binary_from_base64.hpp)
+
+// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+// See http://www.boost.org for updates, documentation, and revision history.
+
+#include <cassert>
+
+// See binary_from_base32hex.h for why we need _from_base64.hpp here.
+#include <boost/archive/iterators/binary_from_base64.hpp>
+
+#include <exceptions/exceptions.h>
+
+namespace boost {
+namespace archive {
+namespace iterators {
+
+/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
+// convert base16 characters to binary data
+
+namespace detail {
+
+template<class CharType>
+struct to_4_bit {
+ typedef CharType result_type;
+ CharType operator()(CharType t) const{
+ const char lookup_table[] = {
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 00-0f
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 10-1f
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 20-2f
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, // 30-3f
+ -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 40-4f
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 50-5f
+ -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 60-6f
+ };
+ // metrowerks trips this assertion - how come?
+ #if ! defined(__MWERKS__)
+ BOOST_STATIC_ASSERT(0x70 == sizeof(lookup_table));
+ #endif
+ signed char value = -1;
+ if((unsigned)t < sizeof(lookup_table))
+ value = lookup_table[(unsigned)t];
+ if(-1 == value) {
+ isc_throw(isc::BadValue,
+ "attempt to decode a value not in base16 char set");
+ }
+ return (value);
+ }
+};
+
+} // namespace detail
+
+// note: what we would like to do is
+// template<class Base, class CharType = BOOST_DEDUCED_TYPENAME Base::value_type>
+// typedef transform_iterator<
+// from_4_bit<CharType>,
+// transform_width<Base, 4, sizeof(Base::value_type) * 8, CharType>
+// > base16_from_binary;
+// but C++ won't accept this. Rather than using a "type generator" and
+// using a different syntax, make a derivation which should be equivalent.
+//
+// Another issue addressed here is that the transform_iterator doesn't have
+// a templated constructor. This makes it incompatible with the dataflow
+// ideal. This is also addressed here.
+
+template<
+ class Base,
+ class CharType = BOOST_DEDUCED_TYPENAME boost::iterator_value<Base>::type
+>
+class binary_from_base16 : public
+ transform_iterator<
+ detail::to_4_bit<CharType>,
+ Base
+ >
+{
+ friend class boost::iterator_core_access;
+ typedef transform_iterator<
+ detail::to_4_bit<CharType>,
+ Base
+ > super_t;
+public:
+ // make composible buy using templated constructor
+ template<class T>
+ binary_from_base16(BOOST_PFTO_WRAPPER(T) start) :
+ super_t(
+ Base(BOOST_MAKE_PFTO_WRAPPER(static_cast<T>(start))),
+ detail::to_4_bit<CharType>()
+ )
+ {}
+ // intel 7.1 doesn't like default copy constructor
+ binary_from_base16(const binary_from_base16 & rhs) :
+ super_t(
+ Base(rhs.base_reference()),
+ detail::to_4_bit<CharType>()
+ )
+ {}
+// binary_from_base16(){};
+};
+
+} // namespace iterators
+} // namespace archive
+} // namespace boost
+
+#endif // BOOST_ARCHIVE_ITERATORS_BINARY_FROM_BASE16_HPP
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/util/compat/binary_from_base32hex.h b/src/lib/util/compat/binary_from_base32hex.h
new file mode 100644
index 0000000..1d83f54
--- /dev/null
+++ b/src/lib/util/compat/binary_from_base32hex.h
@@ -0,0 +1,123 @@
+#ifndef BOOST_ARCHIVE_ITERATORS_BINARY_FROM_BASE32HEX_HPP
+#define BOOST_ARCHIVE_ITERATORS_BINARY_FROM_BASE32HEX_HPP
+
+// MS compatible compilers support #pragma once
+#if defined(_MSC_VER) && (_MSC_VER >= 1020)
+# pragma once
+#endif
+
+/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
+// binary_from_base32hex.h (derived from boost binary_from_base64.hpp)
+
+// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
+// Use, modification and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+// See http://www.boost.org for updates, documentation, and revision history.
+
+#include <cassert>
+
+// We use the same boost header files used in "_from_base64". Since the
+// precise path to these headers may vary depending on the boost version we
+// simply include the base64 header here.
+#include <boost/archive/iterators/binary_from_base64.hpp>
+
+#include <exceptions/exceptions.h>
+
+namespace boost {
+namespace archive {
+namespace iterators {
+
+/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
+// convert base32hex characters to binary data
+
+namespace detail {
+
+template<class CharType>
+struct to_5_bit {
+ typedef CharType result_type;
+ CharType operator()(CharType t) const{
+ const char lookup_table[] = {
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 00-0f
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 10-1f
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 20-2f
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, // 30-3f
+ -1,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24, // 40-4f
+ 25,26,27,28,29,30,31,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 50-5f
+ -1,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24, // 60-6f
+ 25,26,27,28,29,30,31,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 70-7f
+ };
+ // metrowerks trips this assertion - how come?
+ #if ! defined(__MWERKS__)
+ BOOST_STATIC_ASSERT(0x80 == sizeof(lookup_table));
+ #endif
+ signed char value = -1;
+ if((unsigned)t < sizeof(lookup_table))
+ value = lookup_table[(unsigned)t];
+ if(-1 == value) {
+ isc_throw(isc::BadValue,
+ "attempt to decode a value not in base32hex char set");
+ }
+ return (value);
+ }
+};
+
+} // namespace detail
+
+// note: what we would like to do is
+// template<class Base, class CharType = BOOST_DEDUCED_TYPENAME Base::value_type>
+// typedef transform_iterator<
+// from_5_bit<CharType>,
+// transform_width<Base, 5, sizeof(Base::value_type) * 8, CharType>
+// > base32hex_from_binary;
+// but C++ won't accept this. Rather than using a "type generator" and
+// using a different syntax, make a derivation which should be equivalent.
+//
+// Another issue addressed here is that the transform_iterator doesn't have
+// a templated constructor. This makes it incompatible with the dataflow
+// ideal. This is also addressed here.
+
+template<
+ class Base,
+ class CharType = BOOST_DEDUCED_TYPENAME boost::iterator_value<Base>::type
+>
+class binary_from_base32hex : public
+ transform_iterator<
+ detail::to_5_bit<CharType>,
+ Base
+ >
+{
+ friend class boost::iterator_core_access;
+ typedef transform_iterator<
+ detail::to_5_bit<CharType>,
+ Base
+ > super_t;
+public:
+ // make composible buy using templated constructor
+ template<class T>
+ binary_from_base32hex(BOOST_PFTO_WRAPPER(T) start) :
+ super_t(
+ Base(BOOST_MAKE_PFTO_WRAPPER(static_cast<T>(start))),
+ detail::to_5_bit<CharType>()
+ )
+ {}
+ // intel 7.1 doesn't like default copy constructor
+ binary_from_base32hex(const binary_from_base32hex & rhs) :
+ super_t(
+ Base(rhs.base_reference()),
+ detail::to_5_bit<CharType>()
+ )
+ {}
+// binary_from_base32hex(){};
+};
+
+} // namespace iterators
+} // namespace archive
+} // namespace boost
+
+#endif // BOOST_ARCHIVE_ITERATORS_BINARY_FROM_BASE32HEX_HPP
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/util/compat/hex.h b/src/lib/util/compat/hex.h
new file mode 100644
index 0000000..57fc486
--- /dev/null
+++ b/src/lib/util/compat/hex.h
@@ -0,0 +1,65 @@
+// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __HEX_H
+#define __HEX_H 1
+
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+//
+// Note: this helper module isn't specific to the DNS protocol per se.
+// We should probably move this to somewhere else, possibly in some common
+// utility area.
+//
+
+namespace isc {
+namespace util {
+namespace compat {
+/// \brief Encode binary data in the base16 ('hex') format.
+///
+/// The underlying implementation is shared with \c encodeBase64, and most of
+/// the description except the format (base16) equally applies.
+/// Another notable exception is that the base16 encoding doesn't require
+/// padding, so padding related considerations and the notion of canonical
+/// encoding don't apply.
+///
+/// \param binary A vector object storing the data to be encoded.
+/// \return A newly created string that stores base16 encoded value for
+/// binary.
+std::string encodeHex(const std::vector<uint8_t>& binary);
+
+/// \brief Decode a text encoded in the base16 ('hex') format into the
+/// original %data.
+///
+/// The underlying implementation is shared with \c decodeBase64, and most
+/// of the description except the format (base16) equally applies.
+/// Another notable exception is that the base16 encoding doesn't require
+/// padding, so padding related considerations and the notion of canonical
+/// encoding don't apply.
+///
+/// \param input A text encoded in the base16 format.
+/// \param result A vector in which the decoded %data is to be stored.
+void decodeHex(const std::string& input, std::vector<uint8_t>& result);
+
+} // namespace compat
+} // namespace util
+} // namespace isc
+
+#endif // __HEX_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/util/compat/sha1.cc b/src/lib/util/compat/sha1.cc
new file mode 100644
index 0000000..519ce57
--- /dev/null
+++ b/src/lib/util/compat/sha1.cc
@@ -0,0 +1,484 @@
+/*
+ * Description:
+ * This file implements the Secure Hash Signature Standard
+ * algorithms as defined in the National Institute of Standards
+ * and Technology Federal Information Processing Standards
+ * Publication (FIPS PUB) 180-1 published on April 17, 1995, 180-2
+ * published on August 1, 2002, and the FIPS PUB 180-2 Change
+ * Notice published on February 28, 2004.
+ *
+ * A combined document showing all algorithms is available at
+ * http://csrc.nist.gov/publications/fips/
+ * fips180-2/fips180-2withchangenotice.pdf
+ *
+ * The SHA-1 algorithm produces a 160-bit message digest for a
+ * given data stream. It should take about 2**n steps to find a
+ * message with the same digest as a given message and
+ * 2**(n/2) to find any two messages with the same digest,
+ * when n is the digest size in bits. Therefore, this
+ * algorithm can serve as a means of providing a
+ * "fingerprint" for a message.
+ *
+ * Portability Issues:
+ * SHA-1 is defined in terms of 32-bit "words". This code
+ * uses <stdint.h> (included via "sha.h") to define 32 and 8
+ * bit unsigned integer types. If your C compiler does not
+ * support 32 bit unsigned integers, this code is not
+ * appropriate.
+ *
+ * Caveats:
+ * SHA-1 is designed to work with messages less than 2^64 bits
+ * long. This implementation uses SHA1Input() to hash the bits
+ * that are a multiple of the size of an 8-bit character, and then
+ * uses SHA1FinalBits() to hash the final few bits of the input.
+ *
+ * Authorship:
+ * This file is adapted from RFC 4634, by D. Eastlake et al.
+ * Copyright (C) The Internet Society (2006).
+ *
+ * Permission is granted for all uses, commercial and non-commercial,
+ * of the sample code found in Section 8. Royalty free license to
+ * use, copy, modify and distribute the software found in Section 8 is
+ * granted, provided that this document is identified in all material
+ * mentioning or referencing this software, and provided that
+ * redistributed derivative works do not contain misleading author or
+ * version information.
+ *
+ * The authors make no representations concerning either the
+ * merchantability of this software or the suitability of this
+ * software for any particular purpose. It is provided "as is"
+ * without express or implied warranty of any kind.
+ *
+ */
+#include <util/compat/sha1.h>
+
+/* Local Function Prototyptes */
+static void SHA1Finalize(SHA1Context *, uint8_t Pad_Byte);
+static void SHA1PadMessage(SHA1Context *, uint8_t Pad_Byte);
+static void SHA1ProcessMessageBlock(SHA1Context *);
+
+/*
+ * Define functions used by SHA1 hash
+ */
+static inline uint32_t
+SHA_Ch(const uint32_t x, const uint32_t y, const uint32_t z) {
+ return (((x) & ((y) ^ (z))) ^ (z));
+}
+
+static inline uint32_t
+SHA_Maj(const uint32_t x, const uint32_t y, const uint32_t z) {
+ return (((x) & ((y) | (z))) | ((y) & (z)));
+}
+
+static inline uint32_t
+SHA_Parity(const uint32_t x, const uint32_t y, const uint32_t z) {
+ return ((x) ^ (y) ^ (z));
+}
+
+static inline int
+SHA1CircularShift(uint8_t bits, uint32_t word) {
+ return ((word << bits) | (word >> (32 - bits)));
+}
+
+static inline bool
+SHA1AddLength(SHA1Context *context, uint32_t length) {
+ uint32_t addTemp = context->Length_Low;
+ context->Length_Low += length;
+ if (context->Length_Low < addTemp && ++context->Length_High == 0) {
+ return (true);
+ } else {
+ return (false);
+ }
+}
+
+/*
+ * SHA1Reset
+ *
+ * Description:
+ * This function will initialize the SHA1Context in preparation
+ * for computing a new SHA1 message digest.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to reset.
+ *
+ * Returns:
+ * sha Error Code.
+ *
+ */
+int
+SHA1Reset(SHA1Context *context) {
+ if (!context) {
+ return (SHA_NULL);
+ }
+
+ context->Length_Low = 0;
+ context->Length_High = 0;
+ context->Message_Block_Index = 0;
+
+ context->Intermediate_Hash[0] = 0x67452301;
+ context->Intermediate_Hash[1] = 0xEFCDAB89;
+ context->Intermediate_Hash[2] = 0x98BADCFE;
+ context->Intermediate_Hash[3] = 0x10325476;
+ context->Intermediate_Hash[4] = 0xC3D2E1F0;
+
+ context->Computed = 0;
+ context->Corrupted = 0;
+ return (SHA_SUCCESS);
+}
+
+
+/*
+ * SHA1Input
+ *
+ * Description:
+ * This function accepts an array of octets as the next portion
+ * of the message.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The SHA context to update
+ * message_array: [in]
+ * An array of characters representing the next portion of
+ * the message.
+ * length: [in]
+ * The length of the message in message_array
+ *
+ * Returns:
+ * sha Error Code.
+ *
+ */
+int
+SHA1Input(SHA1Context *context, const uint8_t *message_array, unsigned length) {
+ if (!length) {
+ return (SHA_SUCCESS);
+ }
+
+ if (!context || !message_array) {
+ return (SHA_NULL);
+ }
+
+ if (context->Computed) {
+ context->Corrupted = SHA_STATEERROR;
+ return (SHA_STATEERROR);
+ }
+
+ if (context->Corrupted) {
+ return (context->Corrupted);
+ }
+
+ while(length-- && !context->Corrupted) {
+ context->Message_Block[context->Message_Block_Index++] =
+ (*message_array & 0xFF);
+
+ if (!SHA1AddLength(context, 8) &&
+ (context->Message_Block_Index == SHA1_BLOCKSIZE))
+ {
+ SHA1ProcessMessageBlock(context);
+ }
+
+ message_array++;
+ }
+
+ return (SHA_SUCCESS);
+}
+
+/*
+ * SHA1FinalBits
+ *
+ * Description:
+ * This function will add in any final bits of the message.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The SHA context to update
+ * message_bits: [in]
+ * The final bits of the message, in the upper portion of the
+ * byte. (Use 0b###00000 instead of 0b00000### to input the
+ * three bits ###.)
+ * length: [in]
+ * The number of bits in message_bits, between 1 and 7.
+ *
+ * Returns:
+ * sha Error Code.
+ */
+int SHA1FinalBits(SHA1Context *context, const uint8_t message_bits,
+ unsigned int length)
+{
+ uint8_t masks[8] = {
+ /* 0 0b00000000 */ 0x00,
+ /* 1 0b10000000 */ 0x80,
+ /* 2 0b11000000 */ 0xC0,
+ /* 3 0b11100000 */ 0xE0,
+ /* 4 0b11110000 */ 0xF0,
+ /* 5 0b11111000 */ 0xF8,
+ /* 6 0b11111100 */ 0xFC,
+ /* 7 0b11111110 */ 0xFE
+ };
+ uint8_t markbit[8] = {
+ /* 0 0b10000000 */ 0x80,
+ /* 1 0b01000000 */ 0x40,
+ /* 2 0b00100000 */ 0x20,
+ /* 3 0b00010000 */ 0x10,
+ /* 4 0b00001000 */ 0x08,
+ /* 5 0b00000100 */ 0x04,
+ /* 6 0b00000010 */ 0x02,
+ /* 7 0b00000001 */ 0x01
+ };
+
+ if (!length) {
+ return (SHA_SUCCESS);
+ }
+
+ if (!context) {
+ return (SHA_NULL);
+ }
+
+ if (context->Computed || (length >= 8) || (length == 0)) {
+ context->Corrupted = SHA_STATEERROR;
+ return (SHA_STATEERROR);
+ }
+
+ if (context->Corrupted) {
+ return (context->Corrupted);
+ }
+
+ SHA1AddLength(context, length);
+ SHA1Finalize(context,
+ (uint8_t) ((message_bits & masks[length]) | markbit[length]));
+
+ return (SHA_SUCCESS);
+}
+
+/*
+ * SHA1Result
+ *
+ * Description:
+ * This function will return the 160-bit message digest into the
+ * Message_Digest array provided by the caller.
+ * NOTE: The first octet of hash is stored in the 0th element,
+ * the last octet of hash in the 19th element.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to use to calculate the SHA-1 hash.
+ * Message_Digest: [out]
+ * Where the digest is returned.
+ *
+ * Returns:
+ * sha Error Code.
+ *
+ */
+int
+SHA1Result(SHA1Context *context, uint8_t Message_Digest[SHA1_HASHSIZE]) {
+ int i;
+
+ if (!context || !Message_Digest) {
+ return (SHA_NULL);
+ }
+
+ if (context->Corrupted) {
+ return (context->Corrupted);
+ }
+
+ if (!context->Computed) {
+ SHA1Finalize(context, 0x80);
+ }
+
+ for(i = 0; i < SHA1_HASHSIZE; ++i) {
+ Message_Digest[i] = context->Intermediate_Hash[i>>2]
+ >> 8 * (3 - (i & 0x03));
+ }
+
+ return (SHA_SUCCESS);
+}
+
+/*
+ * SHA1Finalize
+ *
+ * Description:
+ * This helper function finishes off the digest calculations.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The SHA context to update
+ * Pad_Byte: [in]
+ * The last byte to add to the digest before the 0-padding
+ * and length. This will contain the last bits of the message
+ * followed by another single bit. If the message was an
+ * exact multiple of 8-bits long, Pad_Byte will be 0x80.
+ *
+ * Returns:
+ * sha Error Code.
+ *
+ */
+static void SHA1Finalize(SHA1Context *context, uint8_t Pad_Byte) {
+ int i;
+ SHA1PadMessage(context, Pad_Byte);
+ /* message may be sensitive, clear it out */
+ for (i = 0; i < SHA1_BLOCKSIZE; ++i)
+ context->Message_Block[i] = 0;
+ context->Length_Low = 0; /* and clear length */
+ context->Length_High = 0;
+ context->Computed = 1;
+}
+
+/*
+ * SHA1PadMessage
+ *
+ * Description:
+ * According to the standard, the message must be padded to an even
+ * 512 bits. The first padding bit must be a '1'. The last 64
+ * bits represent the length of the original message. All bits in
+ * between should be 0. This function will pad the message
+ * according to those rules by filling the Message_Block array
+ * accordingly. It will also call the ProcessMessageBlock function
+ * provided appropriately. When it returns, it can be assumed that
+ * the message digest has been computed.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to pad
+ * Pad_Byte: [in]
+ * The last byte to add to the digest before the 0-padding
+ * and length. This will contain the last bits of the message
+ * followed by another single bit. If the message was an
+ * exact multiple of 8-bits long, Pad_Byte will be 0x80.
+ *
+ * Returns:
+ * Nothing.
+ *
+ */
+static void SHA1PadMessage(SHA1Context *context, uint8_t Pad_Byte) {
+ /*
+ * Check to see if the current message block is too small to hold
+ * the initial padding bits and length. If so, we will pad the
+ * block, process it, and then continue padding into a second
+ * block.
+ */
+ if (context->Message_Block_Index >= (SHA1_BLOCKSIZE - 8)) {
+ context->Message_Block[context->Message_Block_Index++] = Pad_Byte;
+ while (context->Message_Block_Index < SHA1_BLOCKSIZE) {
+ context->Message_Block[context->Message_Block_Index++] = 0;
+ }
+
+ SHA1ProcessMessageBlock(context);
+ } else
+ context->Message_Block[context->Message_Block_Index++] = Pad_Byte;
+
+ while (context->Message_Block_Index < (SHA1_BLOCKSIZE - 8))
+ context->Message_Block[context->Message_Block_Index++] = 0;
+
+ /*
+ * Store the message length as the last 8 octets
+ */
+ context->Message_Block[56] = (uint8_t) (context->Length_High >> 24);
+ context->Message_Block[57] = (uint8_t) (context->Length_High >> 16);
+ context->Message_Block[58] = (uint8_t) (context->Length_High >> 8);
+ context->Message_Block[59] = (uint8_t) (context->Length_High);
+ context->Message_Block[60] = (uint8_t) (context->Length_Low >> 24);
+ context->Message_Block[61] = (uint8_t) (context->Length_Low >> 16);
+ context->Message_Block[62] = (uint8_t) (context->Length_Low >> 8);
+ context->Message_Block[63] = (uint8_t) (context->Length_Low);
+
+ SHA1ProcessMessageBlock(context);
+}
+
+/*
+ * SHA1ProcessMessageBlock
+ *
+ * Description:
+ * This helper function will process the next 512 bits of the
+ * message stored in the Message_Block array.
+ *
+ * Parameters:
+ * None.
+ *
+ * Returns:
+ * Nothing.
+ *
+ * Comments:
+ * Many of the variable names in this code, especially the
+ * single character names, were used because those were the
+ * names used in the publication.
+ *
+ *
+ */
+static void
+SHA1ProcessMessageBlock(SHA1Context *context) {
+ /* Constants defined in FIPS-180-2, section 4.2.1 */
+ const uint32_t K[] = {
+ 0x5A827999,
+ 0x6ED9EBA1,
+ 0x8F1BBCDC,
+ 0xCA62C1D6
+ };
+ int t; /* Loop counter */
+ uint32_t temp; /* Temporary word value */
+ uint32_t W[80]; /* Word sequence */
+ uint32_t A, B, C, D, E; /* Word buffers */
+
+ /*
+ * Initialize the first 16 words in the array W
+ */
+ for (t = 0; t < 16; t++) {
+ W[t] = ((uint32_t)context->Message_Block[t * 4]) << 24;
+ W[t] |= ((uint32_t)context->Message_Block[t * 4 + 1]) << 16;
+ W[t] |= ((uint32_t)context->Message_Block[t * 4 + 2]) << 8;
+ W[t] |= ((uint32_t)context->Message_Block[t * 4 + 3]);
+ }
+
+ for (t = 16; t < 80; t++) {
+ W[t] = SHA1CircularShift(1, W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
+ }
+
+ A = context->Intermediate_Hash[0];
+ B = context->Intermediate_Hash[1];
+ C = context->Intermediate_Hash[2];
+ D = context->Intermediate_Hash[3];
+ E = context->Intermediate_Hash[4];
+
+ for (t = 0; t < 20; t++) {
+ temp = SHA1CircularShift(5,A) + SHA_Ch(B, C, D) + E + W[t] + K[0];
+ E = D;
+ D = C;
+ C = SHA1CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ for (t = 20; t < 40; t++) {
+ temp = SHA1CircularShift(5,A) + SHA_Parity(B, C, D) + E + W[t] + K[1];
+ E = D;
+ D = C;
+ C = SHA1CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ for (t = 40; t < 60; t++) {
+ temp = SHA1CircularShift(5,A) + SHA_Maj(B, C, D) + E + W[t] + K[2];
+ E = D;
+ D = C;
+ C = SHA1CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ for (t = 60; t < 80; t++) {
+ temp = SHA1CircularShift(5,A) + SHA_Parity(B, C, D) + E + W[t] + K[3];
+ E = D;
+ D = C;
+ C = SHA1CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ context->Intermediate_Hash[0] += A;
+ context->Intermediate_Hash[1] += B;
+ context->Intermediate_Hash[2] += C;
+ context->Intermediate_Hash[3] += D;
+ context->Intermediate_Hash[4] += E;
+
+ context->Message_Block_Index = 0;
+}
diff --git a/src/lib/util/compat/sha1.h b/src/lib/util/compat/sha1.h
new file mode 100644
index 0000000..f0f45f3
--- /dev/null
+++ b/src/lib/util/compat/sha1.h
@@ -0,0 +1,84 @@
+/*
+ * sha1.h
+ *
+ * Description:
+ * This is the header file for code which implements the Secure
+ * Hashing Algorithm 1 as defined in FIPS PUB 180-1 published
+ * April 17, 1995.
+ *
+ * Many of the variable names in this code, especially the
+ * single character names, were used because those were the names
+ * used in the publication.
+ *
+ * Please read the file sha1.cc for more information.
+ *
+ * Authorship:
+ * This file is adapted from RFC 4634, by D. Eastlake et al.
+ * Copyright (C) The Internet Society (2006).
+ *
+ * Permission is granted for all uses, commercial and non-commercial,
+ * of the sample code found in Section 8. Royalty free license to
+ * use, copy, modify and distribute the software found in Section 8 is
+ * granted, provided that this document is identified in all material
+ * mentioning or referencing this software, and provided that
+ * redistributed derivative works do not contain misleading author or
+ * version information.
+ *
+ * The authors make no representations concerning either the
+ * merchantability of this software or the suitability of this
+ * software for any particular purpose. It is provided "as is"
+ * without express or implied warranty of any kind.
+ */
+
+#ifndef _SHA1_H_
+#define _SHA1_H_
+
+#include <stdint.h>
+/*
+ * If you do not have the ISO standard stdint.h header file, then you
+ * must typdef the following:
+ * name meaning
+ * uint32_t unsigned 32 bit integer
+ * uint8_t unsigned 8 bit integer (i.e., unsigned char)
+ * int_least16_t integer of >= 16 bits
+ *
+ */
+
+enum {
+ SHA_SUCCESS = 0,
+ SHA_NULL, /* Null pointer parameter */
+ SHA_STATEERROR /* called Input after Result */
+};
+
+enum {
+ SHA1_HASHSIZE = 20,
+ SHA1_HASHBITS = 20,
+ SHA1_BLOCKSIZE = 64
+};
+
+/*
+ * This structure will hold context information for the SHA-1
+ * hashing operation
+ */
+typedef struct SHA1Context
+{
+ uint32_t Intermediate_Hash[SHA1_HASHSIZE/4]; /* Message Digest */
+ uint32_t Length_Low; /* Message length in bits */
+ uint32_t Length_High; /* Message length in bits */
+ int_least16_t Message_Block_Index; /* Index into message block array */
+ uint8_t Message_Block[64]; /* 512-bit message blocks */
+ int Computed; /* Is the digest computed? */
+ int Corrupted; /* Is the message digest corrupted? */
+} SHA1Context;
+
+/*
+ * Function Prototypes
+ */
+extern int SHA1Reset(SHA1Context *);
+extern int SHA1Input(SHA1Context *, const uint8_t *bytes,
+ unsigned int bytecount);
+extern int SHA1FinalBits(SHA1Context *, const uint8_t bits,
+ unsigned int bitcount);
+extern int SHA1Result(SHA1Context *, uint8_t Message_Digest[SHA1_HASHSIZE]);
+
+#endif
diff --git a/src/lib/util/compat/time_utilities.cc b/src/lib/util/compat/time_utilities.cc
new file mode 100644
index 0000000..35e7526
--- /dev/null
+++ b/src/lib/util/compat/time_utilities.cc
@@ -0,0 +1,212 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <stdint.h>
+
+#include <sys/time.h>
+
+#include <string>
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+
+#include <stdio.h>
+#include <time.h>
+
+#include <exceptions/exceptions.h>
+
+#include <util/compat/time_utilities.h>
+
+using namespace std;
+
+namespace {
+int days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+inline bool
+isLeap(const int y) {
+ return ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0);
+}
+
+unsigned int
+yearSecs(const int year) {
+ return ((isLeap(year) ? 366 : 365 ) * 86400);
+}
+
+unsigned int
+monthSecs(const int month, const int year) {
+ return ((days[month] + ((month == 1 && isLeap(year)) ? 1 : 0 )) * 86400);
+}
+}
+
+namespace isc {
+namespace util {
+namespace compat {
+
+string
+timeToText64(uint64_t value) {
+ struct tm tm;
+ unsigned int secs;
+
+ // We cannot rely on gmtime() because time_t may not be of 64 bit
+ // integer. The following conversion logic is borrowed from BIND 9.
+ tm.tm_year = 70;
+ while ((secs = yearSecs(tm.tm_year + 1900)) <= value) {
+ value -= secs;
+ ++tm.tm_year;
+ if (tm.tm_year + 1900 > 9999) {
+ isc_throw(InvalidTime,
+ "Time value out of range (year > 9999): " <<
+ tm.tm_year + 1900);
+ }
+ }
+ tm.tm_mon = 0;
+ while ((secs = monthSecs(tm.tm_mon, tm.tm_year + 1900)) <= value) {
+ value -= secs;
+ tm.tm_mon++;
+ }
+ tm.tm_mday = 1;
+ while (86400 <= value) {
+ value -= 86400;
+ ++tm.tm_mday;
+ }
+ tm.tm_hour = 0;
+ while (3600 <= value) {
+ value -= 3600;
+ ++tm.tm_hour;
+ }
+ tm.tm_min = 0;
+ while (60 <= value) {
+ value -= 60;
+ ++tm.tm_min;
+ }
+ tm.tm_sec = value; // now t < 60, so this substitution is safe.
+
+ ostringstream oss;
+ oss << setfill('0')
+ << setw(4) << tm.tm_year + 1900
+ << setw(2) << tm.tm_mon + 1
+ << setw(2) << tm.tm_mday
+ << setw(2) << tm.tm_hour
+ << setw(2) << tm.tm_min
+ << setw(2) << tm.tm_sec;
+ return (oss.str());
+}
+
+// timeToText32() below uses the current system time. To test it with
+// unusual current time values we introduce the following function pointer;
+// when it's non NULL, we call it to get the (normally faked) current time.
+// Otherwise we use the standard gettimeofday(2). This hook is specifically
+// intended for testing purposes, so, even if it's visible outside of this
+// library, it's not even declared in a header file.
+namespace detail {
+int64_t (*gettimeFunction)() = NULL;
+}
+
+namespace {
+int64_t
+gettimeofdayWrapper() {
+ using namespace detail;
+ if (gettimeFunction != NULL) {
+ return (gettimeFunction());
+ }
+
+ struct timeval now;
+ gettimeofday(&now, NULL);
+
+ return (static_cast<int64_t>(now.tv_sec));
+}
+}
+
+string
+timeToText32(const uint32_t value) {
+ // We first adjust the time to the closest epoch based on the current time.
+ // Note that the following variables must be signed in order to handle
+ // time until year 2038 correctly.
+ const int64_t start = gettimeofdayWrapper() - 0x7fffffff;
+ int64_t base = 0;
+ int64_t t;
+ while ((t = (base + value)) < start) {
+ base += 0x100000000LL;
+ }
+
+ // Then convert it to text.
+ return (timeToText64(t));
+}
+
+namespace {
+const size_t DATE_LEN = 14; // YYYYMMDDHHmmSS
+
+inline void
+checkRange(const int min, const int max, const int value,
+ const string& valname)
+{
+ if ((value >= min) && (value <= max)) {
+ return;
+ }
+ isc_throw(InvalidTime, "Invalid " << valname << "value: " << value);
+}
+}
+
+uint64_t
+timeFromText64(const string& time_txt) {
+ // Confirm the source only consists digits. sscanf() allows some
+ // minor exceptions.
+ for (int i = 0; i < time_txt.length(); ++i) {
+ if (!isdigit(time_txt.at(i))) {
+ isc_throw(InvalidTime, "Couldn't convert non-numeric time value: "
+ << time_txt);
+ }
+ }
+
+ int year, month, day, hour, minute, second;
+ if (time_txt.length() != DATE_LEN ||
+ sscanf(time_txt.c_str(), "%4d%2d%2d%2d%2d%2d",
+ &year, &month, &day, &hour, &minute, &second) != 6)
+ {
+ isc_throw(InvalidTime, "Couldn't convert time value: " << time_txt);
+ }
+
+ checkRange(1970, 9999, year, "year");
+ checkRange(1, 12, month, "month");
+ checkRange(1, days[month - 1] + ((month == 2 && isLeap(year)) ? 1 : 0),
+ day, "day");
+ checkRange(0, 23, hour, "hour");
+ checkRange(0, 59, minute, "minute");
+ checkRange(0, 60, second, "second"); // 60 == leap second.
+
+ uint64_t timeval = second + (60 * minute) + (3600 * hour) +
+ ((day - 1) * 86400);
+ for (int m = 0; m < (month - 1); ++m) {
+ timeval += days[m] * 86400;
+ }
+ if (isLeap(year) && month > 2) {
+ timeval += 86400;
+ }
+ for (int y = 1970; y < year; ++y) {
+ timeval += ((isLeap(y) ? 366 : 365 ) * 86400);
+ }
+
+ return (timeval);
+}
+
+uint32_t
+timeFromText32(const string& time_txt) {
+ // The implicit conversion from uint64_t to uint32_t should just work here,
+ // because we only need to drop higher 32 bits.
+ return (timeFromText64(time_txt));
+}
+
+}
+}
+}
diff --git a/src/lib/util/compat/time_utilities.h b/src/lib/util/compat/time_utilities.h
new file mode 100644
index 0000000..64218a8
--- /dev/null
+++ b/src/lib/util/compat/time_utilities.h
@@ -0,0 +1,147 @@
+// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __TIME_UTILITIES_H
+#define __TIME_UTILITIES_H 1
+
+#include <sys/types.h>
+#include <stdint.h>
+
+#include <exceptions/exceptions.h>
+
+//
+// Note: this helper module isn't specific to the DNS protocol per se.
+// We should probably move this to somewhere else, possibly in some common
+// utility area.
+//
+
+namespace isc {
+namespace util {
+namespace compat {
+
+///
+/// \brief A standard DNS (or ISC) module exception that is thrown if
+/// a time conversion function encounters bad input
+///
+class InvalidTime : public Exception {
+public:
+ InvalidTime(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
+///
+/// \name DNSSEC time conversion functions.
+///
+/// These functions convert between times represented in seconds (in integer)
+/// since epoch and those in the textual form used in the RRSIG records.
+/// For integers we provide both 32-bit and 64-bit versions.
+/// The RRSIG expiration and inception fields are both 32-bit unsigned
+/// integers, so 32-bit versions would be more useful for protocol operations.
+/// However, with 32-bit integers we need to take into account wrap-around
+/// points and compare values using the serial number arithmetic as specified
+/// in RFC4034, which would be more error prone. We therefore provide 64-bit
+/// versions, too.
+///
+/// The timezone is always UTC for these functions.
+//@{
+/// Convert textual DNSSEC time to integer, 64-bit version.
+///
+/// The textual form must only consist of digits and be in the form of
+/// YYYYMMDDHHmmSS, where:
+/// - YYYY must be between 1970 and 9999
+/// - MM must be between 01 and 12
+/// - DD must be between 01 and 31 and must be a valid day for the month
+/// represented in 'MM'. For example, if MM is 04, DD cannot be 31.
+/// DD can be 29 when MM is 02 only when YYYY is a leap year.
+/// - HH must be between 00 and 23
+/// - mm must be between 00 and 59
+/// - SS must be between 00 and 60
+///
+/// For all fields the range includes the begin and end values. Note that
+/// 60 is allowed for 'SS', intending a leap second, although in real operation
+/// it's unlikely to be specified.
+///
+/// If the given text is valid, this function converts it to an unsigned
+/// 64-bit number of seconds since epoch (1 January 1970 00:00:00) and returns
+/// the converted value. 64 bits are sufficient to represent all possible
+/// values for the valid format uniquely, so there is no overflow.
+///
+/// \note RFC4034 also defines the textual form of an unsigned decimal integer
+/// for the corresponding time in seconds. This function doesn't support
+/// this form, and if given it throws an exception of class \c InvalidTime.
+///
+/// \exception InvalidTime The given textual representation is invalid.
+///
+/// \param time_txt Textual time in the form of YYYYMMDDHHmmSS
+/// \return Seconds since epoch corresponding to \c time_txt
+uint64_t
+timeFromText64(const std::string& time_txt);
+
+/// Convert textual DNSSEC time to integer, 32-bit version.
+///
+/// This version is the same as \c timeFromText64() except that the return
+/// value is wrapped around to an unsigned 32-bit integer, simply dropping
+/// the upper 32 bits.
+uint32_t
+timeFromText32(const std::string& time_txt);
+
+/// Convert integral DNSSEC time to textual form, 64-bit version.
+///
+/// This function takes an integer that would be seconds since epoch and
+/// converts it in the form of YYYYMMDDHHmmSS. For example, if \c value is
+/// 0, it returns "19700101000000". If the value corresponds to a point
+/// of time on and after year 10,000, which cannot be represented in the
+/// YYYY... form, an exception of class \c InvalidTime will be thrown.
+///
+/// \exception InvalidTime The given time specifies on or after year 10,000.
+/// \exception Other A standard exception, if resource allocation for the
+/// returned text fails.
+///
+/// \param value Seconds since epoch to be converted.
+/// \return Textual representation of \c value in the form of YYYYMMDDHHmmSS.
+std::string
+timeToText64(uint64_t value);
+
+/// Convert integral DNSSEC time to textual form, 32-bit version.
+///
+/// This version is the same as \c timeToText64(), but the time value
+/// is expected to be the lower 32 bits of the full 64-bit value.
+/// These two will be different on and after a certain point of time
+/// in year 2106, so this function internally resolves the ambiguity
+/// using the current system time at the time of function call;
+/// it first identifies the range of [N*2^32 - 2^31, N*2^32 + 2^31)
+/// that contains the current time, and interprets \c value in the context
+/// of that range. It then applies the same process as \c timeToText64().
+///
+/// There is one important exception in this processing, however.
+/// Until 19 Jan 2038 03:14:08 (2^31 seconds since epoch), this range
+/// would contain time before epoch. In order to ensure the returned
+/// value is also a valid input to \c timeFromText, this function uses
+/// a special range [0, 2^32) until that time. As a result, all upper
+/// half of the 32-bit values are treated as a future time. For example,
+/// 2^32-1 (the highest value in 32-bit unsigned integers) will be converted
+/// to "21060207062815", instead of "19691231235959".
+std::string
+timeToText32(const uint32_t value);
+
+//@}
+}
+}
+}
+
+#endif // __DNSSECTIME_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/util/filename.cc b/src/lib/util/filename.cc
new file mode 100644
index 0000000..e46ad6f
--- /dev/null
+++ b/src/lib/util/filename.cc
@@ -0,0 +1,137 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <iostream>
+#include <algorithm>
+#include <string>
+
+#include <ctype.h>
+
+#include <util/filename.h>
+
+using namespace std;
+
+
+namespace isc {
+namespace util {
+
+// Split string into components. Any backslashes are assumed to have
+// been replaced by forward slashes.
+
+void
+Filename::split(const string& full_name, string& directory,
+ string& name, string& extension) const
+{
+ directory = name = extension = "";
+ bool dir_present = false;
+ if (!full_name.empty()) {
+
+ // Find the directory.
+ size_t last_slash = full_name.find_last_of('/');
+ if (last_slash != string::npos) {
+
+ // Found the last slash, so extract directory component and
+ // set where the scan for the last_dot should terminate.
+ directory = full_name.substr(0, last_slash + 1);
+ if (last_slash == full_name.size()) {
+
+ // The entire string was a directory, so exit not and don't
+ // do any more searching.
+ return;
+ }
+
+ // Found a directory so note the fact.
+ dir_present = true;
+ }
+
+ // Now search backwards for the last ".".
+ size_t last_dot = full_name.find_last_of('.');
+ if ((last_dot == string::npos) ||
+ (dir_present && (last_dot < last_slash))) {
+
+ // Last "." either not found or it occurs to the left of the last
+ // slash if a directory was present (so it is part of a directory
+ // name). In this case, the remainder of the string after the slash
+ // is the name part.
+ name = full_name.substr(last_slash + 1);
+ return;
+ }
+
+ // Did find a valid dot, so it and everything to the right is the
+ // extension...
+ extension = full_name.substr(last_dot);
+
+ // ... and the name of the file is everything in between.
+ if ((last_dot - last_slash) > 1) {
+ name = full_name.substr(last_slash + 1, last_dot - last_slash - 1);
+ }
+ }
+
+}
+
+// Expand the stored filename with the default.
+
+string
+Filename::expandWithDefault(const string& defname) const {
+
+ string def_directory("");
+ string def_name("");
+ string def_extension("");
+
+ // Normalize the input string.
+ string copy_defname = isc::strutil::trim(defname);
+#ifdef WIN32
+ isc::strutil::normalizeSlash(copy_defname);
+#endif
+
+ // Split into the components
+ split(copy_defname, def_directory, def_name, def_extension);
+
+ // Now construct the result.
+ string retstring =
+ (directory_.empty() ? def_directory : directory_) +
+ (name_.empty() ? def_name : name_) +
+ (extension_.empty() ? def_extension : extension_);
+ return (retstring);
+}
+
+// Use the stored name as default for a given name
+
+string
+Filename::useAsDefault(const string& name) const {
+
+ string name_directory("");
+ string name_name("");
+ string name_extension("");
+
+ // Normalize the input string.
+ string copy_name = isc::strutil::trim(name);
+#ifdef WIN32
+ isc::strutil::normalizeSlash(copy_name);
+#endif
+
+ // Split into the components
+ split(copy_name, name_directory, name_name, name_extension);
+
+ // Now construct the result.
+ string retstring =
+ (name_directory.empty() ? directory_ : name_directory) +
+ (name_name.empty() ? name_ : name_name) +
+ (name_extension.empty() ? extension_ : name_extension);
+ return (retstring);
+}
+
+
+} // namespace log
+} // namespace isc
diff --git a/src/lib/util/filename.h b/src/lib/util/filename.h
new file mode 100644
index 0000000..e8fbf62
--- /dev/null
+++ b/src/lib/util/filename.h
@@ -0,0 +1,161 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __FILENAME_H
+#define __FILENAME_H
+
+#include <string>
+
+#include <util/strutil.h>
+
+namespace isc {
+namespace util {
+
+/// \brief Class to Manipulate Filenames
+///
+/// This is a utility class to manipulate filenames. It repeats some of the
+/// features found in the Boost filename class, but is self-contained so avoids
+/// the need to link in the Boost library.
+///
+/// A Unix-style filename comprises three parts:
+///
+/// Directory - everything up to and including the last "/". If there is no
+/// "/" in the string, there is no directory component. Note that the
+/// requirement of a trailing slash eliminates the ambiguity of whether a
+/// component is a directory or not, e.g. in /alpha/beta", "beta" could be the
+/// name of a directory or is could be a file. The interpretation here is that
+/// "beta" is the name of a file (although that file could be a directory).
+///
+/// Note: Under Windows, the drive letter is considered to be part of the
+/// directory specification. Unless this class becomes more widely-used on
+/// Windows, there is no point in adding redundant code.
+///
+/// Name - everthing from the character after the last "/" up to but not
+/// including the last ".".
+///
+/// Extension - everthing from the right-most "." (after the right-most "/") to
+/// the end of the string. If there is no "." after the last "/", there is
+/// no file extension.
+///
+/// (Note that on Windows, this function will replace all "\" characters
+/// with "/" characters on input strings.)
+///
+/// This class provides functions for extracting the components and for
+/// substituting components.
+
+
+class Filename {
+public:
+
+ /// \brief Constructor
+ Filename(const std::string& name) :
+ full_name_(""), directory_(""), name_(""), extension_("")
+ {
+ setName(name);
+ }
+
+ /// \brief Sets Stored Filename
+ ///
+ /// \param name New name to replaced currently stored name
+ void setName(const std::string& name) {
+ full_name_ = isc::strutil::trim(name);
+#ifdef WIN32
+ isc::strutil::normalizeSlash(full_name_);
+#endif
+ split(full_name_, directory_, name_, extension_);
+ }
+
+ /// \return Stored Filename
+ std::string fullName() const {
+ return (full_name_);
+ }
+
+ /// \return Directory of Given File Name
+ std::string directory() const {
+ return (directory_);
+ }
+
+ /// \return Name of Given File Name
+ std::string name() const {
+ return (name_);
+ }
+
+ /// \return Extension of Given File Name
+ std::string extension() const {
+ return (extension_);
+ }
+
+ /// \brief Expand Name with Default
+ ///
+ /// A default file specified is supplied and used to fill in any missing
+ /// fields. For example, if the name stored is "/a/b" and the supplied
+ /// name is "c.d", the result is "/a/b.d": the only field missing from the
+ /// stored name is the extension, which is supplied by the default.
+ /// Another example would be to store "a.b" and to supply a default of
+ /// "/c/d/" - the result is "/c/d/a.b". (Note that if the supplied default
+ /// was "/c/d", the result would be "/c/a.b", even if "/c/d" were actually
+ /// a directory.)
+ ///
+ /// \param defname Default name
+ ///
+ /// \return Name expanded with defname.
+ std::string expandWithDefault(const std::string& defname) const;
+
+ /// \brief Use as Default and Substitute into String
+ ///
+ /// Does essentially the inverse of expand(); that filled in the stored
+ /// name with a default and returned the result. This treats the stored
+ /// name as the default and uses it to fill in a given name. In essence,
+ /// the code:
+ /// \code
+ /// Filename f("/a/b");
+ /// result = f.expandWithdefault("c.d");
+ /// \endcode
+ /// gives as a result "/a/b.d". This is the same as:
+ /// \code
+ /// Filename f("c.d");
+ /// result = f.useAsDefault("/a/b");
+ /// \endcode
+ ///
+ /// \param name Name to expand
+ ///
+ /// \return Name expanded with stored name
+ std::string useAsDefault(const std::string& name) const;
+
+private:
+ /// \brief Split Name into Components
+ ///
+ /// Splits the file name into the directory, name and extension parts.
+ /// The name is assumed to have had back slashes replaced by forward
+ /// slashes (if appropriate).
+ ///
+ /// \param full_name Name to split
+ /// \param directory Returned directory part
+ /// \param name Returned name part
+ /// \param extension Returned extension part
+ void split(const std::string& full_name, std::string& directory,
+ std::string& name, std::string& extension) const;
+
+ // Members
+
+ std::string full_name_; ///< Given name
+ std::string directory_; ///< Directory part
+ std::string name_; ///< Name part
+ std::string extension_; ///< Extension part
+};
+
+} // namespace util
+} // namespace isc
+
+#endif // __FILENAME_H
diff --git a/src/lib/util/io/buffer.h b/src/lib/util/io/buffer.h
new file mode 100644
index 0000000..41ac0ac
--- /dev/null
+++ b/src/lib/util/io/buffer.h
@@ -0,0 +1,448 @@
+// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __BUFFER_H
+#define __BUFFER_H 1
+
+#include <vector>
+
+#include <string.h>
+
+#include <stdint.h>
+
+#include <exceptions/exceptions.h>
+
+#include <boost/shared_ptr.hpp>
+
+namespace isc {
+namespace util {
+namespace io {
+
+///
+/// \brief A standard DNS module exception that is thrown if an out-of-range
+/// buffer operation is being performed.
+///
+class InvalidBufferPosition : public Exception {
+public:
+ InvalidBufferPosition(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
+///\brief The \c InputBuffer class is a buffer abstraction for manipulating
+/// read-only data.
+///
+/// The main purpose of this class is to provide a safe placeholder for
+/// examining wire-format data received from a network.
+///
+/// Applications normally use this class only in a limited situation: as an
+/// interface between legacy I/O operation (such as receiving data from a BSD
+/// socket) and the rest of the BIND10 DNS library. One common usage of this
+/// class for an application would therefore be something like this:
+///
+/// \code unsigned char buf[1024];
+/// struct sockaddr addr;
+/// socklen_t addrlen = sizeof(addr);
+/// int cc = recvfrom(s, buf, sizeof(buf), 0, &addr, &addrlen);
+/// InputBuffer buffer(buf, cc);
+/// // pass the buffer to a DNS message object to parse the message \endcode
+///
+/// Other BIND10 DNS classes will then use methods of this class to get access
+/// to the data, but the application normally doesn't have to care about the
+/// details.
+///
+/// An \c InputBuffer object internally holds a reference to the given data,
+/// rather than make a local copy of the data. Also, it does not have an
+/// ownership of the given data. It is application's responsibility to ensure
+/// the data remains valid throughout the lifetime of the \c InputBuffer
+/// object. Likewise, this object generally assumes the data isn't modified
+/// throughout its lifetime; if the application modifies the data while this
+/// object retains a reference to it, the result is undefined. The application
+/// will also be responsible for releasing the data when it's not needed if it
+/// was dynamically acquired.
+///
+/// This is a deliberate design choice: although it's safer to make a local
+/// copy of the given data on construction, it would cause unacceptable
+/// performance overhead, especially considering that a DNS message can be
+/// as large as a few KB. Alternatively, we could allow the object to allocate
+/// memory internally and expose it to the application to store network data
+/// in it. This is also a bad design, however, in that we would effectively
+/// break the abstraction employed in the class, and do so by publishing
+/// "read-only" stuff as a writable memory region. Since there doesn't seem to
+/// be a perfect solution, we have adopted what we thought a "least bad" one.
+///
+/// Methods for reading data from the buffer generally work like an input
+/// stream: it begins with the head of the data, and once some length of data
+/// is read from the buffer, the next read operation will take place from the
+/// head of the unread data. An object of this class internally holds (a
+/// notion of) where the next read operation should start. We call it the
+/// <em>read position</em> in this document.
+class InputBuffer {
+public:
+ ///
+ /// \name Constructors and Destructor
+ //@{
+ /// \brief Constructor from variable length of data.
+ ///
+ /// It is caller's responsibility to ensure that the data is valid as long
+ /// as the buffer exists.
+ /// \param data A pointer to the data stored in the buffer.
+ /// \param len The length of the data in bytes.
+ InputBuffer(const void* data, size_t len) :
+ position_(0), data_(static_cast<const uint8_t*>(data)), len_(len) {}
+ //@}
+
+ ///
+ /// \name Getter Methods
+ //@{
+ /// \brief Return the length of the data stored in the buffer.
+ size_t getLength() const { return (len_); }
+ /// \brief Return the current read position.
+ size_t getPosition() const { return (position_); }
+ //@}
+
+ ///
+ /// \name Setter Methods
+ ///
+ //@{
+ /// \brief Set the read position of the buffer to the given value.
+ ///
+ /// The new position must be in the valid range of the buffer; otherwise
+ /// an exception of class \c isc::dns::InvalidBufferPosition will be thrown.
+ /// \param position The new position (offset from the beginning of the
+ /// buffer).
+ void setPosition(size_t position)
+ {
+ if (position > len_)
+ isc_throw(InvalidBufferPosition, "position is too large");
+ position_ = position;
+ }
+ //@}
+
+ ///
+ /// \name Methods for reading data from the buffer.
+ //@{
+ /// \brief Read an unsigned 8-bit integer from the buffer and return it.
+ ///
+ /// If the remaining length of the buffer is smaller than 8-bit, an
+ /// exception of class \c isc::dns::InvalidBufferPosition will be thrown.
+ uint8_t readUint8()
+ {
+ if (position_ + sizeof(uint8_t) > len_) {
+ isc_throw(InvalidBufferPosition, "read beyond end of buffer");
+ }
+
+ return (data_[position_++]);
+ }
+ /// \brief Read an unsigned 16-bit integer in network byte order from the
+ /// buffer, convert it to host byte order, and return it.
+ ///
+ /// If the remaining length of the buffer is smaller than 16-bit, an
+ /// exception of class \c isc::dns::InvalidBufferPosition will be thrown.
+ uint16_t readUint16()
+ {
+ uint16_t data;
+ const uint8_t* cp;
+
+ if (position_ + sizeof(data) > len_) {
+ isc_throw(InvalidBufferPosition, "read beyond end of buffer");
+ }
+
+ cp = &data_[position_];
+ data = ((unsigned int)(cp[0])) << 8;
+ data |= ((unsigned int)(cp[1]));
+ position_ += sizeof(data);
+
+ return (data);
+ }
+ /// \brief Read an unsigned 32-bit integer in network byte order from the
+ /// buffer, convert it to host byte order, and return it.
+ ///
+ /// If the remaining length of the buffer is smaller than 32-bit, an
+ /// exception of class \c isc::dns::InvalidBufferPosition will be thrown.
+ uint32_t readUint32()
+ {
+ uint32_t data;
+ const uint8_t* cp;
+
+ if (position_ + sizeof(data) > len_) {
+ isc_throw(InvalidBufferPosition, "read beyond end of buffer");
+ }
+
+ cp = &data_[position_];
+ data = ((unsigned int)(cp[0])) << 24;
+ data |= ((unsigned int)(cp[1])) << 16;
+ data |= ((unsigned int)(cp[2])) << 8;
+ data |= ((unsigned int)(cp[3]));
+ position_ += sizeof(data);
+
+ return (data);
+ }
+ /// \brief Read data of the specified length from the buffer and copy it to
+ /// the caller supplied buffer.
+ ///
+ /// The data is copied as stored in the buffer; no conversion is performed.
+ /// If the remaining length of the buffer is smaller than the specified
+ /// length, an exception of class \c isc::dns::InvalidBufferPosition will
+ /// be thrown.
+ void readData(void* data, size_t len)
+ {
+ if (position_ + len > len_) {
+ isc_throw(InvalidBufferPosition, "read beyond end of buffer");
+ }
+
+ memcpy(data, &data_[position_], len);
+ position_ += len;
+ }
+ //@}
+
+private:
+ size_t position_;
+
+ // XXX: The following must be private, but for a short term workaround with
+ // Boost.Python binding, we changed it to protected. We should soon
+ // revisit it.
+protected:
+ const uint8_t* data_;
+ size_t len_;
+};
+
+///
+///\brief The \c OutputBuffer class is a buffer abstraction for manipulating
+/// mutable data.
+///
+/// The main purpose of this class is to provide a safe workplace for
+/// constructing wire-format data to be sent out to a network. Here,
+/// <em>safe</em> means that it automatically allocates necessary memory and
+/// avoid buffer overrun.
+///
+/// Like for the \c InputBuffer class, applications normally use this class only
+/// in a limited situation. One common usage of this class for an application
+/// would be something like this:
+///
+/// \code OutputBuffer buffer(4096); // give a sufficiently large initial size
+/// // pass the buffer to a DNS message object to construct a wire-format
+/// // DNS message.
+/// struct sockaddr to;
+/// sendto(s, buffer.getData(), buffer.getLength(), 0, &to, sizeof(to));
+/// \endcode
+///
+/// where the \c getData() method gives a reference to the internal memory
+/// region stored in the \c buffer object. This is a suboptimal design in that
+/// it exposes an encapsulated "handle" of an object to its user.
+/// Unfortunately, there is no easy way to avoid this without involving
+/// expensive data copy if we want to use this object with a legacy API such as
+/// a BSD socket interface. And, indeed, this is one major purpose for this
+/// object. Applications should use this method only under such a special
+/// circumstance. It should also be noted that the memory region returned by
+/// \c getData() may be invalidated after a subsequent write operation.
+///
+/// An \c OutputBuffer class object automatically extends its memory region when
+/// data is written beyond the end of the current buffer. However, it will
+/// involve performance overhead such as reallocating more memory and copying
+/// data. It is therefore recommended to construct the buffer object with a
+/// sufficiently large initial size.
+/// The \c getCapacity() method provides the current maximum size of data
+/// (including the portion already written) that can be written into the buffer
+/// without causing memory reallocation.
+///
+/// Methods for writing data into the buffer generally work like an output
+/// stream: it begins with the head of the buffer, and once some length of data
+/// is written into the buffer, the next write operation will take place from
+/// the end of the buffer. Other methods to emulate "random access" are also
+/// provided (e.g., \c writeUint16At()). The normal write operations are
+/// normally exception-free as this class automatically extends the buffer
+/// when necessary. However, in extreme cases such as an attempt of writing
+/// multi-GB data, a separate exception (e.g., \c std::bad_alloc) may be thrown
+/// by the system. This also applies to the constructor with a very large
+/// initial size.
+///
+/// Note to developers: it may make more sense to introduce an abstract base
+/// class for the \c OutputBuffer and define the simple implementation as a
+/// a concrete derived class. That way we can provide flexibility for future
+/// extension such as more efficient buffer implementation or allowing users
+/// to have their own customized version without modifying the source code.
+/// We in fact considered that option, but at the moment chose the simpler
+/// approach with a single concrete class because it may make the
+/// implementation unnecessarily complicated while we were still not certain
+/// if we really want that flexibility. We may revisit the class design as
+/// we see more applications of the class. The same considerations apply to
+/// the \c InputBuffer and \c MessageRenderer classes.
+class OutputBuffer {
+public:
+ ///
+ /// \name Constructors and Destructor
+ ///
+ //@{
+ /// \brief Constructor from the initial size of the buffer.
+ ///
+ /// \param len The initial length of the buffer in bytes.
+ OutputBuffer(size_t len) { data_.reserve(len); }
+ //@}
+
+ ///
+ /// \name Getter Methods
+ ///
+ //@{
+ /// \brief Return the current capacity of the buffer.
+ size_t getCapacity() const { return (data_.capacity()); }
+ /// \brief Return a pointer to the head of the data stored in the buffer.
+ ///
+ /// The caller can assume that the subsequent \c getLength() bytes are
+ /// identical to the stored data of the buffer.
+ ///
+ /// Note: The pointer returned by this method may be invalidated after a
+ /// subsequent write operation.
+ const void* getData() const { return (&data_[0]); }
+ /// \brief Return the length of data written in the buffer.
+ size_t getLength() const { return (data_.size()); }
+ /// \brief Return the value of the buffer at the specified position.
+ ///
+ /// \c pos must specify the valid position of the buffer; otherwise an
+ /// exception class of \c InvalidBufferPosition will be thrown.
+ ///
+ /// \param pos The position in the buffer to be returned.
+ const uint8_t& operator[](size_t pos) const
+ {
+ if (pos >= data_.size()) {
+ isc_throw(InvalidBufferPosition, "read at invalid position");
+ }
+ return (data_[pos]);
+ }
+ //@}
+
+ ///
+ /// \name Methods for writing data into the buffer.
+ ///
+ //@{
+ /// \brief Insert a specified length of gap at the end of the buffer.
+ ///
+ /// The caller should not assume any particular value to be inserted.
+ /// This method is provided as a shortcut to make a hole in the buffer
+ /// that is to be filled in later, e.g, by \ref writeUint16At().
+ /// \param len The length of the gap to be inserted in bytes.
+ void skip(size_t len) { data_.insert(data_.end(), len, 0); }
+
+ /// \brief Trim the specified length of data from the end of the buffer.
+ ///
+ /// The specified length must not exceed the current data size of the
+ /// buffer; otherwise an exception of class \c isc::OutOfRange will
+ /// be thrown.
+ ///
+ /// \param len The length of data that should be trimmed.
+ void trim(size_t len)
+ {
+ if (len > data_.size()) {
+ isc_throw(OutOfRange, "trimming too large from output buffer");
+ }
+ data_.resize(data_.size() - len);
+ }
+ /// \brief Clear buffer content.
+ ///
+ /// This method can be used to re-initialize and reuse the buffer without
+ /// constructing a new one.
+ void clear() { data_.clear(); }
+ /// \brief Write an unsigned 8-bit integer into the buffer.
+ ///
+ /// \param data The 8-bit integer to be written into the buffer.
+ void writeUint8(uint8_t data) { data_.push_back(data); }
+
+ /// \brief Write an unsigned 8-bit integer into the buffer.
+ ///
+ /// The position must be lower than the size of the buffer,
+ /// otherwise an exception of class \c isc::dns::InvalidBufferPosition
+ /// will be thrown.
+ ///
+ /// \param data The 8-bit integer to be written into the buffer.
+ /// \param pos The position in the buffer to write the data.
+ void writeUint8At(uint8_t data, size_t pos) {
+ if (pos + sizeof(data) > data_.size()) {
+ isc_throw(InvalidBufferPosition, "write at invalid position");
+ }
+ data_[pos] = data;
+ }
+
+ /// \brief Write an unsigned 16-bit integer in host byte order into the
+ /// buffer in network byte order.
+ ///
+ /// \param data The 16-bit integer to be written into the buffer.
+ void writeUint16(uint16_t data)
+ {
+ data_.push_back(static_cast<uint8_t>((data & 0xff00U) >> 8));
+ data_.push_back(static_cast<uint8_t>(data & 0x00ffU));
+ }
+ /// \brief Write an unsigned 16-bit integer in host byte order at the
+ /// specified position of the buffer in network byte order.
+ ///
+ /// The buffer must have a sufficient room to store the given data at the
+ /// given position, that is, <code>pos + 2 < getLength()</code>;
+ /// otherwise an exception of class \c isc::dns::InvalidBufferPosition will
+ /// be thrown.
+ /// Note also that this method never extends the buffer.
+ ///
+ /// \param data The 16-bit integer to be written into the buffer.
+ /// \param pos The beginning position in the buffer to write the data.
+ void writeUint16At(uint16_t data, size_t pos)
+ {
+ if (pos + sizeof(data) > data_.size()) {
+ isc_throw(InvalidBufferPosition, "write at invalid position");
+ }
+
+ data_[pos] = static_cast<uint8_t>((data & 0xff00U) >> 8);
+ data_[pos + 1] = static_cast<uint8_t>(data & 0x00ffU);
+ }
+ /// \brief Write an unsigned 32-bit integer in host byte order
+ /// into the buffer in network byte order.
+ ///
+ /// \param data The 32-bit integer to be written into the buffer.
+ void writeUint32(uint32_t data)
+ {
+ data_.push_back(static_cast<uint8_t>((data & 0xff000000) >> 24));
+ data_.push_back(static_cast<uint8_t>((data & 0x00ff0000) >> 16));
+ data_.push_back(static_cast<uint8_t>((data & 0x0000ff00) >> 8));
+ data_.push_back(static_cast<uint8_t>(data & 0x000000ff));
+ }
+ /// \brief Copy an arbitrary length of data into the buffer.
+ ///
+ /// No conversion on the copied data is performed.
+ ///
+ /// \param data A pointer to the data to be copied into the buffer.
+ /// \param len The length of the data in bytes.
+ void writeData(const void *data, size_t len)
+ {
+ const uint8_t* cp = static_cast<const uint8_t*>(data);
+ data_.insert(data_.end(), cp, cp + len);
+ }
+ //@}
+
+private:
+ std::vector<uint8_t> data_;
+};
+
+/// \brief Pointer-like types pointing to \c InputBuffer or \c OutputBuffer
+///
+/// These types are expected to be used as an argument in asynchronous
+/// callback functions. The internal reference-counting will ensure that
+/// that ongoing state information will not be lost if the object
+/// that originated the asynchronous call falls out of scope.
+typedef boost::shared_ptr<InputBuffer> InputBufferPtr;
+typedef boost::shared_ptr<OutputBuffer> OutputBufferPtr;
+
+} // namespace io
+} // namespace util
+} // namespace isc
+#endif // __BUFFER_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/util/io/io_utilities.h b/src/lib/util/io/io_utilities.h
new file mode 100644
index 0000000..40b200c
--- /dev/null
+++ b/src/lib/util/io/io_utilities.h
@@ -0,0 +1,65 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __IO_UTILITIES_H
+#define __IO_UTILITIES_H
+
+#include <cstddef>
+
+namespace isc {
+namespace util {
+namespace io {
+
+/// \brief Read Unsigned 16-Bit Integer from Buffer
+///
+/// This is essentially a copy of the isc::util::InputBuffer::readUint16. It
+/// should really be moved into a separate library.
+///
+/// \param buffer Data buffer at least two bytes long of which the first two
+/// bytes are assumed to represent a 16-bit integer in network-byte
+/// order.
+///
+/// \return Value of 16-bit integer
+inline uint16_t
+readUint16(const void* buffer) {
+ const uint8_t* byte_buffer = static_cast<const uint8_t*>(buffer);
+
+ uint16_t result = (static_cast<uint16_t>(byte_buffer[0])) << 8;
+ result |= static_cast<uint16_t>(byte_buffer[1]);
+
+ return (result);
+}
+
+/// \brief Write Unisgned 16-Bit Integer to Buffer
+///
+/// This is essentially a copy of isc::util::OutputBuffer::writeUint16. It
+/// should really be moved into a separate library.
+///
+/// \param value 16-bit value to convert
+/// \param buffer Data buffer at least two bytes long into which the 16-bit
+/// value is written in network-byte order.
+
+inline void
+writeUint16(uint16_t value, void* buffer) {
+ uint8_t* byte_buffer = static_cast<uint8_t*>(buffer);
+
+ byte_buffer[0] = static_cast<uint8_t>((value & 0xff00U) >> 8);
+ byte_buffer[1] = static_cast<uint8_t>(value & 0x00ffU);
+}
+
+} // namespace io
+} // namespace util
+} // namespace isc
+
+#endif // __ASIOLINK_UTILITIES_H
diff --git a/src/lib/util/locks.h b/src/lib/util/locks.h
new file mode 100644
index 0000000..98197c3
--- /dev/null
+++ b/src/lib/util/locks.h
@@ -0,0 +1,116 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// This file (right now) provides dummy locks
+/// It also contains code to use boost/threads locks:
+///
+/// if USE_BOOST_THREADS is defined, we typedef the relevant classes
+/// and derive from the relevant templates so our dummy locks are
+/// replaced by the boost locks (--enable-boost-threads)
+///
+/// If USE_BOOST_THREADS is NOT defined, all locks are dummy classes
+/// that don't actually do anything. At this moment, only the very
+/// minimal set of methods that we actually use is defined.
+///
+/// Note that we need to include <config.h> in our .cc files for that
+/// to be set. we might want to enfore this at compile time with a check
+/// (TODO)
+/// Note that this also contains a workaround for Sunstudio; which
+/// probably won't completely work right now (that is, if the TODO
+/// above is completed), since that would also require some changes
+/// in most (at first glance unrelated) Makefiles
+/// (TODO2)
+
+#ifndef __LOCKS_
+#define __LOCKS_
+
+#ifndef USE_BOOST_THREADS
+
+namespace isc {
+namespace locks {
+
+class mutex {
+};
+
+class recursive_mutex {
+};
+
+class upgradable_mutex {
+};
+
+template <typename T>
+class sharable_lock {
+public:
+ sharable_lock(T) { }
+};
+
+template <typename T>
+class scoped_lock {
+public:
+ scoped_lock(T) { }
+
+ void lock() {}
+ void unlock() {}
+};
+
+}
+}
+
+#else // USE_BOOST_THREADS
+
+// Workaround for a problem with boost and sunstudio 5.10
+// There is a version check in there that appears wrong,
+// which makes including boost/thread.hpp fail
+// This will probably be fixed in a future version of boost,
+// in which case this part can be removed then
+#ifdef NEED_SUNPRO_WORKAROUND
+#if defined(__SUNPRO_CC) && __SUNPRO_CC == 0x5100
+#undef __SUNPRO_CC
+#define __SUNPRO_CC 0x5090
+#endif
+#endif // NEED_SUNPRO_WORKAROUND
+
+#include <boost/thread.hpp>
+#include <boost/interprocess/sync/sharable_lock.hpp>
+#include <boost/interprocess/sync/scoped_lock.hpp>
+#include <boost/interprocess/sync/interprocess_upgradable_mutex.hpp>
+#include <boost/interprocess/sync/interprocess_recursive_mutex.hpp>
+
+namespace isc {
+namespace locks {
+
+typedef boost::mutex mutex;
+typedef boost::interprocess::interprocess_upgradable_mutex upgradable_mutex;
+typedef boost::interprocess::interprocess_recursive_mutex recursive_mutex;
+
+template <typename T>
+struct sharable_lock : public boost::interprocess::sharable_lock<T> {
+public:
+ sharable_lock(T& mtype) : boost::interprocess::sharable_lock<T>(mtype) {}
+};
+
+
+template <class T>
+struct scoped_lock : public boost::interprocess::scoped_lock<T> {
+public:
+ scoped_lock(T& mtype) : boost::interprocess::scoped_lock<T>(mtype) { }
+};
+
+}
+}
+
+
+#endif // USE_BOOST_THREADS
+
+#endif // __LOCKS_
diff --git a/src/lib/util/lru_list.h b/src/lib/util/lru_list.h
new file mode 100644
index 0000000..19462d5
--- /dev/null
+++ b/src/lib/util/lru_list.h
@@ -0,0 +1,260 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __LRU_LIST_H
+#define __LRU_LIST_H
+
+#include <list>
+#include <string>
+
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <util/locks.h>
+
+namespace isc {
+namespace util {
+
+/// \brief LRU List
+///
+/// Provides the LRU list for the zone and nameserver objects. The list is
+/// created with a specific size. Entries are added to the back of the list
+/// and removed from the front. It is also possible to pull an element out
+/// of the middle of the list and add it to the end of the list, an action that
+/// should be done when the element is referenced.
+///
+/// It is not intended that the class be copied, and the derivation from
+/// boost::noncopyable enforces this.
+template <typename T>
+class LruList : boost::noncopyable {
+public:
+ typedef typename std::list<boost::shared_ptr<T> > lru_list;
+ typedef typename lru_list::iterator iterator;
+
+ /// \brief Dropped Operation
+ ///
+ /// When an object is dropped from the LRU list because it has not been
+ /// accessed for some time, it is possible that the action should trigger
+ /// some other functions. For this reason, it is possible to register
+ /// a list-wide functor object to execute in this casee.
+ ///
+ /// Note that the function does not execute as the result of a call to
+ /// remove() - that is an explicit call and it is assumed that the caller
+ /// will handle any additional operations needed.
+ class Dropped {
+ public:
+ /// \brief Constructor
+ Dropped(){}
+
+ /// \brief Virtual Destructor
+ virtual ~Dropped(){}
+
+ /// \brief Dropped Object Handler
+ ///
+ /// Function object called when the object drops off the end of the
+ /// LRU list.
+ ///
+ /// \param drop Object being dropped.
+ virtual void operator()(T* drop) const = 0;
+ };
+
+ /// \brief Constructor
+ ///
+ /// \param max_size Maximum size of the list before elements are dropped.
+ /// \param dropped Pointer to a function object that will get called as
+ /// elements are dropped. This object will be stored using a shared_ptr,
+ /// so should be allocated with new().
+ LruList(uint32_t max_size = 1000, Dropped* dropped = NULL) :
+ max_size_(max_size), count_(0), dropped_(dropped)
+ {}
+
+ /// \brief Virtual Destructor
+ virtual ~LruList()
+ {}
+
+ /// \brief Add Element
+ ///
+ /// Add a new element to the end of the list.
+ ///
+ /// \param element Reference to the element to add.
+ ///
+ /// \return Handle describing the element in the LRU list.
+ virtual void add(boost::shared_ptr<T>& element);
+
+ /// \brief Remove Element
+ ///
+ /// Removes an element from the list. If the element is not present (i.e.
+ /// its internal list pointer is invalid), this is a no-op.
+ ///
+ /// \param element Reference to the element to remove.
+ virtual void remove(boost::shared_ptr<T>& element);
+
+ /// \brief Touch Element
+ ///
+ /// The name comes from the Unix "touch" command. All this does is to
+ /// move the specified entry from the middle of the list to the end of
+ /// the list.
+ ///
+ /// \param element Reference to the element to touch.
+ virtual void touch(boost::shared_ptr<T>& element);
+
+ /// \brief Drop All the Elements in the List .
+ ///
+ /// All the elements will be dropped from the list container, and their
+ /// drop handler(if there is one) will be called, when done, the size of
+ /// of list will be 0.
+ virtual void clear();
+
+ /// \brief Return Size of the List
+ ///
+ /// An independent count is kept of the list size, as list.size() may take
+ /// some time if the list is big.
+ ///
+ /// \return Number of elements in the list
+ virtual uint32_t size() const {
+
+ // Don't bother to lock the mutex. If an update is in progress, we
+ // receive either the value just before the update or just after it.
+ // Either way, this call could have come just before or just after
+ // that operation, so the value would have been just as uncertain.
+ return count_;
+ }
+
+ /// \brief Return Maximum Size
+ ///
+ /// \return Maximum size of the list
+ virtual uint32_t getMaxSize() const {
+ return max_size_;
+ }
+
+ /// \brief Set Maximum Size
+ ///
+ /// \param max_size New maximum list size
+ virtual void setMaxSize(uint32_t max_size) {
+ max_size_ = max_size;
+ }
+
+private:
+ isc::locks::mutex mutex_; ///< List protection
+ std::list<boost::shared_ptr<T> > lru_; ///< The LRU list itself
+ uint32_t max_size_; ///< Max size of the list
+ uint32_t count_; ///< Count of elements
+ boost::shared_ptr<Dropped> dropped_; ///< Dropped object
+};
+
+// Add entry to the list
+template <typename T>
+void LruList<T>::add(boost::shared_ptr<T>& element) {
+
+ // Protect list against concurrent access
+ isc::locks::scoped_lock<isc::locks::mutex> lock(mutex_);
+
+ // Add the entry and set its pointer field to point into the list.
+ // insert() is used to get the pointer.
+ element->setLruIterator(lru_.insert(lru_.end(), element));
+
+ // ... and update the count while we have the mutex.
+ ++count_;
+
+ // If the count takes us above the maximum size of the list, remove elements
+ // from the front. The current list size could be more than one above the
+ // maximum size of the list if the maximum size was changed after
+ // construction.
+ while (count_ > max_size_) {
+ if (!lru_.empty()) {
+
+ // Run the drop handler (if there is one) on the
+
+ // to-be-dropped object.
+ if (dropped_) {
+ (*dropped_)(lru_.begin()->get());
+ }
+
+ // ... and get rid of it from the list
+ lru_.pop_front();
+ --count_;
+ }
+ else {
+
+ // TODO: Log this condition (count_ > 0 when list empty) -
+ // it should not happen
+ count_ = 0;
+ break;
+ }
+ }
+}
+
+// Remove an element from the list
+template <typename T>
+void LruList<T>::remove(boost::shared_ptr<T>& element) {
+
+ // An element can only be removed it its internal pointer is valid.
+ // If it is, the pointer can be used to access the list because no matter
+ // what other elements are added or removed, the pointer remains valid.
+ //
+ // If the pointer is not valid, this is a no-op.
+ if (element->iteratorValid()) {
+
+ // Is valid, so protect list against concurrent access
+ isc::locks::scoped_lock<isc::locks::mutex> lock(mutex_);
+
+ lru_.erase(element->getLruIterator()); // Remove element from list
+ element->invalidateIterator(); // Invalidate pointer
+ --count_; // One less element
+ }
+}
+
+// Touch an element - remove it from the list and add to the end
+template <typename T>
+void LruList<T>::touch(boost::shared_ptr<T>& element) {
+
+ // As before, if the pointer is not valid, this is a no-op.
+ if (element->iteratorValid()) {
+
+ // Protect list against concurrent access
+ isc::locks::scoped_lock<isc::locks::mutex> lock(mutex_);
+
+ // Move the element to the end of the list.
+ lru_.splice(lru_.end(), lru_, element->getLruIterator());
+
+ // Update the iterator in the element to point to it. We can
+ // offset from end() as a list has a bidirectional iterator.
+ iterator i = lru_.end();
+ element->setLruIterator(--i);
+ }
+}
+
+// Clear the list- when done, the size of list will be 0.
+template <typename T>
+void LruList<T>::clear() {
+ // Protect list against concurrent access
+ isc::locks::scoped_lock<isc::locks::mutex> lock(mutex_);
+
+ // ... and update the count while we have the mutex.
+ count_ = 0;
+ typename std::list<boost::shared_ptr<T> >::iterator iter;
+ if (dropped_) {
+ for (iter = lru_.begin(); iter != lru_.end(); ++iter) {
+ // Call the drop handler.
+ (*dropped_)(iter->get());
+ }
+ }
+
+ lru_.clear();
+}
+
+} // namespace util
+} // namespace isc
+
+#endif // __LRU_LIST_H
diff --git a/src/lib/util/random/qid_gen.cc b/src/lib/util/random/qid_gen.cc
new file mode 100644
index 0000000..43041ad
--- /dev/null
+++ b/src/lib/util/random/qid_gen.cc
@@ -0,0 +1,57 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// qid_gen defines a generator for query id's
+//
+// We probably want to merge this with the weighted random in the nsas
+// (and other parts where we need randomness, perhaps another thing
+// for a general libutil?)
+
+#include <util/random/qid_gen.h>
+
+#include <sys/time.h>
+
+namespace isc {
+namespace util {
+namespace random {
+
+QidGenerator qid_generator_instance;
+
+QidGenerator&
+QidGenerator::getInstance() {
+ return (qid_generator_instance);
+}
+
+QidGenerator::QidGenerator() : dist_(0, 65535),
+ vgen_(generator_, dist_)
+{
+ seed();
+}
+
+void
+QidGenerator::seed() {
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+ generator_.seed((tv.tv_sec * 1000000) + tv.tv_usec);
+}
+
+uint16_t
+QidGenerator::generateQid() {
+ return (vgen_());
+}
+
+
+} // namespace random
+} // namespace util
+} // namespace isc
diff --git a/src/lib/util/random/qid_gen.h b/src/lib/util/random/qid_gen.h
new file mode 100644
index 0000000..1af43c1
--- /dev/null
+++ b/src/lib/util/random/qid_gen.h
@@ -0,0 +1,87 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// qid_gen defines a generator for query id's
+//
+// We probably want to merge this with the weighted random in the nsas
+// (and other parts where we need randomness, perhaps another thing
+// for a general libutil?)
+
+#ifndef __QID_GEN_H
+#define __QID_GEN_H
+
+#include <boost/random/mersenne_twister.hpp>
+#include <boost/random/uniform_int.hpp>
+#include <boost/random/variate_generator.hpp>
+
+namespace isc {
+namespace util {
+namespace random {
+
+/// This class generates Qids for outgoing queries
+///
+/// It is implemented as a singleton; the public way to access it
+/// is to call getInstance()->generateQid().
+///
+/// It automatically seeds it with the current time when it is first
+/// used.
+class QidGenerator {
+public:
+ /// \brief Returns the singleton instance of the QidGenerator
+ ///
+ /// Returns a reference to the singleton instance of the generator
+ static QidGenerator& getInstance();
+
+ /// \brief Default constructor
+ ///
+ /// It is recommended that getInstance is used rather than creating
+ /// separate instances of this class.
+ ///
+ /// The constructor automatically seeds the generator with the
+ /// current time.
+ QidGenerator();
+
+ /// Generate a Qid
+ ///
+ /// \return A random Qid
+ uint16_t generateQid();
+
+ /// \brief Seeds the QidGenerator (based on the current time)
+ ///
+ /// This is automatically called by the constructor
+ void seed();
+
+private:
+ // "Mersenne Twister: A 623-dimensionally equidistributed
+ // uniform pseudo-random number generator", Makoto Matsumoto and
+ // Takuji Nishimura, ACM Transactions on Modeling and Computer
+ // Simulation: Special Issue on Uniform Random Number Generation,
+ // Vol. 8, No. 1, January 1998, pp. 3-30.
+ //
+ // mt19937 is an implementation of one of the pseudo random
+ // generators described in this paper.
+ boost::mt19937 generator_;
+
+ // For qid's we want a uniform distribution
+ boost::uniform_int<> dist_;
+
+ boost::variate_generator<boost::mt19937&, boost::uniform_int<> > vgen_;
+};
+
+
+} // namespace random
+} // namespace util
+} // namespace isc
+
+#endif // __QID_GEN_H
diff --git a/src/lib/util/random/random_number_generator.h b/src/lib/util/random/random_number_generator.h
new file mode 100644
index 0000000..485ea7a
--- /dev/null
+++ b/src/lib/util/random/random_number_generator.h
@@ -0,0 +1,208 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __NSAS_RANDOM_NUMBER_GENERATOR_H
+#define __NSAS_RANDOM_NUMBER_GENERATOR_H
+
+#include <algorithm>
+#include <cmath>
+#include <numeric>
+
+#include <exceptions/exceptions.h>
+
+#include <boost/random/mersenne_twister.hpp>
+#include <boost/random/uniform_int.hpp>
+#include <boost/random/uniform_real.hpp>
+#include <boost/random/variate_generator.hpp>
+
+namespace isc {
+namespace util {
+namespace random {
+
+class InvalidLimits : public isc::BadValue {
+public:
+ InvalidLimits(const char* file, size_t line, const char* what) :
+ isc::BadValue(file, line, what) {}
+};
+
+class SumNotOne : public isc::BadValue {
+public:
+ SumNotOne(const char* file, size_t line, const char* what) :
+ isc::BadValue(file, line, what) {}
+};
+
+class InvalidProbValue : public isc::BadValue {
+public:
+ InvalidProbValue(const char* file, size_t line, const char* what) :
+ isc::BadValue(file, line, what) {}
+};
+
+
+
+/// \brief Uniform random integer generator
+///
+/// Generate uniformly distributed integers in range of [min, max]
+class UniformRandomIntegerGenerator{
+public:
+ /// \brief Constructor
+ ///
+ /// \param min The minimum number in the range
+ /// \param max The maximum number in the range
+ UniformRandomIntegerGenerator(int min, int max):
+ min_(std::min(min, max)), max_(std::max(min, max)),
+ dist_(min_, max_), generator_(rng_, dist_)
+ {
+ // To preserve the restriction of the underlying uniform_int class (and
+ // to retain compatibility with earlier versions of the class), we will
+ // abort if the minimum and maximum given are the wrong way round.
+ if (min > max) {
+ isc_throw(InvalidLimits, "minimum limit is greater than maximum "
+ "when initializing UniformRandomIntegerGenerator");
+ }
+
+ // Init with the current time
+ rng_.seed(time(NULL));
+ }
+
+ /// \brief Generate uniformly distributed integer
+ int operator()() { return generator_(); }
+private:
+ /// Hide default and copy constructor
+ UniformRandomIntegerGenerator();///< Default constructor
+ UniformRandomIntegerGenerator(const UniformRandomIntegerGenerator&); ///< Copy constructor
+
+ int min_; ///< The minimum integer that can generate
+ int max_; ///< The maximum integer that can generate
+ boost::uniform_int<> dist_; ///< Distribute uniformly.
+ boost::mt19937 rng_; ///< Mersenne Twister: A 623-dimensionally equidistributed uniform pseudo-random number generator
+ boost::variate_generator<boost::mt19937&, boost::uniform_int<> > generator_; ///< Uniform generator
+};
+
+/// \brief Weighted random integer generator
+///
+/// Generate random integers according different probabilities
+class WeightedRandomIntegerGenerator {
+public:
+ /// \brief Constructor
+ ///
+ /// \param probabilities The probabies for all the integers, the probability must be
+ /// between 0 and 1.0, the sum of probabilities must be equal to 1.
+ /// For example, if the probabilities contains the following values:
+ /// 0.5 0.3 0.2, the 1st integer will be generated more frequently than the
+ /// other integers and the probability is proportional to its value.
+ /// \param min The minimum integer that generated, other integers will be
+ /// min, min + 1, ..., min + probabilities.size() - 1
+ WeightedRandomIntegerGenerator(const std::vector<double>& probabilities,
+ size_t min = 0):
+ dist_(0, 1.0), uniform_real_gen_(rng_, dist_), min_(min)
+ {
+ // The probabilities must be valid. Checking is quite an expensive
+ // operation, so is only done in a debug build.
+ assert(areProbabilitiesValid(probabilities));
+
+ // Calculate the partial sum of probabilities
+ std::partial_sum(probabilities.begin(), probabilities.end(),
+ std::back_inserter(cumulative_));
+ // Init with the current time
+ rng_.seed(time(NULL));
+ }
+
+ /// \brief Default constructor
+ ///
+ WeightedRandomIntegerGenerator():
+ dist_(0, 1.0), uniform_real_gen_(rng_, dist_), min_(0)
+ {
+ }
+
+ /// \brief Reset the probabilities
+ ///
+ /// Change the weights of each integers
+ /// \param probabilities The probabies for all the integers
+ /// \param min The minimum integer that generated
+ void reset(const std::vector<double>& probabilities, size_t min = 0)
+ {
+ // The probabilities must be valid.
+ assert(areProbabilitiesValid(probabilities));
+
+ // Reset the cumulative sum
+ cumulative_.clear();
+
+ // Calculate the partial sum of probabilities
+ std::partial_sum(probabilities.begin(), probabilities.end(),
+ std::back_inserter(cumulative_));
+
+ // Reset the minimum integer
+ min_ = min;
+ }
+
+ /// \brief Generate weighted random integer
+ size_t operator()()
+ {
+ return std::lower_bound(cumulative_.begin(), cumulative_.end(), uniform_real_gen_())
+ - cumulative_.begin() + min_;
+ }
+
+private:
+ /// \brief Check the validation of probabilities vector
+ ///
+ /// The probability must be in range of [0, 1.0] and the sum must be equal
+ /// to 1.0. Empty probabilities are also valid.
+ ///
+ /// Checking the probabilities is quite an expensive operation, so it is
+ /// only done during a debug build (via a call through assert()). However,
+ /// instead of letting assert() call abort(), if this method encounters an
+ /// error, an exception is thrown. This makes unit testing somewhat easier.
+ ///
+ /// \param probabilities Vector of probabilities.
+ bool areProbabilitiesValid(const std::vector<double>& probabilities) const
+ {
+ typedef std::vector<double>::const_iterator Iterator;
+ double sum = probabilities.empty() ? 1.0 : 0.0;
+ for(Iterator it = probabilities.begin(); it != probabilities.end(); ++it){
+ //The probability must be in [0, 1.0]
+ if(*it < 0.0 || *it > 1.0) {
+ isc_throw(InvalidProbValue,
+ "probability must be in the range 0..1");
+ }
+
+ sum += *it;
+ }
+
+ double epsilon = 0.0001;
+ // The sum must be equal to 1
+ if (std::fabs(sum - 1.0) >= epsilon) {
+ isc_throw(SumNotOne, "Sum of probabilities is not equal to 1");
+ }
+
+ return true;
+ }
+
+ std::vector<double> cumulative_; ///< Partial sum of the probabilities
+ boost::mt19937 rng_; ///< Mersenne Twister: A 623-dimensionally equidistributed uniform pseudo-random number generator
+ boost::uniform_real<> dist_; ///< Uniformly distributed real numbers
+
+ // Shortcut typedef
+ // This typedef is placed directly before its use, as the sunstudio
+ // compiler could not handle it being anywhere else (don't know why)
+ typedef boost::variate_generator<boost::mt19937&, boost::uniform_real<> > UniformRealGenerator;
+ UniformRealGenerator uniform_real_gen_; ///< Uniformly distributed random real numbers generator
+
+ size_t min_; ///< The minimum integer that will be generated
+};
+
+} // namespace random
+} // namespace util
+} // namespace isc
+
+#endif//__NSAS_RANDOM_NUMBER_GENERATOR_H
diff --git a/src/lib/util/strutil.cc b/src/lib/util/strutil.cc
new file mode 100644
index 0000000..25e0fbe
--- /dev/null
+++ b/src/lib/util/strutil.cc
@@ -0,0 +1,135 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <numeric>
+
+#include <string.h>
+#include <util/strutil.h>
+
+using namespace std;
+
+namespace isc {
+namespace strutil {
+
+// Normalize slashes
+
+void
+normalizeSlash(std::string& name) {
+ if (!name.empty()) {
+ size_t pos = 0;
+ while ((pos = name.find('\\', pos)) != std::string::npos) {
+ name[pos] = '/';
+ }
+ }
+}
+
+// Trim String
+
+string
+trim(const string& instring) {
+ static const char* blanks = " \t\n";
+
+ string retstring = "";
+ if (!instring.empty()) {
+
+ // Search for first non-blank character in the string
+ size_t first = instring.find_first_not_of(blanks);
+ if (first != string::npos) {
+
+ // String not all blanks, so look for last character
+ size_t last = instring.find_last_not_of(blanks);
+
+ // Extract the trimmed substring
+ retstring = instring.substr(first, (last - first + 1));
+ }
+ }
+
+ return (retstring);
+}
+
+// Tokenise string. As noted in the header, this is locally written to avoid
+// another dependency on a Boost library.
+
+vector<string>
+tokens(const std::string& text, const std::string& delim) {
+ vector<string> result;
+
+ // Search for the first non-delimiter character
+ size_t start = text.find_first_not_of(delim);
+ while (start != string::npos) {
+
+ // Non-delimiter found, look for next delimiter
+ size_t end = text.find_first_of(delim, start);
+ if (end != string::npos) {
+
+ // Delimiter found, so extract string & search for start of next
+ // non-delimiter segment.
+ result.push_back(text.substr(start, (end - start)));
+ start = text.find_first_not_of(delim, end);
+
+ } else {
+
+ // End of string found, extract rest of string and flag to exit
+ result.push_back(text.substr(start));
+ start = string::npos;
+ }
+ }
+
+ return (result);
+}
+
+// Local function to pass to accumulate() for summing up string lengths.
+
+namespace {
+
+size_t
+lengthSum(string::size_type curlen, const string& cur_string) {
+ return (curlen + cur_string.size());
+}
+
+}
+
+// Provide printf-style formatting.
+
+std::string
+format(const std::string& format, const std::vector<std::string>& args) {
+
+ static const string flag = "%s";
+
+ // Initialize return string. To speed things up, we'll reserve an
+ // appropriate amount of space - current string size, plus length of all
+ // the argument strings, less two characters for each argument (the %s in
+ // the format string is being replaced).
+ string result;
+ size_t length = accumulate(args.begin(), args.end(), format.size(),
+ lengthSum) - (args.size() * flag.size());
+ result.reserve(length);
+
+ // Iterate through replacing all tokens
+ result = format;
+ size_t tokenpos = 0; // Position of last token replaced
+ int i = 0; // Index into argument array
+
+ while ((i < args.size()) && (tokenpos != string::npos)) {
+ tokenpos = result.find(flag, tokenpos);
+ if (tokenpos != string::npos) {
+ result.replace(tokenpos, flag.size(), args[i++]);
+ }
+ }
+
+ return (result);
+}
+
+} // namespace strutil
+} // namespace isc
diff --git a/src/lib/util/strutil.h b/src/lib/util/strutil.h
new file mode 100644
index 0000000..087410f
--- /dev/null
+++ b/src/lib/util/strutil.h
@@ -0,0 +1,145 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __STRUTIL_H
+#define __STRUTIL_H
+
+#include <algorithm>
+#include <cctype>
+#include <string>
+#include <vector>
+
+namespace isc {
+namespace strutil {
+
+/// \brief A Set of C++ Utilities for Manipulating Strings
+
+/// \brief Normalize Backslash
+///
+/// Only relevant to Windows, this replaces all "\" in a string with "/" and
+/// returns the result. On other systems it is a no-op. Note that Windows does
+/// recognise file names with the "\" replaced by "/" (at least in system calls,
+/// if not the command line).
+///
+/// \param name Name to be substituted
+void normalizeSlash(std::string& name);
+
+
+/// \brief Trim Leading and Trailing Spaces
+///
+/// Returns a copy of the input string but with any leading or trailing spaces
+/// or tabs removed.
+///
+/// \param instring Input string to modify
+///
+/// \return String with leading and trailing spaces removed
+std::string trim(const std::string& instring);
+
+
+/// \brief Split String into Tokens
+///
+/// Splits a string into tokens (the tokens being delimited by one or more of
+/// the delimiter characters) and returns the tokens in a vector array. Note
+/// that adjacent delimiters are considered to be a single delimiter.
+///
+/// Special cases are:
+/// -# The empty string is considered to be zero tokens.
+/// -# A string comprising nothing but delimiters is considered to be zero
+/// tokens.
+///
+/// The reasoning behind this is that the string can be thought of as having
+/// invisible leading and trailing delimiter characters. Therefore both cases
+/// reduce to a set of contiguous delimiters, which are considered a single
+/// delimiter (so getting rid of the string).
+///
+/// We could use Boost for this, but this (simple) function eliminates one
+/// dependency in the code.
+///
+/// \param text String to be split. Passed by value as the internal copy is
+/// altered during the processing.
+/// \param delim Delimiter characters
+///
+/// \return Vector of tokens.
+std::vector<std::string> tokens(const std::string& text,
+ const std::string& delim = std::string(" \t\n"));
+
+
+/// \brief Uppercase Character
+///
+/// Used in uppercase() to pass as an argument to std::transform(). The
+/// function std::toupper() can't be used as it takes an "int" as its argument;
+/// this confuses the template expansion mechanism because dereferencing a
+/// string::iterator returns a char.
+///
+/// \param chr Character to be upper-cased.
+///
+/// \return Uppercase version of the argument
+inline char toUpper(char chr) {
+ return (static_cast<char>(std::toupper(static_cast<int>(chr))));
+}
+
+
+/// \brief Uppercase String
+///
+/// A convenience function to uppercase a string.
+///
+/// \param text String to be upper-cased.
+inline void uppercase(std::string& text) {
+ std::transform(text.begin(), text.end(), text.begin(),
+ isc::strutil::toUpper);
+}
+
+/// \brief Lowercase Character
+///
+/// Used in lowercase() to pass as an argument to std::transform(). The
+/// function std::tolower() can't be used as it takes an "int" as its argument;
+/// this confuses the template expansion mechanism because dereferencing a
+/// string::iterator returns a char.
+///
+/// \param chr Character to be lower-cased.
+///
+/// \return Lowercase version of the argument
+inline char toLower(char chr) {
+ return (static_cast<char>(std::tolower(static_cast<int>(chr))));
+}
+
+/// \brief Lowercase String
+///
+/// A convenience function to lowercase a string
+///
+/// \param text String to be lower-cased.
+inline void lowercase(std::string& text) {
+ std::transform(text.begin(), text.end(), text.begin(),
+ isc::strutil::toLower);
+}
+
+
+/// \brief Apply Formatting
+///
+/// Given a printf-style format string containing only "%s" place holders
+/// (others are ignored) and a vector of strings, this produces a single string
+/// with the placeholders replaced.
+///
+/// \param format Format string
+/// \param args Vector of argument strings
+///
+/// \return Resultant string
+std::string format(const std::string& format,
+ const std::vector<std::string>& args);
+
+
+} // namespace strutil
+} // namespace isc
+
+#endif // __STRUTIL_H
diff --git a/src/lib/util/tests/Makefile.am b/src/lib/util/tests/Makefile.am
new file mode 100644
index 0000000..7b97202
--- /dev/null
+++ b/src/lib/util/tests/Makefile.am
@@ -0,0 +1,41 @@
+SUBDIRS = .
+
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/util -I$(top_builddir)/src/lib/util
+AM_CPPFLAGS += -I$(top_srcdir)/src/lib/exceptions -I$(top_builddir)/src/lib/exceptions
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+TESTS =
+if HAVE_GTEST
+TESTS += run_unittests
+run_unittests_SOURCES =
+run_unittests_SOURCES += filename_unittest.cc
+run_unittests_SOURCES += strutil_unittest.cc
+run_unittests_SOURCES += run_unittests.cc
+run_unittests_SOURCES += base32hex_unittest.cc
+run_unittests_SOURCES += base64_unittest.cc
+run_unittests_SOURCES += hex_unittest.cc
+run_unittests_SOURCES += sha1_unittest.cc
+run_unittests_SOURCES += buffer_unittest.cc
+run_unittests_SOURCES += time_utilities_unittest.cc
+run_unittests_SOURCES += random_number_generator_unittest.cc
+run_unittests_SOURCES += lru_list_unittest.cc
+run_unittests_SOURCES += io_utilities_unittest.cc
+run_unittests_SOURCES += qid_gen_unittest.cc
+
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+
+run_unittests_LDADD = $(GTEST_LDADD)
+run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+endif
+
+noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/util/tests/base32hex_unittest.cc b/src/lib/util/tests/base32hex_unittest.cc
new file mode 100644
index 0000000..80ab82c
--- /dev/null
+++ b/src/lib/util/tests/base32hex_unittest.cc
@@ -0,0 +1,159 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <stdint.h>
+
+#include <cctype>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <exceptions/exceptions.h>
+
+#include <util/compat/base32hex.h>
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::util::compat;
+
+namespace {
+
+typedef pair<string, string> StringPair;
+
+class Base32HexTest : public ::testing::Test {
+protected:
+ Base32HexTest() : encoding_chars("0123456789ABCDEFGHIJKLMNOPQRSTUV=") {
+ // test vectors from RFC4648
+ test_sequence.push_back(StringPair("", ""));
+ test_sequence.push_back(StringPair("f", "CO======"));
+ test_sequence.push_back(StringPair("fo", "CPNG===="));
+ test_sequence.push_back(StringPair("foo", "CPNMU==="));
+ test_sequence.push_back(StringPair("foob", "CPNMUOG="));
+ test_sequence.push_back(StringPair("fooba", "CPNMUOJ1"));
+ test_sequence.push_back(StringPair("foobar", "CPNMUOJ1E8======"));
+
+ // the same data, encoded using lower case chars (testable only
+ // for the decode side)
+ test_sequence_lower.push_back(StringPair("f", "co======"));
+ test_sequence_lower.push_back(StringPair("fo", "cpng===="));
+ test_sequence_lower.push_back(StringPair("foo", "cpnmu==="));
+ test_sequence_lower.push_back(StringPair("foob", "cpnmuog="));
+ test_sequence_lower.push_back(StringPair("fooba", "cpnmuoj1"));
+ test_sequence_lower.push_back(StringPair("foobar",
+ "cpnmuoj1e8======"));
+ }
+ vector<StringPair> test_sequence;
+ vector<StringPair> test_sequence_lower;
+ vector<uint8_t> decoded_data;
+ const string encoding_chars;
+};
+
+void
+decodeCheck(const string& input_string, vector<uint8_t>& output,
+ const string& expected)
+{
+ decodeBase32Hex(input_string, output);
+ EXPECT_EQ(expected, string(&output[0], &output[0] + output.size()));
+}
+
+TEST_F(Base32HexTest, decode) {
+ for (vector<StringPair>::const_iterator it = test_sequence.begin();
+ it != test_sequence.end();
+ ++it) {
+ decodeCheck((*it).second, decoded_data, (*it).first);
+ }
+
+ // whitespace should be allowed
+ decodeCheck("CP NM\tUOG=", decoded_data, "foob");
+ decodeCheck("CPNMU===\n", decoded_data, "foo");
+
+ // invalid number of padding characters
+ EXPECT_THROW(decodeBase32Hex("CPNMU0==", decoded_data), BadValue);
+ EXPECT_THROW(decodeBase32Hex("CO0=====", decoded_data), BadValue);
+ EXPECT_THROW(decodeBase32Hex("CO=======", decoded_data), // too many ='s
+ BadValue);
+
+ // intermediate padding isn't allowed
+ EXPECT_THROW(decodeBase32Hex("CPNMUOG=CPNMUOG=", decoded_data), BadValue);
+
+ // Non canonical form isn't allowed.
+ // P => 25(11001), so the padding byte would be 01000000
+ EXPECT_THROW(decodeBase32Hex("0P======", decoded_data), BadValue);
+}
+
+TEST_F(Base32HexTest, decodeLower) {
+ for (vector<StringPair>::const_iterator it = test_sequence_lower.begin();
+ it != test_sequence_lower.end();
+ ++it) {
+ decodeCheck((*it).second, decoded_data, (*it).first);
+ }
+}
+
+TEST_F(Base32HexTest, encode) {
+ for (vector<StringPair>::const_iterator it = test_sequence.begin();
+ it != test_sequence.end();
+ ++it) {
+ decoded_data.assign((*it).first.begin(), (*it).first.end());
+ EXPECT_EQ((*it).second, encodeBase32Hex(decoded_data));
+ }
+}
+
+// For Base32Hex we use handmade mappings, so it's prudent to test the
+// entire mapping table explicitly.
+TEST_F(Base32HexTest, decodeMap) {
+ string input(8, '0'); // input placeholder
+
+ // We're going to populate an input string with only the last character
+ // not equal to the zero character ('0') for each valid base32hex encoding
+ // character. Decoding that input should result in a data stream with
+ // the last byte equal to the numeric value represented by the that
+ // character. For example, we'll generate and confirm the following:
+ // "00000000" => should be 0 (as a 40bit integer)
+ // "00000001" => should be 1 (as a 40bit integer)
+ // ...
+ // "0000000V" => should be 31 (as a 40bit integer)
+ // We also check the use of an invalid character for the last character
+ // surely fails. '=' should be accepted as a valid padding in this
+ // context; space characters shouldn't be allowed in this context.
+
+ for (int i = 0; i < 256; ++i) {
+ input[7] = i;
+
+ const char ch = toupper(i);
+ const size_t pos = encoding_chars.find(ch);
+ if (pos == string::npos) {
+ EXPECT_THROW(decodeBase32Hex(input, decoded_data), BadValue);
+ } else {
+ decodeBase32Hex(input, decoded_data);
+ if (ch == '=') {
+ EXPECT_EQ(4, decoded_data.size());
+ } else {
+ EXPECT_EQ(5, decoded_data.size());
+ EXPECT_EQ(pos, decoded_data[4]);
+ }
+ }
+ }
+}
+
+TEST_F(Base32HexTest, encodeMap) {
+ for (int i = 0; i < 32; ++i) {
+ decoded_data.assign(4, 0);
+ decoded_data.push_back(i);
+ EXPECT_EQ(encoding_chars[i], encodeBase32Hex(decoded_data)[7]);
+ }
+}
+
+}
diff --git a/src/lib/util/tests/base64_unittest.cc b/src/lib/util/tests/base64_unittest.cc
new file mode 100644
index 0000000..4aa095a
--- /dev/null
+++ b/src/lib/util/tests/base64_unittest.cc
@@ -0,0 +1,93 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <exceptions/exceptions.h>
+
+#include <util/compat/base64.h>
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::util::compat;
+
+namespace {
+
+typedef pair<string, string> StringPair;
+
+class Base64Test : public ::testing::Test {
+protected:
+ Base64Test()
+ {
+ // test vectors from RFC4648
+ test_sequence.push_back(StringPair("", ""));
+ test_sequence.push_back(StringPair("f", "Zg=="));
+ test_sequence.push_back(StringPair("fo", "Zm8="));
+ test_sequence.push_back(StringPair("foo", "Zm9v"));
+ test_sequence.push_back(StringPair("foob", "Zm9vYg=="));
+ test_sequence.push_back(StringPair("fooba", "Zm9vYmE="));
+ test_sequence.push_back(StringPair("foobar", "Zm9vYmFy"));
+ }
+ vector<StringPair> test_sequence;
+ vector<uint8_t> decoded_data;
+};
+
+void
+decodeCheck(const string& input_string, vector<uint8_t>& output,
+ const string& expected)
+{
+ decodeBase64(input_string, output);
+ EXPECT_EQ(expected, string(&output[0], &output[0] + output.size()));
+}
+
+TEST_F(Base64Test, decode) {
+ for (vector<StringPair>::const_iterator it = test_sequence.begin();
+ it != test_sequence.end();
+ ++it) {
+ decodeCheck((*it).second, decoded_data, (*it).first);
+ }
+
+ // whitespace should be allowed
+ decodeCheck("Zm 9v\tYmF\ny", decoded_data, "foobar");
+ decodeCheck("Zm9vYg==", decoded_data, "foob");
+ decodeCheck("Zm9vYmE=\n", decoded_data, "fooba");
+
+ // only up to 2 padding characters are allowed
+ EXPECT_THROW(decodeBase64("A===", decoded_data), BadValue);
+ EXPECT_THROW(decodeBase64("A= ==", decoded_data), BadValue);
+
+ // intermediate padding isn't allowed
+ EXPECT_THROW(decodeBase64("YmE=YmE=", decoded_data), BadValue);
+
+ // Non canonical form isn't allowed.
+ // Z => 25(011001), m => 38(100110), 9 => 60(111101), so the padding
+ // byte would be 0100 0000.
+ EXPECT_THROW(decodeBase64("Zm9=", decoded_data), BadValue);
+ // Same for the 1st padding byte. This would make it 01100000.
+ EXPECT_THROW(decodeBase64("Zm==", decoded_data), BadValue);
+}
+
+TEST_F(Base64Test, encode) {
+ for (vector<StringPair>::const_iterator it = test_sequence.begin();
+ it != test_sequence.end();
+ ++it) {
+ decoded_data.assign((*it).first.begin(), (*it).first.end());
+ EXPECT_EQ((*it).second, encodeBase64(decoded_data));
+ }
+}
+}
diff --git a/src/lib/util/tests/buffer_unittest.cc b/src/lib/util/tests/buffer_unittest.cc
new file mode 100644
index 0000000..ba62e5c
--- /dev/null
+++ b/src/lib/util/tests/buffer_unittest.cc
@@ -0,0 +1,193 @@
+// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <exceptions/exceptions.h>
+
+#include <util/io/buffer.h>
+
+#include <gtest/gtest.h>
+
+using namespace isc;
+
+namespace {
+
+using isc::util::io::InputBuffer;
+using isc::util::io::OutputBuffer;
+
+class BufferTest : public ::testing::Test {
+protected:
+ BufferTest() : ibuffer(testdata, sizeof(testdata)), obuffer(0),
+ expected_size(0)
+ {
+ data16 = (2 << 8) | 3;
+ data32 = (4 << 24) | (5 << 16) | (6 << 8) | 7;
+ memset(vdata, 0, sizeof(testdata));
+ }
+
+ InputBuffer ibuffer;
+ OutputBuffer obuffer;
+ static const uint8_t testdata[5];
+ uint8_t vdata[sizeof(testdata)];
+ size_t expected_size;
+ uint16_t data16;
+ uint32_t data32;
+};
+
+const uint8_t BufferTest::testdata[5] = {1, 2, 3, 4, 5};
+
+TEST_F(BufferTest, inputBufferRead) {
+ EXPECT_EQ(5, ibuffer.getLength());
+ EXPECT_EQ(1, ibuffer.readUint8());
+ EXPECT_EQ(1, ibuffer.getPosition());
+ data16 = ibuffer.readUint16();
+ EXPECT_EQ((2 << 8) | 3, data16);
+ EXPECT_EQ(3, ibuffer.getPosition());
+ ibuffer.setPosition(1);
+ EXPECT_EQ(1, ibuffer.getPosition());
+ data32 = ibuffer.readUint32();
+ EXPECT_EQ((2 << 24) | (3 << 16) | (4 << 8) | 5, data32);
+ ibuffer.setPosition(0);
+ memset(vdata, 0, sizeof(vdata));
+ ibuffer.readData(vdata, sizeof(vdata));
+ EXPECT_EQ(0, memcmp(vdata, testdata, sizeof(testdata)));
+}
+
+TEST_F(BufferTest, inputBufferException) {
+ EXPECT_THROW(ibuffer.setPosition(6), isc::util::io::InvalidBufferPosition);
+
+ ibuffer.setPosition(sizeof(testdata));
+ EXPECT_THROW(ibuffer.readUint8(), isc::util::io::InvalidBufferPosition);
+
+ ibuffer.setPosition(sizeof(testdata) - 1);
+ EXPECT_THROW(ibuffer.readUint16(), isc::util::io::InvalidBufferPosition);
+
+ ibuffer.setPosition(sizeof(testdata) - 3);
+ EXPECT_THROW(ibuffer.readUint32(), isc::util::io::InvalidBufferPosition);
+
+ ibuffer.setPosition(sizeof(testdata) - 4);
+ EXPECT_THROW(ibuffer.readData(vdata, sizeof(vdata)),
+ isc::util::io::InvalidBufferPosition);
+}
+
+TEST_F(BufferTest, outputBufferExtend) {
+ EXPECT_EQ(0, obuffer.getCapacity());
+ EXPECT_EQ(0, obuffer.getLength());
+ obuffer.writeUint8(10);
+ EXPECT_LT(0, obuffer.getCapacity());
+ EXPECT_EQ(1, obuffer.getLength());
+}
+
+TEST_F(BufferTest, outputBufferWrite) {
+ const uint8_t* cp;
+
+ obuffer.writeUint8(1);
+ expected_size += sizeof(uint8_t);
+ EXPECT_EQ(expected_size, obuffer.getLength());
+ cp = static_cast<const uint8_t*>(obuffer.getData());
+ EXPECT_EQ(1, *cp);
+
+ obuffer.writeUint16(data16);
+ expected_size += sizeof(data16);
+ cp = static_cast<const uint8_t*>(obuffer.getData());
+ EXPECT_EQ(expected_size, obuffer.getLength());
+ EXPECT_EQ(2, *(cp + 1));
+ EXPECT_EQ(3, *(cp + 2));
+
+ obuffer.writeUint32(data32);
+ expected_size += sizeof(data32);
+ cp = static_cast<const uint8_t*>(obuffer.getData());
+ EXPECT_EQ(expected_size, obuffer.getLength());
+ EXPECT_EQ(4, *(cp + 3));
+ EXPECT_EQ(5, *(cp + 4));
+ EXPECT_EQ(6, *(cp + 5));
+ EXPECT_EQ(7, *(cp + 6));
+
+ obuffer.writeData(testdata, sizeof(testdata));
+ expected_size += sizeof(testdata);
+ EXPECT_EQ(expected_size, obuffer.getLength());
+ cp = static_cast<const uint8_t*>(obuffer.getData());
+ EXPECT_EQ(0, memcmp(cp + 7, testdata, sizeof(testdata)));
+}
+
+TEST_F(BufferTest, outputBufferWriteat) {
+ obuffer.writeUint32(data32);
+ expected_size += sizeof(data32);
+
+ // overwrite 2nd byte
+ obuffer.writeUint8At(4, 1);
+ EXPECT_EQ(expected_size, obuffer.getLength()); // length shouldn't change
+ const uint8_t* cp = static_cast<const uint8_t*>(obuffer.getData());
+ EXPECT_EQ(4, *(cp + 1));
+
+ // overwrite 2nd and 3rd bytes
+ obuffer.writeUint16At(data16, 1);
+ EXPECT_EQ(expected_size, obuffer.getLength()); // length shouldn't change
+ cp = static_cast<const uint8_t*>(obuffer.getData());
+ EXPECT_EQ(2, *(cp + 1));
+ EXPECT_EQ(3, *(cp + 2));
+
+ // overwrite 3rd and 4th bytes
+ obuffer.writeUint16At(data16, 2);
+ EXPECT_EQ(expected_size, obuffer.getLength());
+ cp = static_cast<const uint8_t*>(obuffer.getData());
+ EXPECT_EQ(2, *(cp + 2));
+ EXPECT_EQ(3, *(cp + 3));
+
+ EXPECT_THROW(obuffer.writeUint8At(data16, 5),
+ isc::util::io::InvalidBufferPosition);
+ EXPECT_THROW(obuffer.writeUint8At(data16, 4),
+ isc::util::io::InvalidBufferPosition);
+ EXPECT_THROW(obuffer.writeUint16At(data16, 3),
+ isc::util::io::InvalidBufferPosition);
+ EXPECT_THROW(obuffer.writeUint16At(data16, 4),
+ isc::util::io::InvalidBufferPosition);
+ EXPECT_THROW(obuffer.writeUint16At(data16, 5),
+ isc::util::io::InvalidBufferPosition);
+}
+
+TEST_F(BufferTest, outputBufferSkip) {
+ obuffer.skip(4);
+ EXPECT_EQ(4, obuffer.getLength());
+
+ obuffer.skip(2);
+ EXPECT_EQ(6, obuffer.getLength());
+}
+
+TEST_F(BufferTest, outputBufferTrim) {
+ obuffer.writeData(testdata, sizeof(testdata));
+ EXPECT_EQ(5, obuffer.getLength());
+
+ obuffer.trim(1);
+ EXPECT_EQ(4, obuffer.getLength());
+
+ obuffer.trim(2);
+ EXPECT_EQ(2, obuffer.getLength());
+
+ EXPECT_THROW(obuffer.trim(3), OutOfRange);
+}
+
+TEST_F(BufferTest, outputBufferReadat) {
+ obuffer.writeData(testdata, sizeof(testdata));
+ for (int i = 0; i < sizeof(testdata); i ++) {
+ EXPECT_EQ(testdata[i], obuffer[i]);
+ }
+ EXPECT_THROW(obuffer[sizeof(testdata)], isc::util::io::InvalidBufferPosition);
+}
+
+TEST_F(BufferTest, outputBufferClear) {
+ obuffer.writeData(testdata, sizeof(testdata));
+ obuffer.clear();
+ EXPECT_EQ(0, obuffer.getLength());
+}
+}
diff --git a/src/lib/util/tests/filename_unittest.cc b/src/lib/util/tests/filename_unittest.cc
new file mode 100644
index 0000000..33e6456
--- /dev/null
+++ b/src/lib/util/tests/filename_unittest.cc
@@ -0,0 +1,179 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <util/filename.h>
+
+using namespace isc;
+using namespace isc::util;
+using namespace std;
+
+class FilenameTest : public ::testing::Test {
+protected:
+ FilenameTest()
+ {
+ }
+};
+
+
+// Check that the name can be changed
+
+TEST_F(FilenameTest, SetName) {
+ Filename fname("/a/b/c.d");
+ EXPECT_EQ("/a/b/c.d", fname.fullName());
+
+ fname.setName("test.txt");
+ EXPECT_EQ("test.txt", fname.fullName());
+}
+
+
+// Check that the components are split correctly. This is a check of the
+// private member split() method.
+
+TEST_F(FilenameTest, Components) {
+
+ // Complete name
+ Filename fname("/alpha/beta/gamma.delta");
+ EXPECT_EQ("/alpha/beta/", fname.directory());
+ EXPECT_EQ("gamma", fname.name());
+ EXPECT_EQ(".delta", fname.extension());
+
+ // Directory only
+ fname.setName("/gamma/delta/");
+ EXPECT_EQ("/gamma/delta/", fname.directory());
+ EXPECT_EQ("", fname.name());
+ EXPECT_EQ("", fname.extension());
+
+ // Filename only
+ fname.setName("epsilon");
+ EXPECT_EQ("", fname.directory());
+ EXPECT_EQ("epsilon", fname.name());
+ EXPECT_EQ("", fname.extension());
+
+ // Extension only
+ fname.setName(".zeta");
+ EXPECT_EQ("", fname.directory());
+ EXPECT_EQ("", fname.name());
+ EXPECT_EQ(".zeta", fname.extension());
+
+ // Missing directory
+ fname.setName("eta.theta");
+ EXPECT_EQ("", fname.directory());
+ EXPECT_EQ("eta", fname.name());
+ EXPECT_EQ(".theta", fname.extension());
+
+ // Missing filename
+ fname.setName("/iota/.kappa");
+ EXPECT_EQ("/iota/", fname.directory());
+ EXPECT_EQ("", fname.name());
+ EXPECT_EQ(".kappa", fname.extension());
+
+ // Missing extension
+ fname.setName("lambda/mu/nu");
+ EXPECT_EQ("lambda/mu/", fname.directory());
+ EXPECT_EQ("nu", fname.name());
+ EXPECT_EQ("", fname.extension());
+
+ // Check that the decomposition can occur in the presence of leading and
+ // trailing spaces
+ fname.setName(" lambda/mu/nu\t ");
+ EXPECT_EQ("lambda/mu/", fname.directory());
+ EXPECT_EQ("nu", fname.name());
+ EXPECT_EQ("", fname.extension());
+
+ // Empty string
+ fname.setName("");
+ EXPECT_EQ("", fname.directory());
+ EXPECT_EQ("", fname.name());
+ EXPECT_EQ("", fname.extension());
+
+ // ... and just spaces
+ fname.setName(" ");
+ EXPECT_EQ("", fname.directory());
+ EXPECT_EQ("", fname.name());
+ EXPECT_EQ("", fname.extension());
+
+ // Check corner cases - where separators are present, but strings are
+ // absent.
+ fname.setName("/");
+ EXPECT_EQ("/", fname.directory());
+ EXPECT_EQ("", fname.name());
+ EXPECT_EQ("", fname.extension());
+
+ fname.setName(".");
+ EXPECT_EQ("", fname.directory());
+ EXPECT_EQ("", fname.name());
+ EXPECT_EQ(".", fname.extension());
+
+ fname.setName("/.");
+ EXPECT_EQ("/", fname.directory());
+ EXPECT_EQ("", fname.name());
+ EXPECT_EQ(".", fname.extension());
+
+ // Note that the space is a valid filename here; only leading and trailing
+ // spaces should be trimmed.
+ fname.setName("/ .");
+ EXPECT_EQ("/", fname.directory());
+ EXPECT_EQ(" ", fname.name());
+ EXPECT_EQ(".", fname.extension());
+
+ fname.setName(" / . ");
+ EXPECT_EQ("/", fname.directory());
+ EXPECT_EQ(" ", fname.name());
+ EXPECT_EQ(".", fname.extension());
+}
+
+// Check that the expansion with a default works.
+
+TEST_F(FilenameTest, ExpandWithDefault) {
+ Filename fname("a.b");
+
+ // These tests also check that the trimming of the default component is
+ // done properly.
+ EXPECT_EQ("/c/d/a.b", fname.expandWithDefault(" /c/d/ "));
+ EXPECT_EQ("/c/d/a.b", fname.expandWithDefault("/c/d/e.f"));
+ EXPECT_EQ("a.b", fname.expandWithDefault("e.f"));
+
+ fname.setName("/a/b/c");
+ EXPECT_EQ("/a/b/c.d", fname.expandWithDefault(".d"));
+ EXPECT_EQ("/a/b/c.d", fname.expandWithDefault("x.d"));
+ EXPECT_EQ("/a/b/c.d", fname.expandWithDefault("/s/t/u.d"));
+ EXPECT_EQ("/a/b/c", fname.expandWithDefault("/s/t/u"));
+
+ fname.setName(".h");
+ EXPECT_EQ("/a/b/c.h", fname.expandWithDefault("/a/b/c.msg"));
+}
+
+// Check that we can use this as a default in expanding a filename
+
+TEST_F(FilenameTest, UseAsDefault) {
+
+ Filename fname("a.b");
+
+ // These tests also check that the trimming of the default component is
+ // done properly.
+ EXPECT_EQ("/c/d/a.b", fname.useAsDefault(" /c/d/ "));
+ EXPECT_EQ("/c/d/e.f", fname.useAsDefault("/c/d/e.f"));
+ EXPECT_EQ("e.f", fname.useAsDefault("e.f"));
+
+ fname.setName("/a/b/c");
+ EXPECT_EQ("/a/b/c.d", fname.useAsDefault(".d"));
+ EXPECT_EQ("/a/b/x.d", fname.useAsDefault("x.d"));
+ EXPECT_EQ("/s/t/u.d", fname.useAsDefault("/s/t/u.d"));
+ EXPECT_EQ("/s/t/u", fname.useAsDefault("/s/t/u"));
+ EXPECT_EQ("/a/b/c", fname.useAsDefault(""));
+}
diff --git a/src/lib/util/tests/hex_unittest.cc b/src/lib/util/tests/hex_unittest.cc
new file mode 100644
index 0000000..82b5d5f
--- /dev/null
+++ b/src/lib/util/tests/hex_unittest.cc
@@ -0,0 +1,120 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <stdint.h>
+
+#include <vector>
+#include <string>
+
+#include <exceptions/exceptions.h>
+
+#include <util/compat/hex.h>
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::util::compat;
+
+namespace {
+const string hex_txt("DEADBEEFDECADE");
+const string hex_txt_space("DEAD BEEF DECADE");
+const string hex_txt_lower("deadbeefdecade");
+
+class HexTest : public ::testing::Test {
+protected:
+ HexTest() : encoding_chars("0123456789ABCDEF") {}
+ vector<uint8_t> decoded_data;
+ const string encoding_chars;
+};
+
+TEST_F(HexTest, encodeHex) {
+ std::vector<uint8_t> data;
+
+ data.push_back(0xde);
+ data.push_back(0xad);
+ data.push_back(0xbe);
+ data.push_back(0xef);
+ data.push_back(0xde);
+ data.push_back(0xca);
+ data.push_back(0xde);
+ EXPECT_EQ(hex_txt, encodeHex(data));
+}
+
+void
+compareData(const std::vector<uint8_t>& data) {
+ EXPECT_EQ(0xde, data[0]);
+ EXPECT_EQ(0xad, data[1]);
+ EXPECT_EQ(0xbe, data[2]);
+ EXPECT_EQ(0xef, data[3]);
+ EXPECT_EQ(0xde, data[4]);
+ EXPECT_EQ(0xca, data[5]);
+ EXPECT_EQ(0xde, data[6]);
+}
+
+TEST_F(HexTest, decodeHex) {
+ std::vector<uint8_t> result;
+
+ decodeHex(hex_txt, result);
+ compareData(result);
+
+ // lower case hex digits should be accepted
+ result.clear();
+ decodeHex(hex_txt_lower, result);
+ compareData(result);
+
+ // white space should be ignored
+ result.clear();
+ decodeHex(hex_txt_space, result);
+ compareData(result);
+
+ // Bogus input: should fail
+ result.clear();
+ EXPECT_THROW(decodeHex("1x", result), BadValue);
+
+ // Bogus input: encoded string must have an even number of characters.
+ result.clear();
+ EXPECT_THROW(decodeHex("dea", result), BadValue);
+}
+
+// For Hex encode/decode we use handmade mappings, so it's prudent to test the
+// entire mapping table explicitly.
+TEST_F(HexTest, decodeMap) {
+ string input("00"); // input placeholder
+
+ // See Base32HexTest.decodeMap for details of the following tests.
+ for (int i = 0; i < 256; ++i) {
+ input[1] = i;
+
+ const char ch = toupper(i);
+ const size_t pos = encoding_chars.find(ch);
+ if (pos == string::npos) {
+ EXPECT_THROW(decodeHex(input, decoded_data), BadValue);
+ } else {
+ decodeHex(input, decoded_data);
+ EXPECT_EQ(1, decoded_data.size());
+ EXPECT_EQ(pos, decoded_data[0]);
+ }
+ }
+}
+
+TEST_F(HexTest, encodeMap) {
+ for (int i = 0; i < 16; ++i) {
+ decoded_data.clear();
+ decoded_data.push_back(i);
+ EXPECT_EQ(encoding_chars[i], encodeHex(decoded_data)[1]);
+ }
+}
+
+}
diff --git a/src/lib/util/tests/io_utilities_unittest.cc b/src/lib/util/tests/io_utilities_unittest.cc
new file mode 100644
index 0000000..ffe1274
--- /dev/null
+++ b/src/lib/util/tests/io_utilities_unittest.cc
@@ -0,0 +1,73 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// \brief Test of asiolink utilties
+///
+/// Tests the fuctionality of the asiolink utilities code by comparing them
+/// with the equivalent methods in isc::dns::[Input/Output]Buffer.
+
+#include <cstddef>
+
+#include <gtest/gtest.h>
+
+#include <util/io/buffer.h>
+#include <util/io/io_utilities.h>
+
+using namespace isc::util::io;
+
+TEST(asioutil, readUint16) {
+
+ // Reference buffer
+ uint8_t data[2];
+ InputBuffer buffer(data, sizeof(data));
+
+ // Avoid possible compiler warnings by only setting uint8_t variables to
+ // uint8_t values.
+ uint8_t i8 = 0;
+ uint8_t j8 = 0;
+ for (int i = 0; i < (2 << 8); ++i, ++i8) {
+ for (int j = 0; j < (2 << 8); ++j, ++j8) {
+ data[0] = i8;
+ data[1] = j8;
+ buffer.setPosition(0);
+ EXPECT_EQ(buffer.readUint16(), readUint16(data));
+ }
+ }
+}
+
+
+TEST(asioutil, writeUint16) {
+
+ // Reference buffer
+ OutputBuffer buffer(2);
+ uint8_t test[2];
+
+ // Avoid possible compiler warnings by only setting uint16_t variables to
+ // uint16_t values.
+ uint16_t i16 = 0;
+ for (uint32_t i = 0; i < (2 << 16); ++i, ++i16) {
+
+ // Write the reference data
+ buffer.clear();
+ buffer.writeUint16(i16);
+
+ // ... and the test data
+ writeUint16(i16, test);
+
+ // ... and compare
+ const uint8_t* ref = static_cast<const uint8_t*>(buffer.getData());
+ EXPECT_EQ(ref[0], test[0]);
+ EXPECT_EQ(ref[1], test[1]);
+ }
+}
diff --git a/src/lib/util/tests/lru_list_unittest.cc b/src/lib/util/tests/lru_list_unittest.cc
new file mode 100644
index 0000000..25665bf
--- /dev/null
+++ b/src/lib/util/tests/lru_list_unittest.cc
@@ -0,0 +1,427 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <iostream>
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include <exceptions/exceptions.h>
+#include <gtest/gtest.h>
+#include <boost/lexical_cast.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+#include <util/lru_list.h>
+
+using namespace std;
+
+namespace isc {
+namespace util {
+
+/// \brief Invalid Iterator
+///
+/// Thrown if an attempt was made to access the iterator - the pointer into
+/// the LRU list where this element is located - when it is marked as invalid.
+class InvalidLruIterator : public isc::Exception {
+public:
+ InvalidLruIterator(const char* file, size_t line, const char* what) :
+ Exception(file, line, what)
+ {}
+};
+
+template <typename T>
+class TestEntryT : public boost::enable_shared_from_this <T> {
+public:
+
+ /// \brief Constructor
+ ///
+ /// \param name Name that will be used for the object. This will form
+ /// part of the key.
+ /// \param class_code Class associated with the object.
+ TestEntryT() : valid_(false)
+ {}
+
+ /// \brief Virtual Destructor
+ virtual ~TestEntryT()
+ {}
+
+ /// \brief Sets the iterator of the object
+ ///
+ /// Sets the iterator of an object and, as a side effect, marks it as valid.
+ ///
+ /// \param iterator Iterator of this element in the list
+ virtual void setLruIterator(typename LruList<T>::iterator iterator) {
+ iterator_ = iterator;
+ valid_ = true;
+ }
+
+ /// \brief Return Iterator
+ ///
+ /// \return iterator Iterator of this element in the list.
+ ///
+ /// \exception InvalidLruIterator Thrown if the iterator is not valid.
+ virtual typename LruList<T>::iterator getLruIterator() const {
+ /*if (! valid_) {
+ isc_throw(InvalidLruIterator,
+ "pointer of element into LRU list was not valid");
+ }*/
+ return iterator_;
+ }
+
+ /// \brief Iterator Valid
+ ///
+ /// \return true if the stored iterator is valid.
+ virtual bool iteratorValid() const {
+ return valid_;
+ }
+
+ /// \brief Invalidate Iterator
+ ///
+ /// Marks the iterator as invalid; it can oly be set valid again by a call
+ /// to setLruIterator.
+ virtual void invalidateIterator() {
+ valid_ = false;
+ }
+
+private:
+ typename LruList<T>::iterator iterator_; ///< Handle into the LRU List
+ bool valid_; ///< true if handle is valid
+};
+
+class TestEntry : public TestEntryT<TestEntry> {
+public:
+ TestEntry(std::string name, const int & code) :
+ name_(name), code_(code)
+ {}
+
+ /// \brief Get the Name
+ ///
+ /// \return Name given to this object
+ virtual std::string getName() const {
+ return name_;
+ }
+
+ /// \brief Set the Name
+ ///
+ /// \param name New name of the object
+ virtual void setName(const std::string& name) {
+ name_ = name;
+ }
+
+ /// \brief Get the Class
+ ///
+ /// \return Class code assigned to this object
+ virtual const int& getCode() const {
+ return code_;
+ }
+
+ /// \brief Set the Class
+ ///
+ /// \param code New code of the object
+ virtual void setCode(const int& code) {
+ code_ = code;
+ }
+
+private:
+ std::string name_; ///< Name of the object
+ int code_; ///< Class of the object
+
+};
+
+/// \brief Dropped Functor Class
+///
+/// Functor object is called when an object is dropped from the LRU list.
+/// To prove that it has run, this function does nothing more than set the
+/// MS bit on the 16-bit code value.
+class Dropped : public LruList<TestEntry>::Dropped {
+public:
+ virtual void operator()(TestEntry* entry) const {
+ entry->setCode(entry->getCode() | 0x8000);
+ }
+};
+
+
+/// \brief Text Fixture Class
+class LruListTest : public ::testing::Test {
+protected:
+ LruListTest() :
+ entry1_(new TestEntry("alpha", 1)),
+ entry2_(new TestEntry("beta", 3)),
+ entry3_(new TestEntry("gamma", 4)),
+ entry4_(new TestEntry("delta", 1)),
+ entry5_(new TestEntry("epsilon", 4)),
+ entry6_(new TestEntry("zeta", 3)),
+ entry7_(new TestEntry("eta", 1))
+ {}
+
+ virtual ~LruListTest()
+ {}
+
+ boost::shared_ptr<TestEntry> entry1_;
+ boost::shared_ptr<TestEntry> entry2_;
+ boost::shared_ptr<TestEntry> entry3_;
+ boost::shared_ptr<TestEntry> entry4_;
+ boost::shared_ptr<TestEntry> entry5_;
+ boost::shared_ptr<TestEntry> entry6_;
+ boost::shared_ptr<TestEntry> entry7_;
+};
+
+
+// Test of the constructor
+TEST_F(LruListTest, Constructor) {
+ LruList<TestEntry> lru(100);
+ EXPECT_EQ(100, lru.getMaxSize());
+ EXPECT_EQ(0, lru.size());
+}
+
+// Test of Get/Set the maximum number of entrys
+TEST_F(LruListTest, GetSet) {
+ LruList<TestEntry> lru(100);
+ EXPECT_EQ(100, lru.getMaxSize());
+ lru.setMaxSize(42);
+ EXPECT_EQ(42, lru.getMaxSize());
+}
+
+// Test that adding an entry really does add an entry
+TEST_F(LruListTest, Add) {
+ LruList<TestEntry> lru(100);
+ EXPECT_EQ(0, lru.size());
+
+ lru.add(entry1_);
+ EXPECT_EQ(1, lru.size());
+
+ lru.add(entry2_);
+ EXPECT_EQ(2, lru.size());
+}
+
+// Test that removing an entry really does remove it.
+TEST_F(LruListTest, Remove) {
+ LruList<TestEntry> lru(100);
+ EXPECT_EQ(0, lru.size());
+
+ EXPECT_FALSE(entry1_->iteratorValid());
+ lru.add(entry1_);
+ EXPECT_TRUE(entry1_->iteratorValid());
+ EXPECT_EQ(1, lru.size());
+
+ EXPECT_FALSE(entry2_->iteratorValid());
+ lru.add(entry2_);
+ EXPECT_TRUE(entry2_->iteratorValid());
+ EXPECT_EQ(2, lru.size());
+
+ lru.remove(entry1_);
+ EXPECT_FALSE(entry1_->iteratorValid());
+ EXPECT_EQ(1, lru.size());
+}
+
+// Check that adding a new entry to a limited size list does delete the
+// oldest entry from the list.
+TEST_F(LruListTest, SizeLimit) {
+ LruList<TestEntry> lru(3);
+ EXPECT_EQ(0, lru.size());
+
+ // Add first entry and check that the shared pointer's reference count
+ // has increased. There will be two references: one from the "entry1_"
+ // member in the test fixture class, and one from the list.
+ EXPECT_EQ(1, entry1_.use_count());
+ lru.add(entry1_);
+ EXPECT_EQ(2, entry1_.use_count());
+ EXPECT_EQ(1, lru.size());
+
+ // Same for entry 2.
+ EXPECT_EQ(1, entry2_.use_count());
+ lru.add(entry2_);
+ EXPECT_EQ(2, entry2_.use_count());
+ EXPECT_EQ(2, lru.size());
+
+ // Same for entry 3.
+ EXPECT_EQ(1, entry3_.use_count());
+ lru.add(entry3_);
+ EXPECT_EQ(2, entry3_.use_count());
+ EXPECT_EQ(3, lru.size());
+
+ // Adding entry 4 should remove entry 1 from the list. This will
+ // delete the list's shared pointer to the entry and will therefore
+ // drop the reference count back to one (from the "entry1_" member in
+ // the text fixture class).
+ EXPECT_EQ(2, entry1_.use_count());
+ EXPECT_EQ(1, entry4_.use_count());
+ lru.add(entry4_);
+ EXPECT_EQ(1, entry1_.use_count());
+ EXPECT_EQ(2, entry4_.use_count());
+ EXPECT_EQ(3, lru.size());
+
+ // Adding entry 5 should remove entry 2 from the list.
+ EXPECT_EQ(2, entry2_.use_count());
+ EXPECT_EQ(1, entry5_.use_count());
+ lru.add(entry5_);
+ EXPECT_EQ(1, entry2_.use_count());
+ EXPECT_EQ(2, entry5_.use_count());
+ EXPECT_EQ(3, lru.size());
+}
+
+// Check that "touching" an entry adds it to the back of the list.
+TEST_F(LruListTest, Touch) {
+
+ // Create the list
+ LruList<TestEntry> lru(3);
+ EXPECT_EQ(0, lru.size());
+ lru.add(entry1_);
+ lru.add(entry2_);
+ lru.add(entry3_);
+
+ // Check the reference counts of the entrys and the list size
+ EXPECT_EQ(2, entry1_.use_count());
+ EXPECT_EQ(2, entry2_.use_count());
+ EXPECT_EQ(2, entry3_.use_count());
+ EXPECT_EQ(1, entry4_.use_count());
+ EXPECT_EQ(1, entry5_.use_count());
+ EXPECT_EQ(1, entry6_.use_count());
+ EXPECT_EQ(1, entry7_.use_count());
+ EXPECT_EQ(3, lru.size());
+
+ // "Touch" the first entry
+ lru.touch(entry1_);
+
+ // Adding two more entries should not remove the touched entry.
+ lru.add(entry4_);
+ lru.add(entry5_);
+
+ // Check the status of the entrys and the list.
+ EXPECT_EQ(2, entry1_.use_count());
+ EXPECT_EQ(1, entry2_.use_count());
+ EXPECT_EQ(1, entry3_.use_count());
+ EXPECT_EQ(2, entry4_.use_count());
+ EXPECT_EQ(2, entry5_.use_count());
+ EXPECT_EQ(1, entry6_.use_count());
+ EXPECT_EQ(1, entry7_.use_count());
+ EXPECT_EQ(3, lru.size());
+
+ // Now touch the entry agin to move it to the back of the list.
+ // This checks that the iterator stored in the entry as a result of the
+ // last touch operation is valid.
+ lru.touch(entry1_);
+
+ // Check this by adding two more entrys and checking reference counts
+ // to see what is stored.
+ lru.add(entry6_);
+ lru.add(entry7_);
+
+ EXPECT_EQ(2, entry1_.use_count());
+ EXPECT_EQ(1, entry2_.use_count());
+ EXPECT_EQ(1, entry3_.use_count());
+ EXPECT_EQ(1, entry4_.use_count());
+ EXPECT_EQ(1, entry5_.use_count());
+ EXPECT_EQ(2, entry6_.use_count());
+ EXPECT_EQ(2, entry7_.use_count());
+ EXPECT_EQ(3, lru.size());
+}
+
+// Dropped functor tests: tests that the function object is called when an
+// object expires from the list.
+TEST_F(LruListTest, Dropped) {
+
+ // Create an object with an expiration handler.
+ LruList<TestEntry> lru(3, new Dropped());
+
+ // Fill the list
+ lru.add(entry1_);
+ lru.add(entry2_);
+ lru.add(entry3_);
+
+ EXPECT_EQ(1, entry1_->getCode());
+ EXPECT_EQ(3, entry2_->getCode());
+
+ // Add another entry and check that the handler runs.
+ EXPECT_EQ(0, (entry1_->getCode() & 0x8000));
+ lru.add(entry4_);
+ EXPECT_NE(0, (entry1_->getCode() & 0x8000));
+
+ EXPECT_EQ(0, (entry2_->getCode() & 0x8000));
+ lru.add(entry5_);
+ EXPECT_NE(0, (entry2_->getCode() & 0x8000));
+
+ // Delete an entry and check that the handler does not run.
+ EXPECT_EQ(0, (entry3_->getCode() & 0x8000));
+ lru.remove(entry3_);
+ EXPECT_EQ(0, (entry3_->getCode() & 0x8000));
+}
+
+// Clear functor tests: tests whether all the elements in
+// the list are dropped properly and the size of list is
+// set to 0.
+TEST_F(LruListTest, Clear) {
+ // Create an object with an expiration handler.
+ LruList<TestEntry> lru(3, new Dropped());
+
+ // Fill the list
+ lru.add(entry1_);
+ lru.add(entry2_);
+ lru.add(entry3_);
+
+ EXPECT_EQ(1, entry1_->getCode());
+ EXPECT_EQ(3, entry2_->getCode());
+ EXPECT_EQ(4, entry3_->getCode());
+
+ EXPECT_EQ(0, (entry1_->getCode() & 0x8000));
+ EXPECT_EQ(0, (entry2_->getCode() & 0x8000));
+ EXPECT_EQ(0, (entry3_->getCode() & 0x8000));
+
+ // Clear the lru list, and check the drop handler run
+ lru.clear();
+ EXPECT_NE(0, (entry1_->getCode() & 0x8000));
+ EXPECT_NE(0, (entry2_->getCode() & 0x8000));
+ EXPECT_NE(0, (entry3_->getCode() & 0x8000));
+
+ EXPECT_EQ(0, lru.size());
+}
+
+// Miscellaneous tests - pathological conditions
+TEST_F(LruListTest, Miscellaneous) {
+
+ // Zero size list should not allow entrys to be added
+ LruList<TestEntry> lru_1(0);
+ lru_1.add(entry1_);
+ EXPECT_EQ(0, lru_1.size());
+ EXPECT_EQ(1, entry1_.use_count());
+
+ // Removing an uninserted entry should not affect the list.
+ LruList<TestEntry> lru_2(100);
+ lru_2.add(entry1_);
+ lru_2.add(entry2_);
+ lru_2.add(entry3_);
+ EXPECT_EQ(3, lru_2.size());
+
+ lru_2.remove(entry4_);
+ EXPECT_EQ(2, entry1_.use_count());
+ EXPECT_EQ(2, entry2_.use_count());
+ EXPECT_EQ(2, entry3_.use_count());
+ EXPECT_EQ(1, entry4_.use_count());
+ EXPECT_EQ(1, entry5_.use_count());
+ EXPECT_EQ(3, lru_2.size());
+
+ // Touching an uninserted entry should not affect the list.
+ lru_2.touch(entry5_);
+ EXPECT_EQ(2, entry1_.use_count());
+ EXPECT_EQ(2, entry2_.use_count());
+ EXPECT_EQ(2, entry3_.use_count());
+ EXPECT_EQ(1, entry4_.use_count());
+ EXPECT_EQ(1, entry5_.use_count());
+ EXPECT_EQ(3, lru_2.size());
+}
+
+} // namespace nsas
+} // namespace isc
diff --git a/src/lib/util/tests/qid_gen_unittest.cc b/src/lib/util/tests/qid_gen_unittest.cc
new file mode 100644
index 0000000..60c81ae
--- /dev/null
+++ b/src/lib/util/tests/qid_gen_unittest.cc
@@ -0,0 +1,60 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+
+/// \brief Test of QidGenerator
+///
+
+#include <gtest/gtest.h>
+
+#include <util/random/qid_gen.h>
+
+using namespace isc::util::random;
+
+// Tests the operation of the Qid generator
+
+// Check that getInstance returns a singleton
+TEST(QidGenerator, singleton) {
+ QidGenerator& g1 = QidGenerator::getInstance();
+ QidGenerator& g2 = QidGenerator::getInstance();
+
+ EXPECT_TRUE(&g1 == &g2);
+}
+
+TEST(QidGenerator, generate) {
+ // We'll assume that boost's generator is 'good enough', and won't
+ // do full statistical checking here. Let's just call it the xkcd
+ // test (http://xkcd.com/221/), and check if three consecutive
+ // generates are not all the same.
+ uint16_t one, two, three;
+ QidGenerator& gen = QidGenerator::getInstance();
+ one = gen.generateQid();
+ two = gen.generateQid();
+ three = gen.generateQid();
+ ASSERT_FALSE((one == two) && (one == three));
+}
diff --git a/src/lib/util/tests/random_number_generator_unittest.cc b/src/lib/util/tests/random_number_generator_unittest.cc
new file mode 100644
index 0000000..23d5b88
--- /dev/null
+++ b/src/lib/util/tests/random_number_generator_unittest.cc
@@ -0,0 +1,311 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <gtest/gtest.h>
+#include <boost/shared_ptr.hpp>
+
+#include <algorithm>
+#include <iostream>
+#include <vector>
+
+#include <util/random/random_number_generator.h>
+
+namespace isc {
+namespace util {
+namespace random {
+
+using namespace std;
+
+/// \brief Test Fixture Class for uniform random number generator
+///
+/// The hard part for this test is how to test that the number is random?
+/// and how to test that the number is uniformly distributed?
+/// Or maybe we can trust the boost implementation
+class UniformRandomIntegerGeneratorTest : public ::testing::Test {
+public:
+ UniformRandomIntegerGeneratorTest():
+ gen_(min_, max_)
+ {
+ }
+ virtual ~UniformRandomIntegerGeneratorTest(){}
+
+ int gen() { return gen_(); }
+ int max() const { return max_; }
+ int min() const { return min_; }
+
+private:
+ UniformRandomIntegerGenerator gen_;
+
+ const static int min_ = 1;
+ const static int max_ = 10;
+};
+
+// Some validation tests will incur performance penalty, so the tests are
+// made only in "debug" version with assert(). But if NDEBUG is defined
+// the tests will be failed since assert() is non-op in non-debug version.
+// The "#ifndef NDEBUG" is added to make the tests be performed only in
+// non-debug environment.
+// Note: the death test is not supported by all platforms. We need to
+// compile tests using it selectively.
+#if !defined(NDEBUG)
+// Test of the constructor
+TEST_F(UniformRandomIntegerGeneratorTest, Constructor) {
+ // The range must be min<=max
+ ASSERT_THROW(UniformRandomIntegerGenerator(3, 2), InvalidLimits);
+}
+#endif
+
+// Test of the generated integers are in the range [min, max]
+TEST_F(UniformRandomIntegerGeneratorTest, IntegerRange) {
+ vector<int> numbers;
+
+ // Generate a lot of random integers
+ for (int i = 0; i < max()*10; ++i) {
+ numbers.push_back(gen());
+ }
+
+ // Remove the duplicated values
+ sort(numbers.begin(), numbers.end());
+ vector<int>::iterator it = unique(numbers.begin(), numbers.end());
+
+ // make sure the numbers are in range [min, max]
+ ASSERT_EQ(it - numbers.begin(), max() - min() + 1);
+}
+
+/// \brief Test Fixture Class for weighted random number generator
+class WeightedRandomIntegerGeneratorTest : public ::testing::Test {
+public:
+ WeightedRandomIntegerGeneratorTest()
+ { }
+
+ virtual ~WeightedRandomIntegerGeneratorTest()
+ { }
+};
+
+// Test of the weighted random number generator constructor
+TEST_F(WeightedRandomIntegerGeneratorTest, Constructor) {
+ vector<double> probabilities;
+
+ // If no probabilities is provided, the smallest integer will always be generated
+ WeightedRandomIntegerGenerator gen(probabilities, 123);
+ for (int i = 0; i < 100; ++i) {
+ ASSERT_EQ(gen(), 123);
+ }
+
+/// Some validation tests will incur performance penalty, so the tests are
+/// made only in "debug" version with assert(). But if NDEBUG is defined
+/// the tests will be failed since assert() is non-op in non-debug version.
+/// The "#ifndef NDEBUG" is added to make the tests be performed only in
+/// non-debug environment.
+#if !defined(NDEBUG)
+ //The probability must be >= 0
+ probabilities.push_back(-0.1);
+ probabilities.push_back(1.1);
+ ASSERT_THROW(WeightedRandomIntegerGenerator gen2(probabilities),
+ InvalidProbValue);
+
+ //The probability must be <= 1.0
+ probabilities.clear();
+ probabilities.push_back(0.1);
+ probabilities.push_back(1.1);
+ ASSERT_THROW(WeightedRandomIntegerGenerator gen3(probabilities),
+ InvalidProbValue);
+
+ //The sum must be equal to 1.0
+ probabilities.clear();
+ probabilities.push_back(0.2);
+ probabilities.push_back(0.9);
+ ASSERT_THROW(WeightedRandomIntegerGenerator gen4(probabilities), SumNotOne);
+
+ //The sum must be equal to 1.0
+ probabilities.clear();
+ probabilities.push_back(0.3);
+ probabilities.push_back(0.2);
+ probabilities.push_back(0.1);
+ ASSERT_THROW(WeightedRandomIntegerGenerator gen5(probabilities), SumNotOne);
+#endif
+}
+
+// Test the randomization of the generator
+TEST_F(WeightedRandomIntegerGeneratorTest, WeightedRandomization) {
+ const int repeats = 100000;
+ // We repeat the simulation for N=repeats times
+ // for each probability p, its average is mu = N*p
+ // variance sigma^2 = N * p * (1-p)
+ // sigma = sqrt(N*2/9)
+ // we should make sure that mu - 4sigma < count < mu + 4sigma
+ // which means for 99.99366% of the time this should be true
+ {
+ double p = 0.5;
+ vector<double> probabilities;
+ probabilities.push_back(p);
+ probabilities.push_back(p);
+
+ // Uniformly generated integers
+ WeightedRandomIntegerGenerator gen(probabilities);
+ int c1 = 0;
+ int c2 = 0;
+ for (int i = 0; i < repeats; ++i){
+ int n = gen();
+ if (n == 0) {
+ ++c1;
+ } else if (n == 1) {
+ ++c2;
+ }
+ }
+ double mu = repeats * p;
+ double sigma = sqrt(repeats * p * (1 - p));
+ ASSERT_TRUE(fabs(c1 - mu) < 4*sigma);
+ ASSERT_TRUE(fabs(c2 - mu) < 4*sigma);
+ }
+
+ {
+ vector<double> probabilities;
+ int c1 = 0;
+ int c2 = 0;
+ double p1 = 0.2;
+ double p2 = 0.8;
+ probabilities.push_back(p1);
+ probabilities.push_back(p2);
+ WeightedRandomIntegerGenerator gen(probabilities);
+ for (int i = 0; i < repeats; ++i) {
+ int n = gen();
+ if (n == 0) {
+ ++c1;
+ } else if (n == 1) {
+ ++c2;
+ }
+ }
+ double mu1 = repeats * p1;
+ double mu2 = repeats * p2;
+ double sigma1 = sqrt(repeats * p1 * (1 - p1));
+ double sigma2 = sqrt(repeats * p2 * (1 - p2));
+ ASSERT_TRUE(fabs(c1 - mu1) < 4*sigma1);
+ ASSERT_TRUE(fabs(c2 - mu2) < 4*sigma2);
+ }
+
+ {
+ vector<double> probabilities;
+ int c1 = 0;
+ int c2 = 0;
+ double p1 = 0.8;
+ double p2 = 0.2;
+ probabilities.push_back(p1);
+ probabilities.push_back(p2);
+ WeightedRandomIntegerGenerator gen(probabilities);
+ for (int i = 0; i < repeats; ++i) {
+ int n = gen();
+ if (n == 0) {
+ ++c1;
+ } else if (n == 1) {
+ ++c2;
+ }
+ }
+ double mu1 = repeats * p1;
+ double mu2 = repeats * p2;
+ double sigma1 = sqrt(repeats * p1 * (1 - p1));
+ double sigma2 = sqrt(repeats * p2 * (1 - p2));
+ ASSERT_TRUE(fabs(c1 - mu1) < 4*sigma1);
+ ASSERT_TRUE(fabs(c2 - mu2) < 4*sigma2);
+ }
+
+ {
+ vector<double> probabilities;
+ int c1 = 0;
+ int c2 = 0;
+ int c3 = 0;
+ double p1 = 0.5;
+ double p2 = 0.25;
+ double p3 = 0.25;
+ probabilities.push_back(p1);
+ probabilities.push_back(p2);
+ probabilities.push_back(p3);
+ WeightedRandomIntegerGenerator gen(probabilities);
+ for (int i = 0; i < repeats; ++i){
+ int n = gen();
+ if (n == 0) {
+ ++c1;
+ } else if (n == 1) {
+ ++c2;
+ } else if (n == 2) {
+ ++c3;
+ }
+ }
+ double mu1 = repeats * p1;
+ double mu2 = repeats * p2;
+ double mu3 = repeats * p3;
+ double sigma1 = sqrt(repeats * p1 * (1 - p1));
+ double sigma2 = sqrt(repeats * p2 * (1 - p2));
+ double sigma3 = sqrt(repeats * p3 * (1 - p3));
+ ASSERT_TRUE(fabs(c1 - mu1) < 4*sigma1);
+ ASSERT_TRUE(fabs(c2 - mu2) < 4*sigma2);
+ ASSERT_TRUE(fabs(c3 - mu3) < 4*sigma3);
+ }
+}
+
+// Test the reset function of generator
+TEST_F(WeightedRandomIntegerGeneratorTest, ResetProbabilities) {
+ const int repeats = 100000;
+ vector<double> probabilities;
+ int c1 = 0;
+ int c2 = 0;
+ double p1 = 0.8;
+ double p2 = 0.2;
+ probabilities.push_back(p1);
+ probabilities.push_back(p2);
+ WeightedRandomIntegerGenerator gen(probabilities);
+ for (int i = 0; i < repeats; ++i) {
+ int n = gen();
+ if (n == 0) {
+ ++c1;
+ } else if (n == 1) {
+ ++c2;
+ }
+ }
+ double mu1 = repeats * p1;
+ double mu2 = repeats * p2;
+ double sigma1 = sqrt(repeats * p1 * (1 - p1));
+ double sigma2 = sqrt(repeats * p2 * (1 - p2));
+ ASSERT_TRUE(fabs(c1 - mu1) < 4*sigma1);
+ ASSERT_TRUE(fabs(c2 - mu2) < 4*sigma2);
+
+ // Reset the probabilities
+ probabilities.clear();
+ c1 = c2 = 0;
+ p1 = 0.2;
+ p2 = 0.8;
+ probabilities.push_back(p1);
+ probabilities.push_back(p2);
+ gen.reset(probabilities);
+ for (int i = 0; i < repeats; ++i) {
+ int n = gen();
+ if (n == 0) {
+ ++c1;
+ } else if (n == 1) {
+ ++c2;
+ }
+ }
+ mu1 = repeats * p1;
+ mu2 = repeats * p2;
+ sigma1 = sqrt(repeats * p1 * (1 - p1));
+ sigma2 = sqrt(repeats * p2 * (1 - p2));
+ ASSERT_TRUE(fabs(c1 - mu1) < 4*sigma1);
+ ASSERT_TRUE(fabs(c2 - mu2) < 4*sigma2);
+}
+
+} // namespace random
+} // namespace util
+} // namespace isc
diff --git a/src/lib/util/tests/run_unittests.cc b/src/lib/util/tests/run_unittests.cc
new file mode 100644
index 0000000..bd3c4c9
--- /dev/null
+++ b/src/lib/util/tests/run_unittests.cc
@@ -0,0 +1,21 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <gtest/gtest.h>
+
+int
+main(int argc, char* argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return (RUN_ALL_TESTS());
+}
diff --git a/src/lib/util/tests/sha1_unittest.cc b/src/lib/util/tests/sha1_unittest.cc
new file mode 100644
index 0000000..32f929a
--- /dev/null
+++ b/src/lib/util/tests/sha1_unittest.cc
@@ -0,0 +1,107 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <stdint.h>
+#include <string>
+
+#include <util/compat/sha1.h>
+
+#include <gtest/gtest.h>
+
+#include <dns/tests/unittest_util.h>
+
+using isc::UnitTestUtil;
+using namespace std;
+
+namespace {
+class Sha1Test : public ::testing::Test {
+protected:
+ Sha1Test() {}
+};
+
+// Tests copied from RFC 3174
+TEST_F(Sha1Test, Test1) {
+ SHA1Context sha;
+ uint8_t digest[SHA1_HASHSIZE];
+ uint8_t expected[SHA1_HASHSIZE] = {
+ 0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, 0x6a, 0xba, 0x3e,
+ 0x25, 0x71, 0x78, 0x50, 0xc2, 0x6c, 0x9c, 0xd0, 0xd8, 0x9d
+ };
+
+ EXPECT_EQ(0, SHA1Reset(&sha));
+ EXPECT_EQ(0, SHA1Input(&sha, (const uint8_t *) "abc", 3));
+ EXPECT_EQ(0, SHA1Result(&sha, digest));
+ for (int i = 0; i < SHA1_HASHSIZE; i++) {
+ EXPECT_EQ(digest[i], expected[i]);
+ }
+}
+
+TEST_F(Sha1Test, Test2) {
+ SHA1Context sha;
+ string test("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq");
+ uint8_t digest[SHA1_HASHSIZE];
+ uint8_t expected[SHA1_HASHSIZE] = {
+ 0x84, 0x98, 0x3e, 0x44, 0x1c, 0x3b, 0xd2, 0x6e, 0xba, 0xae,
+ 0x4a, 0xa1, 0xf9, 0x51, 0x29, 0xe5, 0xe5, 0x46, 0x70, 0xf1
+ };
+
+ EXPECT_EQ(0, SHA1Reset(&sha));
+ EXPECT_EQ(0, SHA1Input(&sha, (const uint8_t *) test.c_str(),
+ test.length()));
+ EXPECT_EQ(0, SHA1Result(&sha, digest));
+ for (int i = 0; i < SHA1_HASHSIZE; i++) {
+ EXPECT_EQ(digest[i], expected[i]);
+ }
+}
+
+TEST_F(Sha1Test, Test3) {
+ SHA1Context sha;
+ uint8_t digest[SHA1_HASHSIZE];
+ uint8_t expected[SHA1_HASHSIZE] = {
+ 0x34, 0xaa, 0x97, 0x3c, 0xd4, 0xc4, 0xda, 0xa4, 0xf6, 0x1e,
+ 0xeb, 0x2b, 0xdb, 0xad, 0x27, 0x31, 0x65, 0x34, 0x01, 0x6f
+ };
+
+ EXPECT_EQ(0, SHA1Reset(&sha));
+ for (int i = 0; i < 1000000; i++) {
+ EXPECT_EQ(0, SHA1Input(&sha, (const uint8_t *) "a", 1));
+ }
+ EXPECT_EQ(0, SHA1Result(&sha, digest));
+ for (int i = 0; i < SHA1_HASHSIZE; i++) {
+ EXPECT_EQ(digest[i], expected[i]);
+ }
+}
+
+TEST_F(Sha1Test, Test4) {
+ SHA1Context sha;
+ string test("01234567012345670123456701234567"
+ "01234567012345670123456701234567");
+ uint8_t digest[SHA1_HASHSIZE];
+ uint8_t expected[SHA1_HASHSIZE] = {
+ 0xde, 0xa3, 0x56, 0xa2, 0xcd, 0xdd, 0x90, 0xc7, 0xa7, 0xec,
+ 0xed, 0xc5, 0xeb, 0xb5, 0x63, 0x93, 0x4f, 0x46, 0x04, 0x52
+ };
+
+ EXPECT_EQ(0, SHA1Reset(&sha));
+ for (int i = 0; i < 10; i++) {
+ EXPECT_EQ(0, SHA1Input(&sha, (const uint8_t *) test.c_str(),
+ test.length()));
+ }
+ EXPECT_EQ(0, SHA1Result(&sha, digest));
+ for (int i = 0; i < SHA1_HASHSIZE; i++) {
+ EXPECT_EQ(digest[i], expected[i]);
+ }
+}
+}
+
diff --git a/src/lib/util/tests/strutil_unittest.cc b/src/lib/util/tests/strutil_unittest.cc
new file mode 100644
index 0000000..e1b751c
--- /dev/null
+++ b/src/lib/util/tests/strutil_unittest.cc
@@ -0,0 +1,214 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <util/strutil.h>
+
+using namespace isc;
+using namespace std;
+
+class StringUtilTest : public ::testing::Test {
+protected:
+ StringUtilTest()
+ {
+ }
+};
+
+
+// Check for slash replacement
+
+TEST_F(StringUtilTest, Slash) {
+
+ string instring = "";
+ isc::strutil::normalizeSlash(instring);
+ EXPECT_EQ("", instring);
+
+ instring = "C:\\A\\B\\C.D";
+ isc::strutil::normalizeSlash(instring);
+ EXPECT_EQ("C:/A/B/C.D", instring);
+
+ instring = "// \\ //";
+ isc::strutil::normalizeSlash(instring);
+ EXPECT_EQ("// / //", instring);
+}
+
+// Check that leading and trailing space trimming works
+
+TEST_F(StringUtilTest, Trim) {
+
+ // Empty and full string.
+ EXPECT_EQ("", isc::strutil::trim(""));
+ EXPECT_EQ("abcxyz", isc::strutil::trim("abcxyz"));
+
+ // Trim right-most blanks
+ EXPECT_EQ("ABC", isc::strutil::trim("ABC "));
+ EXPECT_EQ("ABC", isc::strutil::trim("ABC\t\t \n\t"));
+
+ // Left-most blank trimming
+ EXPECT_EQ("XYZ", isc::strutil::trim(" XYZ"));
+ EXPECT_EQ("XYZ", isc::strutil::trim("\t\t \tXYZ"));
+
+ // Right and left, with embedded spaces
+ EXPECT_EQ("MN \t OP", isc::strutil::trim("\t\tMN \t OP \t"));
+}
+
+// Check tokenization. Note that ASSERT_EQ is used to check the size of the
+// returned vector; if not as expected, the following references may be invalid
+// so should not be used.
+
+TEST_F(StringUtilTest, Tokens) {
+ vector<string> result;
+
+ // Default delimiters
+
+ // Degenerate cases
+ result = isc::strutil::tokens(""); // Empty string
+ EXPECT_EQ(0, result.size());
+
+ result = isc::strutil::tokens(" \n "); // String is all delimiters
+ EXPECT_EQ(0, result.size());
+
+ result = isc::strutil::tokens("abc"); // String has no delimiters
+ ASSERT_EQ(1, result.size());
+ EXPECT_EQ(string("abc"), result[0]);
+
+ // String containing leading and/or trailing delimiters, no embedded ones.
+ result = isc::strutil::tokens("\txyz"); // One leading delimiter
+ ASSERT_EQ(1, result.size());
+ EXPECT_EQ(string("xyz"), result[0]);
+
+ result = isc::strutil::tokens("\t \nxyz"); // Multiple leading delimiters
+ ASSERT_EQ(1, result.size());
+ EXPECT_EQ(string("xyz"), result[0]);
+
+ result = isc::strutil::tokens("xyz\n"); // One trailing delimiter
+ ASSERT_EQ(1, result.size());
+ EXPECT_EQ(string("xyz"), result[0]);
+
+ result = isc::strutil::tokens("xyz \t"); // Multiple trailing
+ ASSERT_EQ(1, result.size());
+ EXPECT_EQ(string("xyz"), result[0]);
+
+ result = isc::strutil::tokens("\t xyz \n"); // Leading and trailing
+ ASSERT_EQ(1, result.size());
+ EXPECT_EQ(string("xyz"), result[0]);
+
+ // Embedded delimiters
+ result = isc::strutil::tokens("abc\ndef"); // 2 tokens, one separator
+ ASSERT_EQ(2, result.size());
+ EXPECT_EQ(string("abc"), result[0]);
+ EXPECT_EQ(string("def"), result[1]);
+
+ result = isc::strutil::tokens("abc\t\t\ndef"); // 2 tokens, 3 separators
+ ASSERT_EQ(2, result.size());
+ EXPECT_EQ(string("abc"), result[0]);
+ EXPECT_EQ(string("def"), result[1]);
+
+ result = isc::strutil::tokens("abc\n \tdef\t\tghi");
+ ASSERT_EQ(3, result.size()); // Multiple tokens, many delims
+ EXPECT_EQ(string("abc"), result[0]);
+ EXPECT_EQ(string("def"), result[1]);
+ EXPECT_EQ(string("ghi"), result[2]);
+
+ // Embedded and non-embedded delimiters
+
+ result = isc::strutil::tokens("\t\t \nabc\n \tdef\t\tghi \n\n");
+ ASSERT_EQ(3, result.size()); // Multiple tokens, many delims
+ EXPECT_EQ(string("abc"), result[0]);
+ EXPECT_EQ(string("def"), result[1]);
+ EXPECT_EQ(string("ghi"), result[2]);
+
+ // Non-default delimiter
+ result = isc::strutil::tokens("alpha/beta/ /gamma//delta/epsilon/", "/");
+ ASSERT_EQ(6, result.size());
+ EXPECT_EQ(string("alpha"), result[0]);
+ EXPECT_EQ(string("beta"), result[1]);
+ EXPECT_EQ(string(" "), result[2]);
+ EXPECT_EQ(string("gamma"), result[3]);
+ EXPECT_EQ(string("delta"), result[4]);
+ EXPECT_EQ(string("epsilon"), result[5]);
+
+ // Non-default delimiters (plural)
+ result = isc::strutil::tokens("+*--alpha*beta+ -gamma**delta+epsilon-+**",
+ "*+-");
+ ASSERT_EQ(6, result.size());
+ EXPECT_EQ(string("alpha"), result[0]);
+ EXPECT_EQ(string("beta"), result[1]);
+ EXPECT_EQ(string(" "), result[2]);
+ EXPECT_EQ(string("gamma"), result[3]);
+ EXPECT_EQ(string("delta"), result[4]);
+ EXPECT_EQ(string("epsilon"), result[5]);
+}
+
+// Changing case
+
+TEST_F(StringUtilTest, ChangeCase) {
+ string mixed("abcDEFghiJKLmno123[]{=+--+]}");
+ string upper("ABCDEFGHIJKLMNO123[]{=+--+]}");
+ string lower("abcdefghijklmno123[]{=+--+]}");
+
+ string test = mixed;
+ isc::strutil::lowercase(test);
+ EXPECT_EQ(lower, test);
+
+ test = mixed;
+ isc::strutil::uppercase(test);
+ EXPECT_EQ(upper, test);
+}
+
+// Formatting
+
+TEST_F(StringUtilTest, Formatting) {
+
+ vector<string> args;
+ args.push_back("arg1");
+ args.push_back("arg2");
+ args.push_back("arg3");
+
+ string format1 = "This is a string with no tokens";
+ EXPECT_EQ(format1, isc::strutil::format(format1, args));
+
+ string format2 = ""; // Empty string
+ EXPECT_EQ(format2, isc::strutil::format(format2, args));
+
+ string format3 = " "; // Empty string
+ EXPECT_EQ(format3, isc::strutil::format(format3, args));
+
+ string format4 = "String with %d non-string tokens %lf";
+ EXPECT_EQ(format4, isc::strutil::format(format4, args));
+
+ string format5 = "String with %s correct %s number of tokens %s";
+ string result5 = "String with arg1 correct arg2 number of tokens arg3";
+ EXPECT_EQ(result5, isc::strutil::format(format5, args));
+
+ string format6 = "String with %s too %s few tokens";
+ string result6 = "String with arg1 too arg2 few tokens";
+ EXPECT_EQ(result6, isc::strutil::format(format6, args));
+
+ string format7 = "String with %s too %s many %s tokens %s !";
+ string result7 = "String with arg1 too arg2 many arg3 tokens %s !";
+ EXPECT_EQ(result7, isc::strutil::format(format7, args));
+
+ string format8 = "String with embedded%s%s%stokens";
+ string result8 = "String with embeddedarg1arg2arg3tokens";
+ EXPECT_EQ(result8, isc::strutil::format(format8, args));
+
+ // Handle an empty vector
+ args.clear();
+ string format9 = "%s %s";
+ EXPECT_EQ(format9, isc::strutil::format(format9, args));
+}
diff --git a/src/lib/util/tests/time_utilities_unittest.cc b/src/lib/util/tests/time_utilities_unittest.cc
new file mode 100644
index 0000000..8a16708
--- /dev/null
+++ b/src/lib/util/tests/time_utilities_unittest.cc
@@ -0,0 +1,163 @@
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <string>
+
+#include <time.h>
+
+#include <util/compat/time_utilities.h>
+
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc::util::compat;
+
+// See time_utilities.cc
+namespace isc {
+namespace util {
+namespace compat {
+namespace detail {
+extern int64_t (*gettimeFunction)();
+}
+}
+}
+}
+
+namespace {
+
+class DNSSECTimeTest : public ::testing::Test {
+protected:
+ ~DNSSECTimeTest() {
+ detail::gettimeFunction = NULL;
+ }
+};
+
+TEST_F(DNSSECTimeTest, fromText) {
+ // In most cases (in practice) the 32-bit and 64-bit versions should
+ // behave identically, so we'll mainly test the 32-bit version, which
+ // will be more commonly used in actual code (because many of the wire
+ // format time field are 32-bit). The subtle cases where these two
+ // return different values will be tested at the end of this test case.
+
+ // These are bogus and should be rejected
+ EXPECT_THROW(timeFromText32("2011 101120000"), InvalidTime);
+ EXPECT_THROW(timeFromText32("201101011200-0"), InvalidTime);
+
+ // Short length (or "decimal integer" version of representation;
+ // it's valid per RFC4034, but is not supported in this implementation)
+ EXPECT_THROW(timeFromText32("20100223"), InvalidTime);
+
+ // Leap year checks
+ EXPECT_THROW(timeFromText32("20110229120000"), InvalidTime);
+ EXPECT_THROW(timeFromText32("21000229120000"), InvalidTime);
+ EXPECT_NO_THROW(timeFromText32("20000229120000"));
+ EXPECT_NO_THROW(timeFromText32("20120229120000"));
+
+ // unusual case: this implementation allows SS=60 for "leap seconds"
+ EXPECT_NO_THROW(timeFromText32("20110101120060"));
+
+ // Out of range parameters
+ EXPECT_THROW(timeFromText32("19100223214617"), InvalidTime); // YY<1970
+ EXPECT_THROW(timeFromText32("20110001120000"), InvalidTime); // MM=00
+ EXPECT_THROW(timeFromText32("20111301120000"), InvalidTime); // MM=13
+ EXPECT_THROW(timeFromText32("20110100120000"), InvalidTime); // DD=00
+ EXPECT_THROW(timeFromText32("20110132120000"), InvalidTime); // DD=32
+ EXPECT_THROW(timeFromText32("20110431120000"), InvalidTime); // 'Apr31'
+ EXPECT_THROW(timeFromText32("20110101250000"), InvalidTime); // HH=25
+ EXPECT_THROW(timeFromText32("20110101126000"), InvalidTime); // mm=60
+ EXPECT_THROW(timeFromText32("20110101120061"), InvalidTime); // SS=61
+
+ // Feb 7, 06:28:15 UTC 2106 is the possible maximum time that can be
+ // represented as an unsigned 32bit integer without overflow.
+ EXPECT_EQ(4294967295LU, timeFromText32("21060207062815"));
+
+ // After that, timeFromText32() should start returning the second count
+ // modulo 2^32.
+ EXPECT_EQ(0, timeFromText32("21060207062816"));
+ EXPECT_EQ(10, timeFromText32("21060207062826"));
+
+ // On the other hand, the 64-bit version should return monotonically
+ // increasing counters.
+ EXPECT_EQ(4294967296LL, timeFromText64("21060207062816"));
+ EXPECT_EQ(4294967306LL, timeFromText64("21060207062826"));
+}
+
+// This helper templated function tells timeToText32 a faked current time.
+// The template parameter is that faked time in the form of int64_t seconds
+// since epoch.
+template <int64_t NOW>
+int64_t
+testGetTime() {
+ return (NOW);
+}
+
+// Seconds since epoch for the year 10K eve. Commonly used in some tests
+// below.
+const uint64_t YEAR10K_EVE = 253402300799LL;
+
+TEST_F(DNSSECTimeTest, toText) {
+ // Check a basic case with the default (normal) gettimeFunction
+ // based on the "real current time".
+ // Note: this will fail after year 2078, but at that point we won't use
+ // this program anyway:-)
+ EXPECT_EQ("20100311233000", timeToText32(1268350200));
+
+ // Set the current time to: Feb 18 09:04:14 UTC 2012 (an arbitrary choice
+ // in the range of the first half of uint32 since epoch).
+ detail::gettimeFunction = testGetTime<1329555854LL>;
+
+ // Test the "year 2038" problem.
+ // Check the result of toText() for "INT_MIN" in int32_t. It's in the
+ // 68-year range from the faked current time, so the result should be
+ // in year 2038, instead of 1901.
+ EXPECT_EQ("20380119031408", timeToText64(0x80000000L));
+ EXPECT_EQ("20380119031408", timeToText32(0x80000000L));
+
+ // A controversial case: what should we do with "-1"? It's out of range
+ // in future, but according to RFC time before epoch doesn't seem to be
+ // considered "in-range" either. Our toText() implementation handles
+ // this range as a special case and always treats them as future time
+ // until year 2038. This won't be a real issue in practice, though,
+ // since such too large values won't be used in actual deployment by then.
+ EXPECT_EQ("21060207062815", timeToText32(0xffffffffL));
+
+ // After the singular point of year 2038, the first half of uint32 can
+ // point to a future time.
+ // Set the current time to: Apr 1 00:00:00 UTC 2038:
+ detail::gettimeFunction = testGetTime<2153692800LL>;
+ // then time "10" is Feb 7 06:28:26 UTC 2106
+ EXPECT_EQ("21060207062826", timeToText32(10));
+ // in 64-bit, it's 2^32 + 10
+ EXPECT_EQ("21060207062826", timeToText64(0x10000000aLL));
+
+ // After year 2106, the upper half of uint32 can point to past time
+ // (as it should).
+ detail::gettimeFunction = testGetTime<0x10000000aLL>;
+ EXPECT_EQ("21060207062815", timeToText32(0xffffffffL));
+
+ // Try very large time value. Actually it's the possible farthest time
+ // that can be represented in the form of YYYYMMDDHHmmSS.
+ EXPECT_EQ("99991231235959", timeToText64(YEAR10K_EVE));
+ detail::gettimeFunction = testGetTime<YEAR10K_EVE - 10>;
+ EXPECT_EQ("99991231235959", timeToText32(4294197631LU));
+}
+
+TEST_F(DNSSECTimeTest, overflow) {
+ // Jan 1, Year 10,000.
+ EXPECT_THROW(timeToText64(253402300800LL), InvalidTime);
+ detail::gettimeFunction = testGetTime<YEAR10K_EVE - 10>;
+ EXPECT_THROW(timeToText32(4294197632LU), InvalidTime);
+}
+
+}
More information about the bind10-changes
mailing list