Paul J. Lucas has proposed merging lp:~paul-lucas/zorba/bug-1189636 into 
lp:zorba.

Commit message:
* Added hexbinary::streambuf and friends.
* Fixed buffer overflow bug in base64::streambuf::xsputn().

Requested reviews:
  Paul J. Lucas (paul-lucas)
Related bugs:
  Bug #1189636 in Zorba: "transcoding hexBinary streambuf"
  https://bugs.launchpad.net/zorba/+bug/1189636

For more details, see:
https://code.launchpad.net/~paul-lucas/zorba/bug-1189636/+merge/170471

* Added hexbinary::streambuf and friends.
* Fixed buffer overflow bug in base64::streambuf::xsputn().
-- 
https://code.launchpad.net/~paul-lucas/zorba/bug-1189636/+merge/170471
Your team Zorba Coders is subscribed to branch lp:zorba.
=== modified file 'ChangeLog'
--- ChangeLog	2013-06-19 19:27:26 +0000
+++ ChangeLog	2013-06-20 00:55:33 +0000
@@ -23,6 +23,7 @@
 Bug Fixes/Other Changes:
   * Fixed bug #1117952 (Improve XML error output format)
   * Fixed bug #1188084 (fn-replace-42 failing)
+  * Fixed bug #1189636 (transcoding hexBinary streambuf)
   * Fixed bug in hoisting through try-catch expr
   * Fixed bug #1162631 (format-integer 'w' format of negative numbers)
   * Fixed bug #1190261 (relative paths bug in file module)

=== modified file 'include/zorba/util/base64_stream.h'
--- include/zorba/util/base64_stream.h	2013-06-12 00:21:05 +0000
+++ include/zorba/util/base64_stream.h	2013-06-20 00:55:33 +0000
@@ -198,7 +198,7 @@
  * destroyed.
  * \code
  *  void f( ostream &os ) {
- *    base64::auto_attach<ostream> const raii( os, "ISO-8859-1" );
+ *    base64::auto_attach<ostream> const raii( os );
  *    // ...
  *  }
  * \endcode

