/* * Copyright (C) 2008 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "StorageMap.h" #if ENABLE(DOM_STORAGE) namespace WebCore { PassRefPtr StorageMap::create(unsigned quota) { return adoptRef(new StorageMap(quota)); } StorageMap::StorageMap(unsigned quota) : m_iterator(m_map.end()) , m_iteratorIndex(UINT_MAX) , m_quotaSize(quota) // quota measured in bytes , m_currentLength(0) { } PassRefPtr StorageMap::copy() { RefPtr newMap = create(m_quotaSize); newMap->m_map = m_map; newMap->m_currentLength = m_currentLength; return newMap.release(); } void StorageMap::invalidateIterator() { m_iterator = m_map.end(); m_iteratorIndex = UINT_MAX; } void StorageMap::setIteratorToIndex(unsigned index) { // FIXME: Once we have bidirectional iterators for HashMap we can be more intelligent about this. // The requested index will be closest to begin(), our current iterator, or end(), and we // can take the shortest route. // Until that mechanism is available, we'll always increment our iterator from begin() or current. if (m_iteratorIndex == index) return; if (index < m_iteratorIndex) { m_iteratorIndex = 0; m_iterator = m_map.begin(); ASSERT(m_iterator != m_map.end()); } while (m_iteratorIndex < index) { ++m_iteratorIndex; ++m_iterator; ASSERT(m_iterator != m_map.end()); } } unsigned StorageMap::length() const { return m_map.size(); } String StorageMap::key(unsigned index) { if (index >= length()) return String(); setIteratorToIndex(index); return m_iterator->first; } String StorageMap::getItem(const String& key) const { return m_map.get(key); } PassRefPtr StorageMap::setItem(const String& key, const String& value, String& oldValue, bool& quotaException) { ASSERT(!value.isNull()); quotaException = false; // Implement copy-on-write semantics here. We're guaranteed that the only refs of StorageMaps belong to Storage objects // so if more than one Storage object refs this map, copy it before mutating it. if (refCount() > 1) { RefPtr newStorageMap = copy(); newStorageMap->setItem(key, value, oldValue, quotaException); return newStorageMap.release(); } // Quota tracking. This is done in a couple of steps to keep the overflow tracking simple. unsigned newLength = m_currentLength; bool overflow = newLength + value.length() < newLength; newLength += value.length(); oldValue = m_map.get(key); overflow |= newLength - oldValue.length() > newLength; newLength -= oldValue.length(); unsigned adjustedKeyLength = oldValue.isNull() ? key.length() : 0; overflow |= newLength + adjustedKeyLength < newLength; newLength += adjustedKeyLength; ASSERT(!overflow); // Overflow is bad...even if quotas are off. bool overQuota = newLength > m_quotaSize / sizeof(UChar); if (m_quotaSize != noQuota && (overflow || overQuota)) { quotaException = true; return 0; } m_currentLength = newLength; pair::iterator, bool> addResult = m_map.add(key, value); if (!addResult.second) addResult.first->second = value; invalidateIterator(); return 0; } PassRefPtr StorageMap::removeItem(const String& key, String& oldValue) { // Implement copy-on-write semantics here. We're guaranteed that the only refs of StorageMaps belong to Storage objects // so if more than one Storage object refs this map, copy it before mutating it. if (refCount() > 1) { RefPtr newStorage = copy(); newStorage->removeItem(key, oldValue); return newStorage.release(); } oldValue = m_map.take(key); if (!oldValue.isNull()) { invalidateIterator(); ASSERT(m_currentLength - key.length() <= m_currentLength); m_currentLength -= key.length(); } ASSERT(m_currentLength - oldValue.length() <= m_currentLength); m_currentLength -= oldValue.length(); return 0; } bool StorageMap::contains(const String& key) const { return m_map.contains(key); } void StorageMap::importItem(const String& key, const String& value) { // Be sure to copy the keys/values as items imported on a background thread are destined // to cross a thread boundary pair::iterator, bool> result = m_map.add(key.threadsafeCopy(), value.threadsafeCopy()); ASSERT(result.second); // True if the key didn't exist previously. ASSERT(m_currentLength + key.length() >= m_currentLength); m_currentLength += key.length(); ASSERT(m_currentLength + value.length() >= m_currentLength); m_currentLength += value.length(); } } #endif // ENABLE(DOM_STORAGE)