Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/indexedDB/Key.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=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/. */
6
7
8
#include "Key.h"
9
10
#include <algorithm>
11
#include <stdint.h> // for UINT32_MAX, uintptr_t
12
#include "IndexedDatabaseManager.h"
13
#include "js/Date.h"
14
#include "js/Value.h"
15
#include "jsfriendapi.h"
16
#include "mozilla/Casting.h"
17
#include "mozilla/CheckedInt.h"
18
#include "mozilla/EndianUtils.h"
19
#include "mozilla/FloatingPoint.h"
20
#include "mozIStorageStatement.h"
21
#include "mozIStorageValueArray.h"
22
#include "nsAlgorithm.h"
23
#include "nsJSUtils.h"
24
#include "ReportInternalError.h"
25
#include "unicode/ucol.h"
26
#include "xpcpublic.h"
27
28
namespace mozilla {
29
namespace dom {
30
namespace indexedDB {
31
32
/*
33
 Here's how we encode keys:
34
35
 Basic strategy is the following
36
37
 Numbers:  0x10 n n n n n n n n    ("n"s are encoded 64bit float)
38
 Dates:    0x20 n n n n n n n n    ("n"s are encoded 64bit float)
39
 Strings:  0x30 s s s ... 0        ("s"s are encoded unicode bytes)
40
 Binaries: 0x40 s s s ... 0        ("s"s are encoded unicode bytes)
41
 Arrays:   0x50 i i i ... 0        ("i"s are encoded array items)
42
43
44
 When encoding floats, 64bit IEEE 754 are almost sortable, except that
45
 positive sort lower than negative, and negative sort descending. So we use
46
 the following encoding:
47
48
 value < 0 ?
49
   (-to64bitInt(value)) :
50
   (to64bitInt(value) | 0x8000000000000000)
51
52
53
 When encoding strings, we use variable-size encoding per the following table
54
55
 Chars 0         - 7E           are encoded as 0xxxxxxx with 1 added
56
 Chars 7F        - (3FFF+7F)    are encoded as 10xxxxxx xxxxxxxx with 7F subtracted
57
 Chars (3FFF+80) - FFFF         are encoded as 11xxxxxx xxxxxxxx xx000000
58
59
 This ensures that the first byte is never encoded as 0, which means that the
60
 string terminator (per basic-stategy table) sorts before any character.
61
 The reason that (3FFF+80) - FFFF is encoded "shifted up" 6 bits is to maximize
62
 the chance that the last character is 0. See below for why.
63
64
 When encoding binaries, the algorithm is the same to how strings are encoded.
65
 Since each octet in binary is in the range of [0-255], it'll take 1 to 2 encoded
66
 unicode bytes.
67
68
 When encoding Arrays, we use an additional trick. Rather than adding a byte
69
 containing the value 0x50 to indicate type, we instead add 0x50 to the next byte.
70
 This is usually the byte containing the type of the first item in the array.
71
 So simple examples are
72
73
 ["foo"]      0x80 s s s 0 0                              // 0x80 is 0x30 + 0x50
74
 [1, 2]       0x60 n n n n n n n n 1 n n n n n n n n 0    // 0x60 is 0x10 + 0x50
75
76
 Whe do this iteratively if the first item in the array is also an array
77
78
 [["foo"]]    0xA0 s s s 0 0 0
79
80
 However, to avoid overflow in the byte, we only do this 3 times. If the first
81
 item in an array is an array, and that array also has an array as first item,
82
 we simply write out the total value accumulated so far and then follow the
83
 "normal" rules.
84
85
 [[["foo"]]]  0xF0 0x30 s s s 0 0 0 0
86
87
 There is another edge case that can happen though, which is that the array
88
 doesn't have a first item to which we can add 0x50 to the type. Instead the
89
 next byte would normally be the array terminator (per basic-strategy table)
90
 so we simply add the 0x50 there.
91
92
 [[]]         0xA0 0                // 0xA0 is 0x50 + 0x50 + 0
93
 []           0x50                  // 0x50 is 0x50 + 0
94
 [[], "foo"]  0xA0 0x30 s s s 0 0   // 0xA0 is 0x50 + 0x50 + 0
95
96
 Note that the max-3-times rule kicks in before we get a chance to add to the
97
 array terminator
98
99
 [[[]]]       0xF0 0 0 0        // 0xF0 is 0x50 + 0x50 + 0x50
100
101
 As a final optimization we do a post-encoding step which drops all 0s at the
102
 end of the encoded buffer.
103
104
 "foo"         // 0x30 s s s
105
 1             // 0x10 bf f0
106
 ["a", "b"]    // 0x80 s 0 0x30 s
107
 [1, 2]        // 0x60 bf f0 0 0 0 0 0 0 0x10 c0
108
 [[]]          // 0x80
109
*/
110
111
nsresult
112
Key::ToLocaleBasedKey(Key& aTarget, const nsCString& aLocale) const
113
0
{
114
0
  if (IsUnset()) {
115
0
    aTarget.Unset();
116
0
    return NS_OK;
117
0
  }
118
0
119
0
  if (IsFloat() || IsDate() || IsBinary()) {
120
0
    aTarget.mBuffer = mBuffer;
121
0
    return NS_OK;
122
0
  }
123
0
124
0
  aTarget.mBuffer.Truncate();
125
0
  aTarget.mBuffer.SetCapacity(mBuffer.Length());
126
0
127
0
  auto* it = reinterpret_cast<const unsigned char*>(mBuffer.BeginReading());
128
0
  auto* end = reinterpret_cast<const unsigned char*>(mBuffer.EndReading());
129
0
130
0
  // First we do a pass and see if there are any strings in this key. We only
131
0
  // want to copy/decode when necessary.
132
0
  bool canShareBuffers = true;
133
0
  while (it < end) {
134
0
    auto type = *it % eMaxType;
135
0
    if (type == eTerminator || type == eArray) {
136
0
      it++;
137
0
    } else if (type == eFloat || type == eDate) {
138
0
      it++;
139
0
      it += std::min(sizeof(uint64_t), size_t(end - it));
140
0
    } else {
141
0
      // We have a string!
142
0
      canShareBuffers = false;
143
0
      break;
144
0
    }
145
0
  }
146
0
147
0
  if (canShareBuffers) {
148
0
    MOZ_ASSERT(it == end);
149
0
    aTarget.mBuffer = mBuffer;
150
0
    return NS_OK;
151
0
  }
152
0
153
0
  // A string was found, so we need to copy the data we've read so far
154
0
  auto* start = reinterpret_cast<const unsigned char*>(mBuffer.BeginReading());
155
0
  if (it > start) {
156
0
    char* buffer;
157
0
    if (!aTarget.mBuffer.GetMutableData(&buffer, it-start)) {
158
0
      return NS_ERROR_OUT_OF_MEMORY;
159
0
    }
160
0
161
0
    while (start < it) {
162
0
      *(buffer++) = *(start++);
163
0
    }
164
0
  }
165
0
166
0
  // Now continue decoding
167
0
  while (it < end) {
168
0
    char* buffer;
169
0
    uint32_t oldLen = aTarget.mBuffer.Length();
170
0
    auto type = *it % eMaxType;
171
0
172
0
    if (type == eTerminator || type == eArray) {
173
0
      // Copy array TypeID and terminator from raw key
174
0
      if (!aTarget.mBuffer.GetMutableData(&buffer, oldLen + 1)) {
175
0
        return NS_ERROR_OUT_OF_MEMORY;
176
0
      }
177
0
      *(buffer + oldLen) = *(it++);
178
0
    } else if (type == eFloat || type == eDate) {
179
0
      // Copy number from raw key
180
0
      if (!aTarget.mBuffer.GetMutableData(&buffer,
181
0
                                          oldLen + 1 + sizeof(uint64_t))) {
182
0
        return NS_ERROR_OUT_OF_MEMORY;
183
0
      }
184
0
      buffer += oldLen;
185
0
      *(buffer++) = *(it++);
186
0
187
0
      const size_t byteCount = std::min(sizeof(uint64_t), size_t(end - it));
188
0
      for (size_t count = 0; count < byteCount; count++) {
189
0
        *(buffer++) = (*it++);
190
0
      }
191
0
    } else {
192
0
      // Decode string and reencode
193
0
      uint8_t typeOffset = *it - eString;
194
0
      MOZ_ASSERT((typeOffset % eArray == 0) && (typeOffset / eArray <= 2));
195
0
196
0
      nsDependentString str;
197
0
      DecodeString(it, end, str);
198
0
      nsresult rv = aTarget.EncodeLocaleString(str, typeOffset, aLocale);
199
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
200
0
        return rv;
201
0
      }
202
0
    }
203
0
  }
204
0
  aTarget.TrimBuffer();
205
0
  return NS_OK;
206
0
}
207
208
nsresult
209
Key::EncodeJSValInternal(JSContext* aCx, JS::Handle<JS::Value> aVal,
210
                         uint8_t aTypeOffset, uint16_t aRecursionDepth)
211
0
{
212
0
  static_assert(eMaxType * kMaxArrayCollapse < 256,
213
0
                "Unable to encode jsvals.");
214
0
215
0
  if (NS_WARN_IF(aRecursionDepth == kMaxRecursionDepth)) {
216
0
    return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
217
0
  }
218
0
219
0
  if (aVal.isString()) {
220
0
    nsAutoJSString str;
221
0
    if (!str.init(aCx, aVal)) {
222
0
      IDB_REPORT_INTERNAL_ERR();
223
0
      return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
224
0
    }
225
0
    return EncodeString(str, aTypeOffset);
226
0
  }
227
0
228
0
  if (aVal.isNumber()) {
229
0
    double d = aVal.toNumber();
230
0
    if (mozilla::IsNaN(d)) {
231
0
      return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
232
0
    }
233
0
    EncodeNumber(d, eFloat + aTypeOffset);
234
0
    return NS_OK;
235
0
  }
236
0
237
0
  if (aVal.isObject()) {
238
0
    JS::Rooted<JSObject*> obj(aCx, &aVal.toObject());
239
0
240
0
    js::ESClass cls;
241
0
    if (!js::GetBuiltinClass(aCx, obj, &cls)) {
242
0
      IDB_REPORT_INTERNAL_ERR();
243
0
      return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
244
0
    }
245
0
    if (cls == js::ESClass::Array) {
246
0
      aTypeOffset += eMaxType;
247
0
248
0
      if (aTypeOffset == eMaxType * kMaxArrayCollapse) {
249
0
        mBuffer.Append(aTypeOffset);
250
0
        aTypeOffset = 0;
251
0
      }
252
0
      NS_ASSERTION((aTypeOffset % eMaxType) == 0 &&
253
0
                   aTypeOffset < (eMaxType * kMaxArrayCollapse),
254
0
                   "Wrong typeoffset");
255
0
256
0
      uint32_t length;
257
0
      if (!JS_GetArrayLength(aCx, obj, &length)) {
258
0
        IDB_REPORT_INTERNAL_ERR();
259
0
        return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
260
0
      }
261
0
262
0
      for (uint32_t index = 0; index < length; index++) {
263
0
        JS::Rooted<JS::Value> val(aCx);
264
0
        if (!JS_GetElement(aCx, obj, index, &val)) {
265
0
          IDB_REPORT_INTERNAL_ERR();
266
0
          return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
267
0
        }
268
0
269
0
        nsresult rv = EncodeJSValInternal(aCx, val, aTypeOffset,
270
0
                                          aRecursionDepth + 1);
271
0
        if (NS_FAILED(rv)) {
272
0
          return rv;
273
0
        }
274
0
275
0
        aTypeOffset = 0;
276
0
      }
277
0
278
0
      mBuffer.Append(eTerminator + aTypeOffset);
279
0
280
0
      return NS_OK;
281
0
    }
282
0
283
0
    if (cls == js::ESClass::Date) {
284
0
      bool valid;
285
0
      if (!js::DateIsValid(aCx, obj, &valid)) {
286
0
        IDB_REPORT_INTERNAL_ERR();
287
0
        return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
288
0
      }
289
0
      if (!valid)  {
290
0
        return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
291
0
      }
292
0
      double t;
293
0
      if (!js::DateGetMsecSinceEpoch(aCx, obj, &t)) {
294
0
        IDB_REPORT_INTERNAL_ERR();
295
0
        return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
296
0
      }
297
0
      EncodeNumber(t, eDate + aTypeOffset);
298
0
      return NS_OK;
299
0
    }
300
0
301
0
    if (JS_IsArrayBufferObject(obj)) {
302
0
      return EncodeBinary(obj, /* aIsViewObject */ false, aTypeOffset);
303
0
    }
304
0
305
0
    if (JS_IsArrayBufferViewObject(obj)) {
306
0
      return EncodeBinary(obj, /* aIsViewObject */ true, aTypeOffset);
307
0
    }
308
0
  }
309
0
310
0
  return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
311
0
}
312
313
// static
314
nsresult
315
Key::DecodeJSValInternal(const unsigned char*& aPos, const unsigned char* aEnd,
316
                         JSContext* aCx, uint8_t aTypeOffset, JS::MutableHandle<JS::Value> aVal,
317
                         uint16_t aRecursionDepth)
318
0
{
319
0
  if (NS_WARN_IF(aRecursionDepth == kMaxRecursionDepth)) {
320
0
    return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
321
0
  }
322
0
323
0
  if (*aPos - aTypeOffset >= eArray) {
324
0
    JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, 0));
325
0
    if (!array) {
326
0
      NS_WARNING("Failed to make array!");
327
0
      IDB_REPORT_INTERNAL_ERR();
328
0
      return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
329
0
    }
330
0
331
0
    aTypeOffset += eMaxType;
332
0
333
0
    if (aTypeOffset == eMaxType * kMaxArrayCollapse) {
334
0
      ++aPos;
335
0
      aTypeOffset = 0;
336
0
    }
337
0
338
0
    uint32_t index = 0;
339
0
    JS::Rooted<JS::Value> val(aCx);
340
0
    while (aPos < aEnd && *aPos - aTypeOffset != eTerminator) {
341
0
      nsresult rv = DecodeJSValInternal(aPos, aEnd, aCx, aTypeOffset,
342
0
                                        &val, aRecursionDepth + 1);
343
0
      NS_ENSURE_SUCCESS(rv, rv);
344
0
345
0
      aTypeOffset = 0;
346
0
347
0
      if (!JS_DefineElement(aCx, array, index++, val, JSPROP_ENUMERATE)) {
348
0
        NS_WARNING("Failed to set array element!");
349
0
        IDB_REPORT_INTERNAL_ERR();
350
0
        return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
351
0
      }
352
0
    }
353
0
354
0
    NS_ASSERTION(aPos >= aEnd || (*aPos % eMaxType) == eTerminator,
355
0
                 "Should have found end-of-array marker");
356
0
    ++aPos;
357
0
358
0
    aVal.setObject(*array);
359
0
  }
360
0
  else if (*aPos - aTypeOffset == eString) {
361
0
    nsString key;
362
0
    DecodeString(aPos, aEnd, key);
363
0
    if (!xpc::StringToJsval(aCx, key, aVal)) {
364
0
      IDB_REPORT_INTERNAL_ERR();
365
0
      return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
366
0
    }
367
0
  }
368
0
  else if (*aPos - aTypeOffset == eDate) {
369
0
    double msec = static_cast<double>(DecodeNumber(aPos, aEnd));
370
0
    JS::ClippedTime time = JS::TimeClip(msec);
371
0
    MOZ_ASSERT(msec == time.toDouble(),
372
0
               "encoding from a Date object not containing an invalid date "
373
0
               "means we should always have clipped values");
374
0
    JSObject* date = JS::NewDateObject(aCx, time);
375
0
    if (!date) {
376
0
      IDB_WARNING("Failed to make date!");
377
0
      return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
378
0
    }
379
0
380
0
    aVal.setObject(*date);
381
0
  }
382
0
  else if (*aPos - aTypeOffset == eFloat) {
383
0
    aVal.setDouble(DecodeNumber(aPos, aEnd));
384
0
  }
385
0
  else if (*aPos - aTypeOffset == eBinary) {
386
0
    JSObject* binary = DecodeBinary(aPos, aEnd, aCx);
387
0
    if (!binary) {
388
0
      IDB_REPORT_INTERNAL_ERR();
389
0
      return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
390
0
    }
391
0
392
0
    aVal.setObject(*binary);
393
0
  }
394
0
  else {
395
0
    MOZ_ASSERT_UNREACHABLE("Unknown key type!");
396
0
  }
397
0
398
0
  return NS_OK;
399
0
}
400
401
0
#define ONE_BYTE_LIMIT 0x7E
402
0
#define TWO_BYTE_LIMIT (0x3FFF+0x7F)
403
404
0
#define ONE_BYTE_ADJUST 1
405
0
#define TWO_BYTE_ADJUST (-0x7F)
406
0
#define THREE_BYTE_SHIFT 6
407
408
nsresult
409
Key::EncodeJSVal(JSContext* aCx,
410
                 JS::Handle<JS::Value> aVal,
411
                 uint8_t aTypeOffset)
