blob: 98204723f7a4338a89526e2e15a7ba3daa1fbeca [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/containers/flat_map.h"
#include "base/containers/span.h"
#include "base/logging.h"
#include "base/macros.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 {
public:
struct Less {
// Comparison predicate to order keys in a dictionary as required by the
// canonical CBOR order defined in
// https://tools.ietf.org/html/rfc7049#section-3.9
// 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() ||
b.is_invalid_utf8()));
// Below text from https://tools.ietf.org/html/rfc7049 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);
}
default:
break;
}
NOTREACHED();
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 {
UNSIGNED = 0,
NEGATIVE = 1,
BYTE_STRING = 2,
STRING = 3,
ARRAY = 4,
MAP = 5,
TAG = 6,
SIMPLE_VALUE = 7,
NONE = -1,
INVALID_UTF8 = -2,
};
enum class SimpleValue {
FALSE_VALUE = 20,
TRUE_VALUE = 21,
NULL_VALUE = 22,
UNDEFINED = 23,
};
// 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();
// 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;
private:
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();
DISALLOW_COPY_AND_ASSIGN(Value);
};
} // namespace cbor
#endif // CBOR_VALUES_H_