blob: 62ab01c2864baa2ef1945031faf42cbeefbfc72b [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/modules/storage/storage_area_map.h"
namespace blink {
namespace {
// For quota purposes we count each character as 2 bytes.
size_t QuotaForString(const String& s) {
return s.length() * sizeof(UChar);
}
} // namespace
StorageAreaMap::StorageAreaMap(size_t quota) : quota_(quota) {
ResetKeyIterator();
}
unsigned StorageAreaMap::GetLength() const {
return keys_values_.size();
}
String StorageAreaMap::GetKey(unsigned index) const {
if (index >= GetLength())
return String();
// Decide if we should leave |key_iterator_| alone, or reset to either the
// beginning or end of the map for shortest iteration distance.
const unsigned distance_to_current = index > last_key_index_
? index - last_key_index_
: last_key_index_ - index;
const unsigned distance_to_end = GetLength() - index;
if (index < distance_to_current && index < distance_to_end) {
// Distance from start is shortest, so reset iterator to begin.
last_key_index_ = 0;
key_iterator_ = keys_values_.begin();
} else if (distance_to_end < distance_to_current && distance_to_end < index) {
// Distance from end is shortest, so reset iterator to end.
last_key_index_ = GetLength();
key_iterator_ = keys_values_.end();
}
while (last_key_index_ < index) {
++key_iterator_;
++last_key_index_;
}
while (last_key_index_ > index) {
--key_iterator_;
--last_key_index_;
}
return key_iterator_->key;
}
String StorageAreaMap::GetItem(const String& key) const {
auto it = keys_values_.find(key);
if (it == keys_values_.end())
return String();
return it->value;
}
bool StorageAreaMap::SetItem(const String& key,
const String& value,
String* old_value) {
return SetItemInternal(key, value, old_value, true);
}
void StorageAreaMap::SetItemIgnoringQuota(const String& key,
const String& value) {
SetItemInternal(key, value, nullptr, false);
}
bool StorageAreaMap::RemoveItem(const String& key, String* old_value) {
const auto it = keys_values_.find(key);
if (it == keys_values_.end())
return false;
quota_used_ -= QuotaForString(key) + QuotaForString(it->value);
if (old_value)
*old_value = it->value;
keys_values_.erase(it);
ResetKeyIterator();
return true;
}
void StorageAreaMap::ResetKeyIterator() const {
key_iterator_ = keys_values_.begin();
last_key_index_ = 0;
}
bool StorageAreaMap::SetItemInternal(const String& key,
const String& value,
String* old_value,
bool check_quota) {
const auto it = keys_values_.find(key);
size_t old_item_size = 0;
if (it != keys_values_.end()) {
old_item_size = QuotaForString(key) + QuotaForString(it->value);
if (old_value)
*old_value = it->value;
}
DCHECK_GE(quota_used_, old_item_size);
size_t new_item_size = QuotaForString(key) + QuotaForString(value);
size_t new_quota_used = quota_used_ - old_item_size + new_item_size;
// Only check quota if the size is increasing, this allows
// shrinking changes to pre-existing files that are over budget.
if (check_quota && new_item_size > old_item_size && new_quota_used > quota_)
return false;
keys_values_.Set(key, value);
ResetKeyIterator();
quota_used_ = new_quota_used;
return true;
}
} // namespace blink