Matthias Brantner has proposed merging lp:~zorba-coders/zorba/ft-base64Binary 
into lp:zorba.

Requested reviews:
  Zorba Coders (zorba-coders)

For more details, see:
https://code.launchpad.net/~zorba-coders/zorba/ft-base64Binary/+merge/94074

more efficient implementation of xs:base64Binary items
-- 
https://code.launchpad.net/~zorba-coders/zorba/ft-base64Binary/+merge/94074
Your team Zorba Coders is requested to review the proposed merge of 
lp:~zorba-coders/zorba/ft-base64Binary into lp:zorba.
=== modified file 'ChangeLog'
--- ChangeLog	2012-02-21 19:20:00 +0000
+++ ChangeLog	2012-02-22 01:40:29 +0000
@@ -20,6 +20,7 @@
   * Fixed bug #911585 (management of variables during eval)
   * Fixed bug #866423 (fn:empty and fn:exists iterators must reset their input in
     case of early-out)
+  * More efficient implementation for base64Binary items
   * Added index management function to the C++ api's StaticCollectionManager.
   * Fixed bug #872288 (reset recursive flag during node rename)
   * Fixed bug #905041 (allow for the default element and function namespaces to be

=== modified file 'include/zorba/item.h'
--- include/zorba/item.h	2012-01-11 17:30:25 +0000
+++ include/zorba/item.h	2012-02-22 01:40:29 +0000
@@ -363,6 +363,14 @@
   std::istream&
   getStream();
 