412
0
{
413
0
  return EncodeJSValInternal(aCx, aVal, aTypeOffset, 0);
414
0
}
415
416
nsresult
417
Key::EncodeString(const nsAString& aString, uint8_t aTypeOffset)
418
0
{
419
0
  const char16_t* start = aString.BeginReading();
420
0
  const char16_t* end = aString.EndReading();
421
0
  return EncodeString(start, end, aTypeOffset);
422
0
}
423
424
template <typename T>
425
nsresult
426
Key::EncodeString(const T* aStart, const T* aEnd, uint8_t aTypeOffset)
427
0
{
428
0
  return EncodeAsString(aStart, aEnd, eString + aTypeOffset);
429
0
}
Unexecuted instantiation: nsresult mozilla::dom::indexedDB::Key::EncodeString<char16_t>(char16_t const*, char16_t const*, unsigned char)
Unexecuted instantiation: nsresult mozilla::dom::indexedDB::Key::EncodeString<unsigned char>(unsigned char const*, unsigned char const*, unsigned char)
430
431
template <typename T>
432
nsresult
433
Key::EncodeAsString(const T* aStart, const T* aEnd, uint8_t aType)
434
0
{
435
0
  // First measure how long the encoded string will be.
436
0
  if (NS_WARN_IF(aStart > aEnd || UINT32_MAX - 2 < uintptr_t(aEnd - aStart))) {
437
0
    IDB_REPORT_INTERNAL_ERR();
438
0
    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
439
0
  }
440
0
441
0
  // The +2 is for initial 3 and trailing 0. We'll compensate for multi-byte
442
0
  // chars below.
443
0
  uint32_t checkedSize = aEnd - aStart;
444
0
  CheckedUint32 size = checkedSize;
445
0
  size += 2;
446
0
447
0
  MOZ_ASSERT(size.isValid());
448
0
449
0
  const T* start = aStart;
450
0
  const T* end = aEnd;
451
0
  for (const T* iter = start; iter < end; ++iter) {
452
0
    if (*iter > ONE_BYTE_LIMIT) {
453
0
      size += char16_t(*iter) > TWO_BYTE_LIMIT ? 2 : 1;
454
0
      if (!size.isValid()) {
455
0
        IDB_REPORT_INTERNAL_ERR();
456
0
        return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
457
0
      }
458
0
    }
459
0
  }
460
0
461
0
  // Allocate memory for the new size
462
0
  uint32_t oldLen = mBuffer.Length();
463
0
  size += oldLen;
464
0
465
0
  if (!size.isValid()) {
466
0
    IDB_REPORT_INTERNAL_ERR();
467
0
    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
468
0
  }
469
0
470
0
  char* buffer;
471
0
  if (!mBuffer.GetMutableData(&buffer, size.value())) {
472
0
    IDB_REPORT_INTERNAL_ERR();
473
0
    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
474
0
  }
475
0
  buffer += oldLen;
476
0
477
0
  // Write type marker
478
0
  *(buffer++) = aType;
479
0
480
0
  // Encode string
481
0
  for (const T* iter = start; iter < end; ++iter) {
482
0
    if (*iter <= ONE_BYTE_LIMIT) {
483
0
      *(buffer++) = *iter + ONE_BYTE_ADJUST;
484
0
    }
485
0
    else if (char16_t(*iter) <= TWO_BYTE_LIMIT) {
486
0
      char16_t c = char16_t(*iter) + TWO_BYTE_ADJUST + 0x8000;
487
0
      *(buffer++) = (char)(c >> 8);
488
0
      *(buffer++) = (char)(c & 0xFF);
489
0
    }
490
0
    else {
491
0
      uint32_t c = (uint32_t(*iter) << THREE_BYTE_SHIFT) | 0x00C00000;
492
0
      *(buffer++) = (char)(c >> 16);
493
0
      *(buffer++) = (char)(c >> 8);
494
0
      *(buffer++) = (char)c;
495
0
    }
496
0
  }
497
0
498
0
  // Write end marker
499
0
  *(buffer++) = eTerminator;
500
0
501
0
  NS_ASSERTION(buffer == mBuffer.EndReading(), "Wrote wrong number of bytes");
502
0
503
0
  return NS_OK;
504
0
}
Unexecuted instantiation: nsresult mozilla::dom::indexedDB::Key::EncodeAsString<char16_t>(char16_t const*, char16_t const*, unsigned char)
Unexecuted instantiation: nsresult mozilla::dom::indexedDB::Key::EncodeAsString<unsigned char>(unsigned char const*, unsigned char const*, unsigned char)
505
506
nsresult
507
Key::EncodeLocaleString(const nsDependentString& aString, uint8_t aTypeOffset,
508
                        const nsCString& aLocale)
