[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