blob: c5c71a001fdbf40c3e7a5ca24c51bf747df6f83f [file] [log] [blame]
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
#include "proton_bits.hpp"
#include "proton/codec/data.hpp"
#include "proton/value.hpp"
#include "proton/types.hpp"
#include "proton/scalar.hpp"
#include "proton/error.hpp"
#include <ostream>
namespace proton {
using namespace codec;
value::value() {}
value::value(const value& x) { *this = x; }
value::value(const codec::data& x) { if (!x.empty()) data().copy(x); }
value::value(value&& x) { swap(*this, x); }
value& value::operator=(value&& x) { swap(*this, x); return *this; }
value& value::operator=(const value& x) {
if (this != &x) {
if (x.empty())
return *this;
void swap(value& x, value& y) { std::swap(x.data_, y.data_); }
void value::clear() { if (!!data_) data_.clear(); }
namespace internal {
type_id value_base::type() const {
return (!data_ || data_.empty()) ? NULL_TYPE : codec::decoder(*this).next_type();
bool value_base::empty() const { return type() == NULL_TYPE; }
// On demand
codec::data& value_base::data() const {
if (!data_)
data_ = codec::data::create();
return data_;
namespace {
// Compare nodes, return -1 if a<b, 0 if a==b, +1 if a>b
// Forward-declare so we can use it recursively.
int compare_next(decoder& a, decoder& b);
template <class T> int compare(const T& a, const T& b) {
if (a < b) return -1;
else if (a > b) return +1;
else return 0;
int compare_container(decoder& a, decoder& b) {
start sa, sb;
a >> sa;
b >> sb;
// Compare described vs. not-described.
int cmp = compare(sa.is_described, sb.is_described);
if (cmp) return cmp;
// Lexical sort (including descriptor if there is one)
size_t min_size = std::min(sa.size, sb.size) + size_t(sa.is_described);
for (size_t i = 0; i < min_size; ++i) {
cmp = compare_next(a, b);
if (cmp) return cmp;
return compare(sa.size, sb.size);
template <class T> int compare_simple(decoder& a, decoder& b) {
T va = T();
T vb = T();
a >> va;
b >> vb;
return compare(va, vb);
int compare_next(decoder& a, decoder& b) {
// Sort by type_id first.
type_id ta = a.next_type(), tb = b.next_type();
int cmp = compare(ta, tb);
if (cmp) return cmp;
switch (ta) {
case NULL_TYPE: return 0;
case ARRAY:
case LIST:
case MAP:
return compare_container(a, b);
case BOOLEAN: return compare_simple<bool>(a, b);
case UBYTE: return compare_simple<uint8_t>(a, b);
case BYTE: return compare_simple<int8_t>(a, b);
case USHORT: return compare_simple<uint16_t>(a, b);
case SHORT: return compare_simple<int16_t>(a, b);
case UINT: return compare_simple<uint32_t>(a, b);
case INT: return compare_simple<int32_t>(a, b);
case ULONG: return compare_simple<uint64_t>(a, b);
case LONG: return compare_simple<int64_t>(a, b);
case CHAR: return compare_simple<wchar_t>(a, b);
case TIMESTAMP: return compare_simple<timestamp>(a, b);
case FLOAT: return compare_simple<float>(a, b);
case DOUBLE: return compare_simple<double>(a, b);
case DECIMAL32: return compare_simple<decimal32>(a, b);
case DECIMAL64: return compare_simple<decimal64>(a, b);
case DECIMAL128: return compare_simple<decimal128>(a, b);
case UUID: return compare_simple<uuid>(a, b);
case BINARY: return compare_simple<binary>(a, b);
case STRING: return compare_simple<std::string>(a, b);
case SYMBOL: return compare_simple<symbol>(a, b);
// Invalid but equal type_id, treat as equal.
return 0;
int compare(const value& x, const value& y) {
decoder a(x), b(y);
state_guard s1(a), s2(b);
while (a.more() && b.more()) {
int cmp = compare_next(a, b);
if (cmp != 0) return cmp;
if (b.more()) return -1;
if (a.more()) return 1;
return 0;
} // namespace
bool operator==(const value& x, const value& y) {
if (x.empty() && y.empty()) return true;
if (x.empty() || y.empty()) return false;
return compare(x, y) == 0;
bool operator<(const value& x, const value& y) {
if (x.empty() && y.empty()) return false;
if (x.empty()) return true; // empty is < !empty
return compare(x, y) < 0;
namespace internal {
std::ostream& operator<<(std::ostream& o, const internal::value_base& x) {
if (x.empty())
return o << "<null>";
proton::decoder d(x);
// Print the following types with operator<<() consistent with C++.
switch (d.next_type()) {
case BOOLEAN: return o << get<bool>(d); // Respect std::boolalpha settings.
case STRING: return o << get<std::string>(d);
case SYMBOL: return o << get<symbol>(d);
case DECIMAL32: return o << get<decimal32>(d);
case DECIMAL64: return o << get<decimal64>(d);
case DECIMAL128: return o << get<decimal128>(d);
case UUID: return o << get<uuid>(d);
case TIMESTAMP: return o << get<timestamp>(d);
case CHAR: return o << get<wchar_t>(d);
// Use pn_inspect for other types.
return o << d;