509
0
{
510
0
  const int length = aString.Length();
511
0
  if (length == 0) {
512
0
    return NS_OK;
513
0
  }
514
0
  const UChar* ustr = reinterpret_cast<const UChar*>(aString.BeginReading());
515
0
516
0
  UErrorCode uerror = U_ZERO_ERROR;
517
0
  UCollator* collator = ucol_open(aLocale.get(), &uerror);
518
0
  if (NS_WARN_IF(U_FAILURE(uerror))) {
519
0
    return NS_ERROR_FAILURE;
520
0
  }
521
0
  MOZ_ASSERT(collator);
522
0
523
0
  AutoTArray<uint8_t, 128> keyBuffer;
524
0
  int32_t sortKeyLength = ucol_getSortKey(collator, ustr, length,
525
0
                                          keyBuffer.Elements(),
526
0
                                          keyBuffer.Length());
527
0
  if (sortKeyLength > (int32_t)keyBuffer.Length()) {
528
0
    keyBuffer.SetLength(sortKeyLength);
529
0
    sortKeyLength = ucol_getSortKey(collator, ustr, length,
530
0
                                    keyBuffer.Elements(),
531
0
                                    sortKeyLength);
532
0
  }
533
0
534
0
  ucol_close(collator);
535
0
  if (NS_WARN_IF(sortKeyLength == 0)) {
536
0
    return NS_ERROR_FAILURE;
537
0
  }
538
0
539
0
  return EncodeString(keyBuffer.Elements(),
540
0
                      keyBuffer.Elements()+sortKeyLength,
541
0
                      aTypeOffset);
542
0
}
543
544
// static
545
nsresult
546
Key::DecodeJSVal(const unsigned char*& aPos,
547
                 const unsigned char* aEnd,
548
                 JSContext* aCx,
549
                 JS::MutableHandle<JS::Value> aVal)
