325 lines
12 KiB
Diff
325 lines
12 KiB
Diff
diff -up chromium-74.0.3729.169/base/values.cc.2f28731 chromium-74.0.3729.169/base/values.cc
|
|
--- chromium-74.0.3729.169/base/values.cc.2f28731 2019-05-31 15:03:32.200591044 -0400
|
|
+++ chromium-74.0.3729.169/base/values.cc 2019-05-31 15:13:39.546036829 -0400
|
|
@@ -12,6 +12,7 @@
|
|
#include <ostream>
|
|
#include <utility>
|
|
|
|
+#include "base/bit_cast.h"
|
|
#include "base/json/json_writer.h"
|
|
#include "base/logging.h"
|
|
#include "base/memory/ptr_util.h"
|
|
@@ -36,6 +37,9 @@ static_assert(std::is_standard_layout<Va
|
|
"base::Value should be a standard-layout C++ class in order "
|
|
"to avoid undefined behaviour in its implementation!");
|
|
|
|
+static_assert(sizeof(Value::DoubleStorage) == sizeof(double),
|
|
+ "The double and DoubleStorage types should have the same size");
|
|
+
|
|
namespace {
|
|
|
|
const char* const kTypeNames[] = {"null", "boolean", "integer", "double",
|
|
@@ -110,8 +114,6 @@ Value::Value(Value&& that) noexcept {
|
|
InternalMoveConstructFrom(std::move(that));
|
|
}
|
|
|
|
-Value::Value() noexcept : type_(Type::NONE) {}
|
|
-
|
|
Value::Value(Type type) : type_(type) {
|
|
// Initialize with the default value.
|
|
switch (type_) {
|
|
@@ -125,7 +127,7 @@ Value::Value(Type type) : type_(type) {
|
|
int_value_ = 0;
|
|
return;
|
|
case Type::DOUBLE:
|
|
- double_value_ = 0.0;
|
|
+ double_value_ = bit_cast<DoubleStorage>(0.0);
|
|
return;
|
|
case Type::STRING:
|
|
new (&string_value_) std::string();
|
|
@@ -149,21 +151,16 @@ Value::Value(Type type) : type_(type) {
|
|
CHECK(false);
|
|
}
|
|
|
|
-Value::Value(bool in_bool)
|
|
- : bool_type_(Type::BOOLEAN),
|
|
- bool_value_(in_bool) {}
|
|
-
|
|
-Value::Value(int in_int)
|
|
- : int_type_(Type::INTEGER),
|
|
- int_value_(in_int) {}
|
|
+Value::Value(bool in_bool) : type_(Type::BOOLEAN), bool_value_(in_bool) {}
|
|
+
|
|
+Value::Value(int in_int) : type_(Type::INTEGER), int_value_(in_int) {}
|
|
|
|
Value::Value(double in_double)
|
|
- : double_type_(Type::DOUBLE),
|
|
- double_value_(in_double) {
|
|
- if (!std::isfinite(double_value_)) {
|
|
+ : type_(Type::DOUBLE), double_value_(bit_cast<DoubleStorage>(in_double)) {
|
|
+ if (!std::isfinite(in_double)) {
|
|
NOTREACHED() << "Non-finite (i.e. NaN or positive/negative infinity) "
|
|
<< "values cannot be represented in JSON";
|
|
- double_value_ = 0.0;
|
|
+ double_value_ = bit_cast<DoubleStorage>(0.0);
|
|
}
|
|
}
|
|
|
|
@@ -172,8 +169,7 @@ Value::Value(const char* in_string) : Va
|
|
Value::Value(StringPiece in_string) : Value(std::string(in_string)) {}
|
|
|
|
Value::Value(std::string&& in_string) noexcept
|
|
- : string_type_(Type::STRING),
|
|
- string_value_(std::move(in_string)) {
|
|
+ : type_(Type::STRING), string_value_(std::move(in_string)) {
|
|
DCHECK(IsStringUTF8(string_value_));
|
|
}
|
|
|
|
@@ -182,19 +178,15 @@ Value::Value(const char16* in_string16)
|
|
Value::Value(StringPiece16 in_string16) : Value(UTF16ToUTF8(in_string16)) {}
|
|
|
|
Value::Value(const std::vector<char>& in_blob)
|
|
- : binary_type_(Type::BINARY),
|
|
- binary_value_(in_blob.begin(), in_blob.end()) {}
|
|
+ : type_(Type::BINARY), binary_value_(in_blob.begin(), in_blob.end()) {}
|
|
|
|
Value::Value(base::span<const uint8_t> in_blob)
|
|
- : binary_type_(Type::BINARY),
|
|
- binary_value_(in_blob.begin(), in_blob.end()) {}
|
|
+ : type_(Type::BINARY), binary_value_(in_blob.begin(), in_blob.end()) {}
|
|
|
|
Value::Value(BlobStorage&& in_blob) noexcept
|
|
- : binary_type_(Type::BINARY),
|
|
- binary_value_(std::move(in_blob)) {}
|
|
+ : type_(Type::BINARY), binary_value_(std::move(in_blob)) {}
|
|
|
|
-Value::Value(const DictStorage& in_dict)
|
|
- : dict_type_(Type::DICTIONARY), dict_() {
|
|
+Value::Value(const DictStorage& in_dict) : type_(Type::DICTIONARY), dict_() {
|
|
dict_.reserve(in_dict.size());
|
|
for (const auto& it : in_dict) {
|
|
dict_.try_emplace(dict_.end(), it.first,
|
|
@@ -203,18 +195,16 @@ Value::Value(const DictStorage& in_dict)
|
|
}
|
|
|
|
Value::Value(DictStorage&& in_dict) noexcept
|
|
- : dict_type_(Type::DICTIONARY),
|
|
- dict_(std::move(in_dict)) {}
|
|
+ : type_(Type::DICTIONARY), dict_(std::move(in_dict)) {}
|
|
|
|
-Value::Value(const ListStorage& in_list) : list_type_(Type::LIST), list_() {
|
|
+Value::Value(const ListStorage& in_list) : type_(Type::LIST), list_() {
|
|
list_.reserve(in_list.size());
|
|
for (const auto& val : in_list)
|
|
list_.emplace_back(val.Clone());
|
|
}
|
|
|
|
Value::Value(ListStorage&& in_list) noexcept
|
|
- : list_type_(Type::LIST),
|
|
- list_(std::move(in_list)) {}
|
|
+ : type_(Type::LIST), list_(std::move(in_list)) {}
|
|
|
|
Value& Value::operator=(Value&& that) noexcept {
|
|
InternalCleanup();
|
|
@@ -223,6 +213,10 @@ Value& Value::operator=(Value&& that) no
|
|
return *this;
|
|
}
|
|
|
|
+double Value::AsDoubleInternal() const {
|
|
+ return bit_cast<double>(double_value_);
|
|
+}
|
|
+
|
|
Value Value::Clone() const {
|
|
switch (type_) {
|
|
case Type::NONE:
|
|
@@ -232,7 +226,7 @@ Value Value::Clone() const {
|
|
case Type::INTEGER:
|
|
return Value(int_value_);
|
|
case Type::DOUBLE:
|
|
- return Value(double_value_);
|
|
+ return Value(AsDoubleInternal());
|
|
case Type::STRING:
|
|
return Value(string_value_);
|
|
case Type::BINARY:
|
|
@@ -277,7 +271,7 @@ int Value::GetInt() const {
|
|
|
|
double Value::GetDouble() const {
|
|
if (is_double())
|
|
- return double_value_;
|
|
+ return AsDoubleInternal();
|
|
if (is_int())
|
|
return int_value_;
|
|
CHECK(false);
|
|
@@ -342,9 +336,10 @@ base::Optional<double> Value::FindDouble
|
|
const Value* result = FindKey(key);
|
|
if (result) {
|
|
if (result->is_int())
|
|
- return base::make_optional(static_cast<double>(result->int_value_));
|
|
- if (result->is_double())
|
|
- return base::make_optional(result->double_value_);
|
|
+ return static_cast<double>(result->int_value_);
|
|
+ if (result->is_double()) {
|
|
+ return result->AsDoubleInternal();
|
|
+ }
|
|
}
|
|
return base::nullopt;
|
|
}
|
|
@@ -548,7 +543,7 @@ bool Value::GetAsInteger(int* out_value)
|
|
|
|
bool Value::GetAsDouble(double* out_value) const {
|
|
if (out_value && is_double()) {
|
|
- *out_value = double_value_;
|
|
+ *out_value = AsDoubleInternal();
|
|
return true;
|
|
}
|
|
if (out_value && is_int()) {
|
|
@@ -643,7 +638,7 @@ bool operator==(const Value& lhs, const
|
|
case Value::Type::INTEGER:
|
|
return lhs.int_value_ == rhs.int_value_;
|
|
case Value::Type::DOUBLE:
|
|
- return lhs.double_value_ == rhs.double_value_;
|
|
+ return lhs.AsDoubleInternal() == rhs.AsDoubleInternal();
|
|
case Value::Type::STRING:
|
|
return lhs.string_value_ == rhs.string_value_;
|
|
case Value::Type::BINARY:
|
|
@@ -688,7 +683,7 @@ bool operator<(const Value& lhs, const V
|
|
case Value::Type::INTEGER:
|
|
return lhs.int_value_ < rhs.int_value_;
|
|
case Value::Type::DOUBLE:
|
|
- return lhs.double_value_ < rhs.double_value_;
|
|
+ return lhs.AsDoubleInternal() < rhs.AsDoubleInternal();
|
|
case Value::Type::STRING:
|
|
return lhs.string_value_ < rhs.string_value_;
|
|
case Value::Type::BINARY:
|
|
diff -up chromium-74.0.3729.169/base/values.h.2f28731 chromium-74.0.3729.169/base/values.h
|
|
--- chromium-74.0.3729.169/base/values.h.2f28731 2019-05-31 15:13:48.418868163 -0400
|
|
+++ chromium-74.0.3729.169/base/values.h 2019-05-31 15:17:06.015112057 -0400
|
|
@@ -77,12 +77,13 @@ class Value;
|
|
// base::Value dict(base::Value::Type::DICTIONARY);
|
|
// dict.SetKey("mykey", base::Value(foo));
|
|
// return dict;
|
|
-// }
|
|
class BASE_EXPORT Value {
|
|
public:
|
|
using BlobStorage = std::vector<uint8_t>;
|
|
using DictStorage = flat_map<std::string, std::unique_ptr<Value>>;
|
|
using ListStorage = std::vector<Value>;
|
|
+ // See technical note below explaining why this is used.
|
|
+ using DoubleStorage = struct { alignas(4) char v[sizeof(double)]; };
|
|
|
|
enum class Type : unsigned char {
|
|
NONE = 0,
|
|
@@ -111,7 +112,10 @@ class BASE_EXPORT Value {
|
|
static std::unique_ptr<Value> ToUniquePtrValue(Value val);
|
|
|
|
Value(Value&& that) noexcept;
|
|
- Value() noexcept; // A null value.
|
|
+ Value() noexcept {} // A null value
|
|
+ // Fun fact: using '= default' above instead of '{}' does not work because
|
|
+ // the compiler complains that the default constructor was deleted since
|
|
+ // the inner union contains fields with non-default constructors.
|
|
|
|
// Value's copy constructor and copy assignment operator are deleted. Use this
|
|
// to obtain a deep copy explicitly.
|
|
@@ -379,82 +383,29 @@ class BASE_EXPORT Value {
|
|
size_t EstimateMemoryUsage() const;
|
|
|
|
protected:
|
|
- // Technical note:
|
|
- // The naive way to implement a tagged union leads to wasted bytes
|
|
- // in the object on CPUs like ARM ones, which impose an 8-byte alignment
|
|
- // for double values. I.e. if one does something like:
|
|
- //
|
|
- // struct TaggedValue {
|
|
- // int type_; // size = 1, align = 4
|
|
- // union {
|
|
- // bool bool_value_; // size = 1, align = 1
|
|
- // int int_value_; // size = 4, align = 4
|
|
- // double double_value_; // size = 8, align = 8
|
|
- // std::string string_value_; // size = 12, align = 4 (32-bit)
|
|
- // };
|
|
- // };
|
|
- //
|
|
- // The end result is that the union will have an alignment of 8, and a size
|
|
- // of 16, due to 4 extra padding bytes following |string_value_| to respect
|
|
- // the alignment requirement.
|
|
- //
|
|
- // As a consequence, the struct TaggedValue will have a size of 24 bytes,
|
|
- // due to the size of the union (16), the size of |type_| (4) and 4 bytes
|
|
- // of padding between |type_| and the union to respect its alignment.
|
|
- //
|
|
- // This means 8 bytes of unused memory per instance on 32-bit ARM!
|
|
- //
|
|
- // To reclaim these, a union of structs is used instead, in order to ensure
|
|
- // that |double_value_| below is always located at an offset that is a
|
|
- // multiple of 8, relative to the start of the overall data structure.
|
|
- //
|
|
- // Each struct must declare its own |type_| field, which must have a different
|
|
- // name, to appease the C++ compiler.
|
|
- //
|
|
- // Using this technique sizeof(base::Value) == 16 on 32-bit ARM instead
|
|
- // of 24, without losing any information. Results are unchanged for x86,
|
|
- // x86_64 and arm64 (16, 32 and 32 bytes respectively).
|
|
+ // Special case for doubles, which are aligned to 8 bytes on some
|
|
+ // 32-bit architectures. In this case, a simple declaration as a
|
|
+ // double member would make the whole union 8 byte-aligned, which
|
|
+ // would also force 4 bytes of wasted padding space before it in
|
|
+ // the Value layout.
|
|
+ //
|
|
+ // To override this, store the value as an array of 32-bit integers, and
|
|
+ // perform the appropriate bit casts when reading / writing to it.
|
|
+ Type type_ = Type::NONE;
|
|
+
|
|
union {
|
|
- struct {
|
|
- // TODO(crbug.com/646113): Make these private once DictionaryValue and
|
|
- // ListValue are properly inlined.
|
|
- Type type_ : 8;
|
|
- };
|
|
- struct {
|
|
- Type bool_type_ : 8;
|
|
- bool bool_value_;
|
|
- };
|
|
- struct {
|
|
- Type int_type_ : 8;
|
|
- int int_value_;
|
|
- };
|
|
- struct {
|
|
- Type double_type_ : 8;
|
|
- // Subtle: On architectures that require it, the compiler will ensure
|
|
- // that |double_value_|'s offset is a multiple of 8 (e.g. 32-bit ARM).
|
|
- // See technical note above to understand why it is important.
|
|
- double double_value_;
|
|
- };
|
|
- struct {
|
|
- Type string_type_ : 8;
|
|
- std::string string_value_;
|
|
- };
|
|
- struct {
|
|
- Type binary_type_ : 8;
|
|
- BlobStorage binary_value_;
|
|
- };
|
|
- struct {
|
|
- Type dict_type_ : 8;
|
|
- DictStorage dict_;
|
|
- };
|
|
- struct {
|
|
- Type list_type_ : 8;
|
|
- ListStorage list_;
|
|
- };
|
|
+ bool bool_value_;
|
|
+ int int_value_;
|
|
+ DoubleStorage double_value_;
|
|
+ std::string string_value_;
|
|
+ BlobStorage binary_value_;
|
|
+ DictStorage dict_;
|
|
+ ListStorage list_;
|
|
};
|
|
|
|
private:
|
|
friend class ValuesTest_SizeOfValue_Test;
|
|
+ double AsDoubleInternal() const;
|
|
void InternalMoveConstructFrom(Value&& that);
|
|
void InternalCleanup();
|
|
|