blob: 45119edde3d9b2b2529fb1e284cc60dc71823aeb [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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* 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/internal/data.hpp"
#include "proton/value.hpp"
#include "proton/types.hpp"
#include "proton/scalar.hpp"
#include "proton/error.hpp"
#include <ostream>
#include <sstream>
namespace proton {
using codec::decoder;
using codec::encoder;
using codec::start;
value::value() {}
value::value(pn_data_t *d) { data_ = make_wrapper(d); }
value::value(const value& x) { *this = x; }
#if PN_CPP_HAS_RVALUE_REFERENCES
value::value(value&& x) { swap(*this, x); }
value& value::operator=(value&& x) { swap(*this, x); return *this; }
#endif
value& value::operator=(const value& x) {
if (this != &x) {
if (x.empty())
clear();
else
data().copy(x.data_);
}
return *this;
}
void swap(value& x, value& y) { std::swap(x.data_, y.data_); }
void value::clear() { if (!!data_) data_.clear(); }
namespace internal {
// On demand
internal::data& value_base::data() {
if (!data_)
data_ = internal::data::create();
return data_;
}
}
type_id value::type() const {
return (!data_ || data_.empty()) ? NULL_TYPE : codec::decoder(*this).next_type();
}
bool value::empty() const { return type() == NULL_TYPE; }
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:
case DESCRIBED:
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);
}
// Avoid unreached diagnostic from clang
#if defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunreachable-code"
#endif
// Invalid but equal type_id, treat as equal.
return 0;
#if defined(__clang__)
#pragma GCC diagnostic pop
#endif
}
int compare(const value& x, const value& y) {
decoder a(x), b(y);
internal::state_guard s1(a), s2(b);
a.rewind();
b.rewind();
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;
}
std::ostream& operator<<(std::ostream& o, const value& x) {
if (type_id_is_scalar(x.type()))
return o << proton::get<scalar>(x); // Print as a scalar
// Use pn_inspect for complex types.
proton::decoder d(x);
return o << d;
}
std::string to_string(const value& x) {
std::ostringstream os;
os << std::boolalpha << x;
return os.str();
}
void value::reset(pn_data_t *d) { data_ = make_wrapper(d); }
} // namespace proton