LCOV - code coverage report
Current view: top level - dom/indexedDB - Key.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 362 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 26 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13