Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/FloatingPoint.h"
9 #include "Key.h"
10 #include "ReportInternalError.h"
12 #include "jsfriendapi.h"
13 #include "nsAlgorithm.h"
14 #include "nsJSUtils.h"
15 #include "xpcpublic.h"
16 #include "mozilla/Endian.h"
17 #include <algorithm>
19 USING_INDEXEDDB_NAMESPACE
21 /*
22 Here's how we encode keys:
24 Basic strategy is the following
26 Numbers: 1 n n n n n n n n ("n"s are encoded 64bit float)
27 Dates: 2 n n n n n n n n ("n"s are encoded 64bit float)
28 Strings: 3 s s s ... 0 ("s"s are encoded unicode bytes)
29 Arrays: 4 i i i ... 0 ("i"s are encoded array items)
32 When encoding floats, 64bit IEEE 754 are almost sortable, except that
33 positive sort lower than negative, and negative sort descending. So we use
34 the following encoding:
36 value < 0 ?
37 (-to64bitInt(value)) :
38 (to64bitInt(value) | 0x8000000000000000)
41 When encoding strings, we use variable-size encoding per the following table
43 Chars 0 - 7E are encoded as 0xxxxxxx with 1 added
44 Chars 7F - (3FFF+7F) are encoded as 10xxxxxx xxxxxxxx with 7F subtracted
45 Chars (3FFF+80) - FFFF are encoded as 11xxxxxx xxxxxxxx xx000000
47 This ensures that the first byte is never encoded as 0, which means that the
48 string terminator (per basic-stategy table) sorts before any character.
49 The reason that (3FFF+80) - FFFF is encoded "shifted up" 6 bits is to maximize
50 the chance that the last character is 0. See below for why.
53 When encoding Arrays, we use an additional trick. Rather than adding a byte
54 containing the value '4' to indicate type, we instead add 4 to the next byte.
55 This is usually the byte containing the type of the first item in the array.
56 So simple examples are
58 ["foo"] 7 s s s 0 0 // 7 is 3 + 4
59 [1, 2] 5 n n n n n n n n 1 n n n n n n n n 0 // 5 is 1 + 4
61 Whe do this iteratively if the first item in the array is also an array
63 [["foo"]] 11 s s s 0 0 0
65 However, to avoid overflow in the byte, we only do this 3 times. If the first
66 item in an array is an array, and that array also has an array as first item,
67 we simply write out the total value accumulated so far and then follow the
68 "normal" rules.
70 [[["foo"]]] 12 3 s s s 0 0 0 0
72 There is another edge case that can happen though, which is that the array
73 doesn't have a first item to which we can add 4 to the type. Instead the
74 next byte would normally be the array terminator (per basic-strategy table)
75 so we simply add the 4 there.
77 [[]] 8 0 // 8 is 4 + 4 + 0
78 [] 4 // 4 is 4 + 0
79 [[], "foo"] 8 3 s s s 0 0 // 8 is 4 + 4 + 0
81 Note that the max-3-times rule kicks in before we get a chance to add to the
82 array terminator
84 [[[]]] 12 0 0 0 // 12 is 4 + 4 + 4
86 We could use a much higher number than 3 at no complexity or performance cost,
87 however it seems unlikely that it'll make a practical difference, and the low
88 limit makes testing eaiser.
91 As a final optimization we do a post-encoding step which drops all 0s at the
92 end of the encoded buffer.
94 "foo" // 3 s s s
95 1 // 1 bf f0
96 ["a", "b"] // 7 s 3 s
97 [1, 2] // 5 bf f0 0 0 0 0 0 0 1 c0
98 [[]] // 8
99 */
101 const int MaxArrayCollapse = 3;
103 const int MaxRecursionDepth = 256;
105 nsresult
106 Key::EncodeJSValInternal(JSContext* aCx, JS::Handle<JS::Value> aVal,
107 uint8_t aTypeOffset, uint16_t aRecursionDepth)
108 {
109 NS_ENSURE_TRUE(aRecursionDepth < MaxRecursionDepth, NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
111 static_assert(eMaxType * MaxArrayCollapse < 256,
112 "Unable to encode jsvals.");
114 if (aVal.isString()) {
115 nsDependentJSString str;
116 if (!str.init(aCx, aVal)) {
117 IDB_REPORT_INTERNAL_ERR();
118 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
119 }
120 EncodeString(str, aTypeOffset);
121 return NS_OK;
122 }
124 if (aVal.isNumber()) {
125 double d = aVal.toNumber();
126 if (mozilla::IsNaN(d)) {
127 return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
128 }
129 EncodeNumber(d, eFloat + aTypeOffset);
130 return NS_OK;
131 }
133 if (aVal.isObject()) {
134 JS::Rooted<JSObject*> obj(aCx, &aVal.toObject());
135 if (JS_IsArrayObject(aCx, obj)) {
136 aTypeOffset += eMaxType;
138 if (aTypeOffset == eMaxType * MaxArrayCollapse) {
139 mBuffer.Append(aTypeOffset);
140 aTypeOffset = 0;
141 }
142 NS_ASSERTION((aTypeOffset % eMaxType) == 0 &&
143 aTypeOffset < (eMaxType * MaxArrayCollapse),
144 "Wrong typeoffset");
146 uint32_t length;
147 if (!JS_GetArrayLength(aCx, obj, &length)) {
148 IDB_REPORT_INTERNAL_ERR();
149 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
150 }
152 for (uint32_t index = 0; index < length; index++) {
153 JS::Rooted<JS::Value> val(aCx);
154 if (!JS_GetElement(aCx, obj, index, &val)) {
155 IDB_REPORT_INTERNAL_ERR();
156 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
157 }
159 nsresult rv = EncodeJSValInternal(aCx, val, aTypeOffset,
160 aRecursionDepth + 1);
161 if (NS_FAILED(rv)) {
162 return rv;
163 }
165 aTypeOffset = 0;
166 }
168 mBuffer.Append(eTerminator + aTypeOffset);
170 return NS_OK;
171 }
173 if (JS_ObjectIsDate(aCx, obj)) {
174 if (!js_DateIsValid(obj)) {
175 return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
176 }
177 EncodeNumber(js_DateGetMsecSinceEpoch(obj), eDate + aTypeOffset);
178 return NS_OK;
179 }
180 }
182 return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
183 }
185 // static
186 nsresult
187 Key::DecodeJSValInternal(const unsigned char*& aPos, const unsigned char* aEnd,
188 JSContext* aCx, uint8_t aTypeOffset, JS::MutableHandle<JS::Value> aVal,
189 uint16_t aRecursionDepth)
190 {
191 NS_ENSURE_TRUE(aRecursionDepth < MaxRecursionDepth, NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
193 if (*aPos - aTypeOffset >= eArray) {
194 JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, 0));
195 if (!array) {
196 NS_WARNING("Failed to make array!");
197 IDB_REPORT_INTERNAL_ERR();
198 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
199 }
201 aTypeOffset += eMaxType;
203 if (aTypeOffset == eMaxType * MaxArrayCollapse) {
204 ++aPos;
205 aTypeOffset = 0;
206 }
208 uint32_t index = 0;
209 JS::Rooted<JS::Value> val(aCx);
210 while (aPos < aEnd && *aPos - aTypeOffset != eTerminator) {
211 nsresult rv = DecodeJSValInternal(aPos, aEnd, aCx, aTypeOffset,
212 &val, aRecursionDepth + 1);
213 NS_ENSURE_SUCCESS(rv, rv);
215 aTypeOffset = 0;
217 if (!JS_SetElement(aCx, array, index++, val)) {
218 NS_WARNING("Failed to set array element!");
219 IDB_REPORT_INTERNAL_ERR();
220 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
221 }
222 }
224 NS_ASSERTION(aPos >= aEnd || (*aPos % eMaxType) == eTerminator,
225 "Should have found end-of-array marker");
226 ++aPos;
228 aVal.setObject(*array);
229 }
230 else if (*aPos - aTypeOffset == eString) {
231 nsString key;
232 DecodeString(aPos, aEnd, key);
233 if (!xpc::StringToJsval(aCx, key, aVal)) {
234 IDB_REPORT_INTERNAL_ERR();
235 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
236 }
237 }
238 else if (*aPos - aTypeOffset == eDate) {
239 double msec = static_cast<double>(DecodeNumber(aPos, aEnd));
240 JSObject* date = JS_NewDateObjectMsec(aCx, msec);
241 if (!date) {
242 IDB_WARNING("Failed to make date!");
243 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
244 }
246 aVal.setObject(*date);
247 }
248 else if (*aPos - aTypeOffset == eFloat) {
249 aVal.setDouble(DecodeNumber(aPos, aEnd));
250 }
251 else {
252 NS_NOTREACHED("Unknown key type!");
253 }
255 return NS_OK;
256 }
258 #define ONE_BYTE_LIMIT 0x7E
259 #define TWO_BYTE_LIMIT (0x3FFF+0x7F)
261 #define ONE_BYTE_ADJUST 1
262 #define TWO_BYTE_ADJUST (-0x7F)
263 #define THREE_BYTE_SHIFT 6
265 void
266 Key::EncodeString(const nsAString& aString, uint8_t aTypeOffset)
267 {
268 // First measure how long the encoded string will be.
270 // The +2 is for initial 3 and trailing 0. We'll compensate for multi-byte
271 // chars below.
272 uint32_t size = aString.Length() + 2;
274 const char16_t* start = aString.BeginReading();
275 const char16_t* end = aString.EndReading();
276 for (const char16_t* iter = start; iter < end; ++iter) {
277 if (*iter > ONE_BYTE_LIMIT) {
278 size += *iter > TWO_BYTE_LIMIT ? 2 : 1;
279 }
280 }
282 // Allocate memory for the new size
283 uint32_t oldLen = mBuffer.Length();
284 char* buffer;
285 if (!mBuffer.GetMutableData(&buffer, oldLen + size)) {
286 return;
287 }
288 buffer += oldLen;
290 // Write type marker
291 *(buffer++) = eString + aTypeOffset;
293 // Encode string
294 for (const char16_t* iter = start; iter < end; ++iter) {
295 if (*iter <= ONE_BYTE_LIMIT) {
296 *(buffer++) = *iter + ONE_BYTE_ADJUST;
297 }
298 else if (*iter <= TWO_BYTE_LIMIT) {
299 char16_t c = char16_t(*iter) + TWO_BYTE_ADJUST + 0x8000;
300 *(buffer++) = (char)(c >> 8);
301 *(buffer++) = (char)(c & 0xFF);
302 }
303 else {
304 uint32_t c = (uint32_t(*iter) << THREE_BYTE_SHIFT) | 0x00C00000;
305 *(buffer++) = (char)(c >> 16);
306 *(buffer++) = (char)(c >> 8);
307 *(buffer++) = (char)c;
308 }
309 }
311 // Write end marker
312 *(buffer++) = eTerminator;
314 NS_ASSERTION(buffer == mBuffer.EndReading(), "Wrote wrong number of bytes");
315 }
317 // static
318 void
319 Key::DecodeString(const unsigned char*& aPos, const unsigned char* aEnd,
320 nsString& aString)
321 {
322 NS_ASSERTION(*aPos % eMaxType == eString, "Don't call me!");
324 const unsigned char* buffer = aPos + 1;
326 // First measure how big the decoded string will be.
327 uint32_t size = 0;
328 const unsigned char* iter;
329 for (iter = buffer; iter < aEnd && *iter != eTerminator; ++iter) {
330 if (*iter & 0x80) {
331 iter += (*iter & 0x40) ? 2 : 1;
332 }
333 ++size;
334 }
336 // Set end so that we don't have to check for null termination in the loop
337 // below
338 if (iter < aEnd) {
339 aEnd = iter;
340 }
342 char16_t* out;
343 if (size && !aString.GetMutableData(&out, size)) {
344 return;
345 }
347 for (iter = buffer; iter < aEnd;) {
348 if (!(*iter & 0x80)) {
349 *out = *(iter++) - ONE_BYTE_ADJUST;
350 }
351 else if (!(*iter & 0x40)) {
352 char16_t c = (char16_t(*(iter++)) << 8);
353 if (iter < aEnd) {
354 c |= *(iter++);
355 }
356 *out = c - TWO_BYTE_ADJUST - 0x8000;
357 }
358 else {
359 uint32_t c = uint32_t(*(iter++)) << (16 - THREE_BYTE_SHIFT);
360 if (iter < aEnd) {
361 c |= uint32_t(*(iter++)) << (8 - THREE_BYTE_SHIFT);
362 }
363 if (iter < aEnd) {
364 c |= *(iter++) >> THREE_BYTE_SHIFT;
365 }
366 *out = (char16_t)c;
367 }
369 ++out;
370 }
372 NS_ASSERTION(!size || out == aString.EndReading(),
373 "Should have written the whole string");
375 aPos = iter + 1;
376 }
378 union Float64Union {
379 double d;
380 uint64_t u;
381 };
383 void
384 Key::EncodeNumber(double aFloat, uint8_t aType)
385 {
386 // Allocate memory for the new size
387 uint32_t oldLen = mBuffer.Length();
388 char* buffer;
389 if (!mBuffer.GetMutableData(&buffer, oldLen + 1 + sizeof(double))) {
390 return;
391 }
392 buffer += oldLen;
394 *(buffer++) = aType;
396 Float64Union pun;
397 pun.d = aFloat;
398 // Note: The subtraction from 0 below is necessary to fix
399 // MSVC build warning C4146 (negating an unsigned value).
400 uint64_t number = pun.u & PR_UINT64(0x8000000000000000) ?
401 (0 - pun.u) :
402 (pun.u | PR_UINT64(0x8000000000000000));
404 mozilla::BigEndian::writeUint64(buffer, number);
405 }
407 // static
408 double
409 Key::DecodeNumber(const unsigned char*& aPos, const unsigned char* aEnd)
410 {
411 NS_ASSERTION(*aPos % eMaxType == eFloat ||
412 *aPos % eMaxType == eDate, "Don't call me!");
414 ++aPos;
416 uint64_t number = 0;
417 memcpy(&number, aPos, std::min<size_t>(sizeof(number), aEnd - aPos));
418 number = mozilla::NativeEndian::swapFromBigEndian(number);
420 aPos += sizeof(number);
422 Float64Union pun;
423 // Note: The subtraction from 0 below is necessary to fix
424 // MSVC build warning C4146 (negating an unsigned value).
425 pun.u = number & PR_UINT64(0x8000000000000000) ?
426 (number & ~PR_UINT64(0x8000000000000000)) :
427 (0 - number);
429 return pun.d;
430 }