diff options
Diffstat (limited to 'src/google/protobuf/stubs')
25 files changed, 6449 insertions, 48 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..c9c9a76e --- /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 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 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 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 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 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 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 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 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 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 54e00ccb..b895d915 100644 --- a/src/google/protobuf/stubs/common.cc +++ b/src/google/protobuf/stubs/common.cc @@ -32,6 +32,8 @@ #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 <stdio.h> #include <errno.h> #include <vector> @@ -148,6 +150,17 @@ LogMessage& LogMessage::operator<<(const char* value) { return *this; } +LogMessage& LogMessage::operator<<(const StringPiece& value) { + message_ += value.ToString(); + 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(). diff --git a/src/google/protobuf/stubs/common.h b/src/google/protobuf/stubs/common.h index 49d16020..34d79f48 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 + #if defined(_M_IX86) && \ + !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; @@ -678,6 +708,8 @@ class LIBPROTOBUF_EXPORT LogMessage { LogMessage& operator<<(unsigned 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 +728,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 +754,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 +771,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 +798,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 +811,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 +923,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 +1085,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 +1260,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 +1422,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..39957d69 --- /dev/null +++ b/src/google/protobuf/stubs/mathlimits.h @@ -0,0 +1,277 @@ +// 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> + +// ========================================================================= // + +// 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 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 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 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..9a68fad4 --- /dev/null +++ b/src/google/protobuf/stubs/status.cc @@ -0,0 +1,133 @@ +// 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 <stdint.h> +#include <stdio.h> +#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..34d6cf12 --- /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 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'. +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..9495107e --- /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 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..5d4d5fe4 --- /dev/null +++ b/src/google/protobuf/stubs/stringpiece.h @@ -0,0 +1,437 @@ +// 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 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 + 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..3e849324 100644 --- a/src/google/protobuf/stubs/strutil_unittest.cc +++ b/src/google/protobuf/stubs/strutil_unittest.cc @@ -32,9 +32,11 @@ #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> namespace google { namespace protobuf { @@ -68,6 +70,738 @@ 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? + EXPECT_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..73b99af6 --- /dev/null +++ b/src/google/protobuf/stubs/time.cc @@ -0,0 +1,364 @@ +#include <google/protobuf/stubs/time.h> + +#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..0f641dc9 --- /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 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 DateTimeToSeconds(const DateTime& time, int64* seconds); + +void 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 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 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 |