550
0
{
551
0
  return DecodeJSValInternal(aPos, aEnd, aCx, 0, aVal, 0);
552
0
}
553
554
// static
555
void
556
Key::DecodeString(const unsigned char*& aPos, const unsigned char* aEnd,
557
                  nsString& aString)
558
0
{
559
0
  NS_ASSERTION(*aPos % eMaxType == eString, "Don't call me!");
560
0
561
0
  const unsigned char* buffer = aPos + 1;
562
0
563
0
  // First measure how big the decoded string will be.
564
0
  uint32_t size = 0;
565
0
  const unsigned char* iter;
566
0
  for (iter = buffer; iter < aEnd && *iter != eTerminator; ++iter) {
567
0
    if (*iter & 0x80) {
568
0
      iter += (*iter & 0x40) ? 2 : 1;
569
0
    }
570
0
    ++size;
571
0
  }
572
0
573
0
  // Set end so that we don't have to check for null termination in the loop
574
0
  // below
575
0
  if (iter < aEnd) {
576
0
    aEnd = iter;
577
0
  }
578
0
579
0
  char16_t* out;
580
0
  if (size && !aString.GetMutableData(&out, size)) {
581
0
    return;
582
0
  }
583
0
584
0
  for (iter = buffer; iter < aEnd;) {
585
0
    if (!(*iter & 0x80)) {
586
0
      *out = *(iter++) - ONE_BYTE_ADJUST;
587
0
    }
588
0
    else if (!(*iter & 0x40)) {
589
0
      char16_t c = (char16_t(*(iter++)) << 8);
590
0
      if (iter < aEnd) {
591
0
        c |= *(iter++);
592
0
      }
593
0
      *out = c - TWO_BYTE_ADJUST - 0x8000;
594
0
    }
595
0
    else {
596
0
      uint32_t c = uint32_t(*(iter++)) << (16 - THREE_BYTE_SHIFT);
597
0
      if (iter < aEnd) {
598
0
        c |= uint32_t(*(iter++)) << (8 - THREE_BYTE_SHIFT);
599
0
      }
600
0
      if (iter < aEnd) {
601
0
        c |= *(iter++) >> THREE_BYTE_SHIFT;
602
0
      }
603
0
      *out = (char16_t)c;
604
0
    }
605
0
606
0
    ++out;
607
0
  }
608
0
609
0
  NS_ASSERTION(!size || out == aString.EndReading(),
610
0
               "Should have written the whole string");
611
0
612
0
  aPos = iter + 1;
613
0
}
614
615
void
616
Key::EncodeNumber(double aFloat, uint8_t aType)
617
0
{
618
0
  // Allocate memory for the new size
619
0
  uint32_t oldLen = mBuffer.Length();
620
0
  char* buffer;
621
0
  if (!mBuffer.GetMutableData(&buffer, oldLen + 1 + sizeof(double))) {
622
0
    return;
623
0
  }
624
0
  buffer += oldLen;
625
0
626
0
  *(buffer++) = aType;
627
0
628
0
  uint64_t bits = BitwiseCast<uint64_t>(aFloat);
629
0
  // Note: The subtraction from 0 below is necessary to fix
630
0
  // MSVC build warning C4146 (negating an unsigned value).
631
0
  const uint64_t signbit = FloatingPoint<double>::kSignBit;
632
0
  uint64_t number = bits & signbit ? (0 - bits) : (bits | signbit);
633
0
634
0
  mozilla::BigEndian::writeUint64(buffer, number);
635
0
}
636
637
// static
638
double
639
Key::DecodeNumber(const unsigned char*& aPos, const unsigned char* aEnd)
640
0
{
641
0
  NS_ASSERTION(*aPos % eMaxType == eFloat ||
642
0
               *aPos % eMaxType == eDate, "Don't call me!");
643
0
644
0
  ++aPos;
645
0
646
0
  uint64_t number = 0;
647
0
  memcpy(&number, aPos, std::min<size_t>(sizeof(number), aEnd - aPos));
648
0
  number = mozilla::NativeEndian::swapFromBigEndian(number);
649
0
650
0
  aPos += sizeof(number);
651
0
652
0
  // Note: The subtraction from 0 below is necessary to fix
653
0
  // MSVC build warning C4146 (negating an unsigned value).
654
0
  const uint64_t signbit = FloatingPoint<double>::kSignBit;
655
0
  uint64_t bits = number & signbit ? (number & ~signbit) : (0 - number);
656
0
657
0
  return BitwiseCast<double>(bits);
658
0
}
659
660
nsresult
661
Key::EncodeBinary(JSObject* aObject, bool aIsViewObject, uint8_t aTypeOffset)
662
0
{
663
0
  uint8_t* bufferData;
664
0
  uint32_t bufferLength;
665
0
  bool unused;
666
0
667
0
  if (aIsViewObject) {
668
0
    js::GetArrayBufferViewLengthAndData(aObject, &bufferLength, &unused, &bufferData);
669
0
  } else {
670
0
    js::GetArrayBufferLengthAndData(aObject, &bufferLength, &unused, &bufferData);
671
0
  }
672
0
673
0
  return EncodeAsString(bufferData, bufferData + bufferLength, eBinary + aTypeOffset);
674
0
}
675
676
// static
677
JSObject*
678
Key::DecodeBinary(const unsigned char*& aPos,
679
                  const unsigned char* aEnd,
680
                  JSContext* aCx)