+  /**
+   * Returns true if the contents of a binary item is already encoded
+   * 
+   * @return true if the content is already encoded, false otherwise
+   */
+  bool
+  isEncoded() const;
+
   /** \brief Returns the name of the collection this node is stored in.
    *
    * @return The name of the collection or 0 if the given item is not

=== modified file 'include/zorba/item_factory.h'
--- include/zorba/item_factory.h	2012-01-11 17:30:25 +0000
+++ include/zorba/item_factory.h	2012-02-22 01:40:29 +0000
@@ -149,6 +149,25 @@
       virtual Item 
       createBase64Binary(const unsigned char* aBinData, size_t aLength) = 0;
 
+      /** \brief Creates a streamable Base64Binary Item
+       *         see [http://www.w3.org/TR/xmlschema-2/#base64Binary]
+       *
+       * @param stream An istream whence to read the binary's content.
+       * @param streamReleaser A function pointer which is invoked once
+       *        the StreamableBase64Binary is destroyed. Normally this function
+       *        will delete the std::istream object passed to it.
+       * @param seekable is the given stream seekable
+       * @param encoded is the contents of the given stream already base64
+       *        encoded
+       * @return The streamable String Item
+       */
+      virtual Item
+      createStreamableBase64Binary(
+          std::istream &stream,
+          StreamReleaser streamReleaser,
+          bool seekable = false,
+          bool encoded = false) = 0;
+
       /** \brief Creates a Boolean Item
        *         see [http://www.w3.org/TR/xmlschema-2/#bool]
        *

=== modified file 'modules/com/zorba-xquery/www/modules/converters/base64.xq'
--- modules/com/zorba-xquery/www/modules/converters/base64.xq	2011-08-01 10:18:53 +0000
+++ modules/com/zorba-xquery/www/modules/converters/base64.xq	2012-02-22 01:40:29 +0000
@@ -28,14 +28,17 @@
 declare namespace ver = "http://www.zorba-xquery.com/options/versioning";;
 declare option ver:module-version "2.0";
 
-
 (:~
  : Decode a xs:base64Binary.
  :
+ : The function assumes that the content after decoding is valid
+ : UTF-8. 
+ :
  : @param $base64 The xs:base64Binary item to decode
- : @return the decoded xs:base64Binary item as string
+ : @return the base64 decoded value as string
  :)
-declare function base64:decode($base64 as xs:base64Binary) as xs:string external;
+declare function base64:decode($base64 as xs:base64Binary)
+as xs:string external;
 
 (:~
  : Encode a xs:string as xs:base64Binary.

=== modified file 'modules/org/expath/ns/file.xq.src/file.cpp'
--- modules/org/expath/ns/file.xq.src/file.cpp	2012-02-16 14:11:02 +0000
+++ modules/org/expath/ns/file.xq.src/file.cpp	2012-02-22 01:40:29 +0000
@@ -144,19 +144,14 @@
   // actual read
   Item lItem;
   try {
-    std::ifstream lInStream;
-    lFile->openInputStream(lInStream, true, false);
-
-    std::stringstream lStrStream;
-    char lBuf[1024];
-    while (!lInStream.eof()) {
-      lInStream.read(lBuf, 1024);
-      lStrStream.write(lBuf, lInStream.gcount());
-    }  
-
-    String lContent(lStrStream.str());
-    String lEncodedContent = encoding::Base64::encode(lContent);
-    lItem = theModule->getItemFactory()->createBase64Binary(lEncodedContent.data(), lEncodedContent.size());
+    std::unique_ptr<std::ifstream> lInStream;
+    lInStream.reset( new std::ifstream() );
+    lFile->openInputStream(*lInStream.get(), true, false);
+
+    lItem = theModule->getItemFactory()->createStreamableBase64Binary(
+        *lInStream.release(), &FileModule::streamReleaser, true
+      );
+
   } catch (ZorbaException& ze) {
     std::stringstream lSs;
     lSs << "An unknown error occured: " << ze.what() << "Can not read file";

=== modified file 'modules/org/expath/ns/file.xq.src/file_function.cpp'
--- modules/org/expath/ns/file.xq.src/file_function.cpp	2012-02-16 14:11:02 +0000
+++ modules/org/expath/ns/file.xq.src/file_function.cpp	2012-02-22 01:40:29 +0000
@@ -26,6 +26,7 @@
 #include <zorba/user_exception.h>
 #include <zorba/util/path.h>
 #include <zorba/xquery_functions.h>
+#include <zorba/singleton_item_sequence.h>
 #include <zorba/zorba.h>
 
 #include "file_module.h"
@@ -255,10 +256,25 @@
 
     // if this is a binary write
     if (lBinary) {
-      Zorba_SerializerOptions lOptions;
-      lOptions.ser_method = ZORBA_SERIALIZATION_METHOD_BINARY;
-      Serializer_t lSerializer = Serializer::createSerializer(lOptions);
-      lSerializer->serialize(aArgs[1], lOutStream);
+      Item lBinaryItem;
+      Iterator_t lContentSeq = aArgs[1]->getIterator();
+      lContentSeq->open();
+      while (lContentSeq->next(lBinaryItem))
+      {
+        if (lBinaryItem.isStreamable() && !lBinaryItem.isEncoded())
+        {
+          lOutStream << lBinaryItem.getStream().rdbuf();
+        }
+        else
+        {
+          Zorba_SerializerOptions lOptions;
+          lOptions.ser_method = ZORBA_SERIALIZATION_METHOD_BINARY;
+          Serializer_t lSerializer = Serializer::createSerializer(lOptions);
+          SingletonItemSequence lSeq(lBinaryItem);
+          lSerializer->serialize(&lSeq, lOutStream);
+        }
+
+      }
     }
     // if we only write text
     else {

=== modified file 'src/api/item.cpp'
--- src/api/item.cpp	2012-02-02 09:56:52 +0000
+++ src/api/item.cpp	2012-02-22 01:40:29 +0000
@@ -470,6 +470,17 @@
   // TODO: throw exception
 }
 
+bool
+Item::isEncoded() const
+{
+  ITEM_TRY
+    SYNC_CODE(AutoLock lock(GENV_STORE.getGlobalLock(), Lock::READ);)
+
+    return m_item->isEncoded();
+  ITEM_CATCH
+  // TODO: throw exception
+}
+
 Item
 Item::getCollectionName() const
 {

=== modified file 'src/api/itemfactoryimpl.cpp'
--- src/api/itemfactoryimpl.cpp	2012-01-11 17:30:25 +0000
+++ src/api/itemfactoryimpl.cpp	2012-02-22 01:40:29 +0000
@@ -212,13 +212,32 @@
   std::stringstream lSs;
   while (aEncodedStream.good()) 
   {
-    lSs.put(aEncodedStream.get());
+    char c = aEncodedStream.get();
+    if (aEncodedStream.good())
+    {
+      lSs.put(c);
+    }
   }
   std::string lContent = lSs.str();
   return createBase64Binary(lContent.c_str(), lContent.size());
 }
 
 
+Item
+ItemFactoryImpl::createStreamableBase64Binary(
+    std::istream &stream,
+    StreamReleaser streamReleaser,
+    bool seekable,
+    bool encoded)
+{
+  store::Item_t lItem;
+  theItemFactory->createStreamableBase64Binary(
+      lItem, stream, streamReleaser, seekable, encoded
+    );
+  return &*lItem;
+}
+
+
 Item ItemFactoryImpl::createBoolean(bool aValue)
 {
   store::Item_t lItem;

=== modified file 'src/api/itemfactoryimpl.h'
--- src/api/itemfactoryimpl.h	2012-01-11 17:30:25 +0000
+++ src/api/itemfactoryimpl.h	2012-02-22 01:40:29 +0000
@@ -69,6 +69,13 @@
       virtual Item 
       createBase64Binary(const unsigned char* aBinData, size_t aLength);
 
+      virtual Item
+      createStreamableBase64Binary(
+          std::istream &stream,
+          StreamReleaser streamReleaser,
+          bool seekable = false,
+          bool encoded = false);
+
       virtual Item 
       createBoolean(bool aValue);
     

=== modified file 'src/api/options.cpp'
--- src/api/options.cpp	2012-01-11 17:30:25 +0000
+++ src/api/options.cpp	2012-02-22 01:40:29 +0000
@@ -63,6 +63,7 @@
     else if (strcmp(value, "html") == 0) ser_method = ZORBA_SERIALIZATION_METHOD_HTML;
     else if (strcmp(value, "xhtml") == 0) ser_method = ZORBA_SERIALIZATION_METHOD_XHTML;
     else if (strcmp(value, "text") == 0) ser_method = ZORBA_SERIALIZATION_METHOD_TEXT;
+    else if (strcmp(value, "binary") == 0) ser_method = ZORBA_SERIALIZATION_METHOD_BINARY;
     else
     {
       ; // TODO signal errors for incorrect values?

=== modified file 'src/api/serialization/serializer.cpp'
--- src/api/serialization/serializer.cpp	2012-01-11 17:30:25 +0000
+++ src/api/serialization/serializer.cpp	2012-02-22 01:40:29 +0000
@@ -368,22 +368,50 @@
 void serializer::emitter::emit_streamable_item(store::Item* item)
 {
   // Streamable item
-  char buffer[1024];
-  int rollover = 0;
-  std::streambuf *  pbuf;
-  std::streamsize   read_bytes;
-  std::istream& is = item->getStream();
-
-  // read bytes and do string expansion
-  do
-  {
-    //std::istream::read uses a try/catch internally so the Zorba_Exception is lost: that is why we are using std::streambuf::sgetn
-    pbuf = is.rdbuf();
-    read_bytes = pbuf->sgetn(buffer + rollover, 1024 - rollover);
-    rollover = emit_expanded_string(buffer, static_cast<zstring::size_type>(read_bytes + rollover));
-    memmove(buffer, buffer + 1024 - rollover, rollover);
-  }
-  while (read_bytes > 0);
+  store::SchemaTypeCode lTypeCode = item->getTypeCode();
+
+  switch (lTypeCode)
+  {
+  case store::XS_STRING:
+  {
+    char buffer[1024];
+    int rollover = 0;
+    std::streambuf *  pbuf;
+    std::streamsize   read_bytes;
+    std::istream& is = item->getStream();
+
+    // read bytes and do string expansion
+    do
+    {
+      //std::istream::read uses a try/catch internally so the Zorba_Exception is lost: that is why we are using std::streambuf::sgetn
+      pbuf = is.rdbuf();
+      read_bytes = pbuf->sgetn(buffer + rollover, 1024 - rollover);
+      rollover = emit_expanded_string(buffer, static_cast<zstring::size_type>(read_bytes + rollover));
+      memmove(buffer, buffer + 1024 - rollover, rollover);
+    }
+    while (read_bytes > 0);
+    break;
+  }
+  case store::XS_BASE64BINARY:
+  {
+    if (item->isEncoded())
+    {
+      std::istream& is = item->getStream();
+      char buf[1024];
+      while (is.good())
+      {
+        is.read(buf, 1024);
+        tr.write(buf, is.gcount());
+      }
+    }
+    else
+    {
+      tr << item->getStringValue();
+    }
+    break;
+  }
+  default: assert(false);
+  }
 
 }
 
@@ -1866,30 +1894,38 @@
 ********************************************************************************/
 void serializer::binary_emitter::emit_item(store::Item* item)
 {
-  xs_base64Binary lValue;
-
-  // First assume the item is a base64Binary item and try to get its value.
-  try
-  {
-    lValue = item->getBase64BinaryValue();
-  }
-  catch (...)
-  {
-    // If this fails, then just get the string value of the item and convert
-    // it to base64
-    zstring lStringValue;
-    item->getStringValue2(lStringValue);
-    Base64::encode(lStringValue, lValue);
-  }
-
-  std::vector<char> lDecodedData;
-  lValue.decode(lDecodedData);
-
-  for (std::vector<char>::const_iterator lIter = lDecodedData.begin();
-       lIter != lDecodedData.end();
-       ++lIter)
-  {
-    tr << *lIter;
+  if (item->isStreamable())
+  {
+    std::istream& stream = item->getStream();
+    if (item->isEncoded())
+    {
+      tr << Base64::decode(stream);
+    }
+    else
+    {
+      char buf[1024];
+      while (!stream.eof())
+      {
+        stream.read(buf, 1024);
+        tr.write(buf, stream.gcount());
+      }
+    }
+  }
+  else
+  {
+    char* value;
+    size_t len = item->getBase64BinaryValue(value);
+
+    if (item->isEncoded())
+    {
+      std::stringstream tmp;
+      tmp.write(value, len);
+      tr << Base64::decode(tmp);
+    }
+    else
+    {
+      tr.write(value, len);
+    }
   }
 }
 

=== modified file 'src/runtime/base64/base64_impl.cpp'
--- src/runtime/base64/base64_impl.cpp	2011-06-14 17:26:33 +0000
+++ src/runtime/base64/base64_impl.cpp	2012-02-22 01:40:29 +0000
@@ -22,37 +22,74 @@
 
 #include "runtime/base64/base64.h"
 
-
 #include "store/api/item.h"
 #include "store/api/item_factory.h"
 
 namespace zorba {
 
-bool Base64DecodeIterator::nextImpl(store::Item_t& result, PlanState& planState) const
+bool Base64DecodeIterator::nextImpl(
+    store::Item_t& result,
+    PlanState& planState) const
 {
   store::Item_t lItem;
-  Base64 lDecodedData;
   zstring lResultString;
+  char* lContent;
+  size_t lSize;
+  result = NULL;
 
   PlanIteratorState *state;
   DEFAULT_STACK_INIT(PlanIteratorState, state, planState);
 
-  if (consumeNext(lItem, theChildren[0].getp(), planState))
-  {
-    lDecodedData = lItem->getBase64BinaryValue();
-    lResultString = lDecodedData.decode().str();
+  consumeNext(lItem, theChildren[0].getp(), planState);
+
+  if (lItem->isStreamable())
+  {
+    if (lItem->isEncoded())
+    {
+      // decode and eventually transcode
+      lResultString = Base64::decode(lItem->getStream());
+    }
+    else
+    {
+      // streamable string eventually transcoding
+      GENV_ITEMFACTORY->createStreamableString(
+          result,
+          lItem->getStream(),
+          lItem->getStreamReleaser(),
+          lItem->isSeekable());
+    }
+  }
+  else
+  {
+    lSize = lItem->getBase64BinaryValue(lContent);
+
+    if (lItem->isEncoded())
+    {
+      std::vector<char> encoded(lContent, lContent+lSize);
+      std::vector<char> decoded;
+      Base64::decode(encoded, decoded);
+      lResultString.insert(0, &decoded[0], decoded.size());
+    }
+    else
+    {
+      lResultString.insert(0, lContent, lSize);
+    }
+  }
+  if (!result) // otherwise it's a streamable string already
+  {
     GENV_ITEMFACTORY->createString(result, lResultString);
-    STACK_PUSH (true, state);
   }
+  STACK_PUSH (true, state);
 
   STACK_END (state);
 }
 
 
-bool Base64EncodeIterator::nextImpl(store::Item_t& result, PlanState& planState) const
+bool Base64EncodeIterator::nextImpl(
+    store::Item_t& result,
+    PlanState& planState) const
 {
   store::Item_t lItem;
-  Base64        lBase64;
   zstring       lTmpString;
 
   PlanIteratorState* state;
@@ -61,17 +98,12 @@
   if (consumeNext(lItem, theChildren[0].getp(), planState)) 
   {
     lItem->getStringValue2(lTmpString);
-    Base64::encode(lTmpString, lBase64);
-    if (GENV_ITEMFACTORY->createBase64Binary(result, lBase64)) 
-    {
-      STACK_PUSH (true, state);
-    }
-    else
-    {
-      throw XQUERY_EXCEPTION(
-        zerr::ZXQP0025_ITEM_CREATION_FAILED, ERROR_LOC( loc )
-      );
-    } 
+    // create a base64Binary item
+    // the content is the non-encoded string
+    GENV_ITEMFACTORY->createBase64Binary(
+          result, lTmpString.c_str(), lTmpString.size(), false
+        );
+    STACK_PUSH (true, state);
   }
   STACK_END (state);
 }

=== modified file 'src/store/api/item.h'
--- src/store/api/item.h	2012-02-07 15:53:23 +0000
+++ src/store/api/item.h	2012-02-22 01:40:29 +0000
@@ -274,7 +274,14 @@
 
   /** Accessor for xs:base64Binary
    */
-  virtual xs_base64Binary getBase64BinaryValue() const;
+  virtual size_t getBase64BinaryValue(char*& data) const;
+
+  /**
+   * Checks whether a base64 item's content is already encoded
+   *
+   * @return true only if it is.
+   */
+  virtual bool isEncoded() const;
 
   /** Accessor for xs:boolean
    */

=== modified file 'src/store/api/item_factory.h'
--- src/store/api/item_factory.h	2011-12-21 14:40:33 +0000
+++ src/store/api/item_factory.h	2012-02-22 01:40:29 +0000
@@ -205,6 +205,31 @@
   virtual bool createBase64Binary(Item_t& result, xs_base64Binary value) = 0;
 
   /**
+   * Specification: [http://www.w3.org/TR/xmlschema-2/#base64Binary]
+   * creates a base64Binary item with the given content
+   * the encoded flag specifies whether the given content is already
+   * base64 encoded or not.
+   */
+  virtual bool createBase64Binary(
+      Item_t& result,
+      const char* value,
+      size_t size,
+      bool encoded) = 0;
+
+  /**
+   * Specification: [http://www.w3.org/TR/xmlschema-2/#base64Binary]
+   * the encoded flag specifies whether the given content is already
+   * base64 encoded or not.
+   */
+  virtual bool createStreamableBase64Binary(
+      Item_t& result,
+      std::istream&,
+      StreamReleaser,
+      bool seekable = false,
+      bool encoded = false) = 0;
+
+
+  /**
    * Specification: [http://www.w3.org/TR/xmlschema-2/#bool]
    * @param value
    */

=== modified file 'src/store/naive/atomic_items.cpp'
--- src/store/naive/atomic_items.cpp	2012-02-15 10:25:02 +0000
+++ src/store/naive/atomic_items.cpp	2012-02-22 01:40:29 +0000
@@ -3059,6 +3059,45 @@
 /*******************************************************************************
   class Base64BinaryItem
 ********************************************************************************/
+bool
+Base64BinaryItem::equals(
+      const store::Item* other,
+      long timezone,
+      const XQPCollator* aCollation) const
+{
+  if (isEncoded() == other->isEncoded())
+  {
+    char* this_data, *other_data;
+    size_t this_size = getBase64BinaryValue(this_data);
+    size_t other_size = other->getBase64BinaryValue(other_data);
+    return this_size == other_size &&
+      memcmp(this_data, other_data, this_size) == 0;
+  }
+  else
+  {
+    return getStringValue().compare(other->getStringValue()) == 0;
+  }
+}
+
+
+uint32_t
+Base64BinaryItem::hash(long timezone, const XQPCollator* aCollation) const
+{
+  // always need to hash on the string-value because otherwise
+  // a base64 item that is encoded would have a different hash-value
+  // as a base64 item that is decoded but represents the same binary content
+  return utf8::hash(getStringValue(), aCollation);
+}
+
+
+size_t
+Base64BinaryItem::getBase64BinaryValue(char*& data) const
+{
+  data = const_cast<char*>(&theValue[0]);
+  return theValue.size();
+}
+
+
 store::Item* Base64BinaryItem::getType() const
 {
   return GET_STORE().theSchemaTypeNames[store::XS_BASE64BINARY];
@@ -3067,19 +3106,51 @@
 
 zstring Base64BinaryItem::getStringValue() const
 {
-  return theValue.str();
+  if (theIsEncoded)
+  {
+    zstring tmp(&theValue[0], theValue.size());
+    return tmp;
+  }
+  else
+  {
+    std::vector<char> encoded;
+    encoded.reserve(theValue.size());
+    Base64::encode(theValue, encoded);
+    zstring tmp(&encoded[0], encoded.size());
+    return tmp;
+  }
 }
 
 
 void Base64BinaryItem::getStringValue2(zstring& val) const
 {
-  val = theValue.str();
+  if (theIsEncoded)
+  {
+    val.insert(0, &theValue[0], theValue.size());
+  }
+  else
+  {
+    std::vector<char> encoded;
+    encoded.reserve(theValue.size());
+    Base64::encode(theValue, encoded);
+    val.insert(0, &encoded[0], encoded.size());
+  }
 }
 
 
 void Base64BinaryItem::appendStringValue(zstring& buf) const
 {
-  buf += theValue.str();
+  if (theIsEncoded)
+  {
+    buf.insert(buf.size(), &theValue[0], theValue.size());
+  }
+  else
+  {
+    std::vector<char> encoded;
+    encoded.reserve(theValue.size());
+    Base64::encode(theValue, encoded);
+    buf.insert(buf.size(), &encoded[0], encoded.size());
+  }
 }
 
 
@@ -3092,9 +3163,155 @@
 }
 
 
-uint32_t Base64BinaryItem::hash(long timezone, const XQPCollator* aCollation) const
-{
-  return theValue.hash();
+/*******************************************************************************
+  class StreamableStringItem
+********************************************************************************/
+zstring StreamableBase64BinaryItem::getStringValue() const
+{
+  if (!theIsMaterialized)
+  {
+    materialize();
+  }
+  return Base64BinaryItem::getStringValue();
+}
+
+
+void StreamableBase64BinaryItem::getStringValue2(zstring& val) const
+{
+  if (!theIsMaterialized)
+  {
+    materialize();
+  }
+  Base64BinaryItem::getStringValue2(val);
+}
+
+
+void StreamableBase64BinaryItem::appendStringValue(zstring& buf) const
+{
+  if (!theIsMaterialized)
+  {
+    materialize();
+  }
+  Base64BinaryItem::appendStringValue(buf);
+}
+
+
+zstring StreamableBase64BinaryItem::show() const
+{
+  if (!theIsMaterialized)
+  {
+    materialize();
+  }
+  zstring res("xs:base64Binary(");
+  appendStringValue(res);
+  res += ")";
+  return res;
+}
+
+
+uint32_t
+StreamableBase64BinaryItem::hash(long timezone, const XQPCollator* aCollation) const
+{
+  if (!theIsMaterialized)
+  {
+    materialize();
+  }
+  return Base64BinaryItem::hash(timezone, aCollation);
+}
+
+
+size_t
+StreamableBase64BinaryItem::getBase64BinaryValue(char*& data) const
+{
+  if (!theIsMaterialized)
+  {
+    materialize();
+  }
+  return Base64BinaryItem::getBase64BinaryValue(data);
+}
+
+
+bool StreamableBase64BinaryItem::isStreamable() const
+{
+  return true;
+}
+
+
+bool StreamableBase64BinaryItem::isSeekable() const
+{
+  return theIsSeekable;
+}
+
+
+StreamReleaser StreamableBase64BinaryItem::getStreamReleaser()
+{
+  return theStreamReleaser;
+}
+
+
+void StreamableBase64BinaryItem::setStreamReleaser(StreamReleaser aReleaser)
+{
+  theStreamReleaser = aReleaser;
+}
+
+
+std::istream& StreamableBase64BinaryItem::getStream()
+{
+  // a non-seekable stream can only be consumed once
+  // we raise an error if getStream is called twice
+  // if a query requires a stream to be consumed more than once,
+  // the query needs to make sure that the stream is explicitly
+  // materialized before
+  if (!theIsSeekable && theIsConsumed) 
+  {
+    throw ZORBA_EXCEPTION( zerr::ZSTR0055_STREAMABLE_STRING_CONSUMED );
+  }
+  else
+  {
+    // if the stream is seekable, we seek to the beginning.
+    // We are not using theIstream.seekg because the USER_ERROR that is thrown
+    // by Zorba is lost possibly in an internal try/catch of the seekg
+    std::streambuf * pbuf;
+    pbuf = theIstream.rdbuf();
+    pbuf->pubseekoff(0, std::ios::beg);
+  }
+  theIsConsumed = true;
+  return theIstream;
+}
+
+
+void StreamableBase64BinaryItem::materialize() const
+{
+  StreamableBase64BinaryItem* const s
+    = const_cast<StreamableBase64BinaryItem*>(this);
+  std::istream& lStream = s->getStream();
+
+  s->theIsMaterialized = true;
+  s->theIsConsumed = true;
+
+  if (isSeekable())
+  {
+    lStream.seekg(0, std::ios::end);
+    size_t len = lStream.tellg();
+    lStream.seekg(0, std::ios::beg);
+    s->theValue.reserve(len);
+    char buf[1024];
+    while (lStream.good())
+    {
+      lStream.read(buf, 1024);
+      s->theValue.insert(s->theValue.end(), buf, buf+lStream.gcount());
+    }
+  }
+  else
+  {
+    char buf[4048];
+    while (lStream.good())
+    {
+      lStream.read(buf, 4048);
+      s->theValue.reserve(s->theValue.size() + lStream.gcount());
+      s->theValue.insert(s->theValue.end(), buf, buf+lStream.gcount());
+    }
+  }
 }
 
 

=== modified file 'src/store/naive/atomic_items.h'
--- src/store/naive/atomic_items.h	2012-01-26 19:56:14 +0000
+++ src/store/naive/atomic_items.h	2012-02-22 01:40:29 +0000
@@ -20,6 +20,7 @@
 #include <zorba/config.h>
 #include <iostream>
 #include <vector>
+#include <cstring>
 
 #include <zorba/streams.h>
 #ifndef ZORBA_NO_FULL_TEXT
@@ -153,7 +154,10 @@
 
   const zstring& getString() const { return theBaseItem->getString(); }
 
-  xs_base64Binary getBase64BinaryValue() const { return theBaseItem->getBase64BinaryValue(); }
+  size_t getBase64BinaryValue(char*& c) const
+  {
+    return theBaseItem->getBase64BinaryValue(c);
+  }
 
   xs_hexBinary getHexBinaryValue() const { return theBaseItem->getHexBinaryValue(); }
 
@@ -2218,30 +2222,115 @@
   friend class BasicItemFactory;
 
 protected:
-  xs_base64Binary theValue;
+  std::vector<char> theValue;
+  bool        theIsEncoded;
 
 protected:
-  Base64BinaryItem(xs_base64Binary aValue) : theValue(aValue) {}
+  Base64BinaryItem(bool aIsEncoded)
+    : theIsEncoded(aIsEncoded) {}
 
-  Base64BinaryItem() {}
+  Base64BinaryItem(const char* aValue, size_t aSize, bool aIsEncoded = true)
+    : theIsEncoded(aIsEncoded)
+  {
+    theValue.reserve(aSize);
+    theValue.insert(theValue.begin(), aValue, aValue + aSize);
+  }
 
 public:
-  xs_base64Binary getBase64BinaryValue() const { return theValue; }
+  size_t getBase64BinaryValue(char*& data) const;
 
   store::SchemaTypeCode getTypeCode() const { return store::XS_BASE64BINARY; }
 
   store::Item* getType() const;
 
+  bool isEncoded() const { return theIsEncoded; }
+
   uint32_t hash(long timezone = 0, const XQPCollator* aCollation = 0) const;
 
   bool equals(
         const store::Item* other,
         long timezone = 0,
-        const XQPCollator* aCollation = 0 ) const
-  {
-    return theValue.equal(other->getBase64BinaryValue());
+        const XQPCollator* aCollation = 0 ) const;
+
+  zstring getStringValue() const;
+
+  void getStringValue2(zstring& val) const;
+
+  void appendStringValue(zstring& buf) const;
+
+  zstring show() const;
+  
+protected:
+  // used in hash doing simple xor of the data
+  struct hash_functor
+  {
+    uint32_t hash_value;
+
+    void operator() (char c)
+    {
+      hash_value ^= (uint32_t) c;
+    }
+  };
+};
+
+
+/*******************************************************************************
+  class StreamableBase64BinaryItem
+********************************************************************************/
+class StreamableBase64BinaryItem : public Base64BinaryItem
+{
+  friend class BasicItemFactory;
+
+protected:
+  std::istream & theIstream;
+
+  bool theIsMaterialized;
+  bool theIsConsumed;
+  bool theIsSeekable;
+
+  StreamReleaser theStreamReleaser;
+
+protected:
+  StreamableBase64BinaryItem(
+      std::istream& aStream,
+      StreamReleaser streamReleaser,
+      bool seekable = false,
+      bool is_encoded = false)
+    : Base64BinaryItem(is_encoded),
+      theIstream(aStream),
+      theIsMaterialized(false),
+      theIsConsumed(false),
+      theIsSeekable(seekable),
+      theStreamReleaser(streamReleaser)
+  {}
+
+  void materialize() const;
+
+public:
+  virtual ~StreamableBase64BinaryItem()
+  {
+    if (theStreamReleaser) 
+    {
+      theStreamReleaser(&theIstream);
+    }
   }
 
+  bool isStreamable() const;
+
+  bool isSeekable() const;
+
+  std::istream& getStream();
+
+  StreamReleaser getStreamReleaser();
+
+  void setStreamReleaser(StreamReleaser aReleaser);
+
+  size_t getBase64BinaryValue(char*&) const;
+
+  store::SchemaTypeCode getTypeCode() const { return store::XS_BASE64BINARY; }
+
+  uint32_t hash(long timezone = 0, const XQPCollator* aCollation = 0) const;
+
   zstring getStringValue() const;
 
   void getStringValue2(zstring& val) const;

=== modified file 'src/store/naive/item.cpp'
--- src/store/naive/item.cpp	2012-02-15 10:25:02 +0000
+++ src/store/naive/item.cpp	2012-02-22 01:40:29 +0000
@@ -430,7 +430,7 @@
 /**
  * Accessor for xs:base64Binary
  */
-xs_base64Binary Item::getBase64BinaryValue() const
+size_t Item::getBase64BinaryValue(char*&) const
 {
   throw ZORBA_EXCEPTION(
     zerr::ZSTR0040_TYPE_ERROR,
@@ -441,6 +441,24 @@
   );
 }
 
+
+/**
+ * Checks whether a base64 item's content is already encoded
+ *
+ * @return true only if it is.
+ */
+bool Item::isEncoded() const
+{
+  throw ZORBA_EXCEPTION(
+    zerr::ZSTR0040_TYPE_ERROR,
+    ERROR_PARAMS(
+      ZED( OperationNotDef_23 ), "Item::isEncoded()",
+      getType()->getStringValue()
+    )
+  );
+}
+
+
 /**
  * Accessor for xs:boolean
  */

=== modified file 'src/store/naive/simple_item_factory.cpp'
--- src/store/naive/simple_item_factory.cpp	2012-02-15 10:25:02 +0000
+++ src/store/naive/simple_item_factory.cpp	2012-02-22 01:40:29 +0000
@@ -997,9 +997,36 @@
 }
 
 
-bool BasicItemFactory::createBase64Binary(store::Item_t& result, xs_base64Binary value)
-{
-  result = new Base64BinaryItem(value);
+bool BasicItemFactory::createBase64Binary(
+    store::Item_t& result,
+    xs_base64Binary value)
+{
+  const std::vector<char>& data = value.getData();
+  result = new Base64BinaryItem(&data[0], data.size(), true);
+  return true;
+}
+
+bool BasicItemFactory::createBase64Binary(
+    store::Item_t& result,
+    const char* value,
+    size_t size,
+    bool encoded)
+{
+  result = new Base64BinaryItem(value, size, encoded);
+  return true;
+}
+
+
+bool BasicItemFactory::createStreamableBase64Binary(
+    store::Item_t& result,
+    std::istream& aStream,
+    StreamReleaser aReleaser,
+    bool seekable,
+    bool encoded)
+{
+  result = new StreamableBase64BinaryItem(
+      aStream, aReleaser, seekable, encoded
+    );
   return true;
 }
 

=== modified file 'src/store/naive/simple_item_factory.h'
--- src/store/naive/simple_item_factory.h	2011-12-21 14:40:33 +0000
+++ src/store/naive/simple_item_factory.h	2012-02-22 01:40:29 +0000
@@ -107,6 +107,19 @@
 
   bool createBase64Binary(store::Item_t& result, xs_base64Binary value);
 
+  bool createBase64Binary(
+      store::Item_t& result,
+      const char* value,
+      size_t size,
+      bool encoded);
+
+  bool createStreamableBase64Binary(
+      store::Item_t& result,
+      std::istream&,
+      StreamReleaser,
+      bool seekable = false,
+      bool encoded = false);
+
   bool createBoolean(store::Item_t& result, xs_boolean value);
 
 

=== modified file 'src/types/casting.cpp'
--- src/types/casting.cpp	2012-01-30 15:23:21 +0000
+++ src/types/casting.cpp	2012-02-22 01:40:29 +0000
@@ -1267,7 +1267,18 @@
 
 T1_TO_T2(b64, hxB)
 {
-  return aFactory->createHexBinary(result, xs_hexBinary(aItem->getBase64BinaryValue()));
+  char* c;
+  size_t s = aItem->getBase64BinaryValue(c);
+  Base64 tmp;
+  if (aItem->isEncoded())
+  {
+    Base64::parseString(c, s, tmp);
+  }
+  else
+  {
+    Base64::encode((const unsigned char*)c, s, tmp);
+  }
+  return aFactory->createHexBinary(result, xs_hexBinary(tmp));
 }
 
 

=== modified file 'src/zorbaserialization/zorba_class_serializer.cpp'
--- src/zorbaserialization/zorba_class_serializer.cpp	2012-01-11 17:30:25 +0000
+++ src/zorbaserialization/zorba_class_serializer.cpp	2012-02-22 01:40:29 +0000
@@ -673,8 +673,31 @@
          
       else if(name_of_type == "base64Binary")
       {
-        SERIALIZE_REF_FIELD(xs_base64Binary, value, getBase64BinaryValue());
-        FINALIZE_SERIALIZE(createBase64Binary, (result, value_in));
+        if (ar.is_serializing_out())
+        {
+          char* c;
+          size_t s = obj->getBase64BinaryValue(c);
+          if (obj->isEncoded())
+          {
+            Base64 tmp;
+            Base64::parseString(c, s, tmp);
+            ar.dont_allow_delay();
+            ar & tmp;
+          }
+          else
+          {
+            Base64 tmp((const unsigned char*)c, s);
+            ar.dont_allow_delay();
+            ar & tmp;
+          }
+        }
+        else
+        {
+          ar.dont_allow_delay();
+          Base64 tmp;
+          ar & tmp;
+          FINALIZE_SERIALIZE(createBase64Binary, (result, tmp));
+        }
       }
       else if(name_of_type == "hexBinary")
       {

=== modified file 'src/zorbatypes/binary.cpp'
--- src/zorbatypes/binary.cpp	2011-06-14 17:26:33 +0000
+++ src/zorbatypes/binary.cpp	2012-02-22 01:40:29 +0000
@@ -306,6 +306,16 @@
 }
 
 
+Base64::Base64(const unsigned char *bin_data, size_t len)
+{
+  std::vector<char> tmp;
+  tmp.reserve(len);
+  tmp.insert(tmp.begin(), (const char*)bin_data, ((const char*)bin_data) + len);
+  theData.reserve(len);
+  encode(tmp, theData);
+}
+
+
 void Base64::serialize(::zorba::serialization::Archiver& ar)
 {
   ar & theData;

=== added directory 'test/rbkt/ExpQueryResults/zorba/base64'
=== added file 'test/rbkt/ExpQueryResults/zorba/base64/binary_1.xml.res'
--- test/rbkt/ExpQueryResults/zorba/base64/binary_1.xml.res	1970-01-01 00:00:00 +0000
+++ test/rbkt/ExpQueryResults/zorba/base64/binary_1.xml.res	2012-02-22 01:40:29 +0000
@@ -0,0 +1,1 @@
+true

=== added file 'test/rbkt/ExpQueryResults/zorba/base64/file_read_1.xml.res'
--- test/rbkt/ExpQueryResults/zorba/base64/file_read_1.xml.res	1970-01-01 00:00:00 +0000
+++ test/rbkt/ExpQueryResults/zorba/base64/file_read_1.xml.res	2012-02-22 01:40:29 +0000
@@ -0,0 +1,1 @@
+true true f0VMRgEBAQAAAAAAAAAAAAMAAwABAAAAIPxDADQAAAD0JTMHAAAAADQAIAAHACgAKAAlAAEAAAAAAAAAAAAAAAAAAAAv22gBL9toAQUAAAAAEAAAAQAAAJTbaAGU62gBlOtoAehnBwAwpgcABgAAAAAQAAACAAAAvKBuAbywbgG8sG4BGAEAABgBAAAGAAAABAAAAAQAAAAUAQAAFAEAABQBAAAkAAAAJAAAAAQAAAAEAAAAUOV0ZKyHJwGshycBrIcnAfyBCAD8gQgABAAAAAQAAABR5XRkAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAABAAAAFLldGSU22gBlOtoAZTraAFs5AUAbOQFAAQAAAABAAAABAAAABQAAAADAAAAR05VANMOFlHRDhyfmkw9H3+lYuAncwGhG0AAAAYDAAAAIAAAEgAAABBkRVIkEIgUIwhRukiBHLCOBAAEAgggoSCCg0CkED5YAAAGFAIgABkACJAAiQAwAFCQEwAQIABBAIIFYAIACAASAAZARACAABkTpAQB5EQIYEF0IBIAIgAhciYEoEICZGgAAQAABCAAkBACZAJAAQQUEhGAIIAAKBULIJYAAABRAKFV4AIWgQAEIYgAAIAAQECAMgBGiCIGiRAEkAABAMAIgEQAAAQAACgIJYAAAEAAAAGAAgtCpFwEAAZRQACAABAICAASiBBFgRhIAkEBMAApJchACQZDAIAAgABIAKQEgKEACGE05FgCAAAgACEAgEAAQoxkUAEAiBAGAAkCFiBoISRFFCBEBMV3gARBgBAosUJBQQAEEDIAUUCBAABAEAhgAEAAAAAAACApACIBBgHKkAWBQAAEAAAAAACDURggAkEEEIlEgIEADQZwAAATC5AQNYxCABABAAAAAAEAlQD0WGD3gAAAIEDKCCAQQMGNIAAQAAAACECCAADAbMAWABABaRAAUFQCgAAAgxJmDAEIAiIZCmIAIAIEgAESCSAjETEQIws2A8AIgAEAAAAAkGkAiEAKDEYEBRGJhFEAIACICAIBKMAGAAAHIAQBqIgAUAECgAAAAhoEER4BASMBBhEIIQGCAFbg8IiBYAgCAQgAgEBwCAAAIAEAQATGAj+QQaAIIQAIAAShAgg2AKQAAAQBkCoASAAAFAAgAYFqAQDCcoOEg0sAIhKAABQAAgpAAghAAIQgIBAkAAQBDA4gRAACAAIgAPBAUgBDARCAggB0GQBaEAg1Mk8CABEAAAQgBBgAECIAvEIhFCRAIBgAAEALyBEQAhACQoIKwAABkBiAVM4QAQlFgBAS4AASAIIkAZEkEgAsBMCABSCAKACAIBMAD4JSLEyAAGQBBqATAUECQUgACQGBGA==

=== added directory 'test/rbkt/Queries/zorba/base64'
=== added file 'test/rbkt/Queries/zorba/base64/binary_1.xq'
--- test/rbkt/Queries/zorba/base64/binary_1.xq	1970-01-01 00:00:00 +0000
+++ test/rbkt/Queries/zorba/base64/binary_1.xq	2012-02-22 01:40:29 +0000
@@ -0,0 +1,1 @@
+xs:string(xs:base64Binary("Wm9yYmEgaXMgR3JlYXQhIMOkw7bDvA==")) eq "Wm9yYmEgaXMgR3JlYXQhIMOkw7bDvA=="

=== added file 'test/rbkt/Queries/zorba/base64/decoded'
Binary files test/rbkt/Queries/zorba/base64/decoded	1970-01-01 00:00:00 +0000 and test/rbkt/Queries/zorba/base64/decoded	2012-02-22 01:40:29 +0000 differ
=== added file 'test/rbkt/Queries/zorba/base64/decoded-text'
--- test/rbkt/Queries/zorba/base64/decoded-text	1970-01-01 00:00:00 +0000
+++ test/rbkt/Queries/zorba/base64/decoded-text	2012-02-22 01:40:29 +0000
@@ -0,0 +1,1 @@
+Zorba is Great! äöü
\ No newline at end of file

=== added file 'test/rbkt/Queries/zorba/base64/encoded'
--- test/rbkt/Queries/zorba/base64/encoded	1970-01-01 00:00:00 +0000
+++ test/rbkt/Queries/zorba/base64/encoded	2012-02-22 01:40:29 +0000
@@ -0,0 +1,1 @@
+f0VMRgEBAQAAAAAAAAAAAAMAAwABAAAAIPxDADQAAAD0JTMHAAAAADQAIAAHACgAKAAlAAEAAAAAAAAAAAAAAAAAAAAv22gBL9toAQUAAAAAEAAAAQAAAJTbaAGU62gBlOtoAehnBwAwpgcABgAAAAAQAAACAAAAvKBuAbywbgG8sG4BGAEAABgBAAAGAAAABAAAAAQAAAAUAQAAFAEAABQBAAAkAAAAJAAAAAQAAAAEAAAAUOV0ZKyHJwGshycBrIcnAfyBCAD8gQgABAAAAAQAAABR5XRkAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAABAAAAFLldGSU22gBlOtoAZTraAFs5AUAbOQFAAQAAAABAAAABAAAABQAAAADAAAAR05VANMOFlHRDhyfmkw9H3+lYuAncwGhG0AAAAYDAAAAIAAAEgAAABBkRVIkEIgUIwhRukiBHLCOBAAEAgggoSCCg0CkED5YAAAGFAIgABkACJAAiQAwAFCQEwAQIABBAIIFYAIACAASAAZARACAABkTpAQB5EQIYEF0IBIAIgAhciYEoEICZGgAAQAABCAAkBACZAJAAQQUEhGAIIAAKBULIJYAAABRAKFV4AIWgQAEIYgAAIAAQECAMgBGiCIGiRAEkAABAMAIgEQAAAQAACgIJYAAAEAAAAGAAgtCpFwEAAZRQACAABAICAASiBBFgRhIAkEBMAApJchACQZDAIAAgABIAKQEgKEACGE05FgCAAAgACEAgEAAQoxkUAEAiBAGAAkCFiBoISRFFCBEBMV3gARBgBAosUJBQQAEEDIAUUCBAABAEAhgAEAAAAAAACApACIBBgHKkAWBQAAEAAAAAACDURggAkEEEIlEgIEADQZwAAATC5AQNYxCABABAAAAAAEAlQD0WGD3gAAAIEDKCCAQQMGNIAAQAAAACECCAADAbMAWABABaRAAUFQCgAAAgxJmDAEIAiIZCmIAIAIEgAESCSAjETEQIws2A8AIgAEAAAAAkGkAiEAKDEYEBRGJhFEAIACICAIBKMAGAAAHIAQBqIgAUAECgAAAAhoEER4BASMBBhEIIQGCAFbg8IiBYAgCAQgAgEBwCAAAIAEAQATGAj+QQaAIIQAIAAShAgg2AKQAAAQBkCoASAAAFAAgAYFqAQDCcoOEg0sAIhKAABQAAgpAAghAAIQgIBAkAAQBDA4gRAACAAIgAPBAUgBDARCAggB0GQBaEAg1Mk8CABEAAAQgBBgAECIAvEIhFCRAIBgAAEALyBEQAhACQoIKwAABkBiAVM4QAQlFgBAS4AASAIIkAZEkEgAsBMCABSCAKACAIBMAD4JSLEyAAGQBBqATAUECQUgACQGBGA==
\ No newline at end of file

=== added file 'test/rbkt/Queries/zorba/base64/encoded-text'
--- test/rbkt/Queries/zorba/base64/encoded-text	1970-01-01 00:00:00 +0000
+++ test/rbkt/Queries/zorba/base64/encoded-text	2012-02-22 01:40:29 +0000
@@ -0,0 +1,1 @@
+Wm9yYmEgaXMgR3JlYXQhIMOkw7bDvA==
\ No newline at end of file

=== added file 'test/rbkt/Queries/zorba/base64/file_read_1.xq'
--- test/rbkt/Queries/zorba/base64/file_read_1.xq	1970-01-01 00:00:00 +0000
+++ test/rbkt/Queries/zorba/base64/file_read_1.xq	2012-02-22 01:40:29 +0000
@@ -0,0 +1,10 @@
+import module namespace f = "http://expath.org/ns/file";;
+
+variable $enc-file-name := resolve-uri("encoded");
+variable $dec-file-name := resolve-uri("decoded");
+variable $base64-1 := f:read-binary($dec-file-name);
+variable $base64-2 := f:read-binary($dec-file-name);
+variable $ref-result := f:read-text($enc-file-name);
+
+xs:string($base64-1) eq $ref-result, $base64-1 eq $base64-2,
+fn:serialize($base64-1)

=== added file 'test/rbkt/Queries/zorba/base64/file_read_2.xq'
--- test/rbkt/Queries/zorba/base64/file_read_2.xq	1970-01-01 00:00:00 +0000
+++ test/rbkt/Queries/zorba/base64/file_read_2.xq	2012-02-22 01:40:29 +0000
@@ -0,0 +1,11 @@
+import module namespace f = "http://expath.org/ns/file";;
+import module namespace b = "http://www.zorba-xquery.com/modules/converters/base64";;
+
+variable $enc-file-name := resolve-uri("encoded-text");
+variable $dec-file-name := resolve-uri("decoded-text");
+variable $encoded := f:read-text($enc-file-name);
+variable $decoded := f:read-text($dec-file-name);
+
+$encoded eq xs:string(b:encode($decoded)),
+xs:base64Binary($encoded) eq b:encode($decoded),
+b:decode(xs:base64Binary($encoded)) eq $decoded

-- 
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