blob: eba13888fe11244fe9086d65321433cdd46ed2ee [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/public/common/indexeddb/indexeddb_key.h"
#include <algorithm>
#include <sstream>
#include <string>
#include <utility>
#include "base/strings/string_number_conversions.h"
namespace blink {
namespace {
// Very rough estimate of minimum key size overhead.
const size_t kOverheadSize = 16;
size_t CalculateArraySize(const IndexedDBKey::KeyArray& keys) {
size_t size(0);
for (const auto& key : keys)
size += key.size_estimate();
return size;
}
template <typename T>
int Compare(const T& a, const T& b) {
// Using '<' for both comparisons here is as generic as possible (for e.g.
// objects which only define operator<() and not operator>() or operator==())
// and also allows e.g. floating point NaNs to compare equal.
if (a < b)
return -1;
return (b < a) ? 1 : 0;
}
} // namespace
IndexedDBKey::IndexedDBKey() : IndexedDBKey(mojom::IDBKeyType::None) {}
IndexedDBKey::IndexedDBKey(mojom::IDBKeyType type)
: type_(type), size_estimate_(kOverheadSize) {
DCHECK(!IsValid());
}
IndexedDBKey::IndexedDBKey(double number, mojom::IDBKeyType type)
: type_(type),
number_(number),
size_estimate_(kOverheadSize + sizeof(number)) {
DCHECK(type == mojom::IDBKeyType::Number || type == mojom::IDBKeyType::Date);
}
IndexedDBKey::IndexedDBKey(KeyArray array)
: type_(mojom::IDBKeyType::Array),
array_(std::move(array)),
size_estimate_(kOverheadSize + CalculateArraySize(array_)) {}
IndexedDBKey::IndexedDBKey(std::string binary)
: type_(mojom::IDBKeyType::Binary),
binary_(std::move(binary)),
size_estimate_(kOverheadSize +
(binary_.length() * sizeof(std::string::value_type))) {}
IndexedDBKey::IndexedDBKey(std::u16string string)
: type_(mojom::IDBKeyType::String),
string_(std::move(string)),
size_estimate_(kOverheadSize +
(string_.length() * sizeof(std::u16string::value_type))) {}
IndexedDBKey::IndexedDBKey(IndexedDBKey&& other) = default;
IndexedDBKey& IndexedDBKey::operator=(IndexedDBKey&& other) = default;
IndexedDBKey::~IndexedDBKey() = default;
bool IndexedDBKey::IsValid() const {
switch (type_) {
case mojom::IDBKeyType::Array:
return std::ranges::all_of(array_, &IndexedDBKey::IsValid);
case mojom::IDBKeyType::Binary:
case mojom::IDBKeyType::String:
case mojom::IDBKeyType::Date:
case mojom::IDBKeyType::Number:
return true;
case mojom::IDBKeyType::Invalid:
case mojom::IDBKeyType::None:
case mojom::IDBKeyType::Min:
return false;
}
}
bool IndexedDBKey::IsLessThan(const IndexedDBKey& other) const {
return CompareTo(other) < 0;
}
bool IndexedDBKey::Equals(const IndexedDBKey& other) const {
return !CompareTo(other);
}
bool IndexedDBKey::HasHoles() const {
if (type_ != mojom::IDBKeyType::Array)
return false;
for (const auto& subkey : array_) {
if (subkey.type() == mojom::IDBKeyType::None)
return true;
}
return false;
}
IndexedDBKey IndexedDBKey::Clone() const {
IndexedDBKey clone;
clone.type_ = type_;
for (size_t i = 0; i < array_.size(); ++i) {
clone.array_.emplace_back(array_[i].Clone());
}
clone.binary_ = binary_;
clone.string_ = string_;
clone.number_ = number_;
clone.size_estimate_ = size_estimate_;
return clone;
}
IndexedDBKey IndexedDBKey::FillHoles(const IndexedDBKey& primary_key) const {
if (type_ != mojom::IDBKeyType::Array)
return Clone();
std::vector<IndexedDBKey> subkeys;
subkeys.reserve(array_.size());
for (const IndexedDBKey& subkey : array_) {
if (subkey.type() == mojom::IDBKeyType::None) {
subkeys.emplace_back(primary_key.Clone());
} else {
// "Holes" can only exist at the top level of an array key, as (1) they
// are produced by an index's array keypath when a member matches the
// store's keypath, and (2) array keypaths are flat (no
// arrays-of-arrays).
DCHECK(!subkey.HasHoles());
subkeys.emplace_back(subkey.Clone());
}
}
return IndexedDBKey(std::move(subkeys));
}
std::string IndexedDBKey::DebugString() const {
std::stringstream result;
result << "IDBKey{";
switch (type_) {
case mojom::IDBKeyType::Array: {
result << "array: [";
for (size_t i = 0; i < array_.size(); ++i) {
result << array_[i].DebugString();
if (i != array_.size() - 1)
result << ", ";
}
result << "]";
break;
}
case mojom::IDBKeyType::Binary:
result << "binary: 0x" << base::HexEncode(binary_);
break;
case mojom::IDBKeyType::String:
result << "string: " << string_;
break;
case mojom::IDBKeyType::Date:
result << "date: " << number_;
break;
case mojom::IDBKeyType::Number:
result << "number: " << number_;
break;
case mojom::IDBKeyType::Invalid:
result << "Invalid";
break;
case mojom::IDBKeyType::None:
result << "None";
break;
case mojom::IDBKeyType::Min:
result << "Min";
break;
default:
result << "InvalidKey";
}
result << "}";
return result.str();
}
int IndexedDBKey::CompareTo(const IndexedDBKey& other) const {
DCHECK(IsValid());
DCHECK(other.IsValid());
if (type_ != other.type_)
return type_ > other.type_ ? -1 : 1;
switch (type_) {
case mojom::IDBKeyType::Array:
for (size_t i = 0; i < array_.size() && i < other.array_.size(); ++i) {
int result = array_[i].CompareTo(other.array_[i]);
if (result != 0)
return result;
}
return Compare(array_.size(), other.array_.size());
case mojom::IDBKeyType::Binary:
return binary_.compare(other.binary_);
case mojom::IDBKeyType::String:
return string_.compare(other.string_);
case mojom::IDBKeyType::Date:
case mojom::IDBKeyType::Number:
return Compare(number_, other.number_);
case mojom::IDBKeyType::Invalid:
case mojom::IDBKeyType::None:
case mojom::IDBKeyType::Min:
default:
NOTREACHED();
}
}
} // namespace blink