681
0
{
682
0
  MOZ_ASSERT(*aPos % eMaxType == eBinary, "Don't call me!");
683
0
684
0
  const unsigned char* buffer = ++aPos;
685
0
686
0
  // First measure how big the decoded array buffer will be.
687
0
  size_t size = 0;
688
0
  const unsigned char* iter;
689
0
  for (iter = buffer; iter < aEnd && *iter != eTerminator; ++iter) {
690
0
    if (*iter & 0x80) {
691
0
      iter++;
692
0
    }
693
0
    ++size;
694
0
  }
695
0
696
0
  if (!size) {
697
0
    return JS_NewArrayBuffer(aCx, 0);
698
0
  }
699
0
700
0
  uint8_t* out = static_cast<uint8_t*>(JS_malloc(aCx, size));
701
0
  if (NS_WARN_IF(!out)) {
702
0
    return nullptr;
703
0
  }
704
0
705
0
  uint8_t* pos = out;
706
0
707
0
  // Set end so that we don't have to check for null termination in the loop
708
0
  // below
709
0
  if (iter < aEnd) {
710
0
    aEnd = iter;
711
0
  }
712
0
713
0
  for (iter = buffer; iter < aEnd;) {
714
0
    if (!(*iter & 0x80)) {
715
0
      *pos = *(iter++) - ONE_BYTE_ADJUST;
716
0
    }
717
0
    else {
718
0
      uint16_t c = (uint16_t(*(iter++)) << 8);
719
0
      if (iter < aEnd) {
720
0
        c |= *(iter++);
721
0
      }
722
0
      *pos = static_cast<uint8_t>(c - TWO_BYTE_ADJUST - 0x8000);
723
0
    }
724
0
725
0
    ++pos;
726
0
  }
727
0
728
0
  aPos = iter + 1;
729
0
730
0
  MOZ_ASSERT(static_cast<size_t>(pos - out) == size,
731
0
             "Should have written the whole buffer");
732
0
733
0
  return JS_NewArrayBufferWithContents(aCx, size, out);
734
0
}
735
736
nsresult
737
Key::BindToStatement(mozIStorageStatement* aStatement,
738
                     const nsACString& aParamName) const
