1/* 2 * Copyright (C) 2008 Apple Inc. All Rights Reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "StorageMap.h" 28 29#include <wtf/TemporaryChange.h> 30 31namespace WebCore { 32 33PassRefPtr<StorageMap> StorageMap::create(unsigned quota) 34{ 35 return adoptRef(new StorageMap(quota)); 36} 37 38StorageMap::StorageMap(unsigned quota) 39 : m_iterator(m_map.end()) 40 , m_iteratorIndex(UINT_MAX) 41 , m_quotaSize(quota) // quota measured in bytes 42 , m_currentLength(0) 43{ 44} 45 46PassRefPtr<StorageMap> StorageMap::copy() 47{ 48 RefPtr<StorageMap> newMap = create(m_quotaSize); 49 newMap->m_map = m_map; 50 newMap->m_currentLength = m_currentLength; 51 return newMap.release(); 52} 53 54void StorageMap::invalidateIterator() 55{ 56 m_iterator = m_map.end(); 57 m_iteratorIndex = UINT_MAX; 58} 59 60void StorageMap::setIteratorToIndex(unsigned index) 61{ 62 // FIXME: Once we have bidirectional iterators for HashMap we can be more intelligent about this. 63 // The requested index will be closest to begin(), our current iterator, or end(), and we 64 // can take the shortest route. 65 // Until that mechanism is available, we'll always increment our iterator from begin() or current. 66 67 if (m_iteratorIndex == index) 68 return; 69 70 if (index < m_iteratorIndex) { 71 m_iteratorIndex = 0; 72 m_iterator = m_map.begin(); 73 ASSERT(m_iterator != m_map.end()); 74 } 75 76 while (m_iteratorIndex < index) { 77 ++m_iteratorIndex; 78 ++m_iterator; 79 ASSERT(m_iterator != m_map.end()); 80 } 81} 82 83unsigned StorageMap::length() const 84{ 85 return m_map.size(); 86} 87 88String StorageMap::key(unsigned index) 89{ 90 if (index >= length()) 91 return String(); 92 93 setIteratorToIndex(index); 94 return m_iterator->key; 95} 96 97String StorageMap::getItem(const String& key) const 98{ 99 return m_map.get(key); 100} 101 102PassRefPtr<StorageMap> StorageMap::setItem(const String& key, const String& value, String& oldValue, bool& quotaException) 103{ 104 ASSERT(!value.isNull()); 105 quotaException = false; 106 107 // Implement copy-on-write semantics here. We're guaranteed that the only refs of StorageMaps belong to Storage objects 108 // so if more than one Storage object refs this map, copy it before mutating it. 109 if (refCount() > 1) { 110 RefPtr<StorageMap> newStorageMap = copy(); 111 newStorageMap->setItem(key, value, oldValue, quotaException); 112 return newStorageMap.release(); 113 } 114 115 // Quota tracking. This is done in a couple of steps to keep the overflow tracking simple. 116 unsigned newLength = m_currentLength; 117 bool overflow = newLength + value.length() < newLength; 118 newLength += value.length(); 119 120 oldValue = m_map.get(key); 121 overflow |= newLength - oldValue.length() > newLength; 122 newLength -= oldValue.length(); 123 124 unsigned adjustedKeyLength = oldValue.isNull() ? key.length() : 0; 125 overflow |= newLength + adjustedKeyLength < newLength; 126 newLength += adjustedKeyLength; 127 128 ASSERT(!overflow); // Overflow is bad...even if quotas are off. 129 bool overQuota = newLength > m_quotaSize / sizeof(UChar); 130 if (m_quotaSize != noQuota && (overflow || overQuota)) { 131 quotaException = true; 132 return 0; 133 } 134 m_currentLength = newLength; 135 136 HashMap<String, String>::AddResult addResult = m_map.add(key, value); 137 if (!addResult.isNewEntry) 138 addResult.iterator->value = value; 139 140 invalidateIterator(); 141 142 return 0; 143} 144 145PassRefPtr<StorageMap> StorageMap::setItemIgnoringQuota(const String& key, const String& value) 146{ 147 TemporaryChange<unsigned> quotaSizeChange(m_quotaSize, noQuota); 148 149 String oldValue; 150 bool quotaException; 151 152 RefPtr<StorageMap> map = setItem(key, value, oldValue, quotaException); 153 ASSERT(!quotaException); 154 155 return map.release(); 156} 157 158PassRefPtr<StorageMap> StorageMap::removeItem(const String& key, String& oldValue) 159{ 160 // Implement copy-on-write semantics here. We're guaranteed that the only refs of StorageMaps belong to Storage objects 161 // so if more than one Storage object refs this map, copy it before mutating it. 162 if (refCount() > 1) { 163 RefPtr<StorageMap> newStorage = copy(); 164 newStorage->removeItem(key, oldValue); 165 return newStorage.release(); 166 } 167 168 oldValue = m_map.take(key); 169 if (!oldValue.isNull()) { 170 invalidateIterator(); 171 ASSERT(m_currentLength - key.length() <= m_currentLength); 172 m_currentLength -= key.length(); 173 } 174 ASSERT(m_currentLength - oldValue.length() <= m_currentLength); 175 m_currentLength -= oldValue.length(); 176 177 return 0; 178} 179 180bool StorageMap::contains(const String& key) const 181{ 182 return m_map.contains(key); 183} 184 185void StorageMap::importItems(const HashMap<String, String>& items) 186{ 187 for (HashMap<String, String>::const_iterator it = items.begin(), end = items.end(); it != end; ++it) { 188 const String& key = it->key; 189 const String& value = it->value; 190 191 HashMap<String, String>::AddResult result = m_map.add(key, value); 192 ASSERT_UNUSED(result, result.isNewEntry); // True if the key didn't exist previously. 193 194 ASSERT(m_currentLength + key.length() >= m_currentLength); 195 m_currentLength += key.length(); 196 ASSERT(m_currentLength + value.length() >= m_currentLength); 197 m_currentLength += value.length(); 198 } 199} 200 201} 202