aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/google/protobuf/stubs
diff options
context:
space:
mode:
authorGravatar Feng Xiao <xfxyjwf@gmail.com>2015-06-17 13:12:11 -0700
committerGravatar Feng Xiao <xfxyjwf@gmail.com>2015-06-17 13:12:11 -0700
commite9a122eb19ec54dbca15da80355ed0c17cada9b1 (patch)
treefac37489873ccf47777b2e652db580f9315d7bc7 /src/google/protobuf/stubs
parentb36395b2bac88f70070544c325bc09234cb1f1a6 (diff)
parent818c5eee08840355d70d2f3bdf1a2f17986a5e70 (diff)
Merge pull request #501 from xfxyjwf/down
Down-integrate from internal code base.
Diffstat (limited to 'src/google/protobuf/stubs')
-rw-r--r--src/google/protobuf/stubs/bytestream.cc196
-rw-r--r--src/google/protobuf/stubs/bytestream.h348
-rw-r--r--src/google/protobuf/stubs/bytestream_unittest.cc146
-rw-r--r--src/google/protobuf/stubs/casts.h10
-rw-r--r--src/google/protobuf/stubs/common.cc26
-rw-r--r--src/google/protobuf/stubs/common.h307
-rw-r--r--src/google/protobuf/stubs/mathlimits.cc144
-rw-r--r--src/google/protobuf/stubs/mathlimits.h279
-rw-r--r--src/google/protobuf/stubs/mathutil.h149
-rw-r--r--src/google/protobuf/stubs/status.cc135
-rw-r--r--src/google/protobuf/stubs/status.h116
-rw-r--r--src/google/protobuf/stubs/status_macros.h89
-rw-r--r--src/google/protobuf/stubs/status_test.cc131
-rw-r--r--src/google/protobuf/stubs/statusor.cc46
-rw-r--r--src/google/protobuf/stubs/statusor.h259
-rw-r--r--src/google/protobuf/stubs/statusor_test.cc274
-rw-r--r--src/google/protobuf/stubs/stringpiece.cc268
-rw-r--r--src/google/protobuf/stubs/stringpiece.h440
-rw-r--r--src/google/protobuf/stubs/stringpiece_unittest.cc793
-rw-r--r--src/google/protobuf/stubs/strutil.cc768
-rw-r--r--src/google/protobuf/stubs/strutil.h214
-rw-r--r--src/google/protobuf/stubs/strutil_unittest.cc739
-rw-r--r--src/google/protobuf/stubs/time.cc366
-rw-r--r--src/google/protobuf/stubs/time.h75
-rw-r--r--src/google/protobuf/stubs/time_test.cc208
25 files changed, 6476 insertions, 50 deletions
diff --git a/src/google/protobuf/stubs/bytestream.cc b/src/google/protobuf/stubs/bytestream.cc
new file mode 100644
index 00000000..f4af6a50
--- /dev/null
+++ b/src/google/protobuf/stubs/bytestream.cc
@@ -0,0 +1,196 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <google/protobuf/stubs/bytestream.h>
+
+#include <string.h>
+#include <algorithm>
+
+namespace google {
+namespace protobuf {
+namespace strings {
+
+void ByteSource::CopyTo(ByteSink* sink, size_t n) {
+ while (n > 0) {
+ StringPiece fragment = Peek();
+ if (fragment.empty()) {
+ GOOGLE_LOG(DFATAL) << "ByteSource::CopyTo() overran input.";
+ break;
+ }
+ std::size_t fragment_size = std::min<std::size_t>(n, fragment.size());
+ sink->Append(fragment.data(), fragment_size);
+ Skip(fragment_size);
+ n -= fragment_size;
+ }
+}
+
+void ByteSink::Flush() {}
+
+void UncheckedArrayByteSink::Append(const char* data, size_t n) {
+ if (data != dest_) {
+ // Catch cases where the pointer returned by GetAppendBuffer() was modified.
+ GOOGLE_DCHECK(!(dest_ <= data && data < (dest_ + n)))
+ << "Append() data[] overlaps with dest_[]";
+ memcpy(dest_, data, n);
+ }
+ dest_ += n;
+}
+
+CheckedArrayByteSink::CheckedArrayByteSink(char* outbuf, size_t capacity)
+ : outbuf_(outbuf), capacity_(capacity), size_(0), overflowed_(false) {
+}
+
+void CheckedArrayByteSink::Append(const char* bytes, size_t n) {
+ size_t available = capacity_ - size_;
+ if (n > available) {
+ n = available;
+ overflowed_ = true;
+ }
+ if (n > 0 && bytes != (outbuf_ + size_)) {
+ // Catch cases where the pointer returned by GetAppendBuffer() was modified.
+ GOOGLE_DCHECK(!(outbuf_ <= bytes && bytes < (outbuf_ + capacity_)))
+ << "Append() bytes[] overlaps with outbuf_[]";
+ memcpy(outbuf_ + size_, bytes, n);
+ }
+ size_ += n;
+}
+
+GrowingArrayByteSink::GrowingArrayByteSink(size_t estimated_size)
+ : capacity_(estimated_size),
+ buf_(new char[estimated_size]),
+ size_(0) {
+}
+
+GrowingArrayByteSink::~GrowingArrayByteSink() {
+ delete[] buf_; // Just in case the user didn't call GetBuffer.
+}
+
+void GrowingArrayByteSink::Append(const char* bytes, size_t n) {
+ size_t available = capacity_ - size_;
+ if (bytes != (buf_ + size_)) {
+ // Catch cases where the pointer returned by GetAppendBuffer() was modified.
+ // We need to test for this before calling Expand() which may reallocate.
+ GOOGLE_DCHECK(!(buf_ <= bytes && bytes < (buf_ + capacity_)))
+ << "Append() bytes[] overlaps with buf_[]";
+ }
+ if (n > available) {
+ Expand(n - available);
+ }
+ if (n > 0 && bytes != (buf_ + size_)) {
+ memcpy(buf_ + size_, bytes, n);
+ }
+ size_ += n;
+}
+
+char* GrowingArrayByteSink::GetBuffer(size_t* nbytes) {
+ ShrinkToFit();
+ char* b = buf_;
+ *nbytes = size_;
+ buf_ = NULL;
+ size_ = capacity_ = 0;
+ return b;
+}
+
+void GrowingArrayByteSink::Expand(size_t amount) { // Expand by at least 50%.
+ size_t new_capacity = std::max(capacity_ + amount, (3 * capacity_) / 2);
+ char* bigger = new char[new_capacity];
+ memcpy(bigger, buf_, size_);
+ delete[] buf_;
+ buf_ = bigger;
+ capacity_ = new_capacity;
+}
+
+void GrowingArrayByteSink::ShrinkToFit() {
+ // Shrink only if the buffer is large and size_ is less than 3/4
+ // of capacity_.
+ if (capacity_ > 256 && size_ < (3 * capacity_) / 4) {
+ char* just_enough = new char[size_];
+ memcpy(just_enough, buf_, size_);
+ delete[] buf_;
+ buf_ = just_enough;
+ capacity_ = size_;
+ }
+}
+
+void StringByteSink::Append(const char* data, size_t n) {
+ dest_->append(data, n);
+}
+
+size_t ArrayByteSource::Available() const {
+ return input_.size();
+}
+
+StringPiece ArrayByteSource::Peek() {
+ return input_;
+}
+
+void ArrayByteSource::Skip(size_t n) {
+ GOOGLE_DCHECK_LE(n, input_.size());
+ input_.remove_prefix(n);
+}
+
+LimitByteSource::LimitByteSource(ByteSource *source, size_t limit)
+ : source_(source),
+ limit_(limit) {
+}
+
+size_t LimitByteSource::Available() const {
+ size_t available = source_->Available();
+ if (available > limit_) {
+ available = limit_;
+ }
+
+ return available;
+}
+
+StringPiece LimitByteSource::Peek() {
+ StringPiece piece(source_->Peek());
+ if (piece.size() > limit_) {
+ piece.set(piece.data(), limit_);
+ }
+
+ return piece;
+}
+
+void LimitByteSource::Skip(size_t n) {
+ GOOGLE_DCHECK_LE(n, limit_);
+ source_->Skip(n);
+ limit_ -= n;
+}
+
+void LimitByteSource::CopyTo(ByteSink *sink, size_t n) {
+ GOOGLE_DCHECK_LE(n, limit_);
+ source_->CopyTo(sink, n);
+ limit_ -= n;
+}
+
+} // namespace strings
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/stubs/bytestream.h b/src/google/protobuf/stubs/bytestream.h
new file mode 100644
index 00000000..de8e0204
--- /dev/null
+++ b/src/google/protobuf/stubs/bytestream.h
@@ -0,0 +1,348 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file declares the ByteSink and ByteSource abstract interfaces. These
+// interfaces represent objects that consume (ByteSink) or produce (ByteSource)
+// a sequence of bytes. Using these abstract interfaces in your APIs can help
+// make your code work with a variety of input and output types.
+//
+// This file also declares the following commonly used implementations of these
+// interfaces.
+//
+// ByteSink:
+// UncheckedArrayByteSink Writes to an array, without bounds checking
+// CheckedArrayByteSink Writes to an array, with bounds checking
+// GrowingArrayByteSink Allocates and writes to a growable buffer
+// StringByteSink Writes to an STL string
+// NullByteSink Consumes a never-ending stream of bytes
+//
+// ByteSource:
+// ArrayByteSource Reads from an array or string/StringPiece
+// LimitedByteSource Limits the number of bytes read from an
+
+#ifndef GOOGLE_PROTOBUF_STUBS_BYTESTREAM_H_
+#define GOOGLE_PROTOBUF_STUBS_BYTESTREAM_H_
+
+#include <stddef.h>
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/stringpiece.h>
+
+class CordByteSink;
+class MemBlock;
+
+namespace google {
+namespace protobuf {
+namespace strings {
+
+// An abstract interface for an object that consumes a sequence of bytes. This
+// interface offers 3 different ways to append data, and a Flush() function.
+//
+// Example:
+//
+// string my_data;
+// ...
+// ByteSink* sink = ...
+// sink->Append(my_data.data(), my_data.size());
+// sink->Flush();
+//
+class LIBPROTOBUF_EXPORT ByteSink {
+ public:
+ ByteSink() {}
+ virtual ~ByteSink() {}
+
+ // Appends the "n" bytes starting at "bytes".
+ virtual void Append(const char* bytes, size_t n) = 0;
+
+ // Flushes internal buffers. The default implemenation does nothing. ByteSink
+ // subclasses may use internal buffers that require calling Flush() at the end
+ // of the stream.
+ virtual void Flush();
+
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ByteSink);
+};
+
+// An abstract interface for an object that produces a fixed-size sequence of
+// bytes.
+//
+// Example:
+//
+// ByteSource* source = ...
+// while (source->Available() > 0) {
+// StringPiece data = source->Peek();
+// ... do something with "data" ...
+// source->Skip(data.length());
+// }
+//
+class LIBPROTOBUF_EXPORT ByteSource {
+ public:
+ ByteSource() {}
+ virtual ~ByteSource() {}
+
+ // Returns the number of bytes left to read from the source. Available()
+ // should decrease by N each time Skip(N) is called. Available() may not
+ // increase. Available() returning 0 indicates that the ByteSource is
+ // exhausted.
+ //
+ // Note: Size() may have been a more appropriate name as it's more
+ // indicative of the fixed-size nature of a ByteSource.
+ virtual size_t Available() const = 0;
+
+ // Returns a StringPiece of the next contiguous region of the source. Does not
+ // reposition the source. The returned region is empty iff Available() == 0.
+ //
+ // The returned region is valid until the next call to Skip() or until this
+ // object is destroyed, whichever occurs first.
+ //
+ // The length of the returned StringPiece will be <= Available().
+ virtual StringPiece Peek() = 0;
+
+ // Skips the next n bytes. Invalidates any StringPiece returned by a previous
+ // call to Peek().
+ //
+ // REQUIRES: Available() >= n
+ virtual void Skip(size_t n) = 0;
+
+ // Writes the next n bytes in this ByteSource to the given ByteSink, and
+ // advances this ByteSource past the copied bytes. The default implementation
+ // of this method just copies the bytes normally, but subclasses might
+ // override CopyTo to optimize certain cases.
+ //
+ // REQUIRES: Available() >= n
+ virtual void CopyTo(ByteSink* sink, size_t n);
+
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ByteSource);
+};
+
+//
+// Some commonly used implementations of ByteSink
+//
+
+// Implementation of ByteSink that writes to an unsized byte array. No
+// bounds-checking is performed--it is the caller's responsibility to ensure
+// that the destination array is large enough.
+//
+// Example:
+//
+// char buf[10];
+// UncheckedArrayByteSink sink(buf);
+// sink.Append("hi", 2); // OK
+// sink.Append(data, 100); // WOOPS! Overflows buf[10].
+//
+class LIBPROTOBUF_EXPORT UncheckedArrayByteSink : public ByteSink {
+ public:
+ explicit UncheckedArrayByteSink(char* dest) : dest_(dest) {}
+ virtual void Append(const char* data, size_t n);
+
+ // Returns the current output pointer so that a caller can see how many bytes
+ // were produced.
+ //
+ // Note: this method is not part of the ByteSink interface.
+ char* CurrentDestination() const { return dest_; }
+
+ private:
+ char* dest_;
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(UncheckedArrayByteSink);
+};
+
+// Implementation of ByteSink that writes to a sized byte array. This sink will
+// not write more than "capacity" bytes to outbuf. Once "capacity" bytes are
+// appended, subsequent bytes will be ignored and Overflowed() will return true.
+// Overflowed() does not cause a runtime error (i.e., it does not CHECK fail).
+//
+// Example:
+//
+// char buf[10];
+// CheckedArrayByteSink sink(buf, 10);
+// sink.Append("hi", 2); // OK
+// sink.Append(data, 100); // Will only write 8 more bytes
+//
+class LIBPROTOBUF_EXPORT CheckedArrayByteSink : public ByteSink {
+ public:
+ CheckedArrayByteSink(char* outbuf, size_t capacity);
+ virtual void Append(const char* bytes, size_t n);
+
+ // Returns the number of bytes actually written to the sink.
+ size_t NumberOfBytesWritten() const { return size_; }
+
+ // Returns true if any bytes were discarded, i.e., if there was an
+ // attempt to write more than 'capacity' bytes.
+ bool Overflowed() const { return overflowed_; }
+
+ private:
+ char* outbuf_;
+ const size_t capacity_;
+ size_t size_;
+ bool overflowed_;
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CheckedArrayByteSink);
+};
+
+// Implementation of ByteSink that allocates an internal buffer (a char array)
+// and expands it as needed to accomodate appended data (similar to a string),
+// and allows the caller to take ownership of the internal buffer via the
+// GetBuffer() method. The buffer returned from GetBuffer() must be deleted by
+// the caller with delete[]. GetBuffer() also sets the internal buffer to be
+// empty, and subsequent appends to the sink will create a new buffer. The
+// destructor will free the internal buffer if GetBuffer() was not called.
+//
+// Example:
+//
+// GrowingArrayByteSink sink(10);
+// sink.Append("hi", 2);
+// sink.Append(data, n);
+// const char* buf = sink.GetBuffer(); // Ownership transferred
+// delete[] buf;
+//
+class LIBPROTOBUF_EXPORT GrowingArrayByteSink : public strings::ByteSink {
+ public:
+ explicit GrowingArrayByteSink(size_t estimated_size);
+ virtual ~GrowingArrayByteSink();
+ virtual void Append(const char* bytes, size_t n);
+
+ // Returns the allocated buffer, and sets nbytes to its size. The caller takes
+ // ownership of the buffer and must delete it with delete[].
+ char* GetBuffer(size_t* nbytes);
+
+ private:
+ void Expand(size_t amount);
+ void ShrinkToFit();
+
+ size_t capacity_;
+ char* buf_;
+ size_t size_;
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(GrowingArrayByteSink);
+};
+
+// Implementation of ByteSink that appends to the given string.
+// Existing contents of "dest" are not modified; new data is appended.
+//
+// Example:
+//
+// string dest = "Hello ";
+// StringByteSink sink(&dest);
+// sink.Append("World", 5);
+// assert(dest == "Hello World");
+//
+class LIBPROTOBUF_EXPORT StringByteSink : public ByteSink {
+ public:
+ explicit StringByteSink(string* dest) : dest_(dest) {}
+ virtual void Append(const char* data, size_t n);
+
+ private:
+ string* dest_;
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(StringByteSink);
+};
+
+// Implementation of ByteSink that discards all data.
+//
+// Example:
+//
+// NullByteSink sink;
+// sink.Append(data, data.size()); // All data ignored.
+//
+class LIBPROTOBUF_EXPORT NullByteSink : public ByteSink {
+ public:
+ NullByteSink() {}
+ virtual void Append(const char *data, size_t n) {}
+
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(NullByteSink);
+};
+
+//
+// Some commonly used implementations of ByteSource
+//
+
+// Implementation of ByteSource that reads from a StringPiece.
+//
+// Example:
+//
+// string data = "Hello";
+// ArrayByteSource source(data);
+// assert(source.Available() == 5);
+// assert(source.Peek() == "Hello");
+//
+class LIBPROTOBUF_EXPORT ArrayByteSource : public ByteSource {
+ public:
+ explicit ArrayByteSource(StringPiece s) : input_(s) {}
+
+ virtual size_t Available() const;
+ virtual StringPiece Peek();
+ virtual void Skip(size_t n);
+
+ private:
+ StringPiece input_;
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ArrayByteSource);
+};
+
+// Implementation of ByteSource that wraps another ByteSource, limiting the
+// number of bytes returned.
+//
+// The caller maintains ownership of the underlying source, and may not use the
+// underlying source while using the LimitByteSource object. The underlying
+// source's pointer is advanced by n bytes every time this LimitByteSource
+// object is advanced by n.
+//
+// Example:
+//
+// string data = "Hello World";
+// ArrayByteSource abs(data);
+// assert(abs.Available() == data.size());
+//
+// LimitByteSource limit(abs, 5);
+// assert(limit.Available() == 5);
+// assert(limit.Peek() == "Hello");
+//
+class LIBPROTOBUF_EXPORT LimitByteSource : public ByteSource {
+ public:
+ // Returns at most "limit" bytes from "source".
+ LimitByteSource(ByteSource* source, size_t limit);
+
+ virtual size_t Available() const;
+ virtual StringPiece Peek();
+ virtual void Skip(size_t n);
+
+ // We override CopyTo so that we can forward to the underlying source, in
+ // case it has an efficient implementation of CopyTo.
+ virtual void CopyTo(ByteSink* sink, size_t n);
+
+ private:
+ ByteSource* source_;
+ size_t limit_;
+};
+
+} // namespace strings
+} // namespace protobuf
+} // namespace google
+
+#endif // GOOGLE_PROTOBUF_STUBS_BYTESTREAM_H_
diff --git a/src/google/protobuf/stubs/bytestream_unittest.cc b/src/google/protobuf/stubs/bytestream_unittest.cc
new file mode 100644
index 00000000..06f114ab
--- /dev/null
+++ b/src/google/protobuf/stubs/bytestream_unittest.cc
@@ -0,0 +1,146 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <google/protobuf/stubs/bytestream.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <algorithm>
+
+#include <google/protobuf/testing/googletest.h>
+#include <gtest/gtest.h>
+
+namespace google {
+namespace protobuf {
+namespace strings {
+namespace {
+
+// We use this class instead of ArrayByteSource to simulate a ByteSource that
+// contains multiple fragments. ArrayByteSource returns the entire array in
+// one fragment.
+class MockByteSource : public ByteSource {
+ public:
+ MockByteSource(StringPiece data, int block_size)
+ : data_(data), block_size_(block_size) {}
+
+ size_t Available() const { return data_.size(); }
+ StringPiece Peek() {
+ return data_.substr(0, block_size_);
+ }
+ void Skip(size_t n) { data_.remove_prefix(n); }
+
+ private:
+ StringPiece data_;
+ int block_size_;
+};
+
+TEST(ByteSourceTest, CopyTo) {
+ StringPiece data("Hello world!");
+ MockByteSource source(data, 3);
+ string str;
+ StringByteSink sink(&str);
+
+ source.CopyTo(&sink, data.size());
+ EXPECT_EQ(data, str);
+}
+
+TEST(ByteSourceTest, CopySubstringTo) {
+ StringPiece data("Hello world!");
+ MockByteSource source(data, 3);
+ source.Skip(1);
+ string str;
+ StringByteSink sink(&str);
+
+ source.CopyTo(&sink, data.size() - 2);
+ EXPECT_EQ(data.substr(1, data.size() - 2), str);
+ EXPECT_EQ("!", source.Peek());
+}
+
+TEST(ByteSourceTest, LimitByteSource) {
+ StringPiece data("Hello world!");
+ MockByteSource source(data, 3);
+ LimitByteSource limit_source(&source, 6);
+ EXPECT_EQ(6, limit_source.Available());
+ limit_source.Skip(1);
+ EXPECT_EQ(5, limit_source.Available());
+
+ {
+ string str;
+ StringByteSink sink(&str);
+ limit_source.CopyTo(&sink, limit_source.Available());
+ EXPECT_EQ("ello ", str);
+ EXPECT_EQ(0, limit_source.Available());
+ EXPECT_EQ(6, source.Available());
+ }
+
+ {
+ string str;
+ StringByteSink sink(&str);
+ source.CopyTo(&sink, source.Available());
+ EXPECT_EQ("world!", str);
+ EXPECT_EQ(0, source.Available());
+ }
+}
+
+TEST(ByteSourceTest, CopyToStringByteSink) {
+ StringPiece data("Hello world!");
+ MockByteSource source(data, 3);
+ string str;
+ StringByteSink sink(&str);
+ source.CopyTo(&sink, data.size());
+ EXPECT_EQ(data, str);
+}
+
+// Verify that ByteSink is subclassable and Flush() overridable.
+class FlushingByteSink : public StringByteSink {
+ public:
+ explicit FlushingByteSink(string* dest) : StringByteSink(dest) {}
+ virtual void Flush() { Append("z", 1); }
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FlushingByteSink);
+};
+
+// Write and Flush via the ByteSink superclass interface.
+void WriteAndFlush(ByteSink* s) {
+ s->Append("abc", 3);
+ s->Flush();
+}
+
+TEST(ByteSinkTest, Flush) {
+ string str;
+ FlushingByteSink f_sink(&str);
+ WriteAndFlush(&f_sink);
+ EXPECT_STREQ("abcz", str.c_str());
+}
+
+} // namespace
+} // namespace strings
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/stubs/casts.h b/src/google/protobuf/stubs/casts.h
index cccf65a1..be652849 100644
--- a/src/google/protobuf/stubs/casts.h
+++ b/src/google/protobuf/stubs/casts.h
@@ -111,12 +111,22 @@ inline To down_cast(From& f) {
return *static_cast<ToAsPointer>(&f);
}
+template<typename To, typename From>
+inline To bit_cast(const From& from) {
+ GOOGLE_COMPILE_ASSERT(sizeof(From) == sizeof(To),
+ bit_cast_with_different_sizes);
+ To dest;
+ memcpy(&dest, &from, sizeof(dest));
+ return dest;
+}
+
} // namespace internal
// We made these internal so that they would show up as such in the docs,
// but we don't want to stick "internal::" in front of them everywhere.
using internal::implicit_cast;
using internal::down_cast;
+using internal::bit_cast;
} // namespace protobuf
} // namespace google
diff --git a/src/google/protobuf/stubs/common.cc b/src/google/protobuf/stubs/common.cc
index 22cc5aa1..d470fc72 100644
--- a/src/google/protobuf/stubs/common.cc
+++ b/src/google/protobuf/stubs/common.cc
@@ -32,6 +32,9 @@
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/once.h>
+#include <google/protobuf/stubs/status.h>
+#include <google/protobuf/stubs/stringpiece.h>
+#include <google/protobuf/stubs/strutil.h>
#include <stdio.h>
#include <errno.h>
#include <vector>
@@ -146,6 +149,27 @@ LogMessage& LogMessage::operator<<(const char* value) {
return *this;
}
+LogMessage& LogMessage::operator<<(const StringPiece& value) {
+ message_ += value.ToString();
+ return *this;
+}
+
+LogMessage& LogMessage::operator<<(long long value) {
+ message_ += SimpleItoa(value);
+ return *this;
+}
+
+LogMessage& LogMessage::operator<<(unsigned long long value) {
+ message_ += SimpleItoa(value);
+ return *this;
+}
+
+LogMessage& LogMessage::operator<<(
+ const ::google::protobuf::util::Status& status) {
+ message_ += status.ToString();
+ return *this;
+}
+
// Since this is just for logging, we don't care if the current locale changes
// the results -- in fact, we probably prefer that. So we use snprintf()
// instead of Simple*toa().
@@ -165,7 +189,7 @@ LogMessage& LogMessage::operator<<(const char* value) {
DECLARE_STREAM_OPERATOR(char , "%c" )
DECLARE_STREAM_OPERATOR(int , "%d" )
-DECLARE_STREAM_OPERATOR(uint , "%u" )
+DECLARE_STREAM_OPERATOR(unsigned int , "%u" )
DECLARE_STREAM_OPERATOR(long , "%ld")
DECLARE_STREAM_OPERATOR(unsigned long, "%lu")
DECLARE_STREAM_OPERATOR(double , "%g" )
diff --git a/src/google/protobuf/stubs/common.h b/src/google/protobuf/stubs/common.h
index 49d16020..c3620146 100644
--- a/src/google/protobuf/stubs/common.h
+++ b/src/google/protobuf/stubs/common.h
@@ -48,6 +48,26 @@
#include <stdint.h>
#endif
+#undef PROTOBUF_LITTLE_ENDIAN
+#ifdef _MSC_VER
+ // Assuming windows is always little-endian.
+ #if !defined(PROTOBUF_DISABLE_LITTLE_ENDIAN_OPT_FOR_TEST)
+ #define PROTOBUF_LITTLE_ENDIAN 1
+ #endif
+ #if _MSC_VER >= 1300
+ // If MSVC has "/RTCc" set, it will complain about truncating casts at
+ // runtime. This file contains some intentional truncating casts.
+ #pragma runtime_checks("c", off)
+ #endif
+#else
+ #include <sys/param.h> // __BYTE_ORDER
+ #if ((defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)) || \
+ (defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN)) && \
+ !defined(PROTOBUF_DISABLE_LITTLE_ENDIAN_OPT_FOR_TEST)
+ #define PROTOBUF_LITTLE_ENDIAN 1
+ #endif
+#endif
+
#ifndef PROTOBUF_USE_EXCEPTIONS
#if defined(_MSC_VER) && defined(_CPPUNWIND)
#define PROTOBUF_USE_EXCEPTIONS 1
@@ -100,6 +120,12 @@ namespace protobuf {
TypeName(const TypeName&); \
void operator=(const TypeName&)
+#undef GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS
+#define GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
+ TypeName(); \
+ TypeName(const TypeName&); \
+ void operator=(const TypeName&)
+
#if defined(_MSC_VER) && defined(PROTOBUF_USE_DLLS)
#ifdef LIBPROTOBUF_EXPORTS
#define LIBPROTOBUF_EXPORT __declspec(dllexport)
@@ -660,6 +686,10 @@ enum LogLevel {
#endif
};
+class StringPiece;
+namespace util {
+class Status;
+}
namespace internal {
class LogFinisher;
@@ -673,11 +703,15 @@ class LIBPROTOBUF_EXPORT LogMessage {
LogMessage& operator<<(const char* value);
LogMessage& operator<<(char value);
LogMessage& operator<<(int value);
- LogMessage& operator<<(uint value);
+ LogMessage& operator<<(unsigned int value);
LogMessage& operator<<(long value);
LogMessage& operator<<(unsigned long value);
+ LogMessage& operator<<(long long value);
+ LogMessage& operator<<(unsigned long long value);
LogMessage& operator<<(double value);
LogMessage& operator<<(void* value);
+ LogMessage& operator<<(const StringPiece& value);
+ LogMessage& operator<<(const ::google::protobuf::util::Status& status);
private:
friend class LogFinisher;
@@ -696,6 +730,11 @@ class LIBPROTOBUF_EXPORT LogFinisher {
void operator=(LogMessage& other);
};
+template<typename T>
+bool IsOk(T status) { return status.ok(); }
+template<>
+inline bool IsOk(bool status) { return status; }
+
} // namespace internal
// Undef everything in case we're being mixed with some other Google library
@@ -717,6 +756,7 @@ class LIBPROTOBUF_EXPORT LogFinisher {
#undef GOOGLE_DLOG
#undef GOOGLE_DCHECK
+#undef GOOGLE_DCHECK_OK
#undef GOOGLE_DCHECK_EQ
#undef GOOGLE_DCHECK_NE
#undef GOOGLE_DCHECK_LT
@@ -733,7 +773,7 @@ class LIBPROTOBUF_EXPORT LogFinisher {
#define GOOGLE_CHECK(EXPRESSION) \
GOOGLE_LOG_IF(FATAL, !(EXPRESSION)) << "CHECK failed: " #EXPRESSION ": "
-#define GOOGLE_CHECK_OK(A) GOOGLE_CHECK(A)
+#define GOOGLE_CHECK_OK(A) GOOGLE_CHECK(::google::protobuf::internal::IsOk(A))
#define GOOGLE_CHECK_EQ(A, B) GOOGLE_CHECK((A) == (B))
#define GOOGLE_CHECK_NE(A, B) GOOGLE_CHECK((A) != (B))
#define GOOGLE_CHECK_LT(A, B) GOOGLE_CHECK((A) < (B))
@@ -760,6 +800,7 @@ T* CheckNotNull(const char* /* file */, int /* line */,
#define GOOGLE_DLOG GOOGLE_LOG_IF(INFO, false)
#define GOOGLE_DCHECK(EXPRESSION) while(false) GOOGLE_CHECK(EXPRESSION)
+#define GOOGLE_DCHECK_OK(E) GOOGLE_DCHECK(::google::protobuf::internal::IsOk(E))
#define GOOGLE_DCHECK_EQ(A, B) GOOGLE_DCHECK((A) == (B))
#define GOOGLE_DCHECK_NE(A, B) GOOGLE_DCHECK((A) != (B))
#define GOOGLE_DCHECK_LT(A, B) GOOGLE_DCHECK((A) < (B))
@@ -772,6 +813,7 @@ T* CheckNotNull(const char* /* file */, int /* line */,
#define GOOGLE_DLOG GOOGLE_LOG
#define GOOGLE_DCHECK GOOGLE_CHECK
+#define GOOGLE_DCHECK_OK GOOGLE_CHECK_OK
#define GOOGLE_DCHECK_EQ GOOGLE_CHECK_EQ
#define GOOGLE_DCHECK_NE GOOGLE_CHECK_NE
#define GOOGLE_DCHECK_LT GOOGLE_CHECK_LT
@@ -883,6 +925,30 @@ class LIBPROTOBUF_EXPORT Closure {
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Closure);
};
+template<typename R, typename A1>
+class LIBPROTOBUF_EXPORT ResultCallback1 {
+ public:
+ ResultCallback1() {}
+ virtual ~ResultCallback1() {}
+
+ virtual R Run(A1) = 0;
+
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ResultCallback1);
+};
+
+template<typename R, typename A1, typename A2>
+class LIBPROTOBUF_EXPORT ResultCallback2 {
+ public:
+ ResultCallback2() {}
+ virtual ~ResultCallback2() {}
+
+ virtual R Run(A1,A2) = 0;
+
+ private:
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ResultCallback2);
+};
+
namespace internal {
class LIBPROTOBUF_EXPORT FunctionClosure0 : public Closure {
@@ -1021,6 +1087,96 @@ class MethodClosure2 : public Closure {
Arg2 arg2_;
};
+template<typename R, typename Arg1>
+class FunctionResultCallback_0_1 : public ResultCallback1<R, Arg1> {
+ public:
+ typedef R (*FunctionType)(Arg1 arg1);
+
+ FunctionResultCallback_0_1(FunctionType function, bool self_deleting)
+ : function_(function), self_deleting_(self_deleting) {}
+ ~FunctionResultCallback_0_1() {}
+
+ R Run(Arg1 a1) {
+ bool needs_delete = self_deleting_; // read in case callback deletes
+ R result = function_(a1);
+ if (needs_delete) delete this;
+ return result;
+ }
+
+ private:
+ FunctionType function_;
+ bool self_deleting_;
+};
+
+template<typename R, typename P1, typename A1>
+class FunctionResultCallback_1_1 : public ResultCallback1<R, A1> {
+ public:
+ typedef R (*FunctionType)(P1, A1);
+
+ FunctionResultCallback_1_1(FunctionType function, bool self_deleting,
+ P1 p1)
+ : function_(function), self_deleting_(self_deleting), p1_(p1) {}
+ ~FunctionResultCallback_1_1() {}
+
+ R Run(A1 a1) {
+ bool needs_delete = self_deleting_; // read in case callback deletes
+ R result = function_(p1_, a1);
+ if (needs_delete) delete this;
+ return result;
+ }
+
+ private:
+ FunctionType function_;
+ bool self_deleting_;
+ P1 p1_;
+};
+
+// Duplicate this again in the type_traits.h, due to dependency problems.
+template <class T> struct internal_remove_reference;
+template<typename T> struct internal_remove_reference { typedef T type; };
+template<typename T> struct internal_remove_reference<T&> { typedef T type; };
+
+template <typename T>
+struct InternalConstRef {
+ typedef typename internal_remove_reference<T>::type base_type;
+ typedef const base_type& type;
+};
+
+template <typename R, typename T, typename P1, typename P2, typename P3,
+ typename P4, typename P5, typename A1, typename A2>
+class MethodResultCallback_5_2 : public ResultCallback2<R, A1, A2> {
+ public:
+ typedef R (T::*MethodType)(P1, P2, P3, P4, P5, A1, A2);
+ MethodResultCallback_5_2(T* object, MethodType method, bool self_deleting,
+ P1 p1, P2 p2, P3 p3, P4 p4, P5 p5)
+ : object_(object),
+ method_(method),
+ self_deleting_(self_deleting),
+ p1_(p1),
+ p2_(p2),
+ p3_(p3),
+ p4_(p4),
+ p5_(p5) {}
+ ~MethodResultCallback_5_2() {}
+
+ R Run(A1 a1, A2 a2) {
+ bool needs_delete = self_deleting_;
+ R result = (object_->*method_)(p1_, p2_, p3_, p4_, p5_, a1, a2);
+ if (needs_delete) delete this;
+ return result;
+ }
+
+ private:
+ T* object_;
+ MethodType method_;
+ bool self_deleting_;
+ typename internal_remove_reference<P1>::type p1_;
+ typename internal_remove_reference<P2>::type p2_;
+ typename internal_remove_reference<P3>::type p3_;
+ typename internal_remove_reference<P4>::type p4_;
+ typename internal_remove_reference<P5>::type p5_;
+};
+
} // namespace internal
// See Closure.
@@ -1106,6 +1262,48 @@ inline Closure* NewPermanentCallback(
object, method, false, arg1, arg2);
}
+// See ResultCallback1
+template<typename R, typename A1>
+inline ResultCallback1<R, A1>* NewCallback(R (*function)(A1)) {
+ return new internal::FunctionResultCallback_0_1<R, A1>(function, true);
+}
+
+// See ResultCallback1
+template<typename R, typename A1>
+inline ResultCallback1<R, A1>* NewPermanentCallback(R (*function)(A1)) {
+ return new internal::FunctionResultCallback_0_1<R, A1>(function, false);
+}
+
+// See ResultCallback1
+template<typename R, typename P1, typename A1>
+inline ResultCallback1<R, A1>* NewCallback(R (*function)(P1, A1), P1 p1) {
+ return new internal::FunctionResultCallback_1_1<R, P1, A1>(
+ function, true, p1);
+}
+
+// See ResultCallback1
+template<typename R, typename P1, typename A1>
+inline ResultCallback1<R, A1>* NewPermanentCallback(
+ R (*function)(P1, A1), P1 p1) {
+ return new internal::FunctionResultCallback_1_1<R, P1, A1>(
+ function, false, p1);
+}
+
+// See MethodResultCallback_5_2
+template <typename R, typename T, typename P1, typename P2, typename P3,
+ typename P4, typename P5, typename A1, typename A2>
+inline ResultCallback2<R, A1, A2>* NewPermanentCallback(
+ T* object, R (T::*function)(P1, P2, P3, P4, P5, A1, A2),
+ typename internal::InternalConstRef<P1>::type p1,
+ typename internal::InternalConstRef<P2>::type p2,
+ typename internal::InternalConstRef<P3>::type p3,
+ typename internal::InternalConstRef<P4>::type p4,
+ typename internal::InternalConstRef<P5>::type p5) {
+ return new internal::MethodResultCallback_5_2<R, T, P1, P2, P3, P4, P5, A1,
+ A2>(object, function, false, p1,
+ p2, p3, p4, p5);
+}
+
// A function which does nothing. Useful for creating no-op callbacks, e.g.:
// Closure* nothing = NewCallback(&DoNothing);
void LIBPROTOBUF_EXPORT DoNothing();
@@ -1226,9 +1424,114 @@ LIBPROTOBUF_EXPORT bool IsStructurallyValidUTF8(const char* buf, int len);
} // namespace internal
// ===================================================================
+// from google3/base/port.h
+
+// The following guarantees declaration of the byte swap functions, and
+// defines __BYTE_ORDER for MSVC
+#ifdef _MSC_VER
+#include <stdlib.h> // NOLINT(build/include)
+#define __BYTE_ORDER __LITTLE_ENDIAN
+#define bswap_16(x) _byteswap_ushort(x)
+#define bswap_32(x) _byteswap_ulong(x)
+#define bswap_64(x) _byteswap_uint64(x)
+
+#elif defined(__APPLE__)
+// Mac OS X / Darwin features
+#include <libkern/OSByteOrder.h>
+#define bswap_16(x) OSSwapInt16(x)
+#define bswap_32(x) OSSwapInt32(x)
+#define bswap_64(x) OSSwapInt64(x)
+
+#elif defined(__GLIBC__) || defined(__CYGWIN__)
+#include <byteswap.h> // IWYU pragma: export
+
+#else
+
+static inline uint16 bswap_16(uint16 x) {
+ return static_cast<uint16>(((x & 0xFF) << 8) | ((x & 0xFF00) >> 8));
+}
+#define bswap_16(x) bswap_16(x)
+static inline uint32 bswap_32(uint32 x) {
+ return (((x & 0xFF) << 24) |
+ ((x & 0xFF00) << 8) |
+ ((x & 0xFF0000) >> 8) |
+ ((x & 0xFF000000) >> 24));
+}
+#define bswap_32(x) bswap_32(x)
+static inline uint64 bswap_64(uint64 x) {
+ return (((x & GG_ULONGLONG(0xFF)) << 56) |
+ ((x & GG_ULONGLONG(0xFF00)) << 40) |
+ ((x & GG_ULONGLONG(0xFF0000)) << 24) |
+ ((x & GG_ULONGLONG(0xFF000000)) << 8) |
+ ((x & GG_ULONGLONG(0xFF00000000)) >> 8) |
+ ((x & GG_ULONGLONG(0xFF0000000000)) >> 24) |
+ ((x & GG_ULONGLONG(0xFF000000000000)) >> 40) |
+ ((x & GG_ULONGLONG(0xFF00000000000000)) >> 56));
+}
+#define bswap_64(x) bswap_64(x)
+
+#endif
+
+// ===================================================================
// from google3/util/endian/endian.h
LIBPROTOBUF_EXPORT uint32 ghtonl(uint32 x);
+class BigEndian {
+ public:
+#ifdef PROTOBUF_LITTLE_ENDIAN
+
+ static uint16 FromHost16(uint16 x) { return bswap_16(x); }
+ static uint16 ToHost16(uint16 x) { return bswap_16(x); }
+
+ static uint32 FromHost32(uint32 x) { return bswap_32(x); }
+ static uint32 ToHost32(uint32 x) { return bswap_32(x); }
+
+ static uint64 FromHost64(uint64 x) { return bswap_64(x); }
+ static uint64 ToHost64(uint64 x) { return bswap_64(x); }
+
+ static bool IsLittleEndian() { return true; }
+
+#else
+
+ static uint16 FromHost16(uint16 x) { return x; }
+ static uint16 ToHost16(uint16 x) { return x; }
+
+ static uint32 FromHost32(uint32 x) { return x; }
+ static uint32 ToHost32(uint32 x) { return x; }
+
+ static uint64 FromHost64(uint64 x) { return x; }
+ static uint64 ToHost64(uint64 x) { return x; }
+
+ static bool IsLittleEndian() { return false; }
+
+#endif /* ENDIAN */
+
+ // Functions to do unaligned loads and stores in big-endian order.
+ static uint16 Load16(const void *p) {
+ return ToHost16(GOOGLE_UNALIGNED_LOAD16(p));
+ }
+
+ static void Store16(void *p, uint16 v) {
+ GOOGLE_UNALIGNED_STORE16(p, FromHost16(v));
+ }
+
+ static uint32 Load32(const void *p) {
+ return ToHost32(GOOGLE_UNALIGNED_LOAD32(p));
+ }
+
+ static void Store32(void *p, uint32 v) {
+ GOOGLE_UNALIGNED_STORE32(p, FromHost32(v));
+ }
+
+ static uint64 Load64(const void *p) {
+ return ToHost64(GOOGLE_UNALIGNED_LOAD64(p));
+ }
+
+ static void Store64(void *p, uint64 v) {
+ GOOGLE_UNALIGNED_STORE64(p, FromHost64(v));
+ }
+};
+
// ===================================================================
// Shutdown support.
diff --git a/src/google/protobuf/stubs/mathlimits.cc b/src/google/protobuf/stubs/mathlimits.cc
new file mode 100644
index 00000000..0373b2bb
--- /dev/null
+++ b/src/google/protobuf/stubs/mathlimits.cc
@@ -0,0 +1,144 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// All Rights Reserved.
+//
+// Author: Maxim Lifantsev
+//
+
+#include <google/protobuf/stubs/mathlimits.h>
+
+#include <google/protobuf/stubs/common.h>
+
+namespace google {
+namespace protobuf {
+
+// MSVC++ 2005 and older compilers think the header declaration was a
+// definition, and erroneously flag these as a duplicate definition.
+#if defined(COMPILER_MSVC) || __cpluscplus < 201103L
+
+#define DEF_COMMON_LIMITS(Type)
+#define DEF_UNSIGNED_INT_LIMITS(Type)
+#define DEF_SIGNED_INT_LIMITS(Type)
+#define DEF_PRECISION_LIMITS(Type)
+
+#else
+
+#define DEF_COMMON_LIMITS(Type) \
+const bool MathLimits<Type>::kIsSigned; \
+const bool MathLimits<Type>::kIsInteger; \
+const int MathLimits<Type>::kMin10Exp; \
+const int MathLimits<Type>::kMax10Exp;
+
+#define DEF_UNSIGNED_INT_LIMITS(Type) \
+DEF_COMMON_LIMITS(Type) \
+const Type MathLimits<Type>::kPosMin; \
+const Type MathLimits<Type>::kPosMax; \
+const Type MathLimits<Type>::kMin; \
+const Type MathLimits<Type>::kMax; \
+const Type MathLimits<Type>::kEpsilon; \
+const Type MathLimits<Type>::kStdError;
+
+#define DEF_SIGNED_INT_LIMITS(Type) \
+DEF_UNSIGNED_INT_LIMITS(Type) \
+const Type MathLimits<Type>::kNegMin; \
+const Type MathLimits<Type>::kNegMax;
+
+#define DEF_PRECISION_LIMITS(Type) \
+const int MathLimits<Type>::kPrecisionDigits;
+
+#endif // not COMPILER_MSVC
+
+// http://en.wikipedia.org/wiki/Quadruple_precision_floating-point_format#Double-double_arithmetic
+// With some compilers (gcc 4.6.x) on some platforms (powerpc64),
+// "long double" is implemented as a pair of double: "double double" format.
+// This causes a problem with epsilon (eps).
+// eps is the smallest positive number such that 1.0 + eps > 1.0
+//
+// Normal format: 1.0 + e = 1.0...01 // N-1 zeros for N fraction bits
+// D-D format: 1.0 + e = 1.000...0001 // epsilon can be very small
+//
+// In the normal format, 1.0 + e has to fit in one stretch of bits.
+// The maximum rounding error is half of eps.
+//
+// In the double-double format, 1.0 + e splits across two doubles:
+// 1.0 in the high double, e in the low double, and they do not have to
+// be contiguous. The maximum rounding error on a value close to 1.0 is
+// much larger than eps.
+//
+// Some code checks for errors by comparing a computed value to a golden
+// value +/- some multiple of the maximum rounding error. The maximum
+// rounding error is not available so we use eps as an approximation
+// instead. That fails when long double is in the double-double format.
+// Therefore, we define kStdError as a multiple of
+// max(DBL_EPSILON * DBL_EPSILON, kEpsilon) rather than a multiple of kEpsilon.
+
+#define DEF_FP_LIMITS(Type, PREFIX) \
+DEF_COMMON_LIMITS(Type) \
+const Type MathLimits<Type>::kPosMin = PREFIX##_MIN; \
+const Type MathLimits<Type>::kPosMax = PREFIX##_MAX; \
+const Type MathLimits<Type>::kMin = -MathLimits<Type>::kPosMax; \
+const Type MathLimits<Type>::kMax = MathLimits<Type>::kPosMax; \
+const Type MathLimits<Type>::kNegMin = -MathLimits<Type>::kPosMin; \
+const Type MathLimits<Type>::kNegMax = -MathLimits<Type>::kPosMax; \
+const Type MathLimits<Type>::kEpsilon = PREFIX##_EPSILON; \
+/* 32 is 5 bits of mantissa error; should be adequate for common errors */ \
+const Type MathLimits<Type>::kStdError = \
+ 32 * (DBL_EPSILON * DBL_EPSILON > MathLimits<Type>::kEpsilon \
+ ? DBL_EPSILON * DBL_EPSILON : MathLimits<Type>::kEpsilon); \
+DEF_PRECISION_LIMITS(Type) \
+const Type MathLimits<Type>::kNaN = HUGE_VAL - HUGE_VAL; \
+const Type MathLimits<Type>::kPosInf = HUGE_VAL; \
+const Type MathLimits<Type>::kNegInf = -HUGE_VAL;
+
+// The following are *not* casts!
+DEF_SIGNED_INT_LIMITS(int8)
+DEF_SIGNED_INT_LIMITS(int16) // NOLINT(readability/casting)
+DEF_SIGNED_INT_LIMITS(int32) // NOLINT(readability/casting)
+DEF_SIGNED_INT_LIMITS(int64) // NOLINT(readability/casting)
+DEF_UNSIGNED_INT_LIMITS(uint8)
+DEF_UNSIGNED_INT_LIMITS(uint16) // NOLINT(readability/casting)
+DEF_UNSIGNED_INT_LIMITS(uint32) // NOLINT(readability/casting)
+DEF_UNSIGNED_INT_LIMITS(uint64) // NOLINT(readability/casting)
+
+DEF_SIGNED_INT_LIMITS(long int)
+DEF_UNSIGNED_INT_LIMITS(unsigned long int)
+
+DEF_FP_LIMITS(float, FLT)
+DEF_FP_LIMITS(double, DBL)
+DEF_FP_LIMITS(long double, LDBL);
+
+#undef DEF_COMMON_LIMITS
+#undef DEF_SIGNED_INT_LIMITS
+#undef DEF_UNSIGNED_INT_LIMITS
+#undef DEF_FP_LIMITS
+#undef DEF_PRECISION_LIMITS
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/stubs/mathlimits.h b/src/google/protobuf/stubs/mathlimits.h
new file mode 100644
index 00000000..d9846940
--- /dev/null
+++ b/src/google/protobuf/stubs/mathlimits.h
@@ -0,0 +1,279 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// All Rights Reserved.
+//
+// Author: Maxim Lifantsev
+//
+// Useful integer and floating point limits and type traits.
+//
+// This partially replaces/duplictes numeric_limits<> from <limits>.
+// We get a Google-style class that we have a greater control over
+// and thus can add new features to it or fix whatever happens to be broken in
+// numeric_limits for the compilers we use.
+//
+
+#ifndef UTIL_MATH_MATHLIMITS_H__
+#define UTIL_MATH_MATHLIMITS_H__
+
+// <math.h> lacks a lot of prototypes. However, this file needs <math.h> to
+// access old-fashioned isinf et al. Even worse more: this file must not
+// include <cmath> because that breaks the definition of isinf with gcc 4.9.
+//
+// TODO(mec): after C++11 everywhere, use <cmath> and std::isinf in this file.
+#include <math.h>
+#include <string.h>
+
+#include <cfloat>
+
+#include <google/protobuf/stubs/common.h>
+
+// ========================================================================= //
+
+// Useful integer and floating point limits and type traits.
+// This is just for the documentation;
+// real members are defined in our specializations below.
+namespace google {
+namespace protobuf {
+template<typename T> struct MathLimits {
+ // Type name.
+ typedef T Type;
+ // Unsigned version of the Type with the same byte size.
+ // Same as Type for floating point and unsigned types.
+ typedef T UnsignedType;
+ // If the type supports negative values.
+ static const bool kIsSigned;
+ // If the type supports only integer values.
+ static const bool kIsInteger;
+ // Magnitude-wise smallest representable positive value.
+ static const Type kPosMin;
+ // Magnitude-wise largest representable positive value.
+ static const Type kPosMax;
+ // Smallest representable value.
+ static const Type kMin;
+ // Largest representable value.
+ static const Type kMax;
+ // Magnitude-wise smallest representable negative value.
+ // Present only if kIsSigned.
+ static const Type kNegMin;
+ // Magnitude-wise largest representable negative value.
+ // Present only if kIsSigned.
+ static const Type kNegMax;
+ // Smallest integer x such that 10^x is representable.
+ static const int kMin10Exp;
+ // Largest integer x such that 10^x is representable.
+ static const int kMax10Exp;
+ // Smallest positive value such that Type(1) + kEpsilon != Type(1)
+ static const Type kEpsilon;
+ // Typical rounding error that is enough to cover
+ // a few simple floating-point operations.
+ // Slightly larger than kEpsilon to account for a few rounding errors.
+ // Is zero if kIsInteger.
+ static const Type kStdError;
+ // Number of decimal digits of mantissa precision.
+ // Present only if !kIsInteger.
+ static const int kPrecisionDigits;
+ // Not a number, i.e. result of 0/0.
+ // Present only if !kIsInteger.
+ static const Type kNaN;
+ // Positive infinity, i.e. result of 1/0.
+ // Present only if !kIsInteger.
+ static const Type kPosInf;
+ // Negative infinity, i.e. result of -1/0.
+ // Present only if !kIsInteger.
+ static const Type kNegInf;
+
+ // NOTE: Special floating point values behave
+ // in a special (but mathematically-logical) way
+ // in terms of (in)equalty comparison and mathematical operations
+ // -- see out unittest for examples.
+
+ // Special floating point value testers.
+ // Present in integer types for convenience.
+ static bool IsFinite(const Type x);
+ static bool IsNaN(const Type x);
+ static bool IsInf(const Type x);
+ static bool IsPosInf(const Type x);
+ static bool IsNegInf(const Type x);
+};
+
+// ========================================================================= //
+
+// All #define-s below are simply to refactor the declarations of
+// MathLimits template specializations.
+// They are all #undef-ined below.
+
+// The hoop-jumping in *_INT_(MAX|MIN) below is so that the compiler does not
+// get an overflow while computing the constants.
+
+#define SIGNED_INT_MAX(Type) \
+ (((Type(1) << (sizeof(Type)*8 - 2)) - 1) + (Type(1) << (sizeof(Type)*8 - 2)))
+
+#define SIGNED_INT_MIN(Type) \
+ (-(Type(1) << (sizeof(Type)*8 - 2)) - (Type(1) << (sizeof(Type)*8 - 2)))
+
+#define UNSIGNED_INT_MAX(Type) \
+ (((Type(1) << (sizeof(Type)*8 - 1)) - 1) + (Type(1) << (sizeof(Type)*8 - 1)))
+
+// Compile-time selected log10-related constants for integer types.
+#define SIGNED_MAX_10_EXP(Type) \
+ (sizeof(Type) == 1 ? 2 : ( \
+ sizeof(Type) == 2 ? 4 : ( \
+ sizeof(Type) == 4 ? 9 : ( \
+ sizeof(Type) == 8 ? 18 : -1))))
+
+#define UNSIGNED_MAX_10_EXP(Type) \
+ (sizeof(Type) == 1 ? 2 : ( \
+ sizeof(Type) == 2 ? 4 : ( \
+ sizeof(Type) == 4 ? 9 : ( \
+ sizeof(Type) == 8 ? 19 : -1))))
+
+#define DECL_INT_LIMIT_FUNCS \
+ static bool IsFinite(const Type /*x*/) { return true; } \
+ static bool IsNaN(const Type /*x*/) { return false; } \
+ static bool IsInf(const Type /*x*/) { return false; } \
+ static bool IsPosInf(const Type /*x*/) { return false; } \
+ static bool IsNegInf(const Type /*x*/) { return false; }
+
+#define DECL_SIGNED_INT_LIMITS(IntType, UnsignedIntType) \
+template<> \
+struct LIBPROTOBUF_EXPORT MathLimits<IntType> { \
+ typedef IntType Type; \
+ typedef UnsignedIntType UnsignedType; \
+ static const bool kIsSigned = true; \
+ static const bool kIsInteger = true; \
+ static const Type kPosMin = 1; \
+ static const Type kPosMax = SIGNED_INT_MAX(Type); \
+ static const Type kMin = SIGNED_INT_MIN(Type); \
+ static const Type kMax = kPosMax; \
+ static const Type kNegMin = -1; \
+ static const Type kNegMax = kMin; \
+ static const int kMin10Exp = 0; \
+ static const int kMax10Exp = SIGNED_MAX_10_EXP(Type); \
+ static const Type kEpsilon = 1; \
+ static const Type kStdError = 0; \
+ DECL_INT_LIMIT_FUNCS \
+};
+
+#define DECL_UNSIGNED_INT_LIMITS(IntType) \
+template<> \
+struct LIBPROTOBUF_EXPORT MathLimits<IntType> { \
+ typedef IntType Type; \
+ typedef IntType UnsignedType; \
+ static const bool kIsSigned = false; \
+ static const bool kIsInteger = true; \
+ static const Type kPosMin = 1; \
+ static const Type kPosMax = UNSIGNED_INT_MAX(Type); \
+ static const Type kMin = 0; \
+ static const Type kMax = kPosMax; \
+ static const int kMin10Exp = 0; \
+ static const int kMax10Exp = UNSIGNED_MAX_10_EXP(Type); \
+ static const Type kEpsilon = 1; \
+ static const Type kStdError = 0; \
+ DECL_INT_LIMIT_FUNCS \
+};
+
+DECL_SIGNED_INT_LIMITS(signed char, unsigned char)
+DECL_SIGNED_INT_LIMITS(signed short int, unsigned short int)
+DECL_SIGNED_INT_LIMITS(signed int, unsigned int)
+DECL_SIGNED_INT_LIMITS(signed long int, unsigned long int)
+DECL_SIGNED_INT_LIMITS(signed long long int, unsigned long long int)
+DECL_UNSIGNED_INT_LIMITS(unsigned char)
+DECL_UNSIGNED_INT_LIMITS(unsigned short int)
+DECL_UNSIGNED_INT_LIMITS(unsigned int)
+DECL_UNSIGNED_INT_LIMITS(unsigned long int)
+DECL_UNSIGNED_INT_LIMITS(unsigned long long int)
+
+#undef DECL_SIGNED_INT_LIMITS
+#undef DECL_UNSIGNED_INT_LIMITS
+#undef SIGNED_INT_MAX
+#undef SIGNED_INT_MIN
+#undef UNSIGNED_INT_MAX
+#undef SIGNED_MAX_10_EXP
+#undef UNSIGNED_MAX_10_EXP
+#undef DECL_INT_LIMIT_FUNCS
+
+// ========================================================================= //
+#ifdef WIN32 // Lacks built-in isnan() and isinf()
+#define DECL_FP_LIMIT_FUNCS \
+ static bool IsFinite(const Type x) { return _finite(x); } \
+ static bool IsNaN(const Type x) { return _isnan(x); } \
+ static bool IsInf(const Type x) { return (_fpclass(x) & (_FPCLASS_NINF | _FPCLASS_PINF)) != 0; } \
+ static bool IsPosInf(const Type x) { return _fpclass(x) == _FPCLASS_PINF; } \
+ static bool IsNegInf(const Type x) { return _fpclass(x) == _FPCLASS_NINF; }
+#else
+#define DECL_FP_LIMIT_FUNCS \
+ static bool IsFinite(const Type x) { return !isinf(x) && !isnan(x); } \
+ static bool IsNaN(const Type x) { return isnan(x); } \
+ static bool IsInf(const Type x) { return isinf(x); } \
+ static bool IsPosInf(const Type x) { return isinf(x) && x > 0; } \
+ static bool IsNegInf(const Type x) { return isinf(x) && x < 0; }
+#endif
+
+// We can't put floating-point constant values in the header here because
+// such constants are not considered to be primitive-type constants by gcc.
+// CAVEAT: Hence, they are going to be initialized only during
+// the global objects construction time.
+#define DECL_FP_LIMITS(FP_Type, PREFIX) \
+template<> \
+struct LIBPROTOBUF_EXPORT MathLimits<FP_Type> { \
+ typedef FP_Type Type; \
+ typedef FP_Type UnsignedType; \
+ static const bool kIsSigned = true; \
+ static const bool kIsInteger = false; \
+ static const Type kPosMin; \
+ static const Type kPosMax; \
+ static const Type kMin; \
+ static const Type kMax; \
+ static const Type kNegMin; \
+ static const Type kNegMax; \
+ static const int kMin10Exp = PREFIX##_MIN_10_EXP; \
+ static const int kMax10Exp = PREFIX##_MAX_10_EXP; \
+ static const Type kEpsilon; \
+ static const Type kStdError; \
+ static const int kPrecisionDigits = PREFIX##_DIG; \
+ static const Type kNaN; \
+ static const Type kPosInf; \
+ static const Type kNegInf; \
+ DECL_FP_LIMIT_FUNCS \
+};
+
+DECL_FP_LIMITS(float, FLT)
+DECL_FP_LIMITS(double, DBL)
+DECL_FP_LIMITS(long double, LDBL)
+
+#undef DECL_FP_LIMITS
+#undef DECL_FP_LIMIT_FUNCS
+
+// ========================================================================= //
+} // namespace protobuf
+} // namespace google
+
+#endif // UTIL_MATH_MATHLIMITS_H__
diff --git a/src/google/protobuf/stubs/mathutil.h b/src/google/protobuf/stubs/mathutil.h
new file mode 100644
index 00000000..87ca5e91
--- /dev/null
+++ b/src/google/protobuf/stubs/mathutil.h
@@ -0,0 +1,149 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#ifndef GOOGLE_PROTOBUF_STUBS_MATHUTIL_H_
+#define GOOGLE_PROTOBUF_STUBS_MATHUTIL_H_
+
+#include <float.h>
+#include <math.h>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/mathlimits.h>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+template<typename T>
+bool IsNan(T value) {
+ return false;
+}
+template<>
+inline bool IsNan(float value) { return isnan(value); }
+template<>
+inline bool IsNan(double value) { return isnan(value); }
+
+template<typename T>
+bool AlmostEquals(T a, T b) {
+ return a == b;
+}
+template<>
+inline bool AlmostEquals(float a, float b) {
+ return fabs(a - b) < 32 * FLT_EPSILON;
+}
+
+template<>
+inline bool AlmostEquals(double a, double b) {
+ return fabs(a - b) < 32 * DBL_EPSILON;
+}
+} // namespace internal
+
+class MathUtil {
+ public:
+ template<typename T>
+ static T Sign(T value) {
+ if (value == T(0) || ::google::protobuf::internal::IsNan<T>(value)) {
+ return value;
+ }
+ return value > T(0) ? value : -value;
+ }
+
+ template<typename T>
+ static bool AlmostEquals(T a, T b) {
+ return ::google::protobuf::internal::AlmostEquals(a, b);
+ }
+
+ // Largest of two values.
+ // Works correctly for special floating point values.
+ // Note: 0.0 and -0.0 are not differentiated by Max (Max(0.0, -0.0) is -0.0),
+ // which should be OK because, although they (can) have different
+ // bit representation, they are observably the same when examined
+ // with arithmetic and (in)equality operators.
+ template<typename T>
+ static T Max(const T x, const T y) {
+ return MathLimits<T>::IsNaN(x) || x > y ? x : y;
+ }
+
+ // Absolute value of x
+ // Works correctly for unsigned types and
+ // for special floating point values.
+ // Note: 0.0 and -0.0 are not differentiated by Abs (Abs(0.0) is -0.0),
+ // which should be OK: see the comment for Max above.
+ template<typename T>
+ static T Abs(const T x) {
+ return x > T(0) ? x : -x;
+ }
+
+ // Absolute value of the difference between two numbers.
+ // Works correctly for signed types and special floating point values.
+ template<typename T>
+ static typename MathLimits<T>::UnsignedType AbsDiff(const T x, const T y) {
+ // Carries out arithmetic as unsigned to avoid overflow.
+ typedef typename MathLimits<T>::UnsignedType R;
+ return x > y ? R(x) - R(y) : R(y) - R(x);
+ }
+
+ // If two (usually floating point) numbers are within a certain
+ // fraction of their magnitude or within a certain absolute margin of error.
+ // This is the same as the following but faster:
+ // WithinFraction(x, y, fraction) || WithinMargin(x, y, margin)
+ // E.g. WithinFraction(0.0, 1e-10, 1e-5) is false but
+ // WithinFractionOrMargin(0.0, 1e-10, 1e-5, 1e-5) is true.
+ template<typename T>
+ static bool WithinFractionOrMargin(const T x, const T y,
+ const T fraction, const T margin);
+};
+
+template<typename T>
+bool MathUtil::WithinFractionOrMargin(const T x, const T y,
+ const T fraction, const T margin) {
+ // Not just "0 <= fraction" to fool the compiler for unsigned types.
+ GOOGLE_DCHECK((T(0) < fraction || T(0) == fraction) &&
+ fraction < T(1) &&
+ margin >= T(0));
+
+ // Template specialization will convert the if() condition to a constant,
+ // which will cause the compiler to generate code for either the "if" part
+ // or the "then" part. In this way we avoid a compiler warning
+ // about a potential integer overflow in crosstool v12 (gcc 4.3.1).
+ if (MathLimits<T>::kIsInteger) {
+ return x == y;
+ } else {
+ // IsFinite checks are to make kPosInf and kNegInf not within fraction
+ if (!MathLimits<T>::IsFinite(x) && !MathLimits<T>::IsFinite(y)) {
+ return false;
+ }
+ T relative_margin = static_cast<T>(fraction * Max(Abs(x), Abs(y)));
+ return AbsDiff(x, y) <= Max(margin, relative_margin);
+ }
+}
+
+} // namespace protobuf
+} // namespace google
+
+#endif // GOOGLE_PROTOBUF_STUBS_MATHUTIL_H_
diff --git a/src/google/protobuf/stubs/status.cc b/src/google/protobuf/stubs/status.cc
new file mode 100644
index 00000000..7314c563
--- /dev/null
+++ b/src/google/protobuf/stubs/status.cc
@@ -0,0 +1,135 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#include <google/protobuf/stubs/status.h>
+
+#include <ostream>
+#include <stdint.h>
+#include <stdio.h>
+#include <string>
+#include <utility>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace error {
+inline string CodeEnumToString(error::Code code) {
+ switch (code) {
+ case OK:
+ return "OK";
+ case CANCELLED:
+ return "CANCELLED";
+ case UNKNOWN:
+ return "UNKNOWN";
+ case INVALID_ARGUMENT:
+ return "INVALID_ARGUMENT";
+ case DEADLINE_EXCEEDED:
+ return "DEADLINE_EXCEEDED";
+ case NOT_FOUND:
+ return "NOT_FOUND";
+ case ALREADY_EXISTS:
+ return "ALREADY_EXISTS";
+ case PERMISSION_DENIED:
+ return "PERMISSION_DENIED";
+ case UNAUTHENTICATED:
+ return "UNAUTHENTICATED";
+ case RESOURCE_EXHAUSTED:
+ return "RESOURCE_EXHAUSTED";
+ case FAILED_PRECONDITION:
+ return "FAILED_PRECONDITION";
+ case ABORTED:
+ return "ABORTED";
+ case OUT_OF_RANGE:
+ return "OUT_OF_RANGE";
+ case UNIMPLEMENTED:
+ return "UNIMPLEMENTED";
+ case INTERNAL:
+ return "INTERNAL";
+ case UNAVAILABLE:
+ return "UNAVAILABLE";
+ case DATA_LOSS:
+ return "DATA_LOSS";
+ }
+
+ // No default clause, clang will abort if a code is missing from
+ // above switch.
+ return "UNKNOWN";
+}
+} // namespace error.
+
+const Status Status::OK = Status();
+const Status Status::CANCELLED = Status(error::CANCELLED, "");
+const Status Status::UNKNOWN = Status(error::UNKNOWN, "");
+
+Status::Status() : error_code_(error::OK) {
+}
+
+Status::Status(error::Code error_code, StringPiece error_message)
+ : error_code_(error_code) {
+ if (error_code != error::OK) {
+ error_message_ = error_message.ToString();
+ }
+}
+
+Status::Status(const Status& other)
+ : error_code_(other.error_code_), error_message_(other.error_message_) {
+}
+
+Status& Status::operator=(const Status& other) {
+ error_code_ = other.error_code_;
+ error_message_ = other.error_message_;
+ return *this;
+}
+
+bool Status::operator==(const Status& x) const {
+ return error_code_ == x.error_code_ &&
+ error_message_ == x.error_message_;
+}
+
+string Status::ToString() const {
+ if (error_code_ == error::OK) {
+ return "OK";
+ } else {
+ if (error_message_.empty()) {
+ return error::CodeEnumToString(error_code_);
+ } else {
+ return error::CodeEnumToString(error_code_) + ":" +
+ error_message_;
+ }
+ }
+}
+
+ostream& operator<<(ostream& os, const Status& x) {
+ os << x.ToString();
+ return os;
+}
+
+} // namespace util
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/stubs/status.h b/src/google/protobuf/stubs/status.h
new file mode 100644
index 00000000..614ab994
--- /dev/null
+++ b/src/google/protobuf/stubs/status.h
@@ -0,0 +1,116 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#ifndef GOOGLE_PROTOBUF_STUBS_STATUS_H_
+#define GOOGLE_PROTOBUF_STUBS_STATUS_H_
+
+#include <iosfwd>
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/stringpiece.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace error {
+// These values must match error codes defined in google/rpc/code.proto.
+enum Code {
+ OK = 0,
+ CANCELLED = 1,
+ UNKNOWN = 2,
+ INVALID_ARGUMENT = 3,
+ DEADLINE_EXCEEDED = 4,
+ NOT_FOUND = 5,
+ ALREADY_EXISTS = 6,
+ PERMISSION_DENIED = 7,
+ UNAUTHENTICATED = 16,
+ RESOURCE_EXHAUSTED = 8,
+ FAILED_PRECONDITION = 9,
+ ABORTED = 10,
+ OUT_OF_RANGE = 11,
+ UNIMPLEMENTED = 12,
+ INTERNAL = 13,
+ UNAVAILABLE = 14,
+ DATA_LOSS = 15,
+};
+} // namespace error
+
+class LIBPROTOBUF_EXPORT Status {
+ public:
+ // Creates a "successful" status.
+ Status();
+
+ // Create a status in the canonical error space with the specified
+ // code, and error message. If "code == 0", error_message is
+ // ignored and a Status object identical to Status::OK is
+ // constructed.
+ Status(error::Code error_code, StringPiece error_message);
+ Status(const Status&);
+ Status& operator=(const Status& x);
+ ~Status() {}
+
+ // Some pre-defined Status objects
+ static const Status OK; // Identical to 0-arg constructor
+ static const Status CANCELLED;
+ static const Status UNKNOWN;
+
+ // Accessor
+ bool ok() const {
+ return error_code_ == error::OK;
+ }
+ int error_code() const {
+ return error_code_;
+ }
+ StringPiece error_message() const {
+ return error_message_;
+ }
+
+ bool operator==(const Status& x) const;
+ bool operator!=(const Status& x) const {
+ return !operator==(x);
+ }
+
+ // Return a combination of the error code name and message.
+ string ToString() const;
+
+ private:
+ error::Code error_code_;
+ string error_message_;
+};
+
+// Prints a human-readable representation of 'x' to 'os'.
+LIBPROTOBUF_EXPORT ostream& operator<<(ostream& os, const Status& x);
+
+#define EXPECT_OK(value) EXPECT_TRUE((value).ok())
+
+} // namespace util
+} // namespace protobuf
+} // namespace google
+#endif // GOOGLE_PROTOBUF_STUBS_STATUS_H_
diff --git a/src/google/protobuf/stubs/status_macros.h b/src/google/protobuf/stubs/status_macros.h
new file mode 100644
index 00000000..743e79a7
--- /dev/null
+++ b/src/google/protobuf/stubs/status_macros.h
@@ -0,0 +1,89 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// From: util/task/contrib/status_macros/status_macros.h
+
+#ifndef GOOGLE_PROTOBUF_STUBS_STATUS_MACROS_H_
+#define GOOGLE_PROTOBUF_STUBS_STATUS_MACROS_H_
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/status.h>
+#include <google/protobuf/stubs/statusor.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+
+// Run a command that returns a util::Status. If the called code returns an
+// error status, return that status up out of this method too.
+//
+// Example:
+// RETURN_IF_ERROR(DoThings(4));
+#define RETURN_IF_ERROR(expr) \
+ do { \
+ /* Using _status below to avoid capture problems if expr is "status". */ \
+ const ::google::protobuf::util::Status _status = (expr); \
+ if (GOOGLE_PREDICT_FALSE(!_status.ok())) return _status; \
+ } while (0)
+
+// Internal helper for concatenating macro values.
+#define STATUS_MACROS_CONCAT_NAME_INNER(x, y) x##y
+#define STATUS_MACROS_CONCAT_NAME(x, y) STATUS_MACROS_CONCAT_NAME_INNER(x, y)
+
+template<typename T>
+Status DoAssignOrReturn(T& lhs, StatusOr<T> result) {
+ if (result.ok()) {
+ lhs = result.ValueOrDie();
+ }
+ return result.status();
+}
+
+#define ASSIGN_OR_RETURN_IMPL(status, lhs, rexpr) \
+ Status status = DoAssignOrReturn(lhs, (rexpr)); \
+ if (GOOGLE_PREDICT_FALSE(!status.ok())) return status;
+
+// Executes an expression that returns a util::StatusOr, extracting its value
+// into the variable defined by lhs (or returning on error).
+//
+// Example: Assigning to an existing value
+// ValueType value;
+// ASSIGN_OR_RETURN(value, MaybeGetValue(arg));
+//
+// WARNING: ASSIGN_OR_RETURN expands into multiple statements; it cannot be used
+// in a single statement (e.g. as the body of an if statement without {})!
+#define ASSIGN_OR_RETURN(lhs, rexpr) \
+ ASSIGN_OR_RETURN_IMPL( \
+ STATUS_MACROS_CONCAT_NAME(_status_or_value, __COUNTER__), lhs, rexpr);
+
+} // namespace util
+} // namespace protobuf
+} // namespace google
+
+#endif // GOOGLE_PROTOBUF_STUBS_STATUS_H_
diff --git a/src/google/protobuf/stubs/status_test.cc b/src/google/protobuf/stubs/status_test.cc
new file mode 100644
index 00000000..c70c33c4
--- /dev/null
+++ b/src/google/protobuf/stubs/status_test.cc
@@ -0,0 +1,131 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#include <google/protobuf/stubs/status.h>
+
+#include <stdio.h>
+
+#include <google/protobuf/testing/googletest.h>
+#include <gtest/gtest.h>
+
+namespace google {
+namespace protobuf {
+namespace {
+TEST(Status, Empty) {
+ util::Status status;
+ EXPECT_EQ(util::error::OK, util::Status::OK.error_code());
+ EXPECT_EQ("OK", util::Status::OK.ToString());
+}
+
+TEST(Status, GenericCodes) {
+ EXPECT_EQ(util::error::OK, util::Status::OK.error_code());
+ EXPECT_EQ(util::error::CANCELLED, util::Status::CANCELLED.error_code());
+ EXPECT_EQ(util::error::UNKNOWN, util::Status::UNKNOWN.error_code());
+}
+
+TEST(Status, ConstructorZero) {
+ util::Status status(util::error::OK, "msg");
+ EXPECT_TRUE(status.ok());
+ EXPECT_EQ("OK", status.ToString());
+}
+
+TEST(Status, CheckOK) {
+ util::Status status;
+ GOOGLE_CHECK_OK(status);
+ GOOGLE_CHECK_OK(status) << "Failed";
+ GOOGLE_DCHECK_OK(status) << "Failed";
+}
+
+TEST(Status, ErrorMessage) {
+ util::Status status(util::error::INVALID_ARGUMENT, "");
+ EXPECT_FALSE(status.ok());
+ EXPECT_EQ("", status.error_message().ToString());
+ EXPECT_EQ("INVALID_ARGUMENT", status.ToString());
+ status = util::Status(util::error::INVALID_ARGUMENT, "msg");
+ EXPECT_FALSE(status.ok());
+ EXPECT_EQ("msg", status.error_message().ToString());
+ EXPECT_EQ("INVALID_ARGUMENT:msg", status.ToString());
+ status = util::Status(util::error::OK, "msg");
+ EXPECT_TRUE(status.ok());
+ EXPECT_EQ("", status.error_message().ToString());
+ EXPECT_EQ("OK", status.ToString());
+}
+
+TEST(Status, Copy) {
+ util::Status a(util::error::UNKNOWN, "message");
+ util::Status b(a);
+ ASSERT_EQ(a.ToString(), b.ToString());
+}
+
+TEST(Status, Assign) {
+ util::Status a(util::error::UNKNOWN, "message");
+ util::Status b;
+ b = a;
+ ASSERT_EQ(a.ToString(), b.ToString());
+}
+
+TEST(Status, AssignEmpty) {
+ util::Status a(util::error::UNKNOWN, "message");
+ util::Status b;
+ a = b;
+ ASSERT_EQ(string("OK"), a.ToString());
+ ASSERT_TRUE(b.ok());
+ ASSERT_TRUE(a.ok());
+}
+
+TEST(Status, EqualsOK) {
+ ASSERT_EQ(util::Status::OK, util::Status());
+}
+
+TEST(Status, EqualsSame) {
+ const util::Status a = util::Status(util::error::CANCELLED, "message");
+ const util::Status b = util::Status(util::error::CANCELLED, "message");
+ ASSERT_EQ(a, b);
+}
+
+TEST(Status, EqualsCopy) {
+ const util::Status a = util::Status(util::error::CANCELLED, "message");
+ const util::Status b = a;
+ ASSERT_EQ(a, b);
+}
+
+TEST(Status, EqualsDifferentCode) {
+ const util::Status a = util::Status(util::error::CANCELLED, "message");
+ const util::Status b = util::Status(util::error::UNKNOWN, "message");
+ ASSERT_NE(a, b);
+}
+
+TEST(Status, EqualsDifferentMessage) {
+ const util::Status a = util::Status(util::error::CANCELLED, "message");
+ const util::Status b = util::Status(util::error::CANCELLED, "another");
+ ASSERT_NE(a, b);
+}
+} // namespace
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/stubs/statusor.cc b/src/google/protobuf/stubs/statusor.cc
new file mode 100644
index 00000000..48d1402a
--- /dev/null
+++ b/src/google/protobuf/stubs/statusor.cc
@@ -0,0 +1,46 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <google/protobuf/stubs/statusor.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace internal {
+
+void StatusOrHelper::Crash(const Status& status) {
+ GOOGLE_LOG(FATAL) << "Attempting to fetch value instead of handling error "
+ << status.ToString();
+}
+
+} // namespace internal
+} // namespace util
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/stubs/statusor.h b/src/google/protobuf/stubs/statusor.h
new file mode 100644
index 00000000..a9d2b374
--- /dev/null
+++ b/src/google/protobuf/stubs/statusor.h
@@ -0,0 +1,259 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// StatusOr<T> is the union of a Status object and a T
+// object. StatusOr models the concept of an object that is either a
+// usable value, or an error Status explaining why such a value is
+// not present. To this end, StatusOr<T> does not allow its Status
+// value to be Status::OK. Further, StatusOr<T*> does not allow the
+// contained pointer to be NULL.
+//
+// The primary use-case for StatusOr<T> is as the return value of a
+// function which may fail.
+//
+// Example client usage for a StatusOr<T>, where T is not a pointer:
+//
+// StatusOr<float> result = DoBigCalculationThatCouldFail();
+// if (result.ok()) {
+// float answer = result.ValueOrDie();
+// printf("Big calculation yielded: %f", answer);
+// } else {
+// LOG(ERROR) << result.status();
+// }
+//
+// Example client usage for a StatusOr<T*>:
+//
+// StatusOr<Foo*> result = FooFactory::MakeNewFoo(arg);
+// if (result.ok()) {
+// std::unique_ptr<Foo> foo(result.ValueOrDie());
+// foo->DoSomethingCool();
+// } else {
+// LOG(ERROR) << result.status();
+// }
+//
+// Example client usage for a StatusOr<std::unique_ptr<T>>:
+//
+// StatusOr<std::unique_ptr<Foo>> result = FooFactory::MakeNewFoo(arg);
+// if (result.ok()) {
+// std::unique_ptr<Foo> foo = result.ConsumeValueOrDie();
+// foo->DoSomethingCool();
+// } else {
+// LOG(ERROR) << result.status();
+// }
+//
+// Example factory implementation returning StatusOr<T*>:
+//
+// StatusOr<Foo*> FooFactory::MakeNewFoo(int arg) {
+// if (arg <= 0) {
+// return ::util::Status(::util::error::INVALID_ARGUMENT,
+// "Arg must be positive");
+// } else {
+// return new Foo(arg);
+// }
+// }
+//
+
+#ifndef GOOGLE_PROTOBUF_STUBS_STATUSOR_H_
+#define GOOGLE_PROTOBUF_STUBS_STATUSOR_H_
+
+#include <new>
+#include <string>
+#include <utility>
+
+#include <google/protobuf/stubs/status.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+
+template<typename T>
+class StatusOr {
+ template<typename U> friend class StatusOr;
+
+ public:
+ // Construct a new StatusOr with Status::UNKNOWN status
+ StatusOr();
+
+ // Construct a new StatusOr with the given non-ok status. After calling
+ // this constructor, calls to ValueOrDie() will CHECK-fail.
+ //
+ // NOTE: Not explicit - we want to use StatusOr<T> as a return
+ // value, so it is convenient and sensible to be able to do 'return
+ // Status()' when the return type is StatusOr<T>.
+ //
+ // REQUIRES: status != Status::OK. This requirement is DCHECKed.
+ // In optimized builds, passing Status::OK here will have the effect
+ // of passing PosixErrorSpace::EINVAL as a fallback.
+ StatusOr(const Status& status); // NOLINT
+
+ // Construct a new StatusOr with the given value. If T is a plain pointer,
+ // value must not be NULL. After calling this constructor, calls to
+ // ValueOrDie() will succeed, and calls to status() will return OK.
+ //
+ // NOTE: Not explicit - we want to use StatusOr<T> as a return type
+ // so it is convenient and sensible to be able to do 'return T()'
+ // when when the return type is StatusOr<T>.
+ //
+ // REQUIRES: if T is a plain pointer, value != NULL. This requirement is
+ // DCHECKed. In optimized builds, passing a NULL pointer here will have
+ // the effect of passing PosixErrorSpace::EINVAL as a fallback.
+ StatusOr(const T& value); // NOLINT
+
+ // Copy constructor.
+ StatusOr(const StatusOr& other);
+
+ // Conversion copy constructor, T must be copy constructible from U
+ template<typename U>
+ StatusOr(const StatusOr<U>& other);
+
+ // Assignment operator.
+ StatusOr& operator=(const StatusOr& other);
+
+ // Conversion assignment operator, T must be assignable from U
+ template<typename U>
+ StatusOr& operator=(const StatusOr<U>& other);
+
+ // Returns a reference to our status. If this contains a T, then
+ // returns Status::OK.
+ const Status& status() const;
+
+ // Returns this->status().ok()
+ bool ok() const;
+
+ // Returns a reference to our current value, or CHECK-fails if !this->ok().
+ // If you need to initialize a T object from the stored value,
+ // ConsumeValueOrDie() may be more efficient.
+ const T& ValueOrDie() const;
+
+ private:
+ Status status_;
+ T value_;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// Implementation details for StatusOr<T>
+
+namespace internal {
+
+class LIBPROTOBUF_EXPORT StatusOrHelper {
+ public:
+ // Move type-agnostic error handling to the .cc.
+ static void Crash(const util::Status& status);
+
+ // Customized behavior for StatusOr<T> vs. StatusOr<T*>
+ template<typename T>
+ struct Specialize;
+};
+
+template<typename T>
+struct StatusOrHelper::Specialize {
+ // For non-pointer T, a reference can never be NULL.
+ static inline bool IsValueNull(const T& t) { return false; }
+};
+
+template<typename T>
+struct StatusOrHelper::Specialize<T*> {
+ static inline bool IsValueNull(const T* t) { return t == NULL; }
+};
+
+} // namespace internal
+
+template<typename T>
+inline StatusOr<T>::StatusOr()
+ : status_(util::Status::UNKNOWN) {
+}
+
+template<typename T>
+inline StatusOr<T>::StatusOr(const Status& status) {
+ if (status.ok()) {
+ status_ = Status(error::INTERNAL, "Status::OK is not a valid argument.");
+ } else {
+ status_ = status;
+ }
+}
+
+template<typename T>
+inline StatusOr<T>::StatusOr(const T& value) {
+ if (internal::StatusOrHelper::Specialize<T>::IsValueNull(value)) {
+ status_ = Status(error::INTERNAL, "NULL is not a vaild argument.");
+ } else {
+ status_ = Status::OK;
+ value_ = value;
+ }
+}
+
+template<typename T>
+inline StatusOr<T>::StatusOr(const StatusOr<T>& other)
+ : status_(other.status_), value_(other.value_) {
+}
+
+template<typename T>
+inline StatusOr<T>& StatusOr<T>::operator=(const StatusOr<T>& other) {
+ status_ = other.status_;
+ value_ = other.value_;
+ return *this;
+}
+
+template<typename T>
+template<typename U>
+inline StatusOr<T>::StatusOr(const StatusOr<U>& other)
+ : status_(other.status_), value_(other.value_) {
+}
+
+template<typename T>
+template<typename U>
+inline StatusOr<T>& StatusOr<T>::operator=(const StatusOr<U>& other) {
+ status_ = other.status_;
+ value_ = other.value_;
+ return *this;
+}
+
+template<typename T>
+inline const Status& StatusOr<T>::status() const {
+ return status_;
+}
+
+template<typename T>
+inline bool StatusOr<T>::ok() const {
+ return status().ok();
+}
+
+template<typename T>
+inline const T& StatusOr<T>::ValueOrDie() const {
+ if (!status_.ok()) {
+ internal::StatusOrHelper::Crash(status_);
+ }
+ return value_;
+}
+} // namespace util
+} // namespace protobuf
+} // namespace google
+
+#endif // GOOGLE_PROTOBUF_STUBS_STATUSOR_H_
diff --git a/src/google/protobuf/stubs/statusor_test.cc b/src/google/protobuf/stubs/statusor_test.cc
new file mode 100644
index 00000000..6e2a9e55
--- /dev/null
+++ b/src/google/protobuf/stubs/statusor_test.cc
@@ -0,0 +1,274 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <google/protobuf/stubs/statusor.h>
+
+#include <errno.h>
+#include <memory>
+
+#include <google/protobuf/testing/googletest.h>
+#include <gtest/gtest.h>
+
+namespace google {
+namespace protobuf {
+namespace util {
+namespace {
+
+class Base1 {
+ public:
+ virtual ~Base1() {}
+ int pad;
+};
+
+class Base2 {
+ public:
+ virtual ~Base2() {}
+ int yetotherpad;
+};
+
+class Derived : public Base1, public Base2 {
+ public:
+ virtual ~Derived() {}
+ int evenmorepad;
+};
+
+class CopyNoAssign {
+ public:
+ explicit CopyNoAssign(int value) : foo(value) {}
+ CopyNoAssign(const CopyNoAssign& other) : foo(other.foo) {}
+ int foo;
+ private:
+ const CopyNoAssign& operator=(const CopyNoAssign&);
+};
+
+TEST(StatusOr, TestDefaultCtor) {
+ StatusOr<int> thing;
+ EXPECT_FALSE(thing.ok());
+ EXPECT_EQ(Status::UNKNOWN, thing.status());
+}
+
+TEST(StatusOr, TestStatusCtor) {
+ StatusOr<int> thing(Status::CANCELLED);
+ EXPECT_FALSE(thing.ok());
+ EXPECT_EQ(Status::CANCELLED, thing.status());
+}
+
+TEST(StatusOr, TestValueCtor) {
+ const int kI = 4;
+ StatusOr<int> thing(kI);
+ EXPECT_TRUE(thing.ok());
+ EXPECT_EQ(kI, thing.ValueOrDie());
+}
+
+TEST(StatusOr, TestCopyCtorStatusOk) {
+ const int kI = 4;
+ StatusOr<int> original(kI);
+ StatusOr<int> copy(original);
+ EXPECT_EQ(original.status(), copy.status());
+ EXPECT_EQ(original.ValueOrDie(), copy.ValueOrDie());
+}
+
+TEST(StatusOr, TestCopyCtorStatusNotOk) {
+ StatusOr<int> original(Status::CANCELLED);
+ StatusOr<int> copy(original);
+ EXPECT_EQ(original.status(), copy.status());
+}
+
+TEST(StatusOr, TestCopyCtorStatusOKConverting) {
+ const int kI = 4;
+ StatusOr<int> original(kI);
+ StatusOr<double> copy(original);
+ EXPECT_EQ(original.status(), copy.status());
+ EXPECT_EQ(original.ValueOrDie(), copy.ValueOrDie());
+}
+
+TEST(StatusOr, TestCopyCtorStatusNotOkConverting) {
+ StatusOr<int> original(Status::CANCELLED);
+ StatusOr<double> copy(original);
+ EXPECT_EQ(original.status(), copy.status());
+}
+
+TEST(StatusOr, TestAssignmentStatusOk) {
+ const int kI = 4;
+ StatusOr<int> source(kI);
+ StatusOr<int> target;
+ target = source;
+ EXPECT_EQ(source.status(), target.status());
+ EXPECT_EQ(source.ValueOrDie(), target.ValueOrDie());
+}
+
+TEST(StatusOr, TestAssignmentStatusNotOk) {
+ StatusOr<int> source(Status::CANCELLED);
+ StatusOr<int> target;
+ target = source;
+ EXPECT_EQ(source.status(), target.status());
+}
+
+TEST(StatusOr, TestAssignmentStatusOKConverting) {
+ const int kI = 4;
+ StatusOr<int> source(kI);
+ StatusOr<double> target;
+ target = source;
+ EXPECT_EQ(source.status(), target.status());
+ EXPECT_DOUBLE_EQ(source.ValueOrDie(), target.ValueOrDie());
+}
+
+TEST(StatusOr, TestAssignmentStatusNotOkConverting) {
+ StatusOr<int> source(Status::CANCELLED);
+ StatusOr<double> target;
+ target = source;
+ EXPECT_EQ(source.status(), target.status());
+}
+
+TEST(StatusOr, TestStatus) {
+ StatusOr<int> good(4);
+ EXPECT_TRUE(good.ok());
+ StatusOr<int> bad(Status::CANCELLED);
+ EXPECT_FALSE(bad.ok());
+ EXPECT_EQ(Status::CANCELLED, bad.status());
+}
+
+TEST(StatusOr, TestValue) {
+ const int kI = 4;
+ StatusOr<int> thing(kI);
+ EXPECT_EQ(kI, thing.ValueOrDie());
+}
+
+TEST(StatusOr, TestValueConst) {
+ const int kI = 4;
+ const StatusOr<int> thing(kI);
+ EXPECT_EQ(kI, thing.ValueOrDie());
+}
+
+TEST(StatusOr, TestPointerDefaultCtor) {
+ StatusOr<int*> thing;
+ EXPECT_FALSE(thing.ok());
+ EXPECT_EQ(Status::UNKNOWN, thing.status());
+}
+
+TEST(StatusOr, TestPointerStatusCtor) {
+ StatusOr<int*> thing(Status::CANCELLED);
+ EXPECT_FALSE(thing.ok());
+ EXPECT_EQ(Status::CANCELLED, thing.status());
+}
+
+TEST(StatusOr, TestPointerValueCtor) {
+ const int kI = 4;
+ StatusOr<const int*> thing(&kI);
+ EXPECT_TRUE(thing.ok());
+ EXPECT_EQ(&kI, thing.ValueOrDie());
+}
+
+TEST(StatusOr, TestPointerCopyCtorStatusOk) {
+ const int kI = 0;
+ StatusOr<const int*> original(&kI);
+ StatusOr<const int*> copy(original);
+ EXPECT_EQ(original.status(), copy.status());
+ EXPECT_EQ(original.ValueOrDie(), copy.ValueOrDie());
+}
+
+TEST(StatusOr, TestPointerCopyCtorStatusNotOk) {
+ StatusOr<int*> original(Status::CANCELLED);
+ StatusOr<int*> copy(original);
+ EXPECT_EQ(original.status(), copy.status());
+}
+
+TEST(StatusOr, TestPointerCopyCtorStatusOKConverting) {
+ Derived derived;
+ StatusOr<Derived*> original(&derived);
+ StatusOr<Base2*> copy(original);
+ EXPECT_EQ(original.status(), copy.status());
+ EXPECT_EQ(static_cast<const Base2*>(original.ValueOrDie()),
+ copy.ValueOrDie());
+}
+
+TEST(StatusOr, TestPointerCopyCtorStatusNotOkConverting) {
+ StatusOr<Derived*> original(Status::CANCELLED);
+ StatusOr<Base2*> copy(original);
+ EXPECT_EQ(original.status(), copy.status());
+}
+
+TEST(StatusOr, TestPointerAssignmentStatusOk) {
+ const int kI = 0;
+ StatusOr<const int*> source(&kI);
+ StatusOr<const int*> target;
+ target = source;
+ EXPECT_EQ(source.status(), target.status());
+ EXPECT_EQ(source.ValueOrDie(), target.ValueOrDie());
+}
+
+TEST(StatusOr, TestPointerAssignmentStatusNotOk) {
+ StatusOr<int*> source(Status::CANCELLED);
+ StatusOr<int*> target;
+ target = source;
+ EXPECT_EQ(source.status(), target.status());
+}
+
+TEST(StatusOr, TestPointerAssignmentStatusOKConverting) {
+ Derived derived;
+ StatusOr<Derived*> source(&derived);
+ StatusOr<Base2*> target;
+ target = source;
+ EXPECT_EQ(source.status(), target.status());
+ EXPECT_EQ(static_cast<const Base2*>(source.ValueOrDie()),
+ target.ValueOrDie());
+}
+
+TEST(StatusOr, TestPointerAssignmentStatusNotOkConverting) {
+ StatusOr<Derived*> source(Status::CANCELLED);
+ StatusOr<Base2*> target;
+ target = source;
+ EXPECT_EQ(source.status(), target.status());
+}
+
+TEST(StatusOr, TestPointerStatus) {
+ const int kI = 0;
+ StatusOr<const int*> good(&kI);
+ EXPECT_TRUE(good.ok());
+ StatusOr<const int*> bad(Status::CANCELLED);
+ EXPECT_EQ(Status::CANCELLED, bad.status());
+}
+
+TEST(StatusOr, TestPointerValue) {
+ const int kI = 0;
+ StatusOr<const int*> thing(&kI);
+ EXPECT_EQ(&kI, thing.ValueOrDie());
+}
+
+TEST(StatusOr, TestPointerValueConst) {
+ const int kI = 0;
+ const StatusOr<const int*> thing(&kI);
+ EXPECT_EQ(&kI, thing.ValueOrDie());
+}
+
+} // namespace
+} // namespace util
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/stubs/stringpiece.cc b/src/google/protobuf/stubs/stringpiece.cc
new file mode 100644
index 00000000..989474b7
--- /dev/null
+++ b/src/google/protobuf/stubs/stringpiece.cc
@@ -0,0 +1,268 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#include <google/protobuf/stubs/stringpiece.h>
+
+#include <string.h>
+#include <algorithm>
+#include <climits>
+#include <string>
+#include <ostream>
+
+namespace google {
+namespace protobuf {
+std::ostream& operator<<(std::ostream& o, StringPiece piece) {
+ o.write(piece.data(), piece.size());
+ return o;
+}
+
+// Out-of-line error path.
+void StringPiece::LogFatalSizeTooBig(size_t size, const char* details) {
+ GOOGLE_LOG(FATAL) << "size too big: " << size << " details: " << details;
+}
+
+StringPiece::StringPiece(StringPiece x, stringpiece_ssize_type pos)
+ : ptr_(x.ptr_ + pos), length_(x.length_ - pos) {
+ GOOGLE_DCHECK_LE(0, pos);
+ GOOGLE_DCHECK_LE(pos, x.length_);
+}
+
+StringPiece::StringPiece(StringPiece x,
+ stringpiece_ssize_type pos,
+ stringpiece_ssize_type len)
+ : ptr_(x.ptr_ + pos), length_(std::min(len, x.length_ - pos)) {
+ GOOGLE_DCHECK_LE(0, pos);
+ GOOGLE_DCHECK_LE(pos, x.length_);
+ GOOGLE_DCHECK_GE(len, 0);
+}
+
+void StringPiece::CopyToString(string* target) const {
+ target->assign(ptr_, length_);
+}
+
+void StringPiece::AppendToString(string* target) const {
+ target->append(ptr_, length_);
+}
+
+bool StringPiece::Consume(StringPiece x) {
+ if (starts_with(x)) {
+ ptr_ += x.length_;
+ length_ -= x.length_;
+ return true;
+ }
+ return false;
+}
+
+bool StringPiece::ConsumeFromEnd(StringPiece x) {
+ if (ends_with(x)) {
+ length_ -= x.length_;
+ return true;
+ }
+ return false;
+}
+
+stringpiece_ssize_type StringPiece::copy(char* buf,
+ size_type n,
+ size_type pos) const {
+ stringpiece_ssize_type ret = std::min(length_ - pos, n);
+ memcpy(buf, ptr_ + pos, ret);
+ return ret;
+}
+
+bool StringPiece::contains(StringPiece s) const {
+ return find(s, 0) != npos;
+}
+
+stringpiece_ssize_type StringPiece::find(StringPiece s, size_type pos) const {
+ if (length_ <= 0 || pos > static_cast<size_type>(length_)) {
+ if (length_ == 0 && pos == 0 && s.length_ == 0) return 0;
+ return npos;
+ }
+ const char *result = std::search(ptr_ + pos, ptr_ + length_,
+ s.ptr_, s.ptr_ + s.length_);
+ return result == ptr_ + length_ ? npos : result - ptr_;
+}
+
+stringpiece_ssize_type StringPiece::find(char c, size_type pos) const {
+ if (length_ <= 0 || pos >= static_cast<size_type>(length_)) {
+ return npos;
+ }
+ const char* result = static_cast<const char*>(
+ memchr(ptr_ + pos, c, length_ - pos));
+ return result != NULL ? result - ptr_ : npos;
+}
+
+stringpiece_ssize_type StringPiece::rfind(StringPiece s, size_type pos) const {
+ if (length_ < s.length_) return npos;
+ const size_t ulen = length_;
+ if (s.length_ == 0) return std::min(ulen, pos);
+
+ const char* last = ptr_ + std::min(ulen - s.length_, pos) + s.length_;
+ const char* result = std::find_end(ptr_, last, s.ptr_, s.ptr_ + s.length_);
+ return result != last ? result - ptr_ : npos;
+}
+
+// Search range is [0..pos] inclusive. If pos == npos, search everything.
+stringpiece_ssize_type StringPiece::rfind(char c, size_type pos) const {
+ // Note: memrchr() is not available on Windows.
+ if (length_ <= 0) return npos;
+ for (stringpiece_ssize_type i =
+ std::min(pos, static_cast<size_type>(length_ - 1));
+ i >= 0; --i) {
+ if (ptr_[i] == c) {
+ return i;
+ }
+ }
+ return npos;
+}
+
+// For each character in characters_wanted, sets the index corresponding
+// to the ASCII code of that character to 1 in table. This is used by
+// the find_.*_of methods below to tell whether or not a character is in
+// the lookup table in constant time.
+// The argument `table' must be an array that is large enough to hold all
+// the possible values of an unsigned char. Thus it should be be declared
+// as follows:
+// bool table[UCHAR_MAX + 1]
+static inline void BuildLookupTable(StringPiece characters_wanted,
+ bool* table) {
+ const stringpiece_ssize_type length = characters_wanted.length();
+ const char* const data = characters_wanted.data();
+ for (stringpiece_ssize_type i = 0; i < length; ++i) {
+ table[static_cast<unsigned char>(data[i])] = true;
+ }
+}
+
+stringpiece_ssize_type StringPiece::find_first_of(StringPiece s,
+ size_type pos) const {
+ if (length_ <= 0 || s.length_ <= 0) {
+ return npos;
+ }
+ // Avoid the cost of BuildLookupTable() for a single-character search.
+ if (s.length_ == 1) return find_first_of(s.ptr_[0], pos);
+
+ bool lookup[UCHAR_MAX + 1] = { false };
+ BuildLookupTable(s, lookup);
+ for (stringpiece_ssize_type i = pos; i < length_; ++i) {
+ if (lookup[static_cast<unsigned char>(ptr_[i])]) {
+ return i;
+ }
+ }
+ return npos;
+}
+
+stringpiece_ssize_type StringPiece::find_first_not_of(StringPiece s,
+ size_type pos) const {
+ if (length_ <= 0) return npos;
+ if (s.length_ <= 0) return 0;
+ // Avoid the cost of BuildLookupTable() for a single-character search.
+ if (s.length_ == 1) return find_first_not_of(s.ptr_[0], pos);
+
+ bool lookup[UCHAR_MAX + 1] = { false };
+ BuildLookupTable(s, lookup);
+ for (stringpiece_ssize_type i = pos; i < length_; ++i) {
+ if (!lookup[static_cast<unsigned char>(ptr_[i])]) {
+ return i;
+ }
+ }
+ return npos;
+}
+
+stringpiece_ssize_type StringPiece::find_first_not_of(char c,
+ size_type pos) const {
+ if (length_ <= 0) return npos;
+
+ for (; pos < static_cast<size_type>(length_); ++pos) {
+ if (ptr_[pos] != c) {
+ return pos;
+ }
+ }
+ return npos;
+}
+
+stringpiece_ssize_type StringPiece::find_last_of(StringPiece s,
+ size_type pos) const {
+ if (length_ <= 0 || s.length_ <= 0) return npos;
+ // Avoid the cost of BuildLookupTable() for a single-character search.
+ if (s.length_ == 1) return find_last_of(s.ptr_[0], pos);
+
+ bool lookup[UCHAR_MAX + 1] = { false };
+ BuildLookupTable(s, lookup);
+ for (stringpiece_ssize_type i =
+ std::min(pos, static_cast<size_type>(length_ - 1)); i >= 0; --i) {
+ if (lookup[static_cast<unsigned char>(ptr_[i])]) {
+ return i;
+ }
+ }
+ return npos;
+}
+
+stringpiece_ssize_type StringPiece::find_last_not_of(StringPiece s,
+ size_type pos) const {
+ if (length_ <= 0) return npos;
+
+ stringpiece_ssize_type i = std::min(pos, static_cast<size_type>(length_ - 1));
+ if (s.length_ <= 0) return i;
+
+ // Avoid the cost of BuildLookupTable() for a single-character search.
+ if (s.length_ == 1) return find_last_not_of(s.ptr_[0], pos);
+
+ bool lookup[UCHAR_MAX + 1] = { false };
+ BuildLookupTable(s, lookup);
+ for (; i >= 0; --i) {
+ if (!lookup[static_cast<unsigned char>(ptr_[i])]) {
+ return i;
+ }
+ }
+ return npos;
+}
+
+stringpiece_ssize_type StringPiece::find_last_not_of(char c,
+ size_type pos) const {
+ if (length_ <= 0) return npos;
+
+ for (stringpiece_ssize_type i =
+ std::min(pos, static_cast<size_type>(length_ - 1)); i >= 0; --i) {
+ if (ptr_[i] != c) {
+ return i;
+ }
+ }
+ return npos;
+}
+
+StringPiece StringPiece::substr(size_type pos, size_type n) const {
+ if (pos > length_) pos = length_;
+ if (n > length_ - pos) n = length_ - pos;
+ return StringPiece(ptr_ + pos, n);
+}
+
+const StringPiece::size_type StringPiece::npos = size_type(-1);
+
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/stubs/stringpiece.h b/src/google/protobuf/stubs/stringpiece.h
new file mode 100644
index 00000000..353a60d3
--- /dev/null
+++ b/src/google/protobuf/stubs/stringpiece.h
@@ -0,0 +1,440 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// A StringPiece points to part or all of a string, Cord, double-quoted string
+// literal, or other string-like object. A StringPiece does *not* own the
+// string to which it points. A StringPiece is not null-terminated.
+//
+// You can use StringPiece as a function or method parameter. A StringPiece
+// parameter can receive a double-quoted string literal argument, a "const
+// char*" argument, a string argument, or a StringPiece argument with no data
+// copying. Systematic use of StringPiece for arguments reduces data
+// copies and strlen() calls.
+//
+// Prefer passing StringPieces by value:
+// void MyFunction(StringPiece arg);
+// If circumstances require, you may also pass by const reference:
+// void MyFunction(const StringPiece& arg); // not preferred
+// Both of these have the same lifetime semantics. Passing by value
+// generates slightly smaller code. For more discussion, see the thread
+// go/stringpiecebyvalue on c-users.
+//
+// StringPiece is also suitable for local variables if you know that
+// the lifetime of the underlying object is longer than the lifetime
+// of your StringPiece variable.
+//
+// Beware of binding a StringPiece to a temporary:
+// StringPiece sp = obj.MethodReturningString(); // BAD: lifetime problem
+//
+// This code is okay:
+// string str = obj.MethodReturningString(); // str owns its contents
+// StringPiece sp(str); // GOOD, because str outlives sp
+//
+// StringPiece is sometimes a poor choice for a return value and usually a poor
+// choice for a data member. If you do use a StringPiece this way, it is your
+// responsibility to ensure that the object pointed to by the StringPiece
+// outlives the StringPiece.
+//
+// A StringPiece may represent just part of a string; thus the name "Piece".
+// For example, when splitting a string, vector<StringPiece> is a natural data
+// type for the output. For another example, a Cord is a non-contiguous,
+// potentially very long string-like object. The Cord class has an interface
+// that iteratively provides StringPiece objects that point to the
+// successive pieces of a Cord object.
+//
+// A StringPiece is not null-terminated. If you write code that scans a
+// StringPiece, you must check its length before reading any characters.
+// Common idioms that work on null-terminated strings do not work on
+// StringPiece objects.
+//
+// There are several ways to create a null StringPiece:
+// StringPiece()
+// StringPiece(NULL)
+// StringPiece(NULL, 0)
+// For all of the above, sp.data() == NULL, sp.length() == 0,
+// and sp.empty() == true. Also, if you create a StringPiece with
+// a non-NULL pointer then sp.data() != NULL. Once created,
+// sp.data() will stay either NULL or not-NULL, except if you call
+// sp.clear() or sp.set().
+//
+// Thus, you can use StringPiece(NULL) to signal an out-of-band value
+// that is different from other StringPiece values. This is similar
+// to the way that const char* p1 = NULL; is different from
+// const char* p2 = "";.
+//
+// There are many ways to create an empty StringPiece:
+// StringPiece()
+// StringPiece(NULL)
+// StringPiece(NULL, 0)
+// StringPiece("")
+// StringPiece("", 0)
+// StringPiece("abcdef", 0)
+// StringPiece("abcdef"+6, 0)
+// For all of the above, sp.length() will be 0 and sp.empty() will be true.
+// For some empty StringPiece values, sp.data() will be NULL.
+// For some empty StringPiece values, sp.data() will not be NULL.
+//
+// Be careful not to confuse: null StringPiece and empty StringPiece.
+// The set of empty StringPieces properly includes the set of null StringPieces.
+// That is, every null StringPiece is an empty StringPiece,
+// but some non-null StringPieces are empty Stringpieces too.
+//
+// All empty StringPiece values compare equal to each other.
+// Even a null StringPieces compares equal to a non-null empty StringPiece:
+// StringPiece() == StringPiece("", 0)
+// StringPiece(NULL) == StringPiece("abc", 0)
+// StringPiece(NULL, 0) == StringPiece("abcdef"+6, 0)
+//
+// Look carefully at this example:
+// StringPiece("") == NULL
+// True or false? TRUE, because StringPiece::operator== converts
+// the right-hand side from NULL to StringPiece(NULL),
+// and then compares two zero-length spans of characters.
+// However, we are working to make this example produce a compile error.
+//
+// Suppose you want to write:
+// bool TestWhat?(StringPiece sp) { return sp == NULL; } // BAD
+// Do not do that. Write one of these instead:
+// bool TestNull(StringPiece sp) { return sp.data() == NULL; }
+// bool TestEmpty(StringPiece sp) { return sp.empty(); }
+// The intent of TestWhat? is unclear. Did you mean TestNull or TestEmpty?
+// Right now, TestWhat? behaves likes TestEmpty.
+// We are working to make TestWhat? produce a compile error.
+// TestNull is good to test for an out-of-band signal.
+// TestEmpty is good to test for an empty StringPiece.
+//
+// Caveats (again):
+// (1) The lifetime of the pointed-to string (or piece of a string)
+// must be longer than the lifetime of the StringPiece.
+// (2) There may or may not be a '\0' character after the end of
+// StringPiece data.
+// (3) A null StringPiece is empty.
+// An empty StringPiece may or may not be a null StringPiece.
+
+#ifndef GOOGLE_PROTOBUF_STUBS_STRINGPIECE_H_
+#define GOOGLE_PROTOBUF_STUBS_STRINGPIECE_H_
+
+#include <assert.h>
+#include <stddef.h>
+#include <string.h>
+#include <iosfwd>
+#include <limits>
+#include <string>
+
+#include <google/protobuf/stubs/common.h>
+
+namespace google {
+namespace protobuf {
+// StringPiece has *two* size types.
+// StringPiece::size_type
+// is unsigned
+// is 32 bits in LP32, 64 bits in LP64, 64 bits in LLP64
+// no future changes intended
+// stringpiece_ssize_type
+// is signed
+// is 32 bits in LP32, 64 bits in LP64, 64 bits in LLP64
+// future changes intended: http://go/64BitStringPiece
+//
+typedef string::difference_type stringpiece_ssize_type;
+
+// STRINGPIECE_CHECK_SIZE protects us from 32-bit overflows.
+// TODO(mec): delete this after stringpiece_ssize_type goes 64 bit.
+#if !defined(NDEBUG)
+#define STRINGPIECE_CHECK_SIZE 1
+#elif defined(_FORTIFY_SOURCE) && _FORTIFY_SOURCE > 0
+#define STRINGPIECE_CHECK_SIZE 1
+#else
+#define STRINGPIECE_CHECK_SIZE 0
+#endif
+
+class LIBPROTOBUF_EXPORT StringPiece {
+ private:
+ const char* ptr_;
+ stringpiece_ssize_type length_;
+
+ // Prevent overflow in debug mode or fortified mode.
+ // sizeof(stringpiece_ssize_type) may be smaller than sizeof(size_t).
+ static stringpiece_ssize_type CheckedSsizeTFromSizeT(size_t size) {
+#if STRINGPIECE_CHECK_SIZE > 0
+#ifdef max
+#undef max
+#endif
+ if (size > static_cast<size_t>(
+ std::numeric_limits<stringpiece_ssize_type>::max())) {
+ // Some people grep for this message in logs
+ // so take care if you ever change it.
+ LogFatalSizeTooBig(size, "size_t to int conversion");
+ }
+#endif
+ return static_cast<stringpiece_ssize_type>(size);
+ }
+
+ // Out-of-line error path.
+ static void LogFatalSizeTooBig(size_t size, const char* details);
+
+ public:
+ // We provide non-explicit singleton constructors so users can pass
+ // in a "const char*" or a "string" wherever a "StringPiece" is
+ // expected.
+ //
+ // Style guide exception granted:
+ // http://goto/style-guide-exception-20978288
+ StringPiece() : ptr_(NULL), length_(0) {}
+
+ StringPiece(const char* str) // NOLINT(runtime/explicit)
+ : ptr_(str), length_(0) {
+ if (str != NULL) {
+ length_ = CheckedSsizeTFromSizeT(strlen(str));
+ }
+ }
+
+ template <class Allocator>
+ StringPiece( // NOLINT(runtime/explicit)
+ const std::basic_string<char, std::char_traits<char>, Allocator>& str)
+ : ptr_(str.data()), length_(0) {
+ length_ = CheckedSsizeTFromSizeT(str.size());
+ }
+#if defined(HAS_GLOBAL_STRING)
+ template <class Allocator>
+ StringPiece( // NOLINT(runtime/explicit)
+ const basic_string<char, std::char_traits<char>, Allocator>& str)
+ : ptr_(str.data()), length_(0) {
+ length_ = CheckedSsizeTFromSizeT(str.size());
+ }
+#endif
+
+ StringPiece(const char* offset, stringpiece_ssize_type len)
+ : ptr_(offset), length_(len) {
+ assert(len >= 0);
+ }
+
+ // Substring of another StringPiece.
+ // pos must be non-negative and <= x.length().
+ StringPiece(StringPiece x, stringpiece_ssize_type pos);
+ // Substring of another StringPiece.
+ // pos must be non-negative and <= x.length().
+ // len must be non-negative and will be pinned to at most x.length() - pos.
+ StringPiece(StringPiece x,
+ stringpiece_ssize_type pos,
+ stringpiece_ssize_type len);
+
+ // data() may return a pointer to a buffer with embedded NULs, and the
+ // returned buffer may or may not be null terminated. Therefore it is
+ // typically a mistake to pass data() to a routine that expects a NUL
+ // terminated string.
+ const char* data() const { return ptr_; }
+ stringpiece_ssize_type size() const { return length_; }
+ stringpiece_ssize_type length() const { return length_; }
+ bool empty() const { return length_ == 0; }
+
+ void clear() {
+ ptr_ = NULL;
+ length_ = 0;
+ }
+
+ void set(const char* data, stringpiece_ssize_type len) {
+ assert(len >= 0);
+ ptr_ = data;
+ length_ = len;
+ }
+
+ void set(const char* str) {
+ ptr_ = str;
+ if (str != NULL)
+ length_ = CheckedSsizeTFromSizeT(strlen(str));
+ else
+ length_ = 0;
+ }
+
+ void set(const void* data, stringpiece_ssize_type len) {
+ ptr_ = reinterpret_cast<const char*>(data);
+ length_ = len;
+ }
+
+ char operator[](stringpiece_ssize_type i) const {
+ assert(0 <= i);
+ assert(i < length_);
+ return ptr_[i];
+ }
+
+ void remove_prefix(stringpiece_ssize_type n) {
+ assert(length_ >= n);
+ ptr_ += n;
+ length_ -= n;
+ }
+
+ void remove_suffix(stringpiece_ssize_type n) {
+ assert(length_ >= n);
+ length_ -= n;
+ }
+
+ // returns {-1, 0, 1}
+ int compare(StringPiece x) const {
+ const stringpiece_ssize_type min_size =
+ length_ < x.length_ ? length_ : x.length_;
+ int r = memcmp(ptr_, x.ptr_, min_size);
+ if (r < 0) return -1;
+ if (r > 0) return 1;
+ if (length_ < x.length_) return -1;
+ if (length_ > x.length_) return 1;
+ return 0;
+ }
+
+ string as_string() const {
+ return ToString();
+ }
+ // We also define ToString() here, since many other string-like
+ // interfaces name the routine that converts to a C++ string
+ // "ToString", and it's confusing to have the method that does that
+ // for a StringPiece be called "as_string()". We also leave the
+ // "as_string()" method defined here for existing code.
+ string ToString() const {
+ if (ptr_ == NULL) return string();
+ return string(data(), size());
+ }
+
+ operator string() const {
+ return ToString();
+ }
+
+ void CopyToString(string* target) const;
+ void AppendToString(string* target) const;
+
+ bool starts_with(StringPiece x) const {
+ return (length_ >= x.length_) && (memcmp(ptr_, x.ptr_, x.length_) == 0);
+ }
+
+ bool ends_with(StringPiece x) const {
+ return ((length_ >= x.length_) &&
+ (memcmp(ptr_ + (length_-x.length_), x.ptr_, x.length_) == 0));
+ }
+
+ // Checks whether StringPiece starts with x and if so advances the beginning
+ // of it to past the match. It's basically a shortcut for starts_with
+ // followed by remove_prefix.
+ bool Consume(StringPiece x);
+ // Like above but for the end of the string.
+ bool ConsumeFromEnd(StringPiece x);
+
+ // standard STL container boilerplate
+ typedef char value_type;
+ typedef const char* pointer;
+ typedef const char& reference;
+ typedef const char& const_reference;
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ static const size_type npos;
+ typedef const char* const_iterator;
+ typedef const char* iterator;
+ typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+ typedef std::reverse_iterator<iterator> reverse_iterator;
+ iterator begin() const { return ptr_; }
+ iterator end() const { return ptr_ + length_; }
+ const_reverse_iterator rbegin() const {
+ return const_reverse_iterator(ptr_ + length_);
+ }
+ const_reverse_iterator rend() const {
+ return const_reverse_iterator(ptr_);
+ }
+ stringpiece_ssize_type max_size() const { return length_; }
+ stringpiece_ssize_type capacity() const { return length_; }
+
+ // cpplint.py emits a false positive [build/include_what_you_use]
+ stringpiece_ssize_type copy(char* buf, size_type n, size_type pos = 0) const; // NOLINT
+
+ bool contains(StringPiece s) const;
+
+ stringpiece_ssize_type find(StringPiece s, size_type pos = 0) const;
+ stringpiece_ssize_type find(char c, size_type pos = 0) const;
+ stringpiece_ssize_type rfind(StringPiece s, size_type pos = npos) const;
+ stringpiece_ssize_type rfind(char c, size_type pos = npos) const;
+
+ stringpiece_ssize_type find_first_of(StringPiece s, size_type pos = 0) const;
+ stringpiece_ssize_type find_first_of(char c, size_type pos = 0) const {
+ return find(c, pos);
+ }
+ stringpiece_ssize_type find_first_not_of(StringPiece s,
+ size_type pos = 0) const;
+ stringpiece_ssize_type find_first_not_of(char c, size_type pos = 0) const;
+ stringpiece_ssize_type find_last_of(StringPiece s,
+ size_type pos = npos) const;
+ stringpiece_ssize_type find_last_of(char c, size_type pos = npos) const {
+ return rfind(c, pos);
+ }
+ stringpiece_ssize_type find_last_not_of(StringPiece s,
+ size_type pos = npos) const;
+ stringpiece_ssize_type find_last_not_of(char c, size_type pos = npos) const;
+
+ StringPiece substr(size_type pos, size_type n = npos) const;
+};
+
+// This large function is defined inline so that in a fairly common case where
+// one of the arguments is a literal, the compiler can elide a lot of the
+// following comparisons.
+inline bool operator==(StringPiece x, StringPiece y) {
+ stringpiece_ssize_type len = x.size();
+ if (len != y.size()) {
+ return false;
+ }
+
+ return x.data() == y.data() || len <= 0 ||
+ memcmp(x.data(), y.data(), len) == 0;
+}
+
+inline bool operator!=(StringPiece x, StringPiece y) {
+ return !(x == y);
+}
+
+inline bool operator<(StringPiece x, StringPiece y) {
+ const stringpiece_ssize_type min_size =
+ x.size() < y.size() ? x.size() : y.size();
+ const int r = memcmp(x.data(), y.data(), min_size);
+ return (r < 0) || (r == 0 && x.size() < y.size());
+}
+
+inline bool operator>(StringPiece x, StringPiece y) {
+ return y < x;
+}
+
+inline bool operator<=(StringPiece x, StringPiece y) {
+ return !(x > y);
+}
+
+inline bool operator>=(StringPiece x, StringPiece y) {
+ return !(x < y);
+}
+
+// allow StringPiece to be logged
+extern std::ostream& operator<<(std::ostream& o, StringPiece piece);
+
+} // namespace protobuf
+} // namespace google
+
+#endif // STRINGS_STRINGPIECE_H_
diff --git a/src/google/protobuf/stubs/stringpiece_unittest.cc b/src/google/protobuf/stubs/stringpiece_unittest.cc
new file mode 100644
index 00000000..9b5dae13
--- /dev/null
+++ b/src/google/protobuf/stubs/stringpiece_unittest.cc
@@ -0,0 +1,793 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#include <google/protobuf/stubs/stringpiece.h>
+
+#include <iterator>
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <google/protobuf/testing/googletest.h>
+#include <gtest/gtest.h>
+
+namespace google {
+namespace protobuf {
+namespace {
+TEST(StringPiece, Ctor) {
+ {
+ // Null.
+ StringPiece s10;
+ EXPECT_TRUE(s10.data() == NULL);
+ EXPECT_EQ(0, s10.length());
+ }
+
+ {
+ // const char* without length.
+ const char* hello = "hello";
+ StringPiece s20(hello);
+ EXPECT_TRUE(s20.data() == hello);
+ EXPECT_EQ(5, s20.length());
+
+ // const char* with length.
+ StringPiece s21(hello, 4);
+ EXPECT_TRUE(s21.data() == hello);
+ EXPECT_EQ(4, s21.length());
+
+ // Not recommended, but valid C++
+ StringPiece s22(hello, 6);
+ EXPECT_TRUE(s22.data() == hello);
+ EXPECT_EQ(6, s22.length());
+ }
+
+ {
+ // std::string.
+ std::string hola = "hola";
+ StringPiece s30(hola);
+ EXPECT_TRUE(s30.data() == hola.data());
+ EXPECT_EQ(4, s30.length());
+
+ // std::string with embedded '\0'.
+ hola.push_back('\0');
+ hola.append("h2");
+ hola.push_back('\0');
+ StringPiece s31(hola);
+ EXPECT_TRUE(s31.data() == hola.data());
+ EXPECT_EQ(8, s31.length());
+ }
+
+#if defined(HAS_GLOBAL_STRING)
+ {
+ // ::string
+ string bonjour = "bonjour";
+ StringPiece s40(bonjour);
+ EXPECT_TRUE(s40.data() == bonjour.data());
+ EXPECT_EQ(7, s40.length());
+ }
+#endif
+
+ // TODO(mec): StringPiece(StringPiece x, int pos);
+ // TODO(mec): StringPiece(StringPiece x, int pos, int len);
+ // TODO(mec): StringPiece(const StringPiece&);
+}
+
+TEST(StringPiece, STLComparator) {
+ string s1("foo");
+ string s2("bar");
+ string s3("baz");
+
+ StringPiece p1(s1);
+ StringPiece p2(s2);
+ StringPiece p3(s3);
+
+ typedef std::map<StringPiece, int> TestMap;
+ TestMap map;
+
+ map.insert(std::make_pair(p1, 0));
+ map.insert(std::make_pair(p2, 1));
+ map.insert(std::make_pair(p3, 2));
+ EXPECT_EQ(map.size(), 3);
+
+ TestMap::const_iterator iter = map.begin();
+ EXPECT_EQ(iter->second, 1);
+ ++iter;
+ EXPECT_EQ(iter->second, 2);
+ ++iter;
+ EXPECT_EQ(iter->second, 0);
+ ++iter;
+ EXPECT_TRUE(iter == map.end());
+
+ TestMap::iterator new_iter = map.find("zot");
+ EXPECT_TRUE(new_iter == map.end());
+
+ new_iter = map.find("bar");
+ EXPECT_TRUE(new_iter != map.end());
+
+ map.erase(new_iter);
+ EXPECT_EQ(map.size(), 2);
+
+ iter = map.begin();
+ EXPECT_EQ(iter->second, 2);
+ ++iter;
+ EXPECT_EQ(iter->second, 0);
+ ++iter;
+ EXPECT_TRUE(iter == map.end());
+}
+
+TEST(StringPiece, ComparisonOperators) {
+#define COMPARE(result, op, x, y) \
+ EXPECT_EQ(result, StringPiece((x)) op StringPiece((y))); \
+ EXPECT_EQ(result, StringPiece((x)).compare(StringPiece((y))) op 0)
+
+ COMPARE(true, ==, "", "");
+ COMPARE(true, ==, "", NULL);
+ COMPARE(true, ==, NULL, "");
+ COMPARE(true, ==, "a", "a");
+ COMPARE(true, ==, "aa", "aa");
+ COMPARE(false, ==, "a", "");
+ COMPARE(false, ==, "", "a");
+ COMPARE(false, ==, "a", "b");
+ COMPARE(false, ==, "a", "aa");
+ COMPARE(false, ==, "aa", "a");
+
+ COMPARE(false, !=, "", "");
+ COMPARE(false, !=, "a", "a");
+ COMPARE(false, !=, "aa", "aa");
+ COMPARE(true, !=, "a", "");
+ COMPARE(true, !=, "", "a");
+ COMPARE(true, !=, "a", "b");
+ COMPARE(true, !=, "a", "aa");
+ COMPARE(true, !=, "aa", "a");
+
+ COMPARE(true, <, "a", "b");
+ COMPARE(true, <, "a", "aa");
+ COMPARE(true, <, "aa", "b");
+ COMPARE(true, <, "aa", "bb");
+ COMPARE(false, <, "a", "a");
+ COMPARE(false, <, "b", "a");
+ COMPARE(false, <, "aa", "a");
+ COMPARE(false, <, "b", "aa");
+ COMPARE(false, <, "bb", "aa");
+
+ COMPARE(true, <=, "a", "a");
+ COMPARE(true, <=, "a", "b");
+ COMPARE(true, <=, "a", "aa");
+ COMPARE(true, <=, "aa", "b");
+ COMPARE(true, <=, "aa", "bb");
+ COMPARE(false, <=, "b", "a");
+ COMPARE(false, <=, "aa", "a");
+ COMPARE(false, <=, "b", "aa");
+ COMPARE(false, <=, "bb", "aa");
+
+ COMPARE(false, >=, "a", "b");
+ COMPARE(false, >=, "a", "aa");
+ COMPARE(false, >=, "aa", "b");
+ COMPARE(false, >=, "aa", "bb");
+ COMPARE(true, >=, "a", "a");
+ COMPARE(true, >=, "b", "a");
+ COMPARE(true, >=, "aa", "a");
+ COMPARE(true, >=, "b", "aa");
+ COMPARE(true, >=, "bb", "aa");
+
+ COMPARE(false, >, "a", "a");
+ COMPARE(false, >, "a", "b");
+ COMPARE(false, >, "a", "aa");
+ COMPARE(false, >, "aa", "b");
+ COMPARE(false, >, "aa", "bb");
+ COMPARE(true, >, "b", "a");
+ COMPARE(true, >, "aa", "a");
+ COMPARE(true, >, "b", "aa");
+ COMPARE(true, >, "bb", "aa");
+
+ string x;
+ for (int i = 0; i < 256; i++) {
+ x += 'a';
+ string y = x;
+ COMPARE(true, ==, x, y);
+ for (int j = 0; j < i; j++) {
+ string z = x;
+ z[j] = 'b'; // Differs in position 'j'
+ COMPARE(false, ==, x, z);
+ COMPARE(true, <, x, z);
+ COMPARE(true, >, z, x);
+ if (j + 1 < i) {
+ z[j + 1] = 'A'; // Differs in position 'j+1' as well
+ COMPARE(false, ==, x, z);
+ COMPARE(true, <, x, z);
+ COMPARE(true, >, z, x);
+ z[j + 1] = 'z'; // Differs in position 'j+1' as well
+ COMPARE(false, ==, x, z);
+ COMPARE(true, <, x, z);
+ COMPARE(true, >, z, x);
+ }
+ }
+ }
+
+#undef COMPARE
+}
+
+TEST(StringPiece, STL1) {
+ const StringPiece a("abcdefghijklmnopqrstuvwxyz");
+ const StringPiece b("abc");
+ const StringPiece c("xyz");
+ const StringPiece d("foobar");
+ const StringPiece e;
+ string temp("123");
+ temp += '\0';
+ temp += "456";
+ const StringPiece f(temp);
+
+ EXPECT_EQ(a[6], 'g');
+ EXPECT_EQ(b[0], 'a');
+ EXPECT_EQ(c[2], 'z');
+ EXPECT_EQ(f[3], '\0');
+ EXPECT_EQ(f[5], '5');
+
+ EXPECT_EQ(*d.data(), 'f');
+ EXPECT_EQ(d.data()[5], 'r');
+ EXPECT_TRUE(e.data() == NULL);
+
+ EXPECT_EQ(*a.begin(), 'a');
+ EXPECT_EQ(*(b.begin() + 2), 'c');
+ EXPECT_EQ(*(c.end() - 1), 'z');
+
+ EXPECT_EQ(*a.rbegin(), 'z');
+ EXPECT_EQ(*(b.rbegin() + 2), 'a');
+ EXPECT_EQ(*(c.rend() - 1), 'x');
+ EXPECT_TRUE(a.rbegin() + 26 == a.rend());
+
+ EXPECT_EQ(a.size(), 26);
+ EXPECT_EQ(b.size(), 3);
+ EXPECT_EQ(c.size(), 3);
+ EXPECT_EQ(d.size(), 6);
+ EXPECT_EQ(e.size(), 0);
+ EXPECT_EQ(f.size(), 7);
+
+ EXPECT_TRUE(!d.empty());
+ EXPECT_TRUE(d.begin() != d.end());
+ EXPECT_TRUE(d.begin() + 6 == d.end());
+
+ EXPECT_TRUE(e.empty());
+ EXPECT_TRUE(e.begin() == e.end());
+
+ EXPECT_GE(a.max_size(), a.capacity());
+ EXPECT_GE(a.capacity(), a.size());
+
+ char buf[4] = { '%', '%', '%', '%' };
+ EXPECT_EQ(a.copy(buf, 4), 4);
+ EXPECT_EQ(buf[0], a[0]);
+ EXPECT_EQ(buf[1], a[1]);
+ EXPECT_EQ(buf[2], a[2]);
+ EXPECT_EQ(buf[3], a[3]);
+ EXPECT_EQ(a.copy(buf, 3, 7), 3);
+ EXPECT_EQ(buf[0], a[7]);
+ EXPECT_EQ(buf[1], a[8]);
+ EXPECT_EQ(buf[2], a[9]);
+ EXPECT_EQ(buf[3], a[3]);
+ EXPECT_EQ(c.copy(buf, 99), 3);
+ EXPECT_EQ(buf[0], c[0]);
+ EXPECT_EQ(buf[1], c[1]);
+ EXPECT_EQ(buf[2], c[2]);
+ EXPECT_EQ(buf[3], a[3]);
+}
+
+// Separated from STL1() because some compilers produce an overly
+// large stack frame for the combined function.
+TEST(StringPiece, STL2) {
+ const StringPiece a("abcdefghijklmnopqrstuvwxyz");
+ const StringPiece b("abc");
+ const StringPiece c("xyz");
+ StringPiece d("foobar");
+ const StringPiece e;
+ const StringPiece f("123" "\0" "456", 7);
+
+ d.clear();
+ EXPECT_EQ(d.size(), 0);
+ EXPECT_TRUE(d.empty());
+ EXPECT_TRUE(d.data() == NULL);
+ EXPECT_TRUE(d.begin() == d.end());
+
+ EXPECT_EQ(StringPiece::npos, string::npos);
+
+ EXPECT_EQ(a.find(b), 0);
+ EXPECT_EQ(a.find(b, 1), StringPiece::npos);
+ EXPECT_EQ(a.find(c), 23);
+ EXPECT_EQ(a.find(c, 9), 23);
+ EXPECT_EQ(a.find(c, StringPiece::npos), StringPiece::npos);
+ EXPECT_EQ(b.find(c), StringPiece::npos);
+ EXPECT_EQ(b.find(c, StringPiece::npos), StringPiece::npos);
+ EXPECT_EQ(a.find(d), 0);
+ EXPECT_EQ(a.find(e), 0);
+ EXPECT_EQ(a.find(d, 12), 12);
+ EXPECT_EQ(a.find(e, 17), 17);
+ StringPiece g("xx not found bb");
+ EXPECT_EQ(a.find(g), StringPiece::npos);
+ // empty string nonsense
+ EXPECT_EQ(d.find(b), StringPiece::npos);
+ EXPECT_EQ(e.find(b), StringPiece::npos);
+ EXPECT_EQ(d.find(b, 4), StringPiece::npos);
+ EXPECT_EQ(e.find(b, 7), StringPiece::npos);
+
+ size_t empty_search_pos = string().find(string());
+ EXPECT_EQ(d.find(d), empty_search_pos);
+ EXPECT_EQ(d.find(e), empty_search_pos);
+ EXPECT_EQ(e.find(d), empty_search_pos);
+ EXPECT_EQ(e.find(e), empty_search_pos);
+ EXPECT_EQ(d.find(d, 4), string().find(string(), 4));
+ EXPECT_EQ(d.find(e, 4), string().find(string(), 4));
+ EXPECT_EQ(e.find(d, 4), string().find(string(), 4));
+ EXPECT_EQ(e.find(e, 4), string().find(string(), 4));
+
+ EXPECT_EQ(a.find('a'), 0);
+ EXPECT_EQ(a.find('c'), 2);
+ EXPECT_EQ(a.find('z'), 25);
+ EXPECT_EQ(a.find('$'), StringPiece::npos);
+ EXPECT_EQ(a.find('\0'), StringPiece::npos);
+ EXPECT_EQ(f.find('\0'), 3);
+ EXPECT_EQ(f.find('3'), 2);
+ EXPECT_EQ(f.find('5'), 5);
+ EXPECT_EQ(g.find('o'), 4);
+ EXPECT_EQ(g.find('o', 4), 4);
+ EXPECT_EQ(g.find('o', 5), 8);
+ EXPECT_EQ(a.find('b', 5), StringPiece::npos);
+ // empty string nonsense
+ EXPECT_EQ(d.find('\0'), StringPiece::npos);
+ EXPECT_EQ(e.find('\0'), StringPiece::npos);
+ EXPECT_EQ(d.find('\0', 4), StringPiece::npos);
+ EXPECT_EQ(e.find('\0', 7), StringPiece::npos);
+ EXPECT_EQ(d.find('x'), StringPiece::npos);
+ EXPECT_EQ(e.find('x'), StringPiece::npos);
+ EXPECT_EQ(d.find('x', 4), StringPiece::npos);
+ EXPECT_EQ(e.find('x', 7), StringPiece::npos);
+
+ EXPECT_EQ(a.rfind(b), 0);
+ EXPECT_EQ(a.rfind(b, 1), 0);
+ EXPECT_EQ(a.rfind(c), 23);
+ EXPECT_EQ(a.rfind(c, 22), StringPiece::npos);
+ EXPECT_EQ(a.rfind(c, 1), StringPiece::npos);
+ EXPECT_EQ(a.rfind(c, 0), StringPiece::npos);
+ EXPECT_EQ(b.rfind(c), StringPiece::npos);
+ EXPECT_EQ(b.rfind(c, 0), StringPiece::npos);
+ EXPECT_EQ(a.rfind(d), a.as_string().rfind(string()));
+ EXPECT_EQ(a.rfind(e), a.as_string().rfind(string()));
+ EXPECT_EQ(a.rfind(d, 12), 12);
+ EXPECT_EQ(a.rfind(e, 17), 17);
+ EXPECT_EQ(a.rfind(g), StringPiece::npos);
+ EXPECT_EQ(d.rfind(b), StringPiece::npos);
+ EXPECT_EQ(e.rfind(b), StringPiece::npos);
+ EXPECT_EQ(d.rfind(b, 4), StringPiece::npos);
+ EXPECT_EQ(e.rfind(b, 7), StringPiece::npos);
+ // empty string nonsense
+ EXPECT_EQ(d.rfind(d, 4), string().rfind(string()));
+ EXPECT_EQ(e.rfind(d, 7), string().rfind(string()));
+ EXPECT_EQ(d.rfind(e, 4), string().rfind(string()));
+ EXPECT_EQ(e.rfind(e, 7), string().rfind(string()));
+ EXPECT_EQ(d.rfind(d), string().rfind(string()));
+ EXPECT_EQ(e.rfind(d), string().rfind(string()));
+ EXPECT_EQ(d.rfind(e), string().rfind(string()));
+ EXPECT_EQ(e.rfind(e), string().rfind(string()));
+
+ EXPECT_EQ(g.rfind('o'), 8);
+ EXPECT_EQ(g.rfind('q'), StringPiece::npos);
+ EXPECT_EQ(g.rfind('o', 8), 8);
+ EXPECT_EQ(g.rfind('o', 7), 4);
+ EXPECT_EQ(g.rfind('o', 3), StringPiece::npos);
+ EXPECT_EQ(f.rfind('\0'), 3);
+ EXPECT_EQ(f.rfind('\0', 12), 3);
+ EXPECT_EQ(f.rfind('3'), 2);
+ EXPECT_EQ(f.rfind('5'), 5);
+ // empty string nonsense
+ EXPECT_EQ(d.rfind('o'), StringPiece::npos);
+ EXPECT_EQ(e.rfind('o'), StringPiece::npos);
+ EXPECT_EQ(d.rfind('o', 4), StringPiece::npos);
+ EXPECT_EQ(e.rfind('o', 7), StringPiece::npos);
+
+ EXPECT_EQ(a.find_first_of(b), 0);
+ EXPECT_EQ(a.find_first_of(b, 0), 0);
+ EXPECT_EQ(a.find_first_of(b, 1), 1);
+ EXPECT_EQ(a.find_first_of(b, 2), 2);
+ EXPECT_EQ(a.find_first_of(b, 3), StringPiece::npos);
+ EXPECT_EQ(a.find_first_of(c), 23);
+ EXPECT_EQ(a.find_first_of(c, 23), 23);
+ EXPECT_EQ(a.find_first_of(c, 24), 24);
+ EXPECT_EQ(a.find_first_of(c, 25), 25);
+ EXPECT_EQ(a.find_first_of(c, 26), StringPiece::npos);
+ EXPECT_EQ(g.find_first_of(b), 13);
+ EXPECT_EQ(g.find_first_of(c), 0);
+ EXPECT_EQ(a.find_first_of(f), StringPiece::npos);
+ EXPECT_EQ(f.find_first_of(a), StringPiece::npos);
+ // empty string nonsense
+ EXPECT_EQ(a.find_first_of(d), StringPiece::npos);
+ EXPECT_EQ(a.find_first_of(e), StringPiece::npos);
+ EXPECT_EQ(d.find_first_of(b), StringPiece::npos);
+ EXPECT_EQ(e.find_first_of(b), StringPiece::npos);
+ EXPECT_EQ(d.find_first_of(d), StringPiece::npos);
+ EXPECT_EQ(e.find_first_of(d), StringPiece::npos);
+ EXPECT_EQ(d.find_first_of(e), StringPiece::npos);
+ EXPECT_EQ(e.find_first_of(e), StringPiece::npos);
+
+ EXPECT_EQ(a.find_first_not_of(b), 3);
+ EXPECT_EQ(a.find_first_not_of(c), 0);
+ EXPECT_EQ(b.find_first_not_of(a), StringPiece::npos);
+ EXPECT_EQ(c.find_first_not_of(a), StringPiece::npos);
+ EXPECT_EQ(f.find_first_not_of(a), 0);
+ EXPECT_EQ(a.find_first_not_of(f), 0);
+ EXPECT_EQ(a.find_first_not_of(d), 0);
+ EXPECT_EQ(a.find_first_not_of(e), 0);
+ // empty string nonsense
+ EXPECT_EQ(d.find_first_not_of(a), StringPiece::npos);
+ EXPECT_EQ(e.find_first_not_of(a), StringPiece::npos);
+ EXPECT_EQ(d.find_first_not_of(d), StringPiece::npos);
+ EXPECT_EQ(e.find_first_not_of(d), StringPiece::npos);
+ EXPECT_EQ(d.find_first_not_of(e), StringPiece::npos);
+ EXPECT_EQ(e.find_first_not_of(e), StringPiece::npos);
+
+ StringPiece h("====");
+ EXPECT_EQ(h.find_first_not_of('='), StringPiece::npos);
+ EXPECT_EQ(h.find_first_not_of('=', 3), StringPiece::npos);
+ EXPECT_EQ(h.find_first_not_of('\0'), 0);
+ EXPECT_EQ(g.find_first_not_of('x'), 2);
+ EXPECT_EQ(f.find_first_not_of('\0'), 0);
+ EXPECT_EQ(f.find_first_not_of('\0', 3), 4);
+ EXPECT_EQ(f.find_first_not_of('\0', 2), 2);
+ // empty string nonsense
+ EXPECT_EQ(d.find_first_not_of('x'), StringPiece::npos);
+ EXPECT_EQ(e.find_first_not_of('x'), StringPiece::npos);
+ EXPECT_EQ(d.find_first_not_of('\0'), StringPiece::npos);
+ EXPECT_EQ(e.find_first_not_of('\0'), StringPiece::npos);
+
+ // StringPiece g("xx not found bb");
+ StringPiece i("56");
+ EXPECT_EQ(h.find_last_of(a), StringPiece::npos);
+ EXPECT_EQ(g.find_last_of(a), g.size()-1);
+ EXPECT_EQ(a.find_last_of(b), 2);
+ EXPECT_EQ(a.find_last_of(c), a.size()-1);
+ EXPECT_EQ(f.find_last_of(i), 6);
+ EXPECT_EQ(a.find_last_of('a'), 0);
+ EXPECT_EQ(a.find_last_of('b'), 1);
+ EXPECT_EQ(a.find_last_of('z'), 25);
+ EXPECT_EQ(a.find_last_of('a', 5), 0);
+ EXPECT_EQ(a.find_last_of('b', 5), 1);
+ EXPECT_EQ(a.find_last_of('b', 0), StringPiece::npos);
+ EXPECT_EQ(a.find_last_of('z', 25), 25);
+ EXPECT_EQ(a.find_last_of('z', 24), StringPiece::npos);
+ EXPECT_EQ(f.find_last_of(i, 5), 5);
+ EXPECT_EQ(f.find_last_of(i, 6), 6);
+ EXPECT_EQ(f.find_last_of(a, 4), StringPiece::npos);
+ // empty string nonsense
+ EXPECT_EQ(f.find_last_of(d), StringPiece::npos);
+ EXPECT_EQ(f.find_last_of(e), StringPiece::npos);
+ EXPECT_EQ(f.find_last_of(d, 4), StringPiece::npos);
+ EXPECT_EQ(f.find_last_of(e, 4), StringPiece::npos);
+ EXPECT_EQ(d.find_last_of(d), StringPiece::npos);
+ EXPECT_EQ(d.find_last_of(e), StringPiece::npos);
+ EXPECT_EQ(e.find_last_of(d), StringPiece::npos);
+ EXPECT_EQ(e.find_last_of(e), StringPiece::npos);
+ EXPECT_EQ(d.find_last_of(f), StringPiece::npos);
+ EXPECT_EQ(e.find_last_of(f), StringPiece::npos);
+ EXPECT_EQ(d.find_last_of(d, 4), StringPiece::npos);
+ EXPECT_EQ(d.find_last_of(e, 4), StringPiece::npos);
+ EXPECT_EQ(e.find_last_of(d, 4), StringPiece::npos);
+ EXPECT_EQ(e.find_last_of(e, 4), StringPiece::npos);
+ EXPECT_EQ(d.find_last_of(f, 4), StringPiece::npos);
+ EXPECT_EQ(e.find_last_of(f, 4), StringPiece::npos);
+
+ EXPECT_EQ(a.find_last_not_of(b), a.size()-1);
+ EXPECT_EQ(a.find_last_not_of(c), 22);
+ EXPECT_EQ(b.find_last_not_of(a), StringPiece::npos);
+ EXPECT_EQ(b.find_last_not_of(b), StringPiece::npos);
+ EXPECT_EQ(f.find_last_not_of(i), 4);
+ EXPECT_EQ(a.find_last_not_of(c, 24), 22);
+ EXPECT_EQ(a.find_last_not_of(b, 3), 3);
+ EXPECT_EQ(a.find_last_not_of(b, 2), StringPiece::npos);
+ // empty string nonsense
+ EXPECT_EQ(f.find_last_not_of(d), f.size()-1);
+ EXPECT_EQ(f.find_last_not_of(e), f.size()-1);
+ EXPECT_EQ(f.find_last_not_of(d, 4), 4);
+ EXPECT_EQ(f.find_last_not_of(e, 4), 4);
+ EXPECT_EQ(d.find_last_not_of(d), StringPiece::npos);
+ EXPECT_EQ(d.find_last_not_of(e), StringPiece::npos);
+ EXPECT_EQ(e.find_last_not_of(d), StringPiece::npos);
+ EXPECT_EQ(e.find_last_not_of(e), StringPiece::npos);
+ EXPECT_EQ(d.find_last_not_of(f), StringPiece::npos);
+ EXPECT_EQ(e.find_last_not_of(f), StringPiece::npos);
+ EXPECT_EQ(d.find_last_not_of(d, 4), StringPiece::npos);
+ EXPECT_EQ(d.find_last_not_of(e, 4), StringPiece::npos);
+ EXPECT_EQ(e.find_last_not_of(d, 4), StringPiece::npos);
+ EXPECT_EQ(e.find_last_not_of(e, 4), StringPiece::npos);
+ EXPECT_EQ(d.find_last_not_of(f, 4), StringPiece::npos);
+ EXPECT_EQ(e.find_last_not_of(f, 4), StringPiece::npos);
+
+ EXPECT_EQ(h.find_last_not_of('x'), h.size() - 1);
+ EXPECT_EQ(h.find_last_not_of('='), StringPiece::npos);
+ EXPECT_EQ(b.find_last_not_of('c'), 1);
+ EXPECT_EQ(h.find_last_not_of('x', 2), 2);
+ EXPECT_EQ(h.find_last_not_of('=', 2), StringPiece::npos);
+ EXPECT_EQ(b.find_last_not_of('b', 1), 0);
+ // empty string nonsense
+ EXPECT_EQ(d.find_last_not_of('x'), StringPiece::npos);
+ EXPECT_EQ(e.find_last_not_of('x'), StringPiece::npos);
+ EXPECT_EQ(d.find_last_not_of('\0'), StringPiece::npos);
+ EXPECT_EQ(e.find_last_not_of('\0'), StringPiece::npos);
+
+ EXPECT_EQ(a.substr(0, 3), b);
+ EXPECT_EQ(a.substr(23), c);
+ EXPECT_EQ(a.substr(23, 3), c);
+ EXPECT_EQ(a.substr(23, 99), c);
+ EXPECT_EQ(a.substr(0), a);
+ EXPECT_EQ(a.substr(3, 2), "de");
+ // empty string nonsense
+ EXPECT_EQ(a.substr(99, 2), e);
+ EXPECT_EQ(d.substr(99), e);
+ EXPECT_EQ(d.substr(0, 99), e);
+ EXPECT_EQ(d.substr(99, 99), e);
+ // use of npos
+ EXPECT_EQ(a.substr(0, StringPiece::npos), a);
+ EXPECT_EQ(a.substr(23, StringPiece::npos), c);
+ EXPECT_EQ(a.substr(StringPiece::npos, 0), e);
+ EXPECT_EQ(a.substr(StringPiece::npos, 1), e);
+ EXPECT_EQ(a.substr(StringPiece::npos, StringPiece::npos), e);
+
+ // Substring constructors.
+ EXPECT_EQ(StringPiece(a, 0, 3), b);
+ EXPECT_EQ(StringPiece(a, 23), c);
+ EXPECT_EQ(StringPiece(a, 23, 3), c);
+ EXPECT_EQ(StringPiece(a, 23, 99), c);
+ EXPECT_EQ(StringPiece(a, 0), a);
+ EXPECT_EQ(StringPiece(a, 3, 2), "de");
+ // empty string nonsense
+ EXPECT_EQ(StringPiece(d, 0, 99), e);
+ // Verify that they work taking an actual string, not just a StringPiece.
+ string a2 = a.as_string();
+ EXPECT_EQ(StringPiece(a2, 0, 3), b);
+ EXPECT_EQ(StringPiece(a2, 23), c);
+ EXPECT_EQ(StringPiece(a2, 23, 3), c);
+ EXPECT_EQ(StringPiece(a2, 23, 99), c);
+ EXPECT_EQ(StringPiece(a2, 0), a);
+ EXPECT_EQ(StringPiece(a2, 3, 2), "de");
+}
+
+TEST(StringPiece, Custom) {
+ StringPiece a("foobar");
+ string s1("123");
+ s1 += '\0';
+ s1 += "456";
+ StringPiece b(s1);
+ StringPiece e;
+ string s2;
+
+ // CopyToString
+ a.CopyToString(&s2);
+ EXPECT_EQ(s2.size(), 6);
+ EXPECT_EQ(s2, "foobar");
+ b.CopyToString(&s2);
+ EXPECT_EQ(s2.size(), 7);
+ EXPECT_EQ(s1, s2);
+ e.CopyToString(&s2);
+ EXPECT_TRUE(s2.empty());
+
+ // AppendToString
+ s2.erase();
+ a.AppendToString(&s2);
+ EXPECT_EQ(s2.size(), 6);
+ EXPECT_EQ(s2, "foobar");
+ a.AppendToString(&s2);
+ EXPECT_EQ(s2.size(), 12);
+ EXPECT_EQ(s2, "foobarfoobar");
+
+ // starts_with
+ EXPECT_TRUE(a.starts_with(a));
+ EXPECT_TRUE(a.starts_with("foo"));
+ EXPECT_TRUE(a.starts_with(e));
+ EXPECT_TRUE(b.starts_with(s1));
+ EXPECT_TRUE(b.starts_with(b));
+ EXPECT_TRUE(b.starts_with(e));
+ EXPECT_TRUE(e.starts_with(""));
+ EXPECT_TRUE(!a.starts_with(b));
+ EXPECT_TRUE(!b.starts_with(a));
+ EXPECT_TRUE(!e.starts_with(a));
+
+ // ends with
+ EXPECT_TRUE(a.ends_with(a));
+ EXPECT_TRUE(a.ends_with("bar"));
+ EXPECT_TRUE(a.ends_with(e));
+ EXPECT_TRUE(b.ends_with(s1));
+ EXPECT_TRUE(b.ends_with(b));
+ EXPECT_TRUE(b.ends_with(e));
+ EXPECT_TRUE(e.ends_with(""));
+ EXPECT_TRUE(!a.ends_with(b));
+ EXPECT_TRUE(!b.ends_with(a));
+ EXPECT_TRUE(!e.ends_with(a));
+
+ // remove_prefix
+ StringPiece c(a);
+ c.remove_prefix(3);
+ EXPECT_EQ(c, "bar");
+ c = a;
+ c.remove_prefix(0);
+ EXPECT_EQ(c, a);
+ c.remove_prefix(c.size());
+ EXPECT_EQ(c, e);
+
+ // remove_suffix
+ c = a;
+ c.remove_suffix(3);
+ EXPECT_EQ(c, "foo");
+ c = a;
+ c.remove_suffix(0);
+ EXPECT_EQ(c, a);
+ c.remove_suffix(c.size());
+ EXPECT_EQ(c, e);
+
+ // set
+ c.set("foobar", 6);
+ EXPECT_EQ(c, a);
+ c.set("foobar", 0);
+ EXPECT_EQ(c, e);
+ c.set("foobar", 7);
+ EXPECT_NE(c, a);
+
+ c.set("foobar");
+ EXPECT_EQ(c, a);
+
+ c.set(static_cast<const void*>("foobar"), 6);
+ EXPECT_EQ(c, a);
+ c.set(static_cast<const void*>("foobar"), 0);
+ EXPECT_EQ(c, e);
+ c.set(static_cast<const void*>("foobar"), 7);
+ EXPECT_NE(c, a);
+
+ // as_string
+ string s3(a.as_string().c_str(), 7);
+ EXPECT_EQ(c, s3);
+ string s4(e.as_string());
+ EXPECT_TRUE(s4.empty());
+
+ // ToString
+ {
+ string s5(a.ToString().c_str(), 7);
+ EXPECT_EQ(c, s5);
+ string s6(e.ToString());
+ EXPECT_TRUE(s6.empty());
+ }
+
+ // Consume
+ a.set("foobar");
+ EXPECT_TRUE(a.Consume("foo"));
+ EXPECT_EQ(a, "bar");
+ EXPECT_FALSE(a.Consume("foo"));
+ EXPECT_FALSE(a.Consume("barbar"));
+ EXPECT_FALSE(a.Consume("ar"));
+ EXPECT_EQ(a, "bar");
+
+ a.set("foobar");
+ EXPECT_TRUE(a.ConsumeFromEnd("bar"));
+ EXPECT_EQ(a, "foo");
+ EXPECT_FALSE(a.ConsumeFromEnd("bar"));
+ EXPECT_FALSE(a.ConsumeFromEnd("foofoo"));
+ EXPECT_FALSE(a.ConsumeFromEnd("fo"));
+ EXPECT_EQ(a, "foo");
+}
+
+TEST(StringPiece, Contains) {
+ StringPiece a("abcdefg");
+ StringPiece b("abcd");
+ StringPiece c("efg");
+ StringPiece d("gh");
+ EXPECT_TRUE(a.contains(b));
+ EXPECT_TRUE(a.contains(c));
+ EXPECT_TRUE(!a.contains(d));
+}
+
+TEST(StringPiece, NULLInput) {
+ // we used to crash here, but now we don't.
+ StringPiece s(NULL);
+ EXPECT_EQ(s.data(), (const char*)NULL);
+ EXPECT_EQ(s.size(), 0);
+
+ s.set(NULL);
+ EXPECT_EQ(s.data(), (const char*)NULL);
+ EXPECT_EQ(s.size(), 0);
+
+ // .ToString() on a StringPiece with NULL should produce the empty string.
+ EXPECT_EQ("", s.ToString());
+ EXPECT_EQ("", s.as_string());
+}
+
+TEST(StringPiece, Comparisons2) {
+ StringPiece abc("abcdefghijklmnopqrstuvwxyz");
+
+ // check comparison operations on strings longer than 4 bytes.
+ EXPECT_EQ(abc, StringPiece("abcdefghijklmnopqrstuvwxyz"));
+ EXPECT_EQ(abc.compare(StringPiece("abcdefghijklmnopqrstuvwxyz")), 0);
+
+ EXPECT_LT(abc, StringPiece("abcdefghijklmnopqrstuvwxzz"));
+ EXPECT_LT(abc.compare(StringPiece("abcdefghijklmnopqrstuvwxzz")), 0);
+
+ EXPECT_GT(abc, StringPiece("abcdefghijklmnopqrstuvwxyy"));
+ EXPECT_GT(abc.compare(StringPiece("abcdefghijklmnopqrstuvwxyy")), 0);
+
+ // starts_with
+ EXPECT_TRUE(abc.starts_with(abc));
+ EXPECT_TRUE(abc.starts_with("abcdefghijklm"));
+ EXPECT_TRUE(!abc.starts_with("abcdefguvwxyz"));
+
+ // ends_with
+ EXPECT_TRUE(abc.ends_with(abc));
+ EXPECT_TRUE(!abc.ends_with("abcdefguvwxyz"));
+ EXPECT_TRUE(abc.ends_with("nopqrstuvwxyz"));
+}
+
+TEST(ComparisonOpsTest, StringCompareNotAmbiguous) {
+ EXPECT_EQ("hello", string("hello"));
+ EXPECT_LT("hello", string("world"));
+}
+
+TEST(ComparisonOpsTest, HeterogenousStringPieceEquals) {
+ EXPECT_EQ(StringPiece("hello"), string("hello"));
+ EXPECT_EQ("hello", StringPiece("hello"));
+}
+
+TEST(FindOneCharTest, EdgeCases) {
+ StringPiece a("xxyyyxx");
+
+ // Set a = "xyyyx".
+ a.remove_prefix(1);
+ a.remove_suffix(1);
+
+ EXPECT_EQ(0, a.find('x'));
+ EXPECT_EQ(0, a.find('x', 0));
+ EXPECT_EQ(4, a.find('x', 1));
+ EXPECT_EQ(4, a.find('x', 4));
+ EXPECT_EQ(StringPiece::npos, a.find('x', 5));
+
+ EXPECT_EQ(4, a.rfind('x'));
+ EXPECT_EQ(4, a.rfind('x', 5));
+ EXPECT_EQ(4, a.rfind('x', 4));
+ EXPECT_EQ(0, a.rfind('x', 3));
+ EXPECT_EQ(0, a.rfind('x', 0));
+
+ // Set a = "yyy".
+ a.remove_prefix(1);
+ a.remove_suffix(1);
+
+ EXPECT_EQ(StringPiece::npos, a.find('x'));
+ EXPECT_EQ(StringPiece::npos, a.rfind('x'));
+}
+
+#ifndef NDEBUG
+TEST(NonNegativeLenTest, NonNegativeLen) {
+ EXPECT_DEATH(StringPiece("xyz", -1), "len >= 0");
+}
+#endif // ndef DEBUG
+
+} // namespace
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/stubs/strutil.cc b/src/google/protobuf/stubs/strutil.cc
index 7ecc17ee..99e8bf1d 100644
--- a/src/google/protobuf/stubs/strutil.cc
+++ b/src/google/protobuf/stubs/strutil.cc
@@ -31,6 +31,7 @@
// from google3/strings/strutil.cc
#include <google/protobuf/stubs/strutil.h>
+
#include <errno.h>
#include <float.h> // FLT_DIG and DBL_DIG
#include <limits>
@@ -38,6 +39,8 @@
#include <stdio.h>
#include <iterator>
+#include <google/protobuf/stubs/stl_util.h>
+
#ifdef _WIN32
// MSVC has only _snprintf, not snprintf.
//
@@ -309,17 +312,6 @@ void JoinStrings(const vector<string>& components,
#define IS_OCTAL_DIGIT(c) (((c) >= '0') && ((c) <= '7'))
-inline int hex_digit_to_int(char c) {
- /* Assume ASCII. */
- assert('0' == 0x30 && 'A' == 0x41 && 'a' == 0x61);
- assert(isxdigit(c));
- int x = static_cast<unsigned char>(c);
- if (x > '9') {
- x += 9;
- }
- return x & 0xf;
-}
-
// Protocol buffers doesn't ever care about errors, but I don't want to remove
// the code.
#define LOG_STRING(LEVEL, VECTOR) GOOGLE_LOG_IF(LEVEL, false)
@@ -652,14 +644,15 @@ inline bool safe_parse_sign(string* text /*inout*/,
return true;
}
-inline bool safe_parse_positive_int(
- string text, int32* value_p) {
+template<typename IntType>
+bool safe_parse_positive_int(
+ string text, IntType* value_p) {
int base = 10;
- int32 value = 0;
- const int32 vmax = std::numeric_limits<int32>::max();
+ IntType value = 0;
+ const IntType vmax = std::numeric_limits<IntType>::max();
assert(vmax > 0);
assert(vmax >= base);
- const int32 vmax_over_base = vmax / base;
+ const IntType vmax_over_base = vmax / base;
const char* start = text.data();
const char* end = start + text.size();
// loop over digits
@@ -685,14 +678,15 @@ inline bool safe_parse_positive_int(
return true;
}
-inline bool safe_parse_negative_int(
- string text, int32* value_p) {
+template<typename IntType>
+bool safe_parse_negative_int(
+ const string& text, IntType* value_p) {
int base = 10;
- int32 value = 0;
- const int32 vmin = std::numeric_limits<int32>::min();
+ IntType value = 0;
+ const IntType vmin = std::numeric_limits<IntType>::min();
assert(vmin < 0);
assert(vmin <= 0 - base);
- int32 vmin_over_base = vmin / base;
+ IntType vmin_over_base = vmin / base;
// 2003 c++ standard [expr.mul]
// "... the sign of the remainder is implementation-defined."
// Although (vmin/base)*base + vmin%base is always vmin.
@@ -725,7 +719,8 @@ inline bool safe_parse_negative_int(
return true;
}
-bool safe_int(string text, int32* value_p) {
+template<typename IntType>
+bool safe_int_internal(string text, IntType* value_p) {
*value_p = 0;
bool negative;
if (!safe_parse_sign(&text, &negative)) {
@@ -738,6 +733,16 @@ bool safe_int(string text, int32* value_p) {
}
}
+template<typename IntType>
+bool safe_uint_internal(string text, IntType* value_p) {
+ *value_p = 0;
+ bool negative;
+ if (!safe_parse_sign(&text, &negative) || negative) {
+ return false;
+ }
+ return safe_parse_positive_int(text, value_p);
+}
+
// ----------------------------------------------------------------------
// FastIntToBuffer()
// FastInt64ToBuffer()
@@ -1236,6 +1241,41 @@ char* DoubleToBuffer(double value, char* buffer) {
return buffer;
}
+static int memcasecmp(const char *s1, const char *s2, size_t len) {
+ const unsigned char *us1 = reinterpret_cast<const unsigned char *>(s1);
+ const unsigned char *us2 = reinterpret_cast<const unsigned char *>(s2);
+
+ for ( int i = 0; i < len; i++ ) {
+ const int diff =
+ static_cast<int>(static_cast<unsigned char>(ascii_tolower(us1[i]))) -
+ static_cast<int>(static_cast<unsigned char>(ascii_tolower(us2[i])));
+ if (diff != 0) return diff;
+ }
+ return 0;
+}
+
+inline bool CaseEqual(StringPiece s1, StringPiece s2) {
+ if (s1.size() != s2.size()) return false;
+ return memcasecmp(s1.data(), s2.data(), s1.size()) == 0;
+}
+
+bool safe_strtob(StringPiece str, bool* value) {
+ GOOGLE_CHECK(value != NULL) << "NULL output boolean given.";
+ if (CaseEqual(str, "true") || CaseEqual(str, "t") ||
+ CaseEqual(str, "yes") || CaseEqual(str, "y") ||
+ CaseEqual(str, "1")) {
+ *value = true;
+ return true;
+ }
+ if (CaseEqual(str, "false") || CaseEqual(str, "f") ||
+ CaseEqual(str, "no") || CaseEqual(str, "n") ||
+ CaseEqual(str, "0")) {
+ *value = false;
+ return true;
+ }
+ return false;
+}
+
bool safe_strtof(const char* str, float* value) {
char* endptr;
errno = 0; // errno only gets set on errors
@@ -1247,6 +1287,34 @@ bool safe_strtof(const char* str, float* value) {
return *str != 0 && *endptr == 0 && errno == 0;
}
+bool safe_strtod(const char* str, double* value) {
+ char* endptr;
+ *value = strtod(str, &endptr);
+ if (endptr != str) {
+ while (ascii_isspace(*endptr)) ++endptr;
+ }
+ // Ignore range errors from strtod. The values it
+ // returns on underflow and overflow are the right
+ // fallback in a robust setting.
+ return *str != '\0' && *endptr == '\0';
+}
+
+bool safe_strto32(const string& str, int32* value) {
+ return safe_int_internal(str, value);
+}
+
+bool safe_strtou32(const string& str, uint32* value) {
+ return safe_uint_internal(str, value);
+}
+
+bool safe_strto64(const string& str, int64* value) {
+ return safe_int_internal(str, value);
+}
+
+bool safe_strtou64(const string& str, uint64* value) {
+ return safe_uint_internal(str, value);
+}
+
char* FloatToBuffer(float value, char* buffer) {
// FLT_DIG is 6 for IEEE-754 floats, which are used on almost all
// platforms these days. Just in case some system exists where FLT_DIG
@@ -1518,5 +1586,661 @@ int GlobalReplaceSubstring(const string& substring,
return num_replacements;
}
+int CalculateBase64EscapedLen(int input_len, bool do_padding) {
+ // Base64 encodes three bytes of input at a time. If the input is not
+ // divisible by three, we pad as appropriate.
+ //
+ // (from http://tools.ietf.org/html/rfc3548)
+ // Special processing is performed if fewer than 24 bits are available
+ // at the end of the data being encoded. A full encoding quantum is
+ // always completed at the end of a quantity. When fewer than 24 input
+ // bits are available in an input group, zero bits are added (on the
+ // right) to form an integral number of 6-bit groups. Padding at the
+ // end of the data is performed using the '=' character. Since all base
+ // 64 input is an integral number of octets, only the following cases
+ // can arise:
+
+
+ // Base64 encodes each three bytes of input into four bytes of output.
+ int len = (input_len / 3) * 4;
+
+ if (input_len % 3 == 0) {
+ // (from http://tools.ietf.org/html/rfc3548)
+ // (1) the final quantum of encoding input is an integral multiple of 24
+ // bits; here, the final unit of encoded output will be an integral
+ // multiple of 4 characters with no "=" padding,
+ } else if (input_len % 3 == 1) {
+ // (from http://tools.ietf.org/html/rfc3548)
+ // (2) the final quantum of encoding input is exactly 8 bits; here, the
+ // final unit of encoded output will be two characters followed by two
+ // "=" padding characters, or
+ len += 2;
+ if (do_padding) {
+ len += 2;
+ }
+ } else { // (input_len % 3 == 2)
+ // (from http://tools.ietf.org/html/rfc3548)
+ // (3) the final quantum of encoding input is exactly 16 bits; here, the
+ // final unit of encoded output will be three characters followed by one
+ // "=" padding character.
+ len += 3;
+ if (do_padding) {
+ len += 1;
+ }
+ }
+
+ assert(len >= input_len); // make sure we didn't overflow
+ return len;
+}
+
+// Base64Escape does padding, so this calculation includes padding.
+int CalculateBase64EscapedLen(int input_len) {
+ return CalculateBase64EscapedLen(input_len, true);
+}
+
+// ----------------------------------------------------------------------
+// int Base64Unescape() - base64 decoder
+// int Base64Escape() - base64 encoder
+// int WebSafeBase64Unescape() - Google's variation of base64 decoder
+// int WebSafeBase64Escape() - Google's variation of base64 encoder
+//
+// Check out
+// http://tools.ietf.org/html/rfc2045 for formal description, but what we
+// care about is that...
+// Take the encoded stuff in groups of 4 characters and turn each
+// character into a code 0 to 63 thus:
+// A-Z map to 0 to 25
+// a-z map to 26 to 51
+// 0-9 map to 52 to 61
+// +(- for WebSafe) maps to 62
+// /(_ for WebSafe) maps to 63
+// There will be four numbers, all less than 64 which can be represented
+// by a 6 digit binary number (aaaaaa, bbbbbb, cccccc, dddddd respectively).
+// Arrange the 6 digit binary numbers into three bytes as such:
+// aaaaaabb bbbbcccc ccdddddd
+// Equals signs (one or two) are used at the end of the encoded block to
+// indicate that the text was not an integer multiple of three bytes long.
+// ----------------------------------------------------------------------
+
+int Base64UnescapeInternal(const char *src_param, int szsrc,
+ char *dest, int szdest,
+ const signed char* unbase64) {
+ static const char kPad64Equals = '=';
+ static const char kPad64Dot = '.';
+
+ int decode = 0;
+ int destidx = 0;
+ int state = 0;
+ unsigned int ch = 0;
+ unsigned int temp = 0;
+
+ // If "char" is signed by default, using *src as an array index results in
+ // accessing negative array elements. Treat the input as a pointer to
+ // unsigned char to avoid this.
+ const unsigned char *src = reinterpret_cast<const unsigned char*>(src_param);
+
+ // The GET_INPUT macro gets the next input character, skipping
+ // over any whitespace, and stopping when we reach the end of the
+ // string or when we read any non-data character. The arguments are
+ // an arbitrary identifier (used as a label for goto) and the number
+ // of data bytes that must remain in the input to avoid aborting the
+ // loop.
+#define GET_INPUT(label, remain) \
+ label: \
+ --szsrc; \
+ ch = *src++; \
+ decode = unbase64[ch]; \
+ if (decode < 0) { \
+ if (ascii_isspace(ch) && szsrc >= remain) \
+ goto label; \
+ state = 4 - remain; \
+ break; \
+ }
+
+ // if dest is null, we're just checking to see if it's legal input
+ // rather than producing output. (I suspect this could just be done
+ // with a regexp...). We duplicate the loop so this test can be
+ // outside it instead of in every iteration.
+
+ if (dest) {
+ // This loop consumes 4 input bytes and produces 3 output bytes
+ // per iteration. We can't know at the start that there is enough
+ // data left in the string for a full iteration, so the loop may
+ // break out in the middle; if so 'state' will be set to the
+ // number of input bytes read.
+
+ while (szsrc >= 4) {
+ // We'll start by optimistically assuming that the next four
+ // bytes of the string (src[0..3]) are four good data bytes
+ // (that is, no nulls, whitespace, padding chars, or illegal
+ // chars). We need to test src[0..2] for nulls individually
+ // before constructing temp to preserve the property that we
+ // never read past a null in the string (no matter how long
+ // szsrc claims the string is).
+
+ if (!src[0] || !src[1] || !src[2] ||
+ (temp = ((unsigned(unbase64[src[0]]) << 18) |
+ (unsigned(unbase64[src[1]]) << 12) |
+ (unsigned(unbase64[src[2]]) << 6) |
+ (unsigned(unbase64[src[3]])))) & 0x80000000) {
+ // Iff any of those four characters was bad (null, illegal,
+ // whitespace, padding), then temp's high bit will be set
+ // (because unbase64[] is -1 for all bad characters).
+ //
+ // We'll back up and resort to the slower decoder, which knows
+ // how to handle those cases.
+
+ GET_INPUT(first, 4);
+ temp = decode;
+ GET_INPUT(second, 3);
+ temp = (temp << 6) | decode;
+ GET_INPUT(third, 2);
+ temp = (temp << 6) | decode;
+ GET_INPUT(fourth, 1);
+ temp = (temp << 6) | decode;
+ } else {
+ // We really did have four good data bytes, so advance four
+ // characters in the string.
+
+ szsrc -= 4;
+ src += 4;
+ decode = -1;
+ ch = '\0';
+ }
+
+ // temp has 24 bits of input, so write that out as three bytes.
+
+ if (destidx+3 > szdest) return -1;
+ dest[destidx+2] = temp;
+ temp >>= 8;
+ dest[destidx+1] = temp;
+ temp >>= 8;
+ dest[destidx] = temp;
+ destidx += 3;
+ }
+ } else {
+ while (szsrc >= 4) {
+ if (!src[0] || !src[1] || !src[2] ||
+ (temp = ((unsigned(unbase64[src[0]]) << 18) |
+ (unsigned(unbase64[src[1]]) << 12) |
+ (unsigned(unbase64[src[2]]) << 6) |
+ (unsigned(unbase64[src[3]])))) & 0x80000000) {
+ GET_INPUT(first_no_dest, 4);
+ GET_INPUT(second_no_dest, 3);
+ GET_INPUT(third_no_dest, 2);
+ GET_INPUT(fourth_no_dest, 1);
+ } else {
+ szsrc -= 4;
+ src += 4;
+ decode = -1;
+ ch = '\0';
+ }
+ destidx += 3;
+ }
+ }
+
+#undef GET_INPUT
+
+ // if the loop terminated because we read a bad character, return
+ // now.
+ if (decode < 0 && ch != '\0' &&
+ ch != kPad64Equals && ch != kPad64Dot && !ascii_isspace(ch))
+ return -1;
+
+ if (ch == kPad64Equals || ch == kPad64Dot) {
+ // if we stopped by hitting an '=' or '.', un-read that character -- we'll
+ // look at it again when we count to check for the proper number of
+ // equals signs at the end.
+ ++szsrc;
+ --src;
+ } else {
+ // This loop consumes 1 input byte per iteration. It's used to
+ // clean up the 0-3 input bytes remaining when the first, faster
+ // loop finishes. 'temp' contains the data from 'state' input
+ // characters read by the first loop.
+ while (szsrc > 0) {
+ --szsrc;
+ ch = *src++;
+ decode = unbase64[ch];
+ if (decode < 0) {
+ if (ascii_isspace(ch)) {
+ continue;
+ } else if (ch == '\0') {
+ break;
+ } else if (ch == kPad64Equals || ch == kPad64Dot) {
+ // back up one character; we'll read it again when we check
+ // for the correct number of pad characters at the end.
+ ++szsrc;
+ --src;
+ break;
+ } else {
+ return -1;
+ }
+ }
+
+ // Each input character gives us six bits of output.
+ temp = (temp << 6) | decode;
+ ++state;
+ if (state == 4) {
+ // If we've accumulated 24 bits of output, write that out as
+ // three bytes.
+ if (dest) {
+ if (destidx+3 > szdest) return -1;
+ dest[destidx+2] = temp;
+ temp >>= 8;
+ dest[destidx+1] = temp;
+ temp >>= 8;
+ dest[destidx] = temp;
+ }
+ destidx += 3;
+ state = 0;
+ temp = 0;
+ }
+ }
+ }
+
+ // Process the leftover data contained in 'temp' at the end of the input.
+ int expected_equals = 0;
+ switch (state) {
+ case 0:
+ // Nothing left over; output is a multiple of 3 bytes.
+ break;
+
+ case 1:
+ // Bad input; we have 6 bits left over.
+ return -1;
+
+ case 2:
+ // Produce one more output byte from the 12 input bits we have left.
+ if (dest) {
+ if (destidx+1 > szdest) return -1;
+ temp >>= 4;
+ dest[destidx] = temp;
+ }
+ ++destidx;
+ expected_equals = 2;
+ break;
+
+ case 3:
+ // Produce two more output bytes from the 18 input bits we have left.
+ if (dest) {
+ if (destidx+2 > szdest) return -1;
+ temp >>= 2;
+ dest[destidx+1] = temp;
+ temp >>= 8;
+ dest[destidx] = temp;
+ }
+ destidx += 2;
+ expected_equals = 1;
+ break;
+
+ default:
+ // state should have no other values at this point.
+ GOOGLE_LOG(FATAL) << "This can't happen; base64 decoder state = " << state;
+ }
+
+ // The remainder of the string should be all whitespace, mixed with
+ // exactly 0 equals signs, or exactly 'expected_equals' equals
+ // signs. (Always accepting 0 equals signs is a google extension
+ // not covered in the RFC, as is accepting dot as the pad character.)
+
+ int equals = 0;
+ while (szsrc > 0 && *src) {
+ if (*src == kPad64Equals || *src == kPad64Dot)
+ ++equals;
+ else if (!ascii_isspace(*src))
+ return -1;
+ --szsrc;
+ ++src;
+ }
+
+ return (equals == 0 || equals == expected_equals) ? destidx : -1;
+}
+
+// The arrays below were generated by the following code
+// #include <sys/time.h>
+// #include <stdlib.h>
+// #include <string.h>
+// main()
+// {
+// static const char Base64[] =
+// "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+// char *pos;
+// int idx, i, j;
+// printf(" ");
+// for (i = 0; i < 255; i += 8) {
+// for (j = i; j < i + 8; j++) {
+// pos = strchr(Base64, j);
+// if ((pos == NULL) || (j == 0))
+// idx = -1;
+// else
+// idx = pos - Base64;
+// if (idx == -1)
+// printf(" %2d, ", idx);
+// else
+// printf(" %2d/*%c*/,", idx, j);
+// }
+// printf("\n ");
+// }
+// }
+//
+// where the value of "Base64[]" was replaced by one of the base-64 conversion
+// tables from the functions below.
+static const signed char kUnBase64[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 62/*+*/, -1, -1, -1, 63/*/ */,
+ 52/*0*/, 53/*1*/, 54/*2*/, 55/*3*/, 56/*4*/, 57/*5*/, 58/*6*/, 59/*7*/,
+ 60/*8*/, 61/*9*/, -1, -1, -1, -1, -1, -1,
+ -1, 0/*A*/, 1/*B*/, 2/*C*/, 3/*D*/, 4/*E*/, 5/*F*/, 6/*G*/,
+ 07/*H*/, 8/*I*/, 9/*J*/, 10/*K*/, 11/*L*/, 12/*M*/, 13/*N*/, 14/*O*/,
+ 15/*P*/, 16/*Q*/, 17/*R*/, 18/*S*/, 19/*T*/, 20/*U*/, 21/*V*/, 22/*W*/,
+ 23/*X*/, 24/*Y*/, 25/*Z*/, -1, -1, -1, -1, -1,
+ -1, 26/*a*/, 27/*b*/, 28/*c*/, 29/*d*/, 30/*e*/, 31/*f*/, 32/*g*/,
+ 33/*h*/, 34/*i*/, 35/*j*/, 36/*k*/, 37/*l*/, 38/*m*/, 39/*n*/, 40/*o*/,
+ 41/*p*/, 42/*q*/, 43/*r*/, 44/*s*/, 45/*t*/, 46/*u*/, 47/*v*/, 48/*w*/,
+ 49/*x*/, 50/*y*/, 51/*z*/, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1
+};
+static const signed char kUnWebSafeBase64[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 62/*-*/, -1, -1,
+ 52/*0*/, 53/*1*/, 54/*2*/, 55/*3*/, 56/*4*/, 57/*5*/, 58/*6*/, 59/*7*/,
+ 60/*8*/, 61/*9*/, -1, -1, -1, -1, -1, -1,
+ -1, 0/*A*/, 1/*B*/, 2/*C*/, 3/*D*/, 4/*E*/, 5/*F*/, 6/*G*/,
+ 07/*H*/, 8/*I*/, 9/*J*/, 10/*K*/, 11/*L*/, 12/*M*/, 13/*N*/, 14/*O*/,
+ 15/*P*/, 16/*Q*/, 17/*R*/, 18/*S*/, 19/*T*/, 20/*U*/, 21/*V*/, 22/*W*/,
+ 23/*X*/, 24/*Y*/, 25/*Z*/, -1, -1, -1, -1, 63/*_*/,
+ -1, 26/*a*/, 27/*b*/, 28/*c*/, 29/*d*/, 30/*e*/, 31/*f*/, 32/*g*/,
+ 33/*h*/, 34/*i*/, 35/*j*/, 36/*k*/, 37/*l*/, 38/*m*/, 39/*n*/, 40/*o*/,
+ 41/*p*/, 42/*q*/, 43/*r*/, 44/*s*/, 45/*t*/, 46/*u*/, 47/*v*/, 48/*w*/,
+ 49/*x*/, 50/*y*/, 51/*z*/, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+int WebSafeBase64Unescape(const char *src, int szsrc, char *dest, int szdest) {
+ return Base64UnescapeInternal(src, szsrc, dest, szdest, kUnWebSafeBase64);
+}
+
+static bool Base64UnescapeInternal(const char* src, int slen, string* dest,
+ const signed char* unbase64) {
+ // Determine the size of the output string. Base64 encodes every 3 bytes into
+ // 4 characters. any leftover chars are added directly for good measure.
+ // This is documented in the base64 RFC: http://tools.ietf.org/html/rfc3548
+ const int dest_len = 3 * (slen / 4) + (slen % 4);
+
+ dest->resize(dest_len);
+
+ // We are getting the destination buffer by getting the beginning of the
+ // string and converting it into a char *.
+ const int len = Base64UnescapeInternal(src, slen, string_as_array(dest),
+ dest_len, unbase64);
+ if (len < 0) {
+ dest->clear();
+ return false;
+ }
+
+ // could be shorter if there was padding
+ GOOGLE_DCHECK_LE(len, dest_len);
+ dest->erase(len);
+
+ return true;
+}
+
+bool Base64Unescape(StringPiece src, string* dest) {
+ return Base64UnescapeInternal(src.data(), src.size(), dest, kUnBase64);
+}
+
+bool WebSafeBase64Unescape(StringPiece src, string* dest) {
+ return Base64UnescapeInternal(src.data(), src.size(), dest, kUnWebSafeBase64);
+}
+
+int Base64EscapeInternal(const unsigned char *src, int szsrc,
+ char *dest, int szdest, const char *base64,
+ bool do_padding) {
+ static const char kPad64 = '=';
+
+ if (szsrc <= 0) return 0;
+
+ if (szsrc * 4 > szdest * 3) return 0;
+
+ char *cur_dest = dest;
+ const unsigned char *cur_src = src;
+
+ char *limit_dest = dest + szdest;
+ const unsigned char *limit_src = src + szsrc;
+
+ // Three bytes of data encodes to four characters of cyphertext.
+ // So we can pump through three-byte chunks atomically.
+ while (cur_src < limit_src - 3) { // keep going as long as we have >= 32 bits
+ uint32 in = BigEndian::Load32(cur_src) >> 8;
+
+ cur_dest[0] = base64[in >> 18];
+ in &= 0x3FFFF;
+ cur_dest[1] = base64[in >> 12];
+ in &= 0xFFF;
+ cur_dest[2] = base64[in >> 6];
+ in &= 0x3F;
+ cur_dest[3] = base64[in];
+
+ cur_dest += 4;
+ cur_src += 3;
+ }
+ // To save time, we didn't update szdest or szsrc in the loop. So do it now.
+ szdest = limit_dest - cur_dest;
+ szsrc = limit_src - cur_src;
+
+ /* now deal with the tail (<=3 bytes) */
+ switch (szsrc) {
+ case 0:
+ // Nothing left; nothing more to do.
+ break;
+ case 1: {
+ // One byte left: this encodes to two characters, and (optionally)
+ // two pad characters to round out the four-character cypherblock.
+ if ((szdest -= 2) < 0) return 0;
+ uint32 in = cur_src[0];
+ cur_dest[0] = base64[in >> 2];
+ in &= 0x3;
+ cur_dest[1] = base64[in << 4];
+ cur_dest += 2;
+ if (do_padding) {
+ if ((szdest -= 2) < 0) return 0;
+ cur_dest[0] = kPad64;
+ cur_dest[1] = kPad64;
+ cur_dest += 2;
+ }
+ break;
+ }
+ case 2: {
+ // Two bytes left: this encodes to three characters, and (optionally)
+ // one pad character to round out the four-character cypherblock.
+ if ((szdest -= 3) < 0) return 0;
+ uint32 in = BigEndian::Load16(cur_src);
+ cur_dest[0] = base64[in >> 10];
+ in &= 0x3FF;
+ cur_dest[1] = base64[in >> 4];
+ in &= 0x00F;
+ cur_dest[2] = base64[in << 2];
+ cur_dest += 3;
+ if (do_padding) {
+ if ((szdest -= 1) < 0) return 0;
+ cur_dest[0] = kPad64;
+ cur_dest += 1;
+ }
+ break;
+ }
+ case 3: {
+ // Three bytes left: same as in the big loop above. We can't do this in
+ // the loop because the loop above always reads 4 bytes, and the fourth
+ // byte is past the end of the input.
+ if ((szdest -= 4) < 0) return 0;
+ uint32 in = (cur_src[0] << 16) + BigEndian::Load16(cur_src + 1);
+ cur_dest[0] = base64[in >> 18];
+ in &= 0x3FFFF;
+ cur_dest[1] = base64[in >> 12];
+ in &= 0xFFF;
+ cur_dest[2] = base64[in >> 6];
+ in &= 0x3F;
+ cur_dest[3] = base64[in];
+ cur_dest += 4;
+ break;
+ }
+ default:
+ // Should not be reached: blocks of 4 bytes are handled
+ // in the while loop before this switch statement.
+ GOOGLE_LOG(FATAL) << "Logic problem? szsrc = " << szsrc;
+ break;
+ }
+ return (cur_dest - dest);
+}
+
+static const char kBase64Chars[] =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static const char kWebSafeBase64Chars[] =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
+
+int Base64Escape(const unsigned char *src, int szsrc, char *dest, int szdest) {
+ return Base64EscapeInternal(src, szsrc, dest, szdest, kBase64Chars, true);
+}
+int WebSafeBase64Escape(const unsigned char *src, int szsrc, char *dest,
+ int szdest, bool do_padding) {
+ return Base64EscapeInternal(src, szsrc, dest, szdest,
+ kWebSafeBase64Chars, do_padding);
+}
+
+void Base64EscapeInternal(const unsigned char* src, int szsrc,
+ string* dest, bool do_padding,
+ const char* base64_chars) {
+ const int calc_escaped_size =
+ CalculateBase64EscapedLen(szsrc, do_padding);
+ dest->resize(calc_escaped_size);
+ const int escaped_len = Base64EscapeInternal(src, szsrc,
+ string_as_array(dest),
+ dest->size(),
+ base64_chars,
+ do_padding);
+ GOOGLE_DCHECK_EQ(calc_escaped_size, escaped_len);
+ dest->erase(escaped_len);
+}
+
+void Base64Escape(const unsigned char *src, int szsrc,
+ string* dest, bool do_padding) {
+ Base64EscapeInternal(src, szsrc, dest, do_padding, kBase64Chars);
+}
+
+void WebSafeBase64Escape(const unsigned char *src, int szsrc,
+ string *dest, bool do_padding) {
+ Base64EscapeInternal(src, szsrc, dest, do_padding, kWebSafeBase64Chars);
+}
+
+void Base64Escape(StringPiece src, string* dest) {
+ Base64Escape(reinterpret_cast<const unsigned char*>(src.data()),
+ src.size(), dest, true);
+}
+
+void WebSafeBase64Escape(StringPiece src, string* dest) {
+ WebSafeBase64Escape(reinterpret_cast<const unsigned char*>(src.data()),
+ src.size(), dest, false);
+}
+
+void WebSafeBase64EscapeWithPadding(StringPiece src, string* dest) {
+ WebSafeBase64Escape(reinterpret_cast<const unsigned char*>(src.data()),
+ src.size(), dest, true);
+}
+
+// Helper to append a Unicode code point to a string as UTF8, without bringing
+// in any external dependencies.
+int EncodeAsUTF8Char(uint32 code_point, char* output) {
+ uint32 tmp = 0;
+ int len = 0;
+ if (code_point <= 0x7f) {
+ tmp = code_point;
+ len = 1;
+ } else if (code_point <= 0x07ff) {
+ tmp = 0x0000c080 |
+ ((code_point & 0x07c0) << 2) |
+ (code_point & 0x003f);
+ len = 2;
+ } else if (code_point <= 0xffff) {
+ tmp = 0x00e08080 |
+ ((code_point & 0xf000) << 4) |
+ ((code_point & 0x0fc0) << 2) |
+ (code_point & 0x003f);
+ len = 3;
+ } else {
+ // UTF-16 is only defined for code points up to 0x10FFFF, and UTF-8 is
+ // normally only defined up to there as well.
+ tmp = 0xf0808080 |
+ ((code_point & 0x1c0000) << 6) |
+ ((code_point & 0x03f000) << 4) |
+ ((code_point & 0x000fc0) << 2) |
+ (code_point & 0x003f);
+ len = 4;
+ }
+ tmp = ghtonl(tmp);
+ memcpy(output, reinterpret_cast<const char*>(&tmp) + sizeof(tmp) - len, len);
+ return len;
+}
+
+// Table of UTF-8 character lengths, based on first byte
+static const unsigned char kUTF8LenTbl[256] = {
+ 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
+
+ 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,
+ 3,3,3,3,3,3,3,3, 3,3,3,3,3,3,3,3, 4,4,4,4,4,4,4,4, 4,4,4,4,4,4,4,4
+};
+
+// Return length of a single UTF-8 source character
+int UTF8FirstLetterNumBytes(const char* src, int len) {
+ if (len == 0) {
+ return 0;
+ }
+ return kUTF8LenTbl[*reinterpret_cast<const uint8*>(src)];
+}
+
} // namespace protobuf
} // namespace google
diff --git a/src/google/protobuf/stubs/strutil.h b/src/google/protobuf/stubs/strutil.h
index 397122ef..b22066b6 100644
--- a/src/google/protobuf/stubs/strutil.h
+++ b/src/google/protobuf/stubs/strutil.h
@@ -36,6 +36,7 @@
#include <stdlib.h>
#include <vector>
#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/stringpiece.h>
namespace google {
namespace protobuf {
@@ -72,7 +73,33 @@ inline bool ascii_isdigit(char c) {
}
inline bool ascii_isspace(char c) {
- return c == ' ';
+ return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' ||
+ c == '\r';
+}
+
+inline bool ascii_isupper(char c) {
+ return c >= 'A' && c <= 'Z';
+}
+
+inline bool ascii_islower(char c) {
+ return c >= 'a' && c <= 'z';
+}
+
+inline char ascii_toupper(char c) {
+ return ascii_islower(c) ? c - ('a' - 'A') : c;
+}
+
+inline char ascii_tolower(char c) {
+ return ascii_isupper(c) ? c + ('a' - 'A') : c;
+}
+
+inline int hex_digit_to_int(char c) {
+ /* Assume ASCII. */
+ int x = static_cast<unsigned char>(c);
+ if (x > '9') {
+ x += 9;
+ }
+ return x & 0xf;
}
// ----------------------------------------------------------------------
@@ -360,12 +387,59 @@ inline uint64 strtou64(const char *nptr, char **endptr, int base) {
}
// ----------------------------------------------------------------------
+// safe_strtob()
// safe_strto32()
-// ----------------------------------------------------------------------
-LIBPROTOBUF_EXPORT bool safe_int(string text, int32* value_p);
+// safe_strtou32()
+// safe_strto64()
+// safe_strtou64()
+// safe_strtof()
+// safe_strtod()
+// ----------------------------------------------------------------------
+LIBPROTOBUF_EXPORT bool safe_strtob(StringPiece str, bool* value);
+
+LIBPROTOBUF_EXPORT bool safe_strto32(const string& str, int32* value);
+LIBPROTOBUF_EXPORT bool safe_strtou32(const string& str, uint32* value);
+inline bool safe_strto32(const char* str, int32* value) {
+ return safe_strto32(string(str), value);
+}
+inline bool safe_strto32(StringPiece str, int32* value) {
+ return safe_strto32(str.ToString(), value);
+}
+inline bool safe_strtou32(const char* str, uint32* value) {
+ return safe_strtou32(string(str), value);
+}
+inline bool safe_strtou32(StringPiece str, uint32* value) {
+ return safe_strtou32(str.ToString(), value);
+}
-inline bool safe_strto32(string text, int32* value) {
- return safe_int(text, value);
+LIBPROTOBUF_EXPORT bool safe_strto64(const string& str, int64* value);
+LIBPROTOBUF_EXPORT bool safe_strtou64(const string& str, uint64* value);
+inline bool safe_strto64(const char* str, int64* value) {
+ return safe_strto64(string(str), value);
+}
+inline bool safe_strto64(StringPiece str, int64* value) {
+ return safe_strto64(str.ToString(), value);
+}
+inline bool safe_strtou64(const char* str, uint64* value) {
+ return safe_strtou64(string(str), value);
+}
+inline bool safe_strtou64(StringPiece str, uint64* value) {
+ return safe_strtou64(str.ToString(), value);
+}
+
+LIBPROTOBUF_EXPORT bool safe_strtof(const char* str, float* value);
+LIBPROTOBUF_EXPORT bool safe_strtod(const char* str, double* value);
+inline bool safe_strtof(const string& str, float* value) {
+ return safe_strtof(str.c_str(), value);
+}
+inline bool safe_strtod(const string& str, double* value) {
+ return safe_strtod(str.c_str(), value);
+}
+inline bool safe_strtof(StringPiece str, float* value) {
+ return safe_strtof(str.ToString(), value);
+}
+inline bool safe_strtod(StringPiece str, double* value) {
+ return safe_strtod(str.ToString(), value);
}
// ----------------------------------------------------------------------
@@ -451,6 +525,10 @@ inline char* FastUInt64ToBuffer(uint64 i, char* buffer) {
return buffer;
}
+inline string SimpleBtoa(bool value) {
+ return value ? "true" : "false";
+}
+
// ----------------------------------------------------------------------
// SimpleItoa()
// Description: converts an integer to a string.
@@ -497,28 +575,30 @@ static const int kFloatToBufferSize = 24;
namespace strings {
+enum PadSpec {
+ NO_PAD = 1,
+ ZERO_PAD_2,
+ ZERO_PAD_3,
+ ZERO_PAD_4,
+ ZERO_PAD_5,
+ ZERO_PAD_6,
+ ZERO_PAD_7,
+ ZERO_PAD_8,
+ ZERO_PAD_9,
+ ZERO_PAD_10,
+ ZERO_PAD_11,
+ ZERO_PAD_12,
+ ZERO_PAD_13,
+ ZERO_PAD_14,
+ ZERO_PAD_15,
+ ZERO_PAD_16,
+};
+
struct Hex {
uint64 value;
- enum PadSpec {
- NONE = 1,
- ZERO_PAD_2,
- ZERO_PAD_3,
- ZERO_PAD_4,
- ZERO_PAD_5,
- ZERO_PAD_6,
- ZERO_PAD_7,
- ZERO_PAD_8,
- ZERO_PAD_9,
- ZERO_PAD_10,
- ZERO_PAD_11,
- ZERO_PAD_12,
- ZERO_PAD_13,
- ZERO_PAD_14,
- ZERO_PAD_15,
- ZERO_PAD_16,
- } spec;
+ enum PadSpec spec;
template <class Int>
- explicit Hex(Int v, PadSpec s = NONE)
+ explicit Hex(Int v, PadSpec s = NO_PAD)
: spec(s) {
// Prevent sign-extension by casting integers to
// their unsigned counterparts.
@@ -571,6 +651,9 @@ struct LIBPROTOBUF_EXPORT AlphaNum {
AlphaNum(const string& str)
: piece_data_(str.data()), piece_size_(str.size()) {}
+ AlphaNum(StringPiece str)
+ : piece_data_(str.data()), piece_size_(str.size()) {}
+
size_t size() const { return piece_size_; }
const char *data() const { return piece_data_; }
@@ -692,6 +775,12 @@ string Join(const Range& components,
}
// ----------------------------------------------------------------------
+// ToHex()
+// Return a lower-case hex string representation of the given integer.
+// ----------------------------------------------------------------------
+LIBPROTOBUF_EXPORT string ToHex(uint64 num);
+
+// ----------------------------------------------------------------------
// GlobalReplaceSubstring()
// Replaces all instances of a substring in a string. Does nothing
// if 'substring' is empty. Returns the number of replacements.
@@ -702,6 +791,83 @@ LIBPROTOBUF_EXPORT int GlobalReplaceSubstring(const string& substring,
const string& replacement,
string* s);
+// ----------------------------------------------------------------------
+// Base64Unescape()
+// Converts "src" which is encoded in Base64 to its binary equivalent and
+// writes it to "dest". If src contains invalid characters, dest is cleared
+// and the function returns false. Returns true on success.
+// ----------------------------------------------------------------------
+LIBPROTOBUF_EXPORT bool Base64Unescape(StringPiece src, string* dest);
+
+// ----------------------------------------------------------------------
+// WebSafeBase64Unescape()
+// This is a variation of Base64Unescape which uses '-' instead of '+', and
+// '_' instead of '/'. src is not null terminated, instead specify len. I
+// recommend that slen<szdest, but we honor szdest anyway.
+// RETURNS the length of dest, or -1 if src contains invalid chars.
+
+// The variation that stores into a string clears the string first, and
+// returns false (with dest empty) if src contains invalid chars; for
+// this version src and dest must be different strings.
+// ----------------------------------------------------------------------
+LIBPROTOBUF_EXPORT int WebSafeBase64Unescape(const char* src, int slen,
+ char* dest, int szdest);
+LIBPROTOBUF_EXPORT bool WebSafeBase64Unescape(StringPiece src, string* dest);
+
+// Return the length to use for the output buffer given to the base64 escape
+// routines. Make sure to use the same value for do_padding in both.
+// This function may return incorrect results if given input_len values that
+// are extremely high, which should happen rarely.
+LIBPROTOBUF_EXPORT int CalculateBase64EscapedLen(int input_len,
+ bool do_padding);
+// Use this version when calling Base64Escape without a do_padding arg.
+LIBPROTOBUF_EXPORT int CalculateBase64EscapedLen(int input_len);
+
+// ----------------------------------------------------------------------
+// Base64Escape()
+// WebSafeBase64Escape()
+// Encode "src" to "dest" using base64 encoding.
+// src is not null terminated, instead specify len.
+// 'dest' should have at least CalculateBase64EscapedLen() length.
+// RETURNS the length of dest.
+// The WebSafe variation use '-' instead of '+' and '_' instead of '/'
+// so that we can place the out in the URL or cookies without having
+// to escape them. It also has an extra parameter "do_padding",
+// which when set to false will prevent padding with "=".
+// ----------------------------------------------------------------------
+LIBPROTOBUF_EXPORT int Base64Escape(const unsigned char* src, int slen,
+ char* dest, int szdest);
+LIBPROTOBUF_EXPORT int WebSafeBase64Escape(
+ const unsigned char* src, int slen, char* dest,
+ int szdest, bool do_padding);
+// Encode src into dest with padding.
+LIBPROTOBUF_EXPORT void Base64Escape(StringPiece src, string* dest);
+// Encode src into dest web-safely without padding.
+LIBPROTOBUF_EXPORT void WebSafeBase64Escape(StringPiece src, string* dest);
+// Encode src into dest web-safely with padding.
+LIBPROTOBUF_EXPORT void WebSafeBase64EscapeWithPadding(StringPiece src,
+ string* dest);
+
+LIBPROTOBUF_EXPORT void Base64Escape(const unsigned char* src, int szsrc,
+ string* dest, bool do_padding);
+LIBPROTOBUF_EXPORT void WebSafeBase64Escape(const unsigned char* src, int szsrc,
+ string* dest, bool do_padding);
+
+static const int UTFmax = 4;
+// ----------------------------------------------------------------------
+// EncodeAsUTF8Char()
+// Helper to append a Unicode code point to a string as UTF8, without bringing
+// in any external dependencies. The output buffer must be as least 4 bytes
+// large.
+// ----------------------------------------------------------------------
+LIBPROTOBUF_EXPORT int EncodeAsUTF8Char(uint32 code_point, char* output);
+
+// ----------------------------------------------------------------------
+// UTF8FirstLetterNumBytes()
+// Length of the first UTF-8 character.
+// ----------------------------------------------------------------------
+LIBPROTOBUF_EXPORT int UTF8FirstLetterNumBytes(const char* src, int len);
+
} // namespace protobuf
} // namespace google
diff --git a/src/google/protobuf/stubs/strutil_unittest.cc b/src/google/protobuf/stubs/strutil_unittest.cc
index 7f27dca0..5d62fc4a 100644
--- a/src/google/protobuf/stubs/strutil_unittest.cc
+++ b/src/google/protobuf/stubs/strutil_unittest.cc
@@ -32,9 +32,15 @@
#include <google/protobuf/stubs/strutil.h>
+#include <locale.h>
+
+#include <google/protobuf/stubs/stl_util.h>
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>
-#include <locale.h>
+
+#ifdef _WIN32
+#define snprintf _snprintf
+#endif
namespace google {
namespace protobuf {
@@ -68,6 +74,737 @@ TEST(StringUtilityTest, ImmuneToLocales) {
setlocale(LC_NUMERIC, old_locale.c_str());
}
+#define EXPECT_EQ_ARRAY(len, x, y, msg) \
+ for (int j = 0; j < len; ++j) { \
+ EXPECT_EQ(x[j], y[j]) << "" # x << " != " # y \
+ << " byte " << j << ": " << msg; \
+ }
+
+static struct {
+ int plain_length;
+ const char* plaintext;
+ const char* cyphertext;
+} base64_tests[] = {
+ // Empty string.
+ { 0, "", ""},
+
+ // Basic bit patterns;
+ // values obtained with "echo -n '...' | uuencode -m test"
+
+ { 1, "\000", "AA==" },
+ { 1, "\001", "AQ==" },
+ { 1, "\002", "Ag==" },
+ { 1, "\004", "BA==" },
+ { 1, "\010", "CA==" },
+ { 1, "\020", "EA==" },
+ { 1, "\040", "IA==" },
+ { 1, "\100", "QA==" },
+ { 1, "\200", "gA==" },
+
+ { 1, "\377", "/w==" },
+ { 1, "\376", "/g==" },
+ { 1, "\375", "/Q==" },
+ { 1, "\373", "+w==" },
+ { 1, "\367", "9w==" },
+ { 1, "\357", "7w==" },
+ { 1, "\337", "3w==" },
+ { 1, "\277", "vw==" },
+ { 1, "\177", "fw==" },
+ { 2, "\000\000", "AAA=" },
+ { 2, "\000\001", "AAE=" },
+ { 2, "\000\002", "AAI=" },
+ { 2, "\000\004", "AAQ=" },
+ { 2, "\000\010", "AAg=" },
+ { 2, "\000\020", "ABA=" },
+ { 2, "\000\040", "ACA=" },
+ { 2, "\000\100", "AEA=" },
+ { 2, "\000\200", "AIA=" },
+ { 2, "\001\000", "AQA=" },
+ { 2, "\002\000", "AgA=" },
+ { 2, "\004\000", "BAA=" },
+ { 2, "\010\000", "CAA=" },
+ { 2, "\020\000", "EAA=" },
+ { 2, "\040\000", "IAA=" },
+ { 2, "\100\000", "QAA=" },
+ { 2, "\200\000", "gAA=" },
+
+ { 2, "\377\377", "//8=" },
+ { 2, "\377\376", "//4=" },
+ { 2, "\377\375", "//0=" },
+ { 2, "\377\373", "//s=" },
+ { 2, "\377\367", "//c=" },
+ { 2, "\377\357", "/+8=" },
+ { 2, "\377\337", "/98=" },
+ { 2, "\377\277", "/78=" },
+ { 2, "\377\177", "/38=" },
+ { 2, "\376\377", "/v8=" },
+ { 2, "\375\377", "/f8=" },
+ { 2, "\373\377", "+/8=" },
+ { 2, "\367\377", "9/8=" },
+ { 2, "\357\377", "7/8=" },
+ { 2, "\337\377", "3/8=" },
+ { 2, "\277\377", "v/8=" },
+ { 2, "\177\377", "f/8=" },
+
+ { 3, "\000\000\000", "AAAA" },
+ { 3, "\000\000\001", "AAAB" },
+ { 3, "\000\000\002", "AAAC" },
+ { 3, "\000\000\004", "AAAE" },
+ { 3, "\000\000\010", "AAAI" },
+ { 3, "\000\000\020", "AAAQ" },
+ { 3, "\000\000\040", "AAAg" },
+ { 3, "\000\000\100", "AABA" },
+ { 3, "\000\000\200", "AACA" },
+ { 3, "\000\001\000", "AAEA" },
+ { 3, "\000\002\000", "AAIA" },
+ { 3, "\000\004\000", "AAQA" },
+ { 3, "\000\010\000", "AAgA" },
+ { 3, "\000\020\000", "ABAA" },
+ { 3, "\000\040\000", "ACAA" },
+ { 3, "\000\100\000", "AEAA" },
+ { 3, "\000\200\000", "AIAA" },
+ { 3, "\001\000\000", "AQAA" },
+ { 3, "\002\000\000", "AgAA" },
+ { 3, "\004\000\000", "BAAA" },
+ { 3, "\010\000\000", "CAAA" },
+ { 3, "\020\000\000", "EAAA" },
+ { 3, "\040\000\000", "IAAA" },
+ { 3, "\100\000\000", "QAAA" },
+ { 3, "\200\000\000", "gAAA" },
+
+ { 3, "\377\377\377", "////" },
+ { 3, "\377\377\376", "///+" },
+ { 3, "\377\377\375", "///9" },
+ { 3, "\377\377\373", "///7" },
+ { 3, "\377\377\367", "///3" },
+ { 3, "\377\377\357", "///v" },
+ { 3, "\377\377\337", "///f" },
+ { 3, "\377\377\277", "//+/" },
+ { 3, "\377\377\177", "//9/" },
+ { 3, "\377\376\377", "//7/" },
+ { 3, "\377\375\377", "//3/" },
+ { 3, "\377\373\377", "//v/" },
+ { 3, "\377\367\377", "//f/" },
+ { 3, "\377\357\377", "/+//" },
+ { 3, "\377\337\377", "/9//" },
+ { 3, "\377\277\377", "/7//" },
+ { 3, "\377\177\377", "/3//" },
+ { 3, "\376\377\377", "/v//" },
+ { 3, "\375\377\377", "/f//" },
+ { 3, "\373\377\377", "+///" },
+ { 3, "\367\377\377", "9///" },
+ { 3, "\357\377\377", "7///" },
+ { 3, "\337\377\377", "3///" },
+ { 3, "\277\377\377", "v///" },
+ { 3, "\177\377\377", "f///" },
+
+ // Random numbers: values obtained with
+ //
+ // #! /bin/bash
+ // dd bs=$1 count=1 if=/dev/random of=/tmp/bar.random
+ // od -N $1 -t o1 /tmp/bar.random
+ // uuencode -m test < /tmp/bar.random
+ //
+ // where $1 is the number of bytes (2, 3)
+
+ { 2, "\243\361", "o/E=" },
+ { 2, "\024\167", "FHc=" },
+ { 2, "\313\252", "y6o=" },
+ { 2, "\046\041", "JiE=" },
+ { 2, "\145\236", "ZZ4=" },
+ { 2, "\254\325", "rNU=" },
+ { 2, "\061\330", "Mdg=" },
+ { 2, "\245\032", "pRo=" },
+ { 2, "\006\000", "BgA=" },
+ { 2, "\375\131", "/Vk=" },
+ { 2, "\303\210", "w4g=" },
+ { 2, "\040\037", "IB8=" },
+ { 2, "\261\372", "sfo=" },
+ { 2, "\335\014", "3Qw=" },
+ { 2, "\233\217", "m48=" },
+ { 2, "\373\056", "+y4=" },
+ { 2, "\247\232", "p5o=" },
+ { 2, "\107\053", "Rys=" },
+ { 2, "\204\077", "hD8=" },
+ { 2, "\276\211", "vok=" },
+ { 2, "\313\110", "y0g=" },
+ { 2, "\363\376", "8/4=" },
+ { 2, "\251\234", "qZw=" },
+ { 2, "\103\262", "Q7I=" },
+ { 2, "\142\312", "Yso=" },
+ { 2, "\067\211", "N4k=" },
+ { 2, "\220\001", "kAE=" },
+ { 2, "\152\240", "aqA=" },
+ { 2, "\367\061", "9zE=" },
+ { 2, "\133\255", "W60=" },
+ { 2, "\176\035", "fh0=" },
+ { 2, "\032\231", "Gpk=" },
+
+ { 3, "\013\007\144", "Cwdk" },
+ { 3, "\030\112\106", "GEpG" },
+ { 3, "\047\325\046", "J9Um" },
+ { 3, "\310\160\022", "yHAS" },
+ { 3, "\131\100\237", "WUCf" },
+ { 3, "\064\342\134", "NOJc" },
+ { 3, "\010\177\004", "CH8E" },
+ { 3, "\345\147\205", "5WeF" },
+ { 3, "\300\343\360", "wOPw" },
+ { 3, "\061\240\201", "MaCB" },
+ { 3, "\225\333\044", "ldsk" },
+ { 3, "\215\137\352", "jV/q" },
+ { 3, "\371\147\160", "+Wdw" },
+ { 3, "\030\320\051", "GNAp" },
+ { 3, "\044\174\241", "JHyh" },
+ { 3, "\260\127\037", "sFcf" },
+ { 3, "\111\045\033", "SSUb" },
+ { 3, "\202\114\107", "gkxH" },
+ { 3, "\057\371\042", "L/ki" },
+ { 3, "\223\247\244", "k6ek" },
+ { 3, "\047\216\144", "J45k" },
+ { 3, "\203\070\327", "gzjX" },
+ { 3, "\247\140\072", "p2A6" },
+ { 3, "\124\115\116", "VE1O" },
+ { 3, "\157\162\050", "b3Io" },
+ { 3, "\357\223\004", "75ME" },
+ { 3, "\052\117\156", "Kk9u" },
+ { 3, "\347\154\000", "52wA" },
+ { 3, "\303\012\142", "wwpi" },
+ { 3, "\060\035\362", "MB3y" },
+ { 3, "\130\226\361", "WJbx" },
+ { 3, "\173\013\071", "ews5" },
+ { 3, "\336\004\027", "3gQX" },
+ { 3, "\357\366\234", "7/ac" },
+ { 3, "\353\304\111", "68RJ" },
+ { 3, "\024\264\131", "FLRZ" },
+ { 3, "\075\114\251", "PUyp" },
+ { 3, "\315\031\225", "zRmV" },
+ { 3, "\154\201\276", "bIG+" },
+ { 3, "\200\066\072", "gDY6" },
+ { 3, "\142\350\267", "Yui3" },
+ { 3, "\033\000\166", "GwB2" },
+ { 3, "\210\055\077", "iC0/" },
+ { 3, "\341\037\124", "4R9U" },
+ { 3, "\161\103\152", "cUNq" },
+ { 3, "\270\142\131", "uGJZ" },
+ { 3, "\337\076\074", "3z48" },
+ { 3, "\375\106\362", "/Uby" },
+ { 3, "\227\301\127", "l8FX" },
+ { 3, "\340\002\234", "4AKc" },
+ { 3, "\121\064\033", "UTQb" },
+ { 3, "\157\134\143", "b1xj" },
+ { 3, "\247\055\327", "py3X" },
+ { 3, "\340\142\005", "4GIF" },
+ { 3, "\060\260\143", "MLBj" },
+ { 3, "\075\203\170", "PYN4" },
+ { 3, "\143\160\016", "Y3AO" },
+ { 3, "\313\013\063", "ywsz" },
+ { 3, "\174\236\135", "fJ5d" },
+ { 3, "\103\047\026", "QycW" },
+ { 3, "\365\005\343", "9QXj" },
+ { 3, "\271\160\223", "uXCT" },
+ { 3, "\362\255\172", "8q16" },
+ { 3, "\113\012\015", "SwoN" },
+
+ // various lengths, generated by this python script:
+ //
+ // from string import lowercase as lc
+ // for i in range(27):
+ // print '{ %2d, "%s",%s "%s" },' % (i, lc[:i], ' ' * (26-i),
+ // lc[:i].encode('base64').strip())
+
+ { 0, "", "" },
+ { 1, "a", "YQ==" },
+ { 2, "ab", "YWI=" },
+ { 3, "abc", "YWJj" },
+ { 4, "abcd", "YWJjZA==" },
+ { 5, "abcde", "YWJjZGU=" },
+ { 6, "abcdef", "YWJjZGVm" },
+ { 7, "abcdefg", "YWJjZGVmZw==" },
+ { 8, "abcdefgh", "YWJjZGVmZ2g=" },
+ { 9, "abcdefghi", "YWJjZGVmZ2hp" },
+ { 10, "abcdefghij", "YWJjZGVmZ2hpag==" },
+ { 11, "abcdefghijk", "YWJjZGVmZ2hpams=" },
+ { 12, "abcdefghijkl", "YWJjZGVmZ2hpamts" },
+ { 13, "abcdefghijklm", "YWJjZGVmZ2hpamtsbQ==" },
+ { 14, "abcdefghijklmn", "YWJjZGVmZ2hpamtsbW4=" },
+ { 15, "abcdefghijklmno", "YWJjZGVmZ2hpamtsbW5v" },
+ { 16, "abcdefghijklmnop", "YWJjZGVmZ2hpamtsbW5vcA==" },
+ { 17, "abcdefghijklmnopq", "YWJjZGVmZ2hpamtsbW5vcHE=" },
+ { 18, "abcdefghijklmnopqr", "YWJjZGVmZ2hpamtsbW5vcHFy" },
+ { 19, "abcdefghijklmnopqrs", "YWJjZGVmZ2hpamtsbW5vcHFycw==" },
+ { 20, "abcdefghijklmnopqrst", "YWJjZGVmZ2hpamtsbW5vcHFyc3Q=" },
+ { 21, "abcdefghijklmnopqrstu", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1" },
+ { 22, "abcdefghijklmnopqrstuv", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dg==" },
+ { 23, "abcdefghijklmnopqrstuvw", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnc=" },
+ { 24, "abcdefghijklmnopqrstuvwx", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4" },
+ { 25, "abcdefghijklmnopqrstuvwxy", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eQ==" },
+ { 26, "abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo=" },
+};
+
+static struct {
+ const char* plaintext;
+ const char* cyphertext;
+} base64_strings[] = {
+ // Some google quotes
+ // Cyphertext created with "uuencode (GNU sharutils) 4.6.3"
+ // (Note that we're testing the websafe encoding, though, so if
+ // you add messages, be sure to run "tr -- '+/' '-_'" on the output)
+ { "I was always good at math and science, and I never realized "
+ "that was unusual or somehow undesirable. So one of the things "
+ "I care a lot about is helping to remove that stigma, "
+ "to show girls that you can be feminine, you can like the things "
+ "that girls like, but you can also be really good at technology. "
+ "You can be really good at building things."
+ " - Marissa Meyer, Newsweek, 2010-12-22" "\n",
+
+ "SSB3YXMgYWx3YXlzIGdvb2QgYXQgbWF0aCBhbmQgc2NpZW5jZSwgYW5kIEkg"
+ "bmV2ZXIgcmVhbGl6ZWQgdGhhdCB3YXMgdW51c3VhbCBvciBzb21laG93IHVu"
+ "ZGVzaXJhYmxlLiBTbyBvbmUgb2YgdGhlIHRoaW5ncyBJIGNhcmUgYSBsb3Qg"
+ "YWJvdXQgaXMgaGVscGluZyB0byByZW1vdmUgdGhhdCBzdGlnbWEsIHRvIHNo"
+ "b3cgZ2lybHMgdGhhdCB5b3UgY2FuIGJlIGZlbWluaW5lLCB5b3UgY2FuIGxp"
+ "a2UgdGhlIHRoaW5ncyB0aGF0IGdpcmxzIGxpa2UsIGJ1dCB5b3UgY2FuIGFs"
+ "c28gYmUgcmVhbGx5IGdvb2QgYXQgdGVjaG5vbG9neS4gWW91IGNhbiBiZSBy"
+ "ZWFsbHkgZ29vZCBhdCBidWlsZGluZyB0aGluZ3MuIC0gTWFyaXNzYSBNZXll"
+ "ciwgTmV3c3dlZWssIDIwMTAtMTItMjIK" },
+
+ { "Typical first year for a new cluster: "
+ "~0.5 overheating "
+ "~1 PDU failure "
+ "~1 rack-move "
+ "~1 network rewiring "
+ "~20 rack failures "
+ "~5 racks go wonky "
+ "~8 network maintenances "
+ "~12 router reloads "
+ "~3 router failures "
+ "~dozens of minor 30-second blips for dns "
+ "~1000 individual machine failures "
+ "~thousands of hard drive failures "
+ "slow disks, bad memory, misconfigured machines, flaky machines, etc."
+ " - Jeff Dean, The Joys of Real Hardware" "\n",
+
+ "VHlwaWNhbCBmaXJzdCB5ZWFyIGZvciBhIG5ldyBjbHVzdGVyOiB-MC41IG92"
+ "ZXJoZWF0aW5nIH4xIFBEVSBmYWlsdXJlIH4xIHJhY2stbW92ZSB-MSBuZXR3"
+ "b3JrIHJld2lyaW5nIH4yMCByYWNrIGZhaWx1cmVzIH41IHJhY2tzIGdvIHdv"
+ "bmt5IH44IG5ldHdvcmsgbWFpbnRlbmFuY2VzIH4xMiByb3V0ZXIgcmVsb2Fk"
+ "cyB-MyByb3V0ZXIgZmFpbHVyZXMgfmRvemVucyBvZiBtaW5vciAzMC1zZWNv"
+ "bmQgYmxpcHMgZm9yIGRucyB-MTAwMCBpbmRpdmlkdWFsIG1hY2hpbmUgZmFp"
+ "bHVyZXMgfnRob3VzYW5kcyBvZiBoYXJkIGRyaXZlIGZhaWx1cmVzIHNsb3cg"
+ "ZGlza3MsIGJhZCBtZW1vcnksIG1pc2NvbmZpZ3VyZWQgbWFjaGluZXMsIGZs"
+ "YWt5IG1hY2hpbmVzLCBldGMuIC0gSmVmZiBEZWFuLCBUaGUgSm95cyBvZiBS"
+ "ZWFsIEhhcmR3YXJlCg" },
+
+ { "I'm the head of the webspam team at Google. "
+ "That means that if you type your name into Google and get porn back, "
+ "it's my fault. Unless you're a porn star, in which case porn is a "
+ "completely reasonable response."
+ " - Matt Cutts, Google Plus" "\n",
+
+ "SSdtIHRoZSBoZWFkIG9mIHRoZSB3ZWJzcGFtIHRlYW0gYXQgR29vZ2xlLiAg"
+ "VGhhdCBtZWFucyB0aGF0IGlmIHlvdSB0eXBlIHlvdXIgbmFtZSBpbnRvIEdv"
+ "b2dsZSBhbmQgZ2V0IHBvcm4gYmFjaywgaXQncyBteSBmYXVsdC4gVW5sZXNz"
+ "IHlvdSdyZSBhIHBvcm4gc3RhciwgaW4gd2hpY2ggY2FzZSBwb3JuIGlzIGEg"
+ "Y29tcGxldGVseSByZWFzb25hYmxlIHJlc3BvbnNlLiAtIE1hdHQgQ3V0dHMs"
+ "IEdvb2dsZSBQbHVzCg" },
+
+ { "It will still be a long time before machines approach human intelligence. "
+ "But luckily, machines don't actually have to be intelligent; "
+ "they just have to fake it. Access to a wealth of information, "
+ "combined with a rudimentary decision-making capacity, "
+ "can often be almost as useful. Of course, the results are better yet "
+ "when coupled with intelligence. A reference librarian with access to "
+ "a good search engine is a formidable tool."
+ " - Craig Silverstein, Siemens Pictures of the Future, Spring 2004" "\n",
+
+ "SXQgd2lsbCBzdGlsbCBiZSBhIGxvbmcgdGltZSBiZWZvcmUgbWFjaGluZXMg"
+ "YXBwcm9hY2ggaHVtYW4gaW50ZWxsaWdlbmNlLiBCdXQgbHVja2lseSwgbWFj"
+ "aGluZXMgZG9uJ3QgYWN0dWFsbHkgaGF2ZSB0byBiZSBpbnRlbGxpZ2VudDsg"
+ "dGhleSBqdXN0IGhhdmUgdG8gZmFrZSBpdC4gQWNjZXNzIHRvIGEgd2VhbHRo"
+ "IG9mIGluZm9ybWF0aW9uLCBjb21iaW5lZCB3aXRoIGEgcnVkaW1lbnRhcnkg"
+ "ZGVjaXNpb24tbWFraW5nIGNhcGFjaXR5LCBjYW4gb2Z0ZW4gYmUgYWxtb3N0"
+ "IGFzIHVzZWZ1bC4gT2YgY291cnNlLCB0aGUgcmVzdWx0cyBhcmUgYmV0dGVy"
+ "IHlldCB3aGVuIGNvdXBsZWQgd2l0aCBpbnRlbGxpZ2VuY2UuIEEgcmVmZXJl"
+ "bmNlIGxpYnJhcmlhbiB3aXRoIGFjY2VzcyB0byBhIGdvb2Qgc2VhcmNoIGVu"
+ "Z2luZSBpcyBhIGZvcm1pZGFibGUgdG9vbC4gLSBDcmFpZyBTaWx2ZXJzdGVp"
+ "biwgU2llbWVucyBQaWN0dXJlcyBvZiB0aGUgRnV0dXJlLCBTcHJpbmcgMjAw"
+ "NAo" },
+
+ // Degenerate edge case
+ { "",
+ "" },
+};
+
+TEST(Base64, EscapeAndUnescape) {
+ // Check the short strings; this tests the math (and boundaries)
+ for (int i = 0; i < sizeof(base64_tests) / sizeof(base64_tests[0]); ++i) {
+ char encode_buffer[100];
+ int encode_length;
+ char decode_buffer[100];
+ int decode_length;
+ int cypher_length;
+ string decode_str;
+
+ const unsigned char* unsigned_plaintext =
+ reinterpret_cast<const unsigned char*>(base64_tests[i].plaintext);
+
+ StringPiece plaintext(base64_tests[i].plaintext,
+ base64_tests[i].plain_length);
+
+ cypher_length = strlen(base64_tests[i].cyphertext);
+
+ // The basic escape function:
+ memset(encode_buffer, 0, sizeof(encode_buffer));
+ encode_length = Base64Escape(unsigned_plaintext,
+ base64_tests[i].plain_length,
+ encode_buffer,
+ sizeof(encode_buffer));
+ // Is it of the expected length?
+ EXPECT_EQ(encode_length, cypher_length);
+ // Would it have been okay to allocate only CalculateBase64EscapeLen()?
+ EXPECT_EQ(CalculateBase64EscapedLen(base64_tests[i].plain_length),
+ encode_length);
+
+ // Is it the expected encoded value?
+ ASSERT_STREQ(encode_buffer, base64_tests[i].cyphertext);
+
+ // If we encode it into a buffer of exactly the right length...
+ memset(encode_buffer, 0, sizeof(encode_buffer));
+ encode_length = Base64Escape(unsigned_plaintext,
+ base64_tests[i].plain_length,
+ encode_buffer,
+ cypher_length);
+ // Is it still of the expected length?
+ EXPECT_EQ(encode_length, cypher_length);
+
+ // And is the value still correct? (i.e., not losing the last byte)
+ EXPECT_STREQ(encode_buffer, base64_tests[i].cyphertext);
+
+ // If we decode it back:
+ decode_str.clear();
+ EXPECT_TRUE(Base64Unescape(
+ StringPiece(encode_buffer, cypher_length), &decode_str));
+
+ // Is it of the expected length?
+ EXPECT_EQ(base64_tests[i].plain_length, decode_str.length());
+
+ // Is it the expected decoded value?
+ EXPECT_EQ(plaintext, decode_str);
+
+ // Let's try with a pre-populated string.
+ string encoded("this junk should be ignored");
+ Base64Escape(string(base64_tests[i].plaintext,
+ base64_tests[i].plain_length),
+ &encoded);
+ EXPECT_EQ(encoded, string(encode_buffer, cypher_length));
+
+ string decoded("this junk should be ignored");
+ EXPECT_TRUE(Base64Unescape(
+ StringPiece(encode_buffer, cypher_length), &decoded));
+ EXPECT_EQ(decoded.size(), base64_tests[i].plain_length);
+ EXPECT_EQ_ARRAY(decoded.size(), decoded, base64_tests[i].plaintext, i);
+
+ // Our decoder treats the padding '=' characters at the end as
+ // optional (but if there are any, there must be the correct
+ // number of them.) If encode_buffer has any, run some additional
+ // tests that fiddle with them.
+ char* first_equals = strchr(encode_buffer, '=');
+ if (first_equals) {
+ // How many equals signs does the string start with?
+ int equals = (*(first_equals+1) == '=') ? 2 : 1;
+
+ // Try chopping off the equals sign(s) entirely. The decoder
+ // should still be okay with this.
+ string decoded2("this junk should also be ignored");
+ *first_equals = '\0';
+ EXPECT_TRUE(Base64Unescape(
+ StringPiece(encode_buffer, first_equals - encode_buffer), &decoded2));
+ EXPECT_EQ(decoded.size(), base64_tests[i].plain_length);
+ EXPECT_EQ_ARRAY(decoded.size(), decoded, base64_tests[i].plaintext, i);
+
+ // Now test chopping off the equals sign(s) and adding
+ // whitespace. Our decoder should still accept this.
+ decoded2.assign("this junk should be ignored");
+ *first_equals = ' ';
+ *(first_equals+1) = '\0';
+ EXPECT_TRUE(Base64Unescape(
+ StringPiece(encode_buffer, first_equals - encode_buffer + 1),
+ &decoded2));
+ EXPECT_EQ(decoded.size(), base64_tests[i].plain_length);
+ EXPECT_EQ_ARRAY(decoded.size(), decoded, base64_tests[i].plaintext, i);
+
+ // Now stick a bad character at the end of the string. The decoder
+ // should refuse this string.
+ decoded2.assign("this junk should be ignored");
+ *first_equals = '?';
+ *(first_equals+1) = '\0';
+ EXPECT_TRUE(
+ !Base64Unescape(
+ StringPiece(encode_buffer, first_equals - encode_buffer + 1),
+ &decoded2));
+
+ int len;
+
+ // Test whitespace mixed with the padding. (eg "AA = = ") The
+ // decoder should accept this.
+ if (equals == 2) {
+ snprintf(first_equals, 6, " = = ");
+ len = first_equals - encode_buffer + 5;
+ } else {
+ snprintf(first_equals, 6, " = ");
+ len = first_equals - encode_buffer + 3;
+ }
+ decoded2.assign("this junk should be ignored");
+ EXPECT_TRUE(
+ Base64Unescape(StringPiece(encode_buffer, len), &decoded2));
+ EXPECT_EQ(decoded.size(), base64_tests[i].plain_length);
+ EXPECT_EQ_ARRAY(decoded.size(), decoded, base64_tests[i].plaintext, i);
+
+ // Test whitespace mixed with the padding, but with the wrong
+ // number of equals signs (eg "AA = "). The decoder should
+ // refuse these strings.
+ if (equals == 1) {
+ snprintf(first_equals, 6, " = = ");
+ len = first_equals - encode_buffer + 5;
+ } else {
+ snprintf(first_equals, 6, " = ");
+ len = first_equals - encode_buffer + 3;
+ }
+ EXPECT_TRUE(
+ !Base64Unescape(StringPiece(encode_buffer, len), &decoded2));
+ }
+
+ // Cool! the basic Base64 encoder/decoder works.
+ // Let's try the alternate alphabet: tr -- '+/' '-_'
+
+ char websafe[100];
+ memset(websafe, 0, sizeof(websafe));
+ strncpy(websafe, base64_tests[i].cyphertext, cypher_length);
+ for (int c = 0; c < sizeof(websafe); ++c) {
+ if ('+' == websafe[c]) { websafe[c] = '-'; }
+ if ('/' == websafe[c]) { websafe[c] = '_'; }
+ }
+
+ // The websafe escape function:
+ memset(encode_buffer, 0, sizeof(encode_buffer));
+ encode_length = WebSafeBase64Escape(unsigned_plaintext,
+ base64_tests[i].plain_length,
+ encode_buffer,
+ sizeof(encode_buffer),
+ true);
+ // Is it of the expected length?
+ EXPECT_EQ(encode_length, cypher_length);
+ EXPECT_EQ(
+ CalculateBase64EscapedLen(base64_tests[i].plain_length, true),
+ encode_length);
+
+ // Is it the expected encoded value?
+ EXPECT_STREQ(encode_buffer, websafe);
+
+ // If we encode it into a buffer of exactly the right length...
+ memset(encode_buffer, 0, sizeof(encode_buffer));
+ encode_length = WebSafeBase64Escape(unsigned_plaintext,
+ base64_tests[i].plain_length,
+ encode_buffer,
+ cypher_length,
+ true);
+ // Is it still of the expected length?
+ EXPECT_EQ(encode_length, cypher_length);
+
+ // And is the value still correct? (i.e., not losing the last byte)
+ EXPECT_STREQ(encode_buffer, websafe);
+
+ // Let's try the string version of the encoder
+ encoded = "this junk should be ignored";
+ WebSafeBase64Escape(
+ unsigned_plaintext, base64_tests[i].plain_length,
+ &encoded, true);
+ EXPECT_EQ(encoded.size(), cypher_length);
+ EXPECT_STREQ(encoded.c_str(), websafe);
+
+ // If we decode it back:
+ memset(decode_buffer, 0, sizeof(decode_buffer));
+ decode_length = WebSafeBase64Unescape(encode_buffer,
+ cypher_length,
+ decode_buffer,
+ sizeof(decode_buffer));
+
+ // Is it of the expected length?
+ EXPECT_EQ(decode_length, base64_tests[i].plain_length);
+
+ // Is it the expected decoded value?
+ EXPECT_EQ(0,
+ memcmp(decode_buffer, base64_tests[i].plaintext, decode_length));
+
+ // If we decode it into a buffer of exactly the right length...
+ memset(decode_buffer, 0, sizeof(decode_buffer));
+ decode_length = WebSafeBase64Unescape(encode_buffer,
+ cypher_length,
+ decode_buffer,
+ decode_length);
+
+ // Is it still of the expected length?
+ EXPECT_EQ(decode_length, base64_tests[i].plain_length);
+
+ // And is it the expected decoded value?
+ EXPECT_EQ(0,
+ memcmp(decode_buffer, base64_tests[i].plaintext, decode_length));
+
+ // Try using '.' for the pad character.
+ for (int c = cypher_length - 1; c >= 0 && '=' == encode_buffer[c]; --c) {
+ encode_buffer[c] = '.';
+ }
+
+ // If we decode it back:
+ memset(decode_buffer, 0, sizeof(decode_buffer));
+ decode_length = WebSafeBase64Unescape(encode_buffer,
+ cypher_length,
+ decode_buffer,
+ sizeof(decode_buffer));
+
+ // Is it of the expected length?
+ EXPECT_EQ(decode_length, base64_tests[i].plain_length);
+
+ // Is it the expected decoded value?
+ EXPECT_EQ(0,
+ memcmp(decode_buffer, base64_tests[i].plaintext, decode_length));
+
+ // If we decode it into a buffer of exactly the right length...
+ memset(decode_buffer, 0, sizeof(decode_buffer));
+ decode_length = WebSafeBase64Unescape(encode_buffer,
+ cypher_length,
+ decode_buffer,
+ decode_length);
+
+ // Is it still of the expected length?
+ EXPECT_EQ(decode_length, base64_tests[i].plain_length);
+
+ // And is it the expected decoded value?
+ EXPECT_EQ(0,
+ memcmp(decode_buffer, base64_tests[i].plaintext, decode_length));
+
+ // Let's try the string version of the decoder
+ decoded = "this junk should be ignored";
+ EXPECT_TRUE(WebSafeBase64Unescape(
+ StringPiece(encode_buffer, cypher_length), &decoded));
+ EXPECT_EQ(decoded.size(), base64_tests[i].plain_length);
+ EXPECT_EQ_ARRAY(decoded.size(), decoded, base64_tests[i].plaintext, i);
+
+ // Okay! the websafe Base64 encoder/decoder works.
+ // Let's try the unpadded version
+
+ for (int c = 0; c < sizeof(websafe); ++c) {
+ if ('=' == websafe[c]) {
+ websafe[c] = '\0';
+ cypher_length = c;
+ break;
+ }
+ }
+
+ // The websafe escape function:
+ memset(encode_buffer, 0, sizeof(encode_buffer));
+ encode_length = WebSafeBase64Escape(unsigned_plaintext,
+ base64_tests[i].plain_length,
+ encode_buffer,
+ sizeof(encode_buffer),
+ false);
+ // Is it of the expected length?
+ EXPECT_EQ(encode_length, cypher_length);
+ EXPECT_EQ(
+ CalculateBase64EscapedLen(base64_tests[i].plain_length, false),
+ encode_length);
+
+ // Is it the expected encoded value?
+ EXPECT_STREQ(encode_buffer, websafe);
+
+ // If we encode it into a buffer of exactly the right length...
+ memset(encode_buffer, 0, sizeof(encode_buffer));
+ encode_length = WebSafeBase64Escape(unsigned_plaintext,
+ base64_tests[i].plain_length,
+ encode_buffer,
+ cypher_length,
+ false);
+ // Is it still of the expected length?
+ EXPECT_EQ(encode_length, cypher_length);
+
+ // And is the value still correct? (i.e., not losing the last byte)
+ EXPECT_STREQ(encode_buffer, websafe);
+
+ // Let's try the (other) string version of the encoder
+ string plain(base64_tests[i].plaintext, base64_tests[i].plain_length);
+ encoded = "this junk should be ignored";
+ WebSafeBase64Escape(plain, &encoded);
+ EXPECT_EQ(encoded.size(), cypher_length);
+ EXPECT_STREQ(encoded.c_str(), websafe);
+
+ // If we decode it back:
+ memset(decode_buffer, 0, sizeof(decode_buffer));
+ decode_length = WebSafeBase64Unescape(encode_buffer,
+ cypher_length,
+ decode_buffer,
+ sizeof(decode_buffer));
+
+ // Is it of the expected length?
+ EXPECT_EQ(decode_length, base64_tests[i].plain_length);
+
+ // Is it the expected decoded value?
+ EXPECT_EQ(0,
+ memcmp(decode_buffer, base64_tests[i].plaintext, decode_length));
+
+ // If we decode it into a buffer of exactly the right length...
+ memset(decode_buffer, 0, sizeof(decode_buffer));
+ decode_length = WebSafeBase64Unescape(encode_buffer,
+ cypher_length,
+ decode_buffer,
+ decode_length);
+
+ // Is it still of the expected length?
+ EXPECT_EQ(decode_length, base64_tests[i].plain_length);
+
+ // And is it the expected decoded value?
+ EXPECT_EQ(0,
+ memcmp(decode_buffer, base64_tests[i].plaintext, decode_length));
+
+
+ // Let's try the string version of the decoder
+ decoded = "this junk should be ignored";
+ EXPECT_TRUE(WebSafeBase64Unescape(
+ StringPiece(encode_buffer, cypher_length), &decoded));
+ EXPECT_EQ(decoded.size(), base64_tests[i].plain_length);
+ EXPECT_EQ_ARRAY(decoded.size(), decoded, base64_tests[i].plaintext, i);
+
+ // This value works. Try the next.
+ }
+
+ // Now try the long strings, this tests the streaming
+ for (int i = 0; i < sizeof(base64_strings) / sizeof(base64_strings[0]);
+ ++i) {
+ const unsigned char* unsigned_plaintext =
+ reinterpret_cast<const unsigned char*>(base64_strings[i].plaintext);
+ int plain_length = strlen(base64_strings[i].plaintext);
+ int cypher_length = strlen(base64_strings[i].cyphertext);
+ vector<char> buffer(cypher_length+1);
+ int encode_length = WebSafeBase64Escape(unsigned_plaintext,
+ plain_length,
+ &buffer[0],
+ buffer.size(),
+ false);
+ EXPECT_EQ(cypher_length, encode_length);
+ EXPECT_EQ(
+ CalculateBase64EscapedLen(plain_length, false), encode_length);
+ buffer[ encode_length ] = '\0';
+ EXPECT_STREQ(base64_strings[i].cyphertext, &buffer[0]);
+ }
+
+ // Verify the behavior when decoding bad data
+ {
+ const char* bad_data = "ab-/";
+ string buf;
+ EXPECT_FALSE(Base64Unescape(StringPiece(bad_data), &buf));
+ EXPECT_TRUE(!WebSafeBase64Unescape(bad_data, &buf));
+ EXPECT_TRUE(buf.empty());
+ }
+}
+
} // anonymous namespace
} // namespace protobuf
} // namespace google
diff --git a/src/google/protobuf/stubs/time.cc b/src/google/protobuf/stubs/time.cc
new file mode 100644
index 00000000..3319a244
--- /dev/null
+++ b/src/google/protobuf/stubs/time.cc
@@ -0,0 +1,366 @@
+#include <google/protobuf/stubs/time.h>
+
+#include <ctime>
+
+#include <google/protobuf/stubs/stringprintf.h>
+#include <google/protobuf/stubs/strutil.h>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+namespace {
+static const int64 kSecondsPerMinute = 60;
+static const int64 kSecondsPerHour = 3600;
+static const int64 kSecondsPerDay = kSecondsPerHour * 24;
+static const int64 kSecondsPer400Years =
+ kSecondsPerDay * (400 * 365 + 400 / 4 - 3);
+// Seconds from 0001-01-01T00:00:00 to 1970-01-01T:00:00:00
+static const int64 kSecondsFromEraToEpoch = 62135596800LL;
+// The range of timestamp values we support.
+static const int64 kMinTime = -62135596800LL; // 0001-01-01T00:00:00
+static const int64 kMaxTime = 253402300799LL; // 9999-12-31T23:59:59
+
+static const int kNanosPerSecond = 1000000000;
+static const int kNanosPerMillisecond = 1000000;
+static const int kNanosPerMicrosecond = 1000;
+
+// Count the seconds from the given year (start at Jan 1, 00:00) to 100 years
+// after.
+int64 SecondsPer100Years(int year) {
+ if (year % 400 == 0 || year % 400 > 300) {
+ return kSecondsPerDay * (100 * 365 + 100 / 4);
+ } else {
+ return kSecondsPerDay * (100 * 365 + 100 / 4 - 1);
+ }
+}
+
+// Count the seconds from the given year (start at Jan 1, 00:00) to 4 years
+// after.
+int64 SecondsPer4Years(int year) {
+ if ((year % 100 == 0 || year % 100 > 96) &&
+ !(year % 400 == 0 || year % 400 > 396)) {
+ // No leap years.
+ return kSecondsPerDay * (4 * 365);
+ } else {
+ // One leap years.
+ return kSecondsPerDay * (4 * 365 + 1);
+ }
+}
+
+bool IsLeapYear(int year) {
+ return year % 400 == 0 || (year % 4 == 0 && year % 100 != 0);
+}
+
+int64 SecondsPerYear(int year) {
+ return kSecondsPerDay * (IsLeapYear(year) ? 366 : 365);
+}
+
+static const int kDaysInMonth[13] = {
+ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+int64 SecondsPerMonth(int month, bool leap) {
+ if (month == 2 && leap) {
+ return kSecondsPerDay * (kDaysInMonth[month] + 1);
+ }
+ return kSecondsPerDay * kDaysInMonth[month];
+}
+
+static const int kDaysSinceJan[13] = {
+ 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
+};
+
+bool ValidateDateTime(const DateTime& time) {
+ if (time.year < 1 || time.year > 9999 ||
+ time.month < 1 || time.month > 12 ||
+ time.day < 1 || time.day > 31 ||
+ time.hour < 0 || time.hour > 23 ||
+ time.minute < 0 || time.minute > 59 ||
+ time.second < 0 || time.second > 59) {
+ return false;
+ }
+ if (time.month == 2 && IsLeapYear(time.year)) {
+ return time.month <= kDaysInMonth[time.month] + 1;
+ } else {
+ return time.month <= kDaysInMonth[time.month];
+ }
+}
+
+// Count the number of seconds elapsed from 0001-01-01T00:00:00 to the given
+// time.
+int64 SecondsSinceCommonEra(const DateTime& time) {
+ int64 result = 0;
+ // Years should be between 1 and 9999.
+ assert(time.year >= 1 && time.year <= 9999);
+ int year = 1;
+ if ((time.year - year) >= 400) {
+ int count_400years = (time.year - year) / 400;
+ result += kSecondsPer400Years * count_400years;
+ year += count_400years * 400;
+ }
+ while ((time.year - year) >= 100) {
+ result += SecondsPer100Years(year);
+ year += 100;
+ }
+ while ((time.year - year) >= 4) {
+ result += SecondsPer4Years(year);
+ year += 4;
+ }
+ while (time.year > year) {
+ result += SecondsPerYear(year);
+ ++year;
+ }
+ // Months should be between 1 and 12.
+ assert(time.month >= 1 && time.month <= 12);
+ int month = time.month;
+ result += kSecondsPerDay * kDaysSinceJan[month];
+ if (month > 2 && IsLeapYear(year)) {
+ result += kSecondsPerDay;
+ }
+ assert(time.day >= 1 &&
+ time.day <= (month == 2 && IsLeapYear(year)
+ ? kDaysInMonth[month] + 1
+ : kDaysInMonth[month]));
+ result += kSecondsPerDay * (time.day - 1);
+ result += kSecondsPerHour * time.hour +
+ kSecondsPerMinute * time.minute +
+ time.second;
+ return result;
+}
+
+// Format nanoseconds with either 3, 6, or 9 digits depending on the required
+// precision to represent the exact value.
+string FormatNanos(int32 nanos) {
+ if (nanos % kNanosPerMillisecond == 0) {
+ return StringPrintf("%03d", nanos / kNanosPerMillisecond);
+ } else if (nanos % kNanosPerMicrosecond == 0) {
+ return StringPrintf("%06d", nanos / kNanosPerMicrosecond);
+ } else {
+ return StringPrintf("%09d", nanos);
+ }
+}
+
+// Parses an integer from a null-terminated char sequence. The method
+// consumes at most "width" chars. Returns a pointer after the consumed
+// integer, or NULL if the data does not start with an integer or the
+// integer value does not fall in the range of [min_value, max_value].
+const char* ParseInt(const char* data, int width, int min_value,
+ int max_value, int* result) {
+ if (!ascii_isdigit(*data)) {
+ return NULL;
+ }
+ int value = 0;
+ for (int i = 0; i < width; ++i, ++data) {
+ if (ascii_isdigit(*data)) {
+ value = value * 10 + (*data - '0');
+ } else {
+ break;
+ }
+ }
+ if (value >= min_value && value <= max_value) {
+ *result = value;
+ return data;
+ } else {
+ return NULL;
+ }
+}
+
+// Consumes the fractional parts of a second into nanos. For example,
+// "010" will be parsed to 10000000 nanos.
+const char* ParseNanos(const char* data, int32* nanos) {
+ if (!ascii_isdigit(*data)) {
+ return NULL;
+ }
+ int value = 0;
+ int len = 0;
+ // Consume as many digits as there are but only take the first 9 into
+ // account.
+ while (ascii_isdigit(*data)) {
+ if (len < 9) {
+ value = value * 10 + *data - '0';
+ }
+ ++len;
+ ++data;
+ }
+ while (len < 9) {
+ value = value * 10;
+ ++len;
+ }
+ *nanos = value;
+ return data;
+}
+
+const char* ParseTimezoneOffset(const char* data, int64* offset) {
+ // Accept format "HH:MM". E.g., "08:00"
+ int hour;
+ if ((data = ParseInt(data, 2, 0, 23, &hour)) == NULL) {
+ return NULL;
+ }
+ if (*data++ != ':') {
+ return NULL;
+ }
+ int minute;
+ if ((data = ParseInt(data, 2, 0, 59, &minute)) == NULL) {
+ return NULL;
+ }
+ *offset = (hour * 60 + minute) * 60;
+ return data;
+}
+} // namespace
+
+bool SecondsToDateTime(int64 seconds, DateTime* time) {
+ if (seconds < kMinTime || seconds > kMaxTime) {
+ return false;
+ }
+ // It's easier to calcuate the DateTime starting from 0001-01-01T00:00:00
+ seconds = seconds + kSecondsFromEraToEpoch;
+ int year = 1;
+ if (seconds >= kSecondsPer400Years) {
+ int count_400years = seconds / kSecondsPer400Years;
+ year += 400 * count_400years;
+ seconds %= kSecondsPer400Years;
+ }
+ while (seconds >= SecondsPer100Years(year)) {
+ seconds -= SecondsPer100Years(year);
+ year += 100;
+ }
+ while (seconds >= SecondsPer4Years(year)) {
+ seconds -= SecondsPer4Years(year);
+ year += 4;
+ }
+ while (seconds >= SecondsPerYear(year)) {
+ seconds -= SecondsPerYear(year);
+ year += 1;
+ }
+ bool leap = IsLeapYear(year);
+ int month = 1;
+ while (seconds >= SecondsPerMonth(month, leap)) {
+ seconds -= SecondsPerMonth(month, leap);
+ ++month;
+ }
+ int day = 1 + seconds / kSecondsPerDay;
+ seconds %= kSecondsPerDay;
+ int hour = seconds / kSecondsPerHour;
+ seconds %= kSecondsPerHour;
+ int minute = seconds / kSecondsPerMinute;
+ seconds %= kSecondsPerMinute;
+ time->year = year;
+ time->month = month;
+ time->day = day;
+ time->hour = hour;
+ time->minute = minute;
+ time->second = static_cast<int>(seconds);
+ return true;
+}
+
+bool DateTimeToSeconds(const DateTime& time, int64* seconds) {
+ if (!ValidateDateTime(time)) {
+ return false;
+ }
+ *seconds = SecondsSinceCommonEra(time) - kSecondsFromEraToEpoch;
+ return true;
+}
+
+void GetCurrentTime(int64* seconds, int32* nanos) {
+ // TODO(xiaofeng): Improve the accuracy of this implementation (or just
+ // remove this method from protobuf).
+ *seconds = time(NULL);
+ *nanos = 0;
+}
+
+string FormatTime(int64 seconds, int32 nanos) {
+ DateTime time;
+ if (nanos < 0 || nanos > 999999999 || !SecondsToDateTime(seconds, &time)) {
+ return "InvalidTime";
+ }
+ string result = StringPrintf("%04d-%02d-%02dT%02d:%02d:%02d",
+ time.year, time.month, time.day,
+ time.hour, time.minute, time.second);
+ if (nanos != 0) {
+ result += "." + FormatNanos(nanos);
+ }
+ return result + "Z";
+}
+
+bool ParseTime(const string& value, int64* seconds, int32* nanos) {
+ DateTime time;
+ const char* data = value.c_str();
+ // We only accept:
+ // Z-normalized: 2015-05-20T13:29:35.120Z
+ // With UTC offset: 2015-05-20T13:29:35.120-08:00
+
+ // Parse year
+ if ((data = ParseInt(data, 4, 1, 9999, &time.year)) == NULL) {
+ return false;
+ }
+ // Expect '-'
+ if (*data++ != '-') return false;
+ // Parse month
+ if ((data = ParseInt(data, 2, 1, 12, &time.month)) == NULL) {
+ return false;
+ }
+ // Expect '-'
+ if (*data++ != '-') return false;
+ // Parse day
+ if ((data = ParseInt(data, 2, 1, 31, &time.day)) == NULL) {
+ return false;
+ }
+ // Expect 'T'
+ if (*data++ != 'T') return false;
+ // Parse hour
+ if ((data = ParseInt(data, 2, 0, 23, &time.hour)) == NULL) {
+ return false;
+ }
+ // Expect ':'
+ if (*data++ != ':') return false;
+ // Parse minute
+ if ((data = ParseInt(data, 2, 0, 59, &time.minute)) == NULL) {
+ return false;
+ }
+ // Expect ':'
+ if (*data++ != ':') return false;
+ // Parse second
+ if ((data = ParseInt(data, 2, 0, 59, &time.second)) == NULL) {
+ return false;
+ }
+ if (!DateTimeToSeconds(time, seconds)) {
+ return false;
+ }
+ // Parse nanoseconds.
+ if (*data == '.') {
+ ++data;
+ // Parse nanoseconds.
+ if ((data = ParseNanos(data, nanos)) == NULL) {
+ return false;
+ }
+ } else {
+ *nanos = 0;
+ }
+ // Parse UTC offsets.
+ if (*data == 'Z') {
+ ++data;
+ } else if (*data == '+') {
+ ++data;
+ int64 offset;
+ if ((data = ParseTimezoneOffset(data, &offset)) == NULL) {
+ return false;
+ }
+ *seconds -= offset;
+ } else if (*data == '-') {
+ ++data;
+ int64 offset;
+ if ((data = ParseTimezoneOffset(data, &offset)) == NULL) {
+ return false;
+ }
+ *seconds += offset;
+ } else {
+ return false;
+ }
+ // Done with parsing.
+ return *data == 0;
+}
+
+} // namespace internal
+} // namespace protobuf
+} // namespace google
diff --git a/src/google/protobuf/stubs/time.h b/src/google/protobuf/stubs/time.h
new file mode 100644
index 00000000..20a6b56d
--- /dev/null
+++ b/src/google/protobuf/stubs/time.h
@@ -0,0 +1,75 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#ifndef GOOGLE_PROTOBUF_STUBS_TIME_H_
+#define GOOGLE_PROTOBUF_STUBS_TIME_H_
+
+#include <google/protobuf/stubs/common.h>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+
+struct DateTime {
+ int year;
+ int month;
+ int day;
+ int hour;
+ int minute;
+ int second;
+};
+
+// Converts a timestamp (seconds elapsed since 1970-01-01T00:00:00, could be
+// negative to represent time before 1970-01-01) to DateTime. Returns false
+// if the timestamp is not in the range between 0001-01-01T00:00:00 and
+// 9999-12-31T23:59:59.
+bool LIBPROTOBUF_EXPORT SecondsToDateTime(int64 seconds, DateTime* time);
+// Converts DateTime to a timestamp (seconds since 1970-01-01T00:00:00).
+// Returns false if the DateTime is not valid or is not in the valid range.
+bool LIBPROTOBUF_EXPORT DateTimeToSeconds(const DateTime& time, int64* seconds);
+
+void LIBPROTOBUF_EXPORT GetCurrentTime(int64* seconds, int32* nanos);
+
+// Formats a time string in RFC3339 fromat.
+//
+// For example, "2015-05-20T13:29:35.120Z". For nanos, 0, 3, 6 or 9 fractional
+// digits will be used depending on how many are required to represent the exact
+// value.
+//
+// Note that "nanos" must in the range of [0, 999999999].
+string LIBPROTOBUF_EXPORT FormatTime(int64 seconds, int32 nanos);
+// Parses a time string. This method accepts RFC3339 date/time string with UTC
+// offset. For example, "2015-05-20T13:29:35.120-08:00".
+bool LIBPROTOBUF_EXPORT ParseTime(const string& vaule, int64* seconds, int32* nanos);
+
+} // namespace internal
+} // namespace protobuf
+} // namespace google
+
+#endif // GOOGLE_PROTOBUF_STUBS_TIME_H_
diff --git a/src/google/protobuf/stubs/time_test.cc b/src/google/protobuf/stubs/time_test.cc
new file mode 100644
index 00000000..59e9d1c7
--- /dev/null
+++ b/src/google/protobuf/stubs/time_test.cc
@@ -0,0 +1,208 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#include <google/protobuf/stubs/time.h>
+
+#include <google/protobuf/testing/googletest.h>
+#include <gtest/gtest.h>
+
+namespace google {
+namespace protobuf {
+namespace internal {
+namespace {
+static const int64 kSecondsPerDay = 3600 * 24;
+
+// For DateTime, tests will mostly focuse on the date part because that's
+// the tricky one.
+int64 CreateTimestamp(int year, int month, int day) {
+ DateTime time;
+ time.year = year;
+ time.month = month;
+ time.day = day;
+ time.hour = time.minute = time.second = 0;
+ int64 result;
+ GOOGLE_CHECK(DateTimeToSeconds(time, &result));
+ // Check that a roundtrip produces the same result.
+ GOOGLE_CHECK(SecondsToDateTime(result, &time));
+ GOOGLE_CHECK(time.year == year);
+ GOOGLE_CHECK(time.month == month);
+ GOOGLE_CHECK(time.day == day);
+ return result;
+}
+
+TEST(DateTimeTest, SimpleTime) {
+ DateTime time;
+ ASSERT_TRUE(SecondsToDateTime(1, &time));
+ EXPECT_EQ(1970, time.year);
+ EXPECT_EQ(1, time.month);
+ EXPECT_EQ(1, time.day);
+ EXPECT_EQ(0, time.hour);
+ EXPECT_EQ(0, time.minute);
+ EXPECT_EQ(1, time.second);
+ int64 seconds;
+ ASSERT_TRUE(DateTimeToSeconds(time, &seconds));
+ EXPECT_EQ(1, seconds);
+
+ ASSERT_TRUE(SecondsToDateTime(-1, &time));
+ EXPECT_EQ(1969, time.year);
+ EXPECT_EQ(12, time.month);
+ EXPECT_EQ(31, time.day);
+ EXPECT_EQ(23, time.hour);
+ EXPECT_EQ(59, time.minute);
+ EXPECT_EQ(59, time.second);
+ ASSERT_TRUE(DateTimeToSeconds(time, &seconds));
+ EXPECT_EQ(-1, seconds);
+
+ DateTime start, end;
+ start.year = 1;
+ start.month = 1;
+ start.day = 1;
+ start.hour = 0;
+ start.minute = 0;
+ start.second = 0;
+ end.year = 9999;
+ end.month = 12;
+ end.day = 31;
+ end.hour = 23;
+ end.minute = 59;
+ end.second = 59;
+ int64 start_time, end_time;
+ ASSERT_TRUE(DateTimeToSeconds(start, &start_time));
+ ASSERT_TRUE(DateTimeToSeconds(end, &end_time));
+ EXPECT_EQ(315537897599LL, end_time - start_time);
+ ASSERT_TRUE(SecondsToDateTime(start_time, &time));
+ ASSERT_TRUE(DateTimeToSeconds(time, &seconds));
+ EXPECT_EQ(start_time, seconds);
+ ASSERT_TRUE(SecondsToDateTime(end_time, &time));
+ ASSERT_TRUE(DateTimeToSeconds(time, &seconds));
+ EXPECT_EQ(end_time, seconds);
+}
+
+TEST(DateTimeTest, DayInMonths) {
+ // Check that month boundaries are handled correctly.
+ EXPECT_EQ(kSecondsPerDay,
+ CreateTimestamp(2015, 1, 1) - CreateTimestamp(2014, 12, 31));
+ EXPECT_EQ(kSecondsPerDay,
+ CreateTimestamp(2015, 2, 1) - CreateTimestamp(2015, 1, 31));
+ EXPECT_EQ(kSecondsPerDay,
+ CreateTimestamp(2015, 3, 1) - CreateTimestamp(2015, 2, 28));
+ EXPECT_EQ(kSecondsPerDay,
+ CreateTimestamp(2015, 4, 1) - CreateTimestamp(2015, 3, 31));
+ EXPECT_EQ(kSecondsPerDay,
+ CreateTimestamp(2015, 5, 1) - CreateTimestamp(2015, 4, 30));
+ EXPECT_EQ(kSecondsPerDay,
+ CreateTimestamp(2015, 6, 1) - CreateTimestamp(2015, 5, 31));
+ EXPECT_EQ(kSecondsPerDay,
+ CreateTimestamp(2015, 7, 1) - CreateTimestamp(2015, 6, 30));
+ EXPECT_EQ(kSecondsPerDay,
+ CreateTimestamp(2015, 8, 1) - CreateTimestamp(2015, 7, 31));
+ EXPECT_EQ(kSecondsPerDay,
+ CreateTimestamp(2015, 9, 1) - CreateTimestamp(2015, 8, 31));
+ EXPECT_EQ(kSecondsPerDay,
+ CreateTimestamp(2015, 10, 1) - CreateTimestamp(2015, 9, 30));
+ EXPECT_EQ(kSecondsPerDay,
+ CreateTimestamp(2015, 11, 1) - CreateTimestamp(2015, 10, 31));
+ EXPECT_EQ(kSecondsPerDay,
+ CreateTimestamp(2015, 12, 1) - CreateTimestamp(2015, 11, 30));
+ EXPECT_EQ(kSecondsPerDay,
+ CreateTimestamp(2016, 1, 1) - CreateTimestamp(2015, 12, 31));
+}
+
+TEST(DateTimeTest, LeapYear) {
+ // Non-leap year.
+ EXPECT_EQ(kSecondsPerDay,
+ CreateTimestamp(2015, 3, 1) - CreateTimestamp(2015, 2, 28));
+ // Leap year.
+ EXPECT_EQ(kSecondsPerDay,
+ CreateTimestamp(2016, 3, 1) - CreateTimestamp(2016, 2, 29));
+ // Non-leap year.
+ EXPECT_EQ(kSecondsPerDay,
+ CreateTimestamp(2100, 3, 1) - CreateTimestamp(2100, 2, 28));
+ // Leap year.
+ EXPECT_EQ(kSecondsPerDay,
+ CreateTimestamp(2400, 3, 1) - CreateTimestamp(2400, 2, 29));
+}
+
+TEST(DateTimeTest, StringFormat) {
+ DateTime start, end;
+ start.year = 1;
+ start.month = 1;
+ start.day = 1;
+ start.hour = 0;
+ start.minute = 0;
+ start.second = 0;
+ end.year = 9999;
+ end.month = 12;
+ end.day = 31;
+ end.hour = 23;
+ end.minute = 59;
+ end.second = 59;
+ int64 start_time, end_time;
+ ASSERT_TRUE(DateTimeToSeconds(start, &start_time));
+ ASSERT_TRUE(DateTimeToSeconds(end, &end_time));
+
+ EXPECT_EQ("0001-01-01T00:00:00Z", FormatTime(start_time, 0));
+ EXPECT_EQ("9999-12-31T23:59:59Z", FormatTime(end_time, 0));
+
+ // Make sure the nanoseconds part is formated correctly.
+ EXPECT_EQ("1970-01-01T00:00:00.010Z", FormatTime(0, 10000000));
+ EXPECT_EQ("1970-01-01T00:00:00.000010Z", FormatTime(0, 10000));
+ EXPECT_EQ("1970-01-01T00:00:00.000000010Z", FormatTime(0, 10));
+}
+
+TEST(DateTimeTest, ParseString) {
+ int64 seconds;
+ int32 nanos;
+ ASSERT_TRUE(ParseTime("0001-01-01T00:00:00Z", &seconds, &nanos));
+ EXPECT_EQ("0001-01-01T00:00:00Z", FormatTime(seconds, nanos));
+ ASSERT_TRUE(ParseTime("9999-12-31T23:59:59.999999999Z", &seconds, &nanos));
+ EXPECT_EQ("9999-12-31T23:59:59.999999999Z", FormatTime(seconds, nanos));
+
+ // Test time zone offsets.
+ ASSERT_TRUE(ParseTime("1970-01-01T00:00:00-08:00", &seconds, &nanos));
+ EXPECT_EQ("1970-01-01T08:00:00Z", FormatTime(seconds, nanos));
+ ASSERT_TRUE(ParseTime("1970-01-01T00:00:00+08:00", &seconds, &nanos));
+ EXPECT_EQ("1969-12-31T16:00:00Z", FormatTime(seconds, nanos));
+
+ // Test nanoseconds.
+ ASSERT_TRUE(ParseTime("1970-01-01T00:00:00.01Z", &seconds, &nanos));
+ EXPECT_EQ("1970-01-01T00:00:00.010Z", FormatTime(seconds, nanos));
+ ASSERT_TRUE(ParseTime("1970-01-01T00:00:00.00001-08:00", &seconds, &nanos));
+ EXPECT_EQ("1970-01-01T08:00:00.000010Z", FormatTime(seconds, nanos));
+ ASSERT_TRUE(ParseTime("1970-01-01T00:00:00.00000001+08:00", &seconds, &nanos));
+ EXPECT_EQ("1969-12-31T16:00:00.000000010Z", FormatTime(seconds, nanos));
+ // Fractional parts less than 1 nanosecond will be ignored.
+ ASSERT_TRUE(ParseTime("1970-01-01T00:00:00.0123456789Z", &seconds, &nanos));
+ EXPECT_EQ("1970-01-01T00:00:00.012345678Z", FormatTime(seconds, nanos));
+}
+
+} // namespace
+} // namespace internal
+} // namespace protobuf
+} // namespace google