739
0
{
740
0
  nsresult rv;
741
0
  if (IsUnset()) {
742
0
    rv = aStatement->BindNullByName(aParamName);
743
0
  } else {
744
0
    rv = aStatement->BindBlobByName(aParamName,
745
0
      reinterpret_cast<const uint8_t*>(mBuffer.get()), mBuffer.Length());
746
0
  }
747
0
748
0
  return NS_SUCCEEDED(rv) ? NS_OK : NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
749
0
}
750
751
nsresult
752
Key::SetFromStatement(mozIStorageStatement* aStatement,
753
                      uint32_t aIndex)
754
0
{
755
0
  return SetFromSource(aStatement, aIndex);
756
0
}
757
758
nsresult
759
Key::SetFromValueArray(mozIStorageValueArray* aValues,
760
                      uint32_t aIndex)
761
0
{
762
0
  return SetFromSource(aValues, aIndex);
763
0
}
764
765
nsresult
766
Key::SetFromJSVal(JSContext* aCx,
767
                  JS::Handle<JS::Value> aVal)
768
0
{
769
0
  mBuffer.Truncate();
770
0
771
0
  if (aVal.isNull() || aVal.isUndefined()) {
772
0
    Unset();
773
0
    return NS_OK;
774
0
  }
775
0
776
0
  nsresult rv = EncodeJSVal(aCx, aVal, 0);
777
0
  if (NS_FAILED(rv)) {
778
0
    Unset();
779
0
    return rv;
780
0
  }
781
0
  TrimBuffer();
782
0
783
0
  return NS_OK;
784
0
}
785
786
nsresult
787
Key::ToJSVal(JSContext* aCx,
788
             JS::MutableHandle<JS::Value> aVal) const
