Key.h (9528B)
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 #ifndef mozilla_dom_indexeddb_key_h__ 8 #define mozilla_dom_indexeddb_key_h__ 9 10 #include "mozilla/dom/indexedDB/IDBResult.h" 11 12 class mozIStorageStatement; 13 class mozIStorageValueArray; 14 15 namespace IPC { 16 17 template <typename> 18 struct ParamTraits; 19 20 } // namespace IPC 21 22 namespace JS { 23 class ArrayBufferOrView; 24 class AutoCheckCannotGC; 25 } // namespace JS 26 27 namespace mozilla::dom::indexedDB { 28 29 class Key { 30 friend struct IPC::ParamTraits<Key>; 31 32 nsCString mBuffer; 33 CopyableTArray<uint32_t> mAutoIncrementKeyOffsets; 34 35 public: 36 enum { 37 eTerminator = 0, 38 eFloat = 0x10, 39 eDate = 0x20, 40 eString = 0x30, 41 eBinary = 0x40, 42 eArray = 0x50, 43 eMaxType = eArray 44 }; 45 46 static const uint8_t kMaxArrayCollapse = uint8_t(3); 47 static const uint8_t kMaxRecursionDepth = uint8_t(64); 48 49 Key() { Unset(); } 50 51 explicit Key(nsCString aBuffer) : mBuffer(std::move(aBuffer)) {} 52 53 bool operator==(const Key& aOther) const { 54 MOZ_ASSERT(!mBuffer.IsVoid()); 55 MOZ_ASSERT(!aOther.mBuffer.IsVoid()); 56 57 return mBuffer.Equals(aOther.mBuffer); 58 } 59 60 bool operator!=(const Key& aOther) const { 61 MOZ_ASSERT(!mBuffer.IsVoid()); 62 MOZ_ASSERT(!aOther.mBuffer.IsVoid()); 63 64 return !mBuffer.Equals(aOther.mBuffer); 65 } 66 67 bool operator<(const Key& aOther) const { 68 MOZ_ASSERT(!mBuffer.IsVoid()); 69 MOZ_ASSERT(!aOther.mBuffer.IsVoid()); 70 71 return Compare(mBuffer, aOther.mBuffer) < 0; 72 } 73 74 bool operator>(const Key& aOther) const { 75 MOZ_ASSERT(!mBuffer.IsVoid()); 76 MOZ_ASSERT(!aOther.mBuffer.IsVoid()); 77 78 return Compare(mBuffer, aOther.mBuffer) > 0; 79 } 80 81 bool operator<=(const Key& aOther) const { 82 MOZ_ASSERT(!mBuffer.IsVoid()); 83 MOZ_ASSERT(!aOther.mBuffer.IsVoid()); 84 85 return Compare(mBuffer, aOther.mBuffer) <= 0; 86 } 87 88 bool operator>=(const Key& aOther) const { 89 MOZ_ASSERT(!mBuffer.IsVoid()); 90 MOZ_ASSERT(!aOther.mBuffer.IsVoid()); 91 92 return Compare(mBuffer, aOther.mBuffer) >= 0; 93 } 94 95 void Unset() { 96 mBuffer.SetIsVoid(true); 97 mAutoIncrementKeyOffsets.Clear(); 98 } 99 100 bool IsUnset() const { return mBuffer.IsVoid(); } 101 102 bool IsFloat() const { return !IsUnset() && *BufferStart() == eFloat; } 103 104 bool IsDate() const { return !IsUnset() && *BufferStart() == eDate; } 105 106 bool IsString() const { return !IsUnset() && *BufferStart() == eString; } 107 108 bool IsBinary() const { return !IsUnset() && *BufferStart() == eBinary; } 109 110 bool IsArray() const { return !IsUnset() && *BufferStart() >= eArray; } 111 112 double ToFloat() const { 113 MOZ_ASSERT(IsFloat()); 114 const EncodedDataType* pos = BufferStart(); 115 double res = DecodeNumber(pos, BufferEnd()); 116 MOZ_ASSERT(pos >= BufferEnd()); 117 return res; 118 } 119 120 double ToDateMsec() const { 121 MOZ_ASSERT(IsDate()); 122 const EncodedDataType* pos = BufferStart(); 123 double res = DecodeNumber(pos, BufferEnd()); 124 MOZ_ASSERT(pos >= BufferEnd()); 125 return res; 126 } 127 128 nsAutoString ToString() const { 129 MOZ_ASSERT(IsString()); 130 const EncodedDataType* pos = BufferStart(); 131 auto res = DecodeString(pos, BufferEnd()); 132 MOZ_ASSERT(pos >= BufferEnd()); 133 return res; 134 } 135 136 Result<Ok, nsresult> SetFromString(const nsAString& aString); 137 138 Result<Ok, nsresult> SetFromInteger(int64_t aInt) { 139 mBuffer.Truncate(); 140 auto ret = EncodeNumber(double(aInt), eFloat); 141 TrimBuffer(); 142 return ret; 143 } 144 145 // This function implements the standard algorithm "convert a value to a key". 146 // A key return value is indicated by returning `true` whereas `false` means 147 // either invalid (if `aRv.Failed()` is `false`) or an exception (otherwise). 148 IDBResult<Ok, IDBSpecialValue::Invalid> SetFromJSVal( 149 JSContext* aCx, JS::Handle<JS::Value> aVal); 150 151 nsresult ToJSVal(JSContext* aCx, JS::MutableHandle<JS::Value> aVal) const; 152 153 nsresult ToJSVal(JSContext* aCx, JS::Heap<JS::Value>& aVal) const; 154 155 // See SetFromJSVal() for the meaning of values returned by this function. 156 IDBResult<Ok, IDBSpecialValue::Invalid> AppendItem( 157 JSContext* aCx, bool aFirstOfArray, JS::Handle<JS::Value> aVal); 158 159 Result<Key, nsresult> ToLocaleAwareKey(const nsCString& aLocale) const; 160 161 void FinishArray() { TrimBuffer(); } 162 163 const nsCString& GetBuffer() const { return mBuffer; } 164 165 nsresult BindToStatement(mozIStorageStatement* aStatement, 166 const nsACString& aParamName) const; 167 168 nsresult SetFromStatement(mozIStorageStatement* aStatement, uint32_t aIndex); 169 170 nsresult SetFromValueArray(mozIStorageValueArray* aValues, uint32_t aIndex); 171 172 static int16_t CompareKeys(const Key& aFirst, const Key& aSecond) { 173 int32_t result = Compare(aFirst.mBuffer, aSecond.mBuffer); 174 175 if (result < 0) { 176 return -1; 177 } 178 179 if (result > 0) { 180 return 1; 181 } 182 183 return 0; 184 } 185 186 void ReserveAutoIncrementKey(bool aFirstOfArray); 187 188 void MaybeUpdateAutoIncrementKey(int64_t aKey); 189 190 private: 191 class MOZ_STACK_CLASS ArrayValueEncoder; 192 193 using EncodedDataType = unsigned char; 194 195 const EncodedDataType* BufferStart() const { 196 // TODO it would be nicer if mBuffer was also using EncodedDataType 197 return reinterpret_cast<const EncodedDataType*>(mBuffer.BeginReading()); 198 } 199 200 const EncodedDataType* BufferEnd() const { 201 return reinterpret_cast<const EncodedDataType*>(mBuffer.EndReading()); 202 } 203 204 // Encoding helper. Trims trailing zeros off of mBuffer as a post-processing 205 // step. 206 void TrimBuffer() { 207 const char* end = mBuffer.EndReading() - 1; 208 while (!*end) { 209 --end; 210 } 211 212 mBuffer.Truncate(end + 1 - mBuffer.BeginReading()); 213 } 214 215 // Encoding functions. These append the encoded value to the end of mBuffer 216 IDBResult<Ok, IDBSpecialValue::Invalid> EncodeJSVal( 217 JSContext* aCx, JS::Handle<JS::Value> aVal, uint8_t aTypeOffset); 218 219 Result<Ok, nsresult> EncodeString(const nsAString& aString, 220 uint8_t aTypeOffset); 221 222 template <typename T> 223 Result<Ok, nsresult> EncodeString(Span<const T> aInput, uint8_t aTypeOffset); 224 225 template <typename T> 226 Result<Ok, nsresult> EncodeAsString(Span<const T> aInput, 227 JS::AutoCheckCannotGC&& aNoGC, 228 uint8_t aType); 229 230 Result<Ok, nsresult> EncodeLocaleString(const nsAString& aString, 231 uint8_t aTypeOffset, 232 const nsCString& aLocale); 233 234 Result<Ok, nsresult> EncodeNumber(double aFloat, uint8_t aType); 235 236 Result<Ok, nsresult> EncodeBinary( 237 const JS::ArrayBufferOrView& aArrayBufferOrView, uint8_t aTypeOffset); 238 239 // Decoding functions. aPos points into mBuffer and is adjusted to point 240 // past the consumed value. (Note: this may be beyond aEnd). 241 static nsresult DecodeJSVal(const EncodedDataType*& aPos, 242 const EncodedDataType* aEnd, JSContext* aCx, 243 JS::MutableHandle<JS::Value> aVal); 244 245 static nsAutoString DecodeString(const EncodedDataType*& aPos, 246 const EncodedDataType* aEnd); 247 248 static double DecodeNumber(const EncodedDataType*& aPos, 249 const EncodedDataType* aEnd); 250 251 static JSObject* DecodeBinary(const EncodedDataType*& aPos, 252 const EncodedDataType* aEnd, JSContext* aCx); 253 254 // Returns the size of the decoded data for stringy (string or binary), 255 // excluding a null terminator. 256 // On return, aOutSectionEnd points to the last byte behind the current 257 // encoded section, i.e. either aEnd, or the eTerminator. 258 // T is the base type for the decoded data. 259 template <typename T> 260 static uint32_t CalcDecodedStringySize( 261 const EncodedDataType* aBegin, const EncodedDataType* aEnd, 262 const EncodedDataType** aOutEncodedSectionEnd); 263 264 static uint32_t LengthOfEncodedBinary(const EncodedDataType* aPos, 265 const EncodedDataType* aEnd); 266 267 template <typename T> 268 static void DecodeAsStringy(const EncodedDataType* aEncodedSectionBegin, 269 const EncodedDataType* aEncodedSectionEnd, 270 uint32_t aDecodedLength, T* aOut); 271 272 template <EncodedDataType TypeMask, typename T, typename AcquireBuffer, 273 typename AcquireEmpty> 274 static void DecodeStringy(const EncodedDataType*& aPos, 275 const EncodedDataType* aEnd, 276 const AcquireBuffer& acquireBuffer, 277 const AcquireEmpty& acquireEmpty); 278 279 IDBResult<Ok, IDBSpecialValue::Invalid> EncodeJSValInternal( 280 JSContext* aCx, JS::Handle<JS::Value> aVal, uint8_t aTypeOffset, 281 uint16_t aRecursionDepth); 282 283 static nsresult DecodeJSValInternal(const EncodedDataType*& aPos, 284 const EncodedDataType* aEnd, 285 JSContext* aCx, uint8_t aTypeOffset, 286 JS::MutableHandle<JS::Value> aVal, 287 uint16_t aRecursionDepth); 288 289 template <typename T> 290 nsresult SetFromSource(T* aSource, uint32_t aIndex); 291 292 void WriteDoubleToUint64(char* aBuffer, double aValue); 293 }; 294 295 } // namespace mozilla::dom::indexedDB 296 297 #endif // mozilla_dom_indexeddb_key_h__