=== added file 'include/zorba/util/hexbinary_stream.h'
--- include/zorba/util/hexbinary_stream.h	1970-01-01 00:00:00 +0000
+++ include/zorba/util/hexbinary_stream.h	2013-06-20 00:55:33 +0000
@@ -0,0 +1,310 @@
+/*
+ * Copyright 2006-2008 The FLWOR Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ZORBA_HEXBINARY_STREAM_API_H
+#define ZORBA_HEXBINARY_STREAM_API_H
+
+#include <streambuf>
+
+#include <zorba/config.h>
+#include <zorba/internal/cxx_util.h>
+#include <zorba/internal/streambuf.h>
+
+namespace zorba {
+namespace hexbinary {
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * A %hexbinary::streambuf is-a std::streambuf for encoding to and decoding
+ * from HexBinary on-the-fly.
+ *
+ * To use it, replace a stream's streambuf:
+ * \code
+ *  istream is;
+ *  // ...
+ *  hexbinary::streambuf hb_buf( is.rdbuf() );
+ *  is.ios::rdbuf( &hb_buf );
+ * \endcode
+ * Note that the %hexbinary::streambuf must exist for as long as it's being
+ * used by the stream.  If you are replacing the streabuf for a stream you did
+ * not create, you should set it back to the original streambuf:
+ * \code
+ *  void f( ostream &os ) {
+ *    hexbinary::streambuf hb_buf( os.rdbuf() );
+ *    try {
+ *      os.ios::rdbuf( &hb_buf );
+ *      // ...
+ *    }
+ *    catch ( ... ) {
+ *      os.ios::rdbuf( hb_buf.orig_streambuf() );
+ *      throw;
+ *    }
+ *    os.ios::rdbuf( hb_buf.orig_streambuf() );
+ *  }
+ * \endcode
+ * Alternatively, you may wish to use either \c attach(), \c auto_attach, or
+ * \c hexbinary::stream instead.
+ *
+ * While %hexbinary::streambuf does support seeking, the positions are relative
+ * to the original byte stream.
+ */
+class ZORBA_DLL_PUBLIC streambuf : public std::streambuf {
+public:
+  /**
+   * Constructs a %hexbinary::streambuf.
+   *
+   * @param orig The original streambuf to read/write from/to.
+   * @throws std::invalid_argument if is not supported or \a orig is null.
+   */
+  streambuf( std::streambuf *orig );
+
+  /**
+   * Gets the original streambuf.
+   *
+   * @return said streambuf.
+   */
+  std::streambuf* orig_streambuf() const {
+    return orig_buf_;
+  }
+
+protected:
+  void imbue( std::locale const& );
+  pos_type seekoff( off_type, std::ios_base::seekdir, std::ios_base::openmode );
+  pos_type seekpos( pos_type, std::ios_base::openmode );
+  std::streambuf* setbuf( char_type*, std::streamsize );
+  std::streamsize showmanyc();
+  int_type overflow( int_type );
+  int_type pbackfail( int_type );
+  int_type underflow();
+  std::streamsize xsgetn( char_type*, std::streamsize );
+  std::streamsize xsputn( char_type const*, std::streamsize );
+
+private:
+  std::streambuf *orig_buf_;
+  char gbuf_[2];
+
+  void clear();
+
+  // forbid
+  streambuf( streambuf const& );
+  streambuf& operator=( streambuf const& );
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace hexbinary
+
+namespace internal {
+namespace hexbinary {
+
+ZORBA_DLL_PUBLIC
+std::streambuf* alloc_streambuf( std::streambuf *orig );
+
+ZORBA_DLL_PUBLIC
+int get_streambuf_index();
+
+} // namespace hexbinary
+} // namespace internal
+
+namespace hexbinary {
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Attaches a hexbinary::streambuf to a stream.  Unlike using a
+ * hexbinary::streambuf directly, this function will create the streambuf,
+ * attach it to the stream, and manage it for the lifetime of the stream
+ * automatically.
+ *
+ * @param ios The stream to attach the hexbinary::streambuf to.  If the stream
+ * already has a hexbinary::streambuf attached to it, this function does
+ * nothing.
+ */
+template<typename charT,class Traits> inline
+void attach( std::basic_ios<charT,Traits> &ios ) {
+  int const index = internal::hexbinary::get_streambuf_index();
+  void *&pword = ios.pword( index );
+  if ( !pword ) {
+    std::streambuf *const buf =
+      internal::hexbinary::alloc_streambuf( ios.rdbuf() );
+    ios.rdbuf( buf );
+    pword = buf;
+    ios.register_callback( internal::stream_callback, index );
+  }
+}
+
+/**
+ * Detaches a previously attached hexbinary::streambuf from a stream.  The
+ * streambuf is destroyed and the stream's original streambuf is restored.
+ *
+ * @param ios The stream to detach the hexbinary::streambuf from.  If the
+ * stream doesn't have a hexbinary::streambuf attached to it, this function
+ * does nothing.
+ */
+template<typename charT,class Traits> inline
+void detach( std::basic_ios<charT,Traits> &ios ) {
+  int const index = internal::hexbinary::get_streambuf_index();
+  if ( streambuf *const buf = static_cast<streambuf*>( ios.pword( index ) ) ) {
+    ios.pword( index ) = nullptr;
+    ios.rdbuf( buf->orig_streambuf() );
+    internal::dealloc_streambuf( buf );
+  }
+}
+
+/**
+ * Checks whether the given stream has a hexbinary::streambuf attached.
+ *
+ * @param ios The stream to check.
+ * @return \c true only if a hexbinary::streambuf is attached.
+ */
+template<typename charT,class Traits> inline
+bool is_attached( std::basic_ios<charT,Traits> &ios ) {
+  return !!ios.pword( internal::hexbinary::get_streambuf_index() );
+}
+
+/**
+ * A %hexbinary::auto_attach is a class that attaches a hexbinary::streambuf to
+ * a stream and automatically detaches it when the %auto_attach object is
+ * destroyed.
+ * \code
+ *  void f( ostream &os ) {
+ *    hexbinary::auto_attach<ostream> const raii( os );
+ *    // ...
+ *  }
+ * \endcode
+ * A %hexbinary::auto_attach is useful for streams not created by you.
+ *
+ * @see http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization
+ */
+template<class StreamType>
+class auto_attach {
+public:
+  /**
+   * Constructs an %auto_attach object calling attach() on the given stream.
+   *
+   * @param stream The stream to attach the hexbinary::streambuf to.  If the
+   * stream already has a hexbinary::streambuf attached to it, this contructor
+   * does nothing.
+   */
+  auto_attach( StreamType &stream ) : stream_( stream ) {
+    attach( stream );
+  }
+
+  /**
+   * Destroys this %auto_attach object calling detach() on the previously
+   * attached stream.
+   */
+  ~auto_attach() {
+    detach( stream_ );
+  }
+
+private:
+  StreamType &stream_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * A %hexbinary::stream is used to wrap a C++ standard I/O stream with a
+ * hexbinary::streambuf so that encoding/decoding and the management of the
+ * streambuf happens automatically.
+ *
+ * A %hexbinary::stream is useful for streams created by you.
+ *
+ * @tparam StreamType The I/O stream class type to wrap. It must be a concrete
+ * stream class.
+ */
+template<class StreamType>
+class stream : public StreamType {
+public:
+  /**
+   * Constructs a %hexbinary::stream.
+   */
+  stream() :
+#ifdef WIN32
+# pragma warning( push )
+# pragma warning( disable : 4355 )
+#endif /* WIN32 */
+    hb_buf_( this->rdbuf() )
+#ifdef WIN32
+# pragma warning( pop )
+#endif /* WIN32 */
+  {
+    init();
+  }
+
+  /**
+   * Constructs a %stream.
+   *
+   * @tparam StreamArgType The type of the first argument of \a StreamType's
+   * constructor.
+   * @param stream_arg The argument to pass as the first argument to
+   * \a StreamType's constructor.
+   */
+  template<typename StreamArgType>
+  stream( StreamArgType stream_arg ) :
+    StreamType( stream_arg ),
+#ifdef WIN32
+# pragma warning( push )
+# pragma warning( disable : 4355 )
+#endif /* WIN32 */
+    hb_buf_( this->rdbuf() )
+#ifdef WIN32
+# pragma warning( pop )
+#endif /* WIN32 */
+  {
+    init();
+  }
+
+  /**
+   * Constructs a %hexbinary::stream.
+   *
+   * @tparam StreamArgType The type of the first argument of \a StreamType's
+   * constructor.
+   * @param stream_arg The argument to pass as the first argument to
+   * \a StreamType's constructor.
+   * @param mode The open-mode to pass to \a StreamType's constructor.
+   */
+  template<typename StreamArgType>
+  stream( StreamArgType stream_arg, std::ios_base::openmode mode ) :
+    StreamType( stream_arg, mode ),
+#ifdef WIN32
+# pragma warning( push )
+# pragma warning( disable : 4355 )
+#endif /* WIN32 */
+    hb_buf_( this->rdbuf() )
+#ifdef WIN32
+# pragma warning( pop )
+#endif /* WIN32 */
+  {
+    init();
+  }
+
+private:
+  streambuf hb_buf_;
+
+  void init() {
+    this->std::ios::rdbuf( &hb_buf_ );
+  }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace hexbinary
+} // namespace zorba
+#endif  /* ZORBA_HEXBINARY_STREAM_API_H */
+/* vim:set et sw=2 ts=2: */

=== modified file 'src/api/CMakeLists.txt'
--- src/api/CMakeLists.txt	2013-05-31 03:38:45 +0000
+++ src/api/CMakeLists.txt	2013-06-20 00:55:33 +0000
@@ -25,6 +25,7 @@
     dynamiccontextimpl.cpp
     resultiteratorimpl.cpp
     zorba_string.cpp
+    hexbinary_streambuf.cpp
     itemfactoryimpl.cpp
     item.cpp
     identtypesimpl.cpp

=== modified file 'src/api/base64_streambuf.cpp'
--- src/api/base64_streambuf.cpp	2013-06-11 23:38:49 +0000
+++ src/api/base64_streambuf.cpp	2013-06-20 00:55:33 +0000
@@ -44,6 +44,11 @@
   plen_ = 0;
 }
 
+inline void streambuf::clear() {
+  resetg();
+  resetp();
+}
+
 inline void streambuf::writep() {
   char chunk[4];
   orig_buf_->sputn( chunk, base64::encode( pbuf_, plen_, chunk ) );
@@ -60,11 +65,6 @@
     writep();
 }
 
-void streambuf::clear() {
-  resetg();
-  resetp();
-}
-
 void streambuf::imbue( std::locale const &loc ) {
   orig_buf_->pubimbue( loc );
 }
@@ -153,7 +153,7 @@
 
   if ( streamsize const gsize = egptr() - gptr() ) {
     //
-    // Get any chunk fragment pending the the get buffer first.
+    // Get any chunk fragment pending in the get buffer first.
     //
     streamsize const n = min( gsize, size );
     traits_type::copy( to, gptr(), static_cast<size_t>( n ) );
@@ -198,7 +198,9 @@
 
   while ( size >= 3 ) {
     char ebuf[ Large_External_Buf_Size ];
-    streamsize const put = min( (streamsize)(sizeof ebuf), size );
+    static streamsize const esize =
+      (streamsize)base64::decoded_size( sizeof ebuf );
+    streamsize const put = min( esize, size );
     streamsize const encoded =
       base64::encode( from, static_cast<size_type>( put ), ebuf );
     orig_buf_->sputn( ebuf, encoded );

=== added file 'src/api/hexbinary_streambuf.cpp'
--- src/api/hexbinary_streambuf.cpp	1970-01-01 00:00:00 +0000
+++ src/api/hexbinary_streambuf.cpp	2013-06-20 00:55:33 +0000
@@ -0,0 +1,216 @@
+/*
+ * Copyright 2006-2008 The FLWOR Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "stdafx.h"
+
+#include <stdexcept>
+
+//#define ZORBA_DEBUG_HEXBINARY_STREAMBUF
+#ifdef ZORBA_DEBUG_HEXBINARY_STREAMBUF
+# include <stdio.h>
+#endif
+
+#include <zorba/util/hexbinary_stream.h>
+
+#include "util/hexbinary_util.h"
+
+using namespace std;
+
+namespace zorba {
+namespace hexbinary {
+
+int const Large_External_Buf_Size = 4096;
+
+///////////////////////////////////////////////////////////////////////////////
+
+inline void streambuf::clear() {
+  setg( gbuf_, gbuf_ + sizeof gbuf_, gbuf_ + sizeof gbuf_ );
+}
+
+streambuf::streambuf( std::streambuf *orig ) : orig_buf_( orig ) {
+  if ( !orig )
+    throw invalid_argument( "null streambuf" );
+  clear();
+}
+
+void streambuf::imbue( std::locale const &loc ) {
+  orig_buf_->pubimbue( loc );
+}
+
+streambuf::pos_type streambuf::seekoff( off_type o, ios_base::seekdir d,
+                                        ios_base::openmode m ) {
+  clear();
+  return orig_buf_->pubseekoff( o, d, m );
+}
+
+streambuf::pos_type streambuf::seekpos( pos_type p, ios_base::openmode m ) {
+  clear();
+  return orig_buf_->pubseekpos( p, m );
+}
+
+std::streambuf* streambuf::setbuf( char_type *p, streamsize s ) {
+  orig_buf_->pubsetbuf( p, s );
+  return this;
+}
+
+streamsize streambuf::showmanyc() {
+  return orig_buf_->in_avail();
+}
+
+streambuf::int_type streambuf::overflow( int_type c ) {
+#ifdef ZORBA_DEBUG_HEXBINARY_STREAMBUF
+  printf( "overflow()\n" );
+#endif
+  bool const is_eof = traits_type::eq_int_type( c, traits_type::eof() );
+  if ( !is_eof ) {
+    char const p = traits_type::to_char_type( c );
+    char ebuf[2];
+    orig_buf_->sputn( ebuf, hexbinary::encode( &p, 1, ebuf ) );
+  }
+  return c;
+}
+
+streambuf::int_type streambuf::pbackfail( int_type c ) {
+  if ( !traits_type::eq_int_type( c, traits_type::eof() ) &&
+       gptr() > eback() ) {
+    c = orig_buf_->sputbackc( traits_type::to_char_type( c ) );
+    if ( !traits_type::eq_int_type( c, traits_type::eof() ) )
+      gbump( -1 );
+    return c;
+  }
+  return traits_type::eof();
+}
+
+streambuf::int_type streambuf::underflow() {
+#ifdef ZORBA_DEBUG_HEXBINARY_STREAMBUF
+  printf( "underflow()\n" );
+#endif
+  char chunk[2];
+  int chunk_len = 0;
+
+  while ( gptr() >= egptr() ) {
+    int_type const c = orig_buf_->sbumpc();
+    bool is_eof = false;
+    if ( traits_type::eq_int_type( c, traits_type::eof() ) ) {
+      if ( !chunk_len )
+        return traits_type::eof();
+      is_eof = true;
+    } else {
+      chunk[ chunk_len++ ] = traits_type::to_char_type( c );
+    }
+    if ( chunk_len == sizeof chunk || (is_eof && chunk_len) ) {
+      streamsize const n = hexbinary::decode( chunk, chunk_len, eback() );
+      setg( gbuf_, gbuf_, gbuf_ + n );
+    }
+  }
+  return traits_type::to_int_type( *gptr() );
+}
+
+streamsize streambuf::xsgetn( char_type *to, streamsize size ) {
+#ifdef ZORBA_DEBUG_HEXBINARY_STREAMBUF
+  printf( "xsgetn()\n" );
+#endif
+  streamsize return_size = 0;
+
+  if ( streamsize const gsize = egptr() - gptr() ) {
+    //
+    // Get any chunk fragment pending in the get buffer first.
+    //
+    streamsize const n = min( gsize, size );
+    traits_type::copy( to, gptr(), static_cast<size_t>( n ) );
+    gbump( static_cast<int>( n ) );
+    to += n;
+    size -= n, return_size += n;
+  }
+
+  //
+  // Must get bytes in terms of encoded size.
+  //
+  size = hexbinary::encoded_size( static_cast<size_type>( size ) );
+
+  while ( size ) {
+    char ebuf[ Large_External_Buf_Size ];
+    streamsize const get = min( (streamsize)(sizeof ebuf), size );
+    if ( streamsize got = orig_buf_->sgetn( ebuf, get ) ) {
+      streamsize const decoded =
+        hexbinary::decode( ebuf, static_cast<size_type>( got ), to );
+      to += decoded;
+      size -= got, return_size += decoded;
+    } else
+      break;
+  }
+
+  return return_size;
+}
+
+streamsize streambuf::xsputn( char_type const *from, streamsize size ) {
+#ifdef ZORBA_DEBUG_HEXBINARY_STREAMBUF
+  printf( "xsputn()\n" );
+#endif
+  streamsize return_size = 0;
+
+  while ( size ) {
+    char ebuf[ Large_External_Buf_Size ];
+    static streamsize const esize =
+      (streamsize)hexbinary::decoded_size( sizeof ebuf );
+    streamsize const put = min( esize, size );
+    streamsize const encoded =
+      hexbinary::encode( from, static_cast<size_type>( put ), ebuf );
+    orig_buf_->sputn( ebuf, encoded );
+    from += put, size -= put, return_size += put;
+  }
+
+  return return_size;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace hexbinary
+
+namespace internal {
+namespace hexbinary {
+
+// Both new & delete are done inside Zorba rather than in the header to
+// guarantee that they're cross-DLL-boundary safe on Windows.
+
+std::streambuf* alloc_streambuf( std::streambuf *orig ) {
+  return new zorba::hexbinary::streambuf( orig );
+}
+
+int get_streambuf_index() {
+  //
+  // This function is out-of-line because it has a static constant within it.
+  // It has a static constant within it to guarantee (1) initialization before
+  // use and (2) initialization happens exactly once.
+  //
+  // See: "Standard C++ IOStreams and Locales: Advanced Programmer's Guide and
+  // Reference," Angelika Langer and Klaus Kreft, Addison-Wesley, 2000, section
+  // 3.3.1.1: "Initializing and Maintaining the iword/pword Index."
+  //
+  // See: "The C++ Programming Language," Bjarne Stroustrup, Addison-Wesley,
+  // 2000, section 10.4.8: "Local Static Store."
+  //
+  static int const index = ios_base::xalloc();
+  return index;
+}
+
+} // namespace hexbinary
+} // namespace internal
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace zorba
+/* vim:set et sw=2 ts=2: */

=== modified file 'src/unit_tests/CMakeLists.txt'
--- src/unit_tests/CMakeLists.txt	2013-05-31 03:38:45 +0000
+++ src/unit_tests/CMakeLists.txt	2013-06-20 00:55:33 +0000
@@ -20,6 +20,7 @@
   test_fs_util.cpp
   test_hashmaps.cpp
   test_hexbinary.cpp
+  test_hexbinary_streambuf.cpp
   test_json_parser.cpp
   test_mem_sizeof.cpp
   test_parameters.cpp

=== added file 'src/unit_tests/test_hexbinary_streambuf.cpp'
--- src/unit_tests/test_hexbinary_streambuf.cpp	1970-01-01 00:00:00 +0000
+++ src/unit_tests/test_hexbinary_streambuf.cpp	2013-06-20 00:55:33 +0000
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2006-2008 The FLWOR Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "stdafx.h"
+#include <fstream>
+#include <iostream>
+#include <sstream>
+
+#include <zorba/util/hexbinary_stream.h>
+
+using namespace std;
+using namespace zorba;
+
+struct test {
+  char const *raw_str;
+  char const *hb_str;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static int failures;
+
+static bool assert_true( int no, char const *expr, int line, bool result ) {
+  if ( !result ) {
+    cout << '#' << no << " FAILED, line " << line << ": " << expr << endl;
+    ++failures;
+  }
+  return result;
+}
+
+static void print_exception( int no, char const *expr, int line,
+                             std::exception const &e ) {
+  assert_true( no, expr, line, false );
+  cout << "+ exception: " << e.what() << endl;
+}
+
+#define ASSERT_TRUE( NO, EXPR ) assert_true( NO, #EXPR, __LINE__, !!(EXPR) )
+
+#define ASSERT_TRUE_AND_NO_EXCEPTION( NO, EXPR ) \
+  try { ASSERT_TRUE( NO, EXPR ); } \
+  catch ( exception const &e ) { print_exception( NO, #EXPR, __LINE__, e ); } \
+  catch ( ... ) { assert_true( NO, #EXPR, __LINE__, false ); }
+
+///////////////////////////////////////////////////////////////////////////////
+
+static bool test_getline( test const *t ) {
+  string const hb_str( t->hb_str );
+  istringstream iss( hb_str );
+  hexbinary::streambuf hb_sbuf( iss.rdbuf() );
+  iss.ios::rdbuf( &hb_sbuf );
+
+  char raw_buf[ 1024 ];
+  iss.getline( raw_buf, sizeof raw_buf );
+  if ( iss.gcount() ) {
+    string const raw_str( raw_buf );
+    return raw_str == t->raw_str;
+  }
+  return false;
+}
+
+static bool test_read( test const *t ) {
+  string const hb_str( t->hb_str );
+  istringstream iss( hb_str );
+  hexbinary::streambuf hb_sbuf( iss.rdbuf() );
+  iss.ios::rdbuf( &hb_sbuf );
+
+  char raw_buf[ 1024 ];
+  iss.read( raw_buf, sizeof raw_buf );
+  if ( iss.gcount() ) {
+    string const raw_str(
+      raw_buf, static_cast<string::size_type>( iss.gcount() )
+    );
+    return raw_str == t->raw_str;
+  }
+  return false;
+}
+
+static bool test_insertion( test const *t ) {
+  ostringstream oss;
+  hexbinary::streambuf hb_sbuf( oss.rdbuf() );
+  oss.ios::rdbuf( &hb_sbuf );
+
+  oss << t->raw_str << flush;
+  string const hb_str( oss.str() );
+
+  string const expected_hb_str( t->hb_str );
+  return hb_str == expected_hb_str;
+}
+
+static bool test_put( test const *t ) {
+  ostringstream oss;
+  { // local scope
+  hexbinary::auto_attach<ostringstream> const raii( oss );
+
+  for ( char const *c = t->raw_str; *c; ++c )
+    oss.put( *c );
+  } // local scope
+  string const hb_str( oss.str() );
+
+  string const expected_hb_str( t->hb_str );
+  return hb_str == expected_hb_str;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static test const tests[] = {
+  /* 0 */ { "Now", "4E6F77" },
+  /* 1 */ { "Now is the time", "4E6F77206973207468652074696D65" },
+  { 0, 0 }
+};
+
+namespace zorba {
+namespace UnitTests {
+
+int test_hexbinary_streambuf( int, char*[] ) {
+  int test_no = 0;
+  for ( test const *t = tests; t->raw_str; ++t, ++test_no ) {
+    ASSERT_TRUE_AND_NO_EXCEPTION( test_no, test_getline( t ) );
+    ASSERT_TRUE_AND_NO_EXCEPTION( test_no, test_read( t ) );
+    ASSERT_TRUE_AND_NO_EXCEPTION( test_no, test_insertion( t ) );
+    ASSERT_TRUE_AND_NO_EXCEPTION( test_no, test_put( t ) );
+  }
+  cout << failures << " test(s) failed\n";
+  return failures ? 1 : 0;
+}
+
+} // namespace UnitTests
+} // namespace zorba
+/* vim:set et sw=2 ts=2: */

=== modified file 'src/unit_tests/unit_test_list.h'
--- src/unit_tests/unit_test_list.h	2013-05-31 03:38:45 +0000
+++ src/unit_tests/unit_test_list.h	2013-06-20 00:55:33 +0000
@@ -34,6 +34,7 @@
   int test_fs_util( int, char*[] );
   int test_hashmaps( int argc, char* argv[] );
   int test_hexbinary( int argc, char* argv[] );
+  int test_hexbinary_streambuf( int argc, char* argv[] );
 
 #ifndef ZORBA_NO_ICU
   int test_icu_streambuf( int, char*[] );

=== modified file 'src/unit_tests/unit_tests.cpp'
--- src/unit_tests/unit_tests.cpp	2013-06-12 13:19:13 +0000
+++ src/unit_tests/unit_tests.cpp	2013-06-20 00:55:33 +0000
@@ -45,6 +45,7 @@
   libunittests["fs_util"] = test_fs_util;
   libunittests["hashmaps"] = test_hashmaps;
   libunittests["hexbinary"] = test_hexbinary;
+  libunittests["hexbinary_streambuf"] = test_hexbinary_streambuf;
 
 #ifndef ZORBA_NO_ICU
   libunittests["icu_streambuf"] = test_icu_streambuf;

-- 
Mailing list: https://launchpad.net/~zorba-coders
Post to     : zorba-coders@lists.launchpad.net
Unsubscribe : https://launchpad.net/~zorba-coders
More help   : https://help.launchpad.net/ListHelp

Reply via email to