[ci skip] Change-Id: I9391c09640e0b0b2b21c45a97a1fc91814d95c5dpull/501/head
parent
4cbb612299
commit
e96ff30120
101 changed files with 28226 additions and 198 deletions
@ -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
|
@ -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_
|
@ -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
|
@ -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
|
@ -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__
|
@ -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_
|
@ -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
|
@ -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_
|
@ -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_
|
@ -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
|
@ -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
|
@ -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_
|
@ -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
|
@ -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
|
@ -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_
|
@ -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
|
@ -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
|
@ -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_
|
@ -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
|
@ -0,0 +1,187 @@ |
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// Author: ksroka@google.com (Krzysztof Sroka)
|
||||||
|
|
||||||
|
#include <google/protobuf/util/field_comparator.h> |
||||||
|
|
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include <google/protobuf/descriptor.h> |
||||||
|
#include <google/protobuf/message.h> |
||||||
|
#include <google/protobuf/stubs/map_util.h> |
||||||
|
#include <google/protobuf/stubs/mathlimits.h> |
||||||
|
#include <google/protobuf/stubs/mathutil.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
|
||||||
|
FieldComparator::FieldComparator() {} |
||||||
|
FieldComparator::~FieldComparator() {} |
||||||
|
|
||||||
|
DefaultFieldComparator::DefaultFieldComparator() |
||||||
|
: float_comparison_(EXACT), |
||||||
|
treat_nan_as_equal_(false), |
||||||
|
has_default_tolerance_(false) { |
||||||
|
} |
||||||
|
|
||||||
|
DefaultFieldComparator::~DefaultFieldComparator() {} |
||||||
|
|
||||||
|
FieldComparator::ComparisonResult DefaultFieldComparator::Compare( |
||||||
|
const google::protobuf::Message& message_1, |
||||||
|
const google::protobuf::Message& message_2, |
||||||
|
const google::protobuf::FieldDescriptor* field, |
||||||
|
int index_1, int index_2, |
||||||
|
const google::protobuf::util::FieldContext* field_context) { |
||||||
|
const Reflection* reflection_1 = message_1.GetReflection(); |
||||||
|
const Reflection* reflection_2 = message_2.GetReflection(); |
||||||
|
|
||||||
|
switch (field->cpp_type()) { |
||||||
|
#define COMPARE_FIELD(METHOD) \ |
||||||
|
if (field->is_repeated()) { \
|
||||||
|
return ResultFromBoolean(Compare##METHOD( \
|
||||||
|
*field, \
|
||||||
|
reflection_1->GetRepeated##METHOD(message_1, field, index_1), \
|
||||||
|
reflection_2->GetRepeated##METHOD(message_2, field, index_2))); \
|
||||||
|
} else { \
|
||||||
|
return ResultFromBoolean(Compare##METHOD( \
|
||||||
|
*field, \
|
||||||
|
reflection_1->Get##METHOD(message_1, field), \
|
||||||
|
reflection_2->Get##METHOD(message_2, field))); \
|
||||||
|
} \
|
||||||
|
break; // Make sure no fall-through is introduced.
|
||||||
|
|
||||||
|
case FieldDescriptor::CPPTYPE_BOOL: |
||||||
|
COMPARE_FIELD(Bool); |
||||||
|
case FieldDescriptor::CPPTYPE_DOUBLE: |
||||||
|
COMPARE_FIELD(Double); |
||||||
|
case FieldDescriptor::CPPTYPE_ENUM: |
||||||
|
COMPARE_FIELD(Enum); |
||||||
|
case FieldDescriptor::CPPTYPE_FLOAT: |
||||||
|
COMPARE_FIELD(Float); |
||||||
|
case FieldDescriptor::CPPTYPE_INT32: |
||||||
|
COMPARE_FIELD(Int32); |
||||||
|
case FieldDescriptor::CPPTYPE_INT64: |
||||||
|
COMPARE_FIELD(Int64); |
||||||
|
case FieldDescriptor::CPPTYPE_STRING: |
||||||
|
COMPARE_FIELD(String); |
||||||
|
case FieldDescriptor::CPPTYPE_UINT32: |
||||||
|
COMPARE_FIELD(UInt32); |
||||||
|
case FieldDescriptor::CPPTYPE_UINT64: |
||||||
|
COMPARE_FIELD(UInt64); |
||||||
|
|
||||||
|
#undef COMPARE_FIELD |
||||||
|
|
||||||
|
case FieldDescriptor::CPPTYPE_MESSAGE: |
||||||
|
return RECURSE; |
||||||
|
|
||||||
|
default: |
||||||
|
GOOGLE_LOG(FATAL) << "No comparison code for field " << field->full_name() |
||||||
|
<< " of CppType = " << field->cpp_type(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void DefaultFieldComparator::SetDefaultFractionAndMargin(double fraction, |
||||||
|
double margin) { |
||||||
|
default_tolerance_ = Tolerance(fraction, margin); |
||||||
|
has_default_tolerance_ = true; |
||||||
|
} |
||||||
|
|
||||||
|
void DefaultFieldComparator::SetFractionAndMargin(const FieldDescriptor* field, |
||||||
|
double fraction, |
||||||
|
double margin) { |
||||||
|
GOOGLE_CHECK(FieldDescriptor::CPPTYPE_FLOAT == field->cpp_type() || |
||||||
|
FieldDescriptor::CPPTYPE_DOUBLE == field->cpp_type()) |
||||||
|
<< "Field has to be float or double type. Field name is: " |
||||||
|
<< field->full_name(); |
||||||
|
map_tolerance_[field] = Tolerance(fraction, margin); |
||||||
|
} |
||||||
|
|
||||||
|
bool DefaultFieldComparator::CompareDouble(const FieldDescriptor& field, |
||||||
|
double value_1, double value_2) { |
||||||
|
return CompareDoubleOrFloat(field, value_1, value_2); |
||||||
|
} |
||||||
|
|
||||||
|
bool DefaultFieldComparator::CompareEnum(const FieldDescriptor& field, |
||||||
|
const EnumValueDescriptor* value_1, |
||||||
|
const EnumValueDescriptor* value_2) { |
||||||
|
return value_1->number() == value_2->number(); |
||||||
|
} |
||||||
|
|
||||||
|
bool DefaultFieldComparator::CompareFloat(const FieldDescriptor& field, |
||||||
|
float value_1, float value_2) { |
||||||
|
return CompareDoubleOrFloat(field, value_1, value_2); |
||||||
|
} |
||||||
|
|
||||||
|
template<typename T> |
||||||
|
bool DefaultFieldComparator::CompareDoubleOrFloat(const FieldDescriptor& field, |
||||||
|
T value_1, T value_2) { |
||||||
|
if (value_1 == value_2) { |
||||||
|
// Covers +inf and -inf (which are not within margin or fraction of
|
||||||
|
// themselves), and is a shortcut for finite values.
|
||||||
|
return true; |
||||||
|
} else if (float_comparison_ == EXACT) { |
||||||
|
if (treat_nan_as_equal_ && |
||||||
|
MathLimits<T>::IsNaN(value_1) && MathLimits<T>::IsNaN(value_2)) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} else { |
||||||
|
if (treat_nan_as_equal_ && |
||||||
|
MathLimits<T>::IsNaN(value_1) && MathLimits<T>::IsNaN(value_2)) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
// float_comparison_ == APPROXIMATE covers two use cases.
|
||||||
|
Tolerance* tolerance = FindOrNull(map_tolerance_, &field); |
||||||
|
if (tolerance == NULL && has_default_tolerance_) { |
||||||
|
tolerance = &default_tolerance_; |
||||||
|
} |
||||||
|
if (tolerance == NULL) { |
||||||
|
return MathUtil::AlmostEquals(value_1, value_2); |
||||||
|
} else { |
||||||
|
// Use user-provided fraction and margin. Since they are stored as
|
||||||
|
// doubles, we explicitely cast them to types of values provided. This
|
||||||
|
// is very likely to fail if provided values are not numeric.
|
||||||
|
return MathUtil::WithinFractionOrMargin( |
||||||
|
value_1, value_2, static_cast<T>(tolerance->fraction), |
||||||
|
static_cast<T>(tolerance->margin)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
FieldComparator::ComparisonResult DefaultFieldComparator::ResultFromBoolean( |
||||||
|
bool boolean_result) const { |
||||||
|
return boolean_result ? FieldComparator::SAME : FieldComparator::DIFFERENT; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
} // namespace google
|
@ -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.
|
||||||
|
|
||||||
|
// Author: ksroka@google.com (Krzysztof Sroka)
|
||||||
|
|
||||||
|
#ifndef GOOGLE_PROTOBUF_UTIL_FIELD_COMPARATOR_H__ |
||||||
|
#define GOOGLE_PROTOBUF_UTIL_FIELD_COMPARATOR_H__ |
||||||
|
|
||||||
|
#include <map> |
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include <google/protobuf/stubs/common.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
|
||||||
|
class Message; |
||||||
|
class EnumValueDescriptor; |
||||||
|
class FieldDescriptor; |
||||||
|
|
||||||
|
namespace util { |
||||||
|
|
||||||
|
struct FieldContext; |
||||||
|
|
||||||
|
// Base class specifying the interface for comparing protocol buffer fields.
|
||||||
|
// Regular users should consider using or subclassing DefaultFieldComparator
|
||||||
|
// rather than this interface.
|
||||||
|
// Currently, this does not support comparing unknown fields.
|
||||||
|
class LIBPROTOBUF_EXPORT FieldComparator { |
||||||
|
public: |
||||||
|
FieldComparator(); |
||||||
|
virtual ~FieldComparator(); |
||||||
|
|
||||||
|
enum ComparisonResult { |
||||||
|
SAME, // Compared fields are equal. In case of comparing submessages,
|
||||||
|
// user should not recursively compare their contents.
|
||||||
|
DIFFERENT, // Compared fields are different. In case of comparing
|
||||||
|
// submessages, user should not recursively compare their
|
||||||
|
// contents.
|
||||||
|
RECURSE, // Compared submessages need to be compared recursively.
|
||||||
|
// FieldComparator does not specify the semantics of recursive
|
||||||
|
// comparison. This value should not be returned for simple
|
||||||
|
// values.
|
||||||
|
}; |
||||||
|
|
||||||
|
// Compares the values of a field in two protocol buffer messages.
|
||||||
|
// Returns SAME or DIFFERENT for simple values, and SAME, DIFFERENT or RECURSE
|
||||||
|
// for submessages. Returning RECURSE for fields not being submessages is
|
||||||
|
// illegal.
|
||||||
|
// In case the given FieldDescriptor points to a repeated field, the indices
|
||||||
|
// need to be valid. Otherwise they should be ignored.
|
||||||
|
//
|
||||||
|
// FieldContext contains information about the specific instances of the
|
||||||
|
// fields being compared, versus FieldDescriptor which only contains general
|
||||||
|
// type information about the fields.
|
||||||
|
virtual ComparisonResult Compare( |
||||||
|
const google::protobuf::Message& message_1, |
||||||
|
const google::protobuf::Message& message_2, |
||||||
|
const google::protobuf::FieldDescriptor* field, |
||||||
|
int index_1, int index_2, |
||||||
|
const google::protobuf::util::FieldContext* field_context) = 0; |
||||||
|
|
||||||
|
private: |
||||||
|
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldComparator); |
||||||
|
}; |
||||||
|
|
||||||
|
// Basic implementation of FieldComparator. Supports four modes of floating
|
||||||
|
// point value comparison: exact, approximate using MathUtil::AlmostEqual
|
||||||
|
// method, and arbitrarilly precise using MathUtil::WithinFracionOrMargin.
|
||||||
|
class LIBPROTOBUF_EXPORT DefaultFieldComparator : public FieldComparator { |
||||||
|
public: |
||||||
|
enum FloatComparison { |
||||||
|
EXACT, // Floats and doubles are compared exactly.
|
||||||
|
APPROXIMATE, // Floats and doubles are compared using the
|
||||||
|
// MathUtil::AlmostEqual method or
|
||||||
|
// MathUtil::WithinFractionOrMargin method.
|
||||||
|
// TODO(ksroka): Introduce third value to differenciate uses of AlmostEqual
|
||||||
|
// and WithinFractionOrMargin.
|
||||||
|
}; |
||||||
|
|
||||||
|
// Creates new comparator with float comparison set to EXACT.
|
||||||
|
DefaultFieldComparator(); |
||||||
|
|
||||||
|
virtual ~DefaultFieldComparator(); |
||||||
|
|
||||||
|
virtual ComparisonResult Compare( |
||||||
|
const google::protobuf::Message& message_1, |
||||||
|
const google::protobuf::Message& message_2, |
||||||
|
const google::protobuf::FieldDescriptor* field, |
||||||
|
int index_1, int index_2, |
||||||
|
const google::protobuf::util::FieldContext* field_context); |
||||||
|
|
||||||
|
void set_float_comparison(FloatComparison float_comparison) { |
||||||
|
float_comparison_ = float_comparison; |
||||||
|
} |
||||||
|
|
||||||
|
FloatComparison float_comparison() const { |
||||||
|
return float_comparison_; |
||||||
|
} |
||||||
|
|
||||||
|
// Set whether the FieldComparator shall treat floats or doubles that are both
|
||||||
|
// NaN as equal (treat_nan_as_equal = true) or as different
|
||||||
|
// (treat_nan_as_equal = false). Default is treating NaNs always as different.
|
||||||
|
void set_treat_nan_as_equal(bool treat_nan_as_equal) { |
||||||
|
treat_nan_as_equal_ = treat_nan_as_equal; |
||||||
|
} |
||||||
|
|
||||||
|
bool treat_nan_as_equal() const { |
||||||
|
return treat_nan_as_equal_; |
||||||
|
} |
||||||
|
|
||||||
|
// Sets the fraction and margin for the float comparison of a given field.
|
||||||
|
// Uses MathUtil::WithinFractionOrMargin to compare the values.
|
||||||
|
//
|
||||||
|
// REQUIRES: field->cpp_type == FieldDescriptor::CPPTYPE_DOUBLE or
|
||||||
|
// field->cpp_type == FieldDescriptor::CPPTYPE_FLOAT
|
||||||
|
// REQUIRES: float_comparison_ == APPROXIMATE
|
||||||
|
void SetFractionAndMargin(const FieldDescriptor* field, double fraction, |
||||||
|
double margin); |
||||||
|
|
||||||
|
// Sets the fraction and margin for the float comparison of all float and
|
||||||
|
// double fields, unless a field has been given a specific setting via
|
||||||
|
// SetFractionAndMargin() above.
|
||||||
|
// Uses MathUtil::WithinFractionOrMargin to compare the values.
|
||||||
|
//
|
||||||
|
// REQUIRES: float_comparison_ == APPROXIMATE
|
||||||
|
void SetDefaultFractionAndMargin(double fraction, double margin); |
||||||
|
|
||||||
|
private: |
||||||
|
// Defines the tolerance for floating point comparison (fraction and margin).
|
||||||
|
struct Tolerance { |
||||||
|
double fraction; |
||||||
|
double margin; |
||||||
|
Tolerance() |
||||||
|
: fraction(0.0), |
||||||
|
margin(0.0) {} |
||||||
|
Tolerance(double f, double m) |
||||||
|
: fraction(f), |
||||||
|
margin(m) {} |
||||||
|
}; |
||||||
|
|
||||||
|
// Defines the map to store the tolerances for floating point comparison.
|
||||||
|
typedef map<const FieldDescriptor*, Tolerance> ToleranceMap; |
||||||
|
|
||||||
|
// The following methods get executed when CompareFields is called for the
|
||||||
|
// basic types (instead of submessages). They return true on success. One
|
||||||
|
// can use ResultFromBoolean() to convert that boolean to a ComparisonResult
|
||||||
|
// value.
|
||||||
|
bool CompareBool(const google::protobuf::FieldDescriptor& field, |
||||||
|
bool value_1, bool value_2) { |
||||||
|
return value_1 == value_2; |
||||||
|
} |
||||||
|
|
||||||
|
// Uses CompareDoubleOrFloat, a helper function used by both CompareDouble and
|
||||||
|
// CompareFloat.
|
||||||
|
bool CompareDouble(const google::protobuf::FieldDescriptor& field, |
||||||
|
double value_1, double value_2); |
||||||
|
|
||||||
|
bool CompareEnum(const google::protobuf::FieldDescriptor& field, |
||||||
|
const EnumValueDescriptor* value_1, |
||||||
|
const EnumValueDescriptor* value_2); |
||||||
|
|
||||||
|
// Uses CompareDoubleOrFloat, a helper function used by both CompareDouble and
|
||||||
|
// CompareFloat.
|
||||||
|
bool CompareFloat(const google::protobuf::FieldDescriptor& field, |
||||||
|
float value_1, float value_2); |
||||||
|
|
||||||
|
bool CompareInt32(const google::protobuf::FieldDescriptor& field, |
||||||
|
int32 value_1, int32 value_2) { |
||||||
|
return value_1 == value_2; |
||||||
|
} |
||||||
|
|
||||||
|
bool CompareInt64(const google::protobuf::FieldDescriptor& field, |
||||||
|
int64 value_1, int64 value_2) { |
||||||
|
return value_1 == value_2; |
||||||
|
} |
||||||
|
|
||||||
|
bool CompareString(const google::protobuf::FieldDescriptor& field, |
||||||
|
const string& value_1, const string& value_2) { |
||||||
|
return value_1 == value_2; |
||||||
|
} |
||||||
|
|
||||||
|
bool CompareUInt32(const google::protobuf::FieldDescriptor& field, |
||||||
|
uint32 value_1, uint32 value_2) { |
||||||
|
return value_1 == value_2; |
||||||
|
} |
||||||
|
|
||||||
|
bool CompareUInt64(const google::protobuf::FieldDescriptor& field, |
||||||
|
uint64 value_1, uint64 value_2) { |
||||||
|
return value_1 == value_2; |
||||||
|
} |
||||||
|
|
||||||
|
// This function is used by CompareDouble and CompareFloat to avoid code
|
||||||
|
// duplication. There are no checks done against types of the values passed,
|
||||||
|
// but it's likely to fail if passed non-numeric arguments.
|
||||||
|
template<typename T> |
||||||
|
bool CompareDoubleOrFloat(const google::protobuf::FieldDescriptor& field, |
||||||
|
T value_1, T value_2); |
||||||
|
|
||||||
|
// Returns FieldComparator::SAME if boolean_result is true and
|
||||||
|
// FieldComparator::DIFFERENT otherwise.
|
||||||
|
ComparisonResult ResultFromBoolean(bool boolean_result) const; |
||||||
|
|
||||||
|
FloatComparison float_comparison_; |
||||||
|
|
||||||
|
// If true, floats and doubles that are both NaN are considered to be
|
||||||
|
// equal. Otherwise, two floats or doubles that are NaN are considered to be
|
||||||
|
// different.
|
||||||
|
bool treat_nan_as_equal_; |
||||||
|
|
||||||
|
// True iff default_tolerance_ has been explicitly set.
|
||||||
|
//
|
||||||
|
// If false, then the default tolerance for flaots and doubles is that which
|
||||||
|
// is used by MathUtil::AlmostEquals().
|
||||||
|
bool has_default_tolerance_; |
||||||
|
|
||||||
|
// Default float/double tolerance. Only meaningful if
|
||||||
|
// has_default_tolerance_ == true.
|
||||||
|
Tolerance default_tolerance_; |
||||||
|
|
||||||
|
// Field-specific float/double tolerances, which override any default for
|
||||||
|
// those particular fields.
|
||||||
|
ToleranceMap map_tolerance_; |
||||||
|
|
||||||
|
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DefaultFieldComparator); |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
|
||||||
|
} // namespace google
|
||||||
|
#endif // GOOGLE_PROTOBUF_UTIL_FIELD_COMPARATOR_H__
|
@ -0,0 +1,483 @@ |
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// Author: ksroka@google.com (Krzysztof Sroka)
|
||||||
|
|
||||||
|
#include <google/protobuf/util/field_comparator.h> |
||||||
|
|
||||||
|
#include <google/protobuf/unittest.pb.h> |
||||||
|
#include <google/protobuf/descriptor.h> |
||||||
|
#include <gtest/gtest.h> |
||||||
|
#include <google/protobuf/stubs/mathutil.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace { |
||||||
|
|
||||||
|
using protobuf_unittest::TestAllTypes; |
||||||
|
|
||||||
|
class DefaultFieldComparatorTest : public ::testing::Test { |
||||||
|
protected: |
||||||
|
void SetUp() { |
||||||
|
descriptor_ = TestAllTypes::descriptor(); |
||||||
|
} |
||||||
|
|
||||||
|
const Descriptor* descriptor_; |
||||||
|
DefaultFieldComparator comparator_; |
||||||
|
TestAllTypes message_1_; |
||||||
|
TestAllTypes message_2_; |
||||||
|
}; |
||||||
|
|
||||||
|
TEST_F(DefaultFieldComparatorTest, RecursesIntoGroup) { |
||||||
|
const FieldDescriptor* field = |
||||||
|
descriptor_->FindFieldByName("optionalgroup"); |
||||||
|
EXPECT_EQ(FieldComparator::RECURSE, |
||||||
|
comparator_.Compare(message_1_, message_2_, field, -1, -1, NULL)); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(DefaultFieldComparatorTest, RecursesIntoNestedMessage) { |
||||||
|
const FieldDescriptor* field = |
||||||
|
descriptor_->FindFieldByName("optional_nested_message"); |
||||||
|
EXPECT_EQ(FieldComparator::RECURSE, |
||||||
|
comparator_.Compare(message_1_, message_2_, field, -1, -1, NULL)); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(DefaultFieldComparatorTest, RecursesIntoForeignMessage) { |
||||||
|
const FieldDescriptor* field = |
||||||
|
descriptor_->FindFieldByName("optional_foreign_message"); |
||||||
|
EXPECT_EQ(FieldComparator::RECURSE, |
||||||
|
comparator_.Compare(message_1_, message_2_, field, -1, -1, NULL)); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(DefaultFieldComparatorTest, Int32Comparison) { |
||||||
|
const FieldDescriptor* field = descriptor_->FindFieldByName("optional_int32"); |
||||||
|
message_1_.set_optional_int32(1); |
||||||
|
message_2_.set_optional_int32(1); |
||||||
|
|
||||||
|
EXPECT_EQ(FieldComparator::SAME, |
||||||
|
comparator_.Compare(message_1_, message_2_, field, -1, -1, NULL)); |
||||||
|
|
||||||
|
message_2_.set_optional_int32(-1); |
||||||
|
EXPECT_EQ(FieldComparator::DIFFERENT, |
||||||
|
comparator_.Compare(message_1_, message_2_, field, -1, -1, NULL)); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(DefaultFieldComparatorTest, Int64Comparison) { |
||||||
|
const FieldDescriptor* field = descriptor_->FindFieldByName("optional_int64"); |
||||||
|
message_1_.set_optional_int64(1L); |
||||||
|
message_2_.set_optional_int64(1L); |
||||||
|
|
||||||
|
EXPECT_EQ(FieldComparator::SAME, |
||||||
|
comparator_.Compare(message_1_, message_2_, field, -1, -1, NULL)); |
||||||
|
|
||||||
|
message_2_.set_optional_int64(-1L); |
||||||
|
EXPECT_EQ(FieldComparator::DIFFERENT, |
||||||
|
comparator_.Compare(message_1_, message_2_, field, -1, -1, NULL)); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(DefaultFieldComparatorTest, UInt32Comparison) { |
||||||
|
const FieldDescriptor* field = |
||||||
|
descriptor_->FindFieldByName("optional_uint32"); |
||||||
|
message_1_.set_optional_uint32(1); |
||||||
|
message_2_.set_optional_uint32(1); |
||||||
|
|
||||||
|
EXPECT_EQ(FieldComparator::SAME, |
||||||
|
comparator_.Compare(message_1_, message_2_, field, -1, -1, NULL)); |
||||||
|
|
||||||
|
message_2_.set_optional_uint32(2); |
||||||
|
EXPECT_EQ(FieldComparator::DIFFERENT, |
||||||
|
comparator_.Compare(message_1_, message_2_, field, -1, -1, NULL)); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(DefaultFieldComparatorTest, UInt64Comparison) { |
||||||
|
const FieldDescriptor* field = |
||||||
|
descriptor_->FindFieldByName("optional_uint64"); |
||||||
|
message_1_.set_optional_uint64(1L); |
||||||
|
message_2_.set_optional_uint64(1L); |
||||||
|
|
||||||
|
EXPECT_EQ(FieldComparator::SAME, |
||||||
|
comparator_.Compare(message_1_, message_2_, field, -1, -1, NULL)); |
||||||
|
|
||||||
|
message_2_.set_optional_uint64(2L); |
||||||
|
EXPECT_EQ(FieldComparator::DIFFERENT, |
||||||
|
comparator_.Compare(message_1_, message_2_, field, -1, -1, NULL)); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(DefaultFieldComparatorTest, BooleanComparison) { |
||||||
|
const FieldDescriptor* field = |
||||||
|
descriptor_->FindFieldByName("optional_bool"); |
||||||
|
message_1_.set_optional_bool(true); |
||||||
|
message_2_.set_optional_bool(true); |
||||||
|
|
||||||
|
EXPECT_EQ(FieldComparator::SAME, |
||||||
|
comparator_.Compare(message_1_, message_2_, field, -1, -1, NULL)); |
||||||
|
|
||||||
|
message_2_.set_optional_bool(false); |
||||||
|
EXPECT_EQ(FieldComparator::DIFFERENT, |
||||||
|
comparator_.Compare(message_1_, message_2_, field, -1, -1, NULL)); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(DefaultFieldComparatorTest, EnumComparison) { |
||||||
|
const FieldDescriptor* field = |
||||||
|
descriptor_->FindFieldByName("optional_nested_enum"); |
||||||
|
message_1_.set_optional_nested_enum(TestAllTypes::BAR); |
||||||
|
message_2_.set_optional_nested_enum(TestAllTypes::BAR); |
||||||
|
|
||||||
|
EXPECT_EQ(FieldComparator::SAME, |
||||||
|
comparator_.Compare(message_1_, message_2_, field, -1, -1, NULL)); |
||||||
|
|
||||||
|
message_2_.set_optional_nested_enum(TestAllTypes::BAZ); |
||||||
|
EXPECT_EQ(FieldComparator::DIFFERENT, |
||||||
|
comparator_.Compare(message_1_, message_2_, field, -1, -1, NULL)); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(DefaultFieldComparatorTest, StringComparison) { |
||||||
|
const FieldDescriptor* field = |
||||||
|
descriptor_->FindFieldByName("optional_string"); |
||||||
|
message_1_.set_optional_string("foo"); |
||||||
|
message_2_.set_optional_string("foo"); |
||||||
|
|
||||||
|
EXPECT_EQ(FieldComparator::SAME, |
||||||
|
comparator_.Compare(message_1_, message_2_, field, -1, -1, NULL)); |
||||||
|
|
||||||
|
message_2_.set_optional_string("bar"); |
||||||
|
EXPECT_EQ(FieldComparator::DIFFERENT, |
||||||
|
comparator_.Compare(message_1_, message_2_, field, -1, -1, NULL)); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(DefaultFieldComparatorTest, FloatingPointComparisonExact) { |
||||||
|
const FieldDescriptor* field_float = |
||||||
|
descriptor_->FindFieldByName("optional_float"); |
||||||
|
const FieldDescriptor* field_double = |
||||||
|
descriptor_->FindFieldByName("optional_double"); |
||||||
|
|
||||||
|
message_1_.set_optional_float(0.1f); |
||||||
|
message_2_.set_optional_float(0.1f); |
||||||
|
message_1_.set_optional_double(0.1); |
||||||
|
message_2_.set_optional_double(0.1); |
||||||
|
|
||||||
|
EXPECT_EQ(FieldComparator::SAME, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_float, -1, -1, NULL)); |
||||||
|
EXPECT_EQ(FieldComparator::SAME, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_double, -1, -1, NULL)); |
||||||
|
|
||||||
|
message_2_.set_optional_float(0.2f); |
||||||
|
message_2_.set_optional_double(0.2); |
||||||
|
|
||||||
|
EXPECT_EQ(FieldComparator::DIFFERENT, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_float, -1, -1, NULL)); |
||||||
|
EXPECT_EQ(FieldComparator::DIFFERENT, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_double, -1, -1, NULL)); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(DefaultFieldComparatorTest, FloatingPointComparisonApproximate) { |
||||||
|
const FieldDescriptor* field_float = |
||||||
|
descriptor_->FindFieldByName("optional_float"); |
||||||
|
const FieldDescriptor* field_double = |
||||||
|
descriptor_->FindFieldByName("optional_double"); |
||||||
|
|
||||||
|
message_1_.set_optional_float(2.300005f); |
||||||
|
message_2_.set_optional_float(2.300006f); |
||||||
|
message_1_.set_optional_double(2.3000000000000003); |
||||||
|
message_2_.set_optional_double(2.3000000000000007); |
||||||
|
|
||||||
|
// Approximate comparison depends on MathUtil, so we assert on MathUtil
|
||||||
|
// results first to check if that's where the failure was introduced.
|
||||||
|
ASSERT_NE(message_1_.optional_float(), message_2_.optional_float()); |
||||||
|
ASSERT_NE(message_1_.optional_double(), message_2_.optional_double()); |
||||||
|
ASSERT_TRUE(MathUtil::AlmostEquals(message_1_.optional_float(), |
||||||
|
message_2_.optional_float())); |
||||||
|
ASSERT_TRUE(MathUtil::AlmostEquals(message_1_.optional_double(), |
||||||
|
message_2_.optional_double())); |
||||||
|
|
||||||
|
// DefaultFieldComparator's default float comparison mode is EXACT.
|
||||||
|
ASSERT_EQ(DefaultFieldComparator::EXACT, comparator_.float_comparison()); |
||||||
|
EXPECT_EQ(FieldComparator::DIFFERENT, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_float, -1, -1, NULL)); |
||||||
|
EXPECT_EQ(FieldComparator::DIFFERENT, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_double, -1, -1, NULL)); |
||||||
|
|
||||||
|
comparator_.set_float_comparison(DefaultFieldComparator::APPROXIMATE); |
||||||
|
|
||||||
|
EXPECT_EQ(FieldComparator::SAME, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_float, -1, -1, NULL)); |
||||||
|
EXPECT_EQ(FieldComparator::SAME, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_double, -1, -1, NULL)); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(DefaultFieldComparatorTest, FloatingPointComparisonTreatNaNsAsEqual) { |
||||||
|
const FieldDescriptor* field_float = |
||||||
|
descriptor_->FindFieldByName("optional_float"); |
||||||
|
const FieldDescriptor* field_double = |
||||||
|
descriptor_->FindFieldByName("optional_double"); |
||||||
|
|
||||||
|
message_1_.set_optional_float(MathLimits<float>::kNaN); |
||||||
|
message_2_.set_optional_float(MathLimits<float>::kNaN); |
||||||
|
message_1_.set_optional_double(MathLimits<double>::kNaN); |
||||||
|
message_2_.set_optional_double(MathLimits<double>::kNaN); |
||||||
|
|
||||||
|
// DefaultFieldComparator's default float comparison mode is EXACT with
|
||||||
|
// treating NaNs as different.
|
||||||
|
ASSERT_EQ(DefaultFieldComparator::EXACT, comparator_.float_comparison()); |
||||||
|
ASSERT_EQ(false, comparator_.treat_nan_as_equal()); |
||||||
|
EXPECT_EQ(FieldComparator::DIFFERENT, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_float, -1, -1, NULL)); |
||||||
|
EXPECT_EQ(FieldComparator::DIFFERENT, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_double, -1, -1, NULL)); |
||||||
|
comparator_.set_float_comparison(DefaultFieldComparator::APPROXIMATE); |
||||||
|
EXPECT_EQ(FieldComparator::DIFFERENT, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_float, -1, -1, NULL)); |
||||||
|
EXPECT_EQ(FieldComparator::DIFFERENT, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_double, -1, -1, NULL)); |
||||||
|
|
||||||
|
comparator_.set_treat_nan_as_equal(true); |
||||||
|
ASSERT_EQ(true, comparator_.treat_nan_as_equal()); |
||||||
|
comparator_.set_float_comparison(DefaultFieldComparator::EXACT); |
||||||
|
EXPECT_EQ(FieldComparator::SAME, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_float, -1, -1, NULL)); |
||||||
|
EXPECT_EQ(FieldComparator::SAME, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_double, -1, -1, NULL)); |
||||||
|
comparator_.set_float_comparison(DefaultFieldComparator::APPROXIMATE); |
||||||
|
EXPECT_EQ(FieldComparator::SAME, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_float, -1, -1, NULL)); |
||||||
|
EXPECT_EQ(FieldComparator::SAME, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_double, -1, -1, NULL)); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(DefaultFieldComparatorTest, |
||||||
|
FloatingPointComparisonWithinFractionOrMargin) { |
||||||
|
const FieldDescriptor* field_float = |
||||||
|
descriptor_->FindFieldByName("optional_float"); |
||||||
|
const FieldDescriptor* field_double = |
||||||
|
descriptor_->FindFieldByName("optional_double"); |
||||||
|
|
||||||
|
message_1_.set_optional_float(100.0f); |
||||||
|
message_2_.set_optional_float(109.9f); |
||||||
|
message_1_.set_optional_double(100.0); |
||||||
|
message_2_.set_optional_double(109.9); |
||||||
|
|
||||||
|
comparator_.set_float_comparison(DefaultFieldComparator::APPROXIMATE); |
||||||
|
EXPECT_EQ(FieldComparator::DIFFERENT, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_float, -1, -1, NULL)); |
||||||
|
EXPECT_EQ(FieldComparator::DIFFERENT, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_double, -1, -1, NULL)); |
||||||
|
|
||||||
|
// Should fail since the fraction is too low.
|
||||||
|
comparator_.SetFractionAndMargin(field_float, 0.01, 0.0); |
||||||
|
comparator_.SetFractionAndMargin(field_double, 0.01, 0.0); |
||||||
|
|
||||||
|
EXPECT_EQ(FieldComparator::DIFFERENT, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_float, -1, -1, NULL)); |
||||||
|
EXPECT_EQ(FieldComparator::DIFFERENT, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_double, -1, -1, NULL)); |
||||||
|
|
||||||
|
// Should fail since the margin is too low.
|
||||||
|
comparator_.SetFractionAndMargin(field_float, 0.0, 9.0); |
||||||
|
comparator_.SetFractionAndMargin(field_double, 0.0, 9.0); |
||||||
|
EXPECT_EQ(FieldComparator::DIFFERENT, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_float, -1, -1, NULL)); |
||||||
|
EXPECT_EQ(FieldComparator::DIFFERENT, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_double, -1, -1, NULL)); |
||||||
|
|
||||||
|
// Should succeed since the fraction is high enough.
|
||||||
|
comparator_.SetFractionAndMargin(field_float, 0.2, 0.0); |
||||||
|
comparator_.SetFractionAndMargin(field_double, 0.2, 0.0); |
||||||
|
EXPECT_EQ(FieldComparator::SAME, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_float, -1, -1, NULL)); |
||||||
|
EXPECT_EQ(FieldComparator::SAME, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_double, -1, -1, NULL)); |
||||||
|
|
||||||
|
// Should succeed since the margin is high enough.
|
||||||
|
comparator_.SetFractionAndMargin(field_float, 0.0, 10.0); |
||||||
|
comparator_.SetFractionAndMargin(field_double, 0.0, 10.0); |
||||||
|
EXPECT_EQ(FieldComparator::SAME, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_float, -1, -1, NULL)); |
||||||
|
EXPECT_EQ(FieldComparator::SAME, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_double, -1, -1, NULL)); |
||||||
|
|
||||||
|
// Setting values for one of the fields should not affect the other.
|
||||||
|
comparator_.SetFractionAndMargin(field_double, 0.0, 0.0); |
||||||
|
EXPECT_EQ(FieldComparator::SAME, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_float, -1, -1, NULL)); |
||||||
|
EXPECT_EQ(FieldComparator::DIFFERENT, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_double, -1, -1, NULL)); |
||||||
|
|
||||||
|
// +inf should be equal even though they are not technically within margin or
|
||||||
|
// fraction.
|
||||||
|
message_1_.set_optional_float(numeric_limits<float>::infinity()); |
||||||
|
message_2_.set_optional_float(numeric_limits<float>::infinity()); |
||||||
|
message_1_.set_optional_double(numeric_limits<double>::infinity()); |
||||||
|
message_2_.set_optional_double(numeric_limits<double>::infinity()); |
||||||
|
comparator_.SetFractionAndMargin(field_float, 0.0, 0.0); |
||||||
|
comparator_.SetFractionAndMargin(field_double, 0.0, 0.0); |
||||||
|
EXPECT_EQ(FieldComparator::SAME, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_float, -1, -1, NULL)); |
||||||
|
EXPECT_EQ(FieldComparator::SAME, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_double, -1, -1, NULL)); |
||||||
|
|
||||||
|
// -inf should be equal even though they are not technically within margin or
|
||||||
|
// fraction.
|
||||||
|
message_1_.set_optional_float(-numeric_limits<float>::infinity()); |
||||||
|
message_2_.set_optional_float(-numeric_limits<float>::infinity()); |
||||||
|
message_1_.set_optional_double(-numeric_limits<double>::infinity()); |
||||||
|
message_2_.set_optional_double(-numeric_limits<double>::infinity()); |
||||||
|
comparator_.SetFractionAndMargin(field_float, 0.0, 0.0); |
||||||
|
comparator_.SetFractionAndMargin(field_double, 0.0, 0.0); |
||||||
|
EXPECT_EQ(FieldComparator::SAME, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_float, -1, -1, NULL)); |
||||||
|
EXPECT_EQ(FieldComparator::SAME, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_double, -1, -1, NULL)); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(DefaultFieldComparatorTest, |
||||||
|
FloatingPointComparisonWithinDefaultFractionOrMargin) { |
||||||
|
const FieldDescriptor* field_float = |
||||||
|
descriptor_->FindFieldByName("optional_float"); |
||||||
|
const FieldDescriptor* field_double = |
||||||
|
descriptor_->FindFieldByName("optional_double"); |
||||||
|
|
||||||
|
message_1_.set_optional_float(100.0f); |
||||||
|
message_2_.set_optional_float(109.9f); |
||||||
|
message_1_.set_optional_double(100.0); |
||||||
|
message_2_.set_optional_double(109.9); |
||||||
|
|
||||||
|
comparator_.set_float_comparison(DefaultFieldComparator::APPROXIMATE); |
||||||
|
EXPECT_EQ(FieldComparator::DIFFERENT, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_float, -1, -1, NULL)); |
||||||
|
EXPECT_EQ(FieldComparator::DIFFERENT, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_double, -1, -1, NULL)); |
||||||
|
|
||||||
|
// Set default fraction and margin.
|
||||||
|
comparator_.SetDefaultFractionAndMargin(0.01, 0.0); |
||||||
|
|
||||||
|
// Float comparisons should fail since the fraction is too low.
|
||||||
|
EXPECT_EQ(FieldComparator::DIFFERENT, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_float, -1, -1, NULL)); |
||||||
|
EXPECT_EQ(FieldComparator::DIFFERENT, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_double, -1, -1, NULL)); |
||||||
|
|
||||||
|
// Set field-specific fraction and margin for one field (field_float) but not
|
||||||
|
// the other (field_double)
|
||||||
|
comparator_.SetFractionAndMargin(field_float, 0.2, 0.0); |
||||||
|
|
||||||
|
// The field with the override should succeed, since its field-specific
|
||||||
|
// fraction is high enough.
|
||||||
|
EXPECT_EQ(FieldComparator::SAME, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_float, -1, -1, NULL)); |
||||||
|
// The field with no override should fail, since the default fraction is too
|
||||||
|
// low
|
||||||
|
EXPECT_EQ(FieldComparator::DIFFERENT, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_double, -1, -1, NULL)); |
||||||
|
|
||||||
|
// Set the default fraction and margin high enough so that fields that use
|
||||||
|
// the default should succeed
|
||||||
|
comparator_.SetDefaultFractionAndMargin(0.2, 0.0); |
||||||
|
EXPECT_EQ(FieldComparator::SAME, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_double, -1, -1, NULL)); |
||||||
|
|
||||||
|
// The field with an override should still be OK
|
||||||
|
EXPECT_EQ(FieldComparator::SAME, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_float, -1, -1, NULL)); |
||||||
|
|
||||||
|
// Set fraction and margin for the field with an override to be too low
|
||||||
|
comparator_.SetFractionAndMargin(field_float, 0.01, 0.0); |
||||||
|
|
||||||
|
// Now our default is high enough but field_float's override is too low.
|
||||||
|
EXPECT_EQ(FieldComparator::DIFFERENT, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_float, -1, -1, NULL)); |
||||||
|
EXPECT_EQ(FieldComparator::SAME, |
||||||
|
comparator_.Compare(message_1_, message_2_, |
||||||
|
field_double, -1, -1, NULL)); |
||||||
|
} |
||||||
|
|
||||||
|
// Simple test checking whether we compare values at correct indices.
|
||||||
|
TEST_F(DefaultFieldComparatorTest, RepeatedFieldComparison) { |
||||||
|
const FieldDescriptor* field = |
||||||
|
descriptor_->FindFieldByName("repeated_string"); |
||||||
|
|
||||||
|
message_1_.add_repeated_string("foo"); |
||||||
|
message_1_.add_repeated_string("bar"); |
||||||
|
message_2_.add_repeated_string("bar"); |
||||||
|
message_2_.add_repeated_string("baz"); |
||||||
|
|
||||||
|
EXPECT_EQ(FieldComparator::DIFFERENT, |
||||||
|
comparator_.Compare(message_1_, message_2_, field, 0, 0, NULL)); |
||||||
|
EXPECT_EQ(FieldComparator::DIFFERENT, |
||||||
|
comparator_.Compare(message_1_, message_2_, field, 1, 1, NULL)); |
||||||
|
EXPECT_EQ(FieldComparator::SAME, |
||||||
|
comparator_.Compare(message_1_, message_2_, field, 1, 0, NULL)); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
} // namespace
|
||||||
|
} // namespace google
|
@ -0,0 +1,93 @@ |
|||||||
|
// 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_UTIL_CONVERTER_CONSTANTS_H__ |
||||||
|
#define GOOGLE_PROTOBUF_UTIL_CONVERTER_CONSTANTS_H__ |
||||||
|
|
||||||
|
#include <google/protobuf/stubs/common.h> |
||||||
|
|
||||||
|
// This file contains constants used by //net/proto2/util/converter.
|
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace converter { |
||||||
|
// Prefix for type URLs.
|
||||||
|
const char kTypeServiceBaseUrl[] = "type.googleapis.com"; |
||||||
|
|
||||||
|
// Format string for RFC3339 timestamp formatting.
|
||||||
|
const char kRfc3339TimeFormat[] = "%Y-%m-%dT%H:%M:%S"; |
||||||
|
|
||||||
|
// Minimum seconds allowed in a google.protobuf.TimeStamp or Duration value.
|
||||||
|
const int64 kMinSeconds = -315576000000; |
||||||
|
|
||||||
|
// Maximum seconds allowed in a google.protobuf.TimeStamp or Duration value.
|
||||||
|
const int64 kMaxSeconds = 315576000000; |
||||||
|
|
||||||
|
// Nano seconds in a second.
|
||||||
|
const int32 kNanosPerSecond = 1000000000; |
||||||
|
|
||||||
|
// Type url representing NULL values in google.protobuf.Struct type.
|
||||||
|
const char kStructNullValueTypeUrl[] = |
||||||
|
"type.googleapis.com/google.protobuf.NullValue"; |
||||||
|
|
||||||
|
// Type string for google.protobuf.Struct
|
||||||
|
const char kStructType[] = "google.protobuf.Struct"; |
||||||
|
|
||||||
|
// Type string for struct.proto's google.protobuf.Value value type.
|
||||||
|
const char kStructValueType[] = "google.protobuf.Value"; |
||||||
|
|
||||||
|
// Type string for struct.proto's google.protobuf.ListValue value type.
|
||||||
|
const char kStructListValueType[] = "google.protobuf.ListValue"; |
||||||
|
|
||||||
|
// Type string for google.protobuf.Timestamp
|
||||||
|
const char kTimestampType[] = "google.protobuf.Timestamp"; |
||||||
|
|
||||||
|
// Type string for google.protobuf.Duration
|
||||||
|
const char kDurationType[] = "google.protobuf.Duration"; |
||||||
|
|
||||||
|
// Type URL for struct value type google.protobuf.Value
|
||||||
|
const char kStructValueTypeUrl[] = "type.googleapis.com/google.protobuf.Value"; |
||||||
|
|
||||||
|
// Type URL for struct value type google.protobuf.Value
|
||||||
|
const char kStructTypeUrl[] = "type.googleapis.com/google.protobuf.Struct"; |
||||||
|
|
||||||
|
// Type string for google.protobuf.Any
|
||||||
|
const char kAnyType[] = "google.protobuf.Any"; |
||||||
|
|
||||||
|
// The type URL of google.protobuf.FieldMask;
|
||||||
|
const char kFieldMaskTypeUrl[] = |
||||||
|
"type.googleapis.com/google.protobuf.FieldMask"; |
||||||
|
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
} // namespace google
|
||||||
|
#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_CONSTANTS_H__
|
@ -0,0 +1,285 @@ |
|||||||
|
// 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/util/internal/datapiece.h> |
||||||
|
|
||||||
|
#include <google/protobuf/struct.pb.h> |
||||||
|
#include <google/protobuf/type.pb.h> |
||||||
|
#include <google/protobuf/descriptor.h> |
||||||
|
#include <google/protobuf/util/internal/utility.h> |
||||||
|
#include <google/protobuf/stubs/strutil.h> |
||||||
|
#include <google/protobuf/stubs/mathutil.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace converter { |
||||||
|
|
||||||
|
using google::protobuf::EnumDescriptor; |
||||||
|
using google::protobuf::EnumValueDescriptor; |
||||||
|
; |
||||||
|
; |
||||||
|
using util::error::Code; |
||||||
|
using util::Status; |
||||||
|
using util::StatusOr; |
||||||
|
|
||||||
|
namespace { |
||||||
|
|
||||||
|
inline Status InvalidArgument(StringPiece value_str) { |
||||||
|
return Status(util::error::INVALID_ARGUMENT, value_str); |
||||||
|
} |
||||||
|
|
||||||
|
// For general conversion between
|
||||||
|
// int32, int64, uint32, uint64, double and float
|
||||||
|
// except conversion between double and float.
|
||||||
|
template <typename To, typename From> |
||||||
|
StatusOr<To> NumberConvertAndCheck(From before) { |
||||||
|
if (::google::protobuf::internal::is_same<From, To>::value) return before; |
||||||
|
To after = static_cast<To>(before); |
||||||
|
if (after == before && |
||||||
|
MathUtil::Sign<From>(before) == MathUtil::Sign<To>(after)) { |
||||||
|
return after; |
||||||
|
} else { |
||||||
|
return InvalidArgument(::google::protobuf::internal::is_integral<From>::value |
||||||
|
? ValueAsString(before) |
||||||
|
: ::google::protobuf::internal::is_same<From, double>::value |
||||||
|
? DoubleAsString(before) |
||||||
|
: FloatAsString(before)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// For conversion between double and float only.
|
||||||
|
template <typename To, typename From> |
||||||
|
StatusOr<To> FloatingPointConvertAndCheck(From before) { |
||||||
|
if (isnan(before)) return std::numeric_limits<To>::quiet_NaN(); |
||||||
|
|
||||||
|
To after = static_cast<To>(before); |
||||||
|
if (MathUtil::AlmostEquals<To>(after, before)) { |
||||||
|
return after; |
||||||
|
} else { |
||||||
|
return InvalidArgument(::google::protobuf::internal::is_same<From, double>::value |
||||||
|
? DoubleAsString(before) |
||||||
|
: FloatAsString(before)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
StatusOr<int32> DataPiece::ToInt32() const { |
||||||
|
if (type_ == TYPE_STRING) { |
||||||
|
return StringToNumber<int32>(safe_strto32); |
||||||
|
} |
||||||
|
return GenericConvert<int32>(); |
||||||
|
} |
||||||
|
|
||||||
|
StatusOr<uint32> DataPiece::ToUint32() const { |
||||||
|
if (type_ == TYPE_STRING) { |
||||||
|
return StringToNumber<uint32>(safe_strtou32); |
||||||
|
} |
||||||
|
return GenericConvert<uint32>(); |
||||||
|
} |
||||||
|
|
||||||
|
StatusOr<int64> DataPiece::ToInt64() const { |
||||||
|
if (type_ == TYPE_STRING) { |
||||||
|
return StringToNumber<int64>(safe_strto64); |
||||||
|
} |
||||||
|
return GenericConvert<int64>(); |
||||||
|
} |
||||||
|
|
||||||
|
StatusOr<uint64> DataPiece::ToUint64() const { |
||||||
|
if (type_ == TYPE_STRING) { |
||||||
|
return StringToNumber<uint64>(safe_strtou64); |
||||||
|
} |
||||||
|
return GenericConvert<uint64>(); |
||||||
|
} |
||||||
|
|
||||||
|
StatusOr<double> DataPiece::ToDouble() const { |
||||||
|
if (type_ == TYPE_FLOAT) { |
||||||
|
return FloatingPointConvertAndCheck<double, float>(float_); |
||||||
|
} |
||||||
|
if (type_ == TYPE_STRING) { |
||||||
|
if (str_ == "Infinity") return std::numeric_limits<double>::infinity(); |
||||||
|
if (str_ == "-Infinity") return -std::numeric_limits<double>::infinity(); |
||||||
|
if (str_ == "NaN") return std::numeric_limits<double>::quiet_NaN(); |
||||||
|
return StringToNumber<double>(safe_strtod); |
||||||
|
} |
||||||
|
return GenericConvert<double>(); |
||||||
|
} |
||||||
|
|
||||||
|
StatusOr<float> DataPiece::ToFloat() const { |
||||||
|
if (type_ == TYPE_DOUBLE) { |
||||||
|
return FloatingPointConvertAndCheck<float, double>(double_); |
||||||
|
} |
||||||
|
if (type_ == TYPE_STRING) { |
||||||
|
if (str_ == "Infinity") return std::numeric_limits<float>::infinity(); |
||||||
|
if (str_ == "-Infinity") return -std::numeric_limits<float>::infinity(); |
||||||
|
if (str_ == "NaN") return std::numeric_limits<float>::quiet_NaN(); |
||||||
|
// SafeStrToFloat() is used instead of safe_strtof() because the later
|
||||||
|
// does not fail on inputs like SimpleDtoa(DBL_MAX).
|
||||||
|
return StringToNumber<float>(SafeStrToFloat); |
||||||
|
} |
||||||
|
return GenericConvert<float>(); |
||||||
|
} |
||||||
|
|
||||||
|
StatusOr<bool> DataPiece::ToBool() const { |
||||||
|
switch (type_) { |
||||||
|
case TYPE_BOOL: |
||||||
|
return bool_; |
||||||
|
case TYPE_STRING: |
||||||
|
return StringToNumber<bool>(safe_strtob); |
||||||
|
default: |
||||||
|
return InvalidArgument( |
||||||
|
ValueAsStringOrDefault("Wrong type. Cannot convert to Bool.")); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
StatusOr<string> DataPiece::ToString() const { |
||||||
|
switch (type_) { |
||||||
|
case TYPE_STRING: |
||||||
|
return str_.ToString(); |
||||||
|
case TYPE_BYTES: { |
||||||
|
string base64; |
||||||
|
WebSafeBase64Escape(str_, &base64); |
||||||
|
return base64; |
||||||
|
} |
||||||
|
default: |
||||||
|
return InvalidArgument( |
||||||
|
ValueAsStringOrDefault("Cannot convert to string.")); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
string DataPiece::ValueAsStringOrDefault(StringPiece default_string) const { |
||||||
|
switch (type_) { |
||||||
|
case TYPE_INT32: |
||||||
|
return SimpleItoa(i32_); |
||||||
|
case TYPE_INT64: |
||||||
|
return SimpleItoa(i64_); |
||||||
|
case TYPE_UINT32: |
||||||
|
return SimpleItoa(u32_); |
||||||
|
case TYPE_UINT64: |
||||||
|
return SimpleItoa(u64_); |
||||||
|
case TYPE_DOUBLE: |
||||||
|
return DoubleAsString(double_); |
||||||
|
case TYPE_FLOAT: |
||||||
|
return FloatAsString(float_); |
||||||
|
case TYPE_BOOL: |
||||||
|
return SimpleBtoa(bool_); |
||||||
|
case TYPE_STRING: |
||||||
|
return StrCat("\"", str_.ToString(), "\""); |
||||||
|
case TYPE_BYTES: { |
||||||
|
string base64; |
||||||
|
WebSafeBase64Escape(str_, &base64); |
||||||
|
return StrCat("\"", base64, "\""); |
||||||
|
} |
||||||
|
case TYPE_NULL: |
||||||
|
return "null"; |
||||||
|
default: |
||||||
|
return default_string.ToString(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
StatusOr<string> DataPiece::ToBytes() const { |
||||||
|
if (type_ == TYPE_BYTES) return str_.ToString(); |
||||||
|
if (type_ == TYPE_STRING) { |
||||||
|
string decoded; |
||||||
|
if (!WebSafeBase64Unescape(str_, &decoded)) { |
||||||
|
if (!Base64Unescape(str_, &decoded)) { |
||||||
|
return InvalidArgument( |
||||||
|
ValueAsStringOrDefault("Invalid data in input.")); |
||||||
|
} |
||||||
|
} |
||||||
|
return decoded; |
||||||
|
} else { |
||||||
|
return InvalidArgument(ValueAsStringOrDefault( |
||||||
|
"Wrong type. Only String or Bytes can be converted to Bytes.")); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
StatusOr<int> DataPiece::ToEnum(const google::protobuf::Enum* enum_type) const { |
||||||
|
if (type_ == TYPE_NULL) return google::protobuf::NULL_VALUE; |
||||||
|
|
||||||
|
if (type_ == TYPE_STRING) { |
||||||
|
// First try the given value as a name.
|
||||||
|
string enum_name = str_.ToString(); |
||||||
|
const google::protobuf::EnumValue* value = |
||||||
|
FindEnumValueByNameOrNull(enum_type, enum_name); |
||||||
|
if (value != NULL) return value->number(); |
||||||
|
// Next try a normalized name.
|
||||||
|
for (string::iterator it = enum_name.begin(); it != enum_name.end(); ++it) { |
||||||
|
*it = *it == '-' ? '_' : ascii_toupper(*it); |
||||||
|
} |
||||||
|
value = FindEnumValueByNameOrNull(enum_type, enum_name); |
||||||
|
if (value != NULL) return value->number(); |
||||||
|
} else { |
||||||
|
StatusOr<int32> value = ToInt32(); |
||||||
|
if (value.ok()) { |
||||||
|
if (const google::protobuf::EnumValue* enum_value = |
||||||
|
FindEnumValueByNumberOrNull(enum_type, value.ValueOrDie())) { |
||||||
|
return enum_value->number(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return InvalidArgument( |
||||||
|
ValueAsStringOrDefault("Cannot find enum with given value.")); |
||||||
|
} |
||||||
|
|
||||||
|
template <typename To> |
||||||
|
StatusOr<To> DataPiece::GenericConvert() const { |
||||||
|
switch (type_) { |
||||||
|
case TYPE_INT32: |
||||||
|
return NumberConvertAndCheck<To, int32>(i32_); |
||||||
|
case TYPE_INT64: |
||||||
|
return NumberConvertAndCheck<To, int64>(i64_); |
||||||
|
case TYPE_UINT32: |
||||||
|
return NumberConvertAndCheck<To, uint32>(u32_); |
||||||
|
case TYPE_UINT64: |
||||||
|
return NumberConvertAndCheck<To, uint64>(u64_); |
||||||
|
case TYPE_DOUBLE: |
||||||
|
return NumberConvertAndCheck<To, double>(double_); |
||||||
|
case TYPE_FLOAT: |
||||||
|
return NumberConvertAndCheck<To, float>(float_); |
||||||
|
default: // TYPE_ENUM, TYPE_STRING, TYPE_CORD, TYPE_BOOL
|
||||||
|
return InvalidArgument(ValueAsStringOrDefault( |
||||||
|
"Wrong type. Bool, Enum, String and Cord not supported in " |
||||||
|
"GenericConvert.")); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
template <typename To> |
||||||
|
StatusOr<To> DataPiece::StringToNumber(bool (*func)(StringPiece, To*)) const { |
||||||
|
To result; |
||||||
|
if (func(str_, &result)) return result; |
||||||
|
return InvalidArgument(StrCat("\"", str_.ToString(), "\"")); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
} // namespace google
|
@ -0,0 +1,212 @@ |
|||||||
|
// 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_UTIL_CONVERTER_DATAPIECE_H__ |
||||||
|
#define GOOGLE_PROTOBUF_UTIL_CONVERTER_DATAPIECE_H__ |
||||||
|
|
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include <google/protobuf/stubs/common.h> |
||||||
|
#include <google/protobuf/stubs/stringpiece.h> |
||||||
|
#include <google/protobuf/stubs/statusor.h> |
||||||
|
|
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
class Enum; |
||||||
|
} // namespace protobuf
|
||||||
|
|
||||||
|
|
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace converter { |
||||||
|
|
||||||
|
// Container for a single piece of data together with its data type.
|
||||||
|
//
|
||||||
|
// For primitive types (int32, int64, uint32, uint64, double, float, bool),
|
||||||
|
// the data is stored by value.
|
||||||
|
//
|
||||||
|
// For string, a StringPiece is stored. For Cord, a pointer to Cord is stored.
|
||||||
|
// Just like StringPiece, the DataPiece class does not own the storage for
|
||||||
|
// the actual string or Cord, so it is the user's responsiblity to guarantee
|
||||||
|
// that the underlying storage is still valid when the DataPiece is accessed.
|
||||||
|
class LIBPROTOBUF_EXPORT DataPiece { |
||||||
|
public: |
||||||
|
// Identifies data type of the value.
|
||||||
|
// These are the types supported by DataPiece.
|
||||||
|
enum Type { |
||||||
|
TYPE_INT32 = 1, |
||||||
|
TYPE_INT64 = 2, |
||||||
|
TYPE_UINT32 = 3, |
||||||
|
TYPE_UINT64 = 4, |
||||||
|
TYPE_DOUBLE = 5, |
||||||
|
TYPE_FLOAT = 6, |
||||||
|
TYPE_BOOL = 7, |
||||||
|
TYPE_ENUM = 8, |
||||||
|
TYPE_STRING = 9, |
||||||
|
TYPE_BYTES = 10, |
||||||
|
TYPE_NULL = 11, // explicit NULL type
|
||||||
|
}; |
||||||
|
|
||||||
|
// Constructors and Destructor
|
||||||
|
explicit DataPiece(const int32 value) : type_(TYPE_INT32), i32_(value) {} |
||||||
|
explicit DataPiece(const int64 value) : type_(TYPE_INT64), i64_(value) {} |
||||||
|
explicit DataPiece(const uint32 value) : type_(TYPE_UINT32), u32_(value) {} |
||||||
|
explicit DataPiece(const uint64 value) : type_(TYPE_UINT64), u64_(value) {} |
||||||
|
explicit DataPiece(const double value) : type_(TYPE_DOUBLE), double_(value) {} |
||||||
|
explicit DataPiece(const float value) : type_(TYPE_FLOAT), float_(value) {} |
||||||
|
explicit DataPiece(const bool value) : type_(TYPE_BOOL), bool_(value) {} |
||||||
|
explicit DataPiece(StringPiece value) |
||||||
|
: type_(TYPE_STRING), |
||||||
|
str_(StringPiecePod::CreateFromStringPiece(value)) {} |
||||||
|
// Constructor for bytes. The second parameter is not used.
|
||||||
|
explicit DataPiece(StringPiece value, bool dummy) |
||||||
|
: type_(TYPE_BYTES), str_(StringPiecePod::CreateFromStringPiece(value)) {} |
||||||
|
DataPiece(const DataPiece& r) : type_(r.type_), str_(r.str_) {} |
||||||
|
DataPiece& operator=(const DataPiece& x) { |
||||||
|
type_ = x.type_; |
||||||
|
str_ = x.str_; |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
static DataPiece NullData() { return DataPiece(TYPE_NULL, 0); } |
||||||
|
|
||||||
|
virtual ~DataPiece() {} |
||||||
|
|
||||||
|
// Accessors
|
||||||
|
Type type() const { return type_; } |
||||||
|
|
||||||
|
StringPiece str() const { |
||||||
|
GOOGLE_LOG_IF(DFATAL, type_ != TYPE_STRING) << "Not a string type."; |
||||||
|
return str_; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// Parses, casts or converts the value stored in the DataPiece into an int32.
|
||||||
|
util::StatusOr<int32> ToInt32() const; |
||||||
|
|
||||||
|
// Parses, casts or converts the value stored in the DataPiece into a uint32.
|
||||||
|
util::StatusOr<uint32> ToUint32() const; |
||||||
|
|
||||||
|
// Parses, casts or converts the value stored in the DataPiece into an int64.
|
||||||
|
util::StatusOr<int64> ToInt64() const; |
||||||
|
|
||||||
|
// Parses, casts or converts the value stored in the DataPiece into a uint64.
|
||||||
|
util::StatusOr<uint64> ToUint64() const; |
||||||
|
|
||||||
|
// Parses, casts or converts the value stored in the DataPiece into a double.
|
||||||
|
util::StatusOr<double> ToDouble() const; |
||||||
|
|
||||||
|
// Parses, casts or converts the value stored in the DataPiece into a float.
|
||||||
|
util::StatusOr<float> ToFloat() const; |
||||||
|
|
||||||
|
// Parses, casts or converts the value stored in the DataPiece into a bool.
|
||||||
|
util::StatusOr<bool> ToBool() const; |
||||||
|
|
||||||
|
// Parses, casts or converts the value stored in the DataPiece into a string.
|
||||||
|
util::StatusOr<string> ToString() const; |
||||||
|
|
||||||
|
// Tries to convert the value contained in this datapiece to string. If the
|
||||||
|
// conversion fails, it returns the default_string.
|
||||||
|
string ValueAsStringOrDefault(StringPiece default_string) const; |
||||||
|
|
||||||
|
util::StatusOr<string> ToBytes() const; |
||||||
|
|
||||||
|
// Converts a value into protocol buffer enum number. If the value is a
|
||||||
|
// string, first attempts conversion by name, trying names as follows:
|
||||||
|
// 1) the directly provided string value.
|
||||||
|
// 2) the value upper-cased and replacing '-' by '_'
|
||||||
|
// If the value is not a string, attempts to convert to a 32-bit integer.
|
||||||
|
// If none of these succeeds, returns a conversion error status.
|
||||||
|
util::StatusOr<int> ToEnum(const google::protobuf::Enum* enum_type) const; |
||||||
|
|
||||||
|
private: |
||||||
|
// Disallow implicit constructor.
|
||||||
|
DataPiece(); |
||||||
|
|
||||||
|
// Helper to create NULL or ENUM types.
|
||||||
|
DataPiece(Type type, int32 val) : type_(type), i32_(val) {} |
||||||
|
|
||||||
|
// For numeric conversion between
|
||||||
|
// int32, int64, uint32, uint64, double, float and bool
|
||||||
|
template <typename To> |
||||||
|
util::StatusOr<To> GenericConvert() const; |
||||||
|
|
||||||
|
// For conversion from string to
|
||||||
|
// int32, int64, uint32, uint64, double, float and bool
|
||||||
|
template <typename To> |
||||||
|
util::StatusOr<To> StringToNumber(bool (*func)(StringPiece, To*)) const; |
||||||
|
|
||||||
|
// Data type for this piece of data.
|
||||||
|
Type type_; |
||||||
|
|
||||||
|
// StringPiece is not a POD and can not be used in an union (pre C++11). We
|
||||||
|
// need a POD version of it.
|
||||||
|
struct StringPiecePod { |
||||||
|
const char* data; |
||||||
|
int size; |
||||||
|
|
||||||
|
// Create from a StringPiece.
|
||||||
|
static StringPiecePod CreateFromStringPiece(StringPiece str) { |
||||||
|
StringPiecePod pod; |
||||||
|
pod.data = str.data(); |
||||||
|
pod.size = str.size(); |
||||||
|
return pod; |
||||||
|
} |
||||||
|
|
||||||
|
// Cast to StringPiece.
|
||||||
|
operator StringPiece() const { return StringPiece(data, size); } |
||||||
|
|
||||||
|
bool operator==(const char* value) const { |
||||||
|
return StringPiece(data, size) == StringPiece(value); |
||||||
|
} |
||||||
|
|
||||||
|
string ToString() const { return string(data, size); } |
||||||
|
}; |
||||||
|
|
||||||
|
// Stored piece of data.
|
||||||
|
union { |
||||||
|
const int32 i32_; |
||||||
|
const int64 i64_; |
||||||
|
const uint32 u32_; |
||||||
|
const uint64 u64_; |
||||||
|
const double double_; |
||||||
|
const float float_; |
||||||
|
const bool bool_; |
||||||
|
StringPiecePod str_; |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
|
||||||
|
} // namespace google
|
||||||
|
#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_DATAPIECE_H__
|
@ -0,0 +1,515 @@ |
|||||||
|
// 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/util/internal/default_value_objectwriter.h> |
||||||
|
|
||||||
|
#include <google/protobuf/stubs/hash.h> |
||||||
|
|
||||||
|
#include <google/protobuf/util/internal/constants.h> |
||||||
|
#include <google/protobuf/stubs/map_util.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
using util::Status; |
||||||
|
using util::StatusOr; |
||||||
|
namespace converter { |
||||||
|
|
||||||
|
DefaultValueObjectWriter::DefaultValueObjectWriter( |
||||||
|
TypeResolver* type_resolver, const google::protobuf::Type& type, |
||||||
|
ObjectWriter* ow) |
||||||
|
: typeinfo_(TypeInfo::NewTypeInfo(type_resolver)), |
||||||
|
type_(type), |
||||||
|
disable_normalize_(false), |
||||||
|
current_(NULL), |
||||||
|
root_(NULL), |
||||||
|
ow_(ow) {} |
||||||
|
|
||||||
|
DefaultValueObjectWriter::~DefaultValueObjectWriter() { |
||||||
|
for (int i = 0; i < string_values_.size(); ++i) { |
||||||
|
delete string_values_[i]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
DefaultValueObjectWriter* DefaultValueObjectWriter::RenderBool(StringPiece name, |
||||||
|
bool value) { |
||||||
|
if (current_ == NULL) { |
||||||
|
ow_->RenderBool(name, value); |
||||||
|
} else { |
||||||
|
RenderDataPiece(name, DataPiece(value)); |
||||||
|
} |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
DefaultValueObjectWriter* DefaultValueObjectWriter::RenderInt32( |
||||||
|
StringPiece name, int32 value) { |
||||||
|
if (current_ == NULL) { |
||||||
|
ow_->RenderInt32(name, value); |
||||||
|
} else { |
||||||
|
RenderDataPiece(name, DataPiece(value)); |
||||||
|
} |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
DefaultValueObjectWriter* DefaultValueObjectWriter::RenderUint32( |
||||||
|
StringPiece name, uint32 value) { |
||||||
|
if (current_ == NULL) { |
||||||
|
ow_->RenderUint32(name, value); |
||||||
|
} else { |
||||||
|
RenderDataPiece(name, DataPiece(value)); |
||||||
|
} |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
DefaultValueObjectWriter* DefaultValueObjectWriter::RenderInt64( |
||||||
|
StringPiece name, int64 value) { |
||||||
|
if (current_ == NULL) { |
||||||
|
ow_->RenderInt64(name, value); |
||||||
|
} else { |
||||||
|
RenderDataPiece(name, DataPiece(value)); |
||||||
|
} |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
DefaultValueObjectWriter* DefaultValueObjectWriter::RenderUint64( |
||||||
|
StringPiece name, uint64 value) { |
||||||
|
if (current_ == NULL) { |
||||||
|
ow_->RenderUint64(name, value); |
||||||
|
} else { |
||||||
|
RenderDataPiece(name, DataPiece(value)); |
||||||
|
} |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
DefaultValueObjectWriter* DefaultValueObjectWriter::RenderDouble( |
||||||
|
StringPiece name, double value) { |
||||||
|
if (current_ == NULL) { |
||||||
|
ow_->RenderDouble(name, value); |
||||||
|
} else { |
||||||
|
RenderDataPiece(name, DataPiece(value)); |
||||||
|
} |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
DefaultValueObjectWriter* DefaultValueObjectWriter::RenderFloat( |
||||||
|
StringPiece name, float value) { |
||||||
|
if (current_ == NULL) { |
||||||
|
ow_->RenderBool(name, value); |
||||||
|
} else { |
||||||
|
RenderDataPiece(name, DataPiece(value)); |
||||||
|
} |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
DefaultValueObjectWriter* DefaultValueObjectWriter::RenderString( |
||||||
|
StringPiece name, StringPiece value) { |
||||||
|
if (current_ == NULL) { |
||||||
|
ow_->RenderString(name, value); |
||||||
|
} else { |
||||||
|
// Since StringPiece is essentially a pointer, takes a copy of "value" to
|
||||||
|
// avoid ownership issues.
|
||||||
|
string_values_.push_back(new string(value.ToString())); |
||||||
|
RenderDataPiece(name, DataPiece(*string_values_.back())); |
||||||
|
} |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
DefaultValueObjectWriter* DefaultValueObjectWriter::RenderBytes( |
||||||
|
StringPiece name, StringPiece value) { |
||||||
|
if (current_ == NULL) { |
||||||
|
ow_->RenderBytes(name, value); |
||||||
|
} else { |
||||||
|
RenderDataPiece(name, DataPiece(value)); |
||||||
|
} |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
DefaultValueObjectWriter* DefaultValueObjectWriter::RenderNull( |
||||||
|
StringPiece name) { |
||||||
|
if (current_ == NULL) { |
||||||
|
ow_->RenderNull(name); |
||||||
|
} else { |
||||||
|
RenderDataPiece(name, DataPiece::NullData()); |
||||||
|
} |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
DefaultValueObjectWriter* |
||||||
|
DefaultValueObjectWriter::DisableCaseNormalizationForNextKey() { |
||||||
|
disable_normalize_ = true; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
DefaultValueObjectWriter::Node::Node(const string& name, |
||||||
|
const google::protobuf::Type* type, |
||||||
|
NodeKind kind, const DataPiece& data, |
||||||
|
bool is_placeholder) |
||||||
|
: name_(name), |
||||||
|
type_(type), |
||||||
|
kind_(kind), |
||||||
|
disable_normalize_(false), |
||||||
|
is_any_(false), |
||||||
|
data_(data), |
||||||
|
is_placeholder_(is_placeholder) {} |
||||||
|
|
||||||
|
DefaultValueObjectWriter::Node* DefaultValueObjectWriter::Node::FindChild( |
||||||
|
StringPiece name) { |
||||||
|
if (name.empty() || kind_ != OBJECT) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
for (int i = 0; i < children_.size(); ++i) { |
||||||
|
Node* child = children_[i]; |
||||||
|
if (child->name() == name) { |
||||||
|
return child; |
||||||
|
} |
||||||
|
} |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
void DefaultValueObjectWriter::Node::WriteTo(ObjectWriter* ow) { |
||||||
|
if (disable_normalize_) { |
||||||
|
ow->DisableCaseNormalizationForNextKey(); |
||||||
|
} |
||||||
|
if (kind_ == PRIMITIVE) { |
||||||
|
ObjectWriter::RenderDataPieceTo(data_, name_, ow); |
||||||
|
return; |
||||||
|
} |
||||||
|
if (is_placeholder_) { |
||||||
|
// If is_placeholder_ = true, we didn't see this node in the response, so
|
||||||
|
// skip output.
|
||||||
|
return; |
||||||
|
} |
||||||
|
if (kind_ == LIST) { |
||||||
|
ow->StartList(name_); |
||||||
|
} else { |
||||||
|
ow->StartObject(name_); |
||||||
|
} |
||||||
|
for (int i = 0; i < children_.size(); ++i) { |
||||||
|
Node* child = children_[i]; |
||||||
|
child->WriteTo(ow); |
||||||
|
} |
||||||
|
if (kind_ == LIST) { |
||||||
|
ow->EndList(); |
||||||
|
} else { |
||||||
|
ow->EndObject(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const google::protobuf::Type* DefaultValueObjectWriter::Node::GetMapValueType( |
||||||
|
const google::protobuf::Type& found_type, TypeInfo* typeinfo) { |
||||||
|
// If this field is a map, we should use the type of its "Value" as
|
||||||
|
// the type of the child node.
|
||||||
|
for (int i = 0; i < found_type.fields_size(); ++i) { |
||||||
|
const google::protobuf::Field& sub_field = found_type.fields(i); |
||||||
|
if (sub_field.number() != 2) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
if (sub_field.kind() != google::protobuf::Field_Kind_TYPE_MESSAGE) { |
||||||
|
// This map's value type is not a message type. We don't need to
|
||||||
|
// get the field_type in this case.
|
||||||
|
break; |
||||||
|
} |
||||||
|
util::StatusOr<const google::protobuf::Type*> sub_type = |
||||||
|
typeinfo->ResolveTypeUrl(sub_field.type_url()); |
||||||
|
if (!sub_type.ok()) { |
||||||
|
GOOGLE_LOG(WARNING) << "Cannot resolve type '" << sub_field.type_url() << "'."; |
||||||
|
} else { |
||||||
|
return sub_type.ValueOrDie(); |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
void DefaultValueObjectWriter::Node::PopulateChildren(TypeInfo* typeinfo) { |
||||||
|
// Ignores well known types that don't require automatically populating their
|
||||||
|
// primitive children. For type "Any", we only populate its children when the
|
||||||
|
// "@type" field is set.
|
||||||
|
// TODO(tsun): remove "kStructValueType" from the list. It's being checked
|
||||||
|
// now because of a bug in the tool-chain that causes the "oneof_index"
|
||||||
|
// of kStructValueType to not be set correctly.
|
||||||
|
if (type_ == NULL || type_->name() == kAnyType || |
||||||
|
type_->name() == kStructType || type_->name() == kTimestampType || |
||||||
|
type_->name() == kDurationType || type_->name() == kStructValueType) { |
||||||
|
return; |
||||||
|
} |
||||||
|
std::vector<Node*> new_children; |
||||||
|
hash_map<string, int> orig_children_map; |
||||||
|
|
||||||
|
// Creates a map of child nodes to speed up lookup.
|
||||||
|
for (int i = 0; i < children_.size(); ++i) { |
||||||
|
InsertIfNotPresent(&orig_children_map, children_[i]->name_, i); |
||||||
|
} |
||||||
|
|
||||||
|
for (int i = 0; i < type_->fields_size(); ++i) { |
||||||
|
const google::protobuf::Field& field = type_->fields(i); |
||||||
|
hash_map<string, int>::iterator found = |
||||||
|
orig_children_map.find(field.name()); |
||||||
|
// If the child field has already been set, we just add it to the new list
|
||||||
|
// of children.
|
||||||
|
if (found != orig_children_map.end()) { |
||||||
|
new_children.push_back(children_[found->second]); |
||||||
|
children_[found->second] = NULL; |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
const google::protobuf::Type* field_type = NULL; |
||||||
|
bool is_map = false; |
||||||
|
NodeKind kind = PRIMITIVE; |
||||||
|
|
||||||
|
if (field.kind() == google::protobuf::Field_Kind_TYPE_MESSAGE) { |
||||||
|
kind = OBJECT; |
||||||
|
util::StatusOr<const google::protobuf::Type*> found_result = |
||||||
|
typeinfo->ResolveTypeUrl(field.type_url()); |
||||||
|
if (!found_result.ok()) { |
||||||
|
// "field" is of an unknown type.
|
||||||
|
GOOGLE_LOG(WARNING) << "Cannot resolve type '" << field.type_url() << "'."; |
||||||
|
} else { |
||||||
|
const google::protobuf::Type* found_type = found_result.ValueOrDie(); |
||||||
|
is_map = IsMap(field, *found_type); |
||||||
|
|
||||||
|
if (!is_map) { |
||||||
|
field_type = found_type; |
||||||
|
} else { |
||||||
|
// If this field is a map, we should use the type of its "Value" as
|
||||||
|
// the type of the child node.
|
||||||
|
field_type = GetMapValueType(*found_type, typeinfo); |
||||||
|
kind = MAP; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if (!is_map && |
||||||
|
field.cardinality() == |
||||||
|
google::protobuf::Field_Cardinality_CARDINALITY_REPEATED) { |
||||||
|
kind = LIST; |
||||||
|
} |
||||||
|
// If the child field is of primitive type, sets its data to the default
|
||||||
|
// value of its type.
|
||||||
|
// If oneof_index() != 0, the child field is part of a "oneof", which means
|
||||||
|
// the child field is optional and we shouldn't populate its default value.
|
||||||
|
google::protobuf::scoped_ptr<Node> child( |
||||||
|
new Node(field.name(), field_type, kind, |
||||||
|
((kind == PRIMITIVE && field.oneof_index() == 0) |
||||||
|
? CreateDefaultDataPieceForField(field) |
||||||
|
: DataPiece::NullData()), |
||||||
|
true)); |
||||||
|
new_children.push_back(child.release()); |
||||||
|
} |
||||||
|
// Adds all leftover nodes in children_ to the beginning of new_child.
|
||||||
|
for (int i = 0; i < children_.size(); ++i) { |
||||||
|
if (children_[i] == NULL) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
new_children.insert(new_children.begin(), children_[i]); |
||||||
|
children_[i] = NULL; |
||||||
|
} |
||||||
|
children_.swap(new_children); |
||||||
|
} |
||||||
|
|
||||||
|
void DefaultValueObjectWriter::MaybePopulateChildrenOfAny(Node* node) { |
||||||
|
// If this is an "Any" node with "@type" already given and no other children
|
||||||
|
// have been added, populates its children.
|
||||||
|
if (node != NULL && node->is_any() && node->type() != NULL && |
||||||
|
node->type()->name() != kAnyType && node->number_of_children() == 1) { |
||||||
|
node->PopulateChildren(typeinfo_.get()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
DataPiece DefaultValueObjectWriter::CreateDefaultDataPieceForField( |
||||||
|
const google::protobuf::Field& field) { |
||||||
|
switch (field.kind()) { |
||||||
|
case google::protobuf::Field_Kind_TYPE_DOUBLE: { |
||||||
|
return DataPiece(static_cast<double>(0)); |
||||||
|
} |
||||||
|
case google::protobuf::Field_Kind_TYPE_FLOAT: { |
||||||
|
return DataPiece(static_cast<float>(0)); |
||||||
|
} |
||||||
|
case google::protobuf::Field_Kind_TYPE_INT64: |
||||||
|
case google::protobuf::Field_Kind_TYPE_SINT64: |
||||||
|
case google::protobuf::Field_Kind_TYPE_SFIXED64: { |
||||||
|
return DataPiece(static_cast<int64>(0)); |
||||||
|
} |
||||||
|
case google::protobuf::Field_Kind_TYPE_UINT64: |
||||||
|
case google::protobuf::Field_Kind_TYPE_FIXED64: { |
||||||
|
return DataPiece(static_cast<uint64>(0)); |
||||||
|
} |
||||||
|
case google::protobuf::Field_Kind_TYPE_INT32: |
||||||
|
case google::protobuf::Field_Kind_TYPE_SINT32: |
||||||
|
case google::protobuf::Field_Kind_TYPE_SFIXED32: { |
||||||
|
return DataPiece(static_cast<int32>(0)); |
||||||
|
} |
||||||
|
case google::protobuf::Field_Kind_TYPE_BOOL: { |
||||||
|
return DataPiece(false); |
||||||
|
} |
||||||
|
case google::protobuf::Field_Kind_TYPE_STRING: { |
||||||
|
return DataPiece(string()); |
||||||
|
} |
||||||
|
case google::protobuf::Field_Kind_TYPE_BYTES: { |
||||||
|
return DataPiece("", false); |
||||||
|
} |
||||||
|
case google::protobuf::Field_Kind_TYPE_UINT32: |
||||||
|
case google::protobuf::Field_Kind_TYPE_FIXED32: { |
||||||
|
return DataPiece(static_cast<uint32>(0)); |
||||||
|
} |
||||||
|
default: { return DataPiece::NullData(); } |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
DefaultValueObjectWriter* DefaultValueObjectWriter::StartObject( |
||||||
|
StringPiece name) { |
||||||
|
if (current_ == NULL) { |
||||||
|
root_.reset(new Node(name.ToString(), &type_, OBJECT, DataPiece::NullData(), |
||||||
|
false)); |
||||||
|
root_->set_disable_normalize(GetAndResetDisableNormalize()); |
||||||
|
root_->PopulateChildren(typeinfo_.get()); |
||||||
|
current_ = root_.get(); |
||||||
|
return this; |
||||||
|
} |
||||||
|
MaybePopulateChildrenOfAny(current_); |
||||||
|
Node* child = current_->FindChild(name); |
||||||
|
if (current_->kind() == LIST || current_->kind() == MAP || child == NULL) { |
||||||
|
// If current_ is a list or a map node, we should create a new child and use
|
||||||
|
// the type of current_ as the type of the new child.
|
||||||
|
google::protobuf::scoped_ptr<Node> node(new Node( |
||||||
|
name.ToString(), ((current_->kind() == LIST || current_->kind() == MAP) |
||||||
|
? current_->type() |
||||||
|
: NULL), |
||||||
|
OBJECT, DataPiece::NullData(), false)); |
||||||
|
child = node.get(); |
||||||
|
current_->AddChild(node.release()); |
||||||
|
} |
||||||
|
|
||||||
|
child->set_is_placeholder(false); |
||||||
|
child->set_disable_normalize(GetAndResetDisableNormalize()); |
||||||
|
if (child->kind() == OBJECT && child->number_of_children() == 0) { |
||||||
|
child->PopulateChildren(typeinfo_.get()); |
||||||
|
} |
||||||
|
|
||||||
|
stack_.push(current_); |
||||||
|
current_ = child; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
DefaultValueObjectWriter* DefaultValueObjectWriter::EndObject() { |
||||||
|
if (stack_.empty()) { |
||||||
|
// The root object ends here. Writes out the tree.
|
||||||
|
WriteRoot(); |
||||||
|
return this; |
||||||
|
} |
||||||
|
current_ = stack_.top(); |
||||||
|
stack_.pop(); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
DefaultValueObjectWriter* DefaultValueObjectWriter::StartList( |
||||||
|
StringPiece name) { |
||||||
|
if (current_ == NULL) { |
||||||
|
root_.reset( |
||||||
|
new Node(name.ToString(), &type_, LIST, DataPiece::NullData(), false)); |
||||||
|
root_->set_disable_normalize(GetAndResetDisableNormalize()); |
||||||
|
current_ = root_.get(); |
||||||
|
return this; |
||||||
|
} |
||||||
|
MaybePopulateChildrenOfAny(current_); |
||||||
|
Node* child = current_->FindChild(name); |
||||||
|
if (child == NULL || child->kind() != LIST) { |
||||||
|
GOOGLE_LOG(WARNING) << "Cannot find field '" << name << "'."; |
||||||
|
google::protobuf::scoped_ptr<Node> node( |
||||||
|
new Node(name.ToString(), NULL, LIST, DataPiece::NullData(), false)); |
||||||
|
child = node.get(); |
||||||
|
current_->AddChild(node.release()); |
||||||
|
} |
||||||
|
child->set_is_placeholder(false); |
||||||
|
child->set_disable_normalize(GetAndResetDisableNormalize()); |
||||||
|
|
||||||
|
stack_.push(current_); |
||||||
|
current_ = child; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
void DefaultValueObjectWriter::WriteRoot() { |
||||||
|
root_->WriteTo(ow_); |
||||||
|
root_.reset(NULL); |
||||||
|
current_ = NULL; |
||||||
|
} |
||||||
|
|
||||||
|
DefaultValueObjectWriter* DefaultValueObjectWriter::EndList() { |
||||||
|
if (stack_.empty()) { |
||||||
|
WriteRoot(); |
||||||
|
return this; |
||||||
|
} |
||||||
|
current_ = stack_.top(); |
||||||
|
stack_.pop(); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
void DefaultValueObjectWriter::RenderDataPiece(StringPiece name, |
||||||
|
const DataPiece& data) { |
||||||
|
MaybePopulateChildrenOfAny(current_); |
||||||
|
util::StatusOr<string> data_string = data.ToString(); |
||||||
|
if (current_->type() != NULL && current_->type()->name() == kAnyType && |
||||||
|
name == "@type" && data_string.ok()) { |
||||||
|
const string& string_value = data_string.ValueOrDie(); |
||||||
|
// If the type of current_ is "Any" and its "@type" field is being set here,
|
||||||
|
// sets the type of current_ to be the type specified by the "@type".
|
||||||
|
util::StatusOr<const google::protobuf::Type*> found_type = |
||||||
|
typeinfo_->ResolveTypeUrl(string_value); |
||||||
|
if (!found_type.ok()) { |
||||||
|
GOOGLE_LOG(WARNING) << "Failed to resolve type '" << string_value << "'."; |
||||||
|
} else { |
||||||
|
current_->set_type(found_type.ValueOrDie()); |
||||||
|
} |
||||||
|
current_->set_is_any(true); |
||||||
|
// If the "@type" field is placed after other fields, we should populate
|
||||||
|
// other children of primitive type now. Otherwise, we should wait until the
|
||||||
|
// first value field is rendered before we populate the children, because
|
||||||
|
// the "value" field of a Any message could be omitted.
|
||||||
|
if (current_->number_of_children() > 1 && current_->type() != NULL) { |
||||||
|
current_->PopulateChildren(typeinfo_.get()); |
||||||
|
} |
||||||
|
} |
||||||
|
Node* child = current_->FindChild(name); |
||||||
|
if (child == NULL || child->kind() != PRIMITIVE) { |
||||||
|
GOOGLE_LOG(WARNING) << "Cannot find primitive field '" << name << "'."; |
||||||
|
// No children are found, creates a new child.
|
||||||
|
google::protobuf::scoped_ptr<Node> node( |
||||||
|
new Node(name.ToString(), NULL, PRIMITIVE, data, false)); |
||||||
|
child = node.get(); |
||||||
|
current_->AddChild(node.release()); |
||||||
|
} else { |
||||||
|
child->set_data(data); |
||||||
|
} |
||||||
|
child->set_disable_normalize(GetAndResetDisableNormalize()); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
} // namespace google
|
@ -0,0 +1,238 @@ |
|||||||
|
// 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_UTIL_CONVERTER_DEFAULT_VALUE_OBJECTWRITER_H__ |
||||||
|
#define GOOGLE_PROTOBUF_UTIL_CONVERTER_DEFAULT_VALUE_OBJECTWRITER_H__ |
||||||
|
|
||||||
|
#include <memory> |
||||||
|
#ifndef _SHARED_PTR_H |
||||||
|
#include <google/protobuf/stubs/shared_ptr.h> |
||||||
|
#endif |
||||||
|
#include <stack> |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
#include <google/protobuf/stubs/common.h> |
||||||
|
#include <google/protobuf/util/internal/type_info.h> |
||||||
|
#include <google/protobuf/util/internal/datapiece.h> |
||||||
|
#include <google/protobuf/util/internal/object_writer.h> |
||||||
|
#include <google/protobuf/util/internal/utility.h> |
||||||
|
#include <google/protobuf/util/type_resolver.h> |
||||||
|
#include <google/protobuf/stubs/stringpiece.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace converter { |
||||||
|
|
||||||
|
// An ObjectWriter that renders non-repeated primitive fields of proto messages
|
||||||
|
// with their default values. DefaultValueObjectWriter holds objects, lists and
|
||||||
|
// fields it receives in a tree structure and writes them out to another
|
||||||
|
// ObjectWriter when EndObject() is called on the root object. It also writes
|
||||||
|
// out all non-repeated primitive fields that haven't been explicitly rendered
|
||||||
|
// with their default values (0 for numbers, "" for strings, etc).
|
||||||
|
class DefaultValueObjectWriter : public ObjectWriter { |
||||||
|
public: |
||||||
|
DefaultValueObjectWriter(TypeResolver* type_resolver, |
||||||
|
const google::protobuf::Type& type, |
||||||
|
ObjectWriter* ow); |
||||||
|
|
||||||
|
virtual ~DefaultValueObjectWriter(); |
||||||
|
|
||||||
|
// ObjectWriter methods.
|
||||||
|
virtual DefaultValueObjectWriter* StartObject(StringPiece name); |
||||||
|
|
||||||
|
virtual DefaultValueObjectWriter* EndObject(); |
||||||
|
|
||||||
|
virtual DefaultValueObjectWriter* StartList(StringPiece name); |
||||||
|
|
||||||
|
virtual DefaultValueObjectWriter* EndList(); |
||||||
|
|
||||||
|
virtual DefaultValueObjectWriter* RenderBool(StringPiece name, bool value); |
||||||
|
|
||||||
|
virtual DefaultValueObjectWriter* RenderInt32(StringPiece name, int32 value); |
||||||
|
|
||||||
|
virtual DefaultValueObjectWriter* RenderUint32(StringPiece name, |
||||||
|
uint32 value); |
||||||
|
|
||||||
|
virtual DefaultValueObjectWriter* RenderInt64(StringPiece name, int64 value); |
||||||
|
|
||||||
|
virtual DefaultValueObjectWriter* RenderUint64(StringPiece name, |
||||||
|
uint64 value); |
||||||
|
|
||||||
|
virtual DefaultValueObjectWriter* RenderDouble(StringPiece name, |
||||||
|
double value); |
||||||
|
|
||||||
|
virtual DefaultValueObjectWriter* RenderFloat(StringPiece name, float value); |
||||||
|
|
||||||
|
virtual DefaultValueObjectWriter* RenderString(StringPiece name, |
||||||
|
StringPiece value); |
||||||
|
virtual DefaultValueObjectWriter* RenderBytes(StringPiece name, |
||||||
|
StringPiece value); |
||||||
|
|
||||||
|
virtual DefaultValueObjectWriter* RenderNull(StringPiece name); |
||||||
|
|
||||||
|
virtual DefaultValueObjectWriter* DisableCaseNormalizationForNextKey(); |
||||||
|
|
||||||
|
private: |
||||||
|
enum NodeKind { |
||||||
|
PRIMITIVE = 0, |
||||||
|
OBJECT = 1, |
||||||
|
LIST = 2, |
||||||
|
MAP = 3, |
||||||
|
}; |
||||||
|
|
||||||
|
// "Node" represents a node in the tree that holds the input of
|
||||||
|
// DefaultValueObjectWriter.
|
||||||
|
class Node { |
||||||
|
public: |
||||||
|
Node(const string& name, const google::protobuf::Type* type, NodeKind kind, |
||||||
|
const DataPiece& data, bool is_placeholder); |
||||||
|
virtual ~Node() { |
||||||
|
for (int i = 0; i < children_.size(); ++i) { |
||||||
|
delete children_[i]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Adds a child to this node. Takes ownership of this child.
|
||||||
|
void AddChild(Node* child) { children_.push_back(child); } |
||||||
|
|
||||||
|
// Finds the child given its name.
|
||||||
|
Node* FindChild(StringPiece name); |
||||||
|
|
||||||
|
// Populates children of this Node based on its type. If there are already
|
||||||
|
// children created, they will be merged to the result. Caller should pass
|
||||||
|
// in TypeInfo for looking up types of the children.
|
||||||
|
void PopulateChildren(TypeInfo* typeinfo); |
||||||
|
|
||||||
|
// If this node is a leaf (has data), writes the current node to the
|
||||||
|
// ObjectWriter; if not, then recursively writes the children to the
|
||||||
|
// ObjectWriter.
|
||||||
|
void WriteTo(ObjectWriter* ow); |
||||||
|
|
||||||
|
// Accessors
|
||||||
|
const string& name() const { return name_; } |
||||||
|
|
||||||
|
const google::protobuf::Type* type() { return type_; } |
||||||
|
|
||||||
|
void set_type(const google::protobuf::Type* type) { type_ = type; } |
||||||
|
|
||||||
|
NodeKind kind() { return kind_; } |
||||||
|
|
||||||
|
int number_of_children() { return children_.size(); } |
||||||
|
|
||||||
|
void set_data(const DataPiece& data) { data_ = data; } |
||||||
|
|
||||||
|
void set_disable_normalize(bool disable_normalize) { |
||||||
|
disable_normalize_ = disable_normalize; |
||||||
|
} |
||||||
|
|
||||||
|
bool is_any() { return is_any_; } |
||||||
|
|
||||||
|
void set_is_any(bool is_any) { is_any_ = is_any; } |
||||||
|
|
||||||
|
void set_is_placeholder(bool is_placeholder) { |
||||||
|
is_placeholder_ = is_placeholder; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
// Returns the Value Type of a map given the Type of the map entry and a
|
||||||
|
// TypeInfo instance.
|
||||||
|
const google::protobuf::Type* GetMapValueType( |
||||||
|
const google::protobuf::Type& entry_type, TypeInfo* typeinfo); |
||||||
|
|
||||||
|
// The name of this node.
|
||||||
|
string name_; |
||||||
|
// google::protobuf::Type of this node. Owned by TypeInfo.
|
||||||
|
const google::protobuf::Type* type_; |
||||||
|
// The kind of this node.
|
||||||
|
NodeKind kind_; |
||||||
|
// Whether to disable case normalization of the name.
|
||||||
|
bool disable_normalize_; |
||||||
|
// Whether this is a node for "Any".
|
||||||
|
bool is_any_; |
||||||
|
// The data of this node when it is a leaf node.
|
||||||
|
DataPiece data_; |
||||||
|
// Children of this node.
|
||||||
|
std::vector<Node*> children_; |
||||||
|
// Whether this node is a placeholder for an object or list automatically
|
||||||
|
// generated when creating the parent node. Should be set to false after
|
||||||
|
// the parent node's StartObject()/StartList() method is called with this
|
||||||
|
// node's name.
|
||||||
|
bool is_placeholder_; |
||||||
|
}; |
||||||
|
|
||||||
|
// Populates children of "node" if it is an "any" Node and its real type has
|
||||||
|
// been given.
|
||||||
|
void MaybePopulateChildrenOfAny(Node* node); |
||||||
|
|
||||||
|
// Writes the root_ node to ow_ and resets the root_ and current_ pointer to
|
||||||
|
// NULL.
|
||||||
|
void WriteRoot(); |
||||||
|
|
||||||
|
// Creates a DataPiece containing the default value of the type of the field.
|
||||||
|
static DataPiece CreateDefaultDataPieceForField( |
||||||
|
const google::protobuf::Field& field); |
||||||
|
|
||||||
|
// Returns disable_normalize_ and reset it to false.
|
||||||
|
bool GetAndResetDisableNormalize() { |
||||||
|
return disable_normalize_ ? (disable_normalize_ = false, true) : false; |
||||||
|
} |
||||||
|
|
||||||
|
// Adds or replaces the data_ of a primitive child node.
|
||||||
|
void RenderDataPiece(StringPiece name, const DataPiece& data); |
||||||
|
|
||||||
|
// Type information for all the types used in the descriptor. Used to find
|
||||||
|
// google::protobuf::Type of nested messages/enums.
|
||||||
|
google::protobuf::scoped_ptr<TypeInfo> typeinfo_; |
||||||
|
// google::protobuf::Type of the root message type.
|
||||||
|
const google::protobuf::Type& type_; |
||||||
|
// Holds copies of strings passed to RenderString.
|
||||||
|
vector<string*> string_values_; |
||||||
|
|
||||||
|
// Whether to disable case normalization of the next node.
|
||||||
|
bool disable_normalize_; |
||||||
|
// The current Node. Owned by its parents.
|
||||||
|
Node* current_; |
||||||
|
// The root Node.
|
||||||
|
google::protobuf::scoped_ptr<Node> root_; |
||||||
|
// The stack to hold the path of Nodes from current_ to root_;
|
||||||
|
std::stack<Node*> stack_; |
||||||
|
|
||||||
|
ObjectWriter* ow_; |
||||||
|
|
||||||
|
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DefaultValueObjectWriter); |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
|
||||||
|
} // namespace google
|
||||||
|
#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_DEFAULT_VALUE_OBJECTWRITER_H__
|
@ -0,0 +1,139 @@ |
|||||||
|
// 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/util/internal/default_value_objectwriter.h> |
||||||
|
#include <google/protobuf/util/internal/expecting_objectwriter.h> |
||||||
|
#include <google/protobuf/util/internal/testdata/default_value_test.pb.h> |
||||||
|
#include <google/protobuf/util/internal/type_info_test_helper.h> |
||||||
|
#include <google/protobuf/util/internal/constants.h> |
||||||
|
#include <gtest/gtest.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace converter { |
||||||
|
namespace testing { |
||||||
|
|
||||||
|
using google::protobuf::testing::DefaultValueTest; |
||||||
|
|
||||||
|
// Tests to cover some basic DefaultValueObjectWriter use cases. More tests are
|
||||||
|
// in the marshalling_test.cc and translator_integration_test.cc.
|
||||||
|
class DefaultValueObjectWriterTest |
||||||
|
: public ::testing::TestWithParam<testing::TypeInfoSource> { |
||||||
|
protected: |
||||||
|
DefaultValueObjectWriterTest() |
||||||
|
: helper_(GetParam()), mock_(), expects_(&mock_) { |
||||||
|
helper_.ResetTypeInfo(DefaultValueTest::descriptor()); |
||||||
|
testing_.reset(helper_.NewDefaultValueWriter( |
||||||
|
string(kTypeServiceBaseUrl) + "/" + |
||||||
|
DefaultValueTest::descriptor()->full_name(), |
||||||
|
&mock_)); |
||||||
|
} |
||||||
|
|
||||||
|
virtual ~DefaultValueObjectWriterTest() {} |
||||||
|
|
||||||
|
TypeInfoTestHelper helper_; |
||||||
|
MockObjectWriter mock_; |
||||||
|
ExpectingObjectWriter expects_; |
||||||
|
google::protobuf::scoped_ptr<DefaultValueObjectWriter> testing_; |
||||||
|
}; |
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest, |
||||||
|
DefaultValueObjectWriterTest, |
||||||
|
::testing::Values( |
||||||
|
testing::USE_TYPE_RESOLVER)); |
||||||
|
|
||||||
|
TEST_P(DefaultValueObjectWriterTest, Empty) { |
||||||
|
// Set expectation
|
||||||
|
expects_.StartObject("") |
||||||
|
->RenderDouble("double_value", 0.0) |
||||||
|
->RenderFloat("float_value", 0.0) |
||||||
|
->RenderInt64("int64_value", 0) |
||||||
|
->RenderUint64("uint64_value", 0) |
||||||
|
->RenderInt32("int32_value", 0) |
||||||
|
->RenderUint32("uint32_value", 0) |
||||||
|
->RenderBool("bool_value", false) |
||||||
|
->RenderString("string_value", "") |
||||||
|
->RenderBytes("bytes_value", "") |
||||||
|
->EndObject(); |
||||||
|
|
||||||
|
// Actual testing
|
||||||
|
testing_->StartObject("")->EndObject(); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_P(DefaultValueObjectWriterTest, NonDefaultDouble) { |
||||||
|
// Set expectation
|
||||||
|
expects_.StartObject("") |
||||||
|
->RenderDouble("double_value", 1.0) |
||||||
|
->RenderFloat("float_value", 0.0) |
||||||
|
->RenderInt64("int64_value", 0) |
||||||
|
->RenderUint64("uint64_value", 0) |
||||||
|
->RenderInt32("int32_value", 0) |
||||||
|
->RenderUint32("uint32_value", 0) |
||||||
|
->RenderBool("bool_value", false) |
||||||
|
->RenderString("string_value", "") |
||||||
|
->EndObject(); |
||||||
|
|
||||||
|
// Actual testing
|
||||||
|
testing_->StartObject("")->RenderDouble("double_value", 1.0)->EndObject(); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_P(DefaultValueObjectWriterTest, ShouldRetainUnknownField) { |
||||||
|
// Set expectation
|
||||||
|
expects_.StartObject("") |
||||||
|
->RenderDouble("double_value", 1.0) |
||||||
|
->RenderFloat("float_value", 0.0) |
||||||
|
->RenderInt64("int64_value", 0) |
||||||
|
->RenderUint64("uint64_value", 0) |
||||||
|
->RenderInt32("int32_value", 0) |
||||||
|
->RenderUint32("uint32_value", 0) |
||||||
|
->RenderBool("bool_value", false) |
||||||
|
->RenderString("string_value", "") |
||||||
|
->RenderString("unknown", "abc") |
||||||
|
->StartObject("unknown_object") |
||||||
|
->RenderString("unknown", "def") |
||||||
|
->EndObject() |
||||||
|
->EndObject(); |
||||||
|
|
||||||
|
// Actual testing
|
||||||
|
testing_->StartObject("") |
||||||
|
->RenderDouble("double_value", 1.0) |
||||||
|
->RenderString("unknown", "abc") |
||||||
|
->StartObject("unknown_object") |
||||||
|
->RenderString("unknown", "def") |
||||||
|
->EndObject() |
||||||
|
->EndObject(); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace testing
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
} // namespace google
|
@ -0,0 +1,42 @@ |
|||||||
|
// 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/util/internal/error_listener.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace converter { |
||||||
|
|
||||||
|
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
} // namespace google
|
@ -0,0 +1,99 @@ |
|||||||
|
// 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_UTIL_CONVERTER_ERROR_LISTENER_H__ |
||||||
|
#define GOOGLE_PROTOBUF_UTIL_CONVERTER_ERROR_LISTENER_H__ |
||||||
|
|
||||||
|
#include <memory> |
||||||
|
#ifndef _SHARED_PTR_H |
||||||
|
#include <google/protobuf/stubs/shared_ptr.h> |
||||||
|
#endif |
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include <google/protobuf/stubs/common.h> |
||||||
|
#include <google/protobuf/util/internal/location_tracker.h> |
||||||
|
#include <google/protobuf/stubs/stringpiece.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace converter { |
||||||
|
|
||||||
|
// Interface for error listener.
|
||||||
|
class LIBPROTOBUF_EXPORT ErrorListener { |
||||||
|
public: |
||||||
|
virtual ~ErrorListener() {} |
||||||
|
|
||||||
|
// Reports an invalid name at the given location.
|
||||||
|
virtual void InvalidName(const LocationTrackerInterface& loc, |
||||||
|
StringPiece unknown_name, StringPiece message) = 0; |
||||||
|
|
||||||
|
// Reports an invalid value for a field.
|
||||||
|
virtual void InvalidValue(const LocationTrackerInterface& loc, |
||||||
|
StringPiece type_name, StringPiece value) = 0; |
||||||
|
|
||||||
|
// Reports a missing required field.
|
||||||
|
virtual void MissingField(const LocationTrackerInterface& loc, |
||||||
|
StringPiece missing_name) = 0; |
||||||
|
|
||||||
|
protected: |
||||||
|
ErrorListener() {} |
||||||
|
|
||||||
|
private: |
||||||
|
// Do not add any data members to this class.
|
||||||
|
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ErrorListener); |
||||||
|
}; |
||||||
|
|
||||||
|
// An error listener that ignores all errors.
|
||||||
|
class LIBPROTOBUF_EXPORT NoopErrorListener : public ErrorListener { |
||||||
|
public: |
||||||
|
NoopErrorListener() {} |
||||||
|
virtual ~NoopErrorListener() {} |
||||||
|
|
||||||
|
virtual void InvalidName(const LocationTrackerInterface& loc, |
||||||
|
StringPiece unknown_name, StringPiece message) {} |
||||||
|
|
||||||
|
virtual void InvalidValue(const LocationTrackerInterface& loc, |
||||||
|
StringPiece type_name, StringPiece value) {} |
||||||
|
|
||||||
|
virtual void MissingField(const LocationTrackerInterface& loc, |
||||||
|
StringPiece missing_name) {} |
||||||
|
|
||||||
|
private: |
||||||
|
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(NoopErrorListener); |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
|
||||||
|
} // namespace google
|
||||||
|
#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_ERROR_LISTENER_H__
|
@ -0,0 +1,238 @@ |
|||||||
|
// 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_UTIL_CONVERTER_EXPECTING_OBJECTWRITER_H__ |
||||||
|
#define GOOGLE_PROTOBUF_UTIL_CONVERTER_EXPECTING_OBJECTWRITER_H__ |
||||||
|
|
||||||
|
// An implementation of ObjectWriter that automatically sets the
|
||||||
|
// gmock expectations for the response to a method. Every method
|
||||||
|
// returns the object itself for chaining.
|
||||||
|
//
|
||||||
|
// Usage:
|
||||||
|
// // Setup
|
||||||
|
// MockObjectWriter mock;
|
||||||
|
// ExpectingObjectWriter ow(&mock);
|
||||||
|
//
|
||||||
|
// // Set expectation
|
||||||
|
// ow.StartObject("")
|
||||||
|
// ->RenderString("key", "value")
|
||||||
|
// ->EndObject();
|
||||||
|
//
|
||||||
|
// // Actual testing
|
||||||
|
// mock.StartObject(StringPiece())
|
||||||
|
// ->RenderString("key", "value")
|
||||||
|
// ->EndObject();
|
||||||
|
|
||||||
|
#include <google/protobuf/stubs/common.h> |
||||||
|
#include <google/protobuf/util/internal/object_writer.h> |
||||||
|
#include <gmock/gmock.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace converter { |
||||||
|
|
||||||
|
using testing::IsEmpty; |
||||||
|
using testing::NanSensitiveDoubleEq; |
||||||
|
using testing::NanSensitiveFloatEq; |
||||||
|
using testing::Return; |
||||||
|
using testing::StrEq; |
||||||
|
using testing::TypedEq; |
||||||
|
|
||||||
|
class MockObjectWriter : public ObjectWriter { |
||||||
|
public: |
||||||
|
MockObjectWriter() {} |
||||||
|
|
||||||
|
MOCK_METHOD1(StartObject, ObjectWriter*(StringPiece)); |
||||||
|
MOCK_METHOD0(EndObject, ObjectWriter*()); |
||||||
|
MOCK_METHOD1(StartList, ObjectWriter*(StringPiece)); |
||||||
|
MOCK_METHOD0(EndList, ObjectWriter*()); |
||||||
|
MOCK_METHOD2(RenderBool, ObjectWriter*(StringPiece, const bool)); |
||||||
|
MOCK_METHOD2(RenderInt32, ObjectWriter*(StringPiece, const int32)); |
||||||
|
MOCK_METHOD2(RenderUint32, ObjectWriter*(StringPiece, const uint32)); |
||||||
|
MOCK_METHOD2(RenderInt64, ObjectWriter*(StringPiece, const int64)); |
||||||
|
MOCK_METHOD2(RenderUint64, ObjectWriter*(StringPiece, const uint64)); |
||||||
|
MOCK_METHOD2(RenderDouble, ObjectWriter*(StringPiece, const double)); |
||||||
|
MOCK_METHOD2(RenderFloat, ObjectWriter*(StringPiece, const float)); |
||||||
|
MOCK_METHOD2(RenderString, ObjectWriter*(StringPiece, StringPiece)); |
||||||
|
MOCK_METHOD2(RenderBytes, ObjectWriter*(StringPiece, StringPiece)); |
||||||
|
MOCK_METHOD1(RenderNull, ObjectWriter*(StringPiece)); |
||||||
|
}; |
||||||
|
|
||||||
|
class ExpectingObjectWriter : public ObjectWriter { |
||||||
|
public: |
||||||
|
explicit ExpectingObjectWriter(MockObjectWriter* mock) : mock_(mock) {} |
||||||
|
|
||||||
|
virtual ObjectWriter* StartObject(StringPiece name) { |
||||||
|
(name.empty() |
||||||
|
? EXPECT_CALL(*mock_, StartObject(IsEmpty())) |
||||||
|
: EXPECT_CALL(*mock_, StartObject(StrEq(name.ToString())))) |
||||||
|
.WillOnce(Return(mock_)) |
||||||
|
.RetiresOnSaturation(); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
virtual ObjectWriter* EndObject() { |
||||||
|
EXPECT_CALL(*mock_, EndObject()) |
||||||
|
.WillOnce(Return(mock_)) |
||||||
|
.RetiresOnSaturation(); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
virtual ObjectWriter* StartList(StringPiece name) { |
||||||
|
(name.empty() |
||||||
|
? EXPECT_CALL(*mock_, StartList(IsEmpty())) |
||||||
|
: EXPECT_CALL(*mock_, StartList(StrEq(name.ToString())))) |
||||||
|
.WillOnce(Return(mock_)) |
||||||
|
.RetiresOnSaturation(); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
virtual ObjectWriter* EndList() { |
||||||
|
EXPECT_CALL(*mock_, EndList()) |
||||||
|
.WillOnce(Return(mock_)) |
||||||
|
.RetiresOnSaturation(); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
virtual ObjectWriter* RenderBool(StringPiece name, const bool value) { |
||||||
|
(name.empty() |
||||||
|
? EXPECT_CALL(*mock_, RenderBool(IsEmpty(), TypedEq<bool>(value))) |
||||||
|
: EXPECT_CALL(*mock_, RenderBool(StrEq(name.ToString()), |
||||||
|
TypedEq<bool>(value)))) |
||||||
|
.WillOnce(Return(mock_)) |
||||||
|
.RetiresOnSaturation(); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
virtual ObjectWriter* RenderInt32(StringPiece name, const int32 value) { |
||||||
|
(name.empty() |
||||||
|
? EXPECT_CALL(*mock_, RenderInt32(IsEmpty(), TypedEq<int32>(value))) |
||||||
|
: EXPECT_CALL(*mock_, RenderInt32(StrEq(name.ToString()), |
||||||
|
TypedEq<int32>(value)))) |
||||||
|
.WillOnce(Return(mock_)) |
||||||
|
.RetiresOnSaturation(); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
virtual ObjectWriter* RenderUint32(StringPiece name, const uint32 value) { |
||||||
|
(name.empty() |
||||||
|
? EXPECT_CALL(*mock_, RenderUint32(IsEmpty(), TypedEq<uint32>(value))) |
||||||
|
: EXPECT_CALL(*mock_, RenderUint32(StrEq(name.ToString()), |
||||||
|
TypedEq<uint32>(value)))) |
||||||
|
.WillOnce(Return(mock_)) |
||||||
|
.RetiresOnSaturation(); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
virtual ObjectWriter* RenderInt64(StringPiece name, const int64 value) { |
||||||
|
(name.empty() |
||||||
|
? EXPECT_CALL(*mock_, RenderInt64(IsEmpty(), TypedEq<int64>(value))) |
||||||
|
: EXPECT_CALL(*mock_, RenderInt64(StrEq(name.ToString()), |
||||||
|
TypedEq<int64>(value)))) |
||||||
|
.WillOnce(Return(mock_)) |
||||||
|
.RetiresOnSaturation(); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
virtual ObjectWriter* RenderUint64(StringPiece name, const uint64 value) { |
||||||
|
(name.empty() |
||||||
|
? EXPECT_CALL(*mock_, RenderUint64(IsEmpty(), TypedEq<uint64>(value))) |
||||||
|
: EXPECT_CALL(*mock_, RenderUint64(StrEq(name.ToString()), |
||||||
|
TypedEq<uint64>(value)))) |
||||||
|
.WillOnce(Return(mock_)) |
||||||
|
.RetiresOnSaturation(); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
virtual ObjectWriter* RenderDouble(StringPiece name, const double value) { |
||||||
|
(name.empty() |
||||||
|
? EXPECT_CALL(*mock_, RenderDouble(IsEmpty(), |
||||||
|
NanSensitiveDoubleEq(value))) |
||||||
|
: EXPECT_CALL(*mock_, RenderDouble(StrEq(name.ToString()), |
||||||
|
NanSensitiveDoubleEq(value)))) |
||||||
|
.WillOnce(Return(mock_)) |
||||||
|
.RetiresOnSaturation(); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
virtual ObjectWriter* RenderFloat(StringPiece name, const float value) { |
||||||
|
(name.empty() |
||||||
|
? EXPECT_CALL(*mock_, RenderFloat(IsEmpty(), |
||||||
|
NanSensitiveFloatEq(value))) |
||||||
|
: EXPECT_CALL(*mock_, RenderFloat(StrEq(name.ToString()), |
||||||
|
NanSensitiveFloatEq(value)))) |
||||||
|
.WillOnce(Return(mock_)) |
||||||
|
.RetiresOnSaturation(); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
virtual ObjectWriter* RenderString(StringPiece name, StringPiece value) { |
||||||
|
(name.empty() |
||||||
|
? EXPECT_CALL(*mock_, RenderString(IsEmpty(), |
||||||
|
TypedEq<StringPiece>(value.ToString()))) |
||||||
|
: EXPECT_CALL(*mock_, RenderString(StrEq(name.ToString()), |
||||||
|
TypedEq<StringPiece>(value.ToString())))) |
||||||
|
.WillOnce(Return(mock_)) |
||||||
|
.RetiresOnSaturation(); |
||||||
|
return this; |
||||||
|
} |
||||||
|
virtual ObjectWriter* RenderBytes(StringPiece name, StringPiece value) { |
||||||
|
(name.empty() |
||||||
|
? EXPECT_CALL(*mock_, RenderBytes(IsEmpty(), TypedEq<StringPiece>( |
||||||
|
value.ToString()))) |
||||||
|
: EXPECT_CALL(*mock_, |
||||||
|
RenderBytes(StrEq(name.ToString()), |
||||||
|
TypedEq<StringPiece>(value.ToString())))) |
||||||
|
.WillOnce(Return(mock_)) |
||||||
|
.RetiresOnSaturation(); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
virtual ObjectWriter* RenderNull(StringPiece name) { |
||||||
|
(name.empty() ? EXPECT_CALL(*mock_, RenderNull(IsEmpty())) |
||||||
|
: EXPECT_CALL(*mock_, RenderNull(StrEq(name.ToString()))) |
||||||
|
.WillOnce(Return(mock_)) |
||||||
|
.RetiresOnSaturation()); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
MockObjectWriter* mock_; |
||||||
|
|
||||||
|
GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(ExpectingObjectWriter); |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
|
||||||
|
} // namespace google
|
||||||
|
#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_EXPECTING_OBJECTWRITER_H__
|
@ -0,0 +1,228 @@ |
|||||||
|
// 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/util/internal/field_mask_utility.h> |
||||||
|
|
||||||
|
#include <google/protobuf/stubs/strutil.h> |
||||||
|
#include <google/protobuf/stubs/status_macros.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
|
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace converter { |
||||||
|
|
||||||
|
namespace { |
||||||
|
inline util::Status CallPathSink(PathSinkCallback path_sink, |
||||||
|
StringPiece arg) { |
||||||
|
return path_sink->Run(arg); |
||||||
|
} |
||||||
|
|
||||||
|
util::Status CreatePublicError(util::error::Code code, |
||||||
|
const string& message) { |
||||||
|
return util::Status(code, message); |
||||||
|
} |
||||||
|
|
||||||
|
// Appends a FieldMask path segment to a prefix.
|
||||||
|
string AppendPathSegmentToPrefix(StringPiece prefix, StringPiece segment) { |
||||||
|
if (prefix.empty()) { |
||||||
|
return segment.ToString(); |
||||||
|
} |
||||||
|
if (segment.empty()) { |
||||||
|
return prefix.ToString(); |
||||||
|
} |
||||||
|
// If the segment is a map key, appends it to the prefix without the ".".
|
||||||
|
if (segment.starts_with("[\"")) { |
||||||
|
return StrCat(prefix, segment); |
||||||
|
} |
||||||
|
return StrCat(prefix, ".", segment); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
string ConvertFieldMaskPath(const StringPiece path, |
||||||
|
ConverterCallback converter) { |
||||||
|
string result; |
||||||
|
result.reserve(path.size() << 1); |
||||||
|
|
||||||
|
bool is_quoted = false; |
||||||
|
bool is_escaping = false; |
||||||
|
int current_segment_start = 0; |
||||||
|
|
||||||
|
// Loops until 1 passed the end of the input to make handling the last
|
||||||
|
// segment easier.
|
||||||
|
for (size_t i = 0; i <= path.size(); ++i) { |
||||||
|
// Outputs quoted string as-is.
|
||||||
|
if (is_quoted) { |
||||||
|
if (i == path.size()) { |
||||||
|
break; |
||||||
|
} |
||||||
|
result.push_back(path[i]); |
||||||
|
if (is_escaping) { |
||||||
|
is_escaping = false; |
||||||
|
} else if (path[i] == '\\') { |
||||||
|
is_escaping = true; |
||||||
|
} else if (path[i] == '\"') { |
||||||
|
current_segment_start = i + 1; |
||||||
|
is_quoted = false; |
||||||
|
} |
||||||
|
continue; |
||||||
|
} |
||||||
|
if (i == path.size() || path[i] == '.' || path[i] == '(' || |
||||||
|
path[i] == ')' || path[i] == '\"') { |
||||||
|
result += converter( |
||||||
|
path.substr(current_segment_start, i - current_segment_start)); |
||||||
|
if (i < path.size()) { |
||||||
|
result.push_back(path[i]); |
||||||
|
} |
||||||
|
current_segment_start = i + 1; |
||||||
|
} |
||||||
|
if (i < path.size() && path[i] == '\"') { |
||||||
|
is_quoted = true; |
||||||
|
} |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
util::Status DecodeCompactFieldMaskPaths(StringPiece paths, |
||||||
|
PathSinkCallback path_sink) { |
||||||
|
stack<string> prefix; |
||||||
|
int length = paths.length(); |
||||||
|
int previous_position = 0; |
||||||
|
bool in_map_key = false; |
||||||
|
bool is_escaping = false; |
||||||
|
// Loops until 1 passed the end of the input to make the handle of the last
|
||||||
|
// segment easier.
|
||||||
|
for (int i = 0; i <= length; ++i) { |
||||||
|
if (i != length) { |
||||||
|
// Skips everything in a map key until we hit the end of it, which is
|
||||||
|
// marked by an un-escaped '"' immediately followed by a ']'.
|
||||||
|
if (in_map_key) { |
||||||
|
if (is_escaping) { |
||||||
|
is_escaping = false; |
||||||
|
continue; |
||||||
|
} |
||||||
|
if (paths[i] == '\\') { |
||||||
|
is_escaping = true; |
||||||
|
continue; |
||||||
|
} |
||||||
|
if (paths[i] != '\"') { |
||||||
|
continue; |
||||||
|
} |
||||||
|
// Un-escaped '"' must be followed with a ']'.
|
||||||
|
if (i >= length - 1 || paths[i + 1] != ']') { |
||||||
|
return CreatePublicError( |
||||||
|
util::error::INVALID_ARGUMENT, |
||||||
|
StrCat("Invalid FieldMask '", paths, |
||||||
|
"'. Map keys should be represented as [\"some_key\"].")); |
||||||
|
} |
||||||
|
// The end of the map key ("\"]") has been found.
|
||||||
|
in_map_key = false; |
||||||
|
// Skips ']'.
|
||||||
|
i++; |
||||||
|
// Checks whether the key ends at the end of a path segment.
|
||||||
|
if (i < length - 1 && paths[i + 1] != '.' && paths[i + 1] != ',' && |
||||||
|
paths[i + 1] != ')' && paths[i + 1] != '(') { |
||||||
|
return CreatePublicError( |
||||||
|
util::error::INVALID_ARGUMENT, |
||||||
|
StrCat("Invalid FieldMask '", paths, |
||||||
|
"'. Map keys should be at the end of a path segment.")); |
||||||
|
} |
||||||
|
is_escaping = false; |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
// We are not in a map key, look for the start of one.
|
||||||
|
if (paths[i] == '[') { |
||||||
|
if (i >= length - 1 || paths[i + 1] != '\"') { |
||||||
|
return CreatePublicError( |
||||||
|
util::error::INVALID_ARGUMENT, |
||||||
|
StrCat("Invalid FieldMask '", paths, |
||||||
|
"'. Map keys should be represented as [\"some_key\"].")); |
||||||
|
} |
||||||
|
// "[\"" starts a map key.
|
||||||
|
in_map_key = true; |
||||||
|
i++; // Skips the '\"'.
|
||||||
|
continue; |
||||||
|
} |
||||||
|
// If the current character is not a special character (',', '(' or ')'),
|
||||||
|
// continue to the next.
|
||||||
|
if (paths[i] != ',' && paths[i] != ')' && paths[i] != '(') { |
||||||
|
continue; |
||||||
|
} |
||||||
|
} |
||||||
|
// Gets the current segment - sub-string between previous position (after
|
||||||
|
// '(', ')', ',', or the beginning of the input) and the current position.
|
||||||
|
StringPiece segment = |
||||||
|
paths.substr(previous_position, i - previous_position); |
||||||
|
string current_prefix = prefix.empty() ? "" : prefix.top(); |
||||||
|
|
||||||
|
if (i < length && paths[i] == '(') { |
||||||
|
// Builds a prefix and save it into the stack.
|
||||||
|
prefix.push(AppendPathSegmentToPrefix(current_prefix, segment)); |
||||||
|
} else if (!segment.empty()) { |
||||||
|
// When the current charactor is ')', ',' or the current position has
|
||||||
|
// passed the end of the input, builds and outputs a new paths by
|
||||||
|
// concatenating the last prefix with the current segment.
|
||||||
|
RETURN_IF_ERROR(CallPathSink( |
||||||
|
path_sink, AppendPathSegmentToPrefix(current_prefix, segment))); |
||||||
|
} |
||||||
|
|
||||||
|
// Removes the last prefix after seeing a ')'.
|
||||||
|
if (i < length && paths[i] == ')') { |
||||||
|
if (prefix.empty()) { |
||||||
|
return CreatePublicError( |
||||||
|
util::error::INVALID_ARGUMENT, |
||||||
|
StrCat("Invalid FieldMask '", paths, |
||||||
|
"'. Cannot find matching '(' for all ')'.")); |
||||||
|
} |
||||||
|
prefix.pop(); |
||||||
|
} |
||||||
|
previous_position = i + 1; |
||||||
|
} |
||||||
|
if (in_map_key) { |
||||||
|
return CreatePublicError( |
||||||
|
util::error::INVALID_ARGUMENT, |
||||||
|
StrCat("Invalid FieldMask '", paths, |
||||||
|
"'. Cannot find matching ']' for all '['.")); |
||||||
|
} |
||||||
|
if (!prefix.empty()) { |
||||||
|
return CreatePublicError( |
||||||
|
util::error::INVALID_ARGUMENT, |
||||||
|
StrCat("Invalid FieldMask '", paths, |
||||||
|
"'. Cannot find matching ')' for all '('.")); |
||||||
|
} |
||||||
|
return util::Status::OK; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
} // namespace google
|
@ -0,0 +1,72 @@ |
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// FieldMask related utility methods.
|
||||||
|
|
||||||
|
#ifndef GOOGLE_PROTOBUF_UTIL_CONVERTER_FIELD_MASK_UTILITY_H__ |
||||||
|
#define GOOGLE_PROTOBUF_UTIL_CONVERTER_FIELD_MASK_UTILITY_H__ |
||||||
|
|
||||||
|
#include <functional> |
||||||
|
#include <stack> |
||||||
|
|
||||||
|
#include <google/protobuf/stubs/common.h> |
||||||
|
#include <google/protobuf/stubs/stringpiece.h> |
||||||
|
#include <google/protobuf/stubs/status.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace converter { |
||||||
|
|
||||||
|
typedef string (*ConverterCallback)(StringPiece); |
||||||
|
typedef ResultCallback1<util::Status, StringPiece>* PathSinkCallback; |
||||||
|
|
||||||
|
// Applies a 'converter' to each segment of a FieldMask path and returns the
|
||||||
|
// result. Quoted strings in the 'path' are copied to the output as-is without
|
||||||
|
// converting their content. Escaping is supported within quoted strings.
|
||||||
|
// For example, "ab\"_c" will be returned as "ab\"_c" without any changes.
|
||||||
|
string ConvertFieldMaskPath(const StringPiece path, |
||||||
|
ConverterCallback converter); |
||||||
|
|
||||||
|
// Decodes a compact list of FieldMasks. For example, "a.b,a.c.d,a.c.e" will be
|
||||||
|
// decoded into a list of field paths - "a.b", "a.c.d", "a.c.e". And the results
|
||||||
|
// will be sent to 'path_sink', i.e. 'path_sink' will be called once per
|
||||||
|
// resulting path.
|
||||||
|
// Note that we also support Apiary style FieldMask form. The above example in
|
||||||
|
// the Apiary style will look like "a.b,a.c(d,e)".
|
||||||
|
util::Status DecodeCompactFieldMaskPaths(StringPiece paths, |
||||||
|
PathSinkCallback path_sink); |
||||||
|
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
|
||||||
|
} // namespace google
|
||||||
|
#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_FIELD_MASK_UTILITY_H__
|
@ -0,0 +1,403 @@ |
|||||||
|
// 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/util/internal/json_escaping.h> |
||||||
|
|
||||||
|
#include <google/protobuf/stubs/common.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace converter { |
||||||
|
|
||||||
|
namespace { |
||||||
|
|
||||||
|
// Array of hex characters for conversion to hex.
|
||||||
|
static const char kHex[] = "0123456789abcdef"; |
||||||
|
|
||||||
|
// Characters 0x00 to 0x9f are very commonly used, so we provide a special
|
||||||
|
// table lookup.
|
||||||
|
//
|
||||||
|
// For unicode code point ch < 0xa0:
|
||||||
|
// kCommonEscapes[ch] is the escaped string of ch, if escaping is needed;
|
||||||
|
// or an empty string, if escaping is not needed.
|
||||||
|
static const char kCommonEscapes[160][7] = { |
||||||
|
// C0 (ASCII and derivatives) control characters
|
||||||
|
"\\u0000", "\\u0001", "\\u0002", "\\u0003", // 0x00
|
||||||
|
"\\u0004", "\\u0005", "\\u0006", "\\u0007", |
||||||
|
"\\b", "\\t", "\\n", "\\u000b", |
||||||
|
"\\f", "\\r", "\\u000e", "\\u000f", |
||||||
|
"\\u0010", "\\u0011", "\\u0012", "\\u0013", // 0x10
|
||||||
|
"\\u0014", "\\u0015", "\\u0016", "\\u0017", |
||||||
|
"\\u0018", "\\u0019", "\\u001a", "\\u001b", |
||||||
|
"\\u001c", "\\u001d", "\\u001e", "\\u001f", |
||||||
|
// Escaping of " and \ are required by www.json.org string definition.
|
||||||
|
// Escaping of < and > are required for HTML security.
|
||||||
|
"", "", "\\\"", "", "", "", "", "", // 0x20
|
||||||
|
"", "", "", "", "", "", "", "", |
||||||
|
"", "", "", "", "", "", "", "", // 0x30
|
||||||
|
"", "", "", "", "\\u003c", "", "\\u003e", "", |
||||||
|
"", "", "", "", "", "", "", "", // 0x40
|
||||||
|
"", "", "", "", "", "", "", "", |
||||||
|
"", "", "", "", "", "", "", "", // 0x50
|
||||||
|
"", "", "", "", "\\\\", "", "", "", |
||||||
|
"", "", "", "", "", "", "", "", // 0x60
|
||||||
|
"", "", "", "", "", "", "", "", |
||||||
|
"", "", "", "", "", "", "", "", // 0x70
|
||||||
|
"", "", "", "", "", "", "", "\\u007f", |
||||||
|
// C1 (ISO 8859 and Unicode) extended control characters
|
||||||
|
"\\u0080", "\\u0081", "\\u0082", "\\u0083", // 0x80
|
||||||
|
"\\u0084", "\\u0085", "\\u0086", "\\u0087", |
||||||
|
"\\u0088", "\\u0089", "\\u008a", "\\u008b", |
||||||
|
"\\u008c", "\\u008d", "\\u008e", "\\u008f", |
||||||
|
"\\u0090", "\\u0091", "\\u0092", "\\u0093", // 0x90
|
||||||
|
"\\u0094", "\\u0095", "\\u0096", "\\u0097", |
||||||
|
"\\u0098", "\\u0099", "\\u009a", "\\u009b", |
||||||
|
"\\u009c", "\\u009d", "\\u009e", "\\u009f" |
||||||
|
}; |
||||||
|
|
||||||
|
// Determines if the given char value is a unicode high-surrogate code unit.
|
||||||
|
// Such values do not represent characters by themselves, but are used in the
|
||||||
|
// representation of supplementary characters in the utf-16 encoding.
|
||||||
|
inline bool IsHighSurrogate(uint16 c) { |
||||||
|
// Optimized form of:
|
||||||
|
// return c >= kMinHighSurrogate && c <= kMaxHighSurrogate;
|
||||||
|
// (Reduced from 3 ALU instructions to 2 ALU instructions)
|
||||||
|
return (c & ~(JsonEscaping::kMaxHighSurrogate - |
||||||
|
JsonEscaping::kMinHighSurrogate)) |
||||||
|
== JsonEscaping::kMinHighSurrogate; |
||||||
|
} |
||||||
|
|
||||||
|
// Determines if the given char value is a unicode low-surrogate code unit.
|
||||||
|
// Such values do not represent characters by themselves, but are used in the
|
||||||
|
// representation of supplementary characters in the utf-16 encoding.
|
||||||
|
inline bool IsLowSurrogate(uint16 c) { |
||||||
|
// Optimized form of:
|
||||||
|
// return c >= kMinLowSurrogate && c <= kMaxLowSurrogate;
|
||||||
|
// (Reduced from 3 ALU instructions to 2 ALU instructions)
|
||||||
|
return (c & ~(JsonEscaping::kMaxLowSurrogate - |
||||||
|
JsonEscaping::kMinLowSurrogate)) |
||||||
|
== JsonEscaping::kMinLowSurrogate; |
||||||
|
} |
||||||
|
|
||||||
|
// Determines if the given char value is a unicode surrogate code unit (either
|
||||||
|
// high-surrogate or low-surrogate).
|
||||||
|
inline bool IsSurrogate(uint32 c) { |
||||||
|
// Optimized form of:
|
||||||
|
// return c >= kMinHighSurrogate && c <= kMaxLowSurrogate;
|
||||||
|
// (Reduced from 3 ALU instructions to 2 ALU instructions)
|
||||||
|
return (c & 0xfffff800) == JsonEscaping::kMinHighSurrogate; |
||||||
|
} |
||||||
|
|
||||||
|
// Returns true if the given unicode code point cp is
|
||||||
|
// in the supplementary character range.
|
||||||
|
inline bool IsSupplementalCodePoint(uint32 cp) { |
||||||
|
// Optimized form of:
|
||||||
|
// return kMinSupplementaryCodePoint <= cp && cp <= kMaxCodePoint;
|
||||||
|
// (Reduced from 3 ALU instructions to 2 ALU instructions)
|
||||||
|
return (cp & ~(JsonEscaping::kMinSupplementaryCodePoint - 1)) |
||||||
|
< JsonEscaping::kMaxCodePoint; |
||||||
|
} |
||||||
|
|
||||||
|
// Returns true if the given unicode code point cp is a valid
|
||||||
|
// unicode code point (i.e. in the range 0 <= cp <= kMaxCodePoint).
|
||||||
|
inline bool IsValidCodePoint(uint32 cp) { |
||||||
|
return cp <= JsonEscaping::kMaxCodePoint; |
||||||
|
} |
||||||
|
|
||||||
|
// Converts the specified surrogate pair to its supplementary code point value.
|
||||||
|
// It is the callers' responsibility to validate the specified surrogate pair.
|
||||||
|
inline uint32 ToCodePoint(uint16 high, uint16 low) { |
||||||
|
// Optimized form of:
|
||||||
|
// return ((high - kMinHighSurrogate) << 10)
|
||||||
|
// + (low - kMinLowSurrogate)
|
||||||
|
// + kMinSupplementaryCodePoint;
|
||||||
|
// (Reduced from 5 ALU instructions to 3 ALU instructions)
|
||||||
|
return (high << 10) + low + |
||||||
|
(JsonEscaping::kMinSupplementaryCodePoint |
||||||
|
- (static_cast<unsigned>(JsonEscaping::kMinHighSurrogate) << 10) |
||||||
|
- JsonEscaping::kMinLowSurrogate); |
||||||
|
} |
||||||
|
|
||||||
|
// Returns the low surrogate for the given unicode code point. The result is
|
||||||
|
// meaningless if the given code point is not a supplementary character.
|
||||||
|
inline uint16 ToLowSurrogate(uint32 cp) { |
||||||
|
return (cp & (JsonEscaping::kMaxLowSurrogate |
||||||
|
- JsonEscaping::kMinLowSurrogate)) |
||||||
|
+ JsonEscaping::kMinLowSurrogate; |
||||||
|
} |
||||||
|
|
||||||
|
// Returns the high surrogate for the given unicode code point. The result is
|
||||||
|
// meaningless if the given code point is not a supplementary character.
|
||||||
|
inline uint16 ToHighSurrogate(uint32 cp) { |
||||||
|
return (cp >> 10) + (JsonEscaping::kMinHighSurrogate - |
||||||
|
(JsonEscaping::kMinSupplementaryCodePoint >> 10)); |
||||||
|
} |
||||||
|
|
||||||
|
// Input str is encoded in UTF-8. A unicode code point could be encoded in
|
||||||
|
// UTF-8 using anywhere from 1 to 4 characters, and it could span multiple
|
||||||
|
// reads of the ByteSource.
|
||||||
|
//
|
||||||
|
// This function reads the next unicode code point from the input (str) at
|
||||||
|
// the given position (index), taking into account any left-over partial
|
||||||
|
// code point from the previous iteration (cp), together with the number
|
||||||
|
// of characters left to read to complete this code point (num_left).
|
||||||
|
//
|
||||||
|
// This function assumes that the input (str) is valid at the given position
|
||||||
|
// (index). In order words, at least one character could be read successfully.
|
||||||
|
//
|
||||||
|
// The code point read (partial or complete) is stored in (cp). Upon return,
|
||||||
|
// (num_left) stores the number of characters that has yet to be read in
|
||||||
|
// order to complete the current unicode code point. If the read is complete,
|
||||||
|
// then (num_left) is 0. Also, (num_read) is the number of characters read.
|
||||||
|
//
|
||||||
|
// Returns false if we encounter an invalid UTF-8 string. Returns true
|
||||||
|
// otherwise, including the case when we reach the end of the input (str)
|
||||||
|
// before a complete unicode code point is read.
|
||||||
|
bool ReadCodePoint(StringPiece str, int index, |
||||||
|
uint32 *cp, int* num_left, int *num_read) { |
||||||
|
if (*num_left == 0) { |
||||||
|
// Last read was complete. Start reading a new unicode code point.
|
||||||
|
*cp = str[index++]; |
||||||
|
*num_read = 1; |
||||||
|
// The length of the code point is determined from reading the first byte.
|
||||||
|
//
|
||||||
|
// If the first byte is between:
|
||||||
|
// 0..0x7f: that's the value of the code point.
|
||||||
|
// 0x80..0xbf: <invalid>
|
||||||
|
// 0xc0..0xdf: 11-bit code point encoded in 2 bytes.
|
||||||
|
// bit 10-6, bit 5-0
|
||||||
|
// 0xe0..0xef: 16-bit code point encoded in 3 bytes.
|
||||||
|
// bit 15-12, bit 11-6, bit 5-0
|
||||||
|
// 0xf0..0xf7: 21-bit code point encoded in 4 bytes.
|
||||||
|
// bit 20-18, bit 17-12, bit 11-6, bit 5-0
|
||||||
|
// 0xf8..0xff: <invalid>
|
||||||
|
//
|
||||||
|
// Meaning of each bit:
|
||||||
|
// <msb> bit 7: 0 - single byte code point: bits 6-0 are values.
|
||||||
|
// 1 - multibyte code point
|
||||||
|
// bit 6: 0 - subsequent bytes of multibyte code point:
|
||||||
|
// bits 5-0 are values.
|
||||||
|
// 1 - first byte of multibyte code point
|
||||||
|
// bit 5: 0 - first byte of 2-byte code point: bits 4-0 are values.
|
||||||
|
// 1 - first byte of code point with >= 3 bytes.
|
||||||
|
// bit 4: 0 - first byte of 3-byte code point: bits 3-0 are values.
|
||||||
|
// 1 - first byte of code point with >= 4 bytes.
|
||||||
|
// bit 3: 0 - first byte of 4-byte code point: bits 2-0 are values.
|
||||||
|
// 1 - reserved for future expansion.
|
||||||
|
if (*cp <= 0x7f) { |
||||||
|
return true; |
||||||
|
} else if (*cp <= 0xbf) { |
||||||
|
return false; |
||||||
|
} else if (*cp <= 0xdf) { |
||||||
|
*cp &= 0x1f; |
||||||
|
*num_left = 1; |
||||||
|
} else if (*cp <= 0xef) { |
||||||
|
*cp &= 0x0f; |
||||||
|
*num_left = 2; |
||||||
|
} else if (*cp <= 0xf7) { |
||||||
|
*cp &= 0x07; |
||||||
|
*num_left = 3; |
||||||
|
} else { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} else { |
||||||
|
// Last read was partial. Initialize num_read to 0 and continue reading
|
||||||
|
// the last unicode code point.
|
||||||
|
*num_read = 0; |
||||||
|
} |
||||||
|
while (*num_left > 0 && index < str.size()) { |
||||||
|
uint32 ch = str[index++]; |
||||||
|
--(*num_left); |
||||||
|
++(*num_read); |
||||||
|
*cp = (*cp << 6) | (ch & 0x3f); |
||||||
|
if (ch < 0x80 || ch > 0xbf) return false; |
||||||
|
} |
||||||
|
return *num_left > 0 || (!IsSurrogate(*cp) && IsValidCodePoint(*cp)); |
||||||
|
} |
||||||
|
|
||||||
|
// Stores the 16-bit unicode code point as its hexadecimal digits in buffer
|
||||||
|
// and returns a StringPiece that points to this buffer. The input buffer needs
|
||||||
|
// to be at least 6 bytes long.
|
||||||
|
StringPiece ToHex(uint16 cp, char* buffer) { |
||||||
|
buffer[5] = kHex[cp & 0x0f]; |
||||||
|
cp >>= 4; |
||||||
|
buffer[4] = kHex[cp & 0x0f]; |
||||||
|
cp >>= 4; |
||||||
|
buffer[3] = kHex[cp & 0x0f]; |
||||||
|
cp >>= 4; |
||||||
|
buffer[2] = kHex[cp & 0x0f]; |
||||||
|
return StringPiece(buffer, 0, 6); |
||||||
|
} |
||||||
|
|
||||||
|
// Stores the 32-bit unicode code point as its hexadecimal digits in buffer
|
||||||
|
// and returns a StringPiece that points to this buffer. The input buffer needs
|
||||||
|
// to be at least 12 bytes long.
|
||||||
|
StringPiece ToSurrogateHex(uint32 cp, char* buffer) { |
||||||
|
uint16 low = ToLowSurrogate(cp); |
||||||
|
uint16 high = ToHighSurrogate(cp); |
||||||
|
|
||||||
|
buffer[11] = kHex[low & 0x0f]; |
||||||
|
low >>= 4; |
||||||
|
buffer[10] = kHex[low & 0x0f]; |
||||||
|
low >>= 4; |
||||||
|
buffer[9] = kHex[low & 0x0f]; |
||||||
|
low >>= 4; |
||||||
|
buffer[8] = kHex[low & 0x0f]; |
||||||
|
|
||||||
|
buffer[5] = kHex[high & 0x0f]; |
||||||
|
high >>= 4; |
||||||
|
buffer[4] = kHex[high & 0x0f]; |
||||||
|
high >>= 4; |
||||||
|
buffer[3] = kHex[high & 0x0f]; |
||||||
|
high >>= 4; |
||||||
|
buffer[2] = kHex[high & 0x0f]; |
||||||
|
|
||||||
|
return StringPiece(buffer, 12); |
||||||
|
} |
||||||
|
|
||||||
|
// If the given unicode code point needs escaping, then returns the
|
||||||
|
// escaped form. The returned StringPiece either points to statically
|
||||||
|
// pre-allocated char[] or to the given buffer. The input buffer needs
|
||||||
|
// to be at least 12 bytes long.
|
||||||
|
//
|
||||||
|
// If the given unicode code point does not need escaping, an empty
|
||||||
|
// StringPiece is returned.
|
||||||
|
StringPiece EscapeCodePoint(uint32 cp, char* buffer) { |
||||||
|
if (cp < 0xa0) return kCommonEscapes[cp]; |
||||||
|
switch (cp) { |
||||||
|
// These are not required by json spec
|
||||||
|
// but used to prevent security bugs in javascript.
|
||||||
|
case 0xfeff: // Zero width no-break space
|
||||||
|
case 0xfff9: // Interlinear annotation anchor
|
||||||
|
case 0xfffa: // Interlinear annotation separator
|
||||||
|
case 0xfffb: // Interlinear annotation terminator
|
||||||
|
|
||||||
|
case 0x00ad: // Soft-hyphen
|
||||||
|
case 0x06dd: // Arabic end of ayah
|
||||||
|
case 0x070f: // Syriac abbreviation mark
|
||||||
|
case 0x17b4: // Khmer vowel inherent Aq
|
||||||
|
case 0x17b5: // Khmer vowel inherent Aa
|
||||||
|
return ToHex(cp, buffer); |
||||||
|
|
||||||
|
default: |
||||||
|
if ((cp >= 0x0600 && cp <= 0x0603) || // Arabic signs
|
||||||
|
(cp >= 0x200b && cp <= 0x200f) || // Zero width etc.
|
||||||
|
(cp >= 0x2028 && cp <= 0x202e) || // Separators etc.
|
||||||
|
(cp >= 0x2060 && cp <= 0x2064) || // Invisible etc.
|
||||||
|
(cp >= 0x206a && cp <= 0x206f)) { // Shaping etc.
|
||||||
|
return ToHex(cp, buffer); |
||||||
|
} |
||||||
|
|
||||||
|
if (cp == 0x000e0001 || // Language tag
|
||||||
|
(cp >= 0x0001d173 && cp <= 0x0001d17a) || // Music formatting
|
||||||
|
(cp >= 0x000e0020 && cp <= 0x000e007f)) { // TAG symbols
|
||||||
|
return ToSurrogateHex(cp, buffer); |
||||||
|
} |
||||||
|
} |
||||||
|
return StringPiece(); |
||||||
|
} |
||||||
|
|
||||||
|
// Tries to escape the given code point first. If the given code point
|
||||||
|
// does not need to be escaped, but force_output is true, then render
|
||||||
|
// the given multi-byte code point in UTF8 in the buffer and returns it.
|
||||||
|
StringPiece EscapeCodePoint(uint32 cp, char* buffer, bool force_output) { |
||||||
|
StringPiece sp = EscapeCodePoint(cp, buffer); |
||||||
|
if (force_output && sp.empty()) { |
||||||
|
buffer[5] = (cp & 0x3f) | 0x80; |
||||||
|
cp >>= 6; |
||||||
|
if (cp <= 0x1f) { |
||||||
|
buffer[4] = cp | 0xc0; |
||||||
|
sp.set(buffer + 4, 2); |
||||||
|
return sp; |
||||||
|
} |
||||||
|
buffer[4] = (cp & 0x3f) | 0x80; |
||||||
|
cp >>= 6; |
||||||
|
if (cp <= 0x0f) { |
||||||
|
buffer[3] = cp | 0xe0; |
||||||
|
sp.set(buffer + 3, 3); |
||||||
|
return sp; |
||||||
|
} |
||||||
|
buffer[3] = (cp & 0x3f) | 0x80; |
||||||
|
buffer[2] = ((cp >> 6) & 0x07) | 0xf0; |
||||||
|
sp.set(buffer + 2, 4); |
||||||
|
} |
||||||
|
return sp; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void JsonEscaping::Escape(strings::ByteSource* input, |
||||||
|
strings::ByteSink* output) { |
||||||
|
char buffer[12] = "\\udead\\ubee"; |
||||||
|
uint32 cp = 0; // Current unicode code point.
|
||||||
|
int num_left = 0; // Num of chars to read to complete the code point.
|
||||||
|
while (input->Available() > 0) { |
||||||
|
StringPiece str = input->Peek(); |
||||||
|
StringPiece escaped; |
||||||
|
int i = 0; |
||||||
|
int num_read; |
||||||
|
bool ok; |
||||||
|
bool cp_was_split = num_left > 0; |
||||||
|
// Loop until we encounter either
|
||||||
|
// i) a code point that needs to be escaped; or
|
||||||
|
// ii) a split code point is completely read; or
|
||||||
|
// iii) a character that is not a valid utf8; or
|
||||||
|
// iv) end of the StringPiece str is reached.
|
||||||
|
do { |
||||||
|
ok = ReadCodePoint(str, i, &cp, &num_left, &num_read); |
||||||
|
if (num_left > 0 || !ok) break; // case iii or iv
|
||||||
|
escaped = EscapeCodePoint(cp, buffer, cp_was_split); |
||||||
|
if (!escaped.empty()) break; // case i or ii
|
||||||
|
i += num_read; |
||||||
|
num_read = 0; |
||||||
|
} while (i < str.length()); // case iv
|
||||||
|
// First copy the un-escaped prefix, if any, to the output ByteSink.
|
||||||
|
if (i > 0) input->CopyTo(output, i); |
||||||
|
if (num_read > 0) input->Skip(num_read); |
||||||
|
if (!ok) { |
||||||
|
// Case iii: Report error.
|
||||||
|
// TODO(wpoon): Add error reporting.
|
||||||
|
num_left = 0; |
||||||
|
} else if (num_left == 0 && !escaped.empty()) { |
||||||
|
// Case i or ii: Append the escaped code point to the output ByteSink.
|
||||||
|
output->Append(escaped.data(), escaped.size()); |
||||||
|
} |
||||||
|
} |
||||||
|
if (num_left > 0) { |
||||||
|
// Treat as case iii: report error.
|
||||||
|
// TODO(wpoon): Add error reporting.
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
} // namespace google
|
@ -0,0 +1,91 @@ |
|||||||
|
// 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 NET_PROTO2_UTIL_CONVERTER_STRINGS_JSON_ESCAPING_H_ |
||||||
|
#define NET_PROTO2_UTIL_CONVERTER_STRINGS_JSON_ESCAPING_H_ |
||||||
|
|
||||||
|
#include <google/protobuf/stubs/common.h> |
||||||
|
#include <google/protobuf/stubs/bytestream.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace converter { |
||||||
|
|
||||||
|
class JsonEscaping { |
||||||
|
public: |
||||||
|
// The minimum value of a unicode high-surrogate code unit in the utf-16
|
||||||
|
// encoding. A high-surrogate is also known as a leading-surrogate.
|
||||||
|
// See http://www.unicode.org/glossary/#high_surrogate_code_unit
|
||||||
|
static const uint16 kMinHighSurrogate = 0xd800; |
||||||
|
|
||||||
|
// The maximum value of a unicide high-surrogate code unit in the utf-16
|
||||||
|
// encoding. A high-surrogate is also known as a leading-surrogate.
|
||||||
|
// See http://www.unicode.org/glossary/#high_surrogate_code_unit
|
||||||
|
static const uint16 kMaxHighSurrogate = 0xdbff; |
||||||
|
|
||||||
|
// The minimum value of a unicode low-surrogate code unit in the utf-16
|
||||||
|
// encoding. A low-surrogate is also known as a trailing-surrogate.
|
||||||
|
// See http://www.unicode.org/glossary/#low_surrogate_code_unit
|
||||||
|
static const uint16 kMinLowSurrogate = 0xdc00; |
||||||
|
|
||||||
|
// The maximum value of a unicode low-surrogate code unit in the utf-16
|
||||||
|
// encoding. A low-surrogate is also known as a trailing surrogate.
|
||||||
|
// See http://www.unicode.org/glossary/#low_surrogate_code_unit
|
||||||
|
static const uint16 kMaxLowSurrogate = 0xdfff; |
||||||
|
|
||||||
|
// The minimum value of a unicode supplementary code point.
|
||||||
|
// See http://www.unicode.org/glossary/#supplementary_code_point
|
||||||
|
static const uint32 kMinSupplementaryCodePoint = 0x010000; |
||||||
|
|
||||||
|
// The minimum value of a unicode code point.
|
||||||
|
// See http://www.unicode.org/glossary/#code_point
|
||||||
|
static const uint32 kMinCodePoint = 0x000000; |
||||||
|
|
||||||
|
// The maximum value of a unicode code point.
|
||||||
|
// See http://www.unicode.org/glossary/#code_point
|
||||||
|
static const uint32 kMaxCodePoint = 0x10ffff; |
||||||
|
|
||||||
|
JsonEscaping() {} |
||||||
|
virtual ~JsonEscaping() {} |
||||||
|
|
||||||
|
// Escape the given ByteSource to the given ByteSink.
|
||||||
|
static void Escape(strings::ByteSource* input, strings::ByteSink* output); |
||||||
|
|
||||||
|
private: |
||||||
|
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(JsonEscaping); |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
|
||||||
|
#endif // NET_PROTO2_UTIL_CONVERTER_STRINGS_JSON_ESCAPING_H_
|
||||||
|
} // namespace google
|
@ -0,0 +1,175 @@ |
|||||||
|
// 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/util/internal/json_objectwriter.h> |
||||||
|
|
||||||
|
#include <math.h> |
||||||
|
|
||||||
|
#include <google/protobuf/stubs/casts.h> |
||||||
|
#include <google/protobuf/stubs/common.h> |
||||||
|
#include <google/protobuf/util/internal/utility.h> |
||||||
|
#include <google/protobuf/util/internal/json_escaping.h> |
||||||
|
#include <google/protobuf/stubs/strutil.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace converter { |
||||||
|
|
||||||
|
using strings::ArrayByteSource; |
||||||
|
|
||||||
|
JsonObjectWriter::~JsonObjectWriter() { |
||||||
|
if (!element_->is_root()) { |
||||||
|
GOOGLE_LOG(WARNING) << "JsonObjectWriter was not fully closed."; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
JsonObjectWriter* JsonObjectWriter::StartObject(StringPiece name) { |
||||||
|
WritePrefix(name); |
||||||
|
WriteChar('{'); |
||||||
|
Push(); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
JsonObjectWriter* JsonObjectWriter::EndObject() { |
||||||
|
Pop(); |
||||||
|
WriteChar('}'); |
||||||
|
if (element()->is_root()) NewLine(); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
JsonObjectWriter* JsonObjectWriter::StartList(StringPiece name) { |
||||||
|
WritePrefix(name); |
||||||
|
WriteChar('['); |
||||||
|
Push(); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
JsonObjectWriter* JsonObjectWriter::EndList() { |
||||||
|
Pop(); |
||||||
|
WriteChar(']'); |
||||||
|
if (element()->is_root()) NewLine(); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
JsonObjectWriter* JsonObjectWriter::RenderBool(StringPiece name, |
||||||
|
const bool value) { |
||||||
|
return RenderSimple(name, value ? "true" : "false"); |
||||||
|
} |
||||||
|
|
||||||
|
JsonObjectWriter* JsonObjectWriter::RenderInt32(StringPiece name, |
||||||
|
const int32 value) { |
||||||
|
return RenderSimple(name, SimpleItoa(value)); |
||||||
|
} |
||||||
|
|
||||||
|
JsonObjectWriter* JsonObjectWriter::RenderUint32(StringPiece name, |
||||||
|
const uint32 value) { |
||||||
|
return RenderSimple(name, SimpleItoa(value)); |
||||||
|
} |
||||||
|
|
||||||
|
JsonObjectWriter* JsonObjectWriter::RenderInt64(StringPiece name, |
||||||
|
const int64 value) { |
||||||
|
WritePrefix(name); |
||||||
|
WriteChar('"'); |
||||||
|
stream_->WriteString(SimpleItoa(value)); |
||||||
|
WriteChar('"'); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
JsonObjectWriter* JsonObjectWriter::RenderUint64(StringPiece name, |
||||||
|
const uint64 value) { |
||||||
|
WritePrefix(name); |
||||||
|
WriteChar('"'); |
||||||
|
stream_->WriteString(SimpleItoa(value)); |
||||||
|
WriteChar('"'); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
JsonObjectWriter* JsonObjectWriter::RenderDouble(StringPiece name, |
||||||
|
const double value) { |
||||||
|
if (isfinite(value)) return RenderSimple(name, SimpleDtoa(value)); |
||||||
|
|
||||||
|
// Render quoted with NaN/Infinity-aware DoubleAsString.
|
||||||
|
return RenderString(name, DoubleAsString(value)); |
||||||
|
} |
||||||
|
|
||||||
|
JsonObjectWriter* JsonObjectWriter::RenderFloat(StringPiece name, |
||||||
|
const float value) { |
||||||
|
if (isfinite(value)) return RenderSimple(name, SimpleFtoa(value)); |
||||||
|
|
||||||
|
// Render quoted with NaN/Infinity-aware FloatAsString.
|
||||||
|
return RenderString(name, FloatAsString(value)); |
||||||
|
} |
||||||
|
|
||||||
|
JsonObjectWriter* JsonObjectWriter::RenderString(StringPiece name, |
||||||
|
StringPiece value) { |
||||||
|
WritePrefix(name); |
||||||
|
WriteChar('"'); |
||||||
|
ArrayByteSource source(value); |
||||||
|
JsonEscaping::Escape(&source, &sink_); |
||||||
|
WriteChar('"'); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
JsonObjectWriter* JsonObjectWriter::RenderBytes(StringPiece name, |
||||||
|
StringPiece value) { |
||||||
|
WritePrefix(name); |
||||||
|
string base64; |
||||||
|
WebSafeBase64EscapeWithPadding(value, &base64); |
||||||
|
WriteChar('"'); |
||||||
|
// TODO(wpoon): Consider a ByteSink solution that writes the base64 bytes
|
||||||
|
// directly to the stream, rather than first putting them
|
||||||
|
// into a string and then writing them to the stream.
|
||||||
|
stream_->WriteRaw(base64.data(), base64.size()); |
||||||
|
WriteChar('"'); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
JsonObjectWriter* JsonObjectWriter::RenderNull(StringPiece name) { |
||||||
|
return RenderSimple(name, "null"); |
||||||
|
} |
||||||
|
|
||||||
|
void JsonObjectWriter::WritePrefix(StringPiece name) { |
||||||
|
bool not_first = !element()->is_first(); |
||||||
|
if (not_first) WriteChar(','); |
||||||
|
if (not_first || !element()->is_root()) NewLine(); |
||||||
|
if (!name.empty()) { |
||||||
|
WriteChar('"'); |
||||||
|
ArrayByteSource source(name); |
||||||
|
JsonEscaping::Escape(&source, &sink_); |
||||||
|
stream_->WriteString("\":"); |
||||||
|
if (!indent_string_.empty()) WriteChar(' '); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
} // namespace google
|
@ -0,0 +1,206 @@ |
|||||||
|
// 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_UTIL_CONVERTER_JSON_OBJECTWRITER_H__ |
||||||
|
#define GOOGLE_PROTOBUF_UTIL_CONVERTER_JSON_OBJECTWRITER_H__ |
||||||
|
|
||||||
|
#include <memory> |
||||||
|
#ifndef _SHARED_PTR_H |
||||||
|
#include <google/protobuf/stubs/shared_ptr.h> |
||||||
|
#endif |
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include <google/protobuf/io/coded_stream.h> |
||||||
|
#include <google/protobuf/util/internal/structured_objectwriter.h> |
||||||
|
#include <google/protobuf/stubs/bytestream.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace converter { |
||||||
|
|
||||||
|
// An ObjectWriter implementation that outputs JSON. This ObjectWriter
|
||||||
|
// supports writing a compact form or a pretty printed form.
|
||||||
|
//
|
||||||
|
// Sample usage:
|
||||||
|
// string output;
|
||||||
|
// StringOutputStream* str_stream = new StringOutputStream(&output);
|
||||||
|
// CodedOutputStream* out_stream = new CodedOutputStream(str_stream);
|
||||||
|
// JsonObjectWriter* ow = new JsonObjectWriter(" ", out_stream);
|
||||||
|
// ow->StartObject("")
|
||||||
|
// ->RenderString("name", "value")
|
||||||
|
// ->RenderString("emptystring", string())
|
||||||
|
// ->StartObject("nested")
|
||||||
|
// ->RenderInt64("light", 299792458);
|
||||||
|
// ->RenderDouble("pi", 3.141592653589793);
|
||||||
|
// ->EndObject()
|
||||||
|
// ->StartList("empty")
|
||||||
|
// ->EndList()
|
||||||
|
// ->EndObject();
|
||||||
|
//
|
||||||
|
// And then the output string would become:
|
||||||
|
// {
|
||||||
|
// "name": "value",
|
||||||
|
// "emptystring": "",
|
||||||
|
// "nested": {
|
||||||
|
// "light": "299792458",
|
||||||
|
// "pi": 3.141592653589793
|
||||||
|
// },
|
||||||
|
// "empty": []
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// JsonObjectWriter does not validate if calls actually result in valid JSON.
|
||||||
|
// For example, passing an empty name when one would be required won't result
|
||||||
|
// in an error, just an invalid output.
|
||||||
|
//
|
||||||
|
// Note that all int64 and uint64 are rendered as strings instead of numbers.
|
||||||
|
// This is because JavaScript parses numbers as 64-bit float thus int64 and
|
||||||
|
// uint64 would lose precision if rendered as numbers.
|
||||||
|
//
|
||||||
|
// JsonObjectWriter is thread-unsafe.
|
||||||
|
class LIBPROTOBUF_EXPORT JsonObjectWriter : public StructuredObjectWriter { |
||||||
|
public: |
||||||
|
JsonObjectWriter(StringPiece indent_string, |
||||||
|
google::protobuf::io::CodedOutputStream* out) |
||||||
|
: element_(new Element(NULL)), |
||||||
|
stream_(out), sink_(out), |
||||||
|
indent_string_(indent_string.ToString()) { |
||||||
|
} |
||||||
|
virtual ~JsonObjectWriter(); |
||||||
|
|
||||||
|
// ObjectWriter methods.
|
||||||
|
virtual JsonObjectWriter* StartObject(StringPiece name); |
||||||
|
virtual JsonObjectWriter* EndObject(); |
||||||
|
virtual JsonObjectWriter* StartList(StringPiece name); |
||||||
|
virtual JsonObjectWriter* EndList(); |
||||||
|
virtual JsonObjectWriter* RenderBool(StringPiece name, bool value); |
||||||
|
virtual JsonObjectWriter* RenderInt32(StringPiece name, int32 value); |
||||||
|
virtual JsonObjectWriter* RenderUint32(StringPiece name, uint32 value); |
||||||
|
virtual JsonObjectWriter* RenderInt64(StringPiece name, int64 value); |
||||||
|
virtual JsonObjectWriter* RenderUint64(StringPiece name, uint64 value); |
||||||
|
virtual JsonObjectWriter* RenderDouble(StringPiece name, double value); |
||||||
|
virtual JsonObjectWriter* RenderFloat(StringPiece name, float value); |
||||||
|
virtual JsonObjectWriter* RenderString(StringPiece name, StringPiece value); |
||||||
|
virtual JsonObjectWriter* RenderBytes(StringPiece name, StringPiece value); |
||||||
|
virtual JsonObjectWriter* RenderNull(StringPiece name); |
||||||
|
|
||||||
|
protected: |
||||||
|
class LIBPROTOBUF_EXPORT Element : public BaseElement { |
||||||
|
public: |
||||||
|
explicit Element(Element* parent) : BaseElement(parent), is_first_(true) {} |
||||||
|
|
||||||
|
// Called before each field of the Element is to be processed.
|
||||||
|
// Returns true if this is the first call (processing the first field).
|
||||||
|
bool is_first() { |
||||||
|
if (is_first_) { |
||||||
|
is_first_ = false; |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
bool is_first_; |
||||||
|
|
||||||
|
GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(Element); |
||||||
|
}; |
||||||
|
|
||||||
|
virtual Element* element() { return element_.get(); } |
||||||
|
|
||||||
|
private: |
||||||
|
class LIBPROTOBUF_EXPORT ByteSinkWrapper : public strings::ByteSink { |
||||||
|
public: |
||||||
|
explicit ByteSinkWrapper(google::protobuf::io::CodedOutputStream* stream) |
||||||
|
: stream_(stream) {} |
||||||
|
virtual ~ByteSinkWrapper() {} |
||||||
|
|
||||||
|
// ByteSink methods.
|
||||||
|
virtual void Append(const char* bytes, size_t n) { |
||||||
|
stream_->WriteRaw(bytes, n); |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
google::protobuf::io::CodedOutputStream* stream_; |
||||||
|
|
||||||
|
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ByteSinkWrapper); |
||||||
|
}; |
||||||
|
|
||||||
|
// Renders a simple value as a string. By default all non-string Render
|
||||||
|
// methods convert their argument to a string and call this method. This
|
||||||
|
// method can then be used to render the simple value without escaping it.
|
||||||
|
JsonObjectWriter* RenderSimple(StringPiece name, const string& value) { |
||||||
|
WritePrefix(name); |
||||||
|
stream_->WriteString(value); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
// Pushes a new element to the stack.
|
||||||
|
void Push() { element_.reset(new Element(element_.release())); } |
||||||
|
|
||||||
|
// Pops an element off of the stack and deletes the popped element.
|
||||||
|
void Pop() { |
||||||
|
bool needs_newline = !element_->is_first(); |
||||||
|
element_.reset(element_->pop<Element>()); |
||||||
|
if (needs_newline) NewLine(); |
||||||
|
} |
||||||
|
|
||||||
|
// If pretty printing is enabled, this will write a newline to the output,
|
||||||
|
// followed by optional indentation. Otherwise this method is a noop.
|
||||||
|
void NewLine() { |
||||||
|
if (!indent_string_.empty()) { |
||||||
|
WriteChar('\n'); |
||||||
|
for (int i = 0; i < element()->level(); i++) { |
||||||
|
stream_->WriteString(indent_string_); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Writes a prefix. This will write out any pretty printing and
|
||||||
|
// commas that are required, followed by the name and a ':' if
|
||||||
|
// the name is not null.
|
||||||
|
void WritePrefix(StringPiece name); |
||||||
|
|
||||||
|
// Writes an individual character to the output.
|
||||||
|
void WriteChar(const char c) { stream_->WriteRaw(&c, sizeof(c)); } |
||||||
|
|
||||||
|
google::protobuf::scoped_ptr<Element> element_; |
||||||
|
google::protobuf::io::CodedOutputStream* stream_; |
||||||
|
ByteSinkWrapper sink_; |
||||||
|
const string indent_string_; |
||||||
|
|
||||||
|
GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(JsonObjectWriter); |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
|
||||||
|
} // namespace google
|
||||||
|
#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_JSON_OBJECTWRITER_H__
|
@ -0,0 +1,284 @@ |
|||||||
|
// 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/util/internal/json_objectwriter.h> |
||||||
|
|
||||||
|
#include <google/protobuf/io/zero_copy_stream_impl_lite.h> |
||||||
|
#include <gtest/gtest.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace converter { |
||||||
|
|
||||||
|
using google::protobuf::io::CodedOutputStream; |
||||||
|
using google::protobuf::io::StringOutputStream; |
||||||
|
|
||||||
|
class JsonObjectWriterTest : public ::testing::Test { |
||||||
|
protected: |
||||||
|
JsonObjectWriterTest() |
||||||
|
: str_stream_(new StringOutputStream(&output_)), |
||||||
|
out_stream_(new CodedOutputStream(str_stream_)), |
||||||
|
ow_(NULL) {} |
||||||
|
|
||||||
|
virtual ~JsonObjectWriterTest() { |
||||||
|
delete ow_; |
||||||
|
delete out_stream_; |
||||||
|
delete str_stream_; |
||||||
|
} |
||||||
|
|
||||||
|
string output_; |
||||||
|
StringOutputStream* const str_stream_; |
||||||
|
CodedOutputStream* const out_stream_; |
||||||
|
ObjectWriter* ow_; |
||||||
|
}; |
||||||
|
|
||||||
|
TEST_F(JsonObjectWriterTest, EmptyRootObject) { |
||||||
|
ow_ = new JsonObjectWriter("", out_stream_); |
||||||
|
ow_->StartObject("")->EndObject(); |
||||||
|
EXPECT_EQ("{}", output_.substr(0, out_stream_->ByteCount())); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonObjectWriterTest, EmptyObject) { |
||||||
|
ow_ = new JsonObjectWriter("", out_stream_); |
||||||
|
ow_->StartObject("") |
||||||
|
->RenderString("test", "value") |
||||||
|
->StartObject("empty") |
||||||
|
->EndObject() |
||||||
|
->EndObject(); |
||||||
|
EXPECT_EQ("{\"test\":\"value\",\"empty\":{}}", |
||||||
|
output_.substr(0, out_stream_->ByteCount())); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonObjectWriterTest, EmptyRootList) { |
||||||
|
ow_ = new JsonObjectWriter("", out_stream_); |
||||||
|
ow_->StartList("")->EndList(); |
||||||
|
EXPECT_EQ("[]", output_.substr(0, out_stream_->ByteCount())); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonObjectWriterTest, EmptyList) { |
||||||
|
ow_ = new JsonObjectWriter("", out_stream_); |
||||||
|
ow_->StartObject("") |
||||||
|
->RenderString("test", "value") |
||||||
|
->StartList("empty") |
||||||
|
->EndList() |
||||||
|
->EndObject(); |
||||||
|
EXPECT_EQ("{\"test\":\"value\",\"empty\":[]}", |
||||||
|
output_.substr(0, out_stream_->ByteCount())); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonObjectWriterTest, ObjectInObject) { |
||||||
|
ow_ = new JsonObjectWriter("", out_stream_); |
||||||
|
ow_->StartObject("") |
||||||
|
->StartObject("nested") |
||||||
|
->RenderString("field", "value") |
||||||
|
->EndObject() |
||||||
|
->EndObject(); |
||||||
|
EXPECT_EQ("{\"nested\":{\"field\":\"value\"}}", |
||||||
|
output_.substr(0, out_stream_->ByteCount())); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonObjectWriterTest, ListInObject) { |
||||||
|
ow_ = new JsonObjectWriter("", out_stream_); |
||||||
|
ow_->StartObject("") |
||||||
|
->StartList("nested") |
||||||
|
->RenderString("", "value") |
||||||
|
->EndList() |
||||||
|
->EndObject(); |
||||||
|
EXPECT_EQ("{\"nested\":[\"value\"]}", |
||||||
|
output_.substr(0, out_stream_->ByteCount())); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonObjectWriterTest, ObjectInList) { |
||||||
|
ow_ = new JsonObjectWriter("", out_stream_); |
||||||
|
ow_->StartList("") |
||||||
|
->StartObject("") |
||||||
|
->RenderString("field", "value") |
||||||
|
->EndObject() |
||||||
|
->EndList(); |
||||||
|
EXPECT_EQ("[{\"field\":\"value\"}]", |
||||||
|
output_.substr(0, out_stream_->ByteCount())); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonObjectWriterTest, ListInList) { |
||||||
|
ow_ = new JsonObjectWriter("", out_stream_); |
||||||
|
ow_->StartList("") |
||||||
|
->StartList("") |
||||||
|
->RenderString("", "value") |
||||||
|
->EndList() |
||||||
|
->EndList(); |
||||||
|
EXPECT_EQ("[[\"value\"]]", output_.substr(0, out_stream_->ByteCount())); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonObjectWriterTest, RenderPrimitives) { |
||||||
|
ow_ = new JsonObjectWriter("", out_stream_); |
||||||
|
ow_->StartObject("") |
||||||
|
->RenderBool("bool", true) |
||||||
|
->RenderDouble("double", std::numeric_limits<double>::max()) |
||||||
|
->RenderFloat("float", std::numeric_limits<float>::max()) |
||||||
|
->RenderInt32("int", std::numeric_limits<int32>::min()) |
||||||
|
->RenderInt64("long", std::numeric_limits<int64>::min()) |
||||||
|
->RenderBytes("bytes", "abracadabra") |
||||||
|
->RenderString("string", "string") |
||||||
|
->RenderBytes("emptybytes", "") |
||||||
|
->RenderString("emptystring", string()) |
||||||
|
->EndObject(); |
||||||
|
EXPECT_EQ( |
||||||
|
"{\"bool\":true," |
||||||
|
"\"double\":1.7976931348623157e+308," |
||||||
|
"\"float\":3.4028235e+38," |
||||||
|
"\"int\":-2147483648," |
||||||
|
"\"long\":\"-9223372036854775808\"," |
||||||
|
"\"bytes\":\"YWJyYWNhZGFicmE=\"," |
||||||
|
"\"string\":\"string\"," |
||||||
|
"\"emptybytes\":\"\"," |
||||||
|
"\"emptystring\":\"\"}", |
||||||
|
output_.substr(0, out_stream_->ByteCount())); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonObjectWriterTest, BytesEncodesAsWebSafeBase64) { |
||||||
|
string s; |
||||||
|
s.push_back('\377'); |
||||||
|
s.push_back('\357'); |
||||||
|
ow_ = new JsonObjectWriter("", out_stream_); |
||||||
|
ow_->StartObject("")->RenderBytes("bytes", s)->EndObject(); |
||||||
|
// Non-web-safe would encode this as "/+8="
|
||||||
|
EXPECT_EQ("{\"bytes\":\"_-8=\"}", |
||||||
|
output_.substr(0, out_stream_->ByteCount())); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonObjectWriterTest, PrettyPrintList) { |
||||||
|
ow_ = new JsonObjectWriter(" ", out_stream_); |
||||||
|
ow_->StartObject("") |
||||||
|
->StartList("items") |
||||||
|
->RenderString("", "item1") |
||||||
|
->RenderString("", "item2") |
||||||
|
->RenderString("", "item3") |
||||||
|
->EndList() |
||||||
|
->StartList("empty") |
||||||
|
->EndList() |
||||||
|
->EndObject(); |
||||||
|
EXPECT_EQ( |
||||||
|
"{\n" |
||||||
|
" \"items\": [\n" |
||||||
|
" \"item1\",\n" |
||||||
|
" \"item2\",\n" |
||||||
|
" \"item3\"\n" |
||||||
|
" ],\n" |
||||||
|
" \"empty\": []\n" |
||||||
|
"}\n", |
||||||
|
output_.substr(0, out_stream_->ByteCount())); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonObjectWriterTest, PrettyPrintObject) { |
||||||
|
ow_ = new JsonObjectWriter(" ", out_stream_); |
||||||
|
ow_->StartObject("") |
||||||
|
->StartObject("items") |
||||||
|
->RenderString("key1", "item1") |
||||||
|
->RenderString("key2", "item2") |
||||||
|
->RenderString("key3", "item3") |
||||||
|
->EndObject() |
||||||
|
->StartObject("empty") |
||||||
|
->EndObject() |
||||||
|
->EndObject(); |
||||||
|
EXPECT_EQ( |
||||||
|
"{\n" |
||||||
|
" \"items\": {\n" |
||||||
|
" \"key1\": \"item1\",\n" |
||||||
|
" \"key2\": \"item2\",\n" |
||||||
|
" \"key3\": \"item3\"\n" |
||||||
|
" },\n" |
||||||
|
" \"empty\": {}\n" |
||||||
|
"}\n", |
||||||
|
output_.substr(0, out_stream_->ByteCount())); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonObjectWriterTest, PrettyPrintEmptyObjectInEmptyList) { |
||||||
|
ow_ = new JsonObjectWriter(" ", out_stream_); |
||||||
|
ow_->StartObject("") |
||||||
|
->StartList("list") |
||||||
|
->StartObject("") |
||||||
|
->EndObject() |
||||||
|
->EndList() |
||||||
|
->EndObject(); |
||||||
|
EXPECT_EQ( |
||||||
|
"{\n" |
||||||
|
" \"list\": [\n" |
||||||
|
" {}\n" |
||||||
|
" ]\n" |
||||||
|
"}\n", |
||||||
|
output_.substr(0, out_stream_->ByteCount())); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonObjectWriterTest, PrettyPrintDoubleIndent) { |
||||||
|
ow_ = new JsonObjectWriter(" ", out_stream_); |
||||||
|
ow_->StartObject("") |
||||||
|
->RenderBool("bool", true) |
||||||
|
->RenderInt32("int", 42) |
||||||
|
->EndObject(); |
||||||
|
EXPECT_EQ( |
||||||
|
"{\n" |
||||||
|
" \"bool\": true,\n" |
||||||
|
" \"int\": 42\n" |
||||||
|
"}\n", |
||||||
|
output_.substr(0, out_stream_->ByteCount())); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonObjectWriterTest, StringsEscapedAndEnclosedInDoubleQuotes) { |
||||||
|
ow_ = new JsonObjectWriter("", out_stream_); |
||||||
|
ow_->StartObject("")->RenderString("string", "'<>&\\\"\r\n")->EndObject(); |
||||||
|
EXPECT_EQ("{\"string\":\"'\\u003c\\u003e&\\\\\\\"\\r\\n\"}", |
||||||
|
output_.substr(0, out_stream_->ByteCount())); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonObjectWriterTest, Stringification) { |
||||||
|
ow_ = new JsonObjectWriter("", out_stream_); |
||||||
|
ow_->StartObject("") |
||||||
|
->RenderDouble("double_nan", std::numeric_limits<double>::quiet_NaN()) |
||||||
|
->RenderFloat("float_nan", std::numeric_limits<float>::quiet_NaN()) |
||||||
|
->RenderDouble("double_pos", std::numeric_limits<double>::infinity()) |
||||||
|
->RenderFloat("float_pos", std::numeric_limits<float>::infinity()) |
||||||
|
->RenderDouble("double_neg", -std::numeric_limits<double>::infinity()) |
||||||
|
->RenderFloat("float_neg", -std::numeric_limits<float>::infinity()) |
||||||
|
->EndObject(); |
||||||
|
EXPECT_EQ( |
||||||
|
"{\"double_nan\":\"NaN\"," |
||||||
|
"\"float_nan\":\"NaN\"," |
||||||
|
"\"double_pos\":\"Infinity\"," |
||||||
|
"\"float_pos\":\"Infinity\"," |
||||||
|
"\"double_neg\":\"-Infinity\"," |
||||||
|
"\"float_neg\":\"-Infinity\"}", |
||||||
|
output_.substr(0, out_stream_->ByteCount())); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
} // namespace google
|
@ -0,0 +1,740 @@ |
|||||||
|
// 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/util/internal/json_stream_parser.h> |
||||||
|
|
||||||
|
#include <algorithm> |
||||||
|
#include <cctype> |
||||||
|
#include <cerrno> |
||||||
|
#include <cstdlib> |
||||||
|
#include <cstring> |
||||||
|
#include <memory> |
||||||
|
#ifndef _SHARED_PTR_H |
||||||
|
#include <google/protobuf/stubs/shared_ptr.h> |
||||||
|
#endif |
||||||
|
|
||||||
|
#include <google/protobuf/stubs/common.h> |
||||||
|
#include <google/protobuf/stubs/strutil.h> |
||||||
|
#include <google/protobuf/util/internal/object_writer.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
|
||||||
|
// Allow these symbols to be referenced as util::Status, util::error::* in
|
||||||
|
// this file.
|
||||||
|
using util::Status; |
||||||
|
namespace error { |
||||||
|
using util::error::INTERNAL; |
||||||
|
using util::error::INVALID_ARGUMENT; |
||||||
|
} // namespace error
|
||||||
|
|
||||||
|
namespace converter { |
||||||
|
|
||||||
|
// Number of digits in a unicode escape sequence (/uXXXX)
|
||||||
|
static const int kUnicodeEscapedLength = 6; |
||||||
|
|
||||||
|
// Length of the true, false, and null literals.
|
||||||
|
static const int true_len = strlen("true"); |
||||||
|
static const int false_len = strlen("false"); |
||||||
|
static const int null_len = strlen("null"); |
||||||
|
|
||||||
|
inline bool IsLetter(char c) { |
||||||
|
return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || (c == '_') || |
||||||
|
(c == '$'); |
||||||
|
} |
||||||
|
|
||||||
|
inline bool IsAlphanumeric(char c) { |
||||||
|
return IsLetter(c) || ('0' <= c && c <= '9'); |
||||||
|
} |
||||||
|
|
||||||
|
static bool ConsumeKey(StringPiece* input, StringPiece* key) { |
||||||
|
if (input->empty() || !IsLetter((*input)[0])) return false; |
||||||
|
int len = 1; |
||||||
|
for (; len < input->size(); ++len) { |
||||||
|
if (!IsAlphanumeric((*input)[len])) { |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
*key = StringPiece(input->data(), len); |
||||||
|
*input = StringPiece(input->data() + len, input->size() - len); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
static bool MatchKey(StringPiece input) { |
||||||
|
return !input.empty() && IsLetter(input[0]); |
||||||
|
} |
||||||
|
|
||||||
|
JsonStreamParser::JsonStreamParser(ObjectWriter* ow) |
||||||
|
: ow_(ow), |
||||||
|
stack_(), |
||||||
|
leftover_(), |
||||||
|
json_(), |
||||||
|
p_(), |
||||||
|
key_(), |
||||||
|
key_storage_(), |
||||||
|
finishing_(false), |
||||||
|
parsed_(), |
||||||
|
parsed_storage_(), |
||||||
|
string_open_(0), |
||||||
|
utf8_storage_(), |
||||||
|
utf8_length_(0) { |
||||||
|
// Initialize the stack with a single value to be parsed.
|
||||||
|
stack_.push(VALUE); |
||||||
|
} |
||||||
|
|
||||||
|
JsonStreamParser::~JsonStreamParser() {} |
||||||
|
|
||||||
|
util::Status JsonStreamParser::Parse(StringPiece json) { |
||||||
|
return ParseChunk(json); |
||||||
|
} |
||||||
|
|
||||||
|
util::Status JsonStreamParser::FinishParse() { |
||||||
|
// If we do not expect anything and there is nothing left to parse we're all
|
||||||
|
// done.
|
||||||
|
if (stack_.empty() && leftover_.empty()) { |
||||||
|
return util::Status::OK; |
||||||
|
} |
||||||
|
// Parse the remainder in finishing mode, which reports errors for things like
|
||||||
|
// unterminated strings or unknown tokens that would normally be retried.
|
||||||
|
p_ = json_ = StringPiece(leftover_); |
||||||
|
finishing_ = true; |
||||||
|
util::Status result = RunParser(); |
||||||
|
if (result.ok()) { |
||||||
|
SkipWhitespace(); |
||||||
|
if (!p_.empty()) { |
||||||
|
result = ReportFailure("Parsing terminated before end of input."); |
||||||
|
} |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
util::Status JsonStreamParser::ParseChunk(StringPiece chunk) { |
||||||
|
// If we have leftovers from a previous chunk, append the new chunk to it and
|
||||||
|
// create a new StringPiece pointing at the string's data. This could be
|
||||||
|
// large but we rely on the chunks to be small, assuming they are fragments
|
||||||
|
// of a Cord.
|
||||||
|
if (!leftover_.empty()) { |
||||||
|
chunk.AppendToString(&leftover_); |
||||||
|
p_ = json_ = StringPiece(leftover_); |
||||||
|
} else { |
||||||
|
p_ = json_ = chunk; |
||||||
|
} |
||||||
|
|
||||||
|
finishing_ = false; |
||||||
|
util::Status result = RunParser(); |
||||||
|
if (!result.ok()) return result; |
||||||
|
|
||||||
|
SkipWhitespace(); |
||||||
|
if (p_.empty()) { |
||||||
|
// If we parsed everything we had, clear the leftover.
|
||||||
|
leftover_.clear(); |
||||||
|
} else { |
||||||
|
// If we do not expect anything i.e. stack is empty, and we have non-empty
|
||||||
|
// string left to parse, we report an error.
|
||||||
|
if (stack_.empty()) { |
||||||
|
return ReportFailure("Parsing terminated before end of input."); |
||||||
|
} |
||||||
|
// If we expect future data i.e. stack is non-empty, and we have some
|
||||||
|
// unparsed data left, we save it for later parse.
|
||||||
|
leftover_ = p_.ToString(); |
||||||
|
} |
||||||
|
return util::Status::OK; |
||||||
|
} |
||||||
|
|
||||||
|
util::Status JsonStreamParser::RunParser() { |
||||||
|
while (!stack_.empty()) { |
||||||
|
ParseType type = stack_.top(); |
||||||
|
TokenType t = (string_open_ == 0) ? GetNextTokenType() : BEGIN_STRING; |
||||||
|
stack_.pop(); |
||||||
|
util::Status result; |
||||||
|
switch (type) { |
||||||
|
case VALUE: |
||||||
|
result = ParseValue(t); |
||||||
|
break; |
||||||
|
|
||||||
|
case OBJ_MID: |
||||||
|
result = ParseObjectMid(t); |
||||||
|
break; |
||||||
|
|
||||||
|
case ENTRY: |
||||||
|
result = ParseEntry(t); |
||||||
|
break; |
||||||
|
|
||||||
|
case ENTRY_MID: |
||||||
|
result = ParseEntryMid(t); |
||||||
|
break; |
||||||
|
|
||||||
|
case ARRAY_VALUE: |
||||||
|
result = ParseArrayValue(t); |
||||||
|
break; |
||||||
|
|
||||||
|
case ARRAY_MID: |
||||||
|
result = ParseArrayMid(t); |
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
result = util::Status(util::error::INTERNAL, |
||||||
|
StrCat("Unknown parse type: ", type)); |
||||||
|
break; |
||||||
|
} |
||||||
|
if (!result.ok()) { |
||||||
|
// If we were cancelled, save our state and try again later.
|
||||||
|
if (!finishing_ && result == util::Status::CANCELLED) { |
||||||
|
stack_.push(type); |
||||||
|
// If we have a key we still need to render, make sure to save off the
|
||||||
|
// contents in our own storage.
|
||||||
|
if (!key_.empty() && key_storage_.empty()) { |
||||||
|
key_.AppendToString(&key_storage_); |
||||||
|
key_ = StringPiece(key_storage_); |
||||||
|
} |
||||||
|
result = util::Status::OK; |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
} |
||||||
|
return util::Status::OK; |
||||||
|
} |
||||||
|
|
||||||
|
util::Status JsonStreamParser::ParseValue(TokenType type) { |
||||||
|
switch (type) { |
||||||
|
case BEGIN_OBJECT: |
||||||
|
return HandleBeginObject(); |
||||||
|
case BEGIN_ARRAY: |
||||||
|
return HandleBeginArray(); |
||||||
|
case BEGIN_STRING: |
||||||
|
return ParseString(); |
||||||
|
case BEGIN_NUMBER: |
||||||
|
return ParseNumber(); |
||||||
|
case BEGIN_TRUE: |
||||||
|
return ParseTrue(); |
||||||
|
case BEGIN_FALSE: |
||||||
|
return ParseFalse(); |
||||||
|
case BEGIN_NULL: |
||||||
|
return ParseNull(); |
||||||
|
case UNKNOWN: |
||||||
|
return ReportUnknown("Expected a value."); |
||||||
|
default: { |
||||||
|
// Special case for having been cut off while parsing, wait for more data.
|
||||||
|
// This handles things like 'fals' being at the end of the string, we
|
||||||
|
// don't know if the next char would be e, completing it, or something
|
||||||
|
// else, making it invalid.
|
||||||
|
if (!finishing_ && p_.length() < false_len) { |
||||||
|
return util::Status::CANCELLED; |
||||||
|
} |
||||||
|
return ReportFailure("Unexpected token."); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
util::Status JsonStreamParser::ParseString() { |
||||||
|
util::Status result = ParseStringHelper(); |
||||||
|
if (result.ok()) { |
||||||
|
ow_->RenderString(key_, parsed_); |
||||||
|
key_.clear(); |
||||||
|
parsed_.clear(); |
||||||
|
parsed_storage_.clear(); |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
util::Status JsonStreamParser::ParseStringHelper() { |
||||||
|
// If we haven't seen the start quote, grab it and remember it for later.
|
||||||
|
if (string_open_ == 0) { |
||||||
|
string_open_ = *p_.data(); |
||||||
|
GOOGLE_DCHECK(string_open_ == '\"' || string_open_ == '\''); |
||||||
|
Advance(); |
||||||
|
} |
||||||
|
// Track where we last copied data from so we can minimize copying.
|
||||||
|
const char* last = p_.data(); |
||||||
|
while (!p_.empty()) { |
||||||
|
const char* data = p_.data(); |
||||||
|
if (*data == '\\') { |
||||||
|
// We're about to handle an escape, copy all bytes from last to data.
|
||||||
|
if (last < data) { |
||||||
|
parsed_storage_.append(last, data - last); |
||||||
|
last = data; |
||||||
|
} |
||||||
|
// If we ran out of string after the \, cancel or report an error
|
||||||
|
// depending on if we expect more data later.
|
||||||
|
if (p_.length() == 1) { |
||||||
|
if (!finishing_) { |
||||||
|
return util::Status::CANCELLED; |
||||||
|
} |
||||||
|
return ReportFailure("Closing quote expected in string."); |
||||||
|
} |
||||||
|
// Parse a unicode escape if we found \u in the string.
|
||||||
|
if (data[1] == 'u') { |
||||||
|
util::Status result = ParseUnicodeEscape(); |
||||||
|
if (!result.ok()) { |
||||||
|
return result; |
||||||
|
} |
||||||
|
// Move last pointer past the unicode escape and continue.
|
||||||
|
last = p_.data(); |
||||||
|
continue; |
||||||
|
} |
||||||
|
// Handle the standard set of backslash-escaped characters.
|
||||||
|
switch (data[1]) { |
||||||
|
case 'b': |
||||||
|
parsed_storage_.push_back('\b'); |
||||||
|
break; |
||||||
|
case 'f': |
||||||
|
parsed_storage_.push_back('\f'); |
||||||
|
break; |
||||||
|
case 'n': |
||||||
|
parsed_storage_.push_back('\n'); |
||||||
|
break; |
||||||
|
case 'r': |
||||||
|
parsed_storage_.push_back('\r'); |
||||||
|
break; |
||||||
|
case 't': |
||||||
|
parsed_storage_.push_back('\t'); |
||||||
|
break; |
||||||
|
case 'v': |
||||||
|
parsed_storage_.push_back('\v'); |
||||||
|
break; |
||||||
|
default: |
||||||
|
parsed_storage_.push_back(data[1]); |
||||||
|
} |
||||||
|
// We handled two characters, so advance past them and continue.
|
||||||
|
p_.remove_prefix(2); |
||||||
|
last = p_.data(); |
||||||
|
continue; |
||||||
|
} |
||||||
|
// If we found the closing quote note it, advance past it, and return.
|
||||||
|
if (*data == string_open_) { |
||||||
|
// If we didn't copy anything, reuse the input buffer.
|
||||||
|
if (parsed_storage_.empty()) { |
||||||
|
parsed_ = StringPiece(last, data - last); |
||||||
|
} else { |
||||||
|
if (last < data) { |
||||||
|
parsed_storage_.append(last, data - last); |
||||||
|
last = data; |
||||||
|
} |
||||||
|
parsed_ = StringPiece(parsed_storage_); |
||||||
|
} |
||||||
|
// Clear the quote char so next time we try to parse a string we'll
|
||||||
|
// start fresh.
|
||||||
|
string_open_ = 0; |
||||||
|
Advance(); |
||||||
|
return util::Status::OK; |
||||||
|
} |
||||||
|
// Normal character, just advance past it.
|
||||||
|
Advance(); |
||||||
|
} |
||||||
|
// If we ran out of characters, copy over what we have so far.
|
||||||
|
if (last < p_.data()) { |
||||||
|
parsed_storage_.append(last, p_.data() - last); |
||||||
|
} |
||||||
|
// If we didn't find the closing quote but we expect more data, cancel for now
|
||||||
|
if (!finishing_) { |
||||||
|
return util::Status::CANCELLED; |
||||||
|
} |
||||||
|
// End of string reached without a closing quote, report an error.
|
||||||
|
string_open_ = 0; |
||||||
|
return ReportFailure("Closing quote expected in string."); |
||||||
|
} |
||||||
|
|
||||||
|
// Converts a unicode escaped character to a decimal value stored in a char32
|
||||||
|
// for use in UTF8 encoding utility. We assume that str begins with \uhhhh and
|
||||||
|
// convert that from the hex number to a decimal value.
|
||||||
|
//
|
||||||
|
// There are some security exploits with UTF-8 that we should be careful of:
|
||||||
|
// - http://www.unicode.org/reports/tr36/#UTF-8_Exploit
|
||||||
|
// - http://sites/intl-eng/design-guide/core-application
|
||||||
|
util::Status JsonStreamParser::ParseUnicodeEscape() { |
||||||
|
if (p_.length() < kUnicodeEscapedLength) { |
||||||
|
if (!finishing_) { |
||||||
|
return util::Status::CANCELLED; |
||||||
|
} |
||||||
|
return ReportFailure("Illegal hex string."); |
||||||
|
} |
||||||
|
GOOGLE_DCHECK_EQ('\\', p_.data()[0]); |
||||||
|
GOOGLE_DCHECK_EQ('u', p_.data()[1]); |
||||||
|
uint32 code = 0; |
||||||
|
for (int i = 2; i < kUnicodeEscapedLength; ++i) { |
||||||
|
if (!isxdigit(p_.data()[i])) { |
||||||
|
return ReportFailure("Invalid escape sequence."); |
||||||
|
} |
||||||
|
code = (code << 4) + hex_digit_to_int(p_.data()[i]); |
||||||
|
} |
||||||
|
char buf[UTFmax]; |
||||||
|
int len = EncodeAsUTF8Char(code, buf); |
||||||
|
// Advance past the unicode escape.
|
||||||
|
p_.remove_prefix(kUnicodeEscapedLength); |
||||||
|
parsed_storage_.append(buf, len); |
||||||
|
return util::Status::OK; |
||||||
|
} |
||||||
|
|
||||||
|
util::Status JsonStreamParser::ParseNumber() { |
||||||
|
NumberResult number; |
||||||
|
util::Status result = ParseNumberHelper(&number); |
||||||
|
if (result.ok()) { |
||||||
|
switch (number.type) { |
||||||
|
case NumberResult::DOUBLE: |
||||||
|
ow_->RenderDouble(key_, number.double_val); |
||||||
|
key_.clear(); |
||||||
|
break; |
||||||
|
|
||||||
|
case NumberResult::INT: |
||||||
|
ow_->RenderInt64(key_, number.int_val); |
||||||
|
key_.clear(); |
||||||
|
break; |
||||||
|
|
||||||
|
case NumberResult::UINT: |
||||||
|
ow_->RenderUint64(key_, number.uint_val); |
||||||
|
key_.clear(); |
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
return ReportFailure("Unable to parse number."); |
||||||
|
} |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
util::Status JsonStreamParser::ParseNumberHelper(NumberResult* result) { |
||||||
|
const char* data = p_.data(); |
||||||
|
int length = p_.length(); |
||||||
|
|
||||||
|
// Look for the first non-numeric character, or the end of the string.
|
||||||
|
int index = 0; |
||||||
|
bool floating = false; |
||||||
|
bool negative = data[index] == '-'; |
||||||
|
// Find the first character that cannot be part of the number. Along the way
|
||||||
|
// detect if the number needs to be parsed as a double.
|
||||||
|
// Note that this restricts numbers to the JSON specification, so for example
|
||||||
|
// we do not support hex or octal notations.
|
||||||
|
for (; index < length; ++index) { |
||||||
|
char c = data[index]; |
||||||
|
if (isdigit(c)) continue; |
||||||
|
if (c == '.' || c == 'e' || c == 'E') { |
||||||
|
floating = true; |
||||||
|
continue; |
||||||
|
} |
||||||
|
if (c == '+' || c == '-') continue; |
||||||
|
// Not a valid number character, break out.
|
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
// If the entire input is a valid number, and we may have more content in the
|
||||||
|
// future, we abort for now and resume when we know more.
|
||||||
|
if (index == length && !finishing_) { |
||||||
|
return util::Status::CANCELLED; |
||||||
|
} |
||||||
|
|
||||||
|
// Create a string containing just the number, so we can use safe_strtoX
|
||||||
|
string number = p_.substr(0, index).ToString(); |
||||||
|
|
||||||
|
// Floating point number, parse as a double.
|
||||||
|
if (floating) { |
||||||
|
if (!safe_strtod(number, &result->double_val)) { |
||||||
|
return ReportFailure("Unable to parse number."); |
||||||
|
} |
||||||
|
result->type = NumberResult::DOUBLE; |
||||||
|
p_.remove_prefix(index); |
||||||
|
return util::Status::OK; |
||||||
|
} |
||||||
|
|
||||||
|
// Positive non-floating point number, parse as a uint64.
|
||||||
|
if (!negative) { |
||||||
|
if (!safe_strtou64(number, &result->uint_val)) { |
||||||
|
return ReportFailure("Unable to parse number."); |
||||||
|
} |
||||||
|
result->type = NumberResult::UINT; |
||||||
|
p_.remove_prefix(index); |
||||||
|
return util::Status::OK; |
||||||
|
} |
||||||
|
|
||||||
|
// Negative non-floating point number, parse as an int64.
|
||||||
|
if (!safe_strto64(number, &result->int_val)) { |
||||||
|
return ReportFailure("Unable to parse number."); |
||||||
|
} |
||||||
|
result->type = NumberResult::INT; |
||||||
|
p_.remove_prefix(index); |
||||||
|
return util::Status::OK; |
||||||
|
} |
||||||
|
|
||||||
|
util::Status JsonStreamParser::HandleBeginObject() { |
||||||
|
GOOGLE_DCHECK_EQ('{', *p_.data()); |
||||||
|
Advance(); |
||||||
|
ow_->StartObject(key_); |
||||||
|
key_.clear(); |
||||||
|
stack_.push(ENTRY); |
||||||
|
return util::Status::OK; |
||||||
|
} |
||||||
|
|
||||||
|
util::Status JsonStreamParser::ParseObjectMid(TokenType type) { |
||||||
|
if (type == UNKNOWN) { |
||||||
|
return ReportUnknown("Expected , or } after key:value pair."); |
||||||
|
} |
||||||
|
|
||||||
|
// Object is complete, advance past the comma and render the EndObject.
|
||||||
|
if (type == END_OBJECT) { |
||||||
|
Advance(); |
||||||
|
ow_->EndObject(); |
||||||
|
return util::Status::OK; |
||||||
|
} |
||||||
|
// Found a comma, advance past it and get ready for an entry.
|
||||||
|
if (type == VALUE_SEPARATOR) { |
||||||
|
Advance(); |
||||||
|
stack_.push(ENTRY); |
||||||
|
return util::Status::OK; |
||||||
|
} |
||||||
|
// Illegal token after key:value pair.
|
||||||
|
return ReportFailure("Expected , or } after key:value pair."); |
||||||
|
} |
||||||
|
|
||||||
|
util::Status JsonStreamParser::ParseEntry(TokenType type) { |
||||||
|
if (type == UNKNOWN) { |
||||||
|
return ReportUnknown("Expected an object key or }."); |
||||||
|
} |
||||||
|
|
||||||
|
// Close the object and return. This allows for trailing commas.
|
||||||
|
if (type == END_OBJECT) { |
||||||
|
ow_->EndObject(); |
||||||
|
Advance(); |
||||||
|
return util::Status::OK; |
||||||
|
} |
||||||
|
|
||||||
|
util::Status result; |
||||||
|
if (type == BEGIN_STRING) { |
||||||
|
// Key is a string (standard JSON), parse it and store the string.
|
||||||
|
result = ParseStringHelper(); |
||||||
|
if (result.ok()) { |
||||||
|
key_storage_.clear(); |
||||||
|
if (!parsed_storage_.empty()) { |
||||||
|
parsed_storage_.swap(key_storage_); |
||||||
|
key_ = StringPiece(key_storage_); |
||||||
|
} else { |
||||||
|
key_ = parsed_; |
||||||
|
} |
||||||
|
parsed_.clear(); |
||||||
|
} |
||||||
|
} else if (type == BEGIN_KEY) { |
||||||
|
// Key is a bare key (back compat), create a StringPiece pointing to it.
|
||||||
|
result = ParseKey(); |
||||||
|
} else { |
||||||
|
// Unknown key type, report an error.
|
||||||
|
result = ReportFailure("Expected an object key or }."); |
||||||
|
} |
||||||
|
// On success we next expect an entry mid ':' then an object mid ',' or '}'
|
||||||
|
if (result.ok()) { |
||||||
|
stack_.push(OBJ_MID); |
||||||
|
stack_.push(ENTRY_MID); |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
util::Status JsonStreamParser::ParseEntryMid(TokenType type) { |
||||||
|
if (type == UNKNOWN) { |
||||||
|
return ReportUnknown("Expected : between key:value pair."); |
||||||
|
} |
||||||
|
if (type == ENTRY_SEPARATOR) { |
||||||
|
Advance(); |
||||||
|
stack_.push(VALUE); |
||||||
|
return util::Status::OK; |
||||||
|
} |
||||||
|
return ReportFailure("Expected : between key:value pair."); |
||||||
|
} |
||||||
|
|
||||||
|
util::Status JsonStreamParser::HandleBeginArray() { |
||||||
|
GOOGLE_DCHECK_EQ('[', *p_.data()); |
||||||
|
Advance(); |
||||||
|
ow_->StartList(key_); |
||||||
|
key_.clear(); |
||||||
|
stack_.push(ARRAY_VALUE); |
||||||
|
return util::Status::OK; |
||||||
|
} |
||||||
|
|
||||||
|
util::Status JsonStreamParser::ParseArrayValue(TokenType type) { |
||||||
|
if (type == UNKNOWN) { |
||||||
|
return ReportUnknown("Expected a value or ] within an array."); |
||||||
|
} |
||||||
|
|
||||||
|
if (type == END_ARRAY) { |
||||||
|
ow_->EndList(); |
||||||
|
Advance(); |
||||||
|
return util::Status::OK; |
||||||
|
} |
||||||
|
|
||||||
|
// The ParseValue call may push something onto the stack so we need to make
|
||||||
|
// sure an ARRAY_MID is after it, so we push it on now.
|
||||||
|
stack_.push(ARRAY_MID); |
||||||
|
util::Status result = ParseValue(type); |
||||||
|
if (result == util::Status::CANCELLED) { |
||||||
|
// If we were cancelled, pop back off the ARRAY_MID so we don't try to
|
||||||
|
// push it on again when we try over.
|
||||||
|
stack_.pop(); |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
util::Status JsonStreamParser::ParseArrayMid(TokenType type) { |
||||||
|
if (type == UNKNOWN) { |
||||||
|
return ReportUnknown("Expected , or ] after array value."); |
||||||
|
} |
||||||
|
|
||||||
|
if (type == END_ARRAY) { |
||||||
|
ow_->EndList(); |
||||||
|
Advance(); |
||||||
|
return util::Status::OK; |
||||||
|
} |
||||||
|
|
||||||
|
// Found a comma, advance past it and expect an array value next.
|
||||||
|
if (type == VALUE_SEPARATOR) { |
||||||
|
Advance(); |
||||||
|
stack_.push(ARRAY_VALUE); |
||||||
|
return util::Status::OK; |
||||||
|
} |
||||||
|
// Illegal token after array value.
|
||||||
|
return ReportFailure("Expected , or ] after array value."); |
||||||
|
} |
||||||
|
|
||||||
|
util::Status JsonStreamParser::ParseTrue() { |
||||||
|
ow_->RenderBool(key_, true); |
||||||
|
key_.clear(); |
||||||
|
p_.remove_prefix(true_len); |
||||||
|
return util::Status::OK; |
||||||
|
} |
||||||
|
|
||||||
|
util::Status JsonStreamParser::ParseFalse() { |
||||||
|
ow_->RenderBool(key_, false); |
||||||
|
key_.clear(); |
||||||
|
p_.remove_prefix(false_len); |
||||||
|
return util::Status::OK; |
||||||
|
} |
||||||
|
|
||||||
|
util::Status JsonStreamParser::ParseNull() { |
||||||
|
ow_->RenderNull(key_); |
||||||
|
key_.clear(); |
||||||
|
p_.remove_prefix(null_len); |
||||||
|
return util::Status::OK; |
||||||
|
} |
||||||
|
|
||||||
|
util::Status JsonStreamParser::ReportFailure(StringPiece message) { |
||||||
|
static const int kContextLength = 20; |
||||||
|
const char* p_start = p_.data(); |
||||||
|
const char* json_start = json_.data(); |
||||||
|
const char* begin = max(p_start - kContextLength, json_start); |
||||||
|
const char* end = min(p_start + kContextLength, json_start + json_.size()); |
||||||
|
StringPiece segment(begin, end - begin); |
||||||
|
string location(p_start - begin, ' '); |
||||||
|
location.push_back('^'); |
||||||
|
return util::Status(util::error::INVALID_ARGUMENT, |
||||||
|
StrCat(message, "\n", segment, "\n", location)); |
||||||
|
} |
||||||
|
|
||||||
|
util::Status JsonStreamParser::ReportUnknown(StringPiece message) { |
||||||
|
// If we aren't finishing the parse, cancel parsing and try later.
|
||||||
|
if (!finishing_) { |
||||||
|
return util::Status::CANCELLED; |
||||||
|
} |
||||||
|
if (p_.empty()) { |
||||||
|
return ReportFailure(StrCat("Unexpected end of string. ", message)); |
||||||
|
} |
||||||
|
return ReportFailure(message); |
||||||
|
} |
||||||
|
|
||||||
|
void JsonStreamParser::SkipWhitespace() { |
||||||
|
while (!p_.empty() && ascii_isspace(*p_.data())) { |
||||||
|
Advance(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void JsonStreamParser::Advance() { |
||||||
|
// Advance by moving one UTF8 character while making sure we don't go beyond
|
||||||
|
// the length of StringPiece.
|
||||||
|
p_.remove_prefix( |
||||||
|
min<int>(p_.length(), UTF8FirstLetterNumBytes(p_.data(), p_.length()))); |
||||||
|
} |
||||||
|
|
||||||
|
util::Status JsonStreamParser::ParseKey() { |
||||||
|
StringPiece original = p_; |
||||||
|
if (!ConsumeKey(&p_, &key_)) { |
||||||
|
return ReportFailure("Invalid key or variable name."); |
||||||
|
} |
||||||
|
// If we consumed everything but expect more data, reset p_ and cancel since
|
||||||
|
// we can't know if the key was complete or not.
|
||||||
|
if (!finishing_ && p_.empty()) { |
||||||
|
p_ = original; |
||||||
|
return util::Status::CANCELLED; |
||||||
|
} |
||||||
|
// Since we aren't using the key storage, clear it out.
|
||||||
|
key_storage_.clear(); |
||||||
|
return util::Status::OK; |
||||||
|
} |
||||||
|
|
||||||
|
JsonStreamParser::TokenType JsonStreamParser::GetNextTokenType() { |
||||||
|
SkipWhitespace(); |
||||||
|
|
||||||
|
int size = p_.size(); |
||||||
|
if (size == 0) { |
||||||
|
// If we ran out of data, report unknown and we'll place the previous parse
|
||||||
|
// type onto the stack and try again when we have more data.
|
||||||
|
return UNKNOWN; |
||||||
|
} |
||||||
|
// TODO(sven): Split this method based on context since different contexts
|
||||||
|
// support different tokens. Would slightly speed up processing?
|
||||||
|
const char* data = p_.data(); |
||||||
|
if (*data == '\"' || *data == '\'') return BEGIN_STRING; |
||||||
|
if (*data == '-' || ('0' <= *data && *data <= '9')) { |
||||||
|
return BEGIN_NUMBER; |
||||||
|
} |
||||||
|
if (size >= true_len && !strncmp(data, "true", true_len)) { |
||||||
|
return BEGIN_TRUE; |
||||||
|
} |
||||||
|
if (size >= false_len && !strncmp(data, "false", false_len)) { |
||||||
|
return BEGIN_FALSE; |
||||||
|
} |
||||||
|
if (size >= null_len && !strncmp(data, "null", null_len)) { |
||||||
|
return BEGIN_NULL; |
||||||
|
} |
||||||
|
if (*data == '{') return BEGIN_OBJECT; |
||||||
|
if (*data == '}') return END_OBJECT; |
||||||
|
if (*data == '[') return BEGIN_ARRAY; |
||||||
|
if (*data == ']') return END_ARRAY; |
||||||
|
if (*data == ':') return ENTRY_SEPARATOR; |
||||||
|
if (*data == ',') return VALUE_SEPARATOR; |
||||||
|
if (MatchKey(p_)) { |
||||||
|
return BEGIN_KEY; |
||||||
|
} |
||||||
|
|
||||||
|
// We don't know that we necessarily have an invalid token here, just that we
|
||||||
|
// can't parse what we have so far. So we don't report an error and just
|
||||||
|
// return UNKNOWN so we can try again later when we have more data, or if we
|
||||||
|
// finish and we have leftovers.
|
||||||
|
return UNKNOWN; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
} // namespace google
|
@ -0,0 +1,256 @@ |
|||||||
|
// 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_UTIL_CONVERTER_JSON_STREAM_PARSER_H__ |
||||||
|
#define GOOGLE_PROTOBUF_UTIL_CONVERTER_JSON_STREAM_PARSER_H__ |
||||||
|
|
||||||
|
#include <stack> |
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include <google/protobuf/stubs/common.h> |
||||||
|
#include <google/protobuf/stubs/stringpiece.h> |
||||||
|
#include <google/protobuf/stubs/status.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace util { |
||||||
|
class Status; |
||||||
|
} // namespace util
|
||||||
|
|
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace converter { |
||||||
|
|
||||||
|
class ObjectWriter; |
||||||
|
|
||||||
|
// A JSON parser that can parse a stream of JSON chunks rather than needing the
|
||||||
|
// entire JSON string up front. It is a modified version of the parser in
|
||||||
|
// //net/proto/json/json-parser.h that has been changed in the following ways:
|
||||||
|
// - Changed from recursion to an explicit stack to allow resumption
|
||||||
|
// - Added support for int64 and uint64 numbers
|
||||||
|
// - Removed support for octal and decimal escapes
|
||||||
|
// - Removed support for numeric keys
|
||||||
|
// - Removed support for functions (javascript)
|
||||||
|
// - Removed some lax-comma support (but kept trailing comma support)
|
||||||
|
// - Writes directly to an ObjectWriter rather than using subclassing
|
||||||
|
//
|
||||||
|
// Here is an example usage:
|
||||||
|
// JsonStreamParser parser(ow_.get());
|
||||||
|
// util::Status result = parser.Parse(chunk1);
|
||||||
|
// result.Update(parser.Parse(chunk2));
|
||||||
|
// result.Update(parser.FinishParse());
|
||||||
|
// GOOGLE_DCHECK(result.ok()) << "Failed to parse JSON";
|
||||||
|
//
|
||||||
|
// This parser is thread-compatible as long as only one thread is calling a
|
||||||
|
// Parse() method at a time.
|
||||||
|
class LIBPROTOBUF_EXPORT JsonStreamParser { |
||||||
|
public: |
||||||
|
// Creates a JsonStreamParser that will write to the given ObjectWriter.
|
||||||
|
explicit JsonStreamParser(ObjectWriter* ow); |
||||||
|
virtual ~JsonStreamParser(); |
||||||
|
|
||||||
|
// Parse a JSON string (UTF-8 encoded).
|
||||||
|
util::Status Parse(StringPiece json); |
||||||
|
|
||||||
|
// Finish parsing the JSON string.
|
||||||
|
util::Status FinishParse(); |
||||||
|
|
||||||
|
private: |
||||||
|
enum TokenType { |
||||||
|
BEGIN_STRING, // " or '
|
||||||
|
BEGIN_NUMBER, // - or digit
|
||||||
|
BEGIN_TRUE, // true
|
||||||
|
BEGIN_FALSE, // false
|
||||||
|
BEGIN_NULL, // null
|
||||||
|
BEGIN_OBJECT, // {
|
||||||
|
END_OBJECT, // }
|
||||||
|
BEGIN_ARRAY, // [
|
||||||
|
END_ARRAY, // ]
|
||||||
|
ENTRY_SEPARATOR, // :
|
||||||
|
VALUE_SEPARATOR, // ,
|
||||||
|
BEGIN_KEY, // letter, _, $ or digit. Must begin with non-digit
|
||||||
|
UNKNOWN // Unknown token or we ran out of the stream.
|
||||||
|
}; |
||||||
|
|
||||||
|
enum ParseType { |
||||||
|
VALUE, // Expects a {, [, true, false, null, string or number
|
||||||
|
OBJ_MID, // Expects a ',' or }
|
||||||
|
ENTRY, // Expects a key or }
|
||||||
|
ENTRY_MID, // Expects a :
|
||||||
|
ARRAY_VALUE, // Expects a value or ]
|
||||||
|
ARRAY_MID // Expects a ',' or ]
|
||||||
|
}; |
||||||
|
|
||||||
|
// Holds the result of parsing a number
|
||||||
|
struct NumberResult { |
||||||
|
enum Type { DOUBLE, INT, UINT }; |
||||||
|
Type type; |
||||||
|
union { |
||||||
|
double double_val; |
||||||
|
int64 int_val; |
||||||
|
uint64 uint_val; |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
// Parses a single chunk of JSON, returning an error if the JSON was invalid.
|
||||||
|
util::Status ParseChunk(StringPiece json); |
||||||
|
|
||||||
|
// Runs the parser based on stack_ and p_, until the stack is empty or p_ runs
|
||||||
|
// out of data. If we unexpectedly run out of p_ we push the latest back onto
|
||||||
|
// the stack and return.
|
||||||
|
util::Status RunParser(); |
||||||
|
|
||||||
|
// Parses a value from p_ and writes it to ow_.
|
||||||
|
// A value may be an object, array, true, false, null, string or number.
|
||||||
|
util::Status ParseValue(TokenType type); |
||||||
|
|
||||||
|
// Parses a string and writes it out to the ow_.
|
||||||
|
util::Status ParseString(); |
||||||
|
|
||||||
|
// Parses a string, storing the result in parsed_.
|
||||||
|
util::Status ParseStringHelper(); |
||||||
|
|
||||||
|
// This function parses unicode escape sequences in strings. It returns an
|
||||||
|
// error when there's a parsing error, either the size is not the expected
|
||||||
|
// size or a character is not a hex digit. When it returns str will contain
|
||||||
|
// what has been successfully parsed so far.
|
||||||
|
util::Status ParseUnicodeEscape(); |
||||||
|
|
||||||
|
// Expects p_ to point to a JSON number, writes the number to the writer using
|
||||||
|
// the appropriate Render method based on the type of number.
|
||||||
|
util::Status ParseNumber(); |
||||||
|
|
||||||
|
// Parse a number into a NumberResult, reporting an error if no number could
|
||||||
|
// be parsed. This method will try to parse into a uint64, int64, or double
|
||||||
|
// based on whether the number was positive or negative or had a decimal
|
||||||
|
// component.
|
||||||
|
util::Status ParseNumberHelper(NumberResult* result); |
||||||
|
|
||||||
|
// Handles a { during parsing of a value.
|
||||||
|
util::Status HandleBeginObject(); |
||||||
|
|
||||||
|
// Parses from the ENTRY state.
|
||||||
|
util::Status ParseEntry(TokenType type); |
||||||
|
|
||||||
|
// Parses from the ENTRY_MID state.
|
||||||
|
util::Status ParseEntryMid(TokenType type); |
||||||
|
|
||||||
|
// Parses from the OBJ_MID state.
|
||||||
|
util::Status ParseObjectMid(TokenType type); |
||||||
|
|
||||||
|
// Handles a [ during parsing of a value.
|
||||||
|
util::Status HandleBeginArray(); |
||||||
|
|
||||||
|
// Parses from the ARRAY_VALUE state.
|
||||||
|
util::Status ParseArrayValue(TokenType type); |
||||||
|
|
||||||
|
// Parses from the ARRAY_MID state.
|
||||||
|
util::Status ParseArrayMid(TokenType type); |
||||||
|
|
||||||
|
// Expects p_ to point to an unquoted literal
|
||||||
|
util::Status ParseTrue(); |
||||||
|
util::Status ParseFalse(); |
||||||
|
util::Status ParseNull(); |
||||||
|
|
||||||
|
// Report a failure as a util::Status.
|
||||||
|
util::Status ReportFailure(StringPiece message); |
||||||
|
|
||||||
|
// Report a failure due to an UNKNOWN token type. We check if we hit the
|
||||||
|
// end of the stream and if we're finishing or not to detect what type of
|
||||||
|
// status to return in this case.
|
||||||
|
util::Status ReportUnknown(StringPiece message); |
||||||
|
|
||||||
|
// Advance p_ past all whitespace or until the end of the string.
|
||||||
|
void SkipWhitespace(); |
||||||
|
|
||||||
|
// Advance p_ one UTF-8 character
|
||||||
|
void Advance(); |
||||||
|
|
||||||
|
// Expects p_ to point to the beginning of a key.
|
||||||
|
util::Status ParseKey(); |
||||||
|
|
||||||
|
// Return the type of the next token at p_.
|
||||||
|
TokenType GetNextTokenType(); |
||||||
|
|
||||||
|
// The object writer to write parse events to.
|
||||||
|
ObjectWriter* ow_; |
||||||
|
|
||||||
|
// The stack of parsing we still need to do. When the stack runs empty we will
|
||||||
|
// have parsed a single value from the root (e.g. an object or list).
|
||||||
|
std::stack<ParseType> stack_; |
||||||
|
|
||||||
|
// Contains any leftover text from a previous chunk that we weren't able to
|
||||||
|
// fully parse, for example the start of a key or number.
|
||||||
|
string leftover_; |
||||||
|
|
||||||
|
// The current chunk of JSON being parsed. Primarily used for providing
|
||||||
|
// context during error reporting.
|
||||||
|
StringPiece json_; |
||||||
|
|
||||||
|
// A pointer within the current JSON being parsed, used to track location.
|
||||||
|
StringPiece p_; |
||||||
|
|
||||||
|
// Stores the last key read, as we separate parsing of keys and values.
|
||||||
|
StringPiece key_; |
||||||
|
|
||||||
|
// Storage for key_ if we need to keep ownership, for example between chunks
|
||||||
|
// or if the key was unescaped from a JSON string.
|
||||||
|
string key_storage_; |
||||||
|
|
||||||
|
// True during the FinishParse() call, so we know that any errors are fatal.
|
||||||
|
// For example an unterminated string will normally result in cancelling and
|
||||||
|
// trying during the next chunk, but during FinishParse() it is an error.
|
||||||
|
bool finishing_; |
||||||
|
|
||||||
|
// String we parsed during a call to ParseStringHelper().
|
||||||
|
StringPiece parsed_; |
||||||
|
|
||||||
|
// Storage for the string we parsed. This may be empty if the string was able
|
||||||
|
// to be parsed directly from the input.
|
||||||
|
string parsed_storage_; |
||||||
|
|
||||||
|
// The character that opened the string, either ' or ".
|
||||||
|
// A value of 0 indicates that string parsing is not in process.
|
||||||
|
char string_open_; |
||||||
|
|
||||||
|
// Storage for utf8-coerced bytes.
|
||||||
|
google::protobuf::scoped_array<char> utf8_storage_; |
||||||
|
|
||||||
|
// Length of the storage for utf8-coerced bytes.
|
||||||
|
int utf8_length_; |
||||||
|
|
||||||
|
GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(JsonStreamParser); |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
|
||||||
|
} // namespace google
|
||||||
|
#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_JSON_STREAM_PARSER_H__
|
@ -0,0 +1,697 @@ |
|||||||
|
// 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/util/internal/json_stream_parser.h> |
||||||
|
|
||||||
|
#include <google/protobuf/stubs/common.h> |
||||||
|
#include <google/protobuf/stubs/time.h> |
||||||
|
#include <google/protobuf/util/internal/expecting_objectwriter.h> |
||||||
|
#include <google/protobuf/util/internal/object_writer.h> |
||||||
|
#include <google/protobuf/stubs/strutil.h> |
||||||
|
#include <gtest/gtest.h> |
||||||
|
#include <google/protobuf/stubs/status.h> |
||||||
|
|
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
using util::Status; |
||||||
|
namespace error { |
||||||
|
using util::error::INVALID_ARGUMENT; |
||||||
|
} // namespace error
|
||||||
|
namespace converter { |
||||||
|
|
||||||
|
using util::Status; |
||||||
|
|
||||||
|
// Tests for the JSON Stream Parser. These tests are intended to be
|
||||||
|
// comprehensive and cover the following:
|
||||||
|
//
|
||||||
|
// Positive tests:
|
||||||
|
// - true, false, null
|
||||||
|
// - empty object or array.
|
||||||
|
// - negative and positive double and int, unsigned int
|
||||||
|
// - single and double quoted strings
|
||||||
|
// - string key, unquoted key, numeric key
|
||||||
|
// - array containing array, object, value
|
||||||
|
// - object containing array, object, value
|
||||||
|
// - unicode handling in strings
|
||||||
|
// - ascii escaping (\b, \f, \n, \r, \t, \v)
|
||||||
|
// - trailing commas
|
||||||
|
//
|
||||||
|
// Negative tests:
|
||||||
|
// - illegal literals
|
||||||
|
// - mismatched quotes failure on strings
|
||||||
|
// - unterminated string failure
|
||||||
|
// - unexpected end of string failure
|
||||||
|
// - mismatched object and array closing
|
||||||
|
// - Failure to close array or object
|
||||||
|
// - numbers too large
|
||||||
|
// - invalid unicode escapes.
|
||||||
|
// - invalid unicode sequences.
|
||||||
|
// - numbers as keys
|
||||||
|
//
|
||||||
|
// For each test we split the input string on every possible character to ensure
|
||||||
|
// the parser is able to handle arbitrarily split input for all cases. We also
|
||||||
|
// do a final test of the entire test case one character at a time.
|
||||||
|
class JsonStreamParserTest : public ::testing::Test { |
||||||
|
protected: |
||||||
|
JsonStreamParserTest() : mock_(), ow_(&mock_) {} |
||||||
|
virtual ~JsonStreamParserTest() {} |
||||||
|
|
||||||
|
util::Status RunTest(StringPiece json, int split) { |
||||||
|
JsonStreamParser parser(&mock_); |
||||||
|
|
||||||
|
// Special case for split == length, test parsing one character at a time.
|
||||||
|
if (split == json.length()) { |
||||||
|
GOOGLE_LOG(INFO) << "Testing split every char: " << json; |
||||||
|
for (int i = 0; i < json.length(); ++i) { |
||||||
|
StringPiece single = json.substr(i, 1); |
||||||
|
util::Status result = parser.Parse(single); |
||||||
|
if (!result.ok()) { |
||||||
|
return result; |
||||||
|
} |
||||||
|
} |
||||||
|
return parser.FinishParse(); |
||||||
|
} |
||||||
|
|
||||||
|
// Normal case, split at the split point and parse two substrings.
|
||||||
|
StringPiece first = json.substr(0, split); |
||||||
|
StringPiece rest = json.substr(split); |
||||||
|
GOOGLE_LOG(INFO) << "Testing split: " << first << "><" << rest; |
||||||
|
util::Status result = parser.Parse(first); |
||||||
|
if (result.ok()) { |
||||||
|
result = parser.Parse(rest); |
||||||
|
if (result.ok()) { |
||||||
|
result = parser.FinishParse(); |
||||||
|
} |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
void DoTest(StringPiece json, int split) { |
||||||
|
util::Status result = RunTest(json, split); |
||||||
|
if (!result.ok()) { |
||||||
|
GOOGLE_LOG(WARNING) << result; |
||||||
|
} |
||||||
|
EXPECT_OK(result); |
||||||
|
} |
||||||
|
|
||||||
|
void DoErrorTest(StringPiece json, int split, StringPiece error_prefix) { |
||||||
|
util::Status result = RunTest(json, split); |
||||||
|
EXPECT_EQ(util::error::INVALID_ARGUMENT, result.error_code()); |
||||||
|
StringPiece error_message(result.error_message()); |
||||||
|
EXPECT_EQ(error_prefix, error_message.substr(0, error_prefix.size())); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
MockObjectWriter mock_; |
||||||
|
ExpectingObjectWriter ow_; |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
// Positive tests
|
||||||
|
|
||||||
|
// - true, false, null
|
||||||
|
TEST_F(JsonStreamParserTest, SimpleTrue) { |
||||||
|
StringPiece str = "true"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.RenderBool("", true); |
||||||
|
DoTest(str, i); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, SimpleFalse) { |
||||||
|
StringPiece str = "false"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.RenderBool("", false); |
||||||
|
DoTest(str, i); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, SimpleNull) { |
||||||
|
StringPiece str = "null"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.RenderNull(""); |
||||||
|
DoTest(str, i); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// - empty object and array.
|
||||||
|
TEST_F(JsonStreamParserTest, EmptyObject) { |
||||||
|
StringPiece str = "{}"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.StartObject("")->EndObject(); |
||||||
|
DoTest(str, i); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, EmptyList) { |
||||||
|
StringPiece str = "[]"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.StartList("")->EndList(); |
||||||
|
DoTest(str, i); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// - negative and positive double and int, unsigned int
|
||||||
|
TEST_F(JsonStreamParserTest, SimpleDouble) { |
||||||
|
StringPiece str = "42.5"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.RenderDouble("", 42.5); |
||||||
|
DoTest(str, i); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, ScientificDouble) { |
||||||
|
StringPiece str = "1.2345e-10"; |
||||||
|
for (int i = 0; i < str.length(); ++i) { |
||||||
|
ow_.RenderDouble("", 1.2345e-10); |
||||||
|
DoTest(str, i); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, SimpleNegativeDouble) { |
||||||
|
StringPiece str = "-1045.235"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.RenderDouble("", -1045.235); |
||||||
|
DoTest(str, i); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, SimpleInt) { |
||||||
|
StringPiece str = "123456"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.RenderUint64("", 123456); |
||||||
|
DoTest(str, i); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, SimpleNegativeInt) { |
||||||
|
StringPiece str = "-79497823553162765"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.RenderInt64("", -79497823553162765LL); |
||||||
|
DoTest(str, i); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, SimpleUnsignedInt) { |
||||||
|
StringPiece str = "11779497823553162765"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.RenderUint64("", 11779497823553162765ULL); |
||||||
|
DoTest(str, i); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// - single and double quoted strings
|
||||||
|
TEST_F(JsonStreamParserTest, EmptyDoubleQuotedString) { |
||||||
|
StringPiece str = "\"\""; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.RenderString("", ""); |
||||||
|
DoTest(str, i); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, EmptySingleQuotedString) { |
||||||
|
StringPiece str = "''"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.RenderString("", ""); |
||||||
|
DoTest(str, i); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, SimpleDoubleQuotedString) { |
||||||
|
StringPiece str = "\"Some String\""; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.RenderString("", "Some String"); |
||||||
|
DoTest(str, i); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, SimpleSingleQuotedString) { |
||||||
|
StringPiece str = "'Another String'"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.RenderString("", "Another String"); |
||||||
|
DoTest(str, i); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// - string key, unquoted key, numeric key
|
||||||
|
TEST_F(JsonStreamParserTest, ObjectKeyTypes) { |
||||||
|
StringPiece str = |
||||||
|
"{'s': true, \"d\": false, key: null, snake_key: [], camelKey: {}}"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.StartObject("") |
||||||
|
->RenderBool("s", true) |
||||||
|
->RenderBool("d", false) |
||||||
|
->RenderNull("key") |
||||||
|
->StartList("snake_key") |
||||||
|
->EndList() |
||||||
|
->StartObject("camelKey") |
||||||
|
->EndObject() |
||||||
|
->EndObject(); |
||||||
|
DoTest(str, i); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// - array containing array, object, values (true, false, null, num, string)
|
||||||
|
TEST_F(JsonStreamParserTest, ArrayValues) { |
||||||
|
StringPiece str = |
||||||
|
"[true, false, null, 'a string', \"another string\", [22, -127, 45.3, " |
||||||
|
"-1056.4, 11779497823553162765], {'key': true}]"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.StartList("") |
||||||
|
->RenderBool("", true) |
||||||
|
->RenderBool("", false) |
||||||
|
->RenderNull("") |
||||||
|
->RenderString("", "a string") |
||||||
|
->RenderString("", "another string") |
||||||
|
->StartList("") |
||||||
|
->RenderUint64("", 22) |
||||||
|
->RenderInt64("", -127) |
||||||
|
->RenderDouble("", 45.3) |
||||||
|
->RenderDouble("", -1056.4) |
||||||
|
->RenderUint64("", 11779497823553162765ULL) |
||||||
|
->EndList() |
||||||
|
->StartObject("") |
||||||
|
->RenderBool("key", true) |
||||||
|
->EndObject() |
||||||
|
->EndList(); |
||||||
|
DoTest(str, i); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// - object containing array, object, value (true, false, null, num, string)
|
||||||
|
TEST_F(JsonStreamParserTest, ObjectValues) { |
||||||
|
StringPiece str = |
||||||
|
"{t: true, f: false, n: null, s: 'a string', d: \"another string\", pi: " |
||||||
|
"22, ni: -127, pd: 45.3, nd: -1056.4, pl: 11779497823553162765, l: [[]], " |
||||||
|
"o: {'key': true}}"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.StartObject("") |
||||||
|
->RenderBool("t", true) |
||||||
|
->RenderBool("f", false) |
||||||
|
->RenderNull("n") |
||||||
|
->RenderString("s", "a string") |
||||||
|
->RenderString("d", "another string") |
||||||
|
->RenderUint64("pi", 22) |
||||||
|
->RenderInt64("ni", -127) |
||||||
|
->RenderDouble("pd", 45.3) |
||||||
|
->RenderDouble("nd", -1056.4) |
||||||
|
->RenderUint64("pl", 11779497823553162765ULL) |
||||||
|
->StartList("l") |
||||||
|
->StartList("") |
||||||
|
->EndList() |
||||||
|
->EndList() |
||||||
|
->StartObject("o") |
||||||
|
->RenderBool("key", true) |
||||||
|
->EndObject() |
||||||
|
->EndObject(); |
||||||
|
DoTest(str, i); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// - unicode handling in strings
|
||||||
|
TEST_F(JsonStreamParserTest, UnicodeEscaping) { |
||||||
|
StringPiece str = "[\"\\u0639\\u0631\\u0628\\u0649\"]"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.StartList("")->RenderString("", "\u0639\u0631\u0628\u0649")->EndList(); |
||||||
|
DoTest(str, i); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// - ascii escaping (\b, \f, \n, \r, \t, \v)
|
||||||
|
TEST_F(JsonStreamParserTest, AsciiEscaping) { |
||||||
|
StringPiece str = |
||||||
|
"[\"\\b\", \"\\ning\", \"test\\f\", \"\\r\\t\", \"test\\\\\\ving\"]"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.StartList("") |
||||||
|
->RenderString("", "\b") |
||||||
|
->RenderString("", "\ning") |
||||||
|
->RenderString("", "test\f") |
||||||
|
->RenderString("", "\r\t") |
||||||
|
->RenderString("", "test\\\ving") |
||||||
|
->EndList(); |
||||||
|
DoTest(str, i); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// - trailing commas, we support a single trailing comma but no internal commas.
|
||||||
|
TEST_F(JsonStreamParserTest, TrailingCommas) { |
||||||
|
StringPiece str = "[['a',true,], {b: null,},]"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.StartList("") |
||||||
|
->StartList("") |
||||||
|
->RenderString("", "a") |
||||||
|
->RenderBool("", true) |
||||||
|
->EndList() |
||||||
|
->StartObject("") |
||||||
|
->RenderNull("b") |
||||||
|
->EndObject() |
||||||
|
->EndList(); |
||||||
|
DoTest(str, i); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Negative tests
|
||||||
|
|
||||||
|
// illegal literals
|
||||||
|
TEST_F(JsonStreamParserTest, ExtraTextAfterTrue) { |
||||||
|
StringPiece str = "truee"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.RenderBool("", true); |
||||||
|
DoErrorTest(str, i, "Parsing terminated before end of input."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, InvalidNumberDashOnly) { |
||||||
|
StringPiece str = "-"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
DoErrorTest(str, i, "Unable to parse number."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, InvalidNumberDashName) { |
||||||
|
StringPiece str = "-foo"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
DoErrorTest(str, i, "Unable to parse number."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, InvalidLiteralInArray) { |
||||||
|
StringPiece str = "[nule]"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.StartList(""); |
||||||
|
DoErrorTest(str, i, "Unexpected token."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, InvalidLiteralInObject) { |
||||||
|
StringPiece str = "{123false}"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.StartObject(""); |
||||||
|
DoErrorTest(str, i, "Expected an object key or }."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// mismatched quotes failure on strings
|
||||||
|
TEST_F(JsonStreamParserTest, MismatchedSingleQuotedLiteral) { |
||||||
|
StringPiece str = "'Some str\""; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
DoErrorTest(str, i, "Closing quote expected in string."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, MismatchedDoubleQuotedLiteral) { |
||||||
|
StringPiece str = "\"Another string that ends poorly!'"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
DoErrorTest(str, i, "Closing quote expected in string."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// unterminated strings
|
||||||
|
TEST_F(JsonStreamParserTest, UnterminatedLiteralString) { |
||||||
|
StringPiece str = "\"Forgot the rest of i"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
DoErrorTest(str, i, "Closing quote expected in string."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, UnterminatedStringEscape) { |
||||||
|
StringPiece str = "\"Forgot the rest of \\"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
DoErrorTest(str, i, "Closing quote expected in string."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, UnterminatedStringInArray) { |
||||||
|
StringPiece str = "[\"Forgot to close the string]"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.StartList(""); |
||||||
|
DoErrorTest(str, i, "Closing quote expected in string."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, UnterminatedStringInObject) { |
||||||
|
StringPiece str = "{f: \"Forgot to close the string}"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.StartObject(""); |
||||||
|
DoErrorTest(str, i, "Closing quote expected in string."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, UnterminatedObject) { |
||||||
|
StringPiece str = "{"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.StartObject(""); |
||||||
|
DoErrorTest(str, i, "Unexpected end of string."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// mismatched object and array closing
|
||||||
|
TEST_F(JsonStreamParserTest, MismatchedCloseObject) { |
||||||
|
StringPiece str = "{'key': true]"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.StartObject("")->RenderBool("key", true); |
||||||
|
DoErrorTest(str, i, "Expected , or } after key:value pair."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, MismatchedCloseArray) { |
||||||
|
StringPiece str = "[true, null}"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.StartList("")->RenderBool("", true)->RenderNull(""); |
||||||
|
DoErrorTest(str, i, "Expected , or ] after array value."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Invalid object keys.
|
||||||
|
TEST_F(JsonStreamParserTest, InvalidNumericObjectKey) { |
||||||
|
StringPiece str = "{42: true}"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.StartObject(""); |
||||||
|
DoErrorTest(str, i, "Expected an object key or }."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, InvalidLiteralObjectInObject) { |
||||||
|
StringPiece str = "{{bob: true}}"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.StartObject(""); |
||||||
|
DoErrorTest(str, i, "Expected an object key or }."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, InvalidLiteralArrayInObject) { |
||||||
|
StringPiece str = "{[null]}"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.StartObject(""); |
||||||
|
DoErrorTest(str, i, "Expected an object key or }."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, InvalidLiteralValueInObject) { |
||||||
|
StringPiece str = "{false}"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.StartObject(""); |
||||||
|
DoErrorTest(str, i, "Expected an object key or }."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, MissingColonAfterStringInObject) { |
||||||
|
StringPiece str = "{\"key\"}"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.StartObject(""); |
||||||
|
DoErrorTest(str, i, "Expected : between key:value pair."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, MissingColonAfterKeyInObject) { |
||||||
|
StringPiece str = "{key}"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.StartObject(""); |
||||||
|
DoErrorTest(str, i, "Expected : between key:value pair."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, EndOfTextAfterKeyInObject) { |
||||||
|
StringPiece str = "{key"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.StartObject(""); |
||||||
|
DoErrorTest(str, i, "Unexpected end of string."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, MissingValueAfterColonInObject) { |
||||||
|
StringPiece str = "{key:}"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.StartObject(""); |
||||||
|
DoErrorTest(str, i, "Unexpected token."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, MissingCommaBetweenObjectEntries) { |
||||||
|
StringPiece str = "{key:20 'hello': true}"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.StartObject("")->RenderUint64("key", 20); |
||||||
|
DoErrorTest(str, i, "Expected , or } after key:value pair."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, InvalidLiteralAsObjectKey) { |
||||||
|
StringPiece str = "{false: 20}"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.StartObject(""); |
||||||
|
DoErrorTest(str, i, "Expected an object key or }."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, ExtraCharactersAfterObject) { |
||||||
|
StringPiece str = "{}}"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.StartObject("")->EndObject(); |
||||||
|
DoErrorTest(str, i, "Parsing terminated before end of input."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// numbers too large
|
||||||
|
TEST_F(JsonStreamParserTest, PositiveNumberTooBig) { |
||||||
|
StringPiece str = "[18446744073709551616]"; // 2^64
|
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.StartList(""); |
||||||
|
DoErrorTest(str, i, "Unable to parse number."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, NegativeNumberTooBig) { |
||||||
|
StringPiece str = "[-18446744073709551616]"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.StartList(""); |
||||||
|
DoErrorTest(str, i, "Unable to parse number."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
TODO(sven): Fail parsing when parsing a double that is too large. |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, DoubleTooBig) { |
||||||
|
StringPiece str = "[184464073709551232321616.45]"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.StartList(""); |
||||||
|
DoErrorTest(str, i, "Unable to parse number"); |
||||||
|
} |
||||||
|
} |
||||||
|
*/ |
||||||
|
|
||||||
|
// invalid unicode sequence.
|
||||||
|
TEST_F(JsonStreamParserTest, UnicodeEscapeCutOff) { |
||||||
|
StringPiece str = "\"\\u12"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
DoErrorTest(str, i, "Illegal hex string."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, UnicodeEscapeInvalidCharacters) { |
||||||
|
StringPiece str = "\"\\u12$4hello"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
DoErrorTest(str, i, "Invalid escape sequence."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Extra commas with an object or array.
|
||||||
|
TEST_F(JsonStreamParserTest, ExtraCommaInObject) { |
||||||
|
StringPiece str = "{'k1': true,,'k2': false}"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.StartObject("")->RenderBool("k1", true); |
||||||
|
DoErrorTest(str, i, "Expected an object key or }."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, ExtraCommaInArray) { |
||||||
|
StringPiece str = "[true,,false}"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.StartList("")->RenderBool("", true); |
||||||
|
DoErrorTest(str, i, "Unexpected token."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Extra text beyond end of value.
|
||||||
|
TEST_F(JsonStreamParserTest, ExtraTextAfterLiteral) { |
||||||
|
StringPiece str = "'hello', 'world'"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.RenderString("", "hello"); |
||||||
|
DoErrorTest(str, i, "Parsing terminated before end of input."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, ExtraTextAfterObject) { |
||||||
|
StringPiece str = "{'key': true} 'oops'"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.StartObject("")->RenderBool("key", true)->EndObject(); |
||||||
|
DoErrorTest(str, i, "Parsing terminated before end of input."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, ExtraTextAfterArray) { |
||||||
|
StringPiece str = "[null] 'oops'"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.StartList("")->RenderNull("")->EndList(); |
||||||
|
DoErrorTest(str, i, "Parsing terminated before end of input."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Random unknown text in the value.
|
||||||
|
TEST_F(JsonStreamParserTest, UnknownCharactersAsValue) { |
||||||
|
StringPiece str = "*"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
DoErrorTest(str, i, "Expected a value."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, UnknownCharactersInArray) { |
||||||
|
StringPiece str = "[*]"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.StartList(""); |
||||||
|
DoErrorTest(str, i, "Expected a value or ] within an array."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonStreamParserTest, UnknownCharactersInObject) { |
||||||
|
StringPiece str = "{'key': *}"; |
||||||
|
for (int i = 0; i <= str.length(); ++i) { |
||||||
|
ow_.StartObject(""); |
||||||
|
DoErrorTest(str, i, "Expected a value."); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
} // namespace google
|
@ -0,0 +1,65 @@ |
|||||||
|
// 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_UTIL_CONVERTER_LOCATION_TRACKER_H__ |
||||||
|
#define GOOGLE_PROTOBUF_UTIL_CONVERTER_LOCATION_TRACKER_H__ |
||||||
|
|
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include <google/protobuf/stubs/common.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace converter { |
||||||
|
|
||||||
|
// LocationTrackerInterface is an interface for classes that track
|
||||||
|
// the location information for the purpose of error reporting.
|
||||||
|
class LIBPROTOBUF_EXPORT LocationTrackerInterface { |
||||||
|
public: |
||||||
|
virtual ~LocationTrackerInterface() {} |
||||||
|
|
||||||
|
// Returns the object location as human readable string.
|
||||||
|
virtual string ToString() const = 0; |
||||||
|
|
||||||
|
protected: |
||||||
|
LocationTrackerInterface() {} |
||||||
|
|
||||||
|
private: |
||||||
|
// Please do not add any data members to this class.
|
||||||
|
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(LocationTrackerInterface); |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
|
||||||
|
} // namespace google
|
||||||
|
#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_LOCATION_TRACKER_H__
|
@ -0,0 +1,63 @@ |
|||||||
|
// 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_UTIL_CONVERTER_MOCK_ERROR_LISTENER_H__ |
||||||
|
#define GOOGLE_PROTOBUF_UTIL_CONVERTER_MOCK_ERROR_LISTENER_H__ |
||||||
|
|
||||||
|
#include <google/protobuf/stubs/stringpiece.h> |
||||||
|
#include <google/protobuf/util/internal/error_listener.h> |
||||||
|
#include <google/protobuf/util/internal/location_tracker.h> |
||||||
|
#include <gmock/gmock.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace converter { |
||||||
|
|
||||||
|
class MockErrorListener : public ErrorListener { |
||||||
|
public: |
||||||
|
MockErrorListener() {} |
||||||
|
virtual ~MockErrorListener() {} |
||||||
|
|
||||||
|
MOCK_METHOD3(InvalidName, void(const LocationTrackerInterface& loc, |
||||||
|
StringPiece unknown_name, |
||||||
|
StringPiece message)); |
||||||
|
MOCK_METHOD3(InvalidValue, void(const LocationTrackerInterface& loc, |
||||||
|
StringPiece type_name, StringPiece value)); |
||||||
|
MOCK_METHOD2(MissingField, void(const LocationTrackerInterface& loc, |
||||||
|
StringPiece missing_name)); |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
|
||||||
|
} // namespace google
|
||||||
|
#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_MOCK_ERROR_LISTENER_H__
|
@ -0,0 +1,64 @@ |
|||||||
|
// 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_UTIL_CONVERTER_OBJECT_LOCATION_TRACKER_H__ |
||||||
|
#define GOOGLE_PROTOBUF_UTIL_CONVERTER_OBJECT_LOCATION_TRACKER_H__ |
||||||
|
|
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include <google/protobuf/stubs/common.h> |
||||||
|
#include <google/protobuf/util/internal/location_tracker.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace converter { |
||||||
|
|
||||||
|
// An empty concrete implementation of LocationTrackerInterface.
|
||||||
|
class ObjectLocationTracker : public LocationTrackerInterface { |
||||||
|
public: |
||||||
|
// Creates an empty location tracker.
|
||||||
|
ObjectLocationTracker() {} |
||||||
|
|
||||||
|
virtual ~ObjectLocationTracker() {} |
||||||
|
|
||||||
|
// Returns empty because nothing is tracked.
|
||||||
|
virtual string ToString() const { return ""; } |
||||||
|
|
||||||
|
private: |
||||||
|
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ObjectLocationTracker); |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
|
||||||
|
} // namespace google
|
||||||
|
#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_OBJECT_LOCATION_TRACKER_H__
|
@ -0,0 +1,79 @@ |
|||||||
|
// 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_UTIL_CONVERTER_OBJECT_SOURCE_H__ |
||||||
|
#define GOOGLE_PROTOBUF_UTIL_CONVERTER_OBJECT_SOURCE_H__ |
||||||
|
|
||||||
|
#include <google/protobuf/stubs/common.h> |
||||||
|
#include <google/protobuf/stubs/stringpiece.h> |
||||||
|
#include <google/protobuf/stubs/status.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace converter { |
||||||
|
|
||||||
|
class ObjectWriter; |
||||||
|
|
||||||
|
// An ObjectSource is anything that can write to an ObjectWriter.
|
||||||
|
// Implementation of this interface typically provide constructors or
|
||||||
|
// factory methods to create an instance based on some source data, for
|
||||||
|
// example, a character stream, or protobuf.
|
||||||
|
//
|
||||||
|
// Derived classes could be thread-unsafe.
|
||||||
|
class LIBPROTOBUF_EXPORT ObjectSource { |
||||||
|
public: |
||||||
|
virtual ~ObjectSource() {} |
||||||
|
|
||||||
|
// Writes to the ObjectWriter
|
||||||
|
virtual util::Status WriteTo(ObjectWriter* ow) const { |
||||||
|
return NamedWriteTo("", ow); |
||||||
|
} |
||||||
|
|
||||||
|
// Writes to the ObjectWriter with a custom name for the message.
|
||||||
|
// This is useful when you chain ObjectSource together by embedding one
|
||||||
|
// within another.
|
||||||
|
virtual util::Status NamedWriteTo(StringPiece name, |
||||||
|
ObjectWriter* ow) const = 0; |
||||||
|
|
||||||
|
protected: |
||||||
|
ObjectSource() {} |
||||||
|
|
||||||
|
private: |
||||||
|
// Do not add any data members to this class.
|
||||||
|
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ObjectSource); |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
|
||||||
|
} // namespace google
|
||||||
|
#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_OBJECT_SOURCE_H__
|
@ -0,0 +1,92 @@ |
|||||||
|
// 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/util/internal/object_writer.h> |
||||||
|
|
||||||
|
#include <google/protobuf/util/internal/datapiece.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace converter { |
||||||
|
|
||||||
|
// static
|
||||||
|
void ObjectWriter::RenderDataPieceTo(const DataPiece& data, StringPiece name, |
||||||
|
ObjectWriter* ow) { |
||||||
|
switch (data.type()) { |
||||||
|
case DataPiece::TYPE_INT32: { |
||||||
|
ow->RenderInt32(name, data.ToInt32().ValueOrDie()); |
||||||
|
break; |
||||||
|
} |
||||||
|
case DataPiece::TYPE_INT64: { |
||||||
|
ow->RenderInt64(name, data.ToInt64().ValueOrDie()); |
||||||
|
break; |
||||||
|
} |
||||||
|
case DataPiece::TYPE_UINT32: { |
||||||
|
ow->RenderUint32(name, data.ToUint32().ValueOrDie()); |
||||||
|
break; |
||||||
|
} |
||||||
|
case DataPiece::TYPE_UINT64: { |
||||||
|
ow->RenderUint64(name, data.ToUint64().ValueOrDie()); |
||||||
|
break; |
||||||
|
} |
||||||
|
case DataPiece::TYPE_DOUBLE: { |
||||||
|
ow->RenderDouble(name, data.ToDouble().ValueOrDie()); |
||||||
|
break; |
||||||
|
} |
||||||
|
case DataPiece::TYPE_FLOAT: { |
||||||
|
ow->RenderFloat(name, data.ToFloat().ValueOrDie()); |
||||||
|
break; |
||||||
|
} |
||||||
|
case DataPiece::TYPE_BOOL: { |
||||||
|
ow->RenderBool(name, data.ToBool().ValueOrDie()); |
||||||
|
break; |
||||||
|
} |
||||||
|
case DataPiece::TYPE_STRING: { |
||||||
|
ow->RenderString(name, data.ToString().ValueOrDie()); |
||||||
|
break; |
||||||
|
} |
||||||
|
case DataPiece::TYPE_BYTES: { |
||||||
|
ow->RenderBytes(name, data.ToBytes().ValueOrDie()); |
||||||
|
break; |
||||||
|
} |
||||||
|
case DataPiece::TYPE_NULL: { |
||||||
|
ow->RenderNull(name); |
||||||
|
break; |
||||||
|
} |
||||||
|
default: |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
} // namespace google
|
@ -0,0 +1,126 @@ |
|||||||
|
// 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_UTIL_CONVERTER_OBJECT_WRITER_H__ |
||||||
|
#define GOOGLE_PROTOBUF_UTIL_CONVERTER_OBJECT_WRITER_H__ |
||||||
|
|
||||||
|
#include <google/protobuf/stubs/common.h> |
||||||
|
#include <google/protobuf/stubs/stringpiece.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace converter { |
||||||
|
|
||||||
|
class DataPiece; |
||||||
|
|
||||||
|
// An ObjectWriter is an interface for writing a stream of events
|
||||||
|
// representing objects and collections. Implementation of this
|
||||||
|
// interface can be used to write an object stream to an in-memory
|
||||||
|
// structure, protobufs, JSON, XML, or any other output format
|
||||||
|
// desired. The ObjectSource interface is typically used as the
|
||||||
|
// source of an object stream.
|
||||||
|
//
|
||||||
|
// See JsonObjectWriter for a sample implementation of ObjectWriter
|
||||||
|
// and its use.
|
||||||
|
//
|
||||||
|
// Derived classes could be thread-unsafe.
|
||||||
|
//
|
||||||
|
// TODO(xinb): seems like a prime candidate to apply the RAII paradigm
|
||||||
|
// and get rid the need to call EndXXX().
|
||||||
|
class LIBPROTOBUF_EXPORT ObjectWriter { |
||||||
|
public: |
||||||
|
virtual ~ObjectWriter() {} |
||||||
|
|
||||||
|
// Starts an object. If the name is empty, the object will not be named.
|
||||||
|
virtual ObjectWriter* StartObject(StringPiece name) = 0; |
||||||
|
|
||||||
|
// Ends an object.
|
||||||
|
virtual ObjectWriter* EndObject() = 0; |
||||||
|
|
||||||
|
// Starts a list. If the name is empty, the list will not be named.
|
||||||
|
virtual ObjectWriter* StartList(StringPiece name) = 0; |
||||||
|
|
||||||
|
// Ends a list.
|
||||||
|
virtual ObjectWriter* EndList() = 0; |
||||||
|
|
||||||
|
// Renders a boolean value.
|
||||||
|
virtual ObjectWriter* RenderBool(StringPiece name, bool value) = 0; |
||||||
|
|
||||||
|
// Renders an 32-bit integer value.
|
||||||
|
virtual ObjectWriter* RenderInt32(StringPiece name, int32 value) = 0; |
||||||
|
|
||||||
|
// Renders an 32-bit unsigned integer value.
|
||||||
|
virtual ObjectWriter* RenderUint32(StringPiece name, uint32 value) = 0; |
||||||
|
|
||||||
|
// Renders a 64-bit integer value.
|
||||||
|
virtual ObjectWriter* RenderInt64(StringPiece name, int64 value) = 0; |
||||||
|
|
||||||
|
// Renders an 64-bit unsigned integer value.
|
||||||
|
virtual ObjectWriter* RenderUint64(StringPiece name, uint64 value) = 0; |
||||||
|
|
||||||
|
// Renders a double value.
|
||||||
|
virtual ObjectWriter* RenderDouble(StringPiece name, double value) = 0; |
||||||
|
|
||||||
|
// Renders a float value.
|
||||||
|
virtual ObjectWriter* RenderFloat(StringPiece name, float value) = 0; |
||||||
|
|
||||||
|
// Renders a StringPiece value. This is for rendering strings.
|
||||||
|
virtual ObjectWriter* RenderString(StringPiece name, StringPiece value) = 0; |
||||||
|
|
||||||
|
// Renders a bytes value.
|
||||||
|
virtual ObjectWriter* RenderBytes(StringPiece name, StringPiece value) = 0; |
||||||
|
|
||||||
|
// Renders a Null value.
|
||||||
|
virtual ObjectWriter* RenderNull(StringPiece name) = 0; |
||||||
|
|
||||||
|
// Disables case normalization. Any RenderTYPE call after calling this
|
||||||
|
// function will output the name field as-is. No normalization is attempted on
|
||||||
|
// it. This setting is reset immediately after the next RenderTYPE is called.
|
||||||
|
virtual ObjectWriter* DisableCaseNormalizationForNextKey() { return this; } |
||||||
|
|
||||||
|
// Renders a DataPiece object to a ObjectWriter.
|
||||||
|
static void RenderDataPieceTo(const DataPiece& data, StringPiece name, |
||||||
|
ObjectWriter* ow); |
||||||
|
|
||||||
|
protected: |
||||||
|
ObjectWriter() {} |
||||||
|
|
||||||
|
private: |
||||||
|
// Do not add any data members to this class.
|
||||||
|
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ObjectWriter); |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
|
||||||
|
} // namespace google
|
||||||
|
#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_OBJECT_WRITER_H__
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,245 @@ |
|||||||
|
// 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_UTIL_CONVERTER_PROTOSTREAM_OBJECTSOURCE_H__ |
||||||
|
#define GOOGLE_PROTOBUF_UTIL_CONVERTER_PROTOSTREAM_OBJECTSOURCE_H__ |
||||||
|
|
||||||
|
#include <functional> |
||||||
|
#include <google/protobuf/stubs/hash.h> |
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include <google/protobuf/stubs/common.h> |
||||||
|
#include <google/protobuf/type.pb.h> |
||||||
|
#include <google/protobuf/util/internal/object_source.h> |
||||||
|
#include <google/protobuf/util/internal/object_writer.h> |
||||||
|
#include <google/protobuf/util/internal/type_info.h> |
||||||
|
#include <google/protobuf/util/type_resolver.h> |
||||||
|
#include <google/protobuf/stubs/stringpiece.h> |
||||||
|
#include <google/protobuf/stubs/status.h> |
||||||
|
#include <google/protobuf/stubs/statusor.h> |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
class Field; |
||||||
|
class Type; |
||||||
|
} // namespace protobuf
|
||||||
|
|
||||||
|
|
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace converter { |
||||||
|
|
||||||
|
class TypeInfo; |
||||||
|
|
||||||
|
// An ObjectSource that can parse a stream of bytes as a protocol buffer.
|
||||||
|
// This implementation uses a tech Type for tag lookup.
|
||||||
|
//
|
||||||
|
// Sample usage: (suppose input is: string proto)
|
||||||
|
// ArrayInputStream arr_stream(proto.data(), proto.size());
|
||||||
|
// CodedInputStream in_stream(&arr_stream);
|
||||||
|
// ProtoStreamObjectSource os(&in_stream, /*ServiceTypeInfo*/ typeinfo,
|
||||||
|
// <your message google::protobuf::Type>);
|
||||||
|
//
|
||||||
|
// Status status = os.WriteTo(<some ObjectWriter>);
|
||||||
|
class LIBPROTOBUF_EXPORT ProtoStreamObjectSource : public ObjectSource { |
||||||
|
public: |
||||||
|
ProtoStreamObjectSource(google::protobuf::io::CodedInputStream* stream, |
||||||
|
TypeResolver* type_resolver, |
||||||
|
const google::protobuf::Type& type); |
||||||
|
|
||||||
|
virtual ~ProtoStreamObjectSource(); |
||||||
|
|
||||||
|
virtual util::Status NamedWriteTo(StringPiece name, ObjectWriter* ow) const; |
||||||
|
|
||||||
|
protected: |
||||||
|
// Writes a proto2 Message to the ObjectWriter. When the given end_tag is
|
||||||
|
// found this method will complete, allowing it to be used for parsing both
|
||||||
|
// nested messages (end with 0) and nested groups (end with group end tag).
|
||||||
|
// The include_start_and_end parameter allows this method to be called when
|
||||||
|
// already inside of an object, and skip calling StartObject and EndObject.
|
||||||
|
virtual util::Status WriteMessage(const google::protobuf::Type& descriptor, |
||||||
|
StringPiece name, const uint32 end_tag, |
||||||
|
bool include_start_and_end, |
||||||
|
ObjectWriter* ow) const; |
||||||
|
|
||||||
|
private: |
||||||
|
ProtoStreamObjectSource(google::protobuf::io::CodedInputStream* stream, |
||||||
|
TypeInfo* typeinfo, |
||||||
|
const google::protobuf::Type& type); |
||||||
|
// Function that renders a well known type with a modified behavior.
|
||||||
|
typedef util::Status (*TypeRenderer)(const ProtoStreamObjectSource*, |
||||||
|
const google::protobuf::Type&, |
||||||
|
StringPiece, ObjectWriter*); |
||||||
|
|
||||||
|
// Looks up a field and verify its consistency with wire type in tag.
|
||||||
|
const google::protobuf::Field* FindAndVerifyField( |
||||||
|
const google::protobuf::Type& type, uint32 tag) const; |
||||||
|
|
||||||
|
// TODO(skarvaje): Mark these methods as non-const as they modify internal
|
||||||
|
// state (stream_).
|
||||||
|
//
|
||||||
|
// Renders a repeating field (packed or unpacked).
|
||||||
|
// Returns the next tag after reading all sequential repeating elements. The
|
||||||
|
// caller should use this tag before reading more tags from the stream.
|
||||||
|
util::StatusOr<uint32> RenderList(const google::protobuf::Field* field, |
||||||
|
StringPiece name, uint32 list_tag, |
||||||
|
ObjectWriter* ow) const; |
||||||
|
// Renders a NWP map.
|
||||||
|
// Returns the next tag after reading all map entries. The caller should use
|
||||||
|
// this tag before reading more tags from the stream.
|
||||||
|
util::StatusOr<uint32> RenderMap(const google::protobuf::Field* field, |
||||||
|
StringPiece name, uint32 list_tag, |
||||||
|
ObjectWriter* ow) const; |
||||||
|
|
||||||
|
// Renders an entry in a map, advancing stream pointers appropriately.
|
||||||
|
util::Status RenderMapEntry(const google::protobuf::Type* type, |
||||||
|
ObjectWriter* ow) const; |
||||||
|
|
||||||
|
// Renders a packed repeating field. A packed field is stored as:
|
||||||
|
// {tag length item1 item2 item3} instead of the less efficient
|
||||||
|
// {tag item1 tag item2 tag item3}.
|
||||||
|
util::Status RenderPacked(const google::protobuf::Field* field, |
||||||
|
ObjectWriter* ow) const; |
||||||
|
|
||||||
|
// Equivalent of RenderPacked, but for map entries.
|
||||||
|
util::Status RenderPackedMapEntry(const google::protobuf::Type* type, |
||||||
|
ObjectWriter* ow) const; |
||||||
|
|
||||||
|
// Renders a google.protobuf.Timestamp value to ObjectWriter
|
||||||
|
static util::Status RenderTimestamp(const ProtoStreamObjectSource* os, |
||||||
|
const google::protobuf::Type& type, |
||||||
|
StringPiece name, ObjectWriter* ow); |
||||||
|
|
||||||
|
// Renders a google.protobuf.Duration value to ObjectWriter
|
||||||
|
static util::Status RenderDuration(const ProtoStreamObjectSource* os, |
||||||
|
const google::protobuf::Type& type, |
||||||
|
StringPiece name, ObjectWriter* ow); |
||||||
|
|
||||||
|
// Following RenderTYPE functions render well known types in
|
||||||
|
// google/protobuf/wrappers.proto corresponding to TYPE.
|
||||||
|
static util::Status RenderDouble(const ProtoStreamObjectSource* os, |
||||||
|
const google::protobuf::Type& type, |
||||||
|
StringPiece name, ObjectWriter* ow); |
||||||
|
static util::Status RenderFloat(const ProtoStreamObjectSource* os, |
||||||
|
const google::protobuf::Type& type, |
||||||
|
StringPiece name, ObjectWriter* ow); |
||||||
|
static util::Status RenderInt64(const ProtoStreamObjectSource* os, |
||||||
|
const google::protobuf::Type& type, |
||||||
|
StringPiece name, ObjectWriter* ow); |
||||||
|
static util::Status RenderUInt64(const ProtoStreamObjectSource* os, |
||||||
|
const google::protobuf::Type& type, |
||||||
|
StringPiece name, ObjectWriter* ow); |
||||||
|
static util::Status RenderInt32(const ProtoStreamObjectSource* os, |
||||||
|
const google::protobuf::Type& type, |
||||||
|
StringPiece name, ObjectWriter* ow); |
||||||
|
static util::Status RenderUInt32(const ProtoStreamObjectSource* os, |
||||||
|
const google::protobuf::Type& type, |
||||||
|
StringPiece name, ObjectWriter* ow); |
||||||
|
static util::Status RenderBool(const ProtoStreamObjectSource* os, |
||||||
|
const google::protobuf::Type& type, |
||||||
|
StringPiece name, ObjectWriter* ow); |
||||||
|
static util::Status RenderString(const ProtoStreamObjectSource* os, |
||||||
|
const google::protobuf::Type& type, |
||||||
|
StringPiece name, ObjectWriter* ow); |
||||||
|
static util::Status RenderBytes(const ProtoStreamObjectSource* os, |
||||||
|
const google::protobuf::Type& type, |
||||||
|
StringPiece name, ObjectWriter* ow); |
||||||
|
|
||||||
|
// Renders a google.protobuf.Struct to ObjectWriter.
|
||||||
|
static util::Status RenderStruct(const ProtoStreamObjectSource* os, |
||||||
|
const google::protobuf::Type& type, |
||||||
|
StringPiece name, ObjectWriter* ow); |
||||||
|
|
||||||
|
// Helper to render google.protobuf.Struct's Value fields to ObjectWriter.
|
||||||
|
static util::Status RenderStructValue(const ProtoStreamObjectSource* os, |
||||||
|
const google::protobuf::Type& type, |
||||||
|
StringPiece name, ObjectWriter* ow); |
||||||
|
|
||||||
|
// Helper to render google.protobuf.Struct's ListValue fields to ObjectWriter.
|
||||||
|
static util::Status RenderStructListValue( |
||||||
|
const ProtoStreamObjectSource* os, |
||||||
|
const google::protobuf::Type& type, |
||||||
|
StringPiece name, ObjectWriter* ow); |
||||||
|
|
||||||
|
// Render the "Any" type.
|
||||||
|
static util::Status RenderAny(const ProtoStreamObjectSource* os, |
||||||
|
const google::protobuf::Type& type, |
||||||
|
StringPiece name, ObjectWriter* ow); |
||||||
|
|
||||||
|
// Render the "FieldMask" type.
|
||||||
|
static util::Status RenderFieldMask(const ProtoStreamObjectSource* os, |
||||||
|
const google::protobuf::Type& type, |
||||||
|
StringPiece name, ObjectWriter* ow); |
||||||
|
|
||||||
|
static hash_map<string, TypeRenderer>* CreateRendererMap(); |
||||||
|
static TypeRenderer* FindTypeRenderer(const string& type_url); |
||||||
|
|
||||||
|
// Renders a field value to the ObjectWriter.
|
||||||
|
util::Status RenderField(const google::protobuf::Field* field, |
||||||
|
StringPiece field_name, ObjectWriter* ow) const; |
||||||
|
|
||||||
|
// Reads field value according to Field spec in 'field' and returns the read
|
||||||
|
// value as string. This only works for primitive datatypes (no message
|
||||||
|
// types).
|
||||||
|
const string ReadFieldValueAsString( |
||||||
|
const google::protobuf::Field& field) const; |
||||||
|
|
||||||
|
// Utility function to detect proto maps. The 'field' MUST be repeated.
|
||||||
|
bool IsMap(const google::protobuf::Field& field) const; |
||||||
|
|
||||||
|
// Utility to read int64 and int32 values from a message type in stream_.
|
||||||
|
// Used for reading google.protobuf.Timestamp and Duration messages.
|
||||||
|
std::pair<int64, int32> ReadSecondsAndNanos( |
||||||
|
const google::protobuf::Type& type) const; |
||||||
|
|
||||||
|
// Input stream to read from. Ownership rests with the caller.
|
||||||
|
google::protobuf::io::CodedInputStream* stream_; |
||||||
|
|
||||||
|
// Type information for all the types used in the descriptor. Used to find
|
||||||
|
// google::protobuf::Type of nested messages/enums.
|
||||||
|
TypeInfo* typeinfo_; |
||||||
|
// Whether this class owns the typeinfo_ object. If true the typeinfo_ object
|
||||||
|
// should be deleted in the destructor.
|
||||||
|
bool own_typeinfo_; |
||||||
|
|
||||||
|
// google::protobuf::Type of the message source.
|
||||||
|
const google::protobuf::Type& type_; |
||||||
|
|
||||||
|
GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(ProtoStreamObjectSource); |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
|
||||||
|
} // namespace google
|
||||||
|
#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_PROTOSTREAM_OBJECTSOURCE_H__
|
@ -0,0 +1,824 @@ |
|||||||
|
// 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/util/internal/protostream_objectsource.h> |
||||||
|
|
||||||
|
#include <memory> |
||||||
|
#ifndef _SHARED_PTR_H |
||||||
|
#include <google/protobuf/stubs/shared_ptr.h> |
||||||
|
#endif |
||||||
|
#include <sstream> |
||||||
|
|
||||||
|
#include <google/protobuf/stubs/casts.h> |
||||||
|
#include <google/protobuf/any.pb.h> |
||||||
|
#include <google/protobuf/io/coded_stream.h> |
||||||
|
#include <google/protobuf/io/zero_copy_stream_impl_lite.h> |
||||||
|
#include <google/protobuf/descriptor.h> |
||||||
|
#include <google/protobuf/util/internal/expecting_objectwriter.h> |
||||||
|
#include <google/protobuf/util/internal/testdata/books.pb.h> |
||||||
|
#include <google/protobuf/util/internal/testdata/field_mask.pb.h> |
||||||
|
#include <google/protobuf/util/internal/type_info_test_helper.h> |
||||||
|
#include <google/protobuf/util/internal/constants.h> |
||||||
|
#include <google/protobuf/stubs/strutil.h> |
||||||
|
#include <google/protobuf/util/internal/testdata/anys.pb.h> |
||||||
|
#include <google/protobuf/util/internal/testdata/maps.pb.h> |
||||||
|
#include <google/protobuf/util/internal/testdata/struct.pb.h> |
||||||
|
#include <gtest/gtest.h> |
||||||
|
|
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace converter { |
||||||
|
|
||||||
|
using google::protobuf::Descriptor; |
||||||
|
using google::protobuf::DescriptorPool; |
||||||
|
using google::protobuf::FileDescriptorProto; |
||||||
|
using google::protobuf::Message; |
||||||
|
using google::protobuf::io::ArrayInputStream; |
||||||
|
using google::protobuf::io::CodedInputStream; |
||||||
|
using util::Status; |
||||||
|
using google::protobuf::testing::Author; |
||||||
|
using google::protobuf::testing::BadAuthor; |
||||||
|
using google::protobuf::testing::BadNestedBook; |
||||||
|
using google::protobuf::testing::Book; |
||||||
|
using google::protobuf::testing::Book_Label; |
||||||
|
using google::protobuf::testing::NestedBook; |
||||||
|
using google::protobuf::testing::PackedPrimitive; |
||||||
|
using google::protobuf::testing::Primitive; |
||||||
|
using google::protobuf::testing::more_author; |
||||||
|
using google::protobuf::testing::maps::MapOut; |
||||||
|
using google::protobuf::testing::anys::AnyOut; |
||||||
|
using google::protobuf::testing::anys::AnyM; |
||||||
|
using google::protobuf::testing::FieldMaskTest; |
||||||
|
using google::protobuf::testing::NestedFieldMask; |
||||||
|
using google::protobuf::testing::structs::StructType; |
||||||
|
using ::testing::_; |
||||||
|
|
||||||
|
|
||||||
|
namespace { |
||||||
|
string GetTypeUrl(const Descriptor* descriptor) { |
||||||
|
return string(kTypeServiceBaseUrl) + "/" + descriptor->full_name(); |
||||||
|
} |
||||||
|
} // namespace
|
||||||
|
|
||||||
|
class ProtostreamObjectSourceTest |
||||||
|
: public ::testing::TestWithParam<testing::TypeInfoSource> { |
||||||
|
protected: |
||||||
|
ProtostreamObjectSourceTest() : helper_(GetParam()), mock_(), ow_(&mock_) { |
||||||
|
helper_.ResetTypeInfo(Book::descriptor()); |
||||||
|
} |
||||||
|
|
||||||
|
virtual ~ProtostreamObjectSourceTest() {} |
||||||
|
|
||||||
|
void DoTest(const Message& msg, const Descriptor* descriptor) { |
||||||
|
Status status = ExecuteTest(msg, descriptor); |
||||||
|
EXPECT_EQ(Status::OK, status); |
||||||
|
} |
||||||
|
|
||||||
|
Status ExecuteTest(const Message& msg, const Descriptor* descriptor) { |
||||||
|
ostringstream oss; |
||||||
|
msg.SerializePartialToOstream(&oss); |
||||||
|
string proto = oss.str(); |
||||||
|
ArrayInputStream arr_stream(proto.data(), proto.size()); |
||||||
|
CodedInputStream in_stream(&arr_stream); |
||||||
|
|
||||||
|
google::protobuf::scoped_ptr<ProtoStreamObjectSource> os( |
||||||
|
helper_.NewProtoSource(&in_stream, GetTypeUrl(descriptor))); |
||||||
|
return os->WriteTo(&mock_); |
||||||
|
} |
||||||
|
|
||||||
|
void PrepareExpectingObjectWriterForRepeatedPrimitive() { |
||||||
|
ow_.StartObject("") |
||||||
|
->StartList("rep_fix32") |
||||||
|
->RenderUint32("", bit_cast<uint32>(3201)) |
||||||
|
->RenderUint32("", bit_cast<uint32>(0)) |
||||||
|
->RenderUint32("", bit_cast<uint32>(3202)) |
||||||
|
->EndList() |
||||||
|
->StartList("rep_u32") |
||||||
|
->RenderUint32("", bit_cast<uint32>(3203)) |
||||||
|
->RenderUint32("", bit_cast<uint32>(0)) |
||||||
|
->EndList() |
||||||
|
->StartList("rep_i32") |
||||||
|
->RenderInt32("", 0) |
||||||
|
->RenderInt32("", 3204) |
||||||
|
->RenderInt32("", 3205) |
||||||
|
->EndList() |
||||||
|
->StartList("rep_sf32") |
||||||
|
->RenderInt32("", 3206) |
||||||
|
->RenderInt32("", 0) |
||||||
|
->EndList() |
||||||
|
->StartList("rep_s32") |
||||||
|
->RenderInt32("", 0) |
||||||
|
->RenderInt32("", 3207) |
||||||
|
->RenderInt32("", 3208) |
||||||
|
->EndList() |
||||||
|
->StartList("rep_fix64") |
||||||
|
->RenderUint64("", bit_cast<uint64>(6401L)) |
||||||
|
->RenderUint64("", bit_cast<uint64>(0L)) |
||||||
|
->EndList() |
||||||
|
->StartList("rep_u64") |
||||||
|
->RenderUint64("", bit_cast<uint64>(0L)) |
||||||
|
->RenderUint64("", bit_cast<uint64>(6402L)) |
||||||
|
->RenderUint64("", bit_cast<uint64>(6403L)) |
||||||
|
->EndList() |
||||||
|
->StartList("rep_i64") |
||||||
|
->RenderInt64("", 6404L) |
||||||
|
->RenderInt64("", 0L) |
||||||
|
->EndList() |
||||||
|
->StartList("rep_sf64") |
||||||
|
->RenderInt64("", 0L) |
||||||
|
->RenderInt64("", 6405L) |
||||||
|
->RenderInt64("", 6406L) |
||||||
|
->EndList() |
||||||
|
->StartList("rep_s64") |
||||||
|
->RenderInt64("", 6407L) |
||||||
|
->RenderInt64("", 0L) |
||||||
|
->EndList() |
||||||
|
->StartList("rep_float") |
||||||
|
->RenderFloat("", 0.0f) |
||||||
|
->RenderFloat("", 32.1f) |
||||||
|
->RenderFloat("", 32.2f) |
||||||
|
->EndList() |
||||||
|
->StartList("rep_double") |
||||||
|
->RenderDouble("", 64.1L) |
||||||
|
->RenderDouble("", 0.0L) |
||||||
|
->EndList() |
||||||
|
->StartList("rep_bool") |
||||||
|
->RenderBool("", true) |
||||||
|
->RenderBool("", false) |
||||||
|
->EndList() |
||||||
|
->EndObject(); |
||||||
|
} |
||||||
|
|
||||||
|
Primitive PrepareRepeatedPrimitive() { |
||||||
|
Primitive primitive; |
||||||
|
primitive.add_rep_fix32(3201); |
||||||
|
primitive.add_rep_fix32(0); |
||||||
|
primitive.add_rep_fix32(3202); |
||||||
|
primitive.add_rep_u32(3203); |
||||||
|
primitive.add_rep_u32(0); |
||||||
|
primitive.add_rep_i32(0); |
||||||
|
primitive.add_rep_i32(3204); |
||||||
|
primitive.add_rep_i32(3205); |
||||||
|
primitive.add_rep_sf32(3206); |
||||||
|
primitive.add_rep_sf32(0); |
||||||
|
primitive.add_rep_s32(0); |
||||||
|
primitive.add_rep_s32(3207); |
||||||
|
primitive.add_rep_s32(3208); |
||||||
|
primitive.add_rep_fix64(6401L); |
||||||
|
primitive.add_rep_fix64(0L); |
||||||
|
primitive.add_rep_u64(0L); |
||||||
|
primitive.add_rep_u64(6402L); |
||||||
|
primitive.add_rep_u64(6403L); |
||||||
|
primitive.add_rep_i64(6404L); |
||||||
|
primitive.add_rep_i64(0L); |
||||||
|
primitive.add_rep_sf64(0L); |
||||||
|
primitive.add_rep_sf64(6405L); |
||||||
|
primitive.add_rep_sf64(6406L); |
||||||
|
primitive.add_rep_s64(6407L); |
||||||
|
primitive.add_rep_s64(0L); |
||||||
|
primitive.add_rep_float(0.0f); |
||||||
|
primitive.add_rep_float(32.1f); |
||||||
|
primitive.add_rep_float(32.2f); |
||||||
|
primitive.add_rep_double(64.1L); |
||||||
|
primitive.add_rep_double(0.0); |
||||||
|
primitive.add_rep_bool(true); |
||||||
|
primitive.add_rep_bool(false); |
||||||
|
|
||||||
|
PrepareExpectingObjectWriterForRepeatedPrimitive(); |
||||||
|
return primitive; |
||||||
|
} |
||||||
|
|
||||||
|
PackedPrimitive PreparePackedPrimitive() { |
||||||
|
PackedPrimitive primitive; |
||||||
|
primitive.add_rep_fix32(3201); |
||||||
|
primitive.add_rep_fix32(0); |
||||||
|
primitive.add_rep_fix32(3202); |
||||||
|
primitive.add_rep_u32(3203); |
||||||
|
primitive.add_rep_u32(0); |
||||||
|
primitive.add_rep_i32(0); |
||||||
|
primitive.add_rep_i32(3204); |
||||||
|
primitive.add_rep_i32(3205); |
||||||
|
primitive.add_rep_sf32(3206); |
||||||
|
primitive.add_rep_sf32(0); |
||||||
|
primitive.add_rep_s32(0); |
||||||
|
primitive.add_rep_s32(3207); |
||||||
|
primitive.add_rep_s32(3208); |
||||||
|
primitive.add_rep_fix64(6401L); |
||||||
|
primitive.add_rep_fix64(0L); |
||||||
|
primitive.add_rep_u64(0L); |
||||||
|
primitive.add_rep_u64(6402L); |
||||||
|
primitive.add_rep_u64(6403L); |
||||||
|
primitive.add_rep_i64(6404L); |
||||||
|
primitive.add_rep_i64(0L); |
||||||
|
primitive.add_rep_sf64(0L); |
||||||
|
primitive.add_rep_sf64(6405L); |
||||||
|
primitive.add_rep_sf64(6406L); |
||||||
|
primitive.add_rep_s64(6407L); |
||||||
|
primitive.add_rep_s64(0L); |
||||||
|
primitive.add_rep_float(0.0f); |
||||||
|
primitive.add_rep_float(32.1f); |
||||||
|
primitive.add_rep_float(32.2f); |
||||||
|
primitive.add_rep_double(64.1L); |
||||||
|
primitive.add_rep_double(0.0); |
||||||
|
primitive.add_rep_bool(true); |
||||||
|
primitive.add_rep_bool(false); |
||||||
|
|
||||||
|
PrepareExpectingObjectWriterForRepeatedPrimitive(); |
||||||
|
return primitive; |
||||||
|
} |
||||||
|
|
||||||
|
testing::TypeInfoTestHelper helper_; |
||||||
|
|
||||||
|
::testing::NiceMock<MockObjectWriter> mock_; |
||||||
|
ExpectingObjectWriter ow_; |
||||||
|
}; |
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest, |
||||||
|
ProtostreamObjectSourceTest, |
||||||
|
::testing::Values( |
||||||
|
testing::USE_TYPE_RESOLVER)); |
||||||
|
|
||||||
|
TEST_P(ProtostreamObjectSourceTest, EmptyMessage) { |
||||||
|
Book empty; |
||||||
|
ow_.StartObject("")->EndObject(); |
||||||
|
DoTest(empty, Book::descriptor()); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_P(ProtostreamObjectSourceTest, Primitives) { |
||||||
|
Primitive primitive; |
||||||
|
primitive.set_fix32(3201); |
||||||
|
primitive.set_u32(3202); |
||||||
|
primitive.set_i32(3203); |
||||||
|
primitive.set_sf32(3204); |
||||||
|
primitive.set_s32(3205); |
||||||
|
primitive.set_fix64(6401L); |
||||||
|
primitive.set_u64(6402L); |
||||||
|
primitive.set_i64(6403L); |
||||||
|
primitive.set_sf64(6404L); |
||||||
|
primitive.set_s64(6405L); |
||||||
|
primitive.set_str("String Value"); |
||||||
|
primitive.set_bytes("Some Bytes"); |
||||||
|
primitive.set_float_(32.1f); |
||||||
|
primitive.set_double_(64.1L); |
||||||
|
primitive.set_bool_(true); |
||||||
|
|
||||||
|
ow_.StartObject("") |
||||||
|
->RenderUint32("fix32", bit_cast<uint32>(3201)) |
||||||
|
->RenderUint32("u32", bit_cast<uint32>(3202)) |
||||||
|
->RenderInt32("i32", 3203) |
||||||
|
->RenderInt32("sf32", 3204) |
||||||
|
->RenderInt32("s32", 3205) |
||||||
|
->RenderUint64("fix64", bit_cast<uint64>(6401L)) |
||||||
|
->RenderUint64("u64", bit_cast<uint64>(6402L)) |
||||||
|
->RenderInt64("i64", 6403L) |
||||||
|
->RenderInt64("sf64", 6404L) |
||||||
|
->RenderInt64("s64", 6405L) |
||||||
|
->RenderString("str", "String Value") |
||||||
|
->RenderBytes("bytes", "Some Bytes") |
||||||
|
->RenderFloat("float", 32.1f) |
||||||
|
->RenderDouble("double", 64.1L) |
||||||
|
->RenderBool("bool", true) |
||||||
|
->EndObject(); |
||||||
|
DoTest(primitive, Primitive::descriptor()); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_P(ProtostreamObjectSourceTest, RepeatingPrimitives) { |
||||||
|
Primitive primitive = PrepareRepeatedPrimitive(); |
||||||
|
primitive.add_rep_str("String One"); |
||||||
|
primitive.add_rep_str("String Two"); |
||||||
|
primitive.add_rep_bytes("Some Bytes"); |
||||||
|
|
||||||
|
ow_.StartList("rep_str") |
||||||
|
->RenderString("", "String One") |
||||||
|
->RenderString("", "String Two") |
||||||
|
->EndList() |
||||||
|
->StartList("rep_bytes") |
||||||
|
->RenderBytes("", "Some Bytes") |
||||||
|
->EndList(); |
||||||
|
DoTest(primitive, Primitive::descriptor()); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_P(ProtostreamObjectSourceTest, NestedMessage) { |
||||||
|
Author* author = new Author(); |
||||||
|
author->set_id(101L); |
||||||
|
author->set_name("Tolstoy"); |
||||||
|
Book book; |
||||||
|
book.set_title("My Book"); |
||||||
|
book.set_allocated_author(author); |
||||||
|
|
||||||
|
ow_.StartObject("") |
||||||
|
->RenderString("title", "My Book") |
||||||
|
->StartObject("author") |
||||||
|
->RenderUint64("id", bit_cast<uint64>(101L)) |
||||||
|
->RenderString("name", "Tolstoy") |
||||||
|
->EndObject() |
||||||
|
->EndObject(); |
||||||
|
DoTest(book, Book::descriptor()); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_P(ProtostreamObjectSourceTest, RepeatingField) { |
||||||
|
Author author; |
||||||
|
author.set_alive(false); |
||||||
|
author.set_name("john"); |
||||||
|
author.add_pseudonym("phil"); |
||||||
|
author.add_pseudonym("bob"); |
||||||
|
|
||||||
|
ow_.StartObject("") |
||||||
|
->RenderBool("alive", false) |
||||||
|
->RenderString("name", "john") |
||||||
|
->StartList("pseudonym") |
||||||
|
->RenderString("", "phil") |
||||||
|
->RenderString("", "bob") |
||||||
|
->EndList() |
||||||
|
->EndObject(); |
||||||
|
DoTest(author, Author::descriptor()); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_P(ProtostreamObjectSourceTest, PackedRepeatingFields) { |
||||||
|
DoTest(PreparePackedPrimitive(), PackedPrimitive::descriptor()); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_P(ProtostreamObjectSourceTest, NonPackedPackableFieldsActuallyPacked) { |
||||||
|
// Protostream is packed, but parse with non-packed Primitive.
|
||||||
|
DoTest(PreparePackedPrimitive(), Primitive::descriptor()); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_P(ProtostreamObjectSourceTest, PackedPackableFieldNotActuallyPacked) { |
||||||
|
// Protostream is not packed, but parse with PackedPrimitive.
|
||||||
|
DoTest(PrepareRepeatedPrimitive(), PackedPrimitive::descriptor()); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_P(ProtostreamObjectSourceTest, BadAuthor) { |
||||||
|
Author author; |
||||||
|
author.set_alive(false); |
||||||
|
author.set_name("john"); |
||||||
|
author.set_id(1234L); |
||||||
|
author.add_pseudonym("phil"); |
||||||
|
author.add_pseudonym("bob"); |
||||||
|
|
||||||
|
ow_.StartObject("") |
||||||
|
->StartList("alive") |
||||||
|
->RenderBool("", false) |
||||||
|
->EndList() |
||||||
|
->StartList("name") |
||||||
|
->RenderUint64("", static_cast<uint64>('j')) |
||||||
|
->RenderUint64("", static_cast<uint64>('o')) |
||||||
|
->RenderUint64("", static_cast<uint64>('h')) |
||||||
|
->RenderUint64("", static_cast<uint64>('n')) |
||||||
|
->EndList() |
||||||
|
->RenderString("pseudonym", "phil") |
||||||
|
->RenderString("pseudonym", "bob") |
||||||
|
->EndObject(); |
||||||
|
// Protostream created with Author, but parsed with BadAuthor.
|
||||||
|
DoTest(author, BadAuthor::descriptor()); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_P(ProtostreamObjectSourceTest, NestedBookToBadNestedBook) { |
||||||
|
Book* book = new Book(); |
||||||
|
book->set_length(250); |
||||||
|
book->set_published(2014L); |
||||||
|
NestedBook nested; |
||||||
|
nested.set_allocated_book(book); |
||||||
|
|
||||||
|
ow_.StartObject("") |
||||||
|
->StartList("book") |
||||||
|
->RenderUint32("", 24) // tag for field length (3 << 3)
|
||||||
|
->RenderUint32("", 250) |
||||||
|
->RenderUint32("", 32) // tag for field published (4 << 3)
|
||||||
|
->RenderUint32("", 2014) |
||||||
|
->EndList() |
||||||
|
->EndObject(); |
||||||
|
// Protostream created with NestedBook, but parsed with BadNestedBook.
|
||||||
|
DoTest(nested, BadNestedBook::descriptor()); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_P(ProtostreamObjectSourceTest, BadNestedBookToNestedBook) { |
||||||
|
BadNestedBook nested; |
||||||
|
nested.add_book(1); |
||||||
|
nested.add_book(2); |
||||||
|
nested.add_book(3); |
||||||
|
nested.add_book(4); |
||||||
|
nested.add_book(5); |
||||||
|
nested.add_book(6); |
||||||
|
nested.add_book(7); |
||||||
|
|
||||||
|
ow_.StartObject("")->StartObject("book")->EndObject()->EndObject(); |
||||||
|
// Protostream created with BadNestedBook, but parsed with NestedBook.
|
||||||
|
DoTest(nested, NestedBook::descriptor()); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_P(ProtostreamObjectSourceTest, |
||||||
|
LongRepeatedListDoesNotBreakIntoMultipleJsonLists) { |
||||||
|
Book book; |
||||||
|
|
||||||
|
int repeat = 10000; |
||||||
|
for (int i = 0; i < repeat; ++i) { |
||||||
|
Book_Label* label = book.add_labels(); |
||||||
|
label->set_key(StrCat("i", i)); |
||||||
|
label->set_value(StrCat("v", i)); |
||||||
|
} |
||||||
|
|
||||||
|
// Make sure StartList and EndList are called exactly once (see b/18227499 for
|
||||||
|
// problems when this doesn't happen)
|
||||||
|
EXPECT_CALL(mock_, StartList(_)).Times(1); |
||||||
|
EXPECT_CALL(mock_, EndList()).Times(1); |
||||||
|
|
||||||
|
DoTest(book, Book::descriptor()); |
||||||
|
} |
||||||
|
|
||||||
|
class ProtostreamObjectSourceMapsTest : public ProtostreamObjectSourceTest { |
||||||
|
protected: |
||||||
|
ProtostreamObjectSourceMapsTest() { |
||||||
|
helper_.ResetTypeInfo(MapOut::descriptor()); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest, |
||||||
|
ProtostreamObjectSourceMapsTest, |
||||||
|
::testing::Values( |
||||||
|
testing::USE_TYPE_RESOLVER)); |
||||||
|
|
||||||
|
// Tests JSON map.
|
||||||
|
//
|
||||||
|
// This is the example expected output.
|
||||||
|
// {
|
||||||
|
// "map1": {
|
||||||
|
// "key1": {
|
||||||
|
// "foo": "foovalue"
|
||||||
|
// },
|
||||||
|
// "key2": {
|
||||||
|
// "foo": "barvalue"
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// "map2": {
|
||||||
|
// "nestedself": {
|
||||||
|
// "map1": {
|
||||||
|
// "nested_key1": {
|
||||||
|
// "foo": "nested_foo"
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// "bar": "nested_bar_string"
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// "map3": {
|
||||||
|
// "111": "one one one"
|
||||||
|
// },
|
||||||
|
// "bar": "top bar"
|
||||||
|
// }
|
||||||
|
TEST_P(ProtostreamObjectSourceMapsTest, MapsTest) { |
||||||
|
MapOut out; |
||||||
|
(*out.mutable_map1())["key1"].set_foo("foovalue"); |
||||||
|
(*out.mutable_map1())["key2"].set_foo("barvalue"); |
||||||
|
|
||||||
|
MapOut* nested_value = &(*out.mutable_map2())["nestedself"]; |
||||||
|
(*nested_value->mutable_map1())["nested_key1"].set_foo("nested_foo"); |
||||||
|
nested_value->set_bar("nested_bar_string"); |
||||||
|
|
||||||
|
(*out.mutable_map3())[111] = "one one one"; |
||||||
|
|
||||||
|
out.set_bar("top bar"); |
||||||
|
|
||||||
|
ow_.StartObject("") |
||||||
|
->StartObject("map1") |
||||||
|
->StartObject("key1") |
||||||
|
->RenderString("foo", "foovalue") |
||||||
|
->EndObject() |
||||||
|
->StartObject("key2") |
||||||
|
->RenderString("foo", "barvalue") |
||||||
|
->EndObject() |
||||||
|
->StartObject("map2") |
||||||
|
->StartObject("nestedself") |
||||||
|
->StartObject("map1") |
||||||
|
->StartObject("nested_key1") |
||||||
|
->RenderString("foo", "nested_foo") |
||||||
|
->EndObject() |
||||||
|
->EndObject() |
||||||
|
->RenderString("bar", "nested_bar_string") |
||||||
|
->EndObject() |
||||||
|
->EndObject() |
||||||
|
->StartObject("map3") |
||||||
|
->RenderString("111", "one one one") |
||||||
|
->EndObject() |
||||||
|
->EndObject() |
||||||
|
->RenderString("bar", "top bar") |
||||||
|
->EndObject(); |
||||||
|
|
||||||
|
DoTest(out, MapOut::descriptor()); |
||||||
|
} |
||||||
|
|
||||||
|
class ProtostreamObjectSourceAnysTest : public ProtostreamObjectSourceTest { |
||||||
|
protected: |
||||||
|
ProtostreamObjectSourceAnysTest() { |
||||||
|
helper_.ResetTypeInfo(AnyOut::descriptor(), |
||||||
|
google::protobuf::Any::descriptor()); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest, |
||||||
|
ProtostreamObjectSourceAnysTest, |
||||||
|
::testing::Values( |
||||||
|
testing::USE_TYPE_RESOLVER)); |
||||||
|
|
||||||
|
// Tests JSON any support.
|
||||||
|
//
|
||||||
|
// This is the example expected output.
|
||||||
|
// {
|
||||||
|
// "any": {
|
||||||
|
// "@type": "type.googleapis.com/google.protobuf.testing.anys.AnyM"
|
||||||
|
// "foo": "foovalue"
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
TEST_P(ProtostreamObjectSourceAnysTest, BasicAny) { |
||||||
|
AnyOut out; |
||||||
|
::google::protobuf::Any* any = out.mutable_any(); |
||||||
|
|
||||||
|
AnyM m; |
||||||
|
m.set_foo("foovalue"); |
||||||
|
any->PackFrom(m); |
||||||
|
|
||||||
|
ow_.StartObject("") |
||||||
|
->StartObject("any") |
||||||
|
->RenderString("@type", |
||||||
|
"type.googleapis.com/google.protobuf.testing.anys.AnyM") |
||||||
|
->RenderString("foo", "foovalue") |
||||||
|
->EndObject() |
||||||
|
->EndObject(); |
||||||
|
|
||||||
|
DoTest(out, AnyOut::descriptor()); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_P(ProtostreamObjectSourceAnysTest, RecursiveAny) { |
||||||
|
AnyOut out; |
||||||
|
::google::protobuf::Any* any = out.mutable_any(); |
||||||
|
any->set_type_url("type.googleapis.com/google.protobuf.Any"); |
||||||
|
|
||||||
|
::google::protobuf::Any nested_any; |
||||||
|
nested_any.set_type_url( |
||||||
|
"type.googleapis.com/google.protobuf.testing.anys.AnyM"); |
||||||
|
|
||||||
|
AnyM m; |
||||||
|
m.set_foo("foovalue"); |
||||||
|
nested_any.set_value(m.SerializeAsString()); |
||||||
|
|
||||||
|
any->set_value(nested_any.SerializeAsString()); |
||||||
|
|
||||||
|
ow_.StartObject("") |
||||||
|
->StartObject("any") |
||||||
|
->RenderString("@type", "type.googleapis.com/google.protobuf.Any") |
||||||
|
->StartObject("value") |
||||||
|
->RenderString("@type", |
||||||
|
"type.googleapis.com/google.protobuf.testing.anys.AnyM") |
||||||
|
->RenderString("foo", "foovalue") |
||||||
|
->EndObject() |
||||||
|
->EndObject() |
||||||
|
->EndObject(); |
||||||
|
|
||||||
|
DoTest(out, AnyOut::descriptor()); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_P(ProtostreamObjectSourceAnysTest, DoubleRecursiveAny) { |
||||||
|
AnyOut out; |
||||||
|
::google::protobuf::Any* any = out.mutable_any(); |
||||||
|
any->set_type_url("type.googleapis.com/google.protobuf.Any"); |
||||||
|
|
||||||
|
::google::protobuf::Any nested_any; |
||||||
|
nested_any.set_type_url("type.googleapis.com/google.protobuf.Any"); |
||||||
|
|
||||||
|
::google::protobuf::Any second_nested_any; |
||||||
|
second_nested_any.set_type_url( |
||||||
|
"type.googleapis.com/google.protobuf.testing.anys.AnyM"); |
||||||
|
|
||||||
|
AnyM m; |
||||||
|
m.set_foo("foovalue"); |
||||||
|
second_nested_any.set_value(m.SerializeAsString()); |
||||||
|
nested_any.set_value(second_nested_any.SerializeAsString()); |
||||||
|
any->set_value(nested_any.SerializeAsString()); |
||||||
|
|
||||||
|
ow_.StartObject("") |
||||||
|
->StartObject("any") |
||||||
|
->RenderString("@type", "type.googleapis.com/google.protobuf.Any") |
||||||
|
->StartObject("value") |
||||||
|
->RenderString("@type", "type.googleapis.com/google.protobuf.Any") |
||||||
|
->StartObject("value") |
||||||
|
->RenderString("@type", |
||||||
|
"type.googleapis.com/google.protobuf.testing.anys.AnyM") |
||||||
|
->RenderString("foo", "foovalue") |
||||||
|
->EndObject() |
||||||
|
->EndObject() |
||||||
|
->EndObject() |
||||||
|
->EndObject(); |
||||||
|
|
||||||
|
DoTest(out, AnyOut::descriptor()); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_P(ProtostreamObjectSourceAnysTest, EmptyAnyOutputsEmptyObject) { |
||||||
|
AnyOut out; |
||||||
|
out.mutable_any(); |
||||||
|
|
||||||
|
ow_.StartObject("")->StartObject("any")->EndObject()->EndObject(); |
||||||
|
|
||||||
|
DoTest(out, AnyOut::descriptor()); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_P(ProtostreamObjectSourceAnysTest, EmptyWithTypeAndNoValueOutputsType) { |
||||||
|
AnyOut out; |
||||||
|
out.mutable_any()->set_type_url("foo.googleapis.com/my.Type"); |
||||||
|
|
||||||
|
ow_.StartObject("") |
||||||
|
->StartObject("any") |
||||||
|
->RenderString("@type", "foo.googleapis.com/my.Type") |
||||||
|
->EndObject() |
||||||
|
->EndObject(); |
||||||
|
|
||||||
|
DoTest(out, AnyOut::descriptor()); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_P(ProtostreamObjectSourceAnysTest, MissingTypeUrlError) { |
||||||
|
AnyOut out; |
||||||
|
::google::protobuf::Any* any = out.mutable_any(); |
||||||
|
|
||||||
|
AnyM m; |
||||||
|
m.set_foo("foovalue"); |
||||||
|
any->set_value(m.SerializeAsString()); |
||||||
|
|
||||||
|
// We start the "AnyOut" part and then fail when we hit the Any object.
|
||||||
|
ow_.StartObject(""); |
||||||
|
|
||||||
|
Status status = ExecuteTest(out, AnyOut::descriptor()); |
||||||
|
EXPECT_EQ(util::error::INTERNAL, status.error_code()); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_P(ProtostreamObjectSourceAnysTest, UnknownTypeServiceError) { |
||||||
|
AnyOut out; |
||||||
|
::google::protobuf::Any* any = out.mutable_any(); |
||||||
|
any->set_type_url("foo.googleapis.com/my.own.Type"); |
||||||
|
|
||||||
|
AnyM m; |
||||||
|
m.set_foo("foovalue"); |
||||||
|
any->set_value(m.SerializeAsString()); |
||||||
|
|
||||||
|
// We start the "AnyOut" part and then fail when we hit the Any object.
|
||||||
|
ow_.StartObject(""); |
||||||
|
|
||||||
|
Status status = ExecuteTest(out, AnyOut::descriptor()); |
||||||
|
EXPECT_EQ(util::error::INTERNAL, status.error_code()); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_P(ProtostreamObjectSourceAnysTest, UnknownTypeError) { |
||||||
|
AnyOut out; |
||||||
|
::google::protobuf::Any* any = out.mutable_any(); |
||||||
|
any->set_type_url("type.googleapis.com/unknown.Type"); |
||||||
|
|
||||||
|
AnyM m; |
||||||
|
m.set_foo("foovalue"); |
||||||
|
any->set_value(m.SerializeAsString()); |
||||||
|
|
||||||
|
// We start the "AnyOut" part and then fail when we hit the Any object.
|
||||||
|
ow_.StartObject(""); |
||||||
|
|
||||||
|
Status status = ExecuteTest(out, AnyOut::descriptor()); |
||||||
|
EXPECT_EQ(util::error::INTERNAL, status.error_code()); |
||||||
|
} |
||||||
|
|
||||||
|
class ProtostreamObjectSourceStructTest : public ProtostreamObjectSourceTest { |
||||||
|
protected: |
||||||
|
ProtostreamObjectSourceStructTest() { |
||||||
|
helper_.ResetTypeInfo(StructType::descriptor(), |
||||||
|
google::protobuf::Struct::descriptor()); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest, |
||||||
|
ProtostreamObjectSourceStructTest, |
||||||
|
::testing::Values( |
||||||
|
testing::USE_TYPE_RESOLVER)); |
||||||
|
|
||||||
|
// Tests struct
|
||||||
|
//
|
||||||
|
// "object": {
|
||||||
|
// "k1": 123,
|
||||||
|
// "k2": true
|
||||||
|
// }
|
||||||
|
TEST_P(ProtostreamObjectSourceStructTest, StructRenderSuccess) { |
||||||
|
StructType out; |
||||||
|
google::protobuf::Struct* s = out.mutable_object(); |
||||||
|
s->mutable_fields()->operator[]("k1").set_number_value(123); |
||||||
|
s->mutable_fields()->operator[]("k2").set_bool_value(true); |
||||||
|
|
||||||
|
ow_.StartObject("") |
||||||
|
->StartObject("object") |
||||||
|
->RenderDouble("k1", 123) |
||||||
|
->RenderBool("k2", true) |
||||||
|
->EndObject() |
||||||
|
->EndObject(); |
||||||
|
|
||||||
|
DoTest(out, StructType::descriptor()); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_P(ProtostreamObjectSourceStructTest, MissingValueSkipsField) { |
||||||
|
StructType out; |
||||||
|
google::protobuf::Struct* s = out.mutable_object(); |
||||||
|
s->mutable_fields()->operator[]("k1"); |
||||||
|
|
||||||
|
ow_.StartObject("")->StartObject("object")->EndObject()->EndObject(); |
||||||
|
|
||||||
|
DoTest(out, StructType::descriptor()); |
||||||
|
} |
||||||
|
|
||||||
|
class ProtostreamObjectSourceFieldMaskTest |
||||||
|
: public ProtostreamObjectSourceTest { |
||||||
|
protected: |
||||||
|
ProtostreamObjectSourceFieldMaskTest() { |
||||||
|
helper_.ResetTypeInfo(FieldMaskTest::descriptor(), |
||||||
|
google::protobuf::FieldMask::descriptor()); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest, |
||||||
|
ProtostreamObjectSourceFieldMaskTest, |
||||||
|
::testing::Values( |
||||||
|
testing::USE_TYPE_RESOLVER)); |
||||||
|
|
||||||
|
TEST_P(ProtostreamObjectSourceFieldMaskTest, FieldMaskRenderSuccess) { |
||||||
|
FieldMaskTest out; |
||||||
|
out.set_id("1"); |
||||||
|
out.mutable_single_mask()->add_paths("path1"); |
||||||
|
out.mutable_single_mask()->add_paths("snake_case_path2"); |
||||||
|
::google::protobuf::FieldMask* mask = out.add_repeated_mask(); |
||||||
|
mask->add_paths("path3"); |
||||||
|
mask = out.add_repeated_mask(); |
||||||
|
mask->add_paths("snake_case_path4"); |
||||||
|
mask->add_paths("path5"); |
||||||
|
NestedFieldMask* nested = out.add_nested_mask(); |
||||||
|
nested->set_data("data"); |
||||||
|
nested->mutable_single_mask()->add_paths("nested.path1"); |
||||||
|
nested->mutable_single_mask()->add_paths("nested_field.snake_case_path2"); |
||||||
|
mask = nested->add_repeated_mask(); |
||||||
|
mask->add_paths("nested_field.path3"); |
||||||
|
mask->add_paths("nested.snake_case_path4"); |
||||||
|
mask = nested->add_repeated_mask(); |
||||||
|
mask->add_paths("nested.path5"); |
||||||
|
mask = nested->add_repeated_mask(); |
||||||
|
mask->add_paths( |
||||||
|
"snake_case.map_field[\"map_key_should_be_ignored\"].nested_snake_case." |
||||||
|
"map_field[\"map_key_sho\\\"uld_be_ignored\"]"); |
||||||
|
|
||||||
|
ow_.StartObject("") |
||||||
|
->RenderString("id", "1") |
||||||
|
->RenderString("single_mask", "path1,snakeCasePath2") |
||||||
|
->StartList("repeated_mask") |
||||||
|
->RenderString("", "path3") |
||||||
|
->RenderString("", "snakeCasePath4,path5") |
||||||
|
->EndList() |
||||||
|
->StartList("nested_mask") |
||||||
|
->StartObject("") |
||||||
|
->RenderString("data", "data") |
||||||
|
->RenderString("single_mask", "nested.path1,nestedField.snakeCasePath2") |
||||||
|
->StartList("repeated_mask") |
||||||
|
->RenderString("", "nestedField.path3,nested.snakeCasePath4") |
||||||
|
->RenderString("", "nested.path5") |
||||||
|
->RenderString("", |
||||||
|
"snakeCase.mapField[\"map_key_should_be_ignored\"]." |
||||||
|
"nestedSnakeCase.mapField[\"map_key_sho\\\"uld_be_" |
||||||
|
"ignored\"]") |
||||||
|
->EndList() |
||||||
|
->EndObject() |
||||||
|
->EndList() |
||||||
|
->EndObject(); |
||||||
|
|
||||||
|
DoTest(out, FieldMaskTest::descriptor()); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
} // namespace google
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,455 @@ |
|||||||
|
// 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_UTIL_CONVERTER_PROTOSTREAM_OBJECTWRITER_H__ |
||||||
|
#define GOOGLE_PROTOBUF_UTIL_CONVERTER_PROTOSTREAM_OBJECTWRITER_H__ |
||||||
|
|
||||||
|
#include <deque> |
||||||
|
#include <google/protobuf/stubs/hash.h> |
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include <google/protobuf/stubs/common.h> |
||||||
|
#include <google/protobuf/io/coded_stream.h> |
||||||
|
#include <google/protobuf/io/zero_copy_stream_impl.h> |
||||||
|
#include <google/protobuf/descriptor.h> |
||||||
|
#include <google/protobuf/util/internal/type_info.h> |
||||||
|
#include <google/protobuf/util/internal/datapiece.h> |
||||||
|
#include <google/protobuf/util/internal/error_listener.h> |
||||||
|
#include <google/protobuf/util/internal/structured_objectwriter.h> |
||||||
|
#include <google/protobuf/util/type_resolver.h> |
||||||
|
#include <google/protobuf/stubs/bytestream.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace io { |
||||||
|
class CodedOutputStream; |
||||||
|
} // namespace io
|
||||||
|
} // namespace protobuf
|
||||||
|
|
||||||
|
|
||||||
|
namespace protobuf { |
||||||
|
class Type; |
||||||
|
class Field; |
||||||
|
} // namespace protobuf
|
||||||
|
|
||||||
|
|
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace converter { |
||||||
|
|
||||||
|
class ObjectLocationTracker; |
||||||
|
|
||||||
|
// An ObjectWriter that can write protobuf bytes directly from writer events.
|
||||||
|
//
|
||||||
|
// It also supports streaming.
|
||||||
|
class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter { |
||||||
|
public: |
||||||
|
// Constructor. Does not take ownership of any parameter passed in.
|
||||||
|
ProtoStreamObjectWriter(TypeResolver* type_resolver, |
||||||
|
const google::protobuf::Type& type, |
||||||
|
strings::ByteSink* output, ErrorListener* listener); |
||||||
|
virtual ~ProtoStreamObjectWriter(); |
||||||
|
|
||||||
|
// ObjectWriter methods.
|
||||||
|
virtual ProtoStreamObjectWriter* StartObject(StringPiece name); |
||||||
|
virtual ProtoStreamObjectWriter* EndObject(); |
||||||
|
virtual ProtoStreamObjectWriter* StartList(StringPiece name); |
||||||
|
virtual ProtoStreamObjectWriter* EndList(); |
||||||
|
virtual ProtoStreamObjectWriter* RenderBool(StringPiece name, |
||||||
|
const bool value) { |
||||||
|
return RenderDataPiece(name, DataPiece(value)); |
||||||
|
} |
||||||
|
virtual ProtoStreamObjectWriter* RenderInt32(StringPiece name, |
||||||
|
const int32 value) { |
||||||
|
return RenderDataPiece(name, DataPiece(value)); |
||||||
|
} |
||||||
|
virtual ProtoStreamObjectWriter* RenderUint32(StringPiece name, |
||||||
|
const uint32 value) { |
||||||
|
return RenderDataPiece(name, DataPiece(value)); |
||||||
|
} |
||||||
|
virtual ProtoStreamObjectWriter* RenderInt64(StringPiece name, |
||||||
|
const int64 value) { |
||||||
|
return RenderDataPiece(name, DataPiece(value)); |
||||||
|
} |
||||||
|
virtual ProtoStreamObjectWriter* RenderUint64(StringPiece name, |
||||||
|
const uint64 value) { |
||||||
|
return RenderDataPiece(name, DataPiece(value)); |
||||||
|
} |
||||||
|
virtual ProtoStreamObjectWriter* RenderDouble(StringPiece name, |
||||||
|
const double value) { |
||||||
|
return RenderDataPiece(name, DataPiece(value)); |
||||||
|
} |
||||||
|
virtual ProtoStreamObjectWriter* RenderFloat(StringPiece name, |
||||||
|
const float value) { |
||||||
|
return RenderDataPiece(name, DataPiece(value)); |
||||||
|
} |
||||||
|
virtual ProtoStreamObjectWriter* RenderString(StringPiece name, |
||||||
|
StringPiece value) { |
||||||
|
return RenderDataPiece(name, DataPiece(value)); |
||||||
|
} |
||||||
|
virtual ProtoStreamObjectWriter* RenderBytes(StringPiece name, |
||||||
|
StringPiece value) { |
||||||
|
return RenderDataPiece(name, DataPiece(value, false)); |
||||||
|
} |
||||||
|
virtual ProtoStreamObjectWriter* RenderNull(StringPiece name) { |
||||||
|
return RenderDataPiece(name, DataPiece::NullData()); |
||||||
|
} |
||||||
|
|
||||||
|
// Renders a DataPiece 'value' into a field whose wire type is determined
|
||||||
|
// from the given field 'name'.
|
||||||
|
ProtoStreamObjectWriter* RenderDataPiece(StringPiece name, |
||||||
|
const DataPiece& value); |
||||||
|
|
||||||
|
// Returns the location tracker to use for tracking locations for errors.
|
||||||
|
const LocationTrackerInterface& location() { |
||||||
|
return element_ != NULL ? *element_ : *tracker_; |
||||||
|
} |
||||||
|
|
||||||
|
// When true, we finished writing to output a complete message.
|
||||||
|
bool done() const { return done_; } |
||||||
|
|
||||||
|
private: |
||||||
|
// Function that renders a well known type with modified behavior.
|
||||||
|
typedef util::Status (*TypeRenderer)(ProtoStreamObjectWriter*, |
||||||
|
const DataPiece&); |
||||||
|
|
||||||
|
// Handles writing Anys out using nested object writers and the like.
|
||||||
|
class LIBPROTOBUF_EXPORT AnyWriter { |
||||||
|
public: |
||||||
|
explicit AnyWriter(ProtoStreamObjectWriter* parent); |
||||||
|
~AnyWriter(); |
||||||
|
|
||||||
|
// Passes a StartObject call through to the Any writer.
|
||||||
|
void StartObject(StringPiece name); |
||||||
|
|
||||||
|
// Passes an EndObject call through to the Any. Returns true if the any
|
||||||
|
// handled the EndObject call, false if the Any is now all done and is no
|
||||||
|
// longer needed.
|
||||||
|
bool EndObject(); |
||||||
|
|
||||||
|
// Passes a StartList call through to the Any writer.
|
||||||
|
void StartList(StringPiece name); |
||||||
|
|
||||||
|
// Passes an EndList call through to the Any writer.
|
||||||
|
void EndList(); |
||||||
|
|
||||||
|
// Renders a data piece on the any.
|
||||||
|
void RenderDataPiece(StringPiece name, const DataPiece& value); |
||||||
|
|
||||||
|
private: |
||||||
|
// Handles starting up the any once we have a type.
|
||||||
|
void StartAny(const DataPiece& value); |
||||||
|
|
||||||
|
// Writes the Any out to the parent writer in its serialized form.
|
||||||
|
void WriteAny(); |
||||||
|
|
||||||
|
// The parent of this writer, needed for various bits such as type info and
|
||||||
|
// the listeners.
|
||||||
|
ProtoStreamObjectWriter* parent_; |
||||||
|
|
||||||
|
// The nested object writer, used to write events.
|
||||||
|
google::protobuf::scoped_ptr<ProtoStreamObjectWriter> ow_; |
||||||
|
|
||||||
|
// The type_url_ that this Any represents.
|
||||||
|
string type_url_; |
||||||
|
|
||||||
|
// Whether this any is invalid. This allows us to only report an invalid
|
||||||
|
// Any message a single time rather than every time we get a nested field.
|
||||||
|
bool invalid_; |
||||||
|
|
||||||
|
// The output data and wrapping ByteSink.
|
||||||
|
string data_; |
||||||
|
strings::StringByteSink output_; |
||||||
|
|
||||||
|
// The depth within the Any, so we can track when we're done.
|
||||||
|
int depth_; |
||||||
|
|
||||||
|
// True if the message type contained in Any has a special "value" message
|
||||||
|
// injected. This is true for well-known message types like Any or Struct.
|
||||||
|
bool has_injected_value_message_; |
||||||
|
}; |
||||||
|
|
||||||
|
class LIBPROTOBUF_EXPORT ProtoElement : public BaseElement, public LocationTrackerInterface { |
||||||
|
public: |
||||||
|
// Indicates the type of element. Special types like LIST, MAP, MAP_ENTRY,
|
||||||
|
// STRUCT etc. are used to deduce other information based on their position
|
||||||
|
// on the stack of elements.
|
||||||
|
enum ElementType { |
||||||
|
MESSAGE, // Simple message
|
||||||
|
LIST, // List/repeated element
|
||||||
|
MAP, // Proto3 map type
|
||||||
|
MAP_ENTRY, // Proto3 map message type, with 'key' and 'value' fields
|
||||||
|
ANY, // Proto3 Any type
|
||||||
|
STRUCT, // Proto3 struct type
|
||||||
|
STRUCT_VALUE, // Struct's Value message type
|
||||||
|
STRUCT_LIST, // List type indicator within a struct
|
||||||
|
STRUCT_LIST_VALUE, // Struct Value's ListValue message type
|
||||||
|
STRUCT_MAP, // Struct within a struct type
|
||||||
|
STRUCT_MAP_ENTRY // Struct map's entry type with 'key' and 'value'
|
||||||
|
// fields
|
||||||
|
}; |
||||||
|
|
||||||
|
// Constructor for the root element. No parent nor field.
|
||||||
|
ProtoElement(TypeInfo* typeinfo, const google::protobuf::Type& type, |
||||||
|
ProtoStreamObjectWriter* enclosing); |
||||||
|
|
||||||
|
// Constructor for a field of an element.
|
||||||
|
ProtoElement(ProtoElement* parent, const google::protobuf::Field* field, |
||||||
|
const google::protobuf::Type& type, ElementType element_type); |
||||||
|
|
||||||
|
virtual ~ProtoElement() {} |
||||||
|
|
||||||
|
// Called just before the destructor for clean up:
|
||||||
|
// - reports any missing required fields
|
||||||
|
// - computes the space needed by the size field, and augment the
|
||||||
|
// length of all parent messages by this additional space.
|
||||||
|
// - releases and returns the parent pointer.
|
||||||
|
ProtoElement* pop(); |
||||||
|
|
||||||
|
// Accessors
|
||||||
|
const google::protobuf::Field* field() const { return field_; } |
||||||
|
const google::protobuf::Type& type() const { return type_; } |
||||||
|
|
||||||
|
// These functions return true if the element type is corresponding to the
|
||||||
|
// type in function name.
|
||||||
|
bool IsMap() { return element_type_ == MAP; } |
||||||
|
bool IsStructMap() { return element_type_ == STRUCT_MAP; } |
||||||
|
bool IsStructMapEntry() { return element_type_ == STRUCT_MAP_ENTRY; } |
||||||
|
bool IsStructList() { return element_type_ == STRUCT_LIST; } |
||||||
|
bool IsAny() { return element_type_ == ANY; } |
||||||
|
|
||||||
|
ElementType element_type() { return element_type_; } |
||||||
|
|
||||||
|
void RegisterField(const google::protobuf::Field* field); |
||||||
|
virtual string ToString() const; |
||||||
|
|
||||||
|
AnyWriter* any() const { return any_.get(); } |
||||||
|
|
||||||
|
virtual ProtoElement* parent() const { |
||||||
|
return static_cast<ProtoElement*>(BaseElement::parent()); |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
// Used for access to variables of the enclosing instance of
|
||||||
|
// ProtoStreamObjectWriter.
|
||||||
|
ProtoStreamObjectWriter* ow_; |
||||||
|
|
||||||
|
// A writer for Any objects, handles all Any-related nonsense.
|
||||||
|
google::protobuf::scoped_ptr<AnyWriter> any_; |
||||||
|
|
||||||
|
// Describes the element as a field in the parent message.
|
||||||
|
// field_ is NULL if and only if this element is the root element.
|
||||||
|
const google::protobuf::Field* field_; |
||||||
|
|
||||||
|
// TypeInfo to lookup types.
|
||||||
|
TypeInfo* typeinfo_; |
||||||
|
|
||||||
|
// Additional variables if this element is a message:
|
||||||
|
// (Root element is always a message).
|
||||||
|
// descriptor_ : describes allowed fields in the message.
|
||||||
|
// required_fields_: set of required fields.
|
||||||
|
// is_repeated_type_ : true if the element is of type list or map.
|
||||||
|
// size_index_ : index into ProtoStreamObjectWriter::size_insert_
|
||||||
|
// for later insertion of serialized message length.
|
||||||
|
const google::protobuf::Type& type_; |
||||||
|
std::set<const google::protobuf::Field*> required_fields_; |
||||||
|
const bool is_repeated_type_; |
||||||
|
const int size_index_; |
||||||
|
|
||||||
|
// Tracks position in repeated fields, needed for LocationTrackerInterface.
|
||||||
|
int array_index_; |
||||||
|
|
||||||
|
// The type of this element, see enum for permissible types.
|
||||||
|
ElementType element_type_; |
||||||
|
|
||||||
|
GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(ProtoElement); |
||||||
|
}; |
||||||
|
|
||||||
|
// Container for inserting 'size' information at the 'pos' position.
|
||||||
|
struct SizeInfo { |
||||||
|
const int pos; |
||||||
|
int size; |
||||||
|
}; |
||||||
|
|
||||||
|
ProtoStreamObjectWriter(TypeInfo* typeinfo, |
||||||
|
const google::protobuf::Type& type, |
||||||
|
strings::ByteSink* output, ErrorListener* listener); |
||||||
|
|
||||||
|
ProtoElement* element() { return element_.get(); } |
||||||
|
|
||||||
|
// Helper methods for calling ErrorListener. See error_listener.h.
|
||||||
|
void InvalidName(StringPiece unknown_name, StringPiece message); |
||||||
|
void InvalidValue(StringPiece type_name, StringPiece value); |
||||||
|
void MissingField(StringPiece missing_name); |
||||||
|
|
||||||
|
// Common code for BeginObject() and BeginList() that does invalid_depth_
|
||||||
|
// bookkeeping associated with name lookup.
|
||||||
|
const google::protobuf::Field* BeginNamed(StringPiece name, bool is_list); |
||||||
|
|
||||||
|
// Lookup the field in the current element. Looks in the base descriptor
|
||||||
|
// and in any extension. This will report an error if the field cannot be
|
||||||
|
// found or if multiple matching extensions are found.
|
||||||
|
const google::protobuf::Field* Lookup(StringPiece name); |
||||||
|
|
||||||
|
// Lookup the field type in the type descriptor. Returns NULL if the type
|
||||||
|
// is not known.
|
||||||
|
const google::protobuf::Type* LookupType( |
||||||
|
const google::protobuf::Field* field); |
||||||
|
|
||||||
|
// Looks up the oneof struct Value field depending on the type.
|
||||||
|
// On failure to find, it returns an appropriate error.
|
||||||
|
util::StatusOr<const google::protobuf::Field*> LookupStructField( |
||||||
|
DataPiece::Type type); |
||||||
|
|
||||||
|
// Starts an entry in map. This will be called after placing map element at
|
||||||
|
// the top of the stack. Uses this information to write map entries.
|
||||||
|
const google::protobuf::Field* StartMapEntry(StringPiece name); |
||||||
|
|
||||||
|
// Starts a google.protobuf.Struct.
|
||||||
|
// 'field' is of type google.protobuf.Struct.
|
||||||
|
// If field is NULL, it indicates that the top-level message is a struct
|
||||||
|
// type.
|
||||||
|
void StartStruct(const google::protobuf::Field* field); |
||||||
|
|
||||||
|
// Starts another struct within a struct.
|
||||||
|
// 'field' is of type google.protobuf.Value (see struct.proto).
|
||||||
|
const google::protobuf::Field* StartStructValueInStruct( |
||||||
|
const google::protobuf::Field* field); |
||||||
|
|
||||||
|
// Starts a list within a struct.
|
||||||
|
// 'field' is of type google.protobuf.ListValue (see struct.proto).
|
||||||
|
const google::protobuf::Field* StartListValueInStruct( |
||||||
|
const google::protobuf::Field* field); |
||||||
|
|
||||||
|
// Starts the repeated "values" field in struct.proto's
|
||||||
|
// google.protobuf.ListValue type. 'field' should be of type
|
||||||
|
// google.protobuf.ListValue.
|
||||||
|
const google::protobuf::Field* StartRepeatedValuesInListValue( |
||||||
|
const google::protobuf::Field* field); |
||||||
|
|
||||||
|
// Pops sentinel elements off the stack.
|
||||||
|
void SkipElements(); |
||||||
|
|
||||||
|
// Write serialized output to the final output ByteSink, inserting all
|
||||||
|
// the size information for nested messages that are missing from the
|
||||||
|
// intermediate Cord buffer.
|
||||||
|
void WriteRootMessage(); |
||||||
|
|
||||||
|
// Returns true if the field is a map.
|
||||||
|
bool IsMap(const google::protobuf::Field& field); |
||||||
|
|
||||||
|
// Returns true if the field is an any.
|
||||||
|
bool IsAny(const google::protobuf::Field& field); |
||||||
|
|
||||||
|
// Helper method to write proto tags based on the given field.
|
||||||
|
void WriteTag(const google::protobuf::Field& field); |
||||||
|
|
||||||
|
// Helper function to render primitive data types in DataPiece.
|
||||||
|
void RenderSimpleDataPiece(const google::protobuf::Field& field, |
||||||
|
const google::protobuf::Type& type, |
||||||
|
const DataPiece& data); |
||||||
|
|
||||||
|
// Renders google.protobuf.Value in struct.proto. It picks the right oneof
|
||||||
|
// type based on value's type.
|
||||||
|
static util::Status RenderStructValue(ProtoStreamObjectWriter* ow, |
||||||
|
const DataPiece& value); |
||||||
|
|
||||||
|
// Renders google.protobuf.Timestamp value.
|
||||||
|
static util::Status RenderTimestamp(ProtoStreamObjectWriter* ow, |
||||||
|
const DataPiece& value); |
||||||
|
|
||||||
|
// Renders google.protobuf.FieldMask value.
|
||||||
|
static util::Status RenderFieldMask(ProtoStreamObjectWriter* ow, |
||||||
|
const DataPiece& value); |
||||||
|
|
||||||
|
// Renders google.protobuf.Duration value.
|
||||||
|
static util::Status RenderDuration(ProtoStreamObjectWriter* ow, |
||||||
|
const DataPiece& value); |
||||||
|
|
||||||
|
// Renders wrapper message types for primitive types in
|
||||||
|
// google/protobuf/wrappers.proto.
|
||||||
|
static util::Status RenderWrapperType(ProtoStreamObjectWriter* ow, |
||||||
|
const DataPiece& value); |
||||||
|
|
||||||
|
// Helper functions to create the map and find functions responsible for
|
||||||
|
// rendering well known types, keyed by type URL.
|
||||||
|
static hash_map<string, TypeRenderer>* CreateRendererMap(); |
||||||
|
static TypeRenderer* FindTypeRenderer(const string& type_url); |
||||||
|
|
||||||
|
// Returns the ProtoElement::ElementType for the given Type.
|
||||||
|
static ProtoElement::ElementType GetElementType( |
||||||
|
const google::protobuf::Type& type); |
||||||
|
|
||||||
|
// Variables for describing the structure of the input tree:
|
||||||
|
// master_type_: descriptor for the whole protobuf message.
|
||||||
|
// typeinfo_ : the TypeInfo object to lookup types.
|
||||||
|
const google::protobuf::Type& master_type_; |
||||||
|
TypeInfo* typeinfo_; |
||||||
|
// Whether we own the typeinfo_ object.
|
||||||
|
bool own_typeinfo_; |
||||||
|
|
||||||
|
// Indicates whether we finished writing root message completely.
|
||||||
|
bool done_; |
||||||
|
|
||||||
|
// Variable for internal state processing:
|
||||||
|
// element_ : the current element.
|
||||||
|
// size_insert_: sizes of nested messages.
|
||||||
|
// pos - position to insert the size field.
|
||||||
|
// size - size value to be inserted.
|
||||||
|
google::protobuf::scoped_ptr<ProtoElement> element_; |
||||||
|
std::deque<SizeInfo> size_insert_; |
||||||
|
|
||||||
|
// Variables for output generation:
|
||||||
|
// output_ : pointer to an external ByteSink for final user-visible output.
|
||||||
|
// buffer_ : buffer holding partial message before being ready for output_.
|
||||||
|
// adapter_ : internal adapter between CodedOutputStream and Cord buffer_.
|
||||||
|
// stream_ : wrapper for writing tags and other encodings in wire format.
|
||||||
|
strings::ByteSink* output_; |
||||||
|
string buffer_; |
||||||
|
google::protobuf::io::StringOutputStream adapter_; |
||||||
|
google::protobuf::scoped_ptr<google::protobuf::io::CodedOutputStream> stream_; |
||||||
|
|
||||||
|
// Variables for error tracking and reporting:
|
||||||
|
// listener_ : a place to report any errors found.
|
||||||
|
// invalid_depth_: number of enclosing invalid nested messages.
|
||||||
|
// tracker_ : the root location tracker interface.
|
||||||
|
ErrorListener* listener_; |
||||||
|
int invalid_depth_; |
||||||
|
google::protobuf::scoped_ptr<LocationTrackerInterface> tracker_; |
||||||
|
|
||||||
|
GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(ProtoStreamObjectWriter); |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
|
||||||
|
} // namespace google
|
||||||
|
#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_PROTOSTREAM_OBJECTWRITER_H__
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,187 @@ |
|||||||
|
// 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_UTIL_CONVERTER_SNAKE2CAMEL_OBJECTWRITER_H__ |
||||||
|
#define GOOGLE_PROTOBUF_UTIL_CONVERTER_SNAKE2CAMEL_OBJECTWRITER_H__ |
||||||
|
|
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include <google/protobuf/stubs/common.h> |
||||||
|
#include <google/protobuf/stubs/strutil.h> |
||||||
|
#include <google/protobuf/stubs/stringpiece.h> |
||||||
|
#include <google/protobuf/util/internal/object_writer.h> |
||||||
|
#include <google/protobuf/util/internal/utility.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace converter { |
||||||
|
|
||||||
|
// Snake2CamelObjectWriter is an ObjectWriter than translates each field name
|
||||||
|
// from snake_case to camelCase. Typical usage is:
|
||||||
|
// ProtoStreamObjectSource psos(...);
|
||||||
|
// JsonObjectWriter jow(...);
|
||||||
|
// Snake2CamelObjectWriter snake_to_camel(&jow);
|
||||||
|
// psos.writeTo(&snake_to_camel);
|
||||||
|
class Snake2CamelObjectWriter : public ObjectWriter { |
||||||
|
public: |
||||||
|
explicit Snake2CamelObjectWriter(ObjectWriter* ow) |
||||||
|
: ow_(ow), normalize_case_(true) {} |
||||||
|
virtual ~Snake2CamelObjectWriter() {} |
||||||
|
|
||||||
|
// ObjectWriter methods.
|
||||||
|
virtual Snake2CamelObjectWriter* StartObject(StringPiece name) { |
||||||
|
ow_->StartObject(ShouldNormalizeCase(name) |
||||||
|
? StringPiece(StringPiece(ToCamelCase(name))) |
||||||
|
: name); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
virtual Snake2CamelObjectWriter* EndObject() { |
||||||
|
ow_->EndObject(); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
virtual Snake2CamelObjectWriter* StartList(StringPiece name) { |
||||||
|
ow_->StartList(ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) |
||||||
|
: name); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
virtual Snake2CamelObjectWriter* EndList() { |
||||||
|
ow_->EndList(); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
virtual Snake2CamelObjectWriter* RenderBool(StringPiece name, bool value) { |
||||||
|
ow_->RenderBool( |
||||||
|
ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name, |
||||||
|
value); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
virtual Snake2CamelObjectWriter* RenderInt32(StringPiece name, int32 value) { |
||||||
|
ow_->RenderInt32( |
||||||
|
ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name, |
||||||
|
value); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
virtual Snake2CamelObjectWriter* RenderUint32(StringPiece name, |
||||||
|
uint32 value) { |
||||||
|
ow_->RenderUint32( |
||||||
|
ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name, |
||||||
|
value); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
virtual Snake2CamelObjectWriter* RenderInt64(StringPiece name, int64 value) { |
||||||
|
ow_->RenderInt64( |
||||||
|
ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name, |
||||||
|
value); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
virtual Snake2CamelObjectWriter* RenderUint64(StringPiece name, |
||||||
|
uint64 value) { |
||||||
|
ow_->RenderUint64( |
||||||
|
ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name, |
||||||
|
value); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
virtual Snake2CamelObjectWriter* RenderDouble(StringPiece name, |
||||||
|
double value) { |
||||||
|
ow_->RenderDouble( |
||||||
|
ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name, |
||||||
|
value); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
virtual Snake2CamelObjectWriter* RenderFloat(StringPiece name, float value) { |
||||||
|
ow_->RenderFloat( |
||||||
|
ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name, |
||||||
|
value); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
virtual Snake2CamelObjectWriter* RenderString(StringPiece name, |
||||||
|
StringPiece value) { |
||||||
|
ow_->RenderString( |
||||||
|
ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name, |
||||||
|
value); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
virtual Snake2CamelObjectWriter* RenderBytes(StringPiece name, |
||||||
|
StringPiece value) { |
||||||
|
ow_->RenderBytes( |
||||||
|
ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name, |
||||||
|
value); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
virtual Snake2CamelObjectWriter* RenderNull(StringPiece name) { |
||||||
|
ow_->RenderNull(ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) |
||||||
|
: name); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
virtual Snake2CamelObjectWriter* DisableCaseNormalizationForNextKey() { |
||||||
|
normalize_case_ = false; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
ObjectWriter* ow_; |
||||||
|
bool normalize_case_; |
||||||
|
|
||||||
|
bool ShouldNormalizeCase(StringPiece name) { |
||||||
|
if (normalize_case_) { |
||||||
|
return !IsCamel(name); |
||||||
|
} else { |
||||||
|
normalize_case_ = true; |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool IsCamel(StringPiece name) { |
||||||
|
return name.empty() || (ascii_islower(name[0]) && !name.contains("_")); |
||||||
|
} |
||||||
|
|
||||||
|
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Snake2CamelObjectWriter); |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
|
||||||
|
} // namespace google
|
||||||
|
#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_SNAKE2CAMEL_OBJECTWRITER_H__
|
@ -0,0 +1,311 @@ |
|||||||
|
// 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/util/internal/snake2camel_objectwriter.h> |
||||||
|
#include <google/protobuf/util/internal/expecting_objectwriter.h> |
||||||
|
#include <gtest/gtest.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace converter { |
||||||
|
|
||||||
|
class Snake2CamelObjectWriterTest : public ::testing::Test { |
||||||
|
protected: |
||||||
|
Snake2CamelObjectWriterTest() : mock_(), expects_(&mock_), testing_(&mock_) {} |
||||||
|
virtual ~Snake2CamelObjectWriterTest() {} |
||||||
|
|
||||||
|
MockObjectWriter mock_; |
||||||
|
ExpectingObjectWriter expects_; |
||||||
|
Snake2CamelObjectWriter testing_; |
||||||
|
}; |
||||||
|
|
||||||
|
TEST_F(Snake2CamelObjectWriterTest, Empty) { |
||||||
|
// Set expectation
|
||||||
|
expects_.StartObject("")->EndObject(); |
||||||
|
|
||||||
|
// Actual testing
|
||||||
|
testing_.StartObject("")->EndObject(); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(Snake2CamelObjectWriterTest, UnderscoresOnly) { |
||||||
|
// Set expectation
|
||||||
|
expects_.StartObject("") |
||||||
|
->RenderInt32("", 1) |
||||||
|
->RenderInt32("", 2) |
||||||
|
->RenderInt32("", 3) |
||||||
|
->RenderInt32("", 4) |
||||||
|
->RenderInt32("", 5) |
||||||
|
->EndObject(); |
||||||
|
|
||||||
|
// Actual testing
|
||||||
|
testing_.StartObject("") |
||||||
|
->RenderInt32("_", 1) |
||||||
|
->RenderInt32("__", 2) |
||||||
|
->RenderInt32("___", 3) |
||||||
|
->RenderInt32("____", 4) |
||||||
|
->RenderInt32("_____", 5) |
||||||
|
->EndObject(); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(Snake2CamelObjectWriterTest, LowercaseOnly) { |
||||||
|
// Set expectation
|
||||||
|
expects_.StartObject("") |
||||||
|
->RenderString("key", "value") |
||||||
|
->RenderString("abracadabra", "magic") |
||||||
|
->EndObject(); |
||||||
|
|
||||||
|
// Actual testing
|
||||||
|
testing_.StartObject("") |
||||||
|
->RenderString("key", "value") |
||||||
|
->RenderString("abracadabra", "magic") |
||||||
|
->EndObject(); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(Snake2CamelObjectWriterTest, UppercaseOnly) { |
||||||
|
// Set expectation
|
||||||
|
expects_.StartObject("") |
||||||
|
->RenderString("key", "VALUE") |
||||||
|
->RenderString("abracadabra", "MAGIC") |
||||||
|
->EndObject(); |
||||||
|
|
||||||
|
// Actual testing
|
||||||
|
testing_.StartObject("") |
||||||
|
->RenderString("KEY", "VALUE") |
||||||
|
->RenderString("ABRACADABRA", "MAGIC") |
||||||
|
->EndObject(); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(Snake2CamelObjectWriterTest, CamelCase) { |
||||||
|
// Set expectation
|
||||||
|
expects_.StartObject("") |
||||||
|
->RenderString("camelCase", "camelCase") |
||||||
|
->RenderString("theQuickBrownFoxJumpsOverTheLazyDog", |
||||||
|
"theQuickBrownFoxJumpsOverTheLazyDog") |
||||||
|
->EndObject(); |
||||||
|
|
||||||
|
// Actual testing
|
||||||
|
testing_.StartObject("") |
||||||
|
->RenderString("camelCase", "camelCase") |
||||||
|
->RenderString("theQuickBrownFoxJumpsOverTheLazyDog", |
||||||
|
"theQuickBrownFoxJumpsOverTheLazyDog") |
||||||
|
->EndObject(); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(Snake2CamelObjectWriterTest, FirstCapCamelCase) { |
||||||
|
// Sets expectation
|
||||||
|
expects_.StartObject("camel") |
||||||
|
->RenderString("camelCase", "CamelCase") |
||||||
|
->RenderString("theQuickBrownFoxJumpsOverTheLazyDog", |
||||||
|
"TheQuickBrownFoxJumpsOverTheLazyDog") |
||||||
|
->EndObject(); |
||||||
|
|
||||||
|
// Actual testing
|
||||||
|
testing_.StartObject("Camel") |
||||||
|
->RenderString("CamelCase", "CamelCase") |
||||||
|
->RenderString("TheQuickBrownFoxJumpsOverTheLazyDog", |
||||||
|
"TheQuickBrownFoxJumpsOverTheLazyDog") |
||||||
|
->EndObject(); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(Snake2CamelObjectWriterTest, LastCapCamelCase) { |
||||||
|
// Sets expectation
|
||||||
|
expects_.StartObject("lastCapCamelCasE")->EndObject(); |
||||||
|
|
||||||
|
// Actual testing
|
||||||
|
testing_.StartObject("lastCapCamelCasE")->EndObject(); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(Snake2CamelObjectWriterTest, MixedCapCamelCase) { |
||||||
|
// Sets expectation
|
||||||
|
expects_.StartObject("googleIsTheBest") |
||||||
|
->RenderFloat("iLoveGOOGLE", 1.61803f) |
||||||
|
->RenderFloat("goGoogleGO", 2.71828f) |
||||||
|
->RenderFloat("gBikeISCool", 3.14159f) |
||||||
|
->EndObject(); |
||||||
|
|
||||||
|
// Actual testing
|
||||||
|
testing_.StartObject("GOOGLEIsTheBest") |
||||||
|
->RenderFloat("ILoveGOOGLE", 1.61803f) |
||||||
|
->RenderFloat("GOGoogleGO", 2.71828f) |
||||||
|
->RenderFloat("GBikeISCool", 3.14159f) |
||||||
|
->EndObject(); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(Snake2CamelObjectWriterTest, MixedCase) { |
||||||
|
// Sets expectation
|
||||||
|
expects_.StartObject("snakeCaseCamelCase") |
||||||
|
->RenderBool("camelCaseSnakeCase", false) |
||||||
|
->RenderBool("mixedCamelAndUnderScores", false) |
||||||
|
->RenderBool("goGOOGLEGo", true) |
||||||
|
->EndObject(); |
||||||
|
|
||||||
|
// Actual testing
|
||||||
|
testing_.StartObject("snake_case_camelCase") |
||||||
|
->RenderBool("camelCase_snake_case", false) |
||||||
|
->RenderBool("MixedCamel_And_UnderScores", false) |
||||||
|
->RenderBool("Go_GOOGLEGo", true) |
||||||
|
->EndObject(); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(Snake2CamelObjectWriterTest, SnakeCase) { |
||||||
|
// Sets expectation
|
||||||
|
expects_.StartObject("") |
||||||
|
->RenderString("snakeCase", "snake_case") |
||||||
|
->RenderString("theQuickBrownFoxJumpsOverTheLazyDog", |
||||||
|
"the_quick_brown_fox_jumps_over_the_lazy_dog") |
||||||
|
->EndObject(); |
||||||
|
|
||||||
|
// Actual testing
|
||||||
|
testing_.StartObject("") |
||||||
|
->RenderString("snake_case", "snake_case") |
||||||
|
->RenderString("the_quick_brown_fox_jumps_over_the_lazy_dog", |
||||||
|
"the_quick_brown_fox_jumps_over_the_lazy_dog") |
||||||
|
->EndObject(); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(Snake2CamelObjectWriterTest, FirstCapSnakeCase) { |
||||||
|
// Sets expectation
|
||||||
|
expects_.StartObject("firstCapSnakeCase") |
||||||
|
->RenderBool("helloWorld", true) |
||||||
|
->EndObject(); |
||||||
|
|
||||||
|
// Actual testing
|
||||||
|
testing_.StartObject("First_Cap_Snake_Case") |
||||||
|
->RenderBool("Hello_World", true) |
||||||
|
->EndObject(); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(Snake2CamelObjectWriterTest, AllCapSnakeCase) { |
||||||
|
// Sets expectation
|
||||||
|
expects_.StartObject("allCAPSNAKECASE") |
||||||
|
->RenderDouble("nyseGOOGL", 600.0L) |
||||||
|
->RenderDouble("aBCDE", 1.0L) |
||||||
|
->RenderDouble("klMNOP", 2.0L) |
||||||
|
->RenderDouble("abcIJKPQRXYZ", 3.0L) |
||||||
|
->EndObject(); |
||||||
|
|
||||||
|
// Actual testing
|
||||||
|
testing_.StartObject("ALL_CAP_SNAKE_CASE") |
||||||
|
->RenderDouble("NYSE_GOOGL", 600.0L) |
||||||
|
->RenderDouble("A_B_C_D_E", 1.0L) |
||||||
|
->RenderDouble("KL_MN_OP", 2.0L) |
||||||
|
->RenderDouble("ABC_IJK_PQR_XYZ", 3.0L) |
||||||
|
->EndObject(); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(Snake2CamelObjectWriterTest, RepeatedUnderScoreSnakeCase) { |
||||||
|
// Sets expectation
|
||||||
|
expects_.StartObject("") |
||||||
|
->RenderInt32("doubleUnderscoreSnakeCase", 2) |
||||||
|
->RenderInt32("tripleUnderscoreFirstCap", 3) |
||||||
|
->RenderInt32("quadrupleUNDERSCOREALLCAP", 4) |
||||||
|
->EndObject(); |
||||||
|
|
||||||
|
// Actual testing
|
||||||
|
testing_.StartObject("") |
||||||
|
->RenderInt32("double__underscore__snake__case", 2) |
||||||
|
->RenderInt32("Triple___Underscore___First___Cap", 3) |
||||||
|
->RenderInt32("QUADRUPLE____UNDERSCORE____ALL____CAP", 4) |
||||||
|
->EndObject(); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(Snake2CamelObjectWriterTest, LeadingUnderscoreSnakeCase) { |
||||||
|
// Sets expectation
|
||||||
|
expects_.StartObject("leadingUnderscoreSnakeCase") |
||||||
|
->RenderUint32("leadingDoubleUnderscore", 2) |
||||||
|
->RenderUint32("leadingTripleUnderscoreFirstCap", 3) |
||||||
|
->RenderUint32("leadingQUADRUPLEUNDERSCOREALLCAP", 4) |
||||||
|
->EndObject(); |
||||||
|
|
||||||
|
// Actual testing
|
||||||
|
testing_.StartObject("_leading_underscore_snake_case") |
||||||
|
->RenderUint32("__leading_double_underscore", 2) |
||||||
|
->RenderUint32("___Leading_Triple_Underscore_First_Cap", 3) |
||||||
|
->RenderUint32("____LEADING_QUADRUPLE_UNDERSCORE_ALL_CAP", 4) |
||||||
|
->EndObject(); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(Snake2CamelObjectWriterTest, TrailingUnderscoreSnakeCase) { |
||||||
|
// Sets expectation
|
||||||
|
expects_.StartObject("trailingUnderscoreSnakeCase") |
||||||
|
->RenderInt64("trailingDoubleUnderscore", 2L) |
||||||
|
->RenderInt64("trailingTripleUnderscoreFirstCap", 3L) |
||||||
|
->RenderInt64("trailingQUADRUPLEUNDERSCOREALLCAP", 4L) |
||||||
|
->EndObject(); |
||||||
|
|
||||||
|
// Actual testing
|
||||||
|
testing_.StartObject("trailing_underscore_snake_case") |
||||||
|
->RenderInt64("trailing_double_underscore__", 2L) |
||||||
|
->RenderInt64("Trailing_Triple_Underscore_First_Cap___", 3L) |
||||||
|
->RenderInt64("TRAILING_QUADRUPLE_UNDERSCORE_ALL_CAP____", 4L) |
||||||
|
->EndObject(); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(Snake2CamelObjectWriterTest, EnclosingUnderscoreSnakeCase) { |
||||||
|
// Sets expectation
|
||||||
|
expects_.StartObject("enclosingUnderscoreSnakeCase") |
||||||
|
->RenderUint64("enclosingDoubleUnderscore", 2L) |
||||||
|
->RenderUint64("enclosingTripleUnderscoreFirstCap", 3L) |
||||||
|
->RenderUint64("enclosingQUADRUPLEUNDERSCOREALLCAP", 4L) |
||||||
|
->EndObject(); |
||||||
|
|
||||||
|
// Actual testing
|
||||||
|
testing_.StartObject("_enclosing_underscore_snake_case_") |
||||||
|
->RenderUint64("__enclosing_double_underscore__", 2L) |
||||||
|
->RenderUint64("___Enclosing_Triple_Underscore_First_Cap___", 3L) |
||||||
|
->RenderUint64("____ENCLOSING_QUADRUPLE_UNDERSCORE_ALL_CAP____", 4L) |
||||||
|
->EndObject(); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(Snake2CamelObjectWriterTest, DisableCaseNormalizationOnlyDisablesFirst) { |
||||||
|
// Sets expectation
|
||||||
|
expects_.StartObject("") |
||||||
|
->RenderString("snakeCase", "snake_case") |
||||||
|
->RenderString( |
||||||
|
"the_quick_brown_fox_jumps_over_the_lazy_dog", // case retained
|
||||||
|
"the_quick_brown_fox_jumps_over_the_lazy_dog") |
||||||
|
->RenderBool("theSlowFox", true) // disable case not in effect
|
||||||
|
->EndObject(); |
||||||
|
|
||||||
|
// Actual testing
|
||||||
|
testing_.StartObject("") |
||||||
|
->RenderString("snake_case", "snake_case") |
||||||
|
->DisableCaseNormalizationForNextKey() |
||||||
|
->RenderString("the_quick_brown_fox_jumps_over_the_lazy_dog", |
||||||
|
"the_quick_brown_fox_jumps_over_the_lazy_dog") |
||||||
|
->RenderBool("the_slow_fox", true) |
||||||
|
->EndObject(); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
} // namespace google
|
@ -0,0 +1,118 @@ |
|||||||
|
// 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_UTIL_CONVERTER_STRUCTURED_OBJECTWRITER_H__ |
||||||
|
#define GOOGLE_PROTOBUF_UTIL_CONVERTER_STRUCTURED_OBJECTWRITER_H__ |
||||||
|
|
||||||
|
#include <memory> |
||||||
|
#ifndef _SHARED_PTR_H |
||||||
|
#include <google/protobuf/stubs/shared_ptr.h> |
||||||
|
#endif |
||||||
|
|
||||||
|
#include <google/protobuf/stubs/casts.h> |
||||||
|
#include <google/protobuf/stubs/common.h> |
||||||
|
#include <google/protobuf/util/internal/object_writer.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace converter { |
||||||
|
|
||||||
|
// An StructuredObjectWriter is an ObjectWriter for writing
|
||||||
|
// tree-structured data in a stream of events representing objects
|
||||||
|
// and collections. Implementation of this interface can be used to
|
||||||
|
// write an object stream to an in-memory structure, protobufs,
|
||||||
|
// JSON, XML, or any other output format desired. The ObjectSource
|
||||||
|
// interface is typically used as the source of an object stream.
|
||||||
|
//
|
||||||
|
// See JsonObjectWriter for a sample implementation of
|
||||||
|
// StructuredObjectWriter and its use.
|
||||||
|
//
|
||||||
|
// Derived classes could be thread-unsafe.
|
||||||
|
class LIBPROTOBUF_EXPORT StructuredObjectWriter : public ObjectWriter { |
||||||
|
public: |
||||||
|
virtual ~StructuredObjectWriter() {} |
||||||
|
|
||||||
|
protected: |
||||||
|
// A base element class for subclasses to extend, makes tracking state easier.
|
||||||
|
//
|
||||||
|
// StructuredObjectWriter behaves as a visitor. BaseElement represents a node
|
||||||
|
// in the input tree. Implementation of StructuredObjectWriter should also
|
||||||
|
// extend BaseElement to keep track of the location in the input tree.
|
||||||
|
class LIBPROTOBUF_EXPORT BaseElement { |
||||||
|
public: |
||||||
|
// Takes ownership of the parent Element.
|
||||||
|
explicit BaseElement(BaseElement* parent) |
||||||
|
: parent_(parent), level_(parent == NULL ? 0 : parent->level() + 1) {} |
||||||
|
virtual ~BaseElement() {} |
||||||
|
|
||||||
|
// Releases ownership of the parent and returns a pointer to it.
|
||||||
|
template <typename ElementType> |
||||||
|
ElementType* pop() { |
||||||
|
return down_cast<ElementType*>(parent_.release()); |
||||||
|
} |
||||||
|
|
||||||
|
// Returns true if this element is the root.
|
||||||
|
bool is_root() const { return parent_ == NULL; } |
||||||
|
|
||||||
|
// Returns the number of hops from this element to the root element.
|
||||||
|
int level() const { return level_; } |
||||||
|
|
||||||
|
protected: |
||||||
|
// Returns pointer to parent element without releasing ownership.
|
||||||
|
virtual BaseElement* parent() const { return parent_.get(); } |
||||||
|
|
||||||
|
private: |
||||||
|
// Pointer to the parent Element.
|
||||||
|
google::protobuf::scoped_ptr<BaseElement> parent_; |
||||||
|
|
||||||
|
// Number of hops to the root Element.
|
||||||
|
// The root Element has NULL parent_ and a level_ of 0.
|
||||||
|
const int level_; |
||||||
|
|
||||||
|
GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(BaseElement); |
||||||
|
}; |
||||||
|
|
||||||
|
StructuredObjectWriter() {} |
||||||
|
|
||||||
|
// Returns the current element. Used for indentation and name overrides.
|
||||||
|
virtual BaseElement* element() = 0; |
||||||
|
|
||||||
|
private: |
||||||
|
// Do not add any data members to this class.
|
||||||
|
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(StructuredObjectWriter); |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
|
||||||
|
} // namespace google
|
||||||
|
#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_STRUCTURED_OBJECTWRITER_H__
|
@ -0,0 +1,53 @@ |
|||||||
|
// 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. |
||||||
|
|
||||||
|
// Proto to test Proto3 Any serialization. |
||||||
|
syntax = "proto3"; |
||||||
|
|
||||||
|
package google.protobuf.testing.anys; |
||||||
|
option java_package = "com.google.protobuf.testing.anys"; |
||||||
|
|
||||||
|
import "google/protobuf/any.proto"; |
||||||
|
|
||||||
|
message AnyIn { |
||||||
|
string something = 1; |
||||||
|
} |
||||||
|
|
||||||
|
message AnyOut { |
||||||
|
google.protobuf.Any any = 1; |
||||||
|
} |
||||||
|
|
||||||
|
message AnyM { |
||||||
|
string foo = 1; |
||||||
|
} |
||||||
|
|
||||||
|
service TestService { |
||||||
|
rpc Call(AnyIn) returns (AnyOut); |
||||||
|
} |
@ -0,0 +1,171 @@ |
|||||||
|
// 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. |
||||||
|
|
||||||
|
// Author: sven@google.com (Sven Mawson) |
||||||
|
// |
||||||
|
// Sample protos for testing. |
||||||
|
syntax = "proto2"; |
||||||
|
|
||||||
|
package google.protobuf.testing; |
||||||
|
|
||||||
|
// A book |
||||||
|
message Book { |
||||||
|
optional string title = 1; |
||||||
|
optional Author author = 2; |
||||||
|
optional uint32 length = 3; |
||||||
|
optional int64 published = 4; |
||||||
|
optional bytes content = 5; |
||||||
|
|
||||||
|
optional group Data = 6 { |
||||||
|
optional uint32 year = 7; |
||||||
|
optional string copyright = 8; |
||||||
|
} |
||||||
|
|
||||||
|
message Label { |
||||||
|
optional string key = 1; |
||||||
|
optional string value = 2; |
||||||
|
} |
||||||
|
|
||||||
|
optional Publisher publisher = 9; |
||||||
|
repeated Label labels = 10; |
||||||
|
|
||||||
|
extensions 200 to 499; |
||||||
|
} |
||||||
|
|
||||||
|
// A publisher of a book, tests required fields. |
||||||
|
message Publisher { |
||||||
|
required string name = 1; |
||||||
|
} |
||||||
|
|
||||||
|
// An author of a book |
||||||
|
message Author { |
||||||
|
optional uint64 id = 1; |
||||||
|
optional string name = 2; |
||||||
|
repeated string pseudonym = 3; |
||||||
|
optional bool alive = 4; |
||||||
|
repeated Author friend = 5; |
||||||
|
} |
||||||
|
|
||||||
|
// For testing resiliency of our protostream parser. |
||||||
|
// Field numbers of Author are reused for something else. |
||||||
|
message BadAuthor { |
||||||
|
optional string id = 1; // non-length-delimited to length-delimited. |
||||||
|
repeated uint64 name = 2; // string to repeated (both length-delimited). |
||||||
|
optional string pseudonym = 3; // Repeated to optional. |
||||||
|
repeated bool alive = 4 [packed=true]; // Optional to repeated. |
||||||
|
} |
||||||
|
|
||||||
|
// All primitive types |
||||||
|
message Primitive { |
||||||
|
// 32 bit numbers: |
||||||
|
optional fixed32 fix32 = 1; |
||||||
|
optional uint32 u32 = 2; |
||||||
|
optional int32 i32 = 3; |
||||||
|
optional sfixed32 sf32 = 4; |
||||||
|
optional sint32 s32 = 5; |
||||||
|
|
||||||
|
// 64 bit numbers: |
||||||
|
optional fixed64 fix64 = 6; |
||||||
|
optional uint64 u64 = 7; |
||||||
|
optional int64 i64 = 8; |
||||||
|
optional sfixed64 sf64 = 9; |
||||||
|
optional sint64 s64 = 10; |
||||||
|
|
||||||
|
// The other stuff. |
||||||
|
optional string str = 11; |
||||||
|
optional bytes bytes = 12; |
||||||
|
optional float float = 13; |
||||||
|
optional double double = 14; |
||||||
|
optional bool bool = 15; |
||||||
|
|
||||||
|
// repeated 32 bit numbers: |
||||||
|
repeated fixed32 rep_fix32 = 16; |
||||||
|
repeated uint32 rep_u32 = 17; |
||||||
|
repeated int32 rep_i32 = 18; |
||||||
|
repeated sfixed32 rep_sf32 = 19; |
||||||
|
repeated sint32 rep_s32 = 20; |
||||||
|
|
||||||
|
// repeated 64 bit numbers: |
||||||
|
repeated fixed64 rep_fix64 = 21; |
||||||
|
repeated uint64 rep_u64 = 22; |
||||||
|
repeated int64 rep_i64 = 23; |
||||||
|
repeated sfixed64 rep_sf64 = 24; |
||||||
|
repeated sint64 rep_s64 = 25; |
||||||
|
|
||||||
|
// repeated other stuff: |
||||||
|
repeated string rep_str = 26; |
||||||
|
repeated bytes rep_bytes = 27; |
||||||
|
repeated float rep_float = 28; |
||||||
|
repeated double rep_double = 29; |
||||||
|
repeated bool rep_bool = 30; |
||||||
|
} |
||||||
|
|
||||||
|
// Test packed versions of all repeated primitives. |
||||||
|
// The field numbers should match their non-packed version in Primitive message. |
||||||
|
message PackedPrimitive { |
||||||
|
// repeated 32 bit numbers: |
||||||
|
repeated fixed32 rep_fix32 = 16 [packed=true]; |
||||||
|
repeated uint32 rep_u32 = 17 [packed=true]; |
||||||
|
repeated int32 rep_i32 = 18 [packed=true]; |
||||||
|
repeated sfixed32 rep_sf32 = 19 [packed=true]; |
||||||
|
repeated sint32 rep_s32 = 20 [packed=true]; |
||||||
|
|
||||||
|
// repeated 64 bit numbers: |
||||||
|
repeated fixed64 rep_fix64 = 21 [packed=true]; |
||||||
|
repeated uint64 rep_u64 = 22 [packed=true]; |
||||||
|
repeated int64 rep_i64 = 23 [packed=true]; |
||||||
|
repeated sfixed64 rep_sf64 = 24 [packed=true]; |
||||||
|
repeated sint64 rep_s64 = 25 [packed=true]; |
||||||
|
|
||||||
|
// repeated other stuff: |
||||||
|
repeated float rep_float = 28 [packed=true]; |
||||||
|
repeated double rep_double = 29 [packed=true]; |
||||||
|
repeated bool rep_bool = 30 [packed=true]; |
||||||
|
} |
||||||
|
|
||||||
|
// Test extensions. |
||||||
|
extend Book { |
||||||
|
repeated Author more_author = 201; |
||||||
|
} |
||||||
|
|
||||||
|
// Test nested extensions. |
||||||
|
message NestedBook { |
||||||
|
extend Book { |
||||||
|
optional NestedBook another_book = 301; |
||||||
|
} |
||||||
|
// Recurse |
||||||
|
optional Book book = 1; |
||||||
|
} |
||||||
|
|
||||||
|
// For testing resiliency of our protostream parser. |
||||||
|
// Field number of NestedBook is reused for something else. |
||||||
|
message BadNestedBook { |
||||||
|
repeated uint32 book = 1 [packed=true]; // Packed to optional message. |
||||||
|
} |
@ -0,0 +1,162 @@ |
|||||||
|
// 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. |
||||||
|
|
||||||
|
syntax = "proto3"; |
||||||
|
|
||||||
|
package google.protobuf.testing; |
||||||
|
|
||||||
|
import "google/protobuf/any.proto"; |
||||||
|
import "google/protobuf/struct.proto"; |
||||||
|
import "google/protobuf/wrappers.proto"; |
||||||
|
|
||||||
|
message DefaultValueTestCases { |
||||||
|
DoubleMessage empty_double = 1; |
||||||
|
DoubleMessage double_with_default_value = 2; |
||||||
|
DoubleMessage double_with_nondefault_value = 3; |
||||||
|
DoubleMessage repeated_double = 4; |
||||||
|
DoubleMessage nested_message = 5; |
||||||
|
DoubleMessage repeated_nested_message = 6; |
||||||
|
StructMessage empty_struct = 201; |
||||||
|
StructMessage empty_struct2 = 202; |
||||||
|
StructMessage struct_with_null_value = 203; |
||||||
|
StructMessage struct_with_values = 204; |
||||||
|
StructMessage struct_with_nested_struct = 205; |
||||||
|
StructMessage struct_with_nested_list = 206; |
||||||
|
StructMessage struct_with_list_of_nulls = 207; |
||||||
|
StructMessage struct_with_list_of_lists = 208; |
||||||
|
StructMessage struct_with_list_of_structs = 209; |
||||||
|
google.protobuf.Struct top_level_struct = 210; |
||||||
|
ValueMessage value_wrapper_simple = 212; |
||||||
|
ValueMessage value_wrapper_with_struct = 213; |
||||||
|
ValueMessage value_wrapper_with_list = 214; |
||||||
|
ListValueMessage list_value_wrapper = 215; |
||||||
|
google.protobuf.Value top_level_value_simple = 216; |
||||||
|
google.protobuf.Value top_level_value_with_struct = 217; |
||||||
|
google.protobuf.Value top_level_value_with_list = 218; |
||||||
|
google.protobuf.ListValue top_level_listvalue = 219; |
||||||
|
AnyMessage empty_any = 301; |
||||||
|
AnyMessage type_only_any = 302; |
||||||
|
AnyMessage recursive_any = 303; |
||||||
|
AnyMessage any_with_message_value = 304; |
||||||
|
AnyMessage any_with_nested_message = 305; |
||||||
|
AnyMessage any_with_message_containing_map = 306; |
||||||
|
AnyMessage any_with_message_containing_struct = 307; |
||||||
|
google.protobuf.Any top_level_any = 308; |
||||||
|
StringtoIntMap empty_map = 401; |
||||||
|
StringtoIntMap string_to_int = 402; |
||||||
|
IntToStringMap int_to_string = 403; |
||||||
|
MixedMap mixed1 = 404; |
||||||
|
MixedMap2 mixed2 = 405; |
||||||
|
MessageMap map_of_objects = 406; |
||||||
|
DoubleValueMessage double_value = 501; |
||||||
|
DoubleValueMessage double_value_default = 502; |
||||||
|
} |
||||||
|
|
||||||
|
message DoubleMessage { |
||||||
|
double double_value = 1; |
||||||
|
repeated double repeated_double = 2; |
||||||
|
DoubleMessage nested_message = 3; |
||||||
|
repeated DoubleMessage repeated_nested_message = 4; |
||||||
|
google.protobuf.DoubleValue double_wrapper = 100; |
||||||
|
} |
||||||
|
|
||||||
|
message StructMessage { |
||||||
|
google.protobuf.Struct struct = 1; |
||||||
|
} |
||||||
|
|
||||||
|
message ValueMessage { |
||||||
|
google.protobuf.Value value = 1; |
||||||
|
} |
||||||
|
|
||||||
|
message ListValueMessage { |
||||||
|
google.protobuf.ListValue shopping_list = 1; |
||||||
|
} |
||||||
|
message RequestMessage { |
||||||
|
string content = 1; |
||||||
|
} |
||||||
|
|
||||||
|
// A test service. |
||||||
|
service DefaultValueTestService { |
||||||
|
// A test method. |
||||||
|
rpc Call(RequestMessage) returns (DefaultValueTestCases); |
||||||
|
} |
||||||
|
|
||||||
|
message AnyMessage { |
||||||
|
google.protobuf.Any any = 1; |
||||||
|
AnyData data = 2; |
||||||
|
} |
||||||
|
|
||||||
|
message AnyData { |
||||||
|
int32 attr = 1; |
||||||
|
string str = 2; |
||||||
|
repeated string msgs = 3; |
||||||
|
AnyData nested_data = 4; |
||||||
|
map<string, string> map_data = 7; |
||||||
|
google.protobuf.Struct struct_data = 8; |
||||||
|
repeated AnyData repeated_data = 9; |
||||||
|
} |
||||||
|
|
||||||
|
message StringtoIntMap { |
||||||
|
map<string, int32> map = 1; |
||||||
|
} |
||||||
|
|
||||||
|
message IntToStringMap { |
||||||
|
map<int32, string> map = 1; |
||||||
|
} |
||||||
|
|
||||||
|
message MixedMap { |
||||||
|
string msg = 1; |
||||||
|
map<string, float> map = 2; |
||||||
|
int32 int_value = 3; |
||||||
|
} |
||||||
|
|
||||||
|
message MixedMap2 { |
||||||
|
enum E { |
||||||
|
E0 = 0; |
||||||
|
E1 = 1; |
||||||
|
E2 = 2; |
||||||
|
E3 = 3; |
||||||
|
} |
||||||
|
map<int32, bool> map = 1; |
||||||
|
E ee = 2; |
||||||
|
string msg = 4; |
||||||
|
} |
||||||
|
|
||||||
|
message MessageMap { |
||||||
|
message M { |
||||||
|
int32 inner_int = 1; |
||||||
|
string inner_text = 2; |
||||||
|
} |
||||||
|
map<string, M> map = 1; |
||||||
|
} |
||||||
|
|
||||||
|
message DoubleValueMessage { |
||||||
|
google.protobuf.DoubleValue double = 1; |
||||||
|
} |
@ -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. |
||||||
|
|
||||||
|
syntax = "proto3"; |
||||||
|
|
||||||
|
package google.protobuf.testing; |
||||||
|
|
||||||
|
message DefaultValueTest { |
||||||
|
double double_value = 1; |
||||||
|
repeated double repeated_double = 2; |
||||||
|
float float_value = 3; |
||||||
|
int64 int64_value = 5; |
||||||
|
uint64 uint64_value = 7; |
||||||
|
int32 int32_value = 9; |
||||||
|
uint32 uint32_value = 11; |
||||||
|
bool bool_value = 13; |
||||||
|
string string_value = 15; |
||||||
|
bytes bytes_value = 17 [ctype = CORD]; |
||||||
|
} |
@ -0,0 +1,71 @@ |
|||||||
|
// 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. |
||||||
|
|
||||||
|
syntax = "proto3"; |
||||||
|
|
||||||
|
package google.protobuf.testing; |
||||||
|
|
||||||
|
import "google/protobuf/field_mask.proto"; |
||||||
|
|
||||||
|
message NestedFieldMask { |
||||||
|
string data = 1; |
||||||
|
google.protobuf.FieldMask single_mask = 2; |
||||||
|
repeated google.protobuf.FieldMask repeated_mask = 3; |
||||||
|
} |
||||||
|
|
||||||
|
message FieldMaskTest { |
||||||
|
string id = 1; |
||||||
|
google.protobuf.FieldMask single_mask = 2; |
||||||
|
repeated google.protobuf.FieldMask repeated_mask = 3; |
||||||
|
repeated NestedFieldMask nested_mask = 4; |
||||||
|
} |
||||||
|
|
||||||
|
message FieldMaskTestCases { |
||||||
|
FieldMaskWrapper single_mask = 1; |
||||||
|
FieldMaskWrapper multiple_mask = 2; |
||||||
|
FieldMaskWrapper snake_camel = 3; |
||||||
|
FieldMaskWrapper empty_field = 4; |
||||||
|
FieldMaskWrapper apiary_format1 = 5; |
||||||
|
FieldMaskWrapper apiary_format2 = 6; |
||||||
|
FieldMaskWrapper apiary_format3 = 7; |
||||||
|
FieldMaskWrapper map_key1 = 8; |
||||||
|
FieldMaskWrapper map_key2 = 9; |
||||||
|
FieldMaskWrapper map_key3 = 10; |
||||||
|
FieldMaskWrapper map_key4 = 11; |
||||||
|
FieldMaskWrapper map_key5 = 12; |
||||||
|
} |
||||||
|
|
||||||
|
message FieldMaskWrapper { |
||||||
|
google.protobuf.FieldMask mask = 1; |
||||||
|
} |
||||||
|
|
||||||
|
service FieldMaskTestService { |
||||||
|
rpc Call(FieldMaskTestCases) returns (FieldMaskTestCases); |
||||||
|
} |
@ -0,0 +1,57 @@ |
|||||||
|
// 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. |
||||||
|
|
||||||
|
// Proto to test proto3 maps. |
||||||
|
syntax = "proto3"; |
||||||
|
|
||||||
|
package google.protobuf.testing.maps; |
||||||
|
option java_package = "com.google.protobuf.testing.maps"; |
||||||
|
|
||||||
|
message MapIn { |
||||||
|
string other = 1; |
||||||
|
repeated string things = 2; |
||||||
|
map<string, string> map_input = 3; |
||||||
|
} |
||||||
|
|
||||||
|
message MapOut { |
||||||
|
map<string, MapM> map1 = 1; |
||||||
|
map<string, MapOut> map2 = 2; |
||||||
|
map<int32, string> map3 = 3; |
||||||
|
string bar = 4; |
||||||
|
} |
||||||
|
|
||||||
|
message MapM { |
||||||
|
string foo = 1; |
||||||
|
} |
||||||
|
|
||||||
|
service TestService { |
||||||
|
rpc Call1(MapIn) returns (MapOut); |
||||||
|
rpc Call2(MapIn) returns (MapOut); |
||||||
|
} |
@ -0,0 +1,45 @@ |
|||||||
|
// 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. |
||||||
|
|
||||||
|
// Proto to test proto3 struct. |
||||||
|
syntax = "proto3"; |
||||||
|
|
||||||
|
package google.protobuf.testing.structs; |
||||||
|
option java_package = "com.google.protobuf.testing.structs"; |
||||||
|
|
||||||
|
import "google/protobuf/struct.proto"; |
||||||
|
|
||||||
|
message StructType { |
||||||
|
google.protobuf.Struct object = 1; |
||||||
|
} |
||||||
|
|
||||||
|
service TestService { |
||||||
|
rpc Call(StructType) returns (StructType); |
||||||
|
} |
@ -0,0 +1,47 @@ |
|||||||
|
// 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. |
||||||
|
|
||||||
|
// Proto to test proto3 Timestamp and Duration. |
||||||
|
syntax = "proto3"; |
||||||
|
|
||||||
|
package google.protobuf.testing.timestampduration; |
||||||
|
option java_package = "com.google.protobuf.testing.timestampduration"; |
||||||
|
|
||||||
|
import "google/protobuf/timestamp.proto"; |
||||||
|
import "google/protobuf/duration.proto"; |
||||||
|
|
||||||
|
message TimestampDuration { |
||||||
|
google.protobuf.Timestamp ts = 1; |
||||||
|
google.protobuf.Duration dur = 2; |
||||||
|
} |
||||||
|
|
||||||
|
service TestService { |
||||||
|
rpc Call(TimestampDuration) returns (TimestampDuration); |
||||||
|
} |
@ -0,0 +1,100 @@ |
|||||||
|
// 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. |
||||||
|
|
||||||
|
syntax = "proto3"; |
||||||
|
|
||||||
|
package google.protobuf.testing; |
||||||
|
|
||||||
|
import "google/protobuf/wrappers.proto"; |
||||||
|
|
||||||
|
// Top-level test cases proto used by MarshallingTest. See description |
||||||
|
// at the top of the class MarshallingTest for details on how to write |
||||||
|
// test cases. |
||||||
|
message WrappersTestCases { |
||||||
|
DoubleWrapper double_wrapper = 1; |
||||||
|
FloatWrapper float_wrapper = 2; |
||||||
|
Int64Wrapper int64_wrapper = 3; |
||||||
|
UInt64Wrapper uint64_wrapper = 4; |
||||||
|
Int32Wrapper int32_wrapper = 5; |
||||||
|
UInt32Wrapper uint32_wrapper = 6; |
||||||
|
BoolWrapper bool_wrapper = 7; |
||||||
|
StringWrapper string_wrapper = 8; |
||||||
|
BytesWrapper bytes_wrapper = 9; |
||||||
|
|
||||||
|
DoubleWrapper double_wrapper_default = 10; |
||||||
|
FloatWrapper float_wrapper_default = 11; |
||||||
|
Int64Wrapper int64_wrapper_default = 12; |
||||||
|
UInt64Wrapper uint64_wrapper_default = 13; |
||||||
|
Int32Wrapper int32_wrapper_default = 14; |
||||||
|
UInt32Wrapper uint32_wrapper_default = 15; |
||||||
|
BoolWrapper bool_wrapper_default = 16; |
||||||
|
StringWrapper string_wrapper_default = 17; |
||||||
|
BytesWrapper bytes_wrapper_default = 18; |
||||||
|
} |
||||||
|
|
||||||
|
message DoubleWrapper { |
||||||
|
google.protobuf.DoubleValue double = 1; |
||||||
|
} |
||||||
|
|
||||||
|
message FloatWrapper { |
||||||
|
google.protobuf.FloatValue float = 1; |
||||||
|
} |
||||||
|
|
||||||
|
message Int64Wrapper { |
||||||
|
google.protobuf.Int64Value int64 = 1; |
||||||
|
} |
||||||
|
|
||||||
|
message UInt64Wrapper { |
||||||
|
google.protobuf.UInt64Value uint64 = 1; |
||||||
|
} |
||||||
|
|
||||||
|
message Int32Wrapper { |
||||||
|
google.protobuf.Int32Value int32 = 1; |
||||||
|
} |
||||||
|
|
||||||
|
message UInt32Wrapper { |
||||||
|
google.protobuf.UInt32Value uint32 = 1; |
||||||
|
} |
||||||
|
|
||||||
|
message BoolWrapper { |
||||||
|
google.protobuf.BoolValue bool = 1; |
||||||
|
} |
||||||
|
|
||||||
|
message StringWrapper { |
||||||
|
google.protobuf.StringValue string = 1; |
||||||
|
} |
||||||
|
|
||||||
|
message BytesWrapper { |
||||||
|
google.protobuf.BytesValue bytes = 1; |
||||||
|
} |
||||||
|
|
||||||
|
service WrappersTestService { |
||||||
|
rpc Call(WrappersTestCases) returns (WrappersTestCases); |
||||||
|
} |
@ -0,0 +1,171 @@ |
|||||||
|
// 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/util/internal/type_info.h> |
||||||
|
|
||||||
|
#include <map> |
||||||
|
#include <set> |
||||||
|
|
||||||
|
#include <google/protobuf/stubs/common.h> |
||||||
|
#include <google/protobuf/type.pb.h> |
||||||
|
#include <google/protobuf/stubs/stringpiece.h> |
||||||
|
#include <google/protobuf/stubs/map_util.h> |
||||||
|
#include <google/protobuf/stubs/status.h> |
||||||
|
#include <google/protobuf/stubs/statusor.h> |
||||||
|
#include <google/protobuf/util/internal/utility.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace converter { |
||||||
|
|
||||||
|
namespace { |
||||||
|
|
||||||
|
// A TypeInfo that looks up information provided by a TypeResolver.
|
||||||
|
class TypeInfoForTypeResolver : public TypeInfo { |
||||||
|
public: |
||||||
|
explicit TypeInfoForTypeResolver(TypeResolver* type_resolver) |
||||||
|
: type_resolver_(type_resolver) {} |
||||||
|
|
||||||
|
virtual ~TypeInfoForTypeResolver() { |
||||||
|
DeleteCachedTypes(&cached_types_); |
||||||
|
DeleteCachedTypes(&cached_enums_); |
||||||
|
} |
||||||
|
|
||||||
|
virtual util::StatusOr<const google::protobuf::Type*> ResolveTypeUrl( |
||||||
|
StringPiece type_url) { |
||||||
|
map<StringPiece, StatusOrType>::iterator it = cached_types_.find(type_url); |
||||||
|
if (it != cached_types_.end()) { |
||||||
|
return it->second; |
||||||
|
} |
||||||
|
// Stores the string value so it can be referenced using StringPiece in the
|
||||||
|
// cached_types_ map.
|
||||||
|
const string& string_type_url = |
||||||
|
*string_storage_.insert(type_url.ToString()).first; |
||||||
|
google::protobuf::scoped_ptr<google::protobuf::Type> type(new google::protobuf::Type()); |
||||||
|
util::Status status = |
||||||
|
type_resolver_->ResolveMessageType(string_type_url, type.get()); |
||||||
|
StatusOrType result = |
||||||
|
status.ok() ? StatusOrType(type.release()) : StatusOrType(status); |
||||||
|
cached_types_[string_type_url] = result; |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
virtual const google::protobuf::Type* GetType(StringPiece type_url) { |
||||||
|
StatusOrType result = ResolveTypeUrl(type_url); |
||||||
|
return result.ok() ? result.ValueOrDie() : NULL; |
||||||
|
} |
||||||
|
|
||||||
|
virtual const google::protobuf::Enum* GetEnum(StringPiece type_url) { |
||||||
|
map<StringPiece, StatusOrEnum>::iterator it = cached_enums_.find(type_url); |
||||||
|
if (it != cached_enums_.end()) { |
||||||
|
return it->second.ok() ? it->second.ValueOrDie() : NULL; |
||||||
|
} |
||||||
|
// Stores the string value so it can be referenced using StringPiece in the
|
||||||
|
// cached_enums_ map.
|
||||||
|
const string& string_type_url = |
||||||
|
*string_storage_.insert(type_url.ToString()).first; |
||||||
|
google::protobuf::scoped_ptr<google::protobuf::Enum> enum_type( |
||||||
|
new google::protobuf::Enum()); |
||||||
|
util::Status status = |
||||||
|
type_resolver_->ResolveEnumType(string_type_url, enum_type.get()); |
||||||
|
StatusOrEnum result = |
||||||
|
status.ok() ? StatusOrEnum(enum_type.release()) : StatusOrEnum(status); |
||||||
|
cached_enums_[string_type_url] = result; |
||||||
|
return result.ok() ? result.ValueOrDie() : NULL; |
||||||
|
} |
||||||
|
|
||||||
|
virtual const google::protobuf::Field* FindField( |
||||||
|
const google::protobuf::Type* type, StringPiece camel_case_name) { |
||||||
|
if (indexed_types_.find(type) == indexed_types_.end()) { |
||||||
|
PopulateNameLookupTable(type); |
||||||
|
indexed_types_.insert(type); |
||||||
|
} |
||||||
|
StringPiece name = |
||||||
|
FindWithDefault(camel_case_name_table_, camel_case_name, StringPiece()); |
||||||
|
if (name.empty()) { |
||||||
|
// Didn't find a mapping. Use whatever provided.
|
||||||
|
name = camel_case_name; |
||||||
|
} |
||||||
|
return FindFieldInTypeOrNull(type, name); |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
typedef util::StatusOr<const google::protobuf::Type*> StatusOrType; |
||||||
|
typedef util::StatusOr<const google::protobuf::Enum*> StatusOrEnum; |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
static void DeleteCachedTypes(map<StringPiece, T>* cached_types) { |
||||||
|
for (typename map<StringPiece, T>::iterator it = cached_types->begin(); |
||||||
|
it != cached_types->end(); ++it) { |
||||||
|
if (it->second.ok()) { |
||||||
|
delete it->second.ValueOrDie(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void PopulateNameLookupTable(const google::protobuf::Type* type) { |
||||||
|
for (int i = 0; i < type->fields_size(); ++i) { |
||||||
|
const google::protobuf::Field& field = type->fields(i); |
||||||
|
StringPiece name = field.name(); |
||||||
|
StringPiece camel_case_name = |
||||||
|
*string_storage_.insert(ToCamelCase(name)).first; |
||||||
|
const StringPiece* existing = InsertOrReturnExisting( |
||||||
|
&camel_case_name_table_, camel_case_name, name); |
||||||
|
if (existing && *existing != name) { |
||||||
|
GOOGLE_LOG(WARNING) << "Field '" << name << "' and '" << *existing |
||||||
|
<< "' map to the same camel case name '" << camel_case_name |
||||||
|
<< "'."; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TypeResolver* type_resolver_; |
||||||
|
|
||||||
|
// Stores string values that will be referenced by StringPieces in
|
||||||
|
// cached_types_, cached_enums_ and camel_case_name_table_.
|
||||||
|
set<string> string_storage_; |
||||||
|
|
||||||
|
map<StringPiece, StatusOrType> cached_types_; |
||||||
|
map<StringPiece, StatusOrEnum> cached_enums_; |
||||||
|
|
||||||
|
set<const google::protobuf::Type*> indexed_types_; |
||||||
|
map<StringPiece, StringPiece> camel_case_name_table_; |
||||||
|
}; |
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TypeInfo* TypeInfo::NewTypeInfo(TypeResolver* type_resolver) { |
||||||
|
return new TypeInfoForTypeResolver(type_resolver); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
} // namespace google
|
@ -0,0 +1,87 @@ |
|||||||
|
// 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_UTIL_CONVERTER_TYPE_INFO_H__ |
||||||
|
#define GOOGLE_PROTOBUF_UTIL_CONVERTER_TYPE_INFO_H__ |
||||||
|
|
||||||
|
#include <google/protobuf/stubs/common.h> |
||||||
|
#include <google/protobuf/type.pb.h> |
||||||
|
#include <google/protobuf/util/type_resolver.h> |
||||||
|
#include <google/protobuf/stubs/stringpiece.h> |
||||||
|
#include <google/protobuf/stubs/status.h> |
||||||
|
#include <google/protobuf/stubs/statusor.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace converter { |
||||||
|
// Internal helper class for type resolving. Note that this class is not
|
||||||
|
// thread-safe and should only be accessed in one thread.
|
||||||
|
class TypeInfo { |
||||||
|
public: |
||||||
|
TypeInfo() {} |
||||||
|
virtual ~TypeInfo() {} |
||||||
|
|
||||||
|
// Resolves a type url into a Type. If the type url is invalid, returns
|
||||||
|
// INVALID_ARGUMENT error status. If the type url is valid but the
|
||||||
|
// corresponding type cannot be found, returns a NOT_FOUND error status.
|
||||||
|
//
|
||||||
|
// This TypeInfo class retains the ownership of the returned pointer.
|
||||||
|
virtual util::StatusOr<const google::protobuf::Type*> ResolveTypeUrl( |
||||||
|
StringPiece type_url) = 0; |
||||||
|
|
||||||
|
// Resolves a type url into a Type. Like ResolveTypeUrl() but returns
|
||||||
|
// NULL if the type url is invalid or the type cannot be found.
|
||||||
|
//
|
||||||
|
// This TypeInfo class retains the ownership of the returned pointer.
|
||||||
|
virtual const google::protobuf::Type* GetType(StringPiece type_url) = 0; |
||||||
|
|
||||||
|
// Resolves a type url for an enum. Returns NULL if the type url is
|
||||||
|
// invalid or the type cannot be found.
|
||||||
|
//
|
||||||
|
// This TypeInfo class retains the ownership of the returned pointer.
|
||||||
|
virtual const google::protobuf::Enum* GetEnum(StringPiece type_url) = 0; |
||||||
|
|
||||||
|
// Looks up a field in the specified type given a CamelCase name.
|
||||||
|
virtual const google::protobuf::Field* FindField( |
||||||
|
const google::protobuf::Type* type, StringPiece camel_case_name) = 0; |
||||||
|
|
||||||
|
static TypeInfo* NewTypeInfo(TypeResolver* type_resolver); |
||||||
|
|
||||||
|
private: |
||||||
|
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(TypeInfo); |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
|
||||||
|
} // namespace google
|
||||||
|
#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_TYPE_INFO_H__
|
@ -0,0 +1,130 @@ |
|||||||
|
// 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/util/internal/type_info_test_helper.h> |
||||||
|
|
||||||
|
#include <memory> |
||||||
|
#ifndef _SHARED_PTR_H |
||||||
|
#include <google/protobuf/stubs/shared_ptr.h> |
||||||
|
#endif |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
#include <google/protobuf/stubs/common.h> |
||||||
|
#include <google/protobuf/descriptor.h> |
||||||
|
#include <google/protobuf/util/internal/default_value_objectwriter.h> |
||||||
|
#include <google/protobuf/util/internal/type_info.h> |
||||||
|
#include <google/protobuf/util/internal/constants.h> |
||||||
|
#include <google/protobuf/util/internal/protostream_objectsource.h> |
||||||
|
#include <google/protobuf/util/internal/protostream_objectwriter.h> |
||||||
|
#include <google/protobuf/util/type_resolver.h> |
||||||
|
#include <google/protobuf/util/type_resolver_util.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace converter { |
||||||
|
namespace testing { |
||||||
|
|
||||||
|
|
||||||
|
void TypeInfoTestHelper::ResetTypeInfo( |
||||||
|
const vector<const Descriptor*>& descriptors) { |
||||||
|
switch (type_) { |
||||||
|
case USE_TYPE_RESOLVER: { |
||||||
|
const DescriptorPool* pool = descriptors[0]->file()->pool(); |
||||||
|
for (int i = 1; i < descriptors.size(); ++i) { |
||||||
|
GOOGLE_CHECK(pool == descriptors[i]->file()->pool()) |
||||||
|
<< "Descriptors from different pools are not supported."; |
||||||
|
} |
||||||
|
type_resolver_.reset( |
||||||
|
NewTypeResolverForDescriptorPool(kTypeServiceBaseUrl, pool)); |
||||||
|
typeinfo_.reset(TypeInfo::NewTypeInfo(type_resolver_.get())); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
GOOGLE_LOG(FATAL) << "Can not reach here."; |
||||||
|
} |
||||||
|
|
||||||
|
void TypeInfoTestHelper::ResetTypeInfo(const Descriptor* descriptor) { |
||||||
|
vector<const Descriptor*> descriptors; |
||||||
|
descriptors.push_back(descriptor); |
||||||
|
ResetTypeInfo(descriptors); |
||||||
|
} |
||||||
|
|
||||||
|
void TypeInfoTestHelper::ResetTypeInfo(const Descriptor* descriptor1, |
||||||
|
const Descriptor* descriptor2) { |
||||||
|
vector<const Descriptor*> descriptors; |
||||||
|
descriptors.push_back(descriptor1); |
||||||
|
descriptors.push_back(descriptor2); |
||||||
|
ResetTypeInfo(descriptors); |
||||||
|
} |
||||||
|
|
||||||
|
TypeInfo* TypeInfoTestHelper::GetTypeInfo() { return typeinfo_.get(); } |
||||||
|
|
||||||
|
ProtoStreamObjectSource* TypeInfoTestHelper::NewProtoSource( |
||||||
|
io::CodedInputStream* coded_input, const string& type_url) { |
||||||
|
const google::protobuf::Type* type = typeinfo_->GetType(type_url); |
||||||
|
switch (type_) { |
||||||
|
case USE_TYPE_RESOLVER: { |
||||||
|
return new ProtoStreamObjectSource(coded_input, type_resolver_.get(), |
||||||
|
*type); |
||||||
|
} |
||||||
|
} |
||||||
|
GOOGLE_LOG(FATAL) << "Can not reach here."; |
||||||
|
} |
||||||
|
|
||||||
|
ProtoStreamObjectWriter* TypeInfoTestHelper::NewProtoWriter( |
||||||
|
const string& type_url, strings::ByteSink* output, |
||||||
|
ErrorListener* listener) { |
||||||
|
const google::protobuf::Type* type = typeinfo_->GetType(type_url); |
||||||
|
switch (type_) { |
||||||
|
case USE_TYPE_RESOLVER: { |
||||||
|
return new ProtoStreamObjectWriter(type_resolver_.get(), *type, output, |
||||||
|
listener); |
||||||
|
} |
||||||
|
} |
||||||
|
GOOGLE_LOG(FATAL) << "Can not reach here."; |
||||||
|
} |
||||||
|
|
||||||
|
DefaultValueObjectWriter* TypeInfoTestHelper::NewDefaultValueWriter( |
||||||
|
const string& type_url, ObjectWriter* writer) { |
||||||
|
const google::protobuf::Type* type = typeinfo_->GetType(type_url); |
||||||
|
switch (type_) { |
||||||
|
case USE_TYPE_RESOLVER: { |
||||||
|
return new DefaultValueObjectWriter(type_resolver_.get(), *type, writer); |
||||||
|
} |
||||||
|
} |
||||||
|
GOOGLE_LOG(FATAL) << "Can not reach here."; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace testing
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
} // namespace google
|
@ -0,0 +1,98 @@ |
|||||||
|
// 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_UTIL_CONVERTER_TYPE_INFO_TEST_HELPER_H__ |
||||||
|
#define GOOGLE_PROTOBUF_UTIL_CONVERTER_TYPE_INFO_TEST_HELPER_H__ |
||||||
|
|
||||||
|
#include <memory> |
||||||
|
#ifndef _SHARED_PTR_H |
||||||
|
#include <google/protobuf/stubs/shared_ptr.h> |
||||||
|
#endif |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
#include <google/protobuf/io/coded_stream.h> |
||||||
|
#include <google/protobuf/descriptor.h> |
||||||
|
#include <google/protobuf/util/internal/type_info.h> |
||||||
|
#include <google/protobuf/util/internal/default_value_objectwriter.h> |
||||||
|
#include <google/protobuf/util/internal/protostream_objectsource.h> |
||||||
|
#include <google/protobuf/util/internal/protostream_objectwriter.h> |
||||||
|
#include <google/protobuf/util/type_resolver.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace converter { |
||||||
|
namespace testing { |
||||||
|
|
||||||
|
enum TypeInfoSource { |
||||||
|
USE_TYPE_RESOLVER, |
||||||
|
}; |
||||||
|
|
||||||
|
// In the unit-tests we want to test two scenarios: one with type info from
|
||||||
|
// ServiceTypeInfo, the other with type info from TypeResolver. This class
|
||||||
|
// wraps the detail of where the type info is from and provides the same
|
||||||
|
// interface so the same unit-test code can test both scenarios.
|
||||||
|
class TypeInfoTestHelper { |
||||||
|
public: |
||||||
|
explicit TypeInfoTestHelper(TypeInfoSource type) : type_(type) {} |
||||||
|
|
||||||
|
// Creates a TypeInfo object for the given set of descriptors.
|
||||||
|
void ResetTypeInfo(const vector<const Descriptor*>& descriptors); |
||||||
|
|
||||||
|
// Convinent overloads.
|
||||||
|
void ResetTypeInfo(const Descriptor* descriptor); |
||||||
|
void ResetTypeInfo(const Descriptor* descriptor1, |
||||||
|
const Descriptor* descriptor2); |
||||||
|
|
||||||
|
// Returns the TypeInfo created after ResetTypeInfo.
|
||||||
|
TypeInfo* GetTypeInfo(); |
||||||
|
|
||||||
|
ProtoStreamObjectSource* NewProtoSource(io::CodedInputStream* coded_input, |
||||||
|
const string& type_url); |
||||||
|
|
||||||
|
ProtoStreamObjectWriter* NewProtoWriter(const string& type_url, |
||||||
|
strings::ByteSink* output, |
||||||
|
ErrorListener* listener); |
||||||
|
|
||||||
|
DefaultValueObjectWriter* NewDefaultValueWriter(const string& type_url, |
||||||
|
ObjectWriter* writer); |
||||||
|
|
||||||
|
private: |
||||||
|
TypeInfoSource type_; |
||||||
|
google::protobuf::scoped_ptr<TypeInfo> typeinfo_; |
||||||
|
google::protobuf::scoped_ptr<TypeResolver> type_resolver_; |
||||||
|
}; |
||||||
|
} // namespace testing
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
|
||||||
|
} // namespace google
|
||||||
|
#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_TYPE_INFO_TEST_HELPER_H__
|
@ -0,0 +1,332 @@ |
|||||||
|
// 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/util/internal/utility.h> |
||||||
|
|
||||||
|
#include <cmath> |
||||||
|
#include <algorithm> |
||||||
|
#include <utility> |
||||||
|
|
||||||
|
#include <google/protobuf/stubs/common.h> |
||||||
|
#include <google/protobuf/wrappers.pb.h> |
||||||
|
#include <google/protobuf/descriptor.pb.h> |
||||||
|
#include <google/protobuf/descriptor.h> |
||||||
|
#include <google/protobuf/util/internal/constants.h> |
||||||
|
#include <google/protobuf/stubs/strutil.h> |
||||||
|
#include <google/protobuf/stubs/map_util.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace converter { |
||||||
|
|
||||||
|
namespace { |
||||||
|
const StringPiece SkipWhiteSpace(StringPiece str) { |
||||||
|
StringPiece::size_type i; |
||||||
|
for (i = 0; i < str.size() && isspace(str[i]); ++i) {} |
||||||
|
GOOGLE_DCHECK(i == str.size() || !isspace(str[i])); |
||||||
|
return StringPiece(str, i); |
||||||
|
} |
||||||
|
} // namespace
|
||||||
|
|
||||||
|
bool GetBoolOptionOrDefault( |
||||||
|
const google::protobuf::RepeatedPtrField<google::protobuf::Option>& options, |
||||||
|
const string& option_name, bool default_value) { |
||||||
|
const google::protobuf::Option* opt = FindOptionOrNull(options, option_name); |
||||||
|
if (opt == NULL) { |
||||||
|
return default_value; |
||||||
|
} |
||||||
|
return GetBoolFromAny(opt->value()); |
||||||
|
} |
||||||
|
|
||||||
|
int64 GetInt64OptionOrDefault( |
||||||
|
const google::protobuf::RepeatedPtrField<google::protobuf::Option>& options, |
||||||
|
const string& option_name, int64 default_value) { |
||||||
|
const google::protobuf::Option* opt = FindOptionOrNull(options, option_name); |
||||||
|
if (opt == NULL) { |
||||||
|
return default_value; |
||||||
|
} |
||||||
|
return GetInt64FromAny(opt->value()); |
||||||
|
} |
||||||
|
|
||||||
|
double GetDoubleOptionOrDefault( |
||||||
|
const google::protobuf::RepeatedPtrField<google::protobuf::Option>& options, |
||||||
|
const string& option_name, double default_value) { |
||||||
|
const google::protobuf::Option* opt = FindOptionOrNull(options, option_name); |
||||||
|
if (opt == NULL) { |
||||||
|
return default_value; |
||||||
|
} |
||||||
|
return GetDoubleFromAny(opt->value()); |
||||||
|
} |
||||||
|
|
||||||
|
string GetStringOptionOrDefault( |
||||||
|
const google::protobuf::RepeatedPtrField<google::protobuf::Option>& options, |
||||||
|
const string& option_name, const string& default_value) { |
||||||
|
const google::protobuf::Option* opt = FindOptionOrNull(options, option_name); |
||||||
|
if (opt == NULL) { |
||||||
|
return default_value; |
||||||
|
} |
||||||
|
return GetStringFromAny(opt->value()); |
||||||
|
} |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
void ParseFromAny(const string& data, T* result) { |
||||||
|
result->ParseFromString(data); |
||||||
|
} |
||||||
|
|
||||||
|
// Returns a boolean value contained in Any type.
|
||||||
|
// TODO(skarvaje): Add type checking & error messages here.
|
||||||
|
bool GetBoolFromAny(const google::protobuf::Any& any) { |
||||||
|
google::protobuf::BoolValue b; |
||||||
|
ParseFromAny(any.value(), &b); |
||||||
|
return b.value(); |
||||||
|
} |
||||||
|
|
||||||
|
int64 GetInt64FromAny(const google::protobuf::Any& any) { |
||||||
|
google::protobuf::Int64Value i; |
||||||
|
ParseFromAny(any.value(), &i); |
||||||
|
return i.value(); |
||||||
|
} |
||||||
|
|
||||||
|
double GetDoubleFromAny(const google::protobuf::Any& any) { |
||||||
|
google::protobuf::DoubleValue i; |
||||||
|
ParseFromAny(any.value(), &i); |
||||||
|
return i.value(); |
||||||
|
} |
||||||
|
|
||||||
|
string GetStringFromAny(const google::protobuf::Any& any) { |
||||||
|
google::protobuf::StringValue s; |
||||||
|
ParseFromAny(any.value(), &s); |
||||||
|
return s.value(); |
||||||
|
} |
||||||
|
|
||||||
|
const StringPiece GetTypeWithoutUrl(StringPiece type_url) { |
||||||
|
size_t idx = type_url.rfind('/'); |
||||||
|
return type_url.substr(idx + 1); |
||||||
|
} |
||||||
|
|
||||||
|
const string GetFullTypeWithUrl(StringPiece simple_type) { |
||||||
|
return StrCat(kTypeServiceBaseUrl, "/", simple_type); |
||||||
|
} |
||||||
|
|
||||||
|
const google::protobuf::Option* FindOptionOrNull( |
||||||
|
const google::protobuf::RepeatedPtrField<google::protobuf::Option>& options, |
||||||
|
const string& option_name) { |
||||||
|
for (int i = 0; i < options.size(); ++i) { |
||||||
|
const google::protobuf::Option& opt = options.Get(i); |
||||||
|
if (opt.name() == option_name) { |
||||||
|
return &opt; |
||||||
|
} |
||||||
|
} |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
const google::protobuf::Field* FindFieldInTypeOrNull( |
||||||
|
const google::protobuf::Type* type, StringPiece field_name) { |
||||||
|
if (type != NULL) { |
||||||
|
for (int i = 0; i < type->fields_size(); ++i) { |
||||||
|
const google::protobuf::Field& field = type->fields(i); |
||||||
|
if (field.name() == field_name) { |
||||||
|
return &field; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
const google::protobuf::EnumValue* FindEnumValueByNameOrNull( |
||||||
|
const google::protobuf::Enum* enum_type, StringPiece enum_name) { |
||||||
|
if (enum_type != NULL) { |
||||||
|
for (int i = 0; i < enum_type->enumvalue_size(); ++i) { |
||||||
|
const google::protobuf::EnumValue& enum_value = enum_type->enumvalue(i); |
||||||
|
if (enum_value.name() == enum_name) { |
||||||
|
return &enum_value; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
const google::protobuf::EnumValue* FindEnumValueByNumberOrNull( |
||||||
|
const google::protobuf::Enum* enum_type, int32 value) { |
||||||
|
if (enum_type != NULL) { |
||||||
|
for (int i = 0; i < enum_type->enumvalue_size(); ++i) { |
||||||
|
const google::protobuf::EnumValue& enum_value = enum_type->enumvalue(i); |
||||||
|
if (enum_value.number() == value) { |
||||||
|
return &enum_value; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
string ToCamelCase(const StringPiece input) { |
||||||
|
bool capitalize_next = false; |
||||||
|
bool was_cap = true; |
||||||
|
bool is_cap = false; |
||||||
|
bool first_word = true; |
||||||
|
string result; |
||||||
|
result.reserve(input.size()); |
||||||
|
|
||||||
|
for (size_t i = 0; i < input.size(); ++i, was_cap = is_cap) { |
||||||
|
is_cap = ascii_isupper(input[i]); |
||||||
|
if (input[i] == '_') { |
||||||
|
capitalize_next = true; |
||||||
|
if (!result.empty()) first_word = false; |
||||||
|
continue; |
||||||
|
} else if (first_word) { |
||||||
|
// Consider when the current character B is capitalized,
|
||||||
|
// first word ends when:
|
||||||
|
// 1) following a lowercase: "...aB..."
|
||||||
|
// 2) followed by a lowercase: "...ABc..."
|
||||||
|
if (!result.empty() && is_cap && |
||||||
|
(!was_cap || (i + 1 < input.size() && ascii_islower(input[i + 1])))) { |
||||||
|
first_word = false; |
||||||
|
} else { |
||||||
|
result.push_back(ascii_tolower(input[i])); |
||||||
|
continue; |
||||||
|
} |
||||||
|
} else if (capitalize_next) { |
||||||
|
capitalize_next = false; |
||||||
|
if (ascii_islower(input[i])) { |
||||||
|
result.push_back(ascii_toupper(input[i])); |
||||||
|
continue; |
||||||
|
} |
||||||
|
} |
||||||
|
result.push_back(input[i]); |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
string ToSnakeCase(StringPiece input) { |
||||||
|
bool was_not_underscore = false; // Initialize to false for case 1 (below)
|
||||||
|
bool was_not_cap = false; |
||||||
|
string result; |
||||||
|
result.reserve(input.size() << 1); |
||||||
|
|
||||||
|
for (size_t i = 0; i < input.size(); ++i) { |
||||||
|
if (ascii_isupper(input[i])) { |
||||||
|
// Consider when the current character B is capitalized:
|
||||||
|
// 1) At beginning of input: "B..." => "b..."
|
||||||
|
// (e.g. "Biscuit" => "biscuit")
|
||||||
|
// 2) Following a lowercase: "...aB..." => "...a_b..."
|
||||||
|
// (e.g. "gBike" => "g_bike")
|
||||||
|
// 3) At the end of input: "...AB" => "...ab"
|
||||||
|
// (e.g. "GoogleLAB" => "google_lab")
|
||||||
|
// 4) Followed by a lowercase: "...ABc..." => "...a_bc..."
|
||||||
|
// (e.g. "GBike" => "g_bike")
|
||||||
|
if (was_not_underscore && // case 1 out
|
||||||
|
(was_not_cap || // case 2 in, case 3 out
|
||||||
|
(i + 1 < input.size() && // case 3 out
|
||||||
|
ascii_islower(input[i + 1])))) { // case 4 in
|
||||||
|
// We add an underscore for case 2 and case 4.
|
||||||
|
result.push_back('_'); |
||||||
|
} |
||||||
|
result.push_back(ascii_tolower(input[i])); |
||||||
|
was_not_underscore = true; |
||||||
|
was_not_cap = false; |
||||||
|
} else { |
||||||
|
result.push_back(input[i]); |
||||||
|
was_not_underscore = input[i] != '_'; |
||||||
|
was_not_cap = true; |
||||||
|
} |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
set<string>* well_known_types_ = NULL; |
||||||
|
GOOGLE_PROTOBUF_DECLARE_ONCE(well_known_types_init_); |
||||||
|
const char* well_known_types_name_array_[] = { |
||||||
|
"google.protobuf.Timestamp", "google.protobuf.Duration", |
||||||
|
"google.protobuf.DoubleValue", "google.protobuf.FloatValue", |
||||||
|
"google.protobuf.Int64Value", "google.protobuf.UInt64Value", |
||||||
|
"google.protobuf.Int32Value", "google.protobuf.UInt32Value", |
||||||
|
"google.protobuf.BoolValue", "google.protobuf.StringValue", |
||||||
|
"google.protobuf.BytesValue", "google.protobuf.FieldMask"}; |
||||||
|
|
||||||
|
void DeleteWellKnownTypes() { delete well_known_types_; } |
||||||
|
|
||||||
|
void InitWellKnownTypes() { |
||||||
|
well_known_types_ = new set<string>; |
||||||
|
for (int i = 0; i < GOOGLE_ARRAYSIZE(well_known_types_name_array_); ++i) { |
||||||
|
well_known_types_->insert(well_known_types_name_array_[i]); |
||||||
|
} |
||||||
|
google::protobuf::internal::OnShutdown(&DeleteWellKnownTypes); |
||||||
|
} |
||||||
|
|
||||||
|
bool IsWellKnownType(const string& type_name) { |
||||||
|
InitWellKnownTypes(); |
||||||
|
return ContainsKey(*well_known_types_, type_name); |
||||||
|
} |
||||||
|
|
||||||
|
bool IsValidBoolString(const string& bool_string) { |
||||||
|
return bool_string == "true" || bool_string == "false" || |
||||||
|
bool_string == "1" || bool_string == "0"; |
||||||
|
} |
||||||
|
|
||||||
|
bool IsMap(const google::protobuf::Field& field, |
||||||
|
const google::protobuf::Type& type) { |
||||||
|
return (field.cardinality() == |
||||||
|
google::protobuf::Field_Cardinality_CARDINALITY_REPEATED && |
||||||
|
GetBoolOptionOrDefault(type.options(), |
||||||
|
"google.protobuf.MessageOptions.map_entry", false)); |
||||||
|
} |
||||||
|
|
||||||
|
string DoubleAsString(double value) { |
||||||
|
if (value == std::numeric_limits<double>::infinity()) return "Infinity"; |
||||||
|
if (value == -std::numeric_limits<double>::infinity()) return "-Infinity"; |
||||||
|
if (isnan(value)) return "NaN"; |
||||||
|
|
||||||
|
return SimpleDtoa(value); |
||||||
|
} |
||||||
|
|
||||||
|
string FloatAsString(float value) { |
||||||
|
if (isfinite(value)) return SimpleFtoa(value); |
||||||
|
return DoubleAsString(value); |
||||||
|
} |
||||||
|
|
||||||
|
bool SafeStrToFloat(StringPiece str, float *value) { |
||||||
|
double double_value; |
||||||
|
if (!safe_strtod(str, &double_value)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
*value = static_cast<float>(double_value); |
||||||
|
|
||||||
|
if ((*value == numeric_limits<float>::infinity()) || |
||||||
|
(*value == -numeric_limits<float>::infinity())) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
} // namespace google
|
@ -0,0 +1,187 @@ |
|||||||
|
// 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_UTIL_CONVERTER_UTILITY_H__ |
||||||
|
#define GOOGLE_PROTOBUF_UTIL_CONVERTER_UTILITY_H__ |
||||||
|
|
||||||
|
#include <memory> |
||||||
|
#ifndef _SHARED_PTR_H |
||||||
|
#include <google/protobuf/stubs/shared_ptr.h> |
||||||
|
#endif |
||||||
|
#include <string> |
||||||
|
#include <utility> |
||||||
|
|
||||||
|
#include <google/protobuf/stubs/common.h> |
||||||
|
#include <google/protobuf/type.pb.h> |
||||||
|
#include <google/protobuf/repeated_field.h> |
||||||
|
#include <google/protobuf/stubs/stringpiece.h> |
||||||
|
#include <google/protobuf/stubs/strutil.h> |
||||||
|
#include <google/protobuf/stubs/status.h> |
||||||
|
#include <google/protobuf/stubs/statusor.h> |
||||||
|
|
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
class Method; |
||||||
|
class Any; |
||||||
|
class Bool; |
||||||
|
class Option; |
||||||
|
class Field; |
||||||
|
class Type; |
||||||
|
class Enum; |
||||||
|
class EnumValue; |
||||||
|
} // namespace protobuf
|
||||||
|
|
||||||
|
|
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace converter { |
||||||
|
// Finds the tech option identified by option_name. Parses the boolean value and
|
||||||
|
// returns it.
|
||||||
|
// When the option with the given name is not found, default_value is returned.
|
||||||
|
bool GetBoolOptionOrDefault( |
||||||
|
const google::protobuf::RepeatedPtrField<google::protobuf::Option>& options, |
||||||
|
const string& option_name, bool default_value); |
||||||
|
|
||||||
|
// Returns int64 option value. If the option isn't found, returns the
|
||||||
|
// default_value.
|
||||||
|
int64 GetInt64OptionOrDefault( |
||||||
|
const google::protobuf::RepeatedPtrField<google::protobuf::Option>& options, |
||||||
|
const string& option_name, int64 default_value); |
||||||
|
|
||||||
|
// Returns double option value. If the option isn't found, returns the
|
||||||
|
// default_value.
|
||||||
|
double GetDoubleOptionOrDefault( |
||||||
|
const google::protobuf::RepeatedPtrField<google::protobuf::Option>& options, |
||||||
|
const string& option_name, double default_value); |
||||||
|
|
||||||
|
// Returns string option value. If the option isn't found, returns the
|
||||||
|
// default_value.
|
||||||
|
string GetStringOptionOrDefault( |
||||||
|
const google::protobuf::RepeatedPtrField<google::protobuf::Option>& options, |
||||||
|
const string& option_name, const string& default_value); |
||||||
|
|
||||||
|
// Returns a boolean value contained in Any type.
|
||||||
|
// TODO(skarvaje): Make these utilities dealing with Any types more generic,
|
||||||
|
// add more error checking and move to a more public/sharable location so others
|
||||||
|
// can use.
|
||||||
|
bool GetBoolFromAny(const google::protobuf::Any& any); |
||||||
|
|
||||||
|
// Returns int64 value contained in Any type.
|
||||||
|
int64 GetInt64FromAny(const google::protobuf::Any& any); |
||||||
|
|
||||||
|
// Returns double value contained in Any type.
|
||||||
|
double GetDoubleFromAny(const google::protobuf::Any& any); |
||||||
|
|
||||||
|
// Returns string value contained in Any type.
|
||||||
|
string GetStringFromAny(const google::protobuf::Any& any); |
||||||
|
|
||||||
|
// Returns the type string without the url prefix. e.g.: If the passed type is
|
||||||
|
// 'type.googleapis.com/tech.type.Bool', the returned value is 'tech.type.Bool'.
|
||||||
|
const StringPiece GetTypeWithoutUrl(StringPiece type_url); |
||||||
|
|
||||||
|
// Returns the simple_type with the base type url (kTypeServiceBaseUrl)
|
||||||
|
// prefixed.
|
||||||
|
//
|
||||||
|
// E.g:
|
||||||
|
// GetFullTypeWithUrl("google.protobuf.Timestamp") returns the string
|
||||||
|
// "type.googleapis.com/google.protobuf.Timestamp".
|
||||||
|
const string GetFullTypeWithUrl(StringPiece simple_type); |
||||||
|
|
||||||
|
// Finds and returns option identified by name and option_name within the
|
||||||
|
// provided map. Returns NULL if none found.
|
||||||
|
const google::protobuf::Option* FindOptionOrNull( |
||||||
|
const google::protobuf::RepeatedPtrField<google::protobuf::Option>& options, |
||||||
|
const string& option_name); |
||||||
|
|
||||||
|
// Finds and returns the field identified by field_name in the passed tech Type
|
||||||
|
// object. Returns NULL if none found.
|
||||||
|
const google::protobuf::Field* FindFieldInTypeOrNull( |
||||||
|
const google::protobuf::Type* type, StringPiece field_name); |
||||||
|
|
||||||
|
// Finds and returns the EnumValue identified by enum_name in the passed tech
|
||||||
|
// Enum object. Returns NULL if none found.
|
||||||
|
const google::protobuf::EnumValue* FindEnumValueByNameOrNull( |
||||||
|
const google::protobuf::Enum* enum_type, StringPiece enum_name); |
||||||
|
|
||||||
|
// Finds and returns the EnumValue identified by value in the passed tech
|
||||||
|
// Enum object. Returns NULL if none found.
|
||||||
|
const google::protobuf::EnumValue* FindEnumValueByNumberOrNull( |
||||||
|
const google::protobuf::Enum* enum_type, int32 value); |
||||||
|
|
||||||
|
// Converts input to camel-case and returns it.
|
||||||
|
// Tests are in wrappers/translator/snake2camel_objectwriter_test.cc
|
||||||
|
// TODO(skarvaje): Isolate tests for this function and put them in
|
||||||
|
// utility_test.cc
|
||||||
|
string ToCamelCase(const StringPiece input); |
||||||
|
|
||||||
|
// Converts input to snake_case and returns it.
|
||||||
|
string ToSnakeCase(StringPiece input); |
||||||
|
|
||||||
|
// Returns true if type_name represents a well-known type.
|
||||||
|
bool IsWellKnownType(const string& type_name); |
||||||
|
|
||||||
|
// Returns true if 'bool_string' represents a valid boolean value. Only "true",
|
||||||
|
// "false", "0" and "1" are allowed.
|
||||||
|
bool IsValidBoolString(const string& bool_string); |
||||||
|
|
||||||
|
// Returns true if "field" is a protobuf map field based on its type.
|
||||||
|
bool IsMap(const google::protobuf::Field& field, |
||||||
|
const google::protobuf::Type& type); |
||||||
|
|
||||||
|
// Infinity/NaN-aware conversion to string.
|
||||||
|
string DoubleAsString(double value); |
||||||
|
string FloatAsString(float value); |
||||||
|
|
||||||
|
// Convert from int32, int64, uint32, uint64, double or float to string.
|
||||||
|
template <typename T> |
||||||
|
string ValueAsString(T value) { |
||||||
|
return SimpleItoa(value); |
||||||
|
} |
||||||
|
|
||||||
|
template <> |
||||||
|
inline string ValueAsString(float value) { |
||||||
|
return FloatAsString(value); |
||||||
|
} |
||||||
|
|
||||||
|
template <> |
||||||
|
inline string ValueAsString(double value) { |
||||||
|
return DoubleAsString(value); |
||||||
|
} |
||||||
|
|
||||||
|
// Converts a string to float. Unlike safe_strtof, conversion will fail if the
|
||||||
|
// value fits into double but not float (e.g., DBL_MAX).
|
||||||
|
bool SafeStrToFloat(StringPiece str, float* value); |
||||||
|
} // namespace converter
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
|
||||||
|
} // namespace google
|
||||||
|
#endif // GOOGLE_PROTOBUF_UTIL_CONVERTER_UTILITY_H__
|
@ -0,0 +1,157 @@ |
|||||||
|
// 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. |
||||||
|
|
||||||
|
syntax = "proto3"; |
||||||
|
|
||||||
|
package proto3; |
||||||
|
|
||||||
|
|
||||||
|
import "google/protobuf/duration.proto"; |
||||||
|
import "google/protobuf/timestamp.proto"; |
||||||
|
import "google/protobuf/wrappers.proto"; |
||||||
|
import "google/protobuf/struct.proto"; |
||||||
|
import "google/protobuf/any.proto"; |
||||||
|
import "google/protobuf/field_mask.proto"; |
||||||
|
|
||||||
|
enum EnumType { |
||||||
|
FOO = 0; |
||||||
|
BAR = 1; |
||||||
|
} |
||||||
|
|
||||||
|
message MessageType { |
||||||
|
int32 value = 1; |
||||||
|
} |
||||||
|
|
||||||
|
message TestMessage { |
||||||
|
bool bool_value = 1; |
||||||
|
int32 int32_value = 2; |
||||||
|
int64 int64_value = 3; |
||||||
|
uint32 uint32_value = 4; |
||||||
|
uint64 uint64_value = 5; |
||||||
|
float float_value = 6; |
||||||
|
double double_value = 7; |
||||||
|
string string_value = 8; |
||||||
|
bytes bytes_value = 9; |
||||||
|
EnumType enum_value = 10; |
||||||
|
MessageType message_value = 11; |
||||||
|
|
||||||
|
repeated bool repeated_bool_value = 21; |
||||||
|
repeated int32 repeated_int32_value = 22; |
||||||
|
repeated int64 repeated_int64_value = 23; |
||||||
|
repeated uint32 repeated_uint32_value = 24; |
||||||
|
repeated uint64 repeated_uint64_value = 25; |
||||||
|
repeated float repeated_float_value = 26; |
||||||
|
repeated double repeated_double_value = 27; |
||||||
|
repeated string repeated_string_value = 28; |
||||||
|
repeated bytes repeated_bytes_value = 29; |
||||||
|
repeated EnumType repeated_enum_value = 30; |
||||||
|
repeated MessageType repeated_message_value = 31; |
||||||
|
} |
||||||
|
|
||||||
|
message TestOneof { |
||||||
|
// In JSON format oneof fields behave mostly the same as optional |
||||||
|
// fields except that: |
||||||
|
// 1. Oneof fields have field presence information and will be |
||||||
|
// printed if it's set no matter whether it's the default value. |
||||||
|
// 2. Multiple oneof fields in the same oneof cannot appear at the |
||||||
|
// same time in the input. |
||||||
|
oneof oneof_value { |
||||||
|
int32 oneof_int32_value = 1; |
||||||
|
string oneof_string_value = 2; |
||||||
|
bytes oneof_bytes_value = 3; |
||||||
|
EnumType oneof_enum_value = 4; |
||||||
|
MessageType oneof_message_value = 5; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
message TestMap { |
||||||
|
map<bool, int32> bool_map = 1; |
||||||
|
map<int32, int32> int32_map = 2; |
||||||
|
map<int64, int32> int64_map = 3; |
||||||
|
map<uint32, int32> uint32_map = 4; |
||||||
|
map<uint64, int32> uint64_map = 5; |
||||||
|
map<string, int32> string_map = 6; |
||||||
|
} |
||||||
|
|
||||||
|
message TestWrapper { |
||||||
|
google.protobuf.BoolValue bool_value = 1; |
||||||
|
google.protobuf.Int32Value int32_value = 2; |
||||||
|
google.protobuf.Int64Value int64_value = 3; |
||||||
|
google.protobuf.UInt32Value uint32_value = 4; |
||||||
|
google.protobuf.UInt64Value uint64_value = 5; |
||||||
|
google.protobuf.FloatValue float_value = 6; |
||||||
|
google.protobuf.DoubleValue double_value = 7; |
||||||
|
google.protobuf.StringValue string_value = 8; |
||||||
|
google.protobuf.BytesValue bytes_value = 9; |
||||||
|
|
||||||
|
repeated google.protobuf.BoolValue repeated_bool_value = 11; |
||||||
|
repeated google.protobuf.Int32Value repeated_int32_value = 12; |
||||||
|
repeated google.protobuf.Int64Value repeated_int64_value = 13; |
||||||
|
repeated google.protobuf.UInt32Value repeated_uint32_value = 14; |
||||||
|
repeated google.protobuf.UInt64Value repeated_uint64_value = 15; |
||||||
|
repeated google.protobuf.FloatValue repeated_float_value = 16; |
||||||
|
repeated google.protobuf.DoubleValue repeated_double_value = 17; |
||||||
|
repeated google.protobuf.StringValue repeated_string_value = 18; |
||||||
|
repeated google.protobuf.BytesValue repeated_bytes_value = 19; |
||||||
|
} |
||||||
|
|
||||||
|
message TestTimestamp { |
||||||
|
google.protobuf.Timestamp value = 1; |
||||||
|
repeated google.protobuf.Timestamp repeated_value = 2; |
||||||
|
} |
||||||
|
|
||||||
|
message TestDuration { |
||||||
|
google.protobuf.Duration value = 1; |
||||||
|
repeated google.protobuf.Duration repeated_value = 2; |
||||||
|
} |
||||||
|
|
||||||
|
message TestFieldMask { |
||||||
|
google.protobuf.FieldMask value = 1; |
||||||
|
} |
||||||
|
|
||||||
|
message TestStruct { |
||||||
|
google.protobuf.Struct value = 1; |
||||||
|
repeated google.protobuf.Struct repeated_value = 2; |
||||||
|
} |
||||||
|
|
||||||
|
message TestAny { |
||||||
|
google.protobuf.Any value = 1; |
||||||
|
repeated google.protobuf.Any repeated_value = 2; |
||||||
|
} |
||||||
|
|
||||||
|
message TestValue { |
||||||
|
google.protobuf.Value value = 1; |
||||||
|
repeated google.protobuf.Value repeated_value = 2; |
||||||
|
} |
||||||
|
|
||||||
|
message TestListValue { |
||||||
|
google.protobuf.ListValue value = 1; |
||||||
|
repeated google.protobuf.ListValue repeated_value = 2; |
||||||
|
} |
@ -0,0 +1,142 @@ |
|||||||
|
// 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/util/json_util.h> |
||||||
|
|
||||||
|
#include <google/protobuf/stubs/common.h> |
||||||
|
#include <google/protobuf/io/coded_stream.h> |
||||||
|
#include <google/protobuf/io/zero_copy_stream.h> |
||||||
|
#include <google/protobuf/util/internal/default_value_objectwriter.h> |
||||||
|
#include <google/protobuf/util/internal/snake2camel_objectwriter.h> |
||||||
|
#include <google/protobuf/util/internal/error_listener.h> |
||||||
|
#include <google/protobuf/util/internal/json_objectwriter.h> |
||||||
|
#include <google/protobuf/util/internal/json_stream_parser.h> |
||||||
|
#include <google/protobuf/util/internal/protostream_objectsource.h> |
||||||
|
#include <google/protobuf/util/internal/protostream_objectwriter.h> |
||||||
|
#include <google/protobuf/util/type_resolver.h> |
||||||
|
#include <google/protobuf/util/type_resolver_util.h> |
||||||
|
#include <google/protobuf/stubs/bytestream.h> |
||||||
|
#include <google/protobuf/stubs/status_macros.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
|
||||||
|
namespace internal { |
||||||
|
void ZeroCopyStreamByteSink::Append(const char* bytes, size_t len) { |
||||||
|
while (len > 0) { |
||||||
|
void* buffer; |
||||||
|
int length; |
||||||
|
if (!stream_->Next(&buffer, &length)) { |
||||||
|
// There isn't a way for ByteSink to report errors.
|
||||||
|
return; |
||||||
|
} |
||||||
|
if (len < length) { |
||||||
|
memcpy(buffer, bytes, len); |
||||||
|
stream_->BackUp(length - len); |
||||||
|
break; |
||||||
|
} else { |
||||||
|
memcpy(buffer, bytes, length); |
||||||
|
bytes += length; |
||||||
|
len -= length; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
util::Status BinaryToJsonStream(TypeResolver* resolver, |
||||||
|
const string& type_url, |
||||||
|
io::ZeroCopyInputStream* binary_input, |
||||||
|
io::ZeroCopyOutputStream* json_output, |
||||||
|
const JsonOptions& options) { |
||||||
|
io::CodedInputStream in_stream(binary_input); |
||||||
|
google::protobuf::Type type; |
||||||
|
RETURN_IF_ERROR(resolver->ResolveMessageType(type_url, &type)); |
||||||
|
converter::ProtoStreamObjectSource proto_source(&in_stream, resolver, type); |
||||||
|
io::CodedOutputStream out_stream(json_output); |
||||||
|
converter::JsonObjectWriter json_writer(options.add_whitespace ? " " : "", |
||||||
|
&out_stream); |
||||||
|
converter::Snake2CamelObjectWriter snake2camel_writer(&json_writer); |
||||||
|
if (options.always_print_primitive_fields) { |
||||||
|
converter::DefaultValueObjectWriter default_value_writer( |
||||||
|
resolver, type, &snake2camel_writer); |
||||||
|
return proto_source.WriteTo(&default_value_writer); |
||||||
|
} else { |
||||||
|
return proto_source.WriteTo(&snake2camel_writer); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
util::Status BinaryToJsonString(TypeResolver* resolver, |
||||||
|
const string& type_url, |
||||||
|
const string& binary_input, |
||||||
|
string* json_output, |
||||||
|
const JsonOptions& options) { |
||||||
|
io::ArrayInputStream input_stream(binary_input.data(), binary_input.size()); |
||||||
|
io::StringOutputStream output_stream(json_output); |
||||||
|
return BinaryToJsonStream(resolver, type_url, &input_stream, &output_stream, |
||||||
|
options); |
||||||
|
} |
||||||
|
|
||||||
|
util::Status JsonToBinaryStream(TypeResolver* resolver, |
||||||
|
const string& type_url, |
||||||
|
io::ZeroCopyInputStream* json_input, |
||||||
|
io::ZeroCopyOutputStream* binary_output) { |
||||||
|
google::protobuf::Type type; |
||||||
|
RETURN_IF_ERROR(resolver->ResolveMessageType(type_url, &type)); |
||||||
|
internal::ZeroCopyStreamByteSink sink(binary_output); |
||||||
|
converter::NoopErrorListener listener; |
||||||
|
converter::ProtoStreamObjectWriter proto_writer(resolver, type, &sink, |
||||||
|
&listener); |
||||||
|
|
||||||
|
converter::JsonStreamParser parser(&proto_writer); |
||||||
|
const void* buffer; |
||||||
|
int length; |
||||||
|
while (json_input->Next(&buffer, &length)) { |
||||||
|
if (length == 0) continue; |
||||||
|
RETURN_IF_ERROR( |
||||||
|
parser.Parse(StringPiece(static_cast<const char*>(buffer), length))); |
||||||
|
} |
||||||
|
RETURN_IF_ERROR(parser.FinishParse()); |
||||||
|
|
||||||
|
return util::Status::OK; |
||||||
|
} |
||||||
|
|
||||||
|
util::Status JsonToBinaryString(TypeResolver* resolver, |
||||||
|
const string& type_url, |
||||||
|
const string& json_input, |
||||||
|
string* binary_output) { |
||||||
|
io::ArrayInputStream input_stream(json_input.data(), json_input.size()); |
||||||
|
io::StringOutputStream output_stream(binary_output); |
||||||
|
return JsonToBinaryStream(resolver, type_url, &input_stream, &output_stream); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
} // namespace google
|
@ -0,0 +1,132 @@ |
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// Utility functions to convert between protobuf binary format and proto3 JSON
|
||||||
|
// format.
|
||||||
|
#ifndef GOOGLE_PROTOBUF_UTIL_JSON_UTIL_H__ |
||||||
|
#define GOOGLE_PROTOBUF_UTIL_JSON_UTIL_H__ |
||||||
|
|
||||||
|
#include <google/protobuf/util/type_resolver.h> |
||||||
|
#include <google/protobuf/stubs/bytestream.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace io { |
||||||
|
class ZeroCopyInputStream; |
||||||
|
class ZeroCopyOutputStream; |
||||||
|
} // namespace io
|
||||||
|
namespace util { |
||||||
|
|
||||||
|
struct JsonOptions { |
||||||
|
// Whether to add spaces, line breaks and indentation to make the JSON output
|
||||||
|
// easy to read.
|
||||||
|
bool add_whitespace; |
||||||
|
// Whether to always print primitive fields. By default primitive fields with
|
||||||
|
// default values will be omitted in JSON joutput. For example, an int32 field
|
||||||
|
// set to 0 will be omitted. Set this flag to true will override the default
|
||||||
|
// behavior and print primitive fields regardless of their values.
|
||||||
|
bool always_print_primitive_fields; |
||||||
|
|
||||||
|
JsonOptions() : add_whitespace(false), |
||||||
|
always_print_primitive_fields(false) { |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
// Converts protobuf binary data to JSON.
|
||||||
|
// The conversion will fail if:
|
||||||
|
// 1. TypeResolver fails to resolve a type.
|
||||||
|
// 2. input is not valid protobuf wire format, or conflicts with the type
|
||||||
|
// information returned by TypeResolver.
|
||||||
|
// Note that unknown fields will be discarded silently.
|
||||||
|
util::Status BinaryToJsonStream(TypeResolver* resolver, |
||||||
|
const string& type_url, |
||||||
|
io::ZeroCopyInputStream* binary_input, |
||||||
|
io::ZeroCopyOutputStream* json_output, |
||||||
|
const JsonOptions& options); |
||||||
|
|
||||||
|
inline util::Status BinaryToJsonStream( |
||||||
|
TypeResolver* resolver, const string& type_url, |
||||||
|
io::ZeroCopyInputStream* binary_input, |
||||||
|
io::ZeroCopyOutputStream* json_output) { |
||||||
|
return BinaryToJsonStream(resolver, type_url, binary_input, json_output, |
||||||
|
JsonOptions()); |
||||||
|
} |
||||||
|
|
||||||
|
util::Status BinaryToJsonString(TypeResolver* resolver, |
||||||
|
const string& type_url, |
||||||
|
const string& binary_input, |
||||||
|
string* json_output, |
||||||
|
const JsonOptions& options); |
||||||
|
|
||||||
|
inline util::Status BinaryToJsonString(TypeResolver* resolver, |
||||||
|
const string& type_url, |
||||||
|
const string& binary_input, |
||||||
|
string* json_output) { |
||||||
|
return BinaryToJsonString(resolver, type_url, binary_input, json_output, |
||||||
|
JsonOptions()); |
||||||
|
} |
||||||
|
|
||||||
|
// Converts JSON data to protobuf binary format.
|
||||||
|
// The conversion will fail if:
|
||||||
|
// 1. TypeResolver fails to resolve a type.
|
||||||
|
// 2. input is not valid JSON format, or conflicts with the type
|
||||||
|
// information returned by TypeResolver.
|
||||||
|
// 3. input has unknown fields.
|
||||||
|
util::Status JsonToBinaryStream(TypeResolver* resolver, |
||||||
|
const string& type_url, |
||||||
|
io::ZeroCopyInputStream* json_input, |
||||||
|
io::ZeroCopyOutputStream* binary_output); |
||||||
|
|
||||||
|
util::Status JsonToBinaryString(TypeResolver* resolver, |
||||||
|
const string& type_url, |
||||||
|
const string& json_input, |
||||||
|
string* binary_output); |
||||||
|
|
||||||
|
namespace internal { |
||||||
|
// Internal helper class. Put in the header so we can write unit-tests for it.
|
||||||
|
class LIBPROTOBUF_EXPORT ZeroCopyStreamByteSink : public strings::ByteSink { |
||||||
|
public: |
||||||
|
explicit ZeroCopyStreamByteSink(io::ZeroCopyOutputStream* stream) |
||||||
|
: stream_(stream) {} |
||||||
|
|
||||||
|
virtual void Append(const char* bytes, size_t len); |
||||||
|
|
||||||
|
private: |
||||||
|
io::ZeroCopyOutputStream* stream_; |
||||||
|
|
||||||
|
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ZeroCopyStreamByteSink); |
||||||
|
}; |
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
|
||||||
|
} // namespace google
|
||||||
|
#endif // GOOGLE_PROTOBUF_UTIL_JSON_UTIL_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.
|
||||||
|
|
||||||
|
#include <google/protobuf/util/json_util.h> |
||||||
|
|
||||||
|
#include <list> |
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include <google/protobuf/io/zero_copy_stream.h> |
||||||
|
#include <google/protobuf/util/json_format_proto3.pb.h> |
||||||
|
#include <google/protobuf/util/type_resolver.h> |
||||||
|
#include <google/protobuf/util/type_resolver_util.h> |
||||||
|
#include <gtest/gtest.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace { |
||||||
|
|
||||||
|
using proto3::FOO; |
||||||
|
using proto3::BAR; |
||||||
|
using proto3::TestMessage; |
||||||
|
|
||||||
|
static const char kTypeUrlPrefix[] = "type.googleapis.com"; |
||||||
|
|
||||||
|
static string GetTypeUrl(const Descriptor* message) { |
||||||
|
return string(kTypeUrlPrefix) + "/" + message->full_name(); |
||||||
|
} |
||||||
|
|
||||||
|
// As functions defined in json_util.h are just thin wrappers around the
|
||||||
|
// JSON conversion code in //net/proto2/util/converter, in this test we
|
||||||
|
// only cover some very basic cases to make sure the wrappers have forwarded
|
||||||
|
// parameters to the underlying implementation correctly. More detailed
|
||||||
|
// tests are contained in the //net/proto2/util/converter directory.
|
||||||
|
class JsonUtilTest : public testing::Test { |
||||||
|
protected: |
||||||
|
JsonUtilTest() { |
||||||
|
resolver_.reset(NewTypeResolverForDescriptorPool( |
||||||
|
kTypeUrlPrefix, DescriptorPool::generated_pool())); |
||||||
|
} |
||||||
|
|
||||||
|
string ToJson(const Message& message, const JsonOptions& options) { |
||||||
|
string result; |
||||||
|
GOOGLE_CHECK_OK(BinaryToJsonString(resolver_.get(), |
||||||
|
GetTypeUrl(message.GetDescriptor()), |
||||||
|
message.SerializeAsString(), &result, options)); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
bool FromJson(const string& json, Message* message) { |
||||||
|
string binary; |
||||||
|
GOOGLE_CHECK_OK(JsonToBinaryString( |
||||||
|
resolver_.get(), GetTypeUrl(message->GetDescriptor()), json, &binary)); |
||||||
|
return message->ParseFromString(binary); |
||||||
|
} |
||||||
|
|
||||||
|
google::protobuf::scoped_ptr<TypeResolver> resolver_; |
||||||
|
}; |
||||||
|
|
||||||
|
TEST_F(JsonUtilTest, TestWhitespaces) { |
||||||
|
TestMessage m; |
||||||
|
m.mutable_message_value(); |
||||||
|
|
||||||
|
JsonOptions options; |
||||||
|
EXPECT_EQ("{\"messageValue\":{}}", ToJson(m, options)); |
||||||
|
options.add_whitespace = true; |
||||||
|
EXPECT_EQ( |
||||||
|
"{\n" |
||||||
|
" \"messageValue\": {}\n" |
||||||
|
"}\n", |
||||||
|
ToJson(m, options)); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonUtilTest, TestDefaultValues) { |
||||||
|
TestMessage m; |
||||||
|
JsonOptions options; |
||||||
|
EXPECT_EQ("{}", ToJson(m, options)); |
||||||
|
options.always_print_primitive_fields = true; |
||||||
|
EXPECT_EQ( |
||||||
|
"{\"boolValue\":false," |
||||||
|
"\"int32Value\":0," |
||||||
|
"\"int64Value\":\"0\"," |
||||||
|
"\"uint32Value\":0," |
||||||
|
"\"uint64Value\":\"0\"," |
||||||
|
"\"floatValue\":0," |
||||||
|
"\"doubleValue\":0," |
||||||
|
"\"stringValue\":\"\"," |
||||||
|
"\"bytesValue\":\"\"," |
||||||
|
// TODO(xiaofeng): The default enum value should be FOO. I believe
|
||||||
|
// this is a bug in DefaultValueObjectWriter.
|
||||||
|
"\"enumValue\":null" |
||||||
|
"}", |
||||||
|
ToJson(m, options)); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_F(JsonUtilTest, ParseMessage) { |
||||||
|
// Some random message but good enough to verify that the parsing warpper
|
||||||
|
// functions are working properly.
|
||||||
|
string input = |
||||||
|
"{\n" |
||||||
|
" \"int32Value\": 1024,\n" |
||||||
|
" \"repeatedInt32Value\": [1, 2],\n" |
||||||
|
" \"messageValue\": {\n" |
||||||
|
" \"value\": 2048\n" |
||||||
|
" },\n" |
||||||
|
" \"repeatedMessageValue\": [\n" |
||||||
|
" {\"value\": 40}, {\"value\": 96}\n" |
||||||
|
" ]\n" |
||||||
|
"}\n"; |
||||||
|
TestMessage m; |
||||||
|
ASSERT_TRUE(FromJson(input, &m)); |
||||||
|
EXPECT_EQ(1024, m.int32_value()); |
||||||
|
ASSERT_EQ(2, m.repeated_int32_value_size()); |
||||||
|
EXPECT_EQ(1, m.repeated_int32_value(0)); |
||||||
|
EXPECT_EQ(2, m.repeated_int32_value(1)); |
||||||
|
EXPECT_EQ(2048, m.message_value().value()); |
||||||
|
ASSERT_EQ(2, m.repeated_message_value_size()); |
||||||
|
EXPECT_EQ(40, m.repeated_message_value(0).value()); |
||||||
|
EXPECT_EQ(96, m.repeated_message_value(1).value()); |
||||||
|
} |
||||||
|
|
||||||
|
typedef pair<char*, int> Segment; |
||||||
|
// A ZeroCopyOutputStream that writes to multiple buffers.
|
||||||
|
class SegmentedZeroCopyOutputStream : public io::ZeroCopyOutputStream { |
||||||
|
public: |
||||||
|
explicit SegmentedZeroCopyOutputStream(list<Segment> segments) |
||||||
|
: segments_(segments), last_segment_(NULL, 0), byte_count_(0) {} |
||||||
|
|
||||||
|
virtual bool Next(void** buffer, int* length) { |
||||||
|
if (segments_.empty()) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
last_segment_ = segments_.front(); |
||||||
|
segments_.pop_front(); |
||||||
|
*buffer = last_segment_.first; |
||||||
|
*length = last_segment_.second; |
||||||
|
byte_count_ += *length; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
virtual void BackUp(int length) { |
||||||
|
GOOGLE_CHECK(length <= last_segment_.second); |
||||||
|
segments_.push_front( |
||||||
|
Segment(last_segment_.first + last_segment_.second - length, length)); |
||||||
|
last_segment_ = Segment(last_segment_.first, last_segment_.second - length); |
||||||
|
byte_count_ -= length; |
||||||
|
} |
||||||
|
|
||||||
|
virtual int64 ByteCount() const { return byte_count_; } |
||||||
|
|
||||||
|
private: |
||||||
|
list<Segment> segments_; |
||||||
|
Segment last_segment_; |
||||||
|
int64 byte_count_; |
||||||
|
}; |
||||||
|
|
||||||
|
// This test splits the output buffer and also the input data into multiple
|
||||||
|
// segments and checks that the implementation of ZeroCopyStreamByteSink
|
||||||
|
// handles all possible cases correctly.
|
||||||
|
TEST(ZeroCopyStreamByteSinkTest, TestAllInputOutputPatterns) { |
||||||
|
static const int kOutputBufferLength = 10; |
||||||
|
// An exhaustive test takes too long, skip some combinations to make the test
|
||||||
|
// run faster.
|
||||||
|
static const int kSkippedPatternCount = 7; |
||||||
|
|
||||||
|
char buffer[kOutputBufferLength]; |
||||||
|
for (int split_pattern = 0; split_pattern < (1 << (kOutputBufferLength - 1)); |
||||||
|
split_pattern += kSkippedPatternCount) { |
||||||
|
// Split the buffer into small segments according to the split_pattern.
|
||||||
|
list<Segment> segments; |
||||||
|
int segment_start = 0; |
||||||
|
for (int i = 0; i < kOutputBufferLength - 1; ++i) { |
||||||
|
if (split_pattern & (1 << i)) { |
||||||
|
segments.push_back( |
||||||
|
Segment(buffer + segment_start, i - segment_start + 1)); |
||||||
|
segment_start = i + 1; |
||||||
|
} |
||||||
|
} |
||||||
|
segments.push_back( |
||||||
|
Segment(buffer + segment_start, kOutputBufferLength - segment_start)); |
||||||
|
|
||||||
|
// Write exactly 10 bytes through the ByteSink.
|
||||||
|
string input_data = "0123456789"; |
||||||
|
for (int input_pattern = 0; input_pattern < (1 << (input_data.size() - 1)); |
||||||
|
input_pattern += kSkippedPatternCount) { |
||||||
|
memset(buffer, 0, sizeof(buffer)); |
||||||
|
{ |
||||||
|
SegmentedZeroCopyOutputStream output_stream(segments); |
||||||
|
internal::ZeroCopyStreamByteSink byte_sink(&output_stream); |
||||||
|
int start = 0; |
||||||
|
for (int j = 0; j < input_data.length() - 1; ++j) { |
||||||
|
if (input_pattern & (1 << j)) { |
||||||
|
byte_sink.Append(&input_data[start], j - start + 1); |
||||||
|
start = j + 1; |
||||||
|
} |
||||||
|
} |
||||||
|
byte_sink.Append(&input_data[start], input_data.length() - start); |
||||||
|
} |
||||||
|
EXPECT_EQ(input_data, string(buffer, input_data.length())); |
||||||
|
} |
||||||
|
|
||||||
|
// Write only 9 bytes through the ByteSink.
|
||||||
|
input_data = "012345678"; |
||||||
|
for (int input_pattern = 0; input_pattern < (1 << (input_data.size() - 1)); |
||||||
|
input_pattern += kSkippedPatternCount) { |
||||||
|
memset(buffer, 0, sizeof(buffer)); |
||||||
|
{ |
||||||
|
SegmentedZeroCopyOutputStream output_stream(segments); |
||||||
|
internal::ZeroCopyStreamByteSink byte_sink(&output_stream); |
||||||
|
int start = 0; |
||||||
|
for (int j = 0; j < input_data.length() - 1; ++j) { |
||||||
|
if (input_pattern & (1 << j)) { |
||||||
|
byte_sink.Append(&input_data[start], j - start + 1); |
||||||
|
start = j + 1; |
||||||
|
} |
||||||
|
} |
||||||
|
byte_sink.Append(&input_data[start], input_data.length() - start); |
||||||
|
} |
||||||
|
EXPECT_EQ(input_data, string(buffer, input_data.length())); |
||||||
|
EXPECT_EQ(0, buffer[input_data.length()]); |
||||||
|
} |
||||||
|
|
||||||
|
// Write 11 bytes through the ByteSink. The extra byte will just
|
||||||
|
// be ignored.
|
||||||
|
input_data = "0123456789A"; |
||||||
|
for (int input_pattern = 0; input_pattern < (1 << (input_data.size() - 1)); |
||||||
|
input_pattern += kSkippedPatternCount) { |
||||||
|
memset(buffer, 0, sizeof(buffer)); |
||||||
|
{ |
||||||
|
SegmentedZeroCopyOutputStream output_stream(segments); |
||||||
|
internal::ZeroCopyStreamByteSink byte_sink(&output_stream); |
||||||
|
int start = 0; |
||||||
|
for (int j = 0; j < input_data.length() - 1; ++j) { |
||||||
|
if (input_pattern & (1 << j)) { |
||||||
|
byte_sink.Append(&input_data[start], j - start + 1); |
||||||
|
start = j + 1; |
||||||
|
} |
||||||
|
} |
||||||
|
byte_sink.Append(&input_data[start], input_data.length() - start); |
||||||
|
} |
||||||
|
EXPECT_EQ(input_data.substr(0, kOutputBufferLength), |
||||||
|
string(buffer, kOutputBufferLength)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
} // namespace google
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,817 @@ |
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// Author: jschorr@google.com (Joseph Schorr)
|
||||||
|
// Based on original Protocol Buffers design by
|
||||||
|
// Sanjay Ghemawat, Jeff Dean, and others.
|
||||||
|
//
|
||||||
|
// This file defines static methods and classes for comparing Protocol
|
||||||
|
// Messages.
|
||||||
|
//
|
||||||
|
// Aug. 2008: Added Unknown Fields Comparison for messages.
|
||||||
|
// Aug. 2009: Added different options to compare repeated fields.
|
||||||
|
// Apr. 2010: Moved field comparison to FieldComparator.
|
||||||
|
|
||||||
|
#ifndef GOOGLE_PROTOBUF_UTIL_MESSAGE_DIFFERENCER_H__ |
||||||
|
#define GOOGLE_PROTOBUF_UTIL_MESSAGE_DIFFERENCER_H__ |
||||||
|
|
||||||
|
#include <map> |
||||||
|
#include <set> |
||||||
|
#include <string> |
||||||
|
#include <vector> |
||||||
|
#include <google/protobuf/descriptor.h> // FieldDescriptor |
||||||
|
#include <google/protobuf/message.h> // Message |
||||||
|
#include <google/protobuf/unknown_field_set.h> |
||||||
|
#include <google/protobuf/util/field_comparator.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
|
||||||
|
class DynamicMessageFactory; |
||||||
|
class FieldDescriptor; |
||||||
|
|
||||||
|
namespace io { |
||||||
|
class ZeroCopyOutputStream; |
||||||
|
class Printer; |
||||||
|
} |
||||||
|
|
||||||
|
namespace util { |
||||||
|
|
||||||
|
class FieldContext; // declared below MessageDifferencer
|
||||||
|
|
||||||
|
// A basic differencer that can be used to determine
|
||||||
|
// the differences between two specified Protocol Messages. If any differences
|
||||||
|
// are found, the Compare method will return false, and any differencer reporter
|
||||||
|
// specified via ReportDifferencesTo will have its reporting methods called (see
|
||||||
|
// below for implementation of the report). Based off of the original
|
||||||
|
// ProtocolDifferencer implementation in //net/proto/protocol-differencer.h
|
||||||
|
// (Thanks Todd!).
|
||||||
|
//
|
||||||
|
// MessageDifferencer REQUIRES that compared messages be the same type, defined
|
||||||
|
// as messages that share the same descriptor. If not, the behavior of this
|
||||||
|
// class is undefined.
|
||||||
|
//
|
||||||
|
// People disagree on what MessageDifferencer should do when asked to compare
|
||||||
|
// messages with different descriptors. Some people think it should always
|
||||||
|
// return false. Others expect it to try to look for similar fields and
|
||||||
|
// compare them anyway -- especially if the descriptors happen to be identical.
|
||||||
|
// If we chose either of these behaviors, some set of people would find it
|
||||||
|
// surprising, and could end up writing code expecting the other behavior
|
||||||
|
// without realizing their error. Therefore, we forbid that usage.
|
||||||
|
//
|
||||||
|
// This class is implemented based on the proto2 reflection. The performance
|
||||||
|
// should be good enough for normal usages. However, for places where the
|
||||||
|
// performance is extremely sensitive, there are several alternatives:
|
||||||
|
// - Comparing serialized string
|
||||||
|
// Downside: false negatives (there are messages that are the same but their
|
||||||
|
// serialized strings are different).
|
||||||
|
// - Equals code generator by compiler plugin (net/proto2/contrib/equals_plugin)
|
||||||
|
// Downside: more generated code; maintenance overhead for the additional rule
|
||||||
|
// (must be in sync with the original proto_library).
|
||||||
|
//
|
||||||
|
// Note on handling of google.protobuf.Any: MessageDifferencer automatically
|
||||||
|
// unpacks Any::value into a Message and compares its individual fields.
|
||||||
|
// Messages encoded in a repeated Any cannot be compared using TreatAsMap.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Note on thread-safety: MessageDifferencer is *not* thread-safe. You need to
|
||||||
|
// guard it with a lock to use the same MessageDifferencer instance from
|
||||||
|
// multiple threads. Note that it's fine to call static comparison methods
|
||||||
|
// (like MessageDifferencer::Equals) concurrently.
|
||||||
|
class LIBPROTOBUF_EXPORT MessageDifferencer { |
||||||
|
public: |
||||||
|
// Determines whether the supplied messages are equal. Equality is defined as
|
||||||
|
// all fields within the two messages being set to the same value. Primitive
|
||||||
|
// fields and strings are compared by value while embedded messages/groups
|
||||||
|
// are compared as if via a recursive call. Use IgnoreField() and Compare()
|
||||||
|
// if some fields should be ignored in the comparison.
|
||||||
|
//
|
||||||
|
// This method REQUIRES that the two messages have the same
|
||||||
|
// Descriptor (message1.GetDescriptor() == message2.GetDescriptor()).
|
||||||
|
static bool Equals(const Message& message1, const Message& message2); |
||||||
|
|
||||||
|
// Determines whether the supplied messages are equivalent. Equivalency is
|
||||||
|
// defined as all fields within the two messages having the same value. This
|
||||||
|
// differs from the Equals method above in that fields with default values
|
||||||
|
// are considered set to said value automatically. For details on how default
|
||||||
|
// values are defined for each field type, see http://shortn/_x2Gv6XFrWt.
|
||||||
|
// Also, Equivalent() ignores unknown fields. Use IgnoreField() and Compare()
|
||||||
|
// if some fields should be ignored in the comparison.
|
||||||
|
//
|
||||||
|
// This method REQUIRES that the two messages have the same
|
||||||
|
// Descriptor (message1.GetDescriptor() == message2.GetDescriptor()).
|
||||||
|
static bool Equivalent(const Message& message1, const Message& message2); |
||||||
|
|
||||||
|
// Determines whether the supplied messages are approximately equal.
|
||||||
|
// Approximate equality is defined as all fields within the two messages
|
||||||
|
// being approximately equal. Primitive (non-float) fields and strings are
|
||||||
|
// compared by value, floats are compared using MathUtil::AlmostEquals() and
|
||||||
|
// embedded messages/groups are compared as if via a recursive call. Use
|
||||||
|
// IgnoreField() and Compare() if some fields should be ignored in the
|
||||||
|
// comparison.
|
||||||
|
//
|
||||||
|
// This method REQUIRES that the two messages have the same
|
||||||
|
// Descriptor (message1.GetDescriptor() == message2.GetDescriptor()).
|
||||||
|
static bool ApproximatelyEquals(const Message& message1, |
||||||
|
const Message& message2); |
||||||
|
|
||||||
|
// Determines whether the supplied messages are approximately equivalent.
|
||||||
|
// Approximate equivalency is defined as all fields within the two messages
|
||||||
|
// being approximately equivalent. As in
|
||||||
|
// MessageDifferencer::ApproximatelyEquals, primitive (non-float) fields and
|
||||||
|
// strings are compared by value, floats are compared using
|
||||||
|
// MathUtil::AlmostEquals() and embedded messages/groups are compared as if
|
||||||
|
// via a recursive call. However, fields with default values are considered
|
||||||
|
// set to said value, as per MessageDiffencer::Equivalent. Use IgnoreField()
|
||||||
|
// and Compare() if some fields should be ignored in the comparison.
|
||||||
|
//
|
||||||
|
// This method REQUIRES that the two messages have the same
|
||||||
|
// Descriptor (message1.GetDescriptor() == message2.GetDescriptor()).
|
||||||
|
static bool ApproximatelyEquivalent(const Message& message1, |
||||||
|
const Message& message2); |
||||||
|
|
||||||
|
// Identifies an individual field in a message instance. Used for field_path,
|
||||||
|
// below.
|
||||||
|
struct SpecificField { |
||||||
|
// For known fields, "field" is filled in and "unknown_field_number" is -1.
|
||||||
|
// For unknown fields, "field" is NULL, "unknown_field_number" is the field
|
||||||
|
// number, and "unknown_field_type" is its type.
|
||||||
|
const FieldDescriptor* field; |
||||||
|
int unknown_field_number; |
||||||
|
UnknownField::Type unknown_field_type; |
||||||
|
|
||||||
|
// If this a repeated field, "index" is the index within it. For unknown
|
||||||
|
// fields, this is the index of the field among all unknown fields of the
|
||||||
|
// same field number and type.
|
||||||
|
int index; |
||||||
|
|
||||||
|
// If "field" is a repeated field which is being treated as a map or
|
||||||
|
// a set (see TreatAsMap() and TreatAsSet(), below), new_index indicates
|
||||||
|
// the index the position to which the element has moved. This only
|
||||||
|
// applies to ReportMoved() and (in the case of TreatAsMap())
|
||||||
|
// ReportModified(). In all other cases, "new_index" will have the same
|
||||||
|
// value as "index".
|
||||||
|
int new_index; |
||||||
|
|
||||||
|
// For unknown fields, these are the pointers to the UnknownFieldSet
|
||||||
|
// containing the unknown fields. In certain cases (e.g. proto1's
|
||||||
|
// MessageSet, or nested groups of unknown fields), these may differ from
|
||||||
|
// the messages' internal UnknownFieldSets.
|
||||||
|
const UnknownFieldSet* unknown_field_set1; |
||||||
|
const UnknownFieldSet* unknown_field_set2; |
||||||
|
|
||||||
|
// For unknown fields, these are the index of the field within the
|
||||||
|
// UnknownFieldSets. One or the other will be -1 when
|
||||||
|
// reporting an addition or deletion.
|
||||||
|
int unknown_field_index1; |
||||||
|
int unknown_field_index2; |
||||||
|
|
||||||
|
SpecificField() |
||||||
|
: field(NULL), |
||||||
|
unknown_field_number(-1), |
||||||
|
index(-1), |
||||||
|
new_index(-1), |
||||||
|
unknown_field_set1(NULL), |
||||||
|
unknown_field_set2(NULL), |
||||||
|
unknown_field_index1(-1), |
||||||
|
unknown_field_index2(-1) {} |
||||||
|
}; |
||||||
|
|
||||||
|
// Abstract base class from which all MessageDifferencer
|
||||||
|
// reporters derive. The five Report* methods below will be called when
|
||||||
|
// a field has been added, deleted, modified, moved, or matched. The third
|
||||||
|
// argument is a vector of FieldDescriptor pointers which describes the chain
|
||||||
|
// of fields that was taken to find the current field. For example, for a
|
||||||
|
// field found in an embedded message, the vector will contain two
|
||||||
|
// FieldDescriptors. The first will be the field of the embedded message
|
||||||
|
// itself and the second will be the actual field in the embedded message
|
||||||
|
// that was added/deleted/modified.
|
||||||
|
class LIBPROTOBUF_EXPORT Reporter { |
||||||
|
public: |
||||||
|
Reporter(); |
||||||
|
virtual ~Reporter(); |
||||||
|
|
||||||
|
// Reports that a field has been added into Message2.
|
||||||
|
virtual void ReportAdded( |
||||||
|
const Message& message1, const Message& message2, |
||||||
|
const vector<SpecificField>& field_path) = 0; |
||||||
|
|
||||||
|
// Reports that a field has been deleted from Message1.
|
||||||
|
virtual void ReportDeleted( |
||||||
|
const Message& message1, |
||||||
|
const Message& message2, |
||||||
|
const vector<SpecificField>& field_path) = 0; |
||||||
|
|
||||||
|
// Reports that the value of a field has been modified.
|
||||||
|
virtual void ReportModified( |
||||||
|
const Message& message1, |
||||||
|
const Message& message2, |
||||||
|
const vector<SpecificField>& field_path) = 0; |
||||||
|
|
||||||
|
// Reports that a repeated field has been moved to another location. This
|
||||||
|
// only applies when using TreatAsSet or TreatAsMap() -- see below. Also
|
||||||
|
// note that for any given field, ReportModified and ReportMoved are
|
||||||
|
// mutually exclusive. If a field has been both moved and modified, then
|
||||||
|
// only ReportModified will be called.
|
||||||
|
virtual void ReportMoved( |
||||||
|
const Message& message1, |
||||||
|
const Message& message2, |
||||||
|
const vector<SpecificField>& field_path) { } |
||||||
|
|
||||||
|
// Reports that two fields match. Useful for doing side-by-side diffs.
|
||||||
|
// This function is mutually exclusive with ReportModified and ReportMoved.
|
||||||
|
// Note that you must call set_report_matches(true) before calling Compare
|
||||||
|
// to make use of this function.
|
||||||
|
virtual void ReportMatched( |
||||||
|
const Message& message1, |
||||||
|
const Message& message2, |
||||||
|
const vector<SpecificField>& field_path) { } |
||||||
|
|
||||||
|
// Reports that two fields would have been compared, but the
|
||||||
|
// comparison has been skipped because the field was marked as
|
||||||
|
// 'ignored' using IgnoreField(). This function is mutually
|
||||||
|
// exclusive with all the other Report() functions.
|
||||||
|
//
|
||||||
|
// The contract of ReportIgnored is slightly different than the
|
||||||
|
// other Report() functions, in that |field_path.back().index| is
|
||||||
|
// always equal to -1, even if the last field is repeated. This is
|
||||||
|
// because while the other Report() functions indicate where in a
|
||||||
|
// repeated field the action (Addition, Deletion, etc...)
|
||||||
|
// happened, when a repeated field is 'ignored', the differencer
|
||||||
|
// simply calls ReportIgnored on the repeated field as a whole and
|
||||||
|
// moves on without looking at its individual elements.
|
||||||
|
//
|
||||||
|
// Furthermore, ReportIgnored() does not indicate whether the
|
||||||
|
// fields were in fact equal or not, as Compare() does not inspect
|
||||||
|
// these fields at all. It is up to the Reporter to decide whether
|
||||||
|
// the fields are equal or not (perhaps with a second call to
|
||||||
|
// Compare()), if it cares.
|
||||||
|
virtual void ReportIgnored( |
||||||
|
const Message& message1, |
||||||
|
const Message& message2, |
||||||
|
const vector<SpecificField>& field_path) { } |
||||||
|
|
||||||
|
private: |
||||||
|
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Reporter); |
||||||
|
}; |
||||||
|
|
||||||
|
// MapKeyComparator is used to determine if two elements have the same key
|
||||||
|
// when comparing elements of a repeated field as a map.
|
||||||
|
class LIBPROTOBUF_EXPORT MapKeyComparator { |
||||||
|
public: |
||||||
|
MapKeyComparator(); |
||||||
|
virtual ~MapKeyComparator(); |
||||||
|
|
||||||
|
// The first IsMatch without parent_fields is only for backward
|
||||||
|
// compatibility. New users should override the second one instead.
|
||||||
|
//
|
||||||
|
// Deprecated.
|
||||||
|
// TODO(ykzhu): remove this function.
|
||||||
|
virtual bool IsMatch(const Message& message1, |
||||||
|
const Message& message2) const { |
||||||
|
GOOGLE_CHECK(false) << "This function shouldn't get called"; |
||||||
|
return false; |
||||||
|
} |
||||||
|
virtual bool IsMatch(const Message& message1, |
||||||
|
const Message& message2, |
||||||
|
const vector<SpecificField>& parent_fields) const { |
||||||
|
return IsMatch(message1, message2); |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MapKeyComparator); |
||||||
|
}; |
||||||
|
|
||||||
|
// Abstract base class from which all IgnoreCriteria derive.
|
||||||
|
// By adding IgnoreCriteria more complex ignore logic can be implemented.
|
||||||
|
// IgnoreCriteria are registed with AddIgnoreCriteria. For each compared
|
||||||
|
// field IsIgnored is called on each added IgnoreCriteria until one returns
|
||||||
|
// true or all return false.
|
||||||
|
// IsIgnored is called for fields where at least one side has a value.
|
||||||
|
class LIBPROTOBUF_EXPORT IgnoreCriteria { |
||||||
|
public: |
||||||
|
IgnoreCriteria(); |
||||||
|
virtual ~IgnoreCriteria(); |
||||||
|
|
||||||
|
// Returns true if the field should be ignored.
|
||||||
|
virtual bool IsIgnored( |
||||||
|
const Message& message1, |
||||||
|
const Message& message2, |
||||||
|
const FieldDescriptor* field, |
||||||
|
const vector<SpecificField>& parent_fields) = 0; |
||||||
|
}; |
||||||
|
|
||||||
|
// To add a Reporter, construct default here, then use ReportDifferencesTo or
|
||||||
|
// ReportDifferencesToString.
|
||||||
|
explicit MessageDifferencer(); |
||||||
|
|
||||||
|
~MessageDifferencer(); |
||||||
|
|
||||||
|
enum MessageFieldComparison { |
||||||
|
EQUAL, // Fields must be present in both messages
|
||||||
|
// for the messages to be considered the same.
|
||||||
|
EQUIVALENT, // Fields with default values are considered set
|
||||||
|
// for comparison purposes even if not explicitly
|
||||||
|
// set in the messages themselves. Unknown fields
|
||||||
|
// are ignored.
|
||||||
|
}; |
||||||
|
|
||||||
|
enum Scope { |
||||||
|
FULL, // All fields of both messages are considered in the comparison.
|
||||||
|
PARTIAL // Only fields present in the first message are considered; fields
|
||||||
|
// set only in the second message will be skipped during
|
||||||
|
// comparison.
|
||||||
|
}; |
||||||
|
|
||||||
|
// DEPRECATED. Use FieldComparator::FloatComparison instead.
|
||||||
|
enum FloatComparison { |
||||||
|
EXACT, // Floats and doubles are compared exactly.
|
||||||
|
APPROXIMATE // Floats and doubles are compared using the
|
||||||
|
// MathUtil::AlmostEquals method.
|
||||||
|
}; |
||||||
|
|
||||||
|
enum RepeatedFieldComparison { |
||||||
|
AS_LIST, // Repeated fields are compared in order. Differing values at
|
||||||
|
// the same index are reported using ReportModified(). If the
|
||||||
|
// repeated fields have different numbers of elements, the
|
||||||
|
// unpaired elements are reported using ReportAdded() or
|
||||||
|
// ReportDeleted().
|
||||||
|
AS_SET, // Treat all the repeated fields as sets by default.
|
||||||
|
// See TreatAsSet(), as below.
|
||||||
|
}; |
||||||
|
|
||||||
|
// The elements of the given repeated field will be treated as a set for
|
||||||
|
// diffing purposes, so different orderings of the same elements will be
|
||||||
|
// considered equal. Elements which are present on both sides of the
|
||||||
|
// comparison but which have changed position will be reported with
|
||||||
|
// ReportMoved(). Elements which only exist on one side or the other are
|
||||||
|
// reported with ReportAdded() and ReportDeleted() regardless of their
|
||||||
|
// positions. ReportModified() is never used for this repeated field. If
|
||||||
|
// the only differences between the compared messages is that some fields
|
||||||
|
// have been moved, then the comparison returns true.
|
||||||
|
//
|
||||||
|
// If the scope of comparison is set to PARTIAL, then in addition to what's
|
||||||
|
// above, extra values added to repeated fields of the second message will
|
||||||
|
// not cause the comparison to fail.
|
||||||
|
//
|
||||||
|
// Note that set comparison is currently O(k * n^2) (where n is the total
|
||||||
|
// number of elements, and k is the average size of each element). In theory
|
||||||
|
// it could be made O(n * k) with a more complex hashing implementation. Feel
|
||||||
|
// free to contribute one if the current implementation is too slow for you.
|
||||||
|
// If partial matching is also enabled, the time complexity will be O(k * n^2
|
||||||
|
// + n^3) in which n^3 is the time complexity of the maximum matching
|
||||||
|
// algorithm.
|
||||||
|
//
|
||||||
|
// REQUIRES: field->is_repeated()
|
||||||
|
void TreatAsSet(const FieldDescriptor* field); |
||||||
|
|
||||||
|
// The elements of the given repeated field will be treated as a map for
|
||||||
|
// diffing purposes, with |key| being the map key. Thus, elements with the
|
||||||
|
// same key will be compared even if they do not appear at the same index.
|
||||||
|
// Differences are reported similarly to TreatAsSet(), except that
|
||||||
|
// ReportModified() is used to report elements with the same key but
|
||||||
|
// different values. Note that if an element is both moved and modified,
|
||||||
|
// only ReportModified() will be called. As with TreatAsSet, if the only
|
||||||
|
// differences between the compared messages is that some fields have been
|
||||||
|
// moved, then the comparison returns true. See TreatAsSet for notes on
|
||||||
|
// performance.
|
||||||
|
//
|
||||||
|
// REQUIRES: field->is_repeated()
|
||||||
|
// REQUIRES: field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE
|
||||||
|
// REQUIRES: key->containing_type() == field->message_type()
|
||||||
|
void TreatAsMap(const FieldDescriptor* field, const FieldDescriptor* key); |
||||||
|
// Same as TreatAsMap except that this method will use multiple fields as
|
||||||
|
// the key in comparison. All specified fields in 'key_fields' should be
|
||||||
|
// present in the compared elements. Two elements will be treated as having
|
||||||
|
// the same key iff they have the same value for every specified field. There
|
||||||
|
// are two steps in the comparison process. The first one is key matching.
|
||||||
|
// Every element from one message will be compared to every element from
|
||||||
|
// the other message. Only fields in 'key_fields' are compared in this step
|
||||||
|
// to decide if two elements have the same key. The second step is value
|
||||||
|
// comparison. Those pairs of elements with the same key (with equal value
|
||||||
|
// for every field in 'key_fields') will be compared in this step.
|
||||||
|
// Time complexity of the first step is O(s * m * n ^ 2) where s is the
|
||||||
|
// average size of the fields specified in 'key_fields', m is the number of
|
||||||
|
// fields in 'key_fields' and n is the number of elements. If partial
|
||||||
|
// matching is enabled, an extra O(n^3) will be incured by the maximum
|
||||||
|
// matching algorithm. The second step is O(k * n) where k is the average
|
||||||
|
// size of each element.
|
||||||
|
void TreatAsMapWithMultipleFieldsAsKey( |
||||||
|
const FieldDescriptor* field, |
||||||
|
const vector<const FieldDescriptor*>& key_fields); |
||||||
|
// Same as TreatAsMapWithMultipleFieldsAsKey, except that each of the field
|
||||||
|
// do not necessarily need to be a direct subfield. Each element in
|
||||||
|
// key_field_paths indicate a path from the message being compared, listing
|
||||||
|
// successive subfield to reach the key field.
|
||||||
|
//
|
||||||
|
// REQUIRES:
|
||||||
|
// for key_field_path in key_field_paths:
|
||||||
|
// key_field_path[0]->containing_type() == field->message_type()
|
||||||
|
// for i in [0, key_field_path.size() - 1):
|
||||||
|
// key_field_path[i+1]->containing_type() ==
|
||||||
|
// key_field_path[i]->message_type()
|
||||||
|
// key_field_path[i]->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE
|
||||||
|
// !key_field_path[i]->is_repeated()
|
||||||
|
void TreatAsMapWithMultipleFieldPathsAsKey( |
||||||
|
const FieldDescriptor* field, |
||||||
|
const vector<vector<const FieldDescriptor*> >& key_field_paths); |
||||||
|
|
||||||
|
// Uses a custom MapKeyComparator to determine if two elements have the same
|
||||||
|
// key when comparing a repeated field as a map.
|
||||||
|
// The caller is responsible to delete the key_comparator.
|
||||||
|
// This method varies from TreatAsMapWithMultipleFieldsAsKey only in the
|
||||||
|
// first key matching step. Rather than comparing some specified fields, it
|
||||||
|
// will invoke the IsMatch method of the given 'key_comparator' to decide if
|
||||||
|
// two elements have the same key.
|
||||||
|
void TreatAsMapUsingKeyComparator( |
||||||
|
const FieldDescriptor* field, |
||||||
|
const MapKeyComparator* key_comparator); |
||||||
|
|
||||||
|
// Add a custom ignore criteria that is evaluated in addition to the
|
||||||
|
// ignored fields added with IgnoreField.
|
||||||
|
// Takes ownership of ignore_criteria.
|
||||||
|
void AddIgnoreCriteria(IgnoreCriteria* ignore_criteria); |
||||||
|
|
||||||
|
// Indicates that any field with the given descriptor should be
|
||||||
|
// ignored for the purposes of comparing two messages. This applies
|
||||||
|
// to fields nested in the message structure as well as top level
|
||||||
|
// ones. When the MessageDifferencer encounters an ignored field,
|
||||||
|
// ReportIgnored is called on the reporter, if one is specified.
|
||||||
|
//
|
||||||
|
// The only place where the field's 'ignored' status is not applied is when
|
||||||
|
// it is being used as a key in a field passed to TreatAsMap or is one of
|
||||||
|
// the fields passed to TreatAsMapWithMultipleFieldsAsKey.
|
||||||
|
// In this case it is compared in key matching but after that it's ignored
|
||||||
|
// in value comparison.
|
||||||
|
void IgnoreField(const FieldDescriptor* field); |
||||||
|
|
||||||
|
// Sets the field comparator used to determine differences between protocol
|
||||||
|
// buffer fields. By default it's set to a DefaultFieldComparator instance.
|
||||||
|
// MessageDifferencer doesn't take ownership over the passed object.
|
||||||
|
// Note that this method must be called before Compare for the comparator to
|
||||||
|
// be used.
|
||||||
|
void set_field_comparator(FieldComparator* comparator); |
||||||
|
|
||||||
|
// DEPRECATED. Pass a DefaultFieldComparator instance instead.
|
||||||
|
// Sets the fraction and margin for the float comparison of a given field.
|
||||||
|
// Uses MathUtil::WithinFractionOrMargin to compare the values.
|
||||||
|
// NOTE: this method does nothing if differencer's field comparator has been
|
||||||
|
// set to a custom object.
|
||||||
|
//
|
||||||
|
// REQUIRES: field->cpp_type == FieldDescriptor::CPPTYPE_DOUBLE or
|
||||||
|
// field->cpp_type == FieldDescriptor::CPPTYPE_FLOAT
|
||||||
|
// REQUIRES: float_comparison_ == APPROXIMATE
|
||||||
|
void SetFractionAndMargin(const FieldDescriptor* field, double fraction, |
||||||
|
double margin); |
||||||
|
|
||||||
|
// Sets the type of comparison (as defined in the MessageFieldComparison
|
||||||
|
// enumeration above) that is used by this differencer when determining how
|
||||||
|
// to compare fields in messages.
|
||||||
|
void set_message_field_comparison(MessageFieldComparison comparison); |
||||||
|
|
||||||
|
// Tells the differencer whether or not to report matches. This method must
|
||||||
|
// be called before Compare. The default for a new differencer is false.
|
||||||
|
void set_report_matches(bool report_matches) { |
||||||
|
report_matches_ = report_matches; |
||||||
|
} |
||||||
|
|
||||||
|
// Sets the scope of the comparison (as defined in the Scope enumeration
|
||||||
|
// above) that is used by this differencer when determining which fields to
|
||||||
|
// compare between the messages.
|
||||||
|
void set_scope(Scope scope); |
||||||
|
|
||||||
|
// Returns the current scope used by this differencer.
|
||||||
|
Scope scope(); |
||||||
|
|
||||||
|
// DEPRECATED. Pass a DefaultFieldComparator instance instead.
|
||||||
|
// Sets the type of comparison (as defined in the FloatComparison enumeration
|
||||||
|
// above) that is used by this differencer when comparing float (and double)
|
||||||
|
// fields in messages.
|
||||||
|
// NOTE: this method does nothing if differencer's field comparator has been
|
||||||
|
// set to a custom object.
|
||||||
|
void set_float_comparison(FloatComparison comparison); |
||||||
|
|
||||||
|
// Sets the type of comparison for repeated field (as defined in the
|
||||||
|
// RepeatedFieldComparison enumeration above) that is used by this
|
||||||
|
// differencer when compare repeated fields in messages.
|
||||||
|
void set_repeated_field_comparison(RepeatedFieldComparison comparison); |
||||||
|
|
||||||
|
// Compares the two specified messages, returning true if they are the same,
|
||||||
|
// false otherwise. If this method returns false, any changes between the
|
||||||
|
// two messages will be reported if a Reporter was specified via
|
||||||
|
// ReportDifferencesTo (see also ReportDifferencesToString).
|
||||||
|
//
|
||||||
|
// This method REQUIRES that the two messages have the same
|
||||||
|
// Descriptor (message1.GetDescriptor() == message2.GetDescriptor()).
|
||||||
|
bool Compare(const Message& message1, const Message& message2); |
||||||
|
|
||||||
|
// Same as above, except comparing only the list of fields specified by the
|
||||||
|
// two vectors of FieldDescriptors.
|
||||||
|
bool CompareWithFields(const Message& message1, const Message& message2, |
||||||
|
const vector<const FieldDescriptor*>& message1_fields, |
||||||
|
const vector<const FieldDescriptor*>& message2_fields); |
||||||
|
|
||||||
|
// Automatically creates a reporter that will output the differences
|
||||||
|
// found (if any) to the specified output string pointer. Note that this
|
||||||
|
// method must be called before Compare.
|
||||||
|
void ReportDifferencesToString(string* output); |
||||||
|
|
||||||
|
// Tells the MessageDifferencer to report differences via the specified
|
||||||
|
// reporter. Note that this method must be called before Compare for
|
||||||
|
// the reporter to be used. It is the responsibility of the caller to delete
|
||||||
|
// this object.
|
||||||
|
// If the provided pointer equals NULL, the MessageDifferencer stops reporting
|
||||||
|
// differences to any previously set reporters or output strings.
|
||||||
|
void ReportDifferencesTo(Reporter* reporter); |
||||||
|
|
||||||
|
// An implementation of the MessageDifferencer Reporter that outputs
|
||||||
|
// any differences found in human-readable form to the supplied
|
||||||
|
// ZeroCopyOutputStream or Printer. If a printer is used, the delimiter
|
||||||
|
// *must* be '$'.
|
||||||
|
class LIBPROTOBUF_EXPORT StreamReporter : public Reporter { |
||||||
|
public: |
||||||
|
explicit StreamReporter(io::ZeroCopyOutputStream* output); |
||||||
|
explicit StreamReporter(io::Printer* printer); // delimiter '$'
|
||||||
|
virtual ~StreamReporter(); |
||||||
|
|
||||||
|
// When set to true, the stream reporter will also output aggregates nodes
|
||||||
|
// (i.e. messages and groups) whose subfields have been modified. When
|
||||||
|
// false, will only report the individual subfields. Defaults to false.
|
||||||
|
void set_report_modified_aggregates(bool report) { |
||||||
|
report_modified_aggregates_ = report; |
||||||
|
} |
||||||
|
|
||||||
|
// The following are implementations of the methods described above.
|
||||||
|
virtual void ReportAdded(const Message& message1, const Message& message2, |
||||||
|
const vector<SpecificField>& field_path); |
||||||
|
|
||||||
|
virtual void ReportDeleted(const Message& message1, |
||||||
|
const Message& message2, |
||||||
|
const vector<SpecificField>& field_path); |
||||||
|
|
||||||
|
virtual void ReportModified(const Message& message1, |
||||||
|
const Message& message2, |
||||||
|
const vector<SpecificField>& field_path); |
||||||
|
|
||||||
|
virtual void ReportMoved(const Message& message1, |
||||||
|
const Message& message2, |
||||||
|
const vector<SpecificField>& field_path); |
||||||
|
|
||||||
|
virtual void ReportMatched(const Message& message1, |
||||||
|
const Message& message2, |
||||||
|
const vector<SpecificField>& field_path); |
||||||
|
|
||||||
|
virtual void ReportIgnored(const Message& message1, |
||||||
|
const Message& message2, |
||||||
|
const vector<SpecificField>& field_path); |
||||||
|
|
||||||
|
protected: |
||||||
|
// Prints the specified path of fields to the buffer.
|
||||||
|
virtual void PrintPath(const vector<SpecificField>& field_path, |
||||||
|
bool left_side); |
||||||
|
|
||||||
|
// Prints the value of fields to the buffer. left_side is true if the
|
||||||
|
// given message is from the left side of the comparison, false if it
|
||||||
|
// was the right. This is relevant only to decide whether to follow
|
||||||
|
// unknown_field_index1 or unknown_field_index2 when an unknown field
|
||||||
|
// is encountered in field_path.
|
||||||
|
virtual void PrintValue(const Message& message, |
||||||
|
const vector<SpecificField>& field_path, |
||||||
|
bool left_side); |
||||||
|
|
||||||
|
// Prints the specified path of unknown fields to the buffer.
|
||||||
|
virtual void PrintUnknownFieldValue(const UnknownField* unknown_field); |
||||||
|
|
||||||
|
// Just print a string
|
||||||
|
void Print(const string& str); |
||||||
|
|
||||||
|
private: |
||||||
|
io::Printer* printer_; |
||||||
|
bool delete_printer_; |
||||||
|
bool report_modified_aggregates_; |
||||||
|
|
||||||
|
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(StreamReporter); |
||||||
|
}; |
||||||
|
|
||||||
|
private: |
||||||
|
// A MapKeyComparator to be used in TreatAsMapUsingKeyComparator.
|
||||||
|
// Implementation of this class needs to do field value comparison which
|
||||||
|
// relies on some private methods of MessageDifferencer. That's why this
|
||||||
|
// class is declared as a nested class of MessageDifferencer.
|
||||||
|
class MultipleFieldsMapKeyComparator; |
||||||
|
// Returns true if field1's number() is less than field2's.
|
||||||
|
static bool FieldBefore(const FieldDescriptor* field1, |
||||||
|
const FieldDescriptor* field2); |
||||||
|
|
||||||
|
// Combine the two lists of fields into the combined_fields output vector.
|
||||||
|
// All fields present in both lists will always be included in the combined
|
||||||
|
// list. Fields only present in one of the lists will only appear in the
|
||||||
|
// combined list if the corresponding fields_scope option is set to FULL.
|
||||||
|
void CombineFields(const vector<const FieldDescriptor*>& fields1, |
||||||
|
Scope fields1_scope, |
||||||
|
const vector<const FieldDescriptor*>& fields2, |
||||||
|
Scope fields2_scope, |
||||||
|
vector<const FieldDescriptor*>* combined_fields); |
||||||
|
|
||||||
|
// Internal version of the Compare method which performs the actual
|
||||||
|
// comparison. The parent_fields vector is a vector containing field
|
||||||
|
// descriptors of all fields accessed to get to this comparison operation
|
||||||
|
// (i.e. if the current message is an embedded message, the parent_fields
|
||||||
|
// vector will contain the field that has this embedded message).
|
||||||
|
bool Compare(const Message& message1, const Message& message2, |
||||||
|
vector<SpecificField>* parent_fields); |
||||||
|
|
||||||
|
// Compares all the unknown fields in two messages.
|
||||||
|
bool CompareUnknownFields(const Message& message1, const Message& message2, |
||||||
|
const google::protobuf::UnknownFieldSet&, |
||||||
|
const google::protobuf::UnknownFieldSet&, |
||||||
|
vector<SpecificField>* parent_fields); |
||||||
|
|
||||||
|
// Compares the specified messages for the requested field lists. The field
|
||||||
|
// lists are modified depending on comparison settings, and then passed to
|
||||||
|
// CompareWithFieldsInternal.
|
||||||
|
bool CompareRequestedFieldsUsingSettings( |
||||||
|
const Message& message1, const Message& message2, |
||||||
|
const vector<const FieldDescriptor*>& message1_fields, |
||||||
|
const vector<const FieldDescriptor*>& message2_fields, |
||||||
|
vector<SpecificField>* parent_fields); |
||||||
|
|
||||||
|
// Compares the specified messages with the specified field lists.
|
||||||
|
bool CompareWithFieldsInternal( |
||||||
|
const Message& message1, const Message& message2, |
||||||
|
const vector<const FieldDescriptor*>& message1_fields, |
||||||
|
const vector<const FieldDescriptor*>& message2_fields, |
||||||
|
vector<SpecificField>* parent_fields); |
||||||
|
|
||||||
|
// Compares the repeated fields, and report the error.
|
||||||
|
bool CompareRepeatedField(const Message& message1, const Message& message2, |
||||||
|
const FieldDescriptor* field, |
||||||
|
vector<SpecificField>* parent_fields); |
||||||
|
|
||||||
|
// Shorthand for CompareFieldValueUsingParentFields with NULL parent_fields.
|
||||||
|
bool CompareFieldValue(const Message& message1, |
||||||
|
const Message& message2, |
||||||
|
const FieldDescriptor* field, |
||||||
|
int index1, |
||||||
|
int index2); |
||||||
|
|
||||||
|
// Compares the specified field on the two messages, returning
|
||||||
|
// true if they are the same, false otherwise. For repeated fields,
|
||||||
|
// this method only compares the value in the specified index. This method
|
||||||
|
// uses Compare functions to recurse into submessages.
|
||||||
|
// The parent_fields vector is used in calls to a Reporter instance calls.
|
||||||
|
// It can be NULL, in which case the MessageDifferencer will create new
|
||||||
|
// list of parent messages if it needs to recursively compare the given field.
|
||||||
|
// To avoid confusing users you should not set it to NULL unless you modified
|
||||||
|
// Reporter to handle the change of parent_fields correctly.
|
||||||
|
bool CompareFieldValueUsingParentFields(const Message& message1, |
||||||
|
const Message& message2, |
||||||
|
const FieldDescriptor* field, |
||||||
|
int index1, |
||||||
|
int index2, |
||||||
|
vector<SpecificField>* parent_fields); |
||||||
|
|
||||||
|
// Compares the specified field on the two messages, returning comparison
|
||||||
|
// result, as returned by appropriate FieldComparator.
|
||||||
|
FieldComparator::ComparisonResult GetFieldComparisonResult( |
||||||
|
const Message& message1, const Message& message2, |
||||||
|
const FieldDescriptor* field, int index1, int index2, |
||||||
|
const FieldContext* field_context); |
||||||
|
|
||||||
|
// Check if the two elements in the repeated field are match to each other.
|
||||||
|
// if the key_comprator is NULL, this function returns true when the two
|
||||||
|
// elements are equal.
|
||||||
|
bool IsMatch(const FieldDescriptor* repeated_field, |
||||||
|
const MapKeyComparator* key_comparator, |
||||||
|
const Message* message1, const Message* message2, |
||||||
|
const vector<SpecificField>& parent_fields, |
||||||
|
int index1, int index2); |
||||||
|
|
||||||
|
// Returns true when this repeated field has been configured to be treated
|
||||||
|
// as a set.
|
||||||
|
bool IsTreatedAsSet(const FieldDescriptor* field); |
||||||
|
|
||||||
|
// Returns true when this repeated field is to be compared as a subset, ie.
|
||||||
|
// has been configured to be treated as a set or map and scope is set to
|
||||||
|
// PARTIAL.
|
||||||
|
bool IsTreatedAsSubset(const FieldDescriptor* field); |
||||||
|
|
||||||
|
// Returns true if this field is to be ignored when this
|
||||||
|
// MessageDifferencer compares messages.
|
||||||
|
bool IsIgnored( |
||||||
|
const Message& message1, |
||||||
|
const Message& message2, |
||||||
|
const FieldDescriptor* field, |
||||||
|
const vector<SpecificField>& parent_fields); |
||||||
|
|
||||||
|
// Returns MapKeyComparator* when this field has been configured to
|
||||||
|
// be treated as a map. If not, returns NULL.
|
||||||
|
const MapKeyComparator* GetMapKeyComparator(const FieldDescriptor* field); |
||||||
|
|
||||||
|
// Attempts to match indices of a repeated field, so that the contained values
|
||||||
|
// match. Clears output vectors and sets their values to indices of paired
|
||||||
|
// messages, ie. if message1[0] matches message2[1], then match_list1[0] == 1
|
||||||
|
// and match_list2[1] == 0. The unmatched indices are indicated by -1.
|
||||||
|
// This method returns false if the match failed. However, it doesn't mean
|
||||||
|
// that the comparison succeeds when this method returns true (you need to
|
||||||
|
// double-check in this case).
|
||||||
|
bool MatchRepeatedFieldIndices(const Message& message1, |
||||||
|
const Message& message2, |
||||||
|
const FieldDescriptor* repeated_field, |
||||||
|
const vector<SpecificField>& parent_fields, |
||||||
|
vector<int>* match_list1, |
||||||
|
vector<int>* match_list2); |
||||||
|
|
||||||
|
// If "any" is of type google.protobuf.Any, extract its payload using
|
||||||
|
// DynamicMessageFactory and store in "data".
|
||||||
|
bool UnpackAny(const Message& any, google::protobuf::scoped_ptr<Message>* data); |
||||||
|
|
||||||
|
// Checks if index is equal to new_index in all the specific fields.
|
||||||
|
static bool CheckPathChanged(const vector<SpecificField>& parent_fields); |
||||||
|
|
||||||
|
// Defines a map between field descriptors and their MapKeyComparators.
|
||||||
|
// Used for repeated fields when they are configured as TreatAsMap.
|
||||||
|
typedef map<const FieldDescriptor*, |
||||||
|
const MapKeyComparator*> FieldKeyComparatorMap; |
||||||
|
|
||||||
|
// Defines a set to store field descriptors. Used for repeated fields when
|
||||||
|
// they are configured as TreatAsSet.
|
||||||
|
typedef set<const FieldDescriptor*> FieldSet; |
||||||
|
|
||||||
|
Reporter* reporter_; |
||||||
|
DefaultFieldComparator default_field_comparator_; |
||||||
|
FieldComparator* field_comparator_; |
||||||
|
MessageFieldComparison message_field_comparison_; |
||||||
|
Scope scope_; |
||||||
|
RepeatedFieldComparison repeated_field_comparison_; |
||||||
|
|
||||||
|
FieldSet set_fields_; |
||||||
|
// Keeps track of MapKeyComparators that are created within
|
||||||
|
// MessageDifferencer. These MapKeyComparators should be deleted
|
||||||
|
// before MessageDifferencer is destroyed.
|
||||||
|
// When TreatAsMap or TreatAsMapWithMultipleFieldsAsKey is called, we don't
|
||||||
|
// store the supplied FieldDescriptors directly. Instead, a new
|
||||||
|
// MapKeyComparator is created for comparison purpose.
|
||||||
|
vector<MapKeyComparator*> owned_key_comparators_; |
||||||
|
FieldKeyComparatorMap map_field_key_comparator_; |
||||||
|
vector<IgnoreCriteria*> ignore_criteria_; |
||||||
|
|
||||||
|
FieldSet ignored_fields_; |
||||||
|
|
||||||
|
bool compare_unknown_fields_; |
||||||
|
bool report_matches_; |
||||||
|
|
||||||
|
string* output_string_; |
||||||
|
|
||||||
|
google::protobuf::scoped_ptr<DynamicMessageFactory> dynamic_message_factory_; |
||||||
|
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageDifferencer); |
||||||
|
}; |
||||||
|
|
||||||
|
// This class provides extra information to the FieldComparator::Compare
|
||||||
|
// function.
|
||||||
|
class LIBPROTOBUF_EXPORT FieldContext { |
||||||
|
public: |
||||||
|
explicit FieldContext( |
||||||
|
vector<MessageDifferencer::SpecificField>* parent_fields) |
||||||
|
: parent_fields_(parent_fields) {} |
||||||
|
|
||||||
|
vector<MessageDifferencer::SpecificField>* parent_fields() const { |
||||||
|
return parent_fields_; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
vector<MessageDifferencer::SpecificField>* parent_fields_; |
||||||
|
}; |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace google
|
||||||
|
#endif // GOOGLE_PROTOBUF_UTIL_MESSAGE_DIFFERENCER_H__
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,74 @@ |
|||||||
|
// 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. |
||||||
|
|
||||||
|
// Author: kenton@google.com (Kenton Varda) |
||||||
|
// Based on original Protocol Buffers design by |
||||||
|
// Sanjay Ghemawat, Jeff Dean, and others. |
||||||
|
// |
||||||
|
// This file contains messages for testing repeated field comparison |
||||||
|
|
||||||
|
syntax = "proto2"; |
||||||
|
package protobuf_unittest; |
||||||
|
|
||||||
|
option optimize_for = SPEED; |
||||||
|
|
||||||
|
message TestField { |
||||||
|
optional int32 a = 3; |
||||||
|
optional int32 b = 4; |
||||||
|
optional int32 c = 1; |
||||||
|
repeated int32 rc = 2; |
||||||
|
optional TestField m = 5; |
||||||
|
|
||||||
|
extend TestDiffMessage { |
||||||
|
optional TestField tf = 100; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
message TestDiffMessage { |
||||||
|
repeated group Item = 1 { |
||||||
|
optional int32 a = 2; // Test basic repeated field comparison. |
||||||
|
optional string b = 4; // Test basic repeated field comparison. |
||||||
|
repeated int32 ra = 3; // Test SetOfSet Comparison. |
||||||
|
repeated string rb = 5; // Test TreatAsMap when key is repeated |
||||||
|
optional TestField m = 6; // Test TreatAsMap when key is a message |
||||||
|
repeated TestField rm = 7; // Test TreatAsMap when key is a repeated |
||||||
|
// message |
||||||
|
} |
||||||
|
|
||||||
|
optional int32 v = 13 [deprecated = true]; |
||||||
|
optional string w = 14; |
||||||
|
optional TestField m = 15; |
||||||
|
repeated int32 rv = 11; // Test for combinations |
||||||
|
repeated string rw = 10; // Test for combinations |
||||||
|
repeated TestField rm = 12 [deprecated = true]; // Test for combinations |
||||||
|
|
||||||
|
extensions 100 to 199; |
||||||
|
} |
||||||
|
|
@ -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_UTIL_TYPE_RESOLVER_H__ |
||||||
|
#define GOOGLE_PROTOBUF_UTIL_TYPE_RESOLVER_H__ |
||||||
|
|
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include <google/protobuf/stubs/common.h> |
||||||
|
#include <google/protobuf/stubs/status.h> |
||||||
|
|
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
class Type; |
||||||
|
class Enum; |
||||||
|
} // namespace protobuf
|
||||||
|
|
||||||
|
|
||||||
|
namespace protobuf { |
||||||
|
class DescriptorPool; |
||||||
|
namespace util { |
||||||
|
|
||||||
|
// Abstract interface for a type resovler.
|
||||||
|
//
|
||||||
|
// Implementations of this interface must be thread-safe.
|
||||||
|
class LIBPROTOBUF_EXPORT TypeResolver { |
||||||
|
public: |
||||||
|
TypeResolver() {} |
||||||
|
virtual ~TypeResolver() {} |
||||||
|
|
||||||
|
// Resolves a type url for a message type.
|
||||||
|
virtual util::Status ResolveMessageType( |
||||||
|
const string& type_url, google::protobuf::Type* message_type) = 0; |
||||||
|
|
||||||
|
// Resolves a type url for an enum type.
|
||||||
|
virtual util::Status ResolveEnumType(const string& type_url, |
||||||
|
google::protobuf::Enum* enum_type) = 0; |
||||||
|
|
||||||
|
private: |
||||||
|
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(TypeResolver); |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
|
||||||
|
} // namespace google
|
||||||
|
#endif // GOOGLE_PROTOBUF_UTIL_TYPE_RESOLVER_H__
|
@ -0,0 +1,212 @@ |
|||||||
|
// 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/util/type_resolver_util.h> |
||||||
|
|
||||||
|
#include <google/protobuf/type.pb.h> |
||||||
|
#include <google/protobuf/wrappers.pb.h> |
||||||
|
#include <google/protobuf/descriptor.pb.h> |
||||||
|
#include <google/protobuf/descriptor.h> |
||||||
|
#include <google/protobuf/util/type_resolver.h> |
||||||
|
#include <google/protobuf/stubs/status.h> |
||||||
|
|
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
namespace util { |
||||||
|
namespace { |
||||||
|
using google::protobuf::BoolValue; |
||||||
|
using google::protobuf::Enum; |
||||||
|
using google::protobuf::EnumValue; |
||||||
|
using google::protobuf::Field; |
||||||
|
using google::protobuf::Option; |
||||||
|
using google::protobuf::Type; |
||||||
|
|
||||||
|
using util::Status; |
||||||
|
using util::error::INVALID_ARGUMENT; |
||||||
|
using util::error::NOT_FOUND; |
||||||
|
|
||||||
|
bool SplitTypeUrl(const string& type_url, |
||||||
|
string* url_prefix, |
||||||
|
string* message_name) { |
||||||
|
size_t pos = type_url.find_last_of("/"); |
||||||
|
if (pos == string::npos) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
*url_prefix = type_url.substr(0, pos); |
||||||
|
*message_name = type_url.substr(pos + 1); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
class DescriptorPoolTypeResolver : public TypeResolver { |
||||||
|
public: |
||||||
|
DescriptorPoolTypeResolver(const string& url_prefix, |
||||||
|
const DescriptorPool* pool) |
||||||
|
: url_prefix_(url_prefix), pool_(pool) { |
||||||
|
} |
||||||
|
|
||||||
|
Status ResolveMessageType(const string& type_url, Type* type) { |
||||||
|
string url_prefix, message_name; |
||||||
|
if (!SplitTypeUrl(type_url, &url_prefix, &message_name) || |
||||||
|
url_prefix != url_prefix_) { |
||||||
|
return Status(INVALID_ARGUMENT, "Failed to parse type url: " + type_url); |
||||||
|
} |
||||||
|
if (url_prefix != url_prefix_) { |
||||||
|
return Status(INVALID_ARGUMENT, |
||||||
|
"Cannot resolve types from URL: " + url_prefix); |
||||||
|
} |
||||||
|
const Descriptor* descriptor = pool_->FindMessageTypeByName(message_name); |
||||||
|
if (descriptor == NULL) { |
||||||
|
return Status(NOT_FOUND, "Cannot found the type: " + message_name); |
||||||
|
} |
||||||
|
ConvertDescriptor(descriptor, type); |
||||||
|
return Status(); |
||||||
|
} |
||||||
|
|
||||||
|
Status ResolveEnumType(const string& type_url, Enum* enum_type) { |
||||||
|
string url_prefix, type_name; |
||||||
|
if (!SplitTypeUrl(type_url, &url_prefix, &type_name) || |
||||||
|
url_prefix != url_prefix_) { |
||||||
|
return Status(INVALID_ARGUMENT, "Failed to parse type url: " + type_url); |
||||||
|
} |
||||||
|
if (url_prefix != url_prefix_) { |
||||||
|
return Status(INVALID_ARGUMENT, |
||||||
|
"Cannot resolve types from URL: " + url_prefix); |
||||||
|
} |
||||||
|
const EnumDescriptor* descriptor = pool_->FindEnumTypeByName(type_name); |
||||||
|
if (descriptor == NULL) { |
||||||
|
return Status(NOT_FOUND, "Cannot found the type: " + type_name); |
||||||
|
} |
||||||
|
ConvertEnumDescriptor(descriptor, enum_type); |
||||||
|
return Status(); |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
void ConvertDescriptor(const Descriptor* descriptor, Type* type) { |
||||||
|
type->Clear(); |
||||||
|
type->set_name(descriptor->full_name()); |
||||||
|
for (int i = 0; i < descriptor->field_count(); ++i) { |
||||||
|
const FieldDescriptor* field = descriptor->field(i); |
||||||
|
if (field->type() == FieldDescriptor::TYPE_GROUP) { |
||||||
|
// Group fields cannot be represented with Type. We discard them.
|
||||||
|
continue; |
||||||
|
} |
||||||
|
ConvertFieldDescriptor(descriptor->field(i), type->add_fields()); |
||||||
|
} |
||||||
|
for (int i = 0; i < descriptor->oneof_decl_count(); ++i) { |
||||||
|
type->add_oneofs(descriptor->oneof_decl(i)->name()); |
||||||
|
} |
||||||
|
type->mutable_source_context()->set_file_name(descriptor->file()->name()); |
||||||
|
ConvertMessageOptions(descriptor->options(), type->mutable_options()); |
||||||
|
} |
||||||
|
|
||||||
|
void ConvertMessageOptions(const MessageOptions& options, |
||||||
|
RepeatedPtrField<Option>* output) { |
||||||
|
if (options.map_entry()) { |
||||||
|
Option* option = output->Add(); |
||||||
|
option->set_name("map_entry"); |
||||||
|
BoolValue value; |
||||||
|
value.set_value(true); |
||||||
|
option->mutable_value()->PackFrom(value); |
||||||
|
} |
||||||
|
|
||||||
|
// TODO(xiaofeng): Set other "options"?
|
||||||
|
} |
||||||
|
|
||||||
|
void ConvertFieldDescriptor(const FieldDescriptor* descriptor, Field* field) { |
||||||
|
field->set_kind(static_cast<Field::Kind>(descriptor->type())); |
||||||
|
switch (descriptor->label()) { |
||||||
|
case FieldDescriptor::LABEL_OPTIONAL: |
||||||
|
field->set_cardinality(Field::CARDINALITY_OPTIONAL); |
||||||
|
break; |
||||||
|
case FieldDescriptor::LABEL_REPEATED: |
||||||
|
field->set_cardinality(Field::CARDINALITY_REPEATED); |
||||||
|
break; |
||||||
|
case FieldDescriptor::LABEL_REQUIRED: |
||||||
|
field->set_cardinality(Field::CARDINALITY_REQUIRED); |
||||||
|
break; |
||||||
|
} |
||||||
|
field->set_number(descriptor->number()); |
||||||
|
field->set_name(descriptor->name()); |
||||||
|
if (descriptor->type() == FieldDescriptor::TYPE_MESSAGE) { |
||||||
|
field->set_type_url(GetTypeUrl(descriptor->message_type())); |
||||||
|
} else if (descriptor->type() == FieldDescriptor::TYPE_ENUM) { |
||||||
|
field->set_type_url(GetTypeUrl(descriptor->enum_type())); |
||||||
|
} |
||||||
|
if (descriptor->containing_oneof() != NULL) { |
||||||
|
field->set_oneof_index(descriptor->containing_oneof()->index() + 1); |
||||||
|
} |
||||||
|
if (descriptor->is_packed()) { |
||||||
|
field->set_packed(true); |
||||||
|
} |
||||||
|
|
||||||
|
// TODO(xiaofeng): Set other field "options"?
|
||||||
|
} |
||||||
|
|
||||||
|
void ConvertEnumDescriptor(const EnumDescriptor* descriptor, |
||||||
|
Enum* enum_type) { |
||||||
|
enum_type->Clear(); |
||||||
|
enum_type->set_name(descriptor->full_name()); |
||||||
|
enum_type->mutable_source_context()->set_file_name( |
||||||
|
descriptor->file()->name()); |
||||||
|
for (int i = 0; i < descriptor->value_count(); ++i) { |
||||||
|
const EnumValueDescriptor* value_descriptor = descriptor->value(i); |
||||||
|
EnumValue* value = |
||||||
|
enum_type->mutable_enumvalue()->Add(); |
||||||
|
value->set_name(value_descriptor->name()); |
||||||
|
value->set_number(value_descriptor->number()); |
||||||
|
|
||||||
|
// TODO(xiaofeng): Set EnumValue options.
|
||||||
|
} |
||||||
|
// TODO(xiaofeng): Set Enum "options".
|
||||||
|
} |
||||||
|
|
||||||
|
string GetTypeUrl(const Descriptor* descriptor) { |
||||||
|
return url_prefix_ + "/" + descriptor->full_name(); |
||||||
|
} |
||||||
|
|
||||||
|
string GetTypeUrl(const EnumDescriptor* descriptor) { |
||||||
|
return url_prefix_ + "/" + descriptor->full_name(); |
||||||
|
} |
||||||
|
|
||||||
|
string url_prefix_; |
||||||
|
const DescriptorPool* pool_; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TypeResolver* NewTypeResolverForDescriptorPool( |
||||||
|
const string& url_prefix, const DescriptorPool* pool) { |
||||||
|
return new DescriptorPoolTypeResolver(url_prefix, pool); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
} // namespace google
|
@ -0,0 +1,52 @@ |
|||||||
|
// 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_UTIL_TYPE_RESOLVER_UTIL_H__ |
||||||
|
#define GOOGLE_PROTOBUF_UTIL_TYPE_RESOLVER_UTIL_H__ |
||||||
|
|
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include <google/protobuf/stubs/common.h> |
||||||
|
namespace google { |
||||||
|
namespace protobuf { |
||||||
|
class DescriptorPool; |
||||||
|
namespace util { |
||||||
|
class TypeResolver; |
||||||
|
|
||||||
|
// Creates a TypeResolver that serves type information in the given descriptor
|
||||||
|
// pool. Caller takes ownership of the returned TypeResolver.
|
||||||
|
TypeResolver* NewTypeResolverForDescriptorPool( |
||||||
|
const string& url_prefix, const DescriptorPool* pool); |
||||||
|
|
||||||
|
} // namespace util
|
||||||
|
} // namespace protobuf
|
||||||
|
|
||||||
|
} // namespace google
|
||||||
|
#endif // GOOGLE_PROTOBUF_UTIL_TYPE_RESOLVER_UTIL_H__
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue