Fix build warnings in WebCore/Modules/indexeddb/server/IDBSerialization.cp
[WebKit-https.git] / Source / WebCore / Modules / indexeddb / server / IDBSerialization.cpp
1 /*
2  * Copyright (C) 2014, 2016 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 #include "config.h"
26 #include "IDBSerialization.h"
27
28 #if ENABLE(INDEXED_DATABASE)
29
30 #include "IDBKeyData.h"
31 #include "IDBKeyPath.h"
32 #include "KeyedCoding.h"
33
34 namespace WebCore {
35
36 enum class KeyPathType { Null, String, Array };
37
38 RefPtr<SharedBuffer> serializeIDBKeyPath(const Optional<IDBKeyPath>& keyPath)
39 {
40     auto encoder = KeyedEncoder::encoder();
41
42     if (keyPath) {
43         auto visitor = WTF::makeVisitor([&](const String& string) {
44             encoder->encodeEnum("type", KeyPathType::String);
45             encoder->encodeString("string", string);
46         }, [&](const Vector<String>& vector) {
47             encoder->encodeEnum("type", KeyPathType::Array);
48             encoder->encodeObjects("array", vector.begin(), vector.end(), [](WebCore::KeyedEncoder& encoder, const String& string) {
49                 encoder.encodeString("string", string);
50             });
51         });
52         WTF::visit(visitor, keyPath.value());
53     } else
54         encoder->encodeEnum("type", KeyPathType::Null);
55
56     return encoder->finishEncoding();
57 }
58
59 bool deserializeIDBKeyPath(const uint8_t* data, size_t size, Optional<IDBKeyPath>& result)
60 {
61     if (!data || !size)
62         return false;
63
64     auto decoder = KeyedDecoder::decoder(data, size);
65
66     KeyPathType type;
67     bool succeeded = decoder->decodeEnum("type", type, [](KeyPathType value) {
68         return value == KeyPathType::Null || value == KeyPathType::String || value == KeyPathType::Array;
69     });
70     if (!succeeded)
71         return false;
72
73     switch (type) {
74     case KeyPathType::Null:
75         break;
76     case KeyPathType::String: {
77         String string;
78         if (!decoder->decodeString("string", string))
79             return false;
80         result = IDBKeyPath(WTFMove(string));
81         break;
82     }
83     case KeyPathType::Array: {
84         Vector<String> vector;
85         succeeded = decoder->decodeObjects("array", vector, [](KeyedDecoder& decoder, String& result) {
86             return decoder.decodeString("string", result);
87         });
88         if (!succeeded)
89             return false;
90         result = IDBKeyPath(WTFMove(vector));
91         break;
92     }
93     }
94     return true;
95 }
96
97 // This is the magic character that begins serialized PropertyLists, and tells us whether
98 // the key we're looking at is an old-style key.
99 #if USE(CF)
100 static const uint8_t LegacySerializedKeyVersion = 'b';
101 #endif
102
103 // FIXME: Linux ports uses KeyedEncoderGlib for their IDBKeys.
104 // When a Glib maintainer comes along to enable the new serialization they'll need to
105 // denote a Glib magic character here.
106
107 /*
108 The IDBKeyData serialization format is as follows:
109 [1 byte version header][Key Buffer]
110
111 The Key Buffer serialization format is as follows:
112 [1 byte key type][Type specific data]
113
114 Type specific serialization formats are as follows for each of the types:
115 Min:
116 [0 bytes]
117
118 Number:
119 [8 bytes representing a double encoded in little endian]
120
121 Date:
122 [8 bytes representing a double encoded in little endian]
123
124 String:
125 [4 bytes representing string "length" in little endian]["length" number of 2-byte pairs representing ECMAScript 16-bit code units]
126
127 Binary:
128 [8 bytes representing the "size" of the binary blob]["size" bytes]
129
130 Array:
131 [8 bytes representing the "length" of the key array]["length" individual Key Buffer entries]
132
133 Max:
134 [0 bytes]
135 */
136
137 // FIXME: If the GLib magic character ends up being 0x00, we should consider changing
138 // this 0x00 so we can support Glib keyed encoding, also.
139 #if USE(CF)
140 static const uint8_t SIDBKeyVersion = 0x00;
141 enum class SIDBKeyType : uint8_t {
142     Min = 0x00,
143     Number = 0x20,
144     Date = 0x40,
145     String = 0x60,
146     Binary = 0x80,
147     Array = 0xA0,
148     Max = 0xFF,
149 };
150
151 static SIDBKeyType serializedTypeForKeyType(IndexedDB::KeyType type)
152 {
153     switch (type) {
154     case IndexedDB::KeyType::Min:
155         return SIDBKeyType::Min;
156     case IndexedDB::KeyType::Number:
157         return SIDBKeyType::Number;
158     case IndexedDB::KeyType::Date:
159         return SIDBKeyType::Date;
160     case IndexedDB::KeyType::String:
161         return SIDBKeyType::String;
162     case IndexedDB::KeyType::Binary:
163         return SIDBKeyType::Binary;
164     case IndexedDB::KeyType::Array:
165         return SIDBKeyType::Array;
166     case IndexedDB::KeyType::Max:
167         return SIDBKeyType::Max;
168     case IndexedDB::KeyType::Invalid:
169         RELEASE_ASSERT_NOT_REACHED();
170     };
171
172     RELEASE_ASSERT_NOT_REACHED();
173 }
174 #endif
175
176 #if CPU(BIG_ENDIAN) || CPU(MIDDLE_ENDIAN) || CPU(NEEDS_ALIGNED_ACCESS)
177 template <typename T> static void writeLittleEndian(Vector<char>& buffer, T value)
178 {
179     for (unsigned i = 0; i < sizeof(T); i++) {
180         buffer.append(value & 0xFF);
181         value >>= 8;
182     }
183 }
184
185 template <typename T> static bool readLittleEndian(const uint8_t*& ptr, const uint8_t* end, T& value)
186 {
187     if (ptr > end - sizeof(value))
188         return false;
189
190     value = 0;
191     for (size_t i = 0; i < sizeof(T); i++)
192         value += ((T)*ptr++) << (i * 8);
193     return true;
194 }
195 #else
196 template <typename T> static void writeLittleEndian(Vector<char>& buffer, T value)
197 {
198     buffer.append(reinterpret_cast<uint8_t*>(&value), sizeof(value));
199 }
200
201 template <typename T> static bool readLittleEndian(const uint8_t*& ptr, const uint8_t* end, T& value)
202 {
203     if (ptr > end - sizeof(value))
204         return false;
205
206     value = *reinterpret_cast<const T*>(ptr);
207     ptr += sizeof(T);
208
209     return true;
210 }
211 #endif
212
213 #if USE(CF)
214 static void writeDouble(Vector<char>& data, double d)
215 {
216     writeLittleEndian(data, *reinterpret_cast<uint64_t*>(&d));
217 }
218
219 static bool readDouble(const uint8_t*& data, const uint8_t* end, double& d)
220 {
221     return readLittleEndian(data, end, *reinterpret_cast<uint64_t*>(&d));
222 }
223
224 static void encodeKey(Vector<char>& data, const IDBKeyData& key)
225 {
226     SIDBKeyType type = serializedTypeForKeyType(key.type());
227     data.append(static_cast<char>(type));
228
229     switch (type) {
230     case SIDBKeyType::Number:
231         writeDouble(data, key.number());
232         break;
233     case SIDBKeyType::Date:
234         writeDouble(data, key.date());
235         break;
236     case SIDBKeyType::String: {
237         auto string = key.string();
238         uint32_t length = string.length();
239         writeLittleEndian(data, length);
240
241         for (size_t i = 0; i < length; ++i)
242             writeLittleEndian(data, string[i]);
243
244         break;
245     }
246     case SIDBKeyType::Binary: {
247         auto& buffer = key.binary();
248         uint64_t size = buffer.size();
249         writeLittleEndian(data, size);
250
251         auto* bufferData = buffer.data();
252         ASSERT(bufferData || !size);
253         if (bufferData)
254             data.append(bufferData->data(), bufferData->size());
255
256         break;
257     }
258     case SIDBKeyType::Array: {
259         auto& array = key.array();
260         uint64_t size = array.size();
261         writeLittleEndian(data, size);
262         for (auto& key : array)
263             encodeKey(data, key);
264
265         break;
266     }
267     case SIDBKeyType::Min:
268     case SIDBKeyType::Max:
269         break;
270     }
271 }
272 #endif
273
274 RefPtr<SharedBuffer> serializeIDBKeyData(const IDBKeyData& key)
275 {
276 #if USE(CF)
277     Vector<char> data;
278     data.append(SIDBKeyVersion);
279
280     encodeKey(data, key);
281     return SharedBuffer::adoptVector(data);
282 #else
283     auto encoder = KeyedEncoder::encoder();
284     key.encode(*encoder);
285     return encoder->finishEncoding();
286 #endif
287
288 }
289
290 #if USE(CF)
291 static bool decodeKey(const uint8_t*& data, const uint8_t* end, IDBKeyData& result)
292 {
293     if (!data || data >= end)
294         return false;
295
296     SIDBKeyType type = static_cast<SIDBKeyType>(data++[0]);
297     switch (type) {
298     case SIDBKeyType::Min:
299         result = IDBKeyData::minimum();
300         return true;
301     case SIDBKeyType::Max:
302         result = IDBKeyData::maximum();
303         return true;
304     case SIDBKeyType::Number: {
305         double d;
306         if (!readDouble(data, end, d))
307             return false;
308
309         result.setNumberValue(d);
310         return true;
311     }
312     case SIDBKeyType::Date: {
313         double d;
314         if (!readDouble(data, end, d))
315             return false;
316
317         result.setDateValue(d);
318         return true;
319     }
320     case SIDBKeyType::String: {
321         uint32_t length;
322         if (!readLittleEndian(data, end, length))
323             return false;
324
325         if (static_cast<uint64_t>(end - data) < length * 2)
326             return false;
327
328         Vector<UChar> buffer;
329         buffer.reserveInitialCapacity(length);
330         for (size_t i = 0; i < length; i++) {
331             uint16_t ch;
332             if (!readLittleEndian(data, end, ch))
333                 return false;
334             buffer.uncheckedAppend(ch);
335         }
336
337         result.setStringValue(String::adopt(WTFMove(buffer)));
338
339         return true;
340     }
341     case SIDBKeyType::Binary: {
342         uint64_t size64;
343         if (!readLittleEndian(data, end, size64))
344             return false;
345
346         if (static_cast<uint64_t>(end - data) < size64)
347             return false;
348
349         if (size64 > std::numeric_limits<size_t>::max())
350             return false;
351
352         size_t size = static_cast<size_t>(size64);
353         Vector<uint8_t> dataVector;
354
355         dataVector.append(data, size);
356         data += size;
357
358         result.setBinaryValue(ThreadSafeDataBuffer::adoptVector(dataVector));
359         return true;
360     }
361     case SIDBKeyType::Array: {
362         uint64_t size64;
363         if (!readLittleEndian(data, end, size64))
364             return false;
365
366         if (size64 > std::numeric_limits<size_t>::max())
367             return false;
368
369         size_t size = static_cast<size_t>(size64);
370         Vector<IDBKeyData> array;
371         array.reserveInitialCapacity(size);
372
373         for (size_t i = 0; i < size; ++i) {
374             IDBKeyData keyData;
375             if (!decodeKey(data, end, keyData))
376                 return false;
377
378             ASSERT(keyData.isValid());
379             array.uncheckedAppend(WTFMove(keyData));
380         }
381
382         result.setArrayValue(array);
383
384         return true;
385     }
386     default:
387         LOG_ERROR("decodeKey encountered unexpected type: %i", (int)type);
388         return false;
389     }
390 }
391 #endif
392
393 bool deserializeIDBKeyData(const uint8_t* data, size_t size, IDBKeyData& result)
394 {
395     if (!data || !size)
396         return false;
397
398 #if USE(CF)
399     if (data[0] == LegacySerializedKeyVersion) {
400         auto decoder = KeyedDecoder::decoder(data, size);
401         return IDBKeyData::decode(*decoder, result);
402     }
403
404     // Verify this is a SerializedIDBKey version we understand.
405     const uint8_t* current = data;
406     const uint8_t* end = data + size;
407     if (current++[0] != SIDBKeyVersion)
408         return false;
409
410     if (decodeKey(current, end, result)) {
411         // Even if we successfully decoded a key, the deserialize is only successful
412         // if we actually consumed all input data.
413         return current == end;
414     }
415
416     return false;
417 #else
418     auto decoder = KeyedDecoder::decoder(data, size);
419     return IDBKeyData::decode(*decoder, result);
420 #endif
421 }
422
423 } // namespace WebCore
424
425 #endif // ENABLE(INDEXED_DATABASE)