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 | #include <wtf/text/StringConcatenateNumbers.h> |
34 | |
35 | namespace WebCore { |
36 | |
37 | IDBKeyData::IDBKeyData(const IDBKey* key) |
38 | : m_type(IndexedDB::KeyType::Invalid) |
39 | { |
40 | if (!key) { |
41 | m_isNull = true; |
42 | return; |
43 | } |
44 | |
45 | m_type = key->type(); |
46 | |
47 | switch (m_type) { |
48 | case IndexedDB::KeyType::Invalid: |
49 | break; |
50 | case IndexedDB::KeyType::Array: { |
51 | m_value = Vector<IDBKeyData>(); |
52 | auto& array = WTF::get<Vector<IDBKeyData>>(m_value); |
53 | for (auto& key2 : key->array()) |
54 | array.append(IDBKeyData(key2.get())); |
55 | break; |
56 | } |
57 | case IndexedDB::KeyType::Binary: |
58 | m_value = key->binary(); |
59 | break; |
60 | case IndexedDB::KeyType::String: |
61 | m_value = key->string(); |
62 | break; |
63 | case IndexedDB::KeyType::Date: |
64 | m_value = key->date(); |
65 | break; |
66 | case IndexedDB::KeyType::Number: |
67 | m_value = key->number(); |
68 | break; |
69 | case IndexedDB::KeyType::Max: |
70 | case IndexedDB::KeyType::Min: |
71 | break; |
72 | } |
73 | } |
74 | |
75 | RefPtr<IDBKey> IDBKeyData::maybeCreateIDBKey() const |
76 | { |
77 | if (m_isNull) |
78 | return nullptr; |
79 | |
80 | switch (m_type) { |
81 | case IndexedDB::KeyType::Invalid: |
82 | return IDBKey::createInvalid(); |
83 | case IndexedDB::KeyType::Array: { |
84 | Vector<RefPtr<IDBKey>> array; |
85 | for (auto& keyData : WTF::get<Vector<IDBKeyData>>(m_value)) { |
86 | array.append(keyData.maybeCreateIDBKey()); |
87 | ASSERT(array.last()); |
88 | } |
89 | return IDBKey::createArray(array); |
90 | } |
91 | case IndexedDB::KeyType::Binary: |
92 | return IDBKey::createBinary(WTF::get<ThreadSafeDataBuffer>(m_value)); |
93 | case IndexedDB::KeyType::String: |
94 | return IDBKey::createString(WTF::get<String>(m_value)); |
95 | case IndexedDB::KeyType::Date: |
96 | return IDBKey::createDate(WTF::get<double>(m_value)); |
97 | case IndexedDB::KeyType::Number: |
98 | return IDBKey::createNumber(WTF::get<double>(m_value)); |
99 | case IndexedDB::KeyType::Max: |
100 | case IndexedDB::KeyType::Min: |
101 | ASSERT_NOT_REACHED(); |
102 | return nullptr; |
103 | } |
104 | |
105 | ASSERT_NOT_REACHED(); |
106 | return nullptr; |
107 | } |
108 | |
109 | IDBKeyData::IDBKeyData(const IDBKeyData& that, IsolatedCopyTag) |
110 | { |
111 | isolatedCopy(that, *this); |
112 | } |
113 | |
114 | IDBKeyData IDBKeyData::isolatedCopy() const |
115 | { |
116 | return { *this, IsolatedCopy }; |
117 | } |
118 | |
119 | void IDBKeyData::isolatedCopy(const IDBKeyData& source, IDBKeyData& destination) |
120 | { |
121 | destination.m_type = source.m_type; |
122 | destination.m_isNull = source.m_isNull; |
123 | |
124 | switch (source.m_type) { |
125 | case IndexedDB::KeyType::Invalid: |
126 | return; |
127 | case IndexedDB::KeyType::Array: { |
128 | destination.m_value = Vector<IDBKeyData>(); |
129 | auto& destinationArray = WTF::get<Vector<IDBKeyData>>(destination.m_value); |
130 | for (auto& key : WTF::get<Vector<IDBKeyData>>(source.m_value)) |
131 | destinationArray.append(key.isolatedCopy()); |
132 | return; |
133 | } |
134 | case IndexedDB::KeyType::Binary: |
135 | destination.m_value = WTF::get<ThreadSafeDataBuffer>(source.m_value); |
136 | return; |
137 | case IndexedDB::KeyType::String: |
138 | destination.m_value = WTF::get<String>(source.m_value).isolatedCopy(); |
139 | return; |
140 | case IndexedDB::KeyType::Date: |
141 | case IndexedDB::KeyType::Number: |
142 | destination.m_value = WTF::get<double>(source.m_value); |
143 | return; |
144 | case IndexedDB::KeyType::Max: |
145 | case IndexedDB::KeyType::Min: |
146 | return; |
147 | } |
148 | |
149 | ASSERT_NOT_REACHED(); |
150 | } |
151 | |
152 | void IDBKeyData::encode(KeyedEncoder& encoder) const |
153 | { |
154 | encoder.encodeBool("null" , m_isNull); |
155 | if (m_isNull) |
156 | return; |
157 | |
158 | encoder.encodeEnum("type" , m_type); |
159 | |
160 | switch (m_type) { |
161 | case IndexedDB::KeyType::Invalid: |
162 | return; |
163 | case IndexedDB::KeyType::Array: { |
164 | auto& array = WTF::get<Vector<IDBKeyData>>(m_value); |
165 | encoder.encodeObjects("array" , array.begin(), array.end(), [](KeyedEncoder& encoder, const IDBKeyData& key) { |
166 | key.encode(encoder); |
167 | }); |
168 | return; |
169 | } |
170 | case IndexedDB::KeyType::Binary: { |
171 | auto* data = WTF::get<ThreadSafeDataBuffer>(m_value).data(); |
172 | encoder.encodeBool("hasBinary" , !!data); |
173 | if (data) |
174 | encoder.encodeBytes("binary" , data->data(), data->size()); |
175 | return; |
176 | } |
177 | case IndexedDB::KeyType::String: |
178 | encoder.encodeString("string" , WTF::get<String>(m_value)); |
179 | return; |
180 | case IndexedDB::KeyType::Date: |
181 | case IndexedDB::KeyType::Number: |
182 | encoder.encodeDouble("number" , WTF::get<double>(m_value)); |
183 | return; |
184 | case IndexedDB::KeyType::Max: |
185 | case IndexedDB::KeyType::Min: |
186 | return; |
187 | } |
188 | |
189 | ASSERT_NOT_REACHED(); |
190 | } |
191 | |
192 | bool IDBKeyData::decode(KeyedDecoder& decoder, IDBKeyData& result) |
193 | { |
194 | if (!decoder.decodeBool("null" , result.m_isNull)) |
195 | return false; |
196 | |
197 | if (result.m_isNull) |
198 | return true; |
199 | |
200 | auto enumFunction = [](int64_t value) { |
201 | return value == IndexedDB::KeyType::Max |
202 | || value == IndexedDB::KeyType::Invalid |
203 | || value == IndexedDB::KeyType::Array |
204 | || value == IndexedDB::KeyType::Binary |
205 | || value == IndexedDB::KeyType::String |
206 | || value == IndexedDB::KeyType::Date |
207 | || value == IndexedDB::KeyType::Number |
208 | || value == IndexedDB::KeyType::Min; |
209 | }; |
210 | if (!decoder.decodeEnum("type" , result.m_type, enumFunction)) |
211 | return false; |
212 | |
213 | if (result.m_type == IndexedDB::KeyType::Invalid) |
214 | return true; |
215 | |
216 | if (result.m_type == IndexedDB::KeyType::Max) |
217 | return true; |
218 | |
219 | if (result.m_type == IndexedDB::KeyType::Min) |
220 | return true; |
221 | |
222 | if (result.m_type == IndexedDB::KeyType::String) { |
223 | result.m_value = String(); |
224 | return decoder.decodeString("string" , WTF::get<String>(result.m_value)); |
225 | } |
226 | |
227 | if (result.m_type == IndexedDB::KeyType::Number || result.m_type == IndexedDB::KeyType::Date) { |
228 | result.m_value = 0.0; |
229 | return decoder.decodeDouble("number" , WTF::get<double>(result.m_value)); |
230 | } |
231 | |
232 | if (result.m_type == IndexedDB::KeyType::Binary) { |
233 | result.m_value = ThreadSafeDataBuffer(); |
234 | |
235 | bool hasBinaryData; |
236 | if (!decoder.decodeBool("hasBinary" , hasBinaryData)) |
237 | return false; |
238 | |
239 | if (!hasBinaryData) |
240 | return true; |
241 | |
242 | Vector<uint8_t> bytes; |
243 | if (!decoder.decodeBytes("binary" , bytes)) |
244 | return false; |
245 | |
246 | result.m_value = ThreadSafeDataBuffer::create(WTFMove(bytes)); |
247 | return true; |
248 | } |
249 | |
250 | ASSERT(result.m_type == IndexedDB::KeyType::Array); |
251 | |
252 | auto arrayFunction = [](KeyedDecoder& decoder, IDBKeyData& result) { |
253 | return decode(decoder, result); |
254 | }; |
255 | |
256 | result.m_value = Vector<IDBKeyData>(); |
257 | return decoder.decodeObjects("array" , WTF::get<Vector<IDBKeyData>>(result.m_value), arrayFunction); |
258 | } |
259 | |
260 | int IDBKeyData::compare(const IDBKeyData& other) const |
261 | { |
262 | if (m_type == IndexedDB::KeyType::Invalid) { |
263 | if (other.m_type != IndexedDB::KeyType::Invalid) |
264 | return -1; |
265 | if (other.m_type == IndexedDB::KeyType::Invalid) |
266 | return 0; |
267 | } else if (other.m_type == IndexedDB::KeyType::Invalid) |
268 | return 1; |
269 | |
270 | // The IDBKey::m_type enum is in reverse sort order. |
271 | if (m_type != other.m_type) |
272 | return m_type < other.m_type ? 1 : -1; |
273 | |
274 | // The types are the same, so handle actual value comparison. |
275 | switch (m_type) { |
276 | case IndexedDB::KeyType::Invalid: |
277 | // Invalid type should have been fully handled above |
278 | ASSERT_NOT_REACHED(); |
279 | return 0; |
280 | case IndexedDB::KeyType::Array: { |
281 | auto& array = WTF::get<Vector<IDBKeyData>>(m_value); |
282 | auto& otherArray = WTF::get<Vector<IDBKeyData>>(other.m_value); |
283 | for (size_t i = 0; i < array.size() && i < otherArray.size(); ++i) { |
284 | if (int result = array[i].compare(otherArray[i])) |
285 | return result; |
286 | } |
287 | if (array.size() < otherArray.size()) |
288 | return -1; |
289 | if (array.size() > otherArray.size()) |
290 | return 1; |
291 | return 0; |
292 | } |
293 | case IndexedDB::KeyType::Binary: |
294 | return compareBinaryKeyData(WTF::get<ThreadSafeDataBuffer>(m_value), WTF::get<ThreadSafeDataBuffer>(other.m_value)); |
295 | case IndexedDB::KeyType::String: |
296 | return codePointCompare(WTF::get<String>(m_value), WTF::get<String>(other.m_value)); |
297 | case IndexedDB::KeyType::Date: |
298 | case IndexedDB::KeyType::Number: { |
299 | auto number = WTF::get<double>(m_value); |
300 | auto otherNumber = WTF::get<double>(other.m_value); |
301 | |
302 | if (number == otherNumber) |
303 | return 0; |
304 | return number > otherNumber ? 1 : -1; |
305 | } |
306 | case IndexedDB::KeyType::Max: |
307 | case IndexedDB::KeyType::Min: |
308 | return 0; |
309 | } |
310 | |
311 | ASSERT_NOT_REACHED(); |
312 | return 0; |
313 | } |
314 | |
315 | #if !LOG_DISABLED |
316 | String IDBKeyData::loggingString() const |
317 | { |
318 | if (m_isNull) |
319 | return "<null>"_s ; |
320 | |
321 | String result; |
322 | |
323 | switch (m_type) { |
324 | case IndexedDB::KeyType::Invalid: |
325 | return "<invalid>"_s ; |
326 | case IndexedDB::KeyType::Array: { |
327 | StringBuilder builder; |
328 | builder.appendLiteral("<array> - { " ); |
329 | auto& array = WTF::get<Vector<IDBKeyData>>(m_value); |
330 | for (size_t i = 0; i < array.size(); ++i) { |
331 | builder.append(array[i].loggingString()); |
332 | if (i < array.size() - 1) |
333 | builder.appendLiteral(", " ); |
334 | } |
335 | builder.appendLiteral(" }" ); |
336 | result = builder.toString(); |
337 | break; |
338 | } |
339 | case IndexedDB::KeyType::Binary: { |
340 | StringBuilder builder; |
341 | builder.appendLiteral("<binary> - " ); |
342 | |
343 | auto* data = WTF::get<ThreadSafeDataBuffer>(m_value).data(); |
344 | if (!data) { |
345 | builder.appendLiteral("(null)" ); |
346 | result = builder.toString(); |
347 | break; |
348 | } |
349 | |
350 | size_t i = 0; |
351 | for (; i < 8 && i < data->size(); ++i) { |
352 | uint8_t byte = data->at(i); |
353 | builder.append(upperNibbleToLowercaseASCIIHexDigit(byte)); |
354 | builder.append(lowerNibbleToLowercaseASCIIHexDigit(byte)); |
355 | } |
356 | |
357 | if (data->size() > 8) |
358 | builder.appendLiteral("..." ); |
359 | |
360 | result = builder.toString(); |
361 | break; |
362 | } |
363 | case IndexedDB::KeyType::String: |
364 | result = "<string> - " + WTF::get<String>(m_value); |
365 | break; |
366 | case IndexedDB::KeyType::Date: |
367 | return makeString("<date> - " , FormattedNumber::fixedWidth(WTF::get<double>(m_value), 6)); |
368 | case IndexedDB::KeyType::Number: |
369 | return makeString("<number> - " , FormattedNumber::fixedWidth(WTF::get<double>(m_value), 6)); |
370 | case IndexedDB::KeyType::Max: |
371 | return "<maximum>"_s ; |
372 | case IndexedDB::KeyType::Min: |
373 | return "<minimum>"_s ; |
374 | } |
375 | |
376 | if (result.length() > 150) { |
377 | result.truncate(147); |
378 | result.append("..."_s ); |
379 | } |
380 | |
381 | return result; |
382 | } |
383 | #endif |
384 | |
385 | void IDBKeyData::setArrayValue(const Vector<IDBKeyData>& value) |
386 | { |
387 | *this = IDBKeyData(); |
388 | m_value = value; |
389 | m_type = IndexedDB::KeyType::Array; |
390 | m_isNull = false; |
391 | } |
392 | |
393 | void IDBKeyData::setBinaryValue(const ThreadSafeDataBuffer& value) |
394 | { |
395 | *this = IDBKeyData(); |
396 | m_value = value; |
397 | m_type = IndexedDB::KeyType::Binary; |
398 | m_isNull = false; |
399 | } |
400 | |
401 | void IDBKeyData::setStringValue(const String& value) |
402 | { |
403 | *this = IDBKeyData(); |
404 | m_value = value; |
405 | m_type = IndexedDB::KeyType::String; |
406 | m_isNull = false; |
407 | } |
408 | |
409 | void IDBKeyData::setDateValue(double value) |
410 | { |
411 | *this = IDBKeyData(); |
412 | m_value = value; |
413 | m_type = IndexedDB::KeyType::Date; |
414 | m_isNull = false; |
415 | } |
416 | |
417 | void IDBKeyData::setNumberValue(double value) |
418 | { |
419 | *this = IDBKeyData(); |
420 | m_value = value; |
421 | m_type = IndexedDB::KeyType::Number; |
422 | m_isNull = false; |
423 | } |
424 | |
425 | IDBKeyData IDBKeyData::deletedValue() |
426 | { |
427 | IDBKeyData result; |
428 | result.m_isNull = false; |
429 | result.m_isDeletedValue = true; |
430 | return result; |
431 | } |
432 | |
433 | bool IDBKeyData::isValid() const |
434 | { |
435 | if (m_type == IndexedDB::KeyType::Invalid) |
436 | return false; |
437 | |
438 | if (m_type == IndexedDB::KeyType::Array) { |
439 | for (auto& key : array()) { |
440 | if (!key.isValid()) |
441 | return false; |
442 | } |
443 | } |
444 | |
445 | return true; |
446 | } |
447 | |
448 | bool IDBKeyData::operator<(const IDBKeyData& rhs) const |
449 | { |
450 | return compare(rhs) < 0; |
451 | } |
452 | |
453 | bool IDBKeyData::operator==(const IDBKeyData& other) const |
454 | { |
455 | if (m_type != other.m_type || m_isNull != other.m_isNull || m_isDeletedValue != other.m_isDeletedValue) |
456 | return false; |
457 | switch (m_type) { |
458 | case IndexedDB::KeyType::Invalid: |
459 | case IndexedDB::KeyType::Max: |
460 | case IndexedDB::KeyType::Min: |
461 | return true; |
462 | case IndexedDB::KeyType::Number: |
463 | case IndexedDB::KeyType::Date: |
464 | return WTF::get<double>(m_value) == WTF::get<double>(other.m_value); |
465 | case IndexedDB::KeyType::String: |
466 | return WTF::get<String>(m_value) == WTF::get<String>(other.m_value); |
467 | case IndexedDB::KeyType::Binary: |
468 | return WTF::get<ThreadSafeDataBuffer>(m_value) == WTF::get<ThreadSafeDataBuffer>(other.m_value); |
469 | case IndexedDB::KeyType::Array: |
470 | return WTF::get<Vector<IDBKeyData>>(m_value) == WTF::get<Vector<IDBKeyData>>(other.m_value); |
471 | } |
472 | RELEASE_ASSERT_NOT_REACHED(); |
473 | } |
474 | |
475 | } // namespace WebCore |
476 | |
477 | #endif // ENABLE(INDEXED_DATABASE) |
478 | |