789
0
{
790
0
  if (IsUnset()) {
791
0
    aVal.setUndefined();
792
0
    return NS_OK;
793
0
  }
794
0
795
0
  const unsigned char* pos = BufferStart();
796
0
  nsresult rv = DecodeJSVal(pos, BufferEnd(), aCx, aVal);
797
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
798
0
    return rv;
799
0
  }
800
0
801
0
  MOZ_ASSERT(pos >= BufferEnd());
802
0
803
0
  return NS_OK;
804
0
}
805
806
nsresult
807
Key::ToJSVal(JSContext* aCx,
808
             JS::Heap<JS::Value>& aVal) const
809
0
{
810
0
  JS::Rooted<JS::Value> value(aCx);
811
0
  nsresult rv = ToJSVal(aCx, &value);
812
0
  if (NS_SUCCEEDED(rv)) {
813
0
    aVal = value;
814
0
  }
815
0
  return rv;
816
0
}
817
818
nsresult
819
Key::AppendItem(JSContext* aCx, bool aFirstOfArray, JS::Handle<JS::Value> aVal)
820
0
{
821
0
  nsresult rv = EncodeJSVal(aCx, aVal, aFirstOfArray ? eMaxType : 0);
822
0
  if (NS_FAILED(rv)) {
823
0
    Unset();
824
0
    return rv;
825
0
  }
826
0
827
0
  return NS_OK;
828
0
}
829
830
template <typename T>
831
nsresult
832
Key::SetFromSource(T* aSource, uint32_t aIndex)
833
0
{
834
0
  const uint8_t* data;
835
0
  uint32_t dataLength = 0;
836
0
837
0
  nsresult rv = aSource->GetSharedBlob(aIndex, &dataLength, &data);
838
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
839
0
    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
840
0
  }
841
0
842
0
  mBuffer.Assign(reinterpret_cast<const char*>(data), dataLength);
843
0
844
0
  return NS_OK;
845
0
}
Unexecuted instantiation: nsresult mozilla::dom::indexedDB::Key::SetFromSource<mozIStorageStatement>(mozIStorageStatement*, unsigned int)
Unexecuted instantiation: nsresult mozilla::dom::indexedDB::Key::SetFromSource<mozIStorageValueArray>(mozIStorageValueArray*, unsigned int)
846
847
#ifdef DEBUG
848
849
void
850
Key::Assert(bool aCondition) const
851
{
852
  MOZ_ASSERT(aCondition);
853
}
854
855
#endif // DEBUG
856
857
} // namespace indexedDB
858
} // namespace dom
859
} // namespace mozilla