1/* 2 * Copyright (C) 2014 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. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "IDBKeyData.h" 28 29#if ENABLE(INDEXED_DATABASE) 30 31#include "KeyedCoding.h" 32#include <wtf/text/StringBuilder.h> 33 34namespace WebCore { 35 36IDBKeyData::IDBKeyData(const IDBKey* key) 37 : type(IDBKey::InvalidType) 38 , numberValue(0) 39 , isNull(false) 40{ 41 if (!key) { 42 isNull = true; 43 return; 44 } 45 46 type = key->type(); 47 numberValue = 0; 48 49 switch (type) { 50 case IDBKey::InvalidType: 51 break; 52 case IDBKey::ArrayType: 53 for (auto& key2 : key->array()) 54 arrayValue.append(IDBKeyData(key2.get())); 55 break; 56 case IDBKey::StringType: 57 stringValue = key->string(); 58 break; 59 case IDBKey::DateType: 60 numberValue = key->date(); 61 break; 62 case IDBKey::NumberType: 63 numberValue = key->number(); 64 break; 65 case IDBKey::MaxType: 66 case IDBKey::MinType: 67 break; 68 } 69} 70 71PassRefPtr<IDBKey> IDBKeyData::maybeCreateIDBKey() const 72{ 73 if (isNull) 74 return nullptr; 75 76 switch (type) { 77 case IDBKey::InvalidType: 78 return IDBKey::createInvalid(); 79 case IDBKey::ArrayType: 80 { 81 IDBKey::KeyArray array; 82 for (auto& keyData : arrayValue) { 83 array.append(keyData.maybeCreateIDBKey()); 84 ASSERT(array.last()); 85 } 86 return IDBKey::createArray(array); 87 } 88 case IDBKey::StringType: 89 return IDBKey::createString(stringValue); 90 case IDBKey::DateType: 91 return IDBKey::createDate(numberValue); 92 case IDBKey::NumberType: 93 return IDBKey::createNumber(numberValue); 94 case IDBKey::MaxType: 95 case IDBKey::MinType: 96 ASSERT_NOT_REACHED(); 97 return nullptr; 98 } 99 100 ASSERT_NOT_REACHED(); 101 return nullptr; 102} 103 104IDBKeyData IDBKeyData::isolatedCopy() const 105{ 106 IDBKeyData result; 107 result.type = type; 108 result.isNull = isNull; 109 110 switch (type) { 111 case IDBKey::InvalidType: 112 return result; 113 case IDBKey::ArrayType: 114 for (auto& key : arrayValue) 115 result.arrayValue.append(key.isolatedCopy()); 116 return result; 117 case IDBKey::StringType: 118 result.stringValue = stringValue.isolatedCopy(); 119 return result; 120 case IDBKey::DateType: 121 case IDBKey::NumberType: 122 result.numberValue = numberValue; 123 return result; 124 case IDBKey::MaxType: 125 case IDBKey::MinType: 126 return result; 127 } 128 129 ASSERT_NOT_REACHED(); 130 return result; 131} 132 133void IDBKeyData::encode(KeyedEncoder& encoder) const 134{ 135 encoder.encodeBool("null", isNull); 136 if (isNull) 137 return; 138 139 encoder.encodeEnum("type", type); 140 141 switch (type) { 142 case IDBKey::InvalidType: 143 return; 144 case IDBKey::ArrayType: 145 encoder.encodeObjects("array", arrayValue.begin(), arrayValue.end(), [](KeyedEncoder& encoder, const IDBKeyData& key) { 146 key.encode(encoder); 147 }); 148 return; 149 case IDBKey::StringType: 150 encoder.encodeString("string", stringValue); 151 return; 152 case IDBKey::DateType: 153 case IDBKey::NumberType: 154 encoder.encodeDouble("number", numberValue); 155 return; 156 case IDBKey::MaxType: 157 case IDBKey::MinType: 158 return; 159 } 160 161 ASSERT_NOT_REACHED(); 162} 163 164bool IDBKeyData::decode(KeyedDecoder& decoder, IDBKeyData& result) 165{ 166 if (!decoder.decodeBool("null", result.isNull)) 167 return false; 168 169 if (result.isNull) 170 return true; 171 172 auto enumFunction = [](int64_t value) { 173 return value == IDBKey::MaxType 174 || value == IDBKey::InvalidType 175 || value == IDBKey::ArrayType 176 || value == IDBKey::StringType 177 || value == IDBKey::DateType 178 || value == IDBKey::NumberType 179 || value == IDBKey::MinType; 180 }; 181 if (!decoder.decodeEnum("type", result.type, enumFunction)) 182 return false; 183 184 if (result.type == IDBKey::InvalidType) 185 return true; 186 187 if (result.type == IDBKey::MaxType) 188 return true; 189 190 if (result.type == IDBKey::MinType) 191 return true; 192 193 if (result.type == IDBKey::StringType) 194 return decoder.decodeString("string", result.stringValue); 195 196 if (result.type == IDBKey::NumberType || result.type == IDBKey::DateType) 197 return decoder.decodeDouble("number", result.numberValue); 198 199 ASSERT(result.type == IDBKey::ArrayType); 200 201 auto arrayFunction = [](KeyedDecoder& decoder, IDBKeyData& result) { 202 return decode(decoder, result); 203 }; 204 return decoder.decodeObjects("array", result.arrayValue, arrayFunction); 205} 206 207int IDBKeyData::compare(const IDBKeyData& other) const 208{ 209 if (type == IDBKey::InvalidType) { 210 if (other.type != IDBKey::InvalidType) 211 return -1; 212 if (other.type == IDBKey::InvalidType) 213 return 0; 214 } else if (other.type == IDBKey::InvalidType) 215 return 1; 216 217 // The IDBKey::Type enum is in reverse sort order. 218 if (type != other.type) 219 return type < other.type ? 1 : -1; 220 221 // The types are the same, so handle actual value comparison. 222 switch (type) { 223 case IDBKey::InvalidType: 224 // InvalidType should have been fully handled above 225 ASSERT_NOT_REACHED(); 226 return 0; 227 case IDBKey::ArrayType: 228 for (size_t i = 0; i < arrayValue.size() && i < other.arrayValue.size(); ++i) { 229 if (int result = arrayValue[i].compare(other.arrayValue[i])) 230 return result; 231 } 232 if (arrayValue.size() < other.arrayValue.size()) 233 return -1; 234 if (arrayValue.size() > other.arrayValue.size()) 235 return 1; 236 return 0; 237 case IDBKey::StringType: 238 return codePointCompare(stringValue, other.stringValue); 239 case IDBKey::DateType: 240 case IDBKey::NumberType: 241 if (numberValue == other.numberValue) 242 return 0; 243 return numberValue > other.numberValue ? 1 : -1; 244 case IDBKey::MaxType: 245 case IDBKey::MinType: 246 return 0; 247 } 248 249 ASSERT_NOT_REACHED(); 250 return 0; 251} 252 253#ifndef NDEBUG 254String IDBKeyData::loggingString() const 255{ 256 if (isNull) 257 return "<null>"; 258 259 switch (type) { 260 case IDBKey::InvalidType: 261 return "<invalid>"; 262 case IDBKey::ArrayType: 263 { 264 StringBuilder result; 265 result.appendLiteral("<array> - { "); 266 for (size_t i = 0; i < arrayValue.size(); ++i) { 267 result.append(arrayValue[i].loggingString()); 268 if (i < arrayValue.size() - 1) 269 result.append(", "); 270 } 271 result.append(" }"); 272 return result.toString(); 273 } 274 case IDBKey::StringType: 275 return "<string> - " + stringValue; 276 case IDBKey::DateType: 277 return String::format("Date type - %f", numberValue); 278 case IDBKey::NumberType: 279 return String::format("<number> - %f", numberValue); 280 case IDBKey::MaxType: 281 return "<maximum>"; 282 case IDBKey::MinType: 283 return "<minimum>"; 284 default: 285 return String(); 286 } 287 ASSERT_NOT_REACHED(); 288} 289#endif 290 291void IDBKeyData::setArrayValue(const Vector<IDBKeyData>& value) 292{ 293 *this = IDBKeyData(); 294 arrayValue = value; 295 type = IDBKey::ArrayType; 296 isNull = false; 297} 298 299void IDBKeyData::setStringValue(const String& value) 300{ 301 *this = IDBKeyData(); 302 stringValue = value; 303 type = IDBKey::StringType; 304 isNull = false; 305} 306 307void IDBKeyData::setDateValue(double value) 308{ 309 *this = IDBKeyData(); 310 numberValue = value; 311 type = IDBKey::DateType; 312 isNull = false; 313} 314 315void IDBKeyData::setNumberValue(double value) 316{ 317 *this = IDBKeyData(); 318 numberValue = value; 319 type = IDBKey::NumberType; 320 isNull = false; 321} 322 323} 324 325#endif // ENABLE(INDEXED_DATABASE) 326