Merge lp:~zorba-coders/zorba/feature-base64_streambuf into lp:zorba

Proposed by Paul J. Lucas
Status: Merged
Approved by: Matthias Brantner
Approved revision: 10894
Merged at revision: 10894
Proposed branch: lp:~zorba-coders/zorba/feature-base64_streambuf
Merge into: lp:zorba
Diff against target: 3118 lines (+2077/-451)
30 files modified
ChangeLog (+2/-1)
doc/cxx/examples/binary.cpp (+17/-27)
include/zorba/base64.h (+7/-10)
include/zorba/base64_stream.h (+325/-0)
include/zorba/internal/diagnostic.h (+20/-31)
include/zorba/internal/streambuf.h (+40/-0)
include/zorba/transcode_stream.h (+11/-15)
src/api/CMakeLists.txt (+2/-0)
src/api/base64_streambuf.cpp (+249/-0)
src/api/base64impl.cpp (+37/-12)
src/api/serialization/serializer.cpp (+6/-4)
src/api/streambuf.cpp (+48/-0)
src/api/transcode_streambuf.cpp (+4/-18)
src/diagnostics/diagnostic_en.xml (+0/-8)
src/diagnostics/pregenerated/dict_en.cpp (+0/-2)
src/runtime/base64/base64_impl.cpp (+4/-7)
src/unit_tests/CMakeLists.txt (+2/-0)
src/unit_tests/test_base64.cpp (+286/-0)
src/unit_tests/test_base64_streambuf.cpp (+141/-0)
src/unit_tests/unit_test_list.h (+7/-12)
src/unit_tests/unit_tests.cpp (+8/-9)
src/util/CMakeLists.txt (+1/-0)
src/util/ascii_util.cpp (+34/-0)
src/util/ascii_util.h (+23/-0)
src/util/base64_util.cpp (+359/-0)
src/util/base64_util.h (+361/-0)
src/util/icu_streambuf.cpp (+1/-1)
src/util/string/rstring.h (+1/-1)
src/zorbatypes/binary.cpp (+78/-287)
src/zorbatypes/binary.h (+3/-6)
To merge this branch: bzr merge lp:~zorba-coders/zorba/feature-base64_streambuf
Reviewer Review Type Date Requested Status
Matthias Brantner Approve
Paul J. Lucas Approve
Review via email: mp+111984@code.launchpad.net

Commit message

Added base64::streambuf class and replaced horribly inefficient base64 code.

Description of the change

Added base64::streambuf class and replaced horribly inefficient base64 code.

To post a comment you must log in.
Revision history for this message
Paul J. Lucas (paul-lucas) :
review: Approve
Revision history for this message
Matthias Brantner (matthias-brantner) wrote :

Did the streambuf api (already released in 2.5) change?

review: Needs Information
Revision history for this message
Paul J. Lucas (paul-lucas) wrote :

> Did the streambuf api (already released in 2.5) change?

No.

Revision history for this message
Zorba Build Bot (zorba-buildbot) wrote :
Revision history for this message
Zorba Build Bot (zorba-buildbot) wrote :

Validation queue job feature-base64_streambuf-2012-06-26T18-40-25.173Z is finished. The final status was:

All tests succeeded!

Revision history for this message
Zorba Build Bot (zorba-buildbot) wrote :

Voting does not meet specified criteria. Required: Approve > 1, Disapprove < 1, Needs Fixing < 1, Pending < 1. Got: 1 Approve, 1 Needs Information.

Revision history for this message
Matthias Brantner (matthias-brantner) :
review: Approve
Revision history for this message
Zorba Build Bot (zorba-buildbot) wrote :
Revision history for this message
Zorba Build Bot (zorba-buildbot) wrote :

Validation queue job feature-base64_streambuf-2012-06-27T00-07-01.759Z is finished. The final status was:

All tests succeeded!

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ChangeLog'
2--- ChangeLog 2012-06-25 17:16:19 +0000
3+++ ChangeLog 2012-06-26 01:34:26 +0000
4@@ -18,7 +18,8 @@
5 * Fixed bug #867357 (Improved parser error messages)
6 * Fixed bug #932314 (non-comparable values must be treated as distinct by
7 fn:distinct-values)
8- * Fixed bug #991088$ (raise XUST0001 in trycatch with mixed updating and simple clauses)
9+ * Fixed bug #1015580 (Add base64_streambuf / replace inefficient base64 code)
10+ * Fixed bug #9910884 (raise XUST0001 in trycatch with mixed updating and simple clauses)
11 * Fixed bug #854506 (ugly type error messages) and partial fix for bug #867008
12 * Fixed bug #1008082 (bug in transform expr when a copy var is not used anywhere)
13 * Fixed bug #1003023$ (optimizer problems due to common subexpression after
14
15=== modified file 'doc/cxx/examples/binary.cpp'
16--- doc/cxx/examples/binary.cpp 2012-06-18 10:06:47 +0000
17+++ doc/cxx/examples/binary.cpp 2012-06-26 01:34:26 +0000
18@@ -25,35 +25,25 @@
19 {
20 String lString("Hello Zorba");
21 String lEncoded = zorba::encoding::Base64::encode(lString);
22- String lExpectedResult("SGVsbG8gWm9yYmE=");
23- return lEncoded == lExpectedResult;
24+ return lEncoded == "SGVsbG8gWm9yYmE=";
25 }
26
27 bool
28 decode_example()
29 {
30- String lEncoded("SGVsbG8gWm9yYmE=");
31- String lDecoded = zorba::encoding::Base64::decode(lEncoded);
32- return lDecoded == "Hello Zorba";
33-
34-}
35-
36-int
37-binary(int argc, char* argv[])
38-{
39-
40-bool res = false;
41-
42-std::cout << "executing example 1 (Base64 encoding of String)" << std::endl;
43-res = encode_example();
44-if (!res) return 1;
45-std::cout << std::endl;
46-
47-std::cout << "executing example 2 (decoding of Base64 encoded String)" << std::endl;
48-res = decode_example();
49-if (!res) return 1;
50-std::cout << std::endl;
51-
52-return 0;
53-
54-}
55+ String lEncoded("SGVsbG8gWm9yYmE=");
56+ String lDecoded = zorba::encoding::Base64::decode(lEncoded);
57+ return lDecoded == "Hello Zorba";
58+}
59+
60+int binary(int argc, char* argv[]) {
61+
62+ std::cout << "executing example 1 (Base64 encoding of String)" << std::endl;
63+ if (!encode_example()) return 1;
64+
65+ std::cout << "executing example 2 (decoding of Base64 encoded String)" << std::endl;
66+ if (!decode_example()) return 1;
67+
68+ return 0;
69+}
70+/* vim:set et sw=2 ts=2: */
71
72=== modified file 'include/zorba/base64.h'
73--- include/zorba/base64.h 2012-06-18 10:06:47 +0000
74+++ include/zorba/base64.h 2012-06-26 01:34:26 +0000
75@@ -21,12 +21,11 @@
76 #include <zorba/config.h>
77 #include <zorba/zorba_string.h>
78
79-namespace zorba { namespace encoding {
80+namespace zorba {
81+namespace encoding {
82
83- class ZORBA_DLL_PUBLIC Base64
84+ struct ZORBA_DLL_PUBLIC Base64
85 {
86- public:
87-
88 static String
89 encode(const String& aString);
90
91@@ -38,12 +37,10 @@
92
93 static String
94 decode(std::istream& aStream);
95-
96 };
97
98-} /* end namespace encoding */
99-
100-} /* end namespace zorba */
101-
102-#endif
103+} // namespace encoding
104+} // namespace zorba
105+
106+#endif /* ZORBA_BASE64_API_H */
107 /* vim:set et sw=2 ts=2: */
108
109=== added file 'include/zorba/base64_stream.h'
110--- include/zorba/base64_stream.h 1970-01-01 00:00:00 +0000
111+++ include/zorba/base64_stream.h 2012-06-26 01:34:26 +0000
112@@ -0,0 +1,325 @@
113+/*
114+ * Copyright 2006-2008 The FLWOR Foundation.
115+ *
116+ * Licensed under the Apache License, Version 2.0 (the "License");
117+ * you may not use this file except in compliance with the License.
118+ * You may obtain a copy of the License at
119+ *
120+ * http://www.apache.org/licenses/LICENSE-2.0
121+ *
122+ * Unless required by applicable law or agreed to in writing, software
123+ * distributed under the License is distributed on an "AS IS" BASIS,
124+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
125+ * See the License for the specific language governing permissions and
126+ * limitations under the License.
127+ */
128+
129+#ifndef ZORBA_BASE64_STREAM_API_H
130+#define ZORBA_BASE64_STREAM_API_H
131+
132+#include <streambuf>
133+
134+#include <zorba/config.h>
135+#include <zorba/internal/streambuf.h>
136+
137+namespace zorba {
138+namespace base64 {
139+
140+///////////////////////////////////////////////////////////////////////////////
141+
142+/**
143+ * A %base64::streambuf is-a std::streambuf for encoding to and decoding from
144+ * Base64 on-the-fly.
145+ *
146+ * To use it, replace a stream's streambuf:
147+ * \code
148+ * istream is;
149+ * // ...
150+ * base64::streambuf b64buf( is.rdbuf() );
151+ * is.ios::rdbuf( &b64buf );
152+ * \endcode
153+ * Note that the %base64::streambuf must exist for as long as it's being used
154+ * by the stream. If you are replacing the streabuf for a stream you did not
155+ * create, you should set it back to the original streambuf:
156+ * \code
157+ * void f( ostream &os ) {
158+ * base64::streambuf b64buf( os.rdbuf() );
159+ * try {
160+ * os.ios::rdbuf( &b64buf );
161+ * // ...
162+ * }
163+ * catch ( ... ) {
164+ * os.ios::rdbuf( b64buf.orig_streambuf() );
165+ * throw;
166+ * }
167+ * os.ios::rdbuf( b64buf.orig_streambuf() );
168+ * }
169+ * \endcode
170+ * Alternatively, you may wish to use either \c attach(), \c auto_attach, or
171+ * \c base64::stream instead.
172+ *
173+ * \b Note: due to the nature of Base64-encoding, when writing, you \e must
174+ * ensure that the streambuf is flushed (by calling either \c pubsync() on the
175+ * streambuf or \c flush() on the owning stream) when done.
176+ *
177+ * While %base64::streambuf does support seeking, the positions are relative
178+ * to the original byte stream.
179+ */
180+class ZORBA_DLL_PUBLIC streambuf : public std::streambuf {
181+public:
182+ /**
183+ * Constructs a %base64::streambuf.
184+ *
185+ * @param orig The original streambuf to read/write from/to.
186+ * @throws std::invalid_argument if is not supported or \a orig is null.
187+ */
188+ streambuf( std::streambuf *orig );
189+
190+ /**
191+ * Destructs a %base64::streambuf.
192+ */
193+ ~streambuf();
194+
195+ /**
196+ * Gets the original streambuf.
197+ *
198+ * @return said streambuf.
199+ */
200+ std::streambuf* orig_streambuf() const {
201+ return orig_buf_;
202+ }
203+
204+protected:
205+ void imbue( std::locale const& );
206+ pos_type seekoff( off_type, std::ios_base::seekdir, std::ios_base::openmode );
207+ pos_type seekpos( pos_type, std::ios_base::openmode );
208+ std::streambuf* setbuf( char_type*, std::streamsize );
209+ std::streamsize showmanyc();
210+ int sync();
211+ int_type overflow( int_type );
212+ int_type pbackfail( int_type );
213+ int_type underflow();
214+ std::streamsize xsgetn( char_type*, std::streamsize );
215+ std::streamsize xsputn( char_type const*, std::streamsize );
216+
217+private:
218+ std::streambuf *orig_buf_;
219+
220+ char gbuf_[3];
221+ char pbuf_[3];
222+ int plen_;
223+
224+ void clear();
225+ void resetg();
226+ void resetp();
227+ void writep();
228+
229+ // forbid
230+ streambuf( streambuf const& );
231+ streambuf& operator=( streambuf const& );
232+};
233+
234+///////////////////////////////////////////////////////////////////////////////
235+
236+} // namespace base64
237+
238+namespace internal {
239+namespace base64 {
240+
241+ZORBA_DLL_PUBLIC
242+std::streambuf* alloc_streambuf( std::streambuf *orig );
243+
244+ZORBA_DLL_PUBLIC
245+int get_streambuf_index();
246+
247+} // namespace base64
248+} // namespace internal
249+
250+namespace base64 {
251+
252+///////////////////////////////////////////////////////////////////////////////
253+
254+/**
255+ * Attaches a base64::streambuf to a stream. Unlike using a
256+ * base64::streambuf directly, this function will create the streambuf,
257+ * attach it to the stream, and manage it for the lifetime of the stream
258+ * automatically.
259+ *
260+ * @param ios The stream to attach the base64::streambuf to. If the stream
261+ * already has a base64::streambuf attached to it, this function does
262+ * nothing.
263+ */
264+template<typename charT,typename Traits> inline
265+void attach( std::basic_ios<charT,Traits> &ios ) {
266+ int const index = internal::base64::get_streambuf_index();
267+ void *&pword = ios.pword( index );
268+ if ( !pword ) {
269+ std::streambuf *const buf =
270+ internal::base64::alloc_streambuf( ios.rdbuf() );
271+ ios.rdbuf( buf );
272+ pword = buf;
273+ ios.register_callback( internal::stream_callback, index );
274+ }
275+}
276+
277+/**
278+ * Detaches a previously attached base64::streambuf from a stream. The
279+ * streambuf is destroyed and the stream's original streambuf is restored.
280+ *
281+ * @param ios The stream to detach the base64::streambuf from. If the
282+ * stream doesn't have a base64::streambuf attached to it, this function
283+ * does nothing.
284+ */
285+template<typename charT,typename Traits> inline
286+void detach( std::basic_ios<charT,Traits> &ios ) {
287+ int const index = internal::base64::get_streambuf_index();
288+ if ( streambuf *const buf = static_cast<streambuf*>( ios.pword( index ) ) ) {
289+ ios.pword( index ) = 0;
290+ ios.rdbuf( buf->orig_streambuf() );
291+ internal::dealloc_streambuf( buf );
292+ }
293+}
294+
295+/**
296+ * Checks whether the given stream has a base64::streambuf attached.
297+ *
298+ * @param ios The stream to check.
299+ * @return \c true only if a base64::streambuf is attached.
300+ */
301+template<typename charT,typename Traits> inline
302+bool is_attached( std::basic_ios<charT,Traits> &ios ) {
303+ return !!ios.pword( internal::base64::get_streambuf_index() );
304+}
305+
306+/**
307+ * A %base64::auto_attach is a class that attaches a base64::streambuf to
308+ * a stream and automatically detaches it when the %auto_attach object is
309+ * destroyed.
310+ * \code
311+ * void f( ostream &os ) {
312+ * base64::auto_attach<ostream> const raii( os, "ISO-8859-1" );
313+ * // ...
314+ * }
315+ * \endcode
316+ * A %base64::auto_attach is useful for streams not created by you.
317+ *
318+ * @see http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization
319+ */
320+template<class StreamType>
321+class auto_attach {
322+public:
323+ /**
324+ * Constructs an %auto_attach object calling attach() on the given stream.
325+ *
326+ * @param stream The stream to attach the base64::streambuf to. If the
327+ * stream already has a base64::streambuf attached to it, this contructor
328+ * does nothing.
329+ */
330+ auto_attach( StreamType &stream ) : stream_( stream ) {
331+ attach( stream );
332+ }
333+
334+ /**
335+ * Destroys this %auto_attach object calling detach() on the previously
336+ * attached stream.
337+ */
338+ ~auto_attach() {
339+ detach( stream_ );
340+ }
341+
342+private:
343+ StreamType &stream_;
344+};
345+
346+///////////////////////////////////////////////////////////////////////////////
347+
348+/**
349+ * A %base64::stream is used to wrap a C++ standard I/O stream with a
350+ * base64::streambuf so that encoding/decoding and the management of the
351+ * streambuf happens automatically.
352+ *
353+ * A %base64::stream is useful for streams created by you.
354+ *
355+ * @tparam StreamType The I/O stream class type to wrap. It must be a concrete
356+ * stream class.
357+ */
358+template<class StreamType>
359+class stream : public StreamType {
360+public:
361+ /**
362+ * Constructs a %base64::stream.
363+ */
364+ stream() :
365+#ifdef WIN32
366+# pragma warning( push )
367+# pragma warning( disable : 4355 )
368+#endif /* WIN32 */
369+ b64buf_( this->rdbuf() )
370+#ifdef WIN32
371+# pragma warning( pop )
372+#endif /* WIN32 */
373+ {
374+ init();
375+ }
376+
377+ /**
378+ * Constructs a %stream.
379+ *
380+ * @tparam StreamArgType The type of the first argument of \a StreamType's
381+ * constructor.
382+ * @param stream_arg The argument to pass as the first argument to
383+ * \a StreamType's constructor.
384+ */
385+ template<typename StreamArgType>
386+ stream( StreamArgType stream_arg ) :
387+ StreamType( stream_arg ),
388+#ifdef WIN32
389+# pragma warning( push )
390+# pragma warning( disable : 4355 )
391+#endif /* WIN32 */
392+ b64buf_( this->rdbuf() )
393+#ifdef WIN32
394+# pragma warning( pop )
395+#endif /* WIN32 */
396+ {
397+ init();
398+ }
399+
400+ /**
401+ * Constructs a %base64::stream.
402+ *
403+ * @tparam StreamArgType The type of the first argument of \a StreamType's
404+ * constructor.
405+ * @param stream_arg The argument to pass as the first argument to
406+ * \a StreamType's constructor.
407+ * @param mode The open-mode to pass to \a StreamType's constructor.
408+ */
409+ template<typename StreamArgType>
410+ stream( StreamArgType stream_arg, std::ios_base::openmode mode ) :
411+ StreamType( stream_arg, mode ),
412+#ifdef WIN32
413+# pragma warning( push )
414+# pragma warning( disable : 4355 )
415+#endif /* WIN32 */
416+ b64buf_( this->rdbuf() )
417+#ifdef WIN32
418+# pragma warning( pop )
419+#endif /* WIN32 */
420+ {
421+ init();
422+ }
423+
424+private:
425+ streambuf b64buf_;
426+
427+ void init() {
428+ this->std::ios::rdbuf( &b64buf_ );
429+ }
430+};
431+
432+///////////////////////////////////////////////////////////////////////////////
433+
434+} // namespace base64
435+} // namespace zorba
436+#endif /* ZORBA_BASE64_STREAM_API_H */
437+/* vim:set et sw=2 ts=2: */
438
439=== modified file 'include/zorba/internal/diagnostic.h'
440--- include/zorba/internal/diagnostic.h 2012-06-18 10:06:47 +0000
441+++ include/zorba/internal/diagnostic.h 2012-06-26 01:34:26 +0000
442@@ -42,7 +42,7 @@
443 ///////////////////////////////////////////////////////////////////////////////
444
445 /**
446- * A %location hold the file location of an error.
447+ * A %location holds the file location of an error.
448 */
449 class ZORBA_DLL_PUBLIC location {
450 public:
451@@ -71,21 +71,17 @@
452 * Constructs a %location.
453 *
454 * @param file The name of the file where the error occurred.
455- * @param line The line number of the file where the expression that
456- * raises the error begins.
457+ * @param line The line number of the file where the expression that raises
458+ * the error begins.
459 * @param column The column number, if any, of the file where the expression
460- * that raises the error begins.
461- * @param line_end The end line number, if any, of the file where the expression
462- * causing the error ends.
463- * @param column_end The end column number, if any, of the file where
464- * the xpression causing the error ends.
465+ * that raises the error begins.
466+ * @param line_end The end line number, if any, of the file where the
467+ * expression causing the error ends.
468+ * @param column_end The end column number, if any, of the file where the
469+ * xpression causing the error ends.
470 */
471- location(
472- char const *file,
473- line_type line,
474- column_type column = 0,
475- line_type line_end = 0,
476- column_type column_end = 0) :
477+ location( char const *file, line_type line, column_type column = 0,
478+ line_type line_end = 0, column_type column_end = 0 ) :
479 file_( file ), line_( line ), column_( column ),
480 line_end_( line_end ), column_end_( column_end )
481 {
482@@ -97,19 +93,16 @@
483 * @tparam StringType The string type for \a file.
484 * @param file The name of the file where the error occurred.
485 * @param line The line number of the file where the error occurred.
486- * @param column The column number, if any, of the file where the error occurred.
487- * @param line_end The end line number, if any, of the file where the expression
488- * causing the error ends.
489- * @param column_end The end column number, if any, of the file where
490- * the xpression causing the error ends.
491+ * @param column The column number, if any, of the file where the error
492+ * occurred.
493+ * @param line_end The end line number, if any, of the file where the
494+ * expression causing the error ends.
495+ * @param column_end The end column number, if any, of the file where the
496+ * xpression causing the error ends.
497 */
498 template<class StringType>
499- location(
500- StringType const &file,
501- line_type line,
502- column_type column = 0,
503- line_type line_end = 0,
504- column_type column_end = 0) :
505+ location( StringType const &file, line_type line, column_type column = 0,
506+ line_type line_end = 0, column_type column_end = 0 ) :
507 file_( file.c_str() ), line_( line ), column_( column ),
508 line_end_( line_end ), column_end_( column_end )
509 {
510@@ -189,12 +182,8 @@
511 * @param column_end The column number, if any, where the error ends.
512 * occurred.
513 */
514- void set(
515- char const *file,
516- line_type line,
517- column_type column = 0,
518- line_type line_end = 0,
519- column_type column_end = 0) {
520+ void set( char const *file, line_type line, column_type column = 0,
521+ line_type line_end = 0, column_type column_end = 0 ) {
522 file_ = file;
523 line_ = line;
524 column_ = column;
525
526=== added file 'include/zorba/internal/streambuf.h'
527--- include/zorba/internal/streambuf.h 1970-01-01 00:00:00 +0000
528+++ include/zorba/internal/streambuf.h 2012-06-26 01:34:26 +0000
529@@ -0,0 +1,40 @@
530+/*
531+ * Copyright 2006-2008 The FLWOR Foundation.
532+ *
533+ * Licensed under the Apache License, Version 2.0 (the "License");
534+ * you may not use this file except in compliance with the License.
535+ * You may obtain a copy of the License at
536+ *
537+ * http://www.apache.org/licenses/LICENSE-2.0
538+ *
539+ * Unless required by applicable law or agreed to in writing, software
540+ * distributed under the License is distributed on an "AS IS" BASIS,
541+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
542+ * See the License for the specific language governing permissions and
543+ * limitations under the License.
544+ */
545+
546+#ifndef ZORBA_INTERNAL_STREAMBUF_H
547+#define ZORBA_INTERNAL_STREAMBUF_H
548+
549+#include <streambuf>
550+
551+#include <zorba/config.h>
552+
553+namespace zorba {
554+namespace internal {
555+
556+///////////////////////////////////////////////////////////////////////////////
557+
558+ZORBA_DLL_PUBLIC
559+void dealloc_streambuf( std::streambuf* );
560+
561+ZORBA_DLL_PUBLIC
562+void stream_callback( std::ios_base::event, std::ios_base&, int index );
563+
564+///////////////////////////////////////////////////////////////////////////////
565+
566+} // namespace internal
567+} // namespace zorba
568+#endif /* ZORBA_INTERNAL_STREAMBUF_H */
569+/* vim:set et sw=2 ts=2: */
570
571=== modified file 'include/zorba/transcode_stream.h'
572--- include/zorba/transcode_stream.h 2012-06-18 10:06:47 +0000
573+++ include/zorba/transcode_stream.h 2012-06-26 01:34:26 +0000
574@@ -19,10 +19,10 @@
575
576 #include <stdexcept>
577 #include <streambuf>
578-#include <string>
579
580 #include <zorba/config.h>
581 #include <zorba/internal/proxy.h>
582+#include <zorba/internal/streambuf.h>
583 #include <zorba/internal/unique_ptr.h>
584
585 namespace zorba {
586@@ -120,20 +120,15 @@
587 } // namespace transcode
588
589 namespace internal {
590-
591-ZORBA_DLL_PUBLIC
592-zorba::transcode::streambuf*
593-alloc_streambuf( char const *charset, std::streambuf *orig );
594-
595-ZORBA_DLL_PUBLIC
596-void dealloc_streambuf( zorba::transcode::streambuf* );
597+namespace transcode {
598+
599+ZORBA_DLL_PUBLIC
600+std::streambuf* alloc_streambuf( char const *charset, std::streambuf *orig );
601
602 ZORBA_DLL_PUBLIC
603 int get_streambuf_index();
604
605-ZORBA_DLL_PUBLIC
606-void stream_callback( std::ios_base::event, std::ios_base&, int index );
607-
608+} // transcode
609 } // namespace internal
610
611 namespace transcode {
612@@ -153,10 +148,11 @@
613 */
614 template<typename charT,typename Traits> inline
615 void attach( std::basic_ios<charT,Traits> &ios, char const *charset ) {
616- int const index = internal::get_streambuf_index();
617+ int const index = internal::transcode::get_streambuf_index();
618 void *&pword = ios.pword( index );
619 if ( !pword ) {
620- streambuf *const buf = internal::alloc_streambuf( charset, ios.rdbuf() );
621+ std::streambuf *const buf =
622+ internal::transcode::alloc_streambuf( charset, ios.rdbuf() );
623 ios.rdbuf( buf );
624 pword = buf;
625 ios.register_callback( internal::stream_callback, index );
626@@ -173,7 +169,7 @@
627 */
628 template<typename charT,typename Traits> inline
629 void detach( std::basic_ios<charT,Traits> &ios ) {
630- int const index = internal::get_streambuf_index();
631+ int const index = internal::transcode::get_streambuf_index();
632 if ( streambuf *const buf = static_cast<streambuf*>( ios.pword( index ) ) ) {
633 ios.pword( index ) = 0;
634 ios.rdbuf( buf->orig_streambuf() );
635@@ -189,7 +185,7 @@
636 */
637 template<typename charT,typename Traits> inline
638 bool is_attached( std::basic_ios<charT,Traits> &ios ) {
639- return !!ios.pword( internal::get_streambuf_index() );
640+ return !!ios.pword( internal::transcode::get_streambuf_index() );
641 }
642
643 /**
644
645=== modified file 'src/api/CMakeLists.txt'
646--- src/api/CMakeLists.txt 2012-06-18 10:06:47 +0000
647+++ src/api/CMakeLists.txt 2012-06-26 01:34:26 +0000
648@@ -49,6 +49,7 @@
649 fileimpl.cpp
650 serializerimpl.cpp
651 base64impl.cpp
652+ base64_streambuf.cpp
653 uriimpl.cpp
654 uriresolverimpl.cpp
655 uri_resolver_wrappers.cpp
656@@ -56,6 +57,7 @@
657 zorba_functions.cpp
658 annotationimpl.cpp
659 auditimpl.cpp
660+ streambuf.cpp
661 transcode_streambuf.cpp
662 )
663
664
665=== added file 'src/api/base64_streambuf.cpp'
666--- src/api/base64_streambuf.cpp 1970-01-01 00:00:00 +0000
667+++ src/api/base64_streambuf.cpp 2012-06-26 01:34:26 +0000
668@@ -0,0 +1,249 @@
669+/*
670+ * Copyright 2006-2008 The FLWOR Foundation.
671+ *
672+ * Licensed under the Apache License, Version 2.0 (the "License");
673+ * you may not use this file except in compliance with the License.
674+ * You may obtain a copy of the License at
675+ *
676+ * http://www.apache.org/licenses/LICENSE-2.0
677+ *
678+ * Unless required by applicable law or agreed to in writing, software
679+ * distributed under the License is distributed on an "AS IS" BASIS,
680+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
681+ * See the License for the specific language governing permissions and
682+ * limitations under the License.
683+ */
684+
685+#include "stdafx.h"
686+
687+#include <stdexcept>
688+
689+//#define ZORBA_DEBUG_BASE64_STREAMBUF
690+#ifdef ZORBA_DEBUG_BASE64_STREAMBUF
691+# include <stdio.h>
692+#endif
693+
694+#include <zorba/base64_stream.h>
695+
696+#include "util/base64_util.h"
697+
698+using namespace std;
699+
700+namespace zorba {
701+namespace base64 {
702+
703+int const Large_External_Buf_Size = 4096; // must be a multiple of 4
704+
705+///////////////////////////////////////////////////////////////////////////////
706+
707+inline void streambuf::resetg() {
708+ setg( gbuf_, gbuf_ + sizeof gbuf_, gbuf_ + sizeof gbuf_ );
709+}
710+
711+inline void streambuf::resetp() {
712+ plen_ = 0;
713+}
714+
715+inline void streambuf::writep() {
716+ char chunk[4];
717+ orig_buf_->sputn( chunk, base64::encode( pbuf_, plen_, chunk ) );
718+}
719+
720+streambuf::streambuf( std::streambuf *orig ) : orig_buf_( orig ) {
721+ if ( !orig )
722+ throw invalid_argument( "null streambuf" );
723+ clear();
724+}
725+
726+streambuf::~streambuf() {
727+ if ( plen_ )
728+ writep();
729+}
730+
731+void streambuf::clear() {
732+ resetg();
733+ resetp();
734+}
735+
736+void streambuf::imbue( std::locale const &loc ) {
737+ orig_buf_->pubimbue( loc );
738+}
739+
740+streambuf::pos_type streambuf::seekoff( off_type o, ios_base::seekdir d,
741+ ios_base::openmode m ) {
742+ clear();
743+ return orig_buf_->pubseekoff( o, d, m );
744+}
745+
746+streambuf::pos_type streambuf::seekpos( pos_type p, ios_base::openmode m ) {
747+ clear();
748+ return orig_buf_->pubseekpos( p, m );
749+}
750+
751+std::streambuf* streambuf::setbuf( char_type *p, streamsize s ) {
752+ orig_buf_->pubsetbuf( p, s );
753+ return this;
754+}
755+
756+streamsize streambuf::showmanyc() {
757+ return orig_buf_->in_avail();
758+}
759+
760+int streambuf::sync() {
761+ if ( plen_ )
762+ writep();
763+ return orig_buf_->pubsync();
764+}
765+
766+streambuf::int_type streambuf::overflow( int_type c ) {
767+#ifdef ZORBA_DEBUG_BASE64_STREAMBUF
768+ printf( "overflow()\n" );
769+#endif
770+ bool const is_eof = traits_type::eq_int_type( c, traits_type::eof() );
771+ if ( !is_eof )
772+ pbuf_[ plen_++ ] = traits_type::to_char_type( c );
773+ if ( plen_ == sizeof pbuf_ || (is_eof && plen_) ) {
774+ writep();
775+ resetp();
776+ }
777+ return c;
778+}
779+
780+streambuf::int_type streambuf::pbackfail( int_type c ) {
781+ if ( gptr() > eback() )
782+ gbump( -1 );
783+ return orig_buf_->sputbackc( traits_type::to_char_type( c ) );
784+}
785+
786+streambuf::int_type streambuf::underflow() {
787+#ifdef ZORBA_DEBUG_BASE64_STREAMBUF
788+ printf( "underflow()\n" );
789+#endif
790+ char chunk[4];
791+ int chunk_len = 0;
792+
793+ while ( gptr() >= egptr() ) {
794+ int_type const c = orig_buf_->sbumpc();
795+ bool is_eof = false;
796+ if ( traits_type::eq_int_type( c, traits_type::eof() ) ) {
797+ if ( !chunk_len )
798+ return traits_type::eof();
799+ is_eof = true;
800+ } else {
801+ chunk[ chunk_len++ ] = traits_type::to_char_type( c );
802+ }
803+ if ( chunk_len == sizeof chunk || (is_eof && chunk_len) ) {
804+ streamsize const n = base64::decode( chunk, chunk_len, eback() );
805+ setg( gbuf_, gbuf_, gbuf_ + n );
806+ }
807+ }
808+ return traits_type::to_int_type( *gptr() );
809+}
810+
811+streamsize streambuf::xsgetn( char_type *to, streamsize size ) {
812+#ifdef ZORBA_DEBUG_BASE64_STREAMBUF
813+ printf( "xsgetn()\n" );
814+#endif
815+ streamsize return_size = 0;
816+
817+ if ( streamsize const gsize = egptr() - gptr() ) {
818+ //
819+ // Get any chunk fragment pending the the get buffer first.
820+ //
821+ streamsize const n = min( gsize, size );
822+ traits_type::copy( to, gptr(), n );
823+ gbump( n );
824+ to += n;
825+ size -= n, return_size += n;
826+ }
827+
828+ //
829+ // Must get bytes in terms of encoded size.
830+ //
831+ size = base64::encoded_size( size );
832+
833+ while ( size ) {
834+ char ebuf[ Large_External_Buf_Size ];
835+ streamsize const get = min( (streamsize)(sizeof ebuf), size );
836+ if ( streamsize got = orig_buf_->sgetn( ebuf, get ) ) {
837+ streamsize const decoded = base64::decode( ebuf, got, to );
838+ to += decoded;
839+ size -= got, return_size += decoded;
840+ } else
841+ break;
842+ }
843+
844+ return return_size;
845+}
846+
847+streamsize streambuf::xsputn( char_type const *from, streamsize size ) {
848+#ifdef ZORBA_DEBUG_BASE64_STREAMBUF
849+ printf( "xsputn()\n" );
850+#endif
851+ streamsize return_size = 0;
852+
853+ //
854+ // Put any chunk fragment pending in the put buffer by completing it first.
855+ //
856+ while ( plen_ && size ) {
857+ overflow( *from );
858+ ++from, --size, ++return_size;
859+ }
860+
861+ while ( size >= 3 ) {
862+ char ebuf[ Large_External_Buf_Size ];
863+ streamsize const put = min( (streamsize)(sizeof ebuf), size );
864+ streamsize const encoded = base64::encode( from, put, ebuf );
865+ orig_buf_->sputn( ebuf, encoded );
866+ from += put, size -= put, return_size += put;
867+ }
868+
869+ //
870+ // Put any remaining chunk fragment into the put buffer.
871+ //
872+ if ( size ) {
873+ traits_type::copy( pbuf_, from, size );
874+ plen_ = size;
875+ }
876+
877+ return return_size;
878+}
879+
880+///////////////////////////////////////////////////////////////////////////////
881+
882+} // namespace base64
883+
884+namespace internal {
885+namespace base64 {
886+
887+// Both new & delete are done inside Zorba rather than in the header to
888+// guarantee that they're cross-DLL-boundary safe on Windows.
889+
890+std::streambuf* alloc_streambuf( std::streambuf *orig ) {
891+ return new zorba::base64::streambuf( orig );
892+}
893+
894+int get_streambuf_index() {
895+ //
896+ // This function is out-of-line because it has a static constant within it.
897+ // It has a static constant within it to guarantee (1) initialization before
898+ // use and (2) initialization happens exactly once.
899+ //
900+ // See: "Standard C++ IOStreams and Locales: Advanced Programmer's Guide and
901+ // Reference," Angelika Langer and Klaus Kreft, Addison-Wesley, 2000, section
902+ // 3.3.1.1: "Initializing and Maintaining the iword/pword Index."
903+ //
904+ // See: "The C++ Programming Language," Bjarne Stroustrup, Addison-Wesley,
905+ // 2000, section 10.4.8: "Local Static Store."
906+ //
907+ static int const index = ios_base::xalloc();
908+ return index;
909+}
910+
911+} // namespace base64
912+} // namespace internal
913+
914+///////////////////////////////////////////////////////////////////////////////
915+
916+} // namespace zorba
917+/* vim:set et sw=2 ts=2: */
918
919=== modified file 'src/api/base64impl.cpp'
920--- src/api/base64impl.cpp 2012-06-18 10:06:47 +0000
921+++ src/api/base64impl.cpp 2012-06-26 01:34:26 +0000
922@@ -17,45 +17,70 @@
923
924 #include <sstream>
925 #include <zorba/base64.h>
926+#include <zorba/diagnostic_list.h>
927
928 #include <zorba/config.h>
929 #include <zorba/zorba_string.h>
930
931-#include "zorbatypes/binary.h"
932+#include "diagnostics/dict.h"
933+#include "diagnostics/xquery_exception.h"
934+#include "util/base64_util.h"
935
936+#define CATCH_BASE64_EXCEPTION() \
937+ catch ( base64::exception const &e ) { \
938+ throw XQUERY_EXCEPTION( \
939+ err::FORG0001, ERROR_PARAMS( e.invalid_char(), ZED( Base64BadChar ) ) \
940+ ); \
941+ } \
942+ catch ( std::invalid_argument const& ) { \
943+ throw XQUERY_EXCEPTION( \
944+ err::FORG0001, ERROR_PARAMS( "", ZED( Base64Multiple4 ) ) \
945+ ); \
946+ }
947
948 namespace zorba {
949 namespace encoding {
950
951 String Base64::encode(const String& aString)
952 {
953- std::stringstream lStream;
954- lStream << aString;
955-
956- return encode(lStream);
957+ String result;
958+ base64::encode( aString.data(), aString.size(), &result );
959+ return result;
960 }
961
962
963 String Base64::encode(std::istream& aStream)
964 {
965- return zorba::Base64::encode(aStream).str();
966+ String result;
967+ base64::encode( aStream, &result );
968+ return result;
969 }
970
971
972 String Base64::decode(const String& aString)
973 {
974- std::stringstream lStream;
975- lStream << aString;
976- return decode(lStream);
977+ try {
978+ String result;
979+ base64::decode(
980+ aString.data(), aString.size(), &result, base64::dopt_ignore_ws
981+ );
982+ return result;
983+ }
984+ CATCH_BASE64_EXCEPTION()
985 }
986
987
988 String Base64::decode(std::istream& aStream)
989 {
990- return zorba::Base64::decode(aStream).str();
991+ try {
992+ String result;
993+ base64::decode( aStream, &result, base64::dopt_ignore_ws );
994+ return result;
995+ }
996+ CATCH_BASE64_EXCEPTION()
997 }
998
999
1000-} /* end namespace encoding */
1001-} /* end namespace zorba */
1002+} // namespace encoding
1003+} // namespace zorba
1004 /* vim:set et sw=2 ts=2: */
1005
1006=== modified file 'src/api/serialization/serializer.cpp'
1007--- src/api/serialization/serializer.cpp 2012-06-18 10:06:47 +0000
1008+++ src/api/serialization/serializer.cpp 2012-06-26 01:34:26 +0000
1009@@ -2300,7 +2300,9 @@
1010 std::istream& stream = item->getStream();
1011 if (item->isEncoded())
1012 {
1013- tr << Base64::decode(stream);
1014+ zstring decoded;
1015+ Base64::decode(stream, &decoded);
1016+ tr << decoded;
1017 }
1018 else
1019 {
1020@@ -2322,9 +2324,9 @@
1021
1022 if (item->isEncoded())
1023 {
1024- std::stringstream tmp;
1025- tmp.write(value, len);
1026- tr << Base64::decode(tmp);
1027+ zstring decoded;
1028+ Base64::decode(value, len, &decoded);
1029+ tr << decoded;
1030 }
1031 else
1032 {
1033
1034=== added file 'src/api/streambuf.cpp'
1035--- src/api/streambuf.cpp 1970-01-01 00:00:00 +0000
1036+++ src/api/streambuf.cpp 2012-06-26 01:34:26 +0000
1037@@ -0,0 +1,48 @@
1038+/*
1039+ * Copyright 2006-2008 The FLWOR Foundation.
1040+ *
1041+ * Licensed under the Apache License, Version 2.0 (the "License");
1042+ * you may not use this file except in compliance with the License.
1043+ * You may obtain a copy of the License at
1044+ *
1045+ * http://www.apache.org/licenses/LICENSE-2.0
1046+ *
1047+ * Unless required by applicable law or agreed to in writing, software
1048+ * distributed under the License is distributed on an "AS IS" BASIS,
1049+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1050+ * See the License for the specific language governing permissions and
1051+ * limitations under the License.
1052+ */
1053+
1054+#include "stdafx.h"
1055+#include <zorba/internal/streambuf.h>
1056+
1057+using namespace std;
1058+
1059+namespace zorba {
1060+namespace internal {
1061+
1062+///////////////////////////////////////////////////////////////////////////////
1063+
1064+// "delete" is done here inside Zorba rather than in the header to guarantee
1065+// that it's cross-DLL-boundary safe on Windows.
1066+
1067+void dealloc_streambuf( streambuf *buf ) {
1068+ delete buf;
1069+}
1070+
1071+void stream_callback( ios_base::event e, ios_base &ios, int index ) {
1072+ //
1073+ // See: "Standard C++ IOStreams and Locales: Advanced Programmer's Guide and
1074+ // Reference," Angelika Langer and Klaus Kreft, Addison-Wesley, 2000, section
1075+ // 3.3.1.4: "Using Stream Callbacks for Memory Management."
1076+ //
1077+ if ( e == ios_base::erase_event )
1078+ delete static_cast<streambuf*>( ios.pword( index ) );
1079+}
1080+
1081+///////////////////////////////////////////////////////////////////////////////
1082+
1083+} // namespace internal
1084+} // namespace zorba
1085+/* vim:set et sw=2 ts=2: */
1086
1087=== modified file 'src/api/transcode_streambuf.cpp'
1088--- src/api/transcode_streambuf.cpp 2012-06-18 10:06:47 +0000
1089+++ src/api/transcode_streambuf.cpp 2012-06-26 01:34:26 +0000
1090@@ -81,8 +81,7 @@
1091 return proxy_buf_->sgetn( to, size );
1092 }
1093
1094-streamsize streambuf::xsputn( char_type const *from,
1095- streamsize size ) {
1096+streamsize streambuf::xsputn( char_type const *from, streamsize size ) {
1097 return proxy_buf_->sputn( from, size );
1098 }
1099
1100@@ -103,19 +102,15 @@
1101 ///////////////////////////////////////////////////////////////////////////////
1102
1103 namespace internal {
1104+namespace transcode {
1105
1106 // Both new & delete are done here inside Zorba rather than in the header to
1107 // guarantee that they're cross-DLL-boundary safe on Windows.
1108
1109-zorba::transcode::streambuf*
1110-alloc_streambuf( char const *charset, std::streambuf *orig ) {
1111+std::streambuf* alloc_streambuf( char const *charset, std::streambuf *orig ) {
1112 return new zorba::transcode::streambuf( charset, orig );
1113 }
1114
1115-void dealloc_streambuf( zorba::transcode::streambuf *buf ) {
1116- delete buf;
1117-}
1118-
1119 int get_streambuf_index() {
1120 //
1121 // This function is out-of-line because it has a static constant within it.
1122@@ -133,16 +128,7 @@
1123 return index;
1124 }
1125
1126-void stream_callback( ios_base::event e, ios_base &ios, int index ) {
1127- //
1128- // See: "Standard C++ IOStreams and Locales: Advanced Programmer's Guide and
1129- // Reference," Angelika Langer and Klaus Kreft, Addison-Wesley, 2000, section
1130- // 3.3.1.4: "Using Stream Callbacks for Memory Management."
1131- //
1132- if ( e == ios_base::erase_event )
1133- delete static_cast<streambuf*>( ios.pword( index ) );
1134-}
1135-
1136+} // namespace transcode
1137 } // namespace internal
1138
1139 ///////////////////////////////////////////////////////////////////////////////
1140
1141=== modified file 'src/diagnostics/diagnostic_en.xml'
1142--- src/diagnostics/diagnostic_en.xml 2012-06-18 10:06:47 +0000
1143+++ src/diagnostics/diagnostic_en.xml 2012-06-26 01:34:26 +0000
1144@@ -2888,14 +2888,6 @@
1145 <value>invalid Base64 character</value>
1146 </entry>
1147
1148- <entry key="Base64Equals">
1149- <value>in Base64, '=' must be at the end and followed by one of [AEIMQUYcgkosw048]</value>
1150- </entry>
1151-
1152- <entry key="Base64EqualsEquals">
1153- <value>in Base64, "==" must be at the end and followed by one of [AQgw]</value>
1154- </entry>
1155-
1156 <entry key="Base64Multiple4">
1157 <value>Base64 data must be a multiple of 4 characters</value>
1158 </entry>
1159
1160=== modified file 'src/diagnostics/pregenerated/dict_en.cpp'
1161--- src/diagnostics/pregenerated/dict_en.cpp 2012-06-18 10:06:47 +0000
1162+++ src/diagnostics/pregenerated/dict_en.cpp 2012-06-26 01:34:26 +0000
1163@@ -546,8 +546,6 @@
1164 { "~BadXMLNoOpeningTag", "closing tag without matching opening tag" },
1165 { "~BadXQueryVersion", "unsupported XQuery version" },
1166 { "~Base64BadChar", "invalid Base64 character" },
1167- { "~Base64Equals", "in Base64, '=' must be at the end and followed by one of [AEIMQUYcgkosw048]" },
1168- { "~Base64EqualsEquals", "in Base64, \"==\" must be at the end and followed by one of [AQgw]" },
1169 { "~Base64Multiple4", "Base64 data must be a multiple of 4 characters" },
1170 { "~BaseURI", "base URI" },
1171 { "~BoxCondTooManyColumns", "box condition has more columns than index" },
1172
1173=== modified file 'src/runtime/base64/base64_impl.cpp'
1174--- src/runtime/base64/base64_impl.cpp 2012-06-18 10:06:47 +0000
1175+++ src/runtime/base64/base64_impl.cpp 2012-06-26 01:34:26 +0000
1176@@ -66,7 +66,7 @@
1177 {
1178 if (lItem->isEncoded())
1179 {
1180- lResultString = Base64::decode(lItem->getStream());
1181+ Base64::decode(lItem->getStream(), &lResultString);
1182 }
1183 else
1184 {
1185@@ -93,10 +93,7 @@
1186
1187 if (lItem->isEncoded())
1188 {
1189- std::vector<char> encoded(lContent, lContent+lSize);
1190- std::vector<char> decoded;
1191- Base64::decode(encoded, decoded);
1192- lResultString.insert(0, &decoded[0], decoded.size());
1193+ Base64::decode( lContent, lSize, &lResultString );
1194 }
1195 else
1196 {
1197@@ -156,8 +153,8 @@
1198 // create a base64Binary item
1199 // the content is the non-encoded string
1200 GENV_ITEMFACTORY->createBase64Binary(
1201- result, lTmpString.c_str(), lTmpString.size(), false
1202- );
1203+ result, lTmpString.c_str(), lTmpString.size(), false
1204+ );
1205 STACK_PUSH (true, state);
1206 }
1207 STACK_END (state);
1208
1209=== modified file 'src/unit_tests/CMakeLists.txt'
1210--- src/unit_tests/CMakeLists.txt 2012-06-18 10:06:47 +0000
1211+++ src/unit_tests/CMakeLists.txt 2012-06-26 01:34:26 +0000
1212@@ -13,6 +13,8 @@
1213 # limitations under the License.
1214
1215 SET(UNIT_TEST_SRCS
1216+ test_base64.cpp
1217+ test_base64_streambuf.cpp
1218 string_instantiate.cpp
1219 string.cpp
1220 test_uri.cpp
1221
1222=== added file 'src/unit_tests/test_base64.cpp'
1223--- src/unit_tests/test_base64.cpp 1970-01-01 00:00:00 +0000
1224+++ src/unit_tests/test_base64.cpp 2012-06-26 01:34:26 +0000
1225@@ -0,0 +1,286 @@
1226+/*
1227+ * Copyright 2006-2008 The FLWOR Foundation.
1228+ *
1229+ * Licensed under the Apache License, Version 2.0 (the "License");
1230+ * you may not use this file except in compliance with the License.
1231+ * You may obtain a copy of the License at
1232+ *
1233+ * http://www.apache.org/licenses/LICENSE-2.0
1234+ *
1235+ * Unless required by applicable law or agreed to in writing, software
1236+ * distributed under the License is distributed on an "AS IS" BASIS,
1237+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1238+ * See the License for the specific language governing permissions and
1239+ * limitations under the License.
1240+ */
1241+
1242+#include "stdafx.h"
1243+#include <cstring>
1244+#include <iostream>
1245+#include <sstream>
1246+#include <stdexcept>
1247+#include <string>
1248+
1249+#include "util/base64_util.h"
1250+
1251+using namespace std;
1252+using namespace zorba;
1253+
1254+struct test {
1255+ char const *input;
1256+ char const *expected;
1257+};
1258+
1259+///////////////////////////////////////////////////////////////////////////////
1260+
1261+static int failures;
1262+
1263+static bool assert_true( int no, char const *expr, int line, bool result ) {
1264+ if ( !result ) {
1265+ cout << '#' << no << " FAILED, line " << line << ": " << expr << endl;
1266+ ++failures;
1267+ }
1268+ return result;
1269+}
1270+
1271+static void print_exception( int no, char const *expr, int line,
1272+ std::exception const &e ) {
1273+ assert_true( no, expr, line, false );
1274+ cout << "+ exception: " << e.what() << endl;
1275+}
1276+
1277+#define ASSERT_TRUE( NO, EXPR ) assert_true( NO, #EXPR, __LINE__, !!(EXPR) )
1278+
1279+#define ASSERT_NO_EXCEPTION( NO, EXPR ) \
1280+ try { EXPR; } \
1281+ catch ( std::exception const &e ) { print_exception( NO, #EXPR, __LINE__, e ); } \
1282+ catch ( ... ) { assert_true( NO, #EXPR, __LINE__, false ); }
1283+
1284+#define ASSERT_EXCEPTION( NO, EXPR, EXCEPTION ) \
1285+ try { EXPR; assert_true( NO, #EXPR, __LINE__, false ); } \
1286+ catch ( EXCEPTION const& ) { }
1287+
1288+///////////////////////////////////////////////////////////////////////////////}
1289+
1290+static void test_decode_buf_to_buf( int no, string const &in,
1291+ string const &expected ) {
1292+ base64::size_type n;
1293+ char out[ 1024 ];
1294+ ASSERT_NO_EXCEPTION(
1295+ no, n = base64::decode( in.data(), in.size(), out, base64::dopt_any_len )
1296+ );
1297+ ASSERT_TRUE( no, n == expected.size() );
1298+ out[ n ] = '\0';
1299+ ASSERT_TRUE( no, out == expected );
1300+}
1301+
1302+static void test_decode_buf_to_string( int no, string const &in,
1303+ string const &expected ) {
1304+ base64::size_type n;
1305+ string out;
1306+ ASSERT_NO_EXCEPTION(
1307+ no,
1308+ n = base64::decode( in.data(), in.size(), &out, base64::dopt_any_len )
1309+ );
1310+ ASSERT_TRUE( no, n == expected.size() );
1311+ ASSERT_TRUE( no, out.size() == expected.size() );
1312+ ASSERT_TRUE( no, out == expected );
1313+}
1314+
1315+static void test_decode_buf_to_vector( int no, string const &in,
1316+ string const &expected ) {
1317+ base64::size_type n;
1318+ vector<char> out;
1319+ ASSERT_NO_EXCEPTION(
1320+ no,
1321+ n = base64::decode( in.data(), in.size(), &out, base64::dopt_any_len )
1322+ );
1323+ ASSERT_TRUE( no, n == expected.size() );
1324+ ASSERT_TRUE( no, out.size() == expected.size() );
1325+ ASSERT_TRUE( no, !strncmp( &out[0], expected.data(), expected.size() ) );
1326+}
1327+
1328+static void test_decode_stream_to_stream( int no, string const &in,
1329+ string const &expected ) {
1330+ base64::size_type n;
1331+ istringstream sin( in );
1332+ ostringstream sout;
1333+ ASSERT_NO_EXCEPTION(
1334+ no, n = base64::decode( sin, sout, base64::dopt_any_len )
1335+ );
1336+ ASSERT_TRUE( no, n == expected.size() );
1337+ ASSERT_TRUE( no, sout.str().size() == expected.size() );
1338+ ASSERT_TRUE( no, sout.str() == expected );
1339+}
1340+
1341+static void test_decode_stream_to_string( int no, string const &in,
1342+ string const &expected ) {
1343+ base64::size_type n;
1344+ istringstream sin( in );
1345+ string out;
1346+ ASSERT_NO_EXCEPTION(
1347+ no, n = base64::decode( sin, &out, base64::dopt_any_len )
1348+ );
1349+ ASSERT_TRUE( no, n == expected.size() );
1350+ ASSERT_TRUE( no, out.size() == expected.size() );
1351+ ASSERT_TRUE( no, out == expected );
1352+}
1353+
1354+static void test_decode_stream_to_vector( int no, string const &in,
1355+ string const &expected ) {
1356+ base64::size_type n;
1357+ istringstream sin( in );
1358+ vector<char> out;
1359+ ASSERT_NO_EXCEPTION(
1360+ no, n = base64::decode( sin, &out, base64::dopt_any_len )
1361+ );
1362+ ASSERT_TRUE( no, n == expected.size() );
1363+ ASSERT_TRUE( no, out.size() == expected.size() );
1364+ ASSERT_TRUE( no, !strncmp( &out[0], expected.data(), expected.size() ) );
1365+}
1366+
1367+static void test_decode_exception( int no, string const &in ) {
1368+ char out[ 1024 ];
1369+ ASSERT_EXCEPTION(
1370+ no, base64::decode( in.data(), in.size(), out ), invalid_argument
1371+ );
1372+}
1373+
1374+///////////////////////////////////////////////////////////////////////////////
1375+
1376+static void test_encode_buf_to_buf( int no, string const &in,
1377+ string const &expected ) {
1378+ char out[ 1024 ];
1379+ base64::size_type const n = base64::encode( in.data(), in.size(), out );
1380+ ASSERT_TRUE( no, n == expected.size() );
1381+ out[ n ] = '\0';
1382+ ASSERT_TRUE( no, out == expected );
1383+}
1384+
1385+static void test_encode_buf_to_string( int no, string const &in,
1386+ string const &expected ) {
1387+ base64::size_type n;
1388+ string out;
1389+ ASSERT_NO_EXCEPTION( no, n = base64::encode( in.data(), in.size(), &out ) );
1390+ ASSERT_TRUE( no, n == expected.size() );
1391+ ASSERT_TRUE( no, out.size() == expected.size() );
1392+ ASSERT_TRUE( no, out == expected );
1393+}
1394+
1395+static void test_encode_buf_to_vector( int no, string const &in,
1396+ string const &expected ) {
1397+ base64::size_type n;
1398+ vector<char> out;
1399+ ASSERT_NO_EXCEPTION( no, n = base64::encode( in.data(), in.size(), &out ) );
1400+ ASSERT_TRUE( no, n == expected.size() );
1401+ ASSERT_TRUE( no, out.size() == expected.size() );
1402+ ASSERT_TRUE( no, !strncmp( &out[0], expected.data(), expected.size() ) );
1403+}
1404+
1405+static void test_encode_stream_to_stream( int no, string const &in,
1406+ string const &expected ) {
1407+ base64::size_type n;
1408+ istringstream sin( in );
1409+ ostringstream sout;
1410+ ASSERT_NO_EXCEPTION( no, n = base64::encode( sin, sout ) );
1411+ ASSERT_TRUE( no, n == expected.size() );
1412+ ASSERT_TRUE( no, sout.str().size() == expected.size() );
1413+ ASSERT_TRUE( no, sout.str() == expected );
1414+}
1415+
1416+static void test_encode_stream_to_string( int no, string const &in,
1417+ string const &expected ) {
1418+ base64::size_type n;
1419+ istringstream sin( in );
1420+ string out;
1421+ ASSERT_NO_EXCEPTION( no, n = base64::encode( sin, &out ) );
1422+ ASSERT_TRUE( no, n == expected.size() );
1423+ ASSERT_TRUE( no, out.size() == expected.size() );
1424+ ASSERT_TRUE( no, out == expected );
1425+}
1426+
1427+static void test_encode_stream_to_vector( int no, string const &in,
1428+ string const &expected ) {
1429+ base64::size_type n;
1430+ istringstream sin( in );
1431+ vector<char> out;
1432+ ASSERT_NO_EXCEPTION( no, n = base64::encode( sin, &out ) );
1433+ ASSERT_TRUE( no, n == expected.size() );
1434+ ASSERT_TRUE( no, out.size() == expected.size() );
1435+ ASSERT_TRUE( no, !strncmp( &out[0], expected.data(), expected.size() ) );
1436+}
1437+
1438+///////////////////////////////////////////////////////////////////////////////
1439+
1440+static test const encode_tests[] = {
1441+ /* 0 */ { "Now is the time", "Tm93IGlzIHRoZSB0aW1l" },
1442+ /* 1 */ { "Now is the time.", "Tm93IGlzIHRoZSB0aW1lLg==" },
1443+ /* 2 */ { "Now is the time..", "Tm93IGlzIHRoZSB0aW1lLi4=" },
1444+
1445+ { 0, 0 }
1446+};
1447+
1448+static test const decode_tests[] = {
1449+ /* 3 */ { "Tm93IGlzIHRoZSB0aW1l", "Now is the time" },
1450+ /* 4 */ { "Tm93IGlzIHRoZSB0aW1lLg==", "Now is the time." },
1451+ /* 5 */ { "Tm93IGlzIHRoZSB0aW1lLi4=", "Now is the time.." },
1452+
1453+ // incomplete Base64 encodings
1454+ /* 6 */ { "Tm93IGlzIHRoZSB0aW1", "Now is the tim" },
1455+ /* 7 */ { "Tm93IGlzIHRoZSB0aW", "Now is the ti" },
1456+ /* 8 */ { "Tm93IGlzIHRoZSB0a", "Now is the t" },
1457+
1458+ { 0, 0 }
1459+};
1460+
1461+static char const *const decode_exception_tests[] = {
1462+ "=",
1463+ "_m93",
1464+ "T_93",
1465+ "Tm_3",
1466+ "Tm9_",
1467+ "=m93",
1468+ "T=93",
1469+ "Tm=3",
1470+ "Tm93=",
1471+ "ZmX=",
1472+ "ZX==",
1473+ "ZX===",
1474+ 0
1475+};
1476+
1477+namespace zorba {
1478+namespace UnitTests {
1479+
1480+int test_base64( int, char*[] ) {
1481+ int test_no = 0;
1482+
1483+ for ( test const *t = encode_tests; t->input; ++t, ++test_no ) {
1484+ test_encode_buf_to_buf( test_no, t->input, t->expected );
1485+ test_encode_buf_to_string( test_no, t->input, t->expected );
1486+ test_encode_buf_to_vector( test_no, t->input, t->expected );
1487+ test_encode_stream_to_stream( test_no, t->input, t->expected );
1488+ test_encode_stream_to_string( test_no, t->input, t->expected );
1489+ test_encode_stream_to_vector( test_no, t->input, t->expected );
1490+ }
1491+
1492+ for ( test const *t = decode_tests; t->input; ++t, ++test_no ) {
1493+ test_decode_buf_to_buf( test_no, t->input, t->expected );
1494+ test_decode_buf_to_string( test_no, t->input, t->expected );
1495+ test_decode_buf_to_vector( test_no, t->input, t->expected );
1496+ test_decode_stream_to_stream( test_no, t->input, t->expected );
1497+ test_decode_stream_to_string( test_no, t->input, t->expected );
1498+ test_decode_stream_to_vector( test_no, t->input, t->expected );
1499+ }
1500+
1501+ for ( char const *const *t = decode_exception_tests; *t; ++t, ++test_no )
1502+ test_decode_exception( test_no, *t );
1503+
1504+ cout << failures << " test(s) failed\n";
1505+ return failures ? 1 : 0;
1506+}
1507+
1508+} // namespace UnitTests
1509+} // namespace zorba
1510+
1511+/* vim:set et sw=2 ts=2: */
1512
1513=== added file 'src/unit_tests/test_base64_streambuf.cpp'
1514--- src/unit_tests/test_base64_streambuf.cpp 1970-01-01 00:00:00 +0000
1515+++ src/unit_tests/test_base64_streambuf.cpp 2012-06-26 01:34:26 +0000
1516@@ -0,0 +1,141 @@
1517+/*
1518+ * Copyright 2006-2008 The FLWOR Foundation.
1519+ *
1520+ * Licensed under the Apache License, Version 2.0 (the "License");
1521+ * you may not use this file except in compliance with the License.
1522+ * You may obtain a copy of the License at
1523+ *
1524+ * http://www.apache.org/licenses/LICENSE-2.0
1525+ *
1526+ * Unless required by applicable law or agreed to in writing, software
1527+ * distributed under the License is distributed on an "AS IS" BASIS,
1528+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1529+ * See the License for the specific language governing permissions and
1530+ * limitations under the License.
1531+ */
1532+
1533+#include "stdafx.h"
1534+#include <fstream>
1535+#include <iostream>
1536+#include <sstream>
1537+
1538+#include <zorba/base64_stream.h>
1539+
1540+using namespace std;
1541+using namespace zorba;
1542+
1543+struct test {
1544+ char const *raw_str;
1545+ char const *b64_str;
1546+};
1547+
1548+///////////////////////////////////////////////////////////////////////////////
1549+
1550+static int failures;
1551+
1552+static bool assert_true( int no, char const *expr, int line, bool result ) {
1553+ if ( !result ) {
1554+ cout << '#' << no << " FAILED, line " << line << ": " << expr << endl;
1555+ ++failures;
1556+ }
1557+ return result;
1558+}
1559+
1560+static void print_exception( int no, char const *expr, int line,
1561+ std::exception const &e ) {
1562+ assert_true( no, expr, line, false );
1563+ cout << "+ exception: " << e.what() << endl;
1564+}
1565+
1566+#define ASSERT_TRUE( NO, EXPR ) assert_true( NO, #EXPR, __LINE__, !!(EXPR) )
1567+
1568+#define ASSERT_TRUE_AND_NO_EXCEPTION( NO, EXPR ) \
1569+ try { ASSERT_TRUE( NO, EXPR ); } \
1570+ catch ( std::exception const &e ) { print_exception( NO, #EXPR, __LINE__, e ); }
1571+
1572+///////////////////////////////////////////////////////////////////////////////
1573+
1574+static bool test_getline( test const *t ) {
1575+ string const b64_str( t->b64_str );
1576+ istringstream iss( b64_str );
1577+ base64::streambuf b64_sbuf( iss.rdbuf() );
1578+ iss.ios::rdbuf( &b64_sbuf );
1579+
1580+ char raw_buf[ 1024 ];
1581+ iss.getline( raw_buf, sizeof raw_buf );
1582+ if ( iss.gcount() ) {
1583+ string const raw_str( raw_buf );
1584+ return raw_str == t->raw_str;
1585+ }
1586+ return false;
1587+}
1588+
1589+static bool test_read( test const *t ) {
1590+ string const b64_str( t->b64_str );
1591+ istringstream iss( b64_str );
1592+ base64::streambuf b64_sbuf( iss.rdbuf() );
1593+ iss.ios::rdbuf( &b64_sbuf );
1594+
1595+ char raw_buf[ 1024 ];
1596+ iss.read( raw_buf, sizeof raw_buf );
1597+ if ( iss.gcount() ) {
1598+ string const raw_str( raw_buf, iss.gcount() );
1599+ return raw_str == t->raw_str;
1600+ }
1601+ return false;
1602+}
1603+
1604+static bool test_insertion( test const *t ) {
1605+ ostringstream oss;
1606+ base64::streambuf b64_sbuf( oss.rdbuf() );
1607+ oss.ios::rdbuf( &b64_sbuf );
1608+
1609+ oss << t->raw_str << flush;
1610+ string const b64_str( oss.str() );
1611+
1612+ string const expected_b64_str( t->b64_str );
1613+ return b64_str == expected_b64_str;
1614+}
1615+
1616+static bool test_put( test const *t ) {
1617+ ostringstream oss;
1618+ { // local scope
1619+ base64::auto_attach<ostringstream> const raii( oss );
1620+
1621+ for ( char const *c = t->raw_str; *c; ++c )
1622+ oss.put( *c );
1623+ } // local scope
1624+ string const b64_str( oss.str() );
1625+
1626+ string const expected_b64_str( t->b64_str );
1627+ return b64_str == expected_b64_str;
1628+}
1629+
1630+///////////////////////////////////////////////////////////////////////////////
1631+
1632+static test const tests[] = {
1633+ /* 0 */ { "Now ", "Tm93IA==" },
1634+ /* 1 */ { "Now is the time", "Tm93IGlzIHRoZSB0aW1l" },
1635+ /* 2 */ { "Now is the time.", "Tm93IGlzIHRoZSB0aW1lLg==" },
1636+ /* 3 */ { "Now is the time..", "Tm93IGlzIHRoZSB0aW1lLi4=" },
1637+ { 0, 0 }
1638+};
1639+
1640+namespace zorba {
1641+namespace UnitTests {
1642+
1643+int test_base64_streambuf( int, char*[] ) {
1644+ int test_no = 0;
1645+ for ( test const *t = tests; t->raw_str; ++t, ++test_no ) {
1646+ ASSERT_TRUE_AND_NO_EXCEPTION( test_no, test_getline( t ) );
1647+ ASSERT_TRUE_AND_NO_EXCEPTION( test_no, test_read( t ) );
1648+ ASSERT_TRUE_AND_NO_EXCEPTION( test_no, test_insertion( t ) );
1649+ ASSERT_TRUE_AND_NO_EXCEPTION( test_no, test_put( t ) );
1650+ }
1651+ cout << failures << " test(s) failed\n";
1652+ return failures ? 1 : 0;
1653+}
1654+
1655+} // namespace UnitTests
1656+} // namespace zorba
1657+/* vim:set et sw=2 ts=2: */
1658
1659=== modified file 'src/unit_tests/unit_test_list.h'
1660--- src/unit_tests/unit_test_list.h 2012-06-18 10:06:47 +0000
1661+++ src/unit_tests/unit_test_list.h 2012-06-26 01:34:26 +0000
1662@@ -21,24 +21,21 @@
1663
1664 #include <zorba/config.h>
1665
1666-namespace zorba
1667-{
1668+namespace zorba {
1669+namespace UnitTests {
1670
1671-namespace UnitTests
1672-{
1673 int runUriTest(int argc, char* argv[]);
1674 int runDebuggerProtocolTest(int argc, char* argv[]);
1675+ int test_base64( int, char*[] );
1676+ int test_base64_streambuf( int, char*[] );
1677+ int test_fs_iterator( int, char*[] );
1678 int test_string( int, char*[] );
1679 int test_unique_ptr( int, char*[] );
1680- int test_fs_iterator( int, char*[] );
1681 #ifndef ZORBA_NO_FULL_TEXT
1682 int test_stemmer( int, char*[] );
1683 int test_thesaurus( int, char*[] );
1684 int test_tokenizer( int, char*[] );
1685 #endif /* ZORBA_NO_FULL_TEXT */
1686- /**
1687- * ADD NEW UNIT TESTS HERE
1688- */
1689 #ifndef ZORBA_NO_ICU
1690 int test_icu_streambuf( int, char*[] );
1691 #endif /* ZORBA_NO_ICU */
1692@@ -46,9 +43,7 @@
1693
1694 void initializeTestList();
1695
1696-
1697-};
1698-} /* namespace zorba */
1699-
1700+} // namespace UnitTests
1701+} // namespace zorba
1702 #endif /* ZORBA_UNIT_TEST_LIST_H */
1703 /* vim:set et sw=2 ts=2: */
1704
1705=== modified file 'src/unit_tests/unit_tests.cpp'
1706--- src/unit_tests/unit_tests.cpp 2012-06-18 10:06:47 +0000
1707+++ src/unit_tests/unit_tests.cpp 2012-06-26 01:34:26 +0000
1708@@ -28,9 +28,7 @@
1709 using namespace std;
1710
1711 namespace zorba {
1712-
1713-namespace UnitTests
1714-{
1715+namespace UnitTests {
1716
1717 map<string,libunittestfunc> libunittests;
1718
1719@@ -39,14 +37,16 @@
1720 */
1721 void initializeTestList()
1722 {
1723+ libunittests["base64"] = test_base64;
1724+ libunittests["base64_streambuf"] = test_base64_streambuf;
1725+ libunittests["fs_iterator"] = test_fs_iterator;
1726+ libunittests["json_parser"] = json_parser;
1727 libunittests["string"] = test_string;
1728+ libunittests["unique_ptr"] = test_unique_ptr;
1729 libunittests["uri"] = runUriTest;
1730- libunittests["fs_iterator"] = test_fs_iterator;
1731 #ifndef ZORBA_NO_ICU
1732 libunittests["icu_streambuf"] = test_icu_streambuf;
1733 #endif /* ZORBA_NO_ICU */
1734- libunittests["json_parser"] = json_parser;
1735- libunittests["unique_ptr"] = test_unique_ptr;
1736 #ifndef ZORBA_NO_FULL_TEXT
1737 libunittests["stemmer"] = test_stemmer;
1738 libunittests["thesaurus"] = test_thesaurus;
1739@@ -78,7 +78,6 @@
1740 }
1741
1742
1743-} /* namespace UnitTests */
1744-} /* namespace zorba */
1745-
1746+} // namespace UnitTests
1747+} // namespace zorba
1748 /* vim:set et sw=2 ts=2: */
1749
1750=== modified file 'src/util/CMakeLists.txt'
1751--- src/util/CMakeLists.txt 2012-06-18 10:06:47 +0000
1752+++ src/util/CMakeLists.txt 2012-06-26 01:34:26 +0000
1753@@ -14,6 +14,7 @@
1754
1755 SET(UTIL_SRCS
1756 ascii_util.cpp
1757+ base64_util.cpp
1758 dynamic_bitset.cpp
1759 error_util.cpp
1760 file.cpp
1761
1762=== modified file 'src/util/ascii_util.cpp'
1763--- src/util/ascii_util.cpp 2012-06-18 10:06:47 +0000
1764+++ src/util/ascii_util.cpp 2012-06-26 01:34:26 +0000
1765@@ -15,6 +15,8 @@
1766 */
1767 #include "stdafx.h"
1768
1769+#include <cstring>
1770+
1771 #include "ascii_util.h"
1772
1773 namespace zorba {
1774@@ -30,6 +32,38 @@
1775 return true;
1776 }
1777
1778+size_type remove_chars( char *s, size_type s_len, char const *chars ) {
1779+ char *end = s + s_len;
1780+ char *c;
1781+
1782+ // remove trailing chars first
1783+ for ( c = end - 1; c >= s; --c )
1784+ if ( !std::strchr( chars, *c ) ) {
1785+ end = c + 1;
1786+ break;
1787+ }
1788+ if ( c < s ) // it was all chars
1789+ return 0;
1790+
1791+ // remove all other chars
1792+ char *first_char = nullptr;
1793+ for ( c = s; c < end; ++c ) {
1794+ if ( std::strchr( chars, *c ) ) {
1795+ if ( !first_char )
1796+ first_char = c;
1797+ } else {
1798+ if ( first_char ) {
1799+ std::memmove( first_char, c, end - c );
1800+ end -= c - first_char;
1801+ c = first_char;
1802+ first_char = nullptr;
1803+ }
1804+ }
1805+ }
1806+
1807+ return end - s;
1808+}
1809+
1810 char const* trim_start( char const *s, char const *chars ) {
1811 for ( ; *s; ++s ) {
1812 if ( !std::strchr( chars, *s ) )
1813
1814=== modified file 'src/util/ascii_util.h'
1815--- src/util/ascii_util.h 2012-06-18 10:06:47 +0000
1816+++ src/util/ascii_util.h 2012-06-26 01:34:26 +0000
1817@@ -649,6 +649,29 @@
1818 }
1819
1820 /**
1821+ * Removes all specified characters by shifting the contents of the buffer to
1822+ * the left.
1823+ *
1824+ * @param s The string.
1825+ * @param s_len The length of \a s.
1826+ * @param chars The characters to remove.
1827+ * @return Returns the new length of \a s with all \a chars removed.
1828+ */
1829+size_type remove_chars( char *s, size_type s_len, char const *chars );
1830+
1831+/**
1832+ * Removes all whitespace characters by shifting the contents of the buffer to
1833+ * the left.
1834+ *
1835+ * @param s The string.
1836+ * @param s_len The length of \a s.
1837+ * @return Returns the new length of \a s with all whitespace removed.
1838+ */
1839+inline size_type remove_whitespace( char *s, size_type s_len ) {
1840+ return remove_chars( s, s_len, whitespace );
1841+}
1842+
1843+/**
1844 * Removes all leading and trailing specified characters.
1845 *
1846 * @tparam InputStringType The input string type.
1847
1848=== added file 'src/util/base64_util.cpp'
1849--- src/util/base64_util.cpp 1970-01-01 00:00:00 +0000
1850+++ src/util/base64_util.cpp 2012-06-26 01:34:26 +0000
1851@@ -0,0 +1,359 @@
1852+/*
1853+ * Copyright 2006-2008 The FLWOR Foundation.
1854+ *
1855+ * Licensed under the Apache License, Version 2.0 (the "License");
1856+ * you may not use this file except in compliance with the License.
1857+ * You may obtain a copy of the License at
1858+ *
1859+ * http://www.apache.org/licenses/LICENSE-2.0
1860+ *
1861+ * Unless required by applicable law or agreed to in writing, software
1862+ * distributed under the License is distributed on an "AS IS" BASIS,
1863+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1864+ * See the License for the specific language governing permissions and
1865+ * limitations under the License.
1866+ */
1867+
1868+#include <algorithm>
1869+#include <cstring>
1870+
1871+#include "ascii_util.h"
1872+#include "base64_util.h"
1873+#include "string_util.h"
1874+
1875+using namespace std;
1876+
1877+namespace zorba {
1878+namespace base64 {
1879+
1880+///////////////////////////////////////////////////////////////////////////////
1881+
1882+static char const alphabet[] =
1883+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
1884+ "abcdefghijklmnopqrstuvwxyz"
1885+ "0123456789+/";
1886+
1887+/**
1888+ * Base64 decoding table. A value of -1 means "invalid"; a value of -2 means
1889+ * "skip" (for newlines and carriage returns).
1890+ */
1891+static signed char const decode_table[] = {
1892+ /* 00-07 */ -1, -1, -1, -1, -1, -1, -1, -1,
1893+ /* 08-0F */ -1, -1, -2, -1, -1, -2, -1, -1, // . .\n . .\r . .
1894+ /* 10-17 */ -1, -1, -1, -1, -1, -1, -1, -1,
1895+ /* 18-1F */ -1, -1, -1, -1, -1, -1, -1, -1,
1896+ /* 20-27 */ -1, -1, -1, -1, -1, -1, -1, -1,
1897+ /* 28-2F */ -1, -1, -1, 62, -1, -1, -1, 63, // . . . + . . . /
1898+ /* 30-37 */ 52, 53, 54, 55, 56, 57, 58, 59, // 0 1 2 3 4 5 6 7
1899+ /* 38-3F */ 60, 61, -1, -1, -1, -1, -1, -1, // 8 9 . . . . . .
1900+ /* 40-47 */ -1, 0, 1, 2, 3, 4, 5, 6, // . A B C D E F G
1901+ /* 48-4F */ 7, 8, 9, 10, 11, 12, 13, 14, // H I J K L M N O
1902+ /* 50-57 */ 15, 16, 17, 18, 19, 20, 21, 22, // P Q R S T U V W
1903+ /* 58-5F */ 23, 24, 25, -1, -1, -1, -1, -1, // X Y Z . . . . .
1904+ /* 60-67 */ -1, 26, 27, 28, 29, 30, 31, 32, // . a b c d e f g
1905+ /* 68-6F */ 33, 34, 35, 36, 37, 38, 39, 40, // h i j k l m n o
1906+ /* 70-77 */ 41, 42, 43, 44, 45, 46, 47, 48, // p q r s t u v w
1907+ /* 78-7F */ 49, 50, 51, -1, -1, -1, -1, -1, // x y z . . . . .
1908+ /* 80-87 */ -1, -1, -1, -1, -1, -1, -1, -1,
1909+ /* 88-8F */ -1, -1, -1, -1, -1, -1, -1, -1,
1910+ /* 90-97 */ -1, -1, -1, -1, -1, -1, -1, -1,
1911+ /* 98-9F */ -1, -1, -1, -1, -1, -1, -1, -1,
1912+ /* A0-A7 */ -1, -1, -1, -1, -1, -1, -1, -1,
1913+ /* A8-AF */ -1, -1, -1, -1, -1, -1, -1, -1,
1914+ /* B0-B7 */ -1, -1, -1, -1, -1, -1, -1, -1,
1915+ /* B8-BF */ -1, -1, -1, -1, -1, -1, -1, -1,
1916+ /* C0-C7 */ -1, -1, -1, -1, -1, -1, -1, -1,
1917+ /* C8-CF */ -1, -1, -1, -1, -1, -1, -1, -1,
1918+ /* D0-D7 */ -1, -1, -1, -1, -1, -1, -1, -1,
1919+ /* D8-DF */ -1, -1, -1, -1, -1, -1, -1, -1,
1920+ /* E0-E7 */ -1, -1, -1, -1, -1, -1, -1, -1,
1921+ /* E8-EF */ -1, -1, -1, -1, -1, -1, -1, -1,
1922+ /* F0-F7 */ -1, -1, -1, -1, -1, -1, -1, -1,
1923+ /* F8-FF */ -1, -1, -1, -1, -1, -1, -1, -1,
1924+};
1925+
1926+inline void decode_chunk( char const *from, char *to ) {
1927+ //
1928+ // | INPUT BYTES
1929+ // +-----------+-----------+-----------+-----------+
1930+ // | 0 | 1 | 2 | 3 |
1931+ // +-----------+-----------+-----------+-----------+
1932+ // | | | | |
1933+ // | | | | | | | | | | | | | | | | | | | | | | | | |
1934+ // | | | |
1935+ // +---------------+---------------+---------------+
1936+ // | 0 | 1 | 2 |
1937+ // +---------------+---------------+---------------+
1938+ // | OUTPUT BYTES
1939+ //
1940+ unsigned char const *const u = reinterpret_cast<unsigned char const*>( from );
1941+ to[0] = (u[0] << 2) | (u[1] >> 4);
1942+ to[1] = (u[1] << 4) | (u[2] >> 2);
1943+ to[2] = ((u[2] << 6) & 0xC0) | u[3] ;
1944+}
1945+
1946+inline void encode_chunk( char const *from, char *to ) {
1947+ //
1948+ // | INPUT BYTES
1949+ // +---------------+---------------+---------------+
1950+ // | 0 | 1 | 2 |
1951+ // +---------------+---------------+---------------+
1952+ // | | | |
1953+ // | | | | | | | | | | | | | | | | | | | | | | | | |
1954+ // | | | | |
1955+ // +-----------+-----------+-----------+-----------+
1956+ // | 0 | 1 | 2 | 3 |
1957+ // +-----------+-----------+-----------+-----------+
1958+ // | OUTPUT BYTES
1959+ //
1960+ unsigned char const *const u = reinterpret_cast<unsigned char const*>( from );
1961+ to[0] = alphabet[ u[0] >> 2 ];
1962+ to[1] = alphabet[ ((u[0] & 0x03) << 4) | (u[1] >> 4) ];
1963+ to[2] = alphabet[ ((u[1] & 0x0F) << 2) | (u[2] >> 6) ];
1964+ to[3] = alphabet[ u[2] & 0x3F ];
1965+}
1966+
1967+streamsize read_without_whitespace( istream &is, char *buf, streamsize n ) {
1968+ char const *const buf_orig = buf;
1969+ char const *const buf_end = buf + n;
1970+
1971+ while ( buf < buf_end ) {
1972+ is.read( buf, n );
1973+ if ( streamsize read = is.gcount() ) {
1974+ read = ascii::remove_whitespace( buf, read );
1975+ buf += read, n -= read;
1976+ } else
1977+ break;
1978+ }
1979+ return buf - buf_orig;
1980+}
1981+
1982+///////////////////////////////////////////////////////////////////////////////
1983+
1984+size_type decode( char const *from, size_type from_len, char *to,
1985+ int options ) {
1986+ char chunk[4];
1987+ int chunk_len = 0;
1988+ bool const ignore_ws = options & dopt_ignore_ws;
1989+ int pads = 0;
1990+ char const *const to_orig = to;
1991+
1992+ for ( size_type pos = 0; pos < from_len; ++pos, ++from ) {
1993+ char const c = *from;
1994+ signed char value;
1995+ if ( c == '=' ) {
1996+ switch ( pos % 4 ) {
1997+ //
1998+ // Ensure '=' occurs only in the 3rd or 4th bytes of a 4-byte chunk
1999+ // and that the byte preceding '=' is valid.
2000+ //
2001+ case 2:
2002+ if ( !strchr( "AQgw", from[-1] ) )
2003+ throw base64::exception(
2004+ c, pos, BUILD_STRING( '\'', c, "': invalid character before '='" )
2005+ );
2006+ break;
2007+ case 3:
2008+ if ( !strchr( "=048AEIMQUYcgkosw", from[-1] ) )
2009+ throw base64::exception(
2010+ c, pos, BUILD_STRING( '\'', c, "': invalid character before '='" )
2011+ );
2012+ break;
2013+ default:
2014+ throw base64::exception( c, pos, "'=' encountered unexpectedly" );
2015+ }
2016+ ++pads;
2017+ value = '\0';
2018+ } else {
2019+ if ( pads )
2020+ throw base64::exception(
2021+ c, pos, BUILD_STRING( '\'', c, "': invalid character after '='" )
2022+ );
2023+ value = decode_table[ static_cast<unsigned char>( c ) ];
2024+ }
2025+ switch ( value ) {
2026+ case -1:
2027+ if ( ascii::is_space( c ) && ignore_ws )
2028+ continue;
2029+ throw base64::exception(
2030+ c, pos, BUILD_STRING( '\'', c, "': invalid character" )
2031+ );
2032+ case -2: // \n or \r
2033+ continue;
2034+ default:
2035+ if ( chunk_len == 4 )
2036+ chunk_len = 0;
2037+ if ( to ) {
2038+ chunk[ chunk_len ] = value;
2039+ if ( ++chunk_len == 4 ) {
2040+ decode_chunk( chunk, to );
2041+ to += 3;
2042+ }
2043+ } else
2044+ ++chunk_len;
2045+ }
2046+ } // for
2047+
2048+ if ( (chunk_len % 4) && !(options & dopt_any_len) )
2049+ throw invalid_argument( "Base64 length is not a multiple of 4" );
2050+
2051+ if ( !to )
2052+ return 0;
2053+
2054+ if ( chunk_len > 1 && chunk_len < 4 ) {
2055+ //
2056+ // The number of non-whitespace bytes was not a multiple of 4, hence the
2057+ // Base64 encoding is incomplete: salvage 1 or 2 characters.
2058+ //
2059+ int const salvageable = chunk_len - 1;
2060+ chunk[3] = '\0';
2061+ if ( salvageable == 1 )
2062+ chunk[2] = '\0';
2063+ decode_chunk( chunk, to );
2064+ to += salvageable;
2065+ }
2066+
2067+ return to - to_orig - pads;
2068+}
2069+
2070+size_type decode( char const *from, size_type from_len, std::vector<char> *to,
2071+ int options ) {
2072+ size_type total_decoded = 0;
2073+ if ( from_len ) {
2074+ std::vector<char>::size_type const orig_size = to->size();
2075+ to->resize( orig_size + decoded_size( from_len ) );
2076+ total_decoded = decode( from, from_len, &(*to)[ orig_size ], options );
2077+ to->resize( orig_size + total_decoded );
2078+ }
2079+ return total_decoded;
2080+}
2081+
2082+size_type decode( istream &from, ostream &to, int options ) {
2083+ size_type total_decoded = 0;
2084+ while ( !from.eof() ) {
2085+ char from_buf[ 1024 * 4 ], to_buf[ 1024 * 3 ];
2086+ streamsize gcount;
2087+ if ( options & dopt_ignore_ws )
2088+ gcount = read_without_whitespace( from, from_buf, sizeof from_buf );
2089+ else {
2090+ from.read( from_buf, sizeof from_buf );
2091+ gcount = from.gcount();
2092+ }
2093+ if ( gcount ) {
2094+ size_type const decoded = decode( from_buf, gcount, to_buf, options );
2095+ to.write( to_buf, decoded );
2096+ total_decoded += decoded;
2097+ } else
2098+ break;
2099+ }
2100+ return total_decoded;
2101+}
2102+
2103+size_type decode( istream &from, vector<char> *to, int options ) {
2104+ vector<char>::size_type const orig_size = to->size();
2105+ size_type total_decoded = 0;
2106+ while ( !from.eof() ) {
2107+ char from_buf[ 1024 * 4 ];
2108+ streamsize gcount;
2109+ if ( options & dopt_ignore_ws )
2110+ gcount = read_without_whitespace( from, from_buf, sizeof from_buf );
2111+ else {
2112+ from.read( from_buf, sizeof from_buf );
2113+ gcount = from.gcount();
2114+ }
2115+ if ( gcount ) {
2116+ to->resize( to->size() + decoded_size( gcount ) );
2117+ total_decoded +=
2118+ decode( from_buf, gcount, &(*to)[ total_decoded ], options );
2119+ } else
2120+ break;
2121+ }
2122+ to->resize( orig_size + total_decoded );
2123+ return total_decoded;
2124+}
2125+
2126+///////////////////////////////////////////////////////////////////////////////
2127+
2128+size_type encode( char const *from, size_type from_len, char *to ) {
2129+ char const *const to_orig = to;
2130+ int chunk_len = 0;
2131+
2132+ while ( from_len-- ) {
2133+ if ( ++chunk_len == 3 ) {
2134+ encode_chunk( from, to );
2135+ from += 3, to += 4;
2136+ chunk_len = 0;
2137+ }
2138+ }
2139+
2140+ if ( chunk_len ) { // must be either 1 or 2
2141+ //
2142+ // Handle the special-case of from_len not being a multiple of 3. First,
2143+ // copy what's left over from "from" to a temporary buffer that's 3 bytes
2144+ // long and encode that buffer so encode_chunk() remains special-case-free
2145+ // (and thus faster) for most of the encoding.
2146+ //
2147+ char from_temp[3];
2148+ from_temp[1] = from_temp[2] = '\0';
2149+ std::copy( from, from + chunk_len, from_temp );
2150+ encode_chunk( from_temp, to );
2151+ //
2152+ // Second, overwrite the trailing byte(s) with Base64 padding characters.
2153+ //
2154+ to[3] = '=';
2155+ if ( chunk_len == 1 )
2156+ to[2] = '=';
2157+ to += 4;
2158+ }
2159+
2160+ return to - to_orig;
2161+}
2162+
2163+size_type encode( char const *from, size_type from_len,
2164+ std::vector<char> *to ) {
2165+ size_type encoded = 0;
2166+ if ( from_len ) {
2167+ std::vector<char>::size_type const orig_size = to->size();
2168+ to->resize( orig_size + encoded_size( from_len ) );
2169+ encoded = encode( from, from_len, &(*to)[ orig_size ] );
2170+ to->resize( orig_size + encoded );
2171+ }
2172+ return encoded;
2173+}
2174+
2175+size_type encode( istream &from, ostream &to ) {
2176+ size_type total_encoded = 0;
2177+ while ( !from.eof() ) {
2178+ char from_buf[ 1024 * 3 ], to_buf[ 1024 * 4 ];
2179+ from.read( from_buf, sizeof from_buf );
2180+ if ( streamsize const gcount = from.gcount() ) {
2181+ size_type const encoded = encode( from_buf, gcount, to_buf );
2182+ to.write( to_buf, encoded );
2183+ total_encoded += encoded;
2184+ } else
2185+ break;
2186+ }
2187+ return total_encoded;
2188+}
2189+
2190+size_type encode( istream &from, vector<char> *to ) {
2191+ vector<char>::size_type const orig_size = to->size();
2192+ size_type total_encoded = 0;
2193+ while ( !from.eof() ) {
2194+ char from_buf[ 1024 * 3 ];
2195+ from.read( from_buf, sizeof from_buf );
2196+ if ( streamsize const gcount = from.gcount() ) {
2197+ to->resize( to->size() + encoded_size( gcount ) );
2198+ total_encoded += encode( from_buf, gcount, &(*to)[ total_encoded ] );
2199+ } else
2200+ break;
2201+ }
2202+ to->resize( orig_size + total_encoded );
2203+ return total_encoded;
2204+}
2205+
2206+///////////////////////////////////////////////////////////////////////////////
2207+
2208+} // namespace base64
2209+} // namespace zorba
2210+/* vim:set et sw=2 ts=2: */
2211
2212=== added file 'src/util/base64_util.h'
2213--- src/util/base64_util.h 1970-01-01 00:00:00 +0000
2214+++ src/util/base64_util.h 2012-06-26 01:34:26 +0000
2215@@ -0,0 +1,361 @@
2216+/*
2217+ * Copyright 2006-2008 The FLWOR Foundation.
2218+ *
2219+ * Licensed under the Apache License, Version 2.0 (the "License");
2220+ * you may not use this file except in compliance with the License.
2221+ * You may obtain a copy of the License at
2222+ *
2223+ * http://www.apache.org/licenses/LICENSE-2.0
2224+ *
2225+ * Unless required by applicable law or agreed to in writing, software
2226+ * distributed under the License is distributed on an "AS IS" BASIS,
2227+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2228+ * See the License for the specific language governing permissions and
2229+ * limitations under the License.
2230+ */
2231+
2232+#pragma once
2233+#ifndef ZORBA_BASE64_UTIL_H
2234+#define ZORBA_BASE64_UTIL_H
2235+
2236+#include <algorithm>
2237+#include <iostream>
2238+#include <stdexcept>
2239+#include <sys/types.h> /* for size_t */
2240+#include <vector>
2241+
2242+#include "cxx_util.h"
2243+
2244+namespace zorba {
2245+namespace base64 {
2246+
2247+////////// Types //////////////////////////////////////////////////////////////
2248+
2249+typedef size_t size_type;
2250+
2251+/**
2252+ * Options to use for decoding.
2253+ */
2254+enum decode_options {
2255+ dopt_none = 0x00, ///< No options.
2256+ dopt_any_len = 0x01, ///< Input length may be non-multiple of 4.
2257+ dopt_ignore_ws = 0x02, ///< Ignore all whitespace.
2258+};
2259+
2260+////////// Exception //////////////////////////////////////////////////////////
2261+
2262+/**
2263+ * A %base64::exception is-an invalid_argument that contains additional details
2264+ * about the exception such as the invalid character and its offset.
2265+ */
2266+class exception : public std::invalid_argument {
2267+public:
2268+ exception( char c, size_type offset, std::string const &msg ) :
2269+ std::invalid_argument( msg ), char_( c ), offset_( offset ) { }
2270+
2271+ char invalid_char() const {
2272+ return char_;
2273+ }
2274+
2275+ size_type char_offset() const {
2276+ return offset_;
2277+ }
2278+
2279+private:
2280+ char char_;
2281+ size_type offset_;
2282+};
2283+
2284+////////// Decoding ///////////////////////////////////////////////////////////
2285+
2286+/**
2287+ * \internal
2288+ * Reads from the given istream until \a n non-whitespace characters are read
2289+ * or until EOF is encountered.
2290+ *
2291+ * @param is The istream to read from.
2292+ * @param buf A pointer to the start of a buffer to read into.
2293+ * @param n The number of non-whitespace characters to read.
2294+ * @return Returns the number of non-whitespace characters read.
2295+ */
2296+std::streamsize read_without_whitespace( std::istream &is, char *buf,
2297+ std::streamsize n );
2298+
2299+/**
2300+ * Calculates the number of bytes required to decode \a n Base64-encoded bytes.
2301+ *
2302+ * @param n The number of bytes to decode.
2303+ * @return Returns the number of bytes needed for Base64 decoding.
2304+ */
2305+inline size_type decoded_size( size_type n ) {
2306+ return ((n / 4) + !!(n % 4)) * 3;
2307+}
2308+
2309+/**
2310+ * Decodes a Base64-encoded buffer. Embedded newlines and carriage-returns are
2311+ * skipped.
2312+ *
2313+ * @param from A pointer to the Base64 buffer to be decoded.
2314+ * @param from_len The number of bytes to decode.
2315+ * @paran to A pointer to the buffer to receive the decoded bytes. The buffer
2316+ * must be large enough to contain them. Note that the buffer is \e not null
2317+ * terminated.
2318+ * @param options The decoding options to use.
2319+ * @return Returns the number of decoded bytes.
2320+ * @throws invalid_argument if \a options does not have the \c dtop_any_len bit
2321+ * set and \a from_len is not a multiple of 4.
2322+ * @throws base64::exception if an \c = is encountered unexpectedly or an
2323+ * invalid byte is encountered.
2324+ * @see decoded_size()
2325+ */
2326+size_type decode( char const *from, size_type from_len, char *to,
2327+ int options = dopt_none );
2328+
2329+/**
2330+ * Decodes a Base64-encoded buffer and appends the decoded bytes onto a
2331+ * vector&lt;char&gt;. Embedded newlines and carriage-returns are skipped.
2332+ *
2333+ * @param from A pointer to the buffer to be encoded.
2334+ * @param from_len The number of bytes to encode.
2335+ * @param to A pointer to the vector to append the encoded bytes appended onto.
2336+ * The vector is made large enough to contain the additional bytes.
2337+ * @param options The decoding options to use.
2338+ * @return Returns the number of decoded bytes.
2339+ * @throws invalid_argument if \a options does not have the \c dopt_any_len bit
2340+ * set and the number of Base64 bytes decoded is not a multiple of 4.
2341+ * @throws base64::exception if an \c = is encountered unexpectedly or an
2342+ * invalid byte is encountered.
2343+ */
2344+size_type decode( char const *from, size_type from_len, std::vector<char> *to,
2345+ int options = dopt_none );
2346+
2347+/**
2348+ * Decodes a Base64-encoded buffer and appends the decoded bytes onto a string.
2349+ * Embedded newlines and carriage-returns are skipped.
2350+ *
2351+ * @tparam ToStringType The string type.
2352+ * @param from A pointer to the Base64 buffer to be decoded.
2353+ * @param from_len The number of bytes to decode.
2354+ * @param to The string to append the decoded bytes to.
2355+ * @param options The options to use.
2356+ * @return Returns the number of decoded bytes.
2357+ * @throws invalid_argument if \a options does not have the \c dopt_any_len bit
2358+ * set and the number of Base64 bytes decoded is not a multiple of 4.
2359+ * @throws base64::exception if an \c = is encountered unexpectedly or an
2360+ * invalid byte is encountered.
2361+ */
2362+template<class ToStringType>
2363+size_type decode( char const *from, size_type from_len, ToStringType *to,
2364+ int options = dopt_none ) {
2365+ size_type total_decoded = 0;
2366+ if ( from_len ) {
2367+ typename ToStringType::size_type const orig_size = to->size();
2368+ to->resize( orig_size + decoded_size( from_len ) );
2369+ total_decoded = decode( from, from_len, &to->at( orig_size ), options );
2370+ to->resize( orig_size + total_decoded );
2371+ }
2372+ return total_decoded;
2373+}
2374+
2375+/**
2376+ * Decodes a Base64-encoded istream. Embedded newlines and carriage-returns
2377+ * are skipped.
2378+ *
2379+ * @param from The istream to read from until EOF is reached.
2380+ * @param to The ostream to write the decoded bytes to.
2381+ * @param options The options to use.
2382+ * 4 otherwise an exception is thrown; if \a false, missing trailing bytes are
2383+ * assumed to be padding.
2384+ * @return Returns the number of decoded bytes.
2385+ * @throws invalid_argument if \a options does not have the \c dopt_any_len bit
2386+ * set and the number of Base64 bytes decoded is not a multiple of 4.
2387+ * @throws base64::exception if an \c = is encountered unexpectedly or an
2388+ * invalid byte is encountered.
2389+ */
2390+size_type decode( std::istream &from, std::ostream &to,
2391+ int options = dopt_none );
2392+
2393+/**
2394+ * Decodes a Base64-encoded istream and appends the decoded bytes to a string.
2395+ * Embedded newlines and carriage-returns are skipped.
2396+ *
2397+ * @tparam ToStringType The string type.
2398+ * @param from The istream to read from until EOF is reached.
2399+ * @param to The string to append the decoded bytes to.
2400+ * @param options The options to use.
2401+ * 4 otherwise an exception is thrown; if \a false, missing trailing bytes are
2402+ * assumed to be padding.
2403+ * @return Returns the number of decoded bytes.
2404+ * @throws invalid_argument if \a options does not have the \c dopt_any_len bit
2405+ * set and the number of Base64 bytes decoded is not a multiple of 4.
2406+ * @throws base64::exception if an \c = is encountered unexpectedly or an
2407+ * invalid byte is encountered.
2408+ */
2409+template<class ToStringType>
2410+size_type decode( std::istream &from, ToStringType *to,
2411+ int options = dopt_none ) {
2412+ size_type total_decoded = 0;
2413+ while ( !from.eof() ) {
2414+ char from_buf[ 1024 * 4 ], to_buf[ 1024 * 3 ];
2415+ std::streamsize gcount;
2416+ if ( options & dopt_ignore_ws )
2417+ gcount = read_without_whitespace( from, from_buf, sizeof from_buf );
2418+ else {
2419+ from.read( from_buf, sizeof from_buf );
2420+ gcount = from.gcount();
2421+ }
2422+ if ( gcount ) {
2423+ size_type const decoded = decode( from_buf, gcount, to_buf, options );
2424+ to->append( to_buf, decoded );
2425+ total_decoded += decoded;
2426+ } else
2427+ break;
2428+ }
2429+ return total_decoded;
2430+}
2431+
2432+/**
2433+ * Decodes a Base64-encoded stream and appends the decoded bytes onto a
2434+ * vector&lt;char;&gt;.
2435+ *
2436+ * @param from The istream to read from until EOF is reached.
2437+ * @param to The string to append the decoded bytes to.
2438+ * @param options The options to use.
2439+ * @param Returns the number of decoded bytes.
2440+ * @throws invalid_argument if \a options does not have the \c dopt_any_len bit
2441+ * set and the number of Base64 bytes decoded is not a multiple of 4.
2442+ * @throws base64::exception if an \c = is encountered unexpectedly or an
2443+ * invalid byte is encountered.
2444+ */
2445+size_type decode( std::istream &from, std::vector<char> *to,
2446+ int options = dopt_none );
2447+
2448+/**
2449+ * Validates a Base64-encoded buffer. Embedded newlines and carriage-returns
2450+ * are skipped.
2451+ *
2452+ * @param buf A pointer to the Base64 buffer to be validated.
2453+ * @param buf_len The number of bytes to validate.
2454+ * @param options The options to use.
2455+ * @throws invalid_argument if \a options does not have the \c dopt_any_len bit
2456+ * set and the number of Base64 bytes validated is not a multiple of 4.
2457+ * @throws base64::exception if an \c = is encountered unexpectedly or an
2458+ * invalid byte is encountered.
2459+ * @see decoded_size()
2460+ */
2461+inline void validate( char const *buf, size_type buf_len,
2462+ int options = dopt_none ) {
2463+ decode( buf, buf_len, static_cast<char*>( nullptr ), options );
2464+}
2465+
2466+////////// Encoding ///////////////////////////////////////////////////////////
2467+
2468+/**
2469+ * Calculates the number of bytes required to Base64-encode \a n bytes.
2470+ *
2471+ * @param n The number of bytes to encode.
2472+ * @return Returns the number of bytes needed for Base64 encoding.
2473+ */
2474+inline size_type encoded_size( size_type n ) {
2475+ return (n + 2) / 3 * 4;
2476+}
2477+
2478+/**
2479+ * Base64-encodes a buffer.
2480+ *
2481+ * @param from A pointer to the buffer to be encoded.
2482+ * @param from_len The number of bytes to encode.
2483+ * @param to A pointer to the buffer to receive the encoded bytes. The buffer
2484+ * must be large enough to contain them. Note that the buffer is \e not null
2485+ * terminated.
2486+ * @return Returns the number of encoded bytes.
2487+ * @see encoded_size()
2488+ */
2489+size_type encode( char const *from, size_type from_len, char *to );
2490+
2491+/**
2492+ * Base64-encodes a buffer and appends the encoded bytes onto a
2493+ * vector&lt;char&gt;.
2494+ *
2495+ * @param from A pointer to the buffer to be encoded.
2496+ * @param from_len The number of bytes to encode.
2497+ * @param to A pointer to the vector to append the encoded bytes appended onto.
2498+ * The vector is made large enough to contain the additional bytes.
2499+ */
2500+size_type encode( char const *from, size_type from_len, std::vector<char> *to );
2501+
2502+/**
2503+ * Base64-encodes a buffer and appends the encoded bytes onto a string.
2504+ *
2505+ * @tparam ToStringType The string type.
2506+ * @param from A pointer to the Base64 buffer to be encoded.
2507+ * @param from_len The number of bytes to encode.
2508+ * @param to A pointer to the string to append the encoded bytes onto.
2509+ * @return Returns the number of encoded bytes.
2510+ */
2511+template<class ToStringType>
2512+size_type encode( char const *from, size_type from_len, ToStringType *to ) {
2513+ size_type total_encoded = 0;
2514+ if ( from_len ) {
2515+ typename ToStringType::size_type const orig_size = to->size();
2516+ to->resize( orig_size + encoded_size( from_len ) );
2517+ total_encoded = encode( from, from_len, &to->at( orig_size ) );
2518+ to->resize( orig_size + total_encoded );
2519+ }
2520+ return total_encoded;
2521+}
2522+
2523+/**
2524+ * Base64-encodes one stream and write the encoded bytes to another.
2525+ *
2526+ * @param from The istream to read from until EOF is reached.
2527+ * @param to The ostream to write the encoded bytes to.
2528+ */
2529+size_type encode( std::istream &from, std::ostream &to );
2530+
2531+/**
2532+ * Encodes a stream to Base64 and appends the encoded bytes to a string.
2533+ *
2534+ * @tparam ToStringType The string type.
2535+ * @param from The istream to read from until EOF is reached.
2536+ * @param to The string to append the encoded bytes to.
2537+ * @return Returns the number of encoded bytes.
2538+ */
2539+template<class ToStringType>
2540+size_type encode( std::istream &from, ToStringType *to ) {
2541+ size_type total_encoded = 0;
2542+ while ( !from.eof() ) {
2543+ char from_buf[ 1024 * 3 ], to_buf[ 1024 * 4 ];
2544+ from.read( from_buf, sizeof from_buf );
2545+ if ( std::streamsize const gcount = from.gcount() ) {
2546+ size_type const encoded = encode( from_buf, gcount, to_buf );
2547+ to->append( to_buf, encoded );
2548+ total_encoded += encoded;
2549+ } else
2550+ break;
2551+ }
2552+ return total_encoded;
2553+}
2554+
2555+/**
2556+ * Base64-encodes a stream and appends the encoded bytes onto a
2557+ * vector&lt;char;&gt;.
2558+ *
2559+ * @param from The istream to read from until EOF is reached.
2560+ * @param to The string to append the encoded bytes to.
2561+ * @param Returns the number of encoded bytes.
2562+ */
2563+size_type encode( std::istream &from, std::vector<char> *to );
2564+
2565+///////////////////////////////////////////////////////////////////////////////
2566+
2567+} // namespace base64
2568+} // namespace zorba
2569+
2570+#endif /* ZORBA_BASE64_UTIL_H */
2571+/*
2572+ * Local variables:
2573+ * mode: c++
2574+ * End:
2575+ */
2576+/* vim:set et sw=2 ts=2: */
2577
2578=== modified file 'src/util/icu_streambuf.cpp'
2579--- src/util/icu_streambuf.cpp 2012-06-18 10:06:47 +0000
2580+++ src/util/icu_streambuf.cpp 2012-06-26 01:34:26 +0000
2581@@ -15,8 +15,8 @@
2582 */
2583
2584 #include "stdafx.h"
2585-#define ZORBA_DEBUG_ICU_STREAMBUF 0
2586
2587+// #define ZORBA_DEBUG_ICU_STREAMBUF
2588 #ifdef ZORBA_DEBUG_ICU_STREAMBUF
2589 # include <stdio.h>
2590 #endif
2591
2592=== modified file 'src/util/string/rstring.h'
2593--- src/util/string/rstring.h 2012-06-18 10:06:47 +0000
2594+++ src/util/string/rstring.h 2012-06-26 01:34:26 +0000
2595@@ -1884,7 +1884,7 @@
2596 }
2597
2598 /**
2599- * Attemts to pre-allocated enough memory to contain the given number of
2600+ * Attemts to pre-allocate enough memory to contain the given number of
2601 * bytes.
2602 *
2603 * @param n The number of bytes.
2604
2605=== modified file 'src/zorbatypes/binary.cpp'
2606--- src/zorbatypes/binary.cpp 2012-06-18 10:06:47 +0000
2607+++ src/zorbatypes/binary.cpp 2012-06-26 01:34:26 +0000
2608@@ -25,32 +25,44 @@
2609 #include "diagnostics/xquery_diagnostics.h"
2610
2611 #include "util/ascii_util.h"
2612+#include "util/base64_util.h"
2613 #include "util/stl_util.h"
2614
2615+#define CATCH_BASE64_EXCEPTION() \
2616+ catch ( base64::exception const &e ) { \
2617+ throw XQUERY_EXCEPTION( \
2618+ err::FORG0001, ERROR_PARAMS( e.invalid_char(), ZED( Base64BadChar ) ) \
2619+ ); \
2620+ } \
2621+ catch ( std::invalid_argument const& ) { \
2622+ throw XQUERY_EXCEPTION( \
2623+ err::FORG0001, ERROR_PARAMS( "", ZED( Base64Multiple4 ) ) \
2624+ ); \
2625+ }
2626+
2627 using namespace std;
2628
2629-namespace zorba
2630-{
2631-
2632-
2633-static const string base64_chars =
2634- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2635- "abcdefghijklmnopqrstuvwxyz"
2636- "0123456789+/";
2637-
2638-
2639-inline bool is_base64(char c)
2640-{
2641- return (isalnum(c) || (c == '+') || (c == '/'));
2642+namespace zorba {
2643+
2644+static size_t copy_without_ws( char const *from, size_t len, char *to ) {
2645+ char const *const end = from + len;
2646+ char const *const to_orig = to;
2647+ for ( ; from < end; ++from )
2648+ if ( !ascii::is_space( *from ) )
2649+ *to++ = *from;
2650+ return to - to_orig;
2651 }
2652
2653
2654 bool Base64::parseString(const char* aString, size_t aLength, Base64& aBase64)
2655 {
2656- aBase64.theData.clear();
2657 try
2658 {
2659- aBase64.insertData(aString, aLength);
2660+ base64::validate( aString, aLength, base64::dopt_ignore_ws );
2661+ aBase64.theData.resize( aLength );
2662+ aBase64.theData.resize(
2663+ copy_without_ws( aString, aLength, &aBase64.theData[0] )
2664+ );
2665 }
2666 catch (...)
2667 {
2668@@ -66,10 +78,13 @@
2669 Base64& aBase64,
2670 string& lErrorMessage)
2671 {
2672- aBase64.theData.clear();
2673 try
2674 {
2675- aBase64.insertData(aString, aLength);
2676+ base64::validate( aString, aLength, base64::dopt_ignore_ws );
2677+ aBase64.theData.resize( aLength );
2678+ aBase64.theData.resize(
2679+ copy_without_ws( aString, aLength, &aBase64.theData[0] )
2680+ );
2681 }
2682 catch (ZorbaException const& e)
2683 {
2684@@ -82,102 +97,27 @@
2685
2686 void Base64::encode(const zstring& aString, Base64& aResult)
2687 {
2688- vector<char> source;
2689- source.reserve(aString.size());
2690-
2691- FOR_EACH( zstring, i, aString )
2692- source.push_back( *i );
2693-
2694- aResult.theData.clear();
2695- encode(source, aResult.theData);
2696+ base64::encode( aString.data(), aString.size(), &aResult.theData );
2697 }
2698
2699
2700 void Base64::encode(istream& aStream, Base64& aResult)
2701 {
2702- vector<char> source;
2703-
2704- char lC;
2705- while (aStream.good())
2706- {
2707- aStream.get(lC);
2708- if (!aStream.good())
2709- {
2710- break;
2711- }
2712- source.push_back(lC);
2713- }
2714-
2715- encode(source, aResult.theData);
2716+ base64::encode( aStream, &aResult.theData );
2717 }
2718
2719
2720 zstring Base64::encode(istream& aStream)
2721 {
2722- vector<char> source;
2723- vector<char> dest;
2724-
2725- char buf[1024];
2726- while (!aStream.eof())
2727- {
2728- aStream.read(buf, 1024);
2729- source.insert(source.end(), buf, buf + aStream.gcount());
2730- }
2731-
2732- encode(source, dest);
2733-
2734 zstring result;
2735- result.reserve(dest.size());
2736-
2737- FOR_EACH( vector<char>, i, dest )
2738- result.push_back( *i );
2739-
2740+ base64::encode( aStream, &result );
2741 return result;
2742 }
2743
2744
2745 void Base64::encode(const vector<char>& aSource, vector<char>& aResult)
2746 {
2747- size_t in_len = aSource.size();
2748- size_t lCurPos = 0;
2749- int i = 0;
2750- int j = 0;
2751- char char_array_3[3] = {'\0','\0','\0'};
2752- char char_array_4[4] = {'\0','\0','\0','\0'};
2753-
2754- while (in_len--)
2755- {
2756- char_array_3[i++] = aSource[lCurPos++];
2757- if (i == 3)
2758- {
2759- char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
2760- char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
2761- char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
2762- char_array_4[3] = char_array_3[2] & 0x3f;
2763-
2764- for(i = 0; (i <4) ; i++)
2765- aResult.push_back(base64_chars[char_array_4[i]]);
2766- i = 0;
2767- }
2768- }
2769-
2770- if (i)
2771- {
2772- for(j = i; j < 3; j++)
2773- char_array_3[j] = '\0';
2774-
2775- char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
2776- char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
2777- char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
2778- char_array_4[3] = char_array_3[2] & 0x3f;
2779-
2780- for (j = 0; (j < i + 1); j++)
2781- aResult.push_back(base64_chars[char_array_4[j]]);
2782-
2783- while((i++ < 3))
2784- aResult.push_back('=');
2785-
2786- }
2787+ base64::encode( &aSource[0], aSource.size(), &aResult );
2788 }
2789
2790
2791@@ -186,110 +126,38 @@
2792 unsigned int in_len,
2793 Base64& aResult)
2794 {
2795- size_t lCurPos = 0;
2796- int i = 0;
2797- int j = 0;
2798- char char_array_3[3] = {'\0','\0','\0'};
2799- char char_array_4[4] = {'\0','\0','\0','\0'};
2800-
2801- aResult.theData.reserve(in_len * 8 / 6 + 10);
2802- while (in_len--)
2803- {
2804- char_array_3[i++] = aSource[lCurPos++];
2805- if (i == 3)
2806- {
2807- char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
2808- char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
2809- char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
2810- char_array_4[3] = char_array_3[2] & 0x3f;
2811-
2812- for(i = 0; (i <4) ; i++)
2813- aResult.theData.push_back(base64_chars[char_array_4[i]]);
2814- i = 0;
2815- }
2816- }
2817-
2818- if (i)
2819- {
2820- for(j = i; j < 3; j++)
2821- char_array_3[j] = '\0';
2822-
2823- char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
2824- char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
2825- char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
2826- char_array_4[3] = char_array_3[2] & 0x3f;
2827-
2828- for (j = 0; (j < i + 1); j++)
2829- aResult.theData.push_back(base64_chars[char_array_4[j]]);
2830-
2831- while((i++ < 3))
2832- aResult.theData.push_back('=');
2833-
2834- }
2835+ base64::encode( (char*)aSource, in_len, &aResult.theData );
2836 }
2837
2838
2839-zstring Base64::decode(istream& aStream)
2840+void Base64::decode(istream& aStream, zstring *result)
2841 {
2842- vector<char> source;
2843- vector<char> dest;
2844-
2845- char buf[1024];
2846- while (!aStream.eof())
2847- {
2848- aStream.read(buf, 1024);
2849- source.insert(source.end(), buf, buf + aStream.gcount());
2850+ try {
2851+ base64::decode(
2852+ aStream, result, base64::dopt_any_len | base64::dopt_ignore_ws
2853+ );
2854 }
2855-
2856- decode(source, dest);
2857-
2858- zstring result;
2859- result.reserve(dest.size());
2860-
2861- FOR_EACH( vector<char>, i, dest )
2862- result.push_back( *i );
2863-
2864- return result;
2865+ CATCH_BASE64_EXCEPTION()
2866 }
2867
2868-
2869 void Base64::decode(const vector<char>& aSource, vector<char>& aResult)
2870 {
2871- size_t in_len = aSource.size();
2872- int i = 0;
2873- int j = 0;
2874- int in_ = 0;
2875- char char_array_4[4], char_array_3[3];
2876-
2877- while (in_len-- && ( aSource[in_] != '=') && is_base64(aSource[in_])) {
2878- char_array_4[i++] = aSource[in_]; in_++;
2879- if (i ==4) {
2880- for (i = 0; i <4; i++)
2881- char_array_4[i] = (char)base64_chars.find(char_array_4[i]);
2882-
2883- char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
2884- char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
2885- char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
2886-
2887- for (i = 0; (i < 3); i++)
2888- aResult.push_back(char_array_3[i]);
2889- i = 0;
2890- }
2891- }
2892-
2893- if (i) {
2894- for (j = i; j <4; j++)
2895- char_array_4[j] = 0;
2896-
2897- for (j = 0; j <4; j++)
2898- char_array_4[j] = (char)base64_chars.find(char_array_4[j]);
2899-
2900- char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
2901- char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
2902- char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
2903-
2904- for (j = 0; (j < i - 1); j++) aResult.push_back(char_array_3[j]);
2905- }
2906+ try {
2907+ base64::decode(
2908+ &aSource[0], aSource.size(), &aResult,
2909+ base64::dopt_any_len | base64::dopt_ignore_ws
2910+ );
2911+ }
2912+ CATCH_BASE64_EXCEPTION()
2913+}
2914+
2915+void Base64::decode( char const *from, size_t from_len, zstring *to ) {
2916+ try {
2917+ base64::decode(
2918+ from, from_len, to, base64::dopt_any_len | base64::dopt_ignore_ws
2919+ );
2920+ }
2921+ CATCH_BASE64_EXCEPTION()
2922 }
2923
2924
2925@@ -303,88 +171,12 @@
2926
2927 Base64::Base64(const unsigned char *bin_data, size_t len)
2928 {
2929- std::vector<char> tmp;
2930- tmp.reserve(len);
2931- tmp.insert(tmp.begin(), (const char*)bin_data, ((const char*)bin_data) + len);
2932- theData.reserve(len);
2933- encode(tmp, theData);
2934-}
2935-
2936-
2937-void Base64::insertData(const char* str, size_t len)
2938-{
2939- ascii::size_type pos = 0;
2940-
2941- ascii::skip_whitespace(str, len, &pos);
2942-
2943- for (size_t i = pos; i < len; ++i)
2944- {
2945- char lChar = str[i];
2946-
2947- if (lChar == ' ')
2948- {
2949- // do nothing
2950- }
2951- else if ((lChar >= 65 && lChar <= 90) // A-Z
2952- || (lChar >= 97 && lChar <= 122) // a-z
2953- || (lChar >= 48 && lChar <= 57) // 0-9
2954- || (lChar == 43) // +
2955- || (lChar == 47)) // /
2956- {
2957- theData.push_back(lChar);
2958- }
2959- else if (lChar == '=' && i > 0 && i == (len-2) && str[i+1] == '=' )
2960- {
2961- if (str[i-1] == 'A' ||
2962- str[i-1] == 'Q' ||
2963- str[i-1] == 'g' ||
2964- str[i-1] == 'w')
2965- {
2966- theData.push_back('=');
2967- theData.push_back('=');
2968- ++i;
2969- }
2970- else
2971- {
2972- throw XQUERY_EXCEPTION(
2973- err::FORG0001, ERROR_PARAMS( "==", ZED( Base64EqualsEquals ) )
2974- );
2975- }
2976- }
2977- else if (lChar == '=' && i > 0 && i == (len-1))
2978- {
2979- switch(str[i-1])
2980- {
2981- case 'A': case 'E': case 'I': case 'M': case 'Q': case 'U': case 'Y':
2982- case 'c': case 'g': case 'k': case 'o': case 's': case 'w': case '0':
2983- case '4': case '8':
2984- theData.push_back('=');
2985- break;
2986- default:
2987- throw XQUERY_EXCEPTION(
2988- err::FORG0001, ERROR_PARAMS( '=', ZED( Base64Equals ) )
2989- );
2990- }
2991- }
2992- else if ( ascii::is_space(lChar) )
2993- {
2994- // ignore it
2995- }
2996- else
2997- {
2998- throw XQUERY_EXCEPTION(
2999- err::FORG0001, ERROR_PARAMS( str[i], ZED( Base64BadChar ) )
3000- );
3001- }
3002- }
3003-
3004- if (theData.size() % 4 != 0)
3005- {
3006- throw XQUERY_EXCEPTION(
3007- err::FORG0001, ERROR_PARAMS( "", ZED( Base64Multiple4 ) )
3008- );
3009- }
3010-}
3011+ try {
3012+ base64::encode( (char const*)bin_data, len, &theData );
3013+ }
3014+ CATCH_BASE64_EXCEPTION()
3015+}
3016+
3017
3018
3019 bool Base64::equal(const Base64& aBase64) const
3020@@ -396,7 +188,7 @@
3021 vector<char>::const_iterator lEnd0 = theData.end();
3022 vector<char>::const_iterator lIter1 = aBase64.theData.begin();
3023
3024- for (; lIter0 != lEnd0 ; )
3025+ while ( lIter0 != lEnd0 )
3026 {
3027 if (*lIter0 != *lIter1)
3028 return false;
3029@@ -408,31 +200,28 @@
3030
3031 zstring Base64::str() const
3032 {
3033- stringstream lStream;
3034- lStream << *this;
3035- return zstring(lStream.str());
3036+ zstring result;
3037+ vector<char>::const_iterator lIter = theData.begin();
3038+ vector<char>::const_iterator lEnd = theData.end();
3039+ for( ; lIter != lEnd ; ++lIter)
3040+ {
3041+ result.push_back( *lIter );
3042+ }
3043+ return result;
3044 }
3045
3046
3047 zstring Base64::decode() const
3048 {
3049- vector<char> lDecodedData;
3050-
3051- Base64::decode(theData, lDecodedData);
3052-
3053 zstring result;
3054- result.reserve( lDecodedData.size() );
3055-
3056- FOR_EACH( vector<char>, i, lDecodedData )
3057- result.push_back( *i );
3058-
3059+ base64::decode( &theData[0], theData.size(), &result );
3060 return result;
3061 }
3062
3063
3064 void Base64::decode(vector<char>& aResult)
3065 {
3066- Base64::decode(theData, aResult);
3067+ base64::decode( &theData[0], theData.size(), &aResult );
3068 }
3069
3070
3071@@ -466,6 +255,7 @@
3072 return os;
3073 }
3074
3075+///////////////////////////////////////////////////////////////////////////////
3076
3077 const char* Base16::ENCODE_TABLE = "0123456789ABCDEF";
3078
3079@@ -662,6 +452,7 @@
3080 return os;
3081 }
3082
3083+///////////////////////////////////////////////////////////////////////////////
3084
3085 } // namespace zorba
3086 /* vim:set et sw=2 ts=2: */
3087
3088=== modified file 'src/zorbatypes/binary.h'
3089--- src/zorbatypes/binary.h 2012-06-18 10:06:47 +0000
3090+++ src/zorbatypes/binary.h 2012-06-26 01:34:26 +0000
3091@@ -76,7 +76,9 @@
3092
3093 static void decode(const std::vector<char>&, std::vector<char>&);
3094
3095- static zstring decode(std::istream& aStream);
3096+ static void decode(std::istream& aStream, zstring*);
3097+
3098+ static void decode(char const*, size_t, zstring*);
3099
3100 public:
3101 Base64(const Base64& aBase64)
3102@@ -90,8 +92,6 @@
3103
3104 Base64() {}
3105
3106- virtual ~Base64() {}
3107-
3108 const std::vector<char>& getData() const { return theData; }
3109
3110 size_t size() const { return theData.size(); }
3111@@ -105,9 +105,6 @@
3112 void decode(std::vector<char>&);
3113
3114 uint32_t hash() const;
3115-
3116-private:
3117- void insertData(const char* aCharStar, size_t len);
3118 };
3119
3120

Subscribers

People subscribed via source and target branches