blob: 46bcb815a0d8e1ac4f72f47aa9297c8ad66a507c [file] [log] [blame]
// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CBOR_VALUES_H_
#define CBOR_VALUES_H_
#include <stdint.h>
#include <string>
#include <tuple>
#include <vector>
#include "base/check.h"
#include "base/containers/flat_map.h"
#include "base/containers/span.h"
#include "base/notreached.h"
#include "base/notreached.h"
#include "base/strings/string_piece.h"
#include "cbor/cbor_export.h"
namespace cbor {
// A class for Concise Binary Object Representation (CBOR) values.
// This does not support:
// * Floating-point numbers.
// * Indefinite-length encodings.
class CBOR_EXPORT Value {
struct Less {
// Comparison predicate to order keys in a dictionary as required by the
// canonical CBOR order defined in
// TODO(808022): Clarify where this stands.
bool operator()(const Value& a, const Value& b) const {
// The current implementation only supports integer, text string, byte
// string and invalid UTF8 keys.
DCHECK((a.is_integer() || a.is_string() || a.is_bytestring() ||
a.is_invalid_utf8()) &&
(b.is_integer() || b.is_string() || b.is_bytestring() ||
// Below text from errata 4409:
// * If the major types are different, the one with the lower value
// in numerical order sorts earlier.
if (a.type() != b.type())
return a.type() < b.type();
// * If two keys have different lengths, the shorter one sorts
// earlier;
// * If two keys have the same length, the one with the lower value
// in (byte-wise) lexical order sorts earlier.
switch (a.type()) {
case Type::UNSIGNED:
// For unsigned integers, the smaller value has shorter length,
// and (byte-wise) lexical representation.
return a.GetInteger() < b.GetInteger();
case Type::NEGATIVE:
// For negative integers, the value closer to zero has shorter length,
// and (byte-wise) lexical representation.
return a.GetInteger() > b.GetInteger();
case Type::STRING: {
const auto& a_str = a.GetString();
const size_t a_length = a_str.size();
const auto& b_str = b.GetString();
const size_t b_length = b_str.size();
return std::tie(a_length, a_str) < std::tie(b_length, b_str);
case Type::BYTE_STRING: {
const auto& a_str = a.GetBytestring();
const size_t a_length = a_str.size();
const auto& b_str = b.GetBytestring();
const size_t b_length = b_str.size();
return std::tie(a_length, a_str) < std::tie(b_length, b_str);
case Type::INVALID_UTF8: {
const auto& a_str = a.GetInvalidUTF8();
const size_t a_length = a_str.size();
const auto& b_str = b.GetInvalidUTF8();
const size_t b_length = b_str.size();
return std::tie(a_length, a_str) < std::tie(b_length, b_str);
return false;
using is_transparent = void;
using BinaryValue = std::vector<uint8_t>;
using ArrayValue = std::vector<Value>;
using MapValue = base::flat_map<Value, Value, Less>;
enum class Type {
ARRAY = 4,
MAP = 5,
TAG = 6,
NONE = -1,
enum class SimpleValue {
// Returns a Value with Type::INVALID_UTF8. This factory method lets tests
// encode such a value as a CBOR string. It should never be used outside of
// tests since encoding may yield invalid CBOR data.
static Value InvalidUTF8StringValueForTesting(base::StringPiece in_string);
Value(Value&& that) noexcept;
Value() noexcept; // A NONE value.
explicit Value(Type type);
explicit Value(SimpleValue in_simple);
explicit Value(bool boolean_value);
explicit Value(int integer_value);
explicit Value(int64_t integer_value);
explicit Value(uint64_t integer_value) = delete;
explicit Value(base::span<const uint8_t> in_bytes);
explicit Value(BinaryValue&& in_bytes) noexcept;
explicit Value(const char* in_string, Type type = Type::STRING);
explicit Value(std::string&& in_string, Type type = Type::STRING) noexcept;
explicit Value(base::StringPiece in_string, Type type = Type::STRING);
explicit Value(const ArrayValue& in_array);
explicit Value(ArrayValue&& in_array) noexcept;
explicit Value(const MapValue& in_map);
explicit Value(MapValue&& in_map) noexcept;
Value& operator=(Value&& that) noexcept;
Value(const Value&) = delete;
Value& operator=(const Value&) = delete;
// Value's copy constructor and copy assignment operator are deleted.
// Use this to obtain a deep copy explicitly.
Value Clone() const;
// Returns the type of the value stored by the current Value object.
Type type() const { return type_; }
// Returns true if the current object represents a given type.
bool is_type(Type type) const { return type == type_; }
bool is_none() const { return type() == Type::NONE; }
bool is_invalid_utf8() const { return type() == Type::INVALID_UTF8; }
bool is_simple() const { return type() == Type::SIMPLE_VALUE; }
bool is_bool() const {
return is_simple() && (simple_value_ == SimpleValue::TRUE_VALUE ||
simple_value_ == SimpleValue::FALSE_VALUE);
bool is_unsigned() const { return type() == Type::UNSIGNED; }
bool is_negative() const { return type() == Type::NEGATIVE; }
bool is_integer() const { return is_unsigned() || is_negative(); }
bool is_bytestring() const { return type() == Type::BYTE_STRING; }
bool is_string() const { return type() == Type::STRING; }
bool is_array() const { return type() == Type::ARRAY; }
bool is_map() const { return type() == Type::MAP; }
// These will all fatally assert if the type doesn't match.
SimpleValue GetSimpleValue() const;
bool GetBool() const;
const int64_t& GetInteger() const;
const int64_t& GetUnsigned() const;
const int64_t& GetNegative() const;
const BinaryValue& GetBytestring() const;
base::StringPiece GetBytestringAsString() const;
// Returned string may contain NUL characters.
const std::string& GetString() const;
const ArrayValue& GetArray() const;
const MapValue& GetMap() const;
const BinaryValue& GetInvalidUTF8() const;
friend class Reader;
// This constructor allows INVALID_UTF8 values to be created, which only
// |Reader| and InvalidUTF8StringValueForTesting() may do.
Value(base::span<const uint8_t> in_bytes, Type type);
Type type_;
union {
SimpleValue simple_value_;
int64_t integer_value_;
BinaryValue bytestring_value_;
std::string string_value_;
ArrayValue array_value_;
MapValue map_value_;
void InternalMoveConstructFrom(Value&& that);
void InternalCleanup();
} // namespace cbor
#endif // CBOR_VALUES_H_