summaryrefslogtreecommitdiffstats
path: root/dom/indexedDB/Key.h
diff options
context:
space:
mode:
Diffstat (limited to 'dom/indexedDB/Key.h')
-rw-r--r--dom/indexedDB/Key.h280
1 files changed, 280 insertions, 0 deletions
diff --git a/dom/indexedDB/Key.h b/dom/indexedDB/Key.h
new file mode 100644
index 0000000000..d19bc94e53
--- /dev/null
+++ b/dom/indexedDB/Key.h
@@ -0,0 +1,280 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_indexeddb_key_h__
+#define mozilla_dom_indexeddb_key_h__
+
+#include "mozilla/dom/indexedDB/IDBResult.h"
+
+class mozIStorageStatement;
+class mozIStorageValueArray;
+
+namespace IPC {
+
+template <typename>
+struct ParamTraits;
+
+} // namespace IPC
+
+namespace mozilla::dom::indexedDB {
+
+class Key {
+ friend struct IPC::ParamTraits<Key>;
+
+ nsCString mBuffer;
+
+ public:
+ enum {
+ eTerminator = 0,
+ eFloat = 0x10,
+ eDate = 0x20,
+ eString = 0x30,
+ eBinary = 0x40,
+ eArray = 0x50,
+ eMaxType = eArray
+ };
+
+ static const uint8_t kMaxArrayCollapse = uint8_t(3);
+ static const uint8_t kMaxRecursionDepth = uint8_t(64);
+
+ Key() { Unset(); }
+
+ explicit Key(nsCString aBuffer) : mBuffer(std::move(aBuffer)) {}
+
+ bool operator==(const Key& aOther) const {
+ MOZ_ASSERT(!mBuffer.IsVoid());
+ MOZ_ASSERT(!aOther.mBuffer.IsVoid());
+
+ return mBuffer.Equals(aOther.mBuffer);
+ }
+
+ bool operator!=(const Key& aOther) const {
+ MOZ_ASSERT(!mBuffer.IsVoid());
+ MOZ_ASSERT(!aOther.mBuffer.IsVoid());
+
+ return !mBuffer.Equals(aOther.mBuffer);
+ }
+
+ bool operator<(const Key& aOther) const {
+ MOZ_ASSERT(!mBuffer.IsVoid());
+ MOZ_ASSERT(!aOther.mBuffer.IsVoid());
+
+ return Compare(mBuffer, aOther.mBuffer) < 0;
+ }
+
+ bool operator>(const Key& aOther) const {
+ MOZ_ASSERT(!mBuffer.IsVoid());
+ MOZ_ASSERT(!aOther.mBuffer.IsVoid());
+
+ return Compare(mBuffer, aOther.mBuffer) > 0;
+ }
+
+ bool operator<=(const Key& aOther) const {
+ MOZ_ASSERT(!mBuffer.IsVoid());
+ MOZ_ASSERT(!aOther.mBuffer.IsVoid());
+
+ return Compare(mBuffer, aOther.mBuffer) <= 0;
+ }
+
+ bool operator>=(const Key& aOther) const {
+ MOZ_ASSERT(!mBuffer.IsVoid());
+ MOZ_ASSERT(!aOther.mBuffer.IsVoid());
+
+ return Compare(mBuffer, aOther.mBuffer) >= 0;
+ }
+
+ void Unset() { mBuffer.SetIsVoid(true); }
+
+ bool IsUnset() const { return mBuffer.IsVoid(); }
+
+ bool IsFloat() const { return !IsUnset() && *BufferStart() == eFloat; }
+
+ bool IsDate() const { return !IsUnset() && *BufferStart() == eDate; }
+
+ bool IsString() const { return !IsUnset() && *BufferStart() == eString; }
+
+ bool IsBinary() const { return !IsUnset() && *BufferStart() == eBinary; }
+
+ bool IsArray() const { return !IsUnset() && *BufferStart() >= eArray; }
+
+ double ToFloat() const {
+ MOZ_ASSERT(IsFloat());
+ const EncodedDataType* pos = BufferStart();
+ double res = DecodeNumber(pos, BufferEnd());
+ MOZ_ASSERT(pos >= BufferEnd());
+ return res;
+ }
+
+ double ToDateMsec() const {
+ MOZ_ASSERT(IsDate());
+ const EncodedDataType* pos = BufferStart();
+ double res = DecodeNumber(pos, BufferEnd());
+ MOZ_ASSERT(pos >= BufferEnd());
+ return res;
+ }
+
+ nsAutoString ToString() const {
+ MOZ_ASSERT(IsString());
+ const EncodedDataType* pos = BufferStart();
+ auto res = DecodeString(pos, BufferEnd());
+ MOZ_ASSERT(pos >= BufferEnd());
+ return res;
+ }
+
+ Result<Ok, nsresult> SetFromString(const nsAString& aString);
+
+ Result<Ok, nsresult> SetFromInteger(int64_t aInt) {
+ mBuffer.Truncate();
+ auto ret = EncodeNumber(double(aInt), eFloat);
+ TrimBuffer();
+ return ret;
+ }
+
+ // This function implements the standard algorithm "convert a value to a key".
+ // A key return value is indicated by returning `true` whereas `false` means
+ // either invalid (if `aRv.Failed()` is `false`) or an exception (otherwise).
+ IDBResult<Ok, IDBSpecialValue::Invalid> SetFromJSVal(
+ JSContext* aCx, JS::Handle<JS::Value> aVal);
+
+ nsresult ToJSVal(JSContext* aCx, JS::MutableHandle<JS::Value> aVal) const;
+
+ nsresult ToJSVal(JSContext* aCx, JS::Heap<JS::Value>& aVal) const;
+
+ // See SetFromJSVal() for the meaning of values returned by this function.
+ IDBResult<Ok, IDBSpecialValue::Invalid> AppendItem(
+ JSContext* aCx, bool aFirstOfArray, JS::Handle<JS::Value> aVal);
+
+ Result<Key, nsresult> ToLocaleAwareKey(const nsCString& aLocale) const;
+
+ void FinishArray() { TrimBuffer(); }
+
+ const nsCString& GetBuffer() const { return mBuffer; }
+
+ nsresult BindToStatement(mozIStorageStatement* aStatement,
+ const nsACString& aParamName) const;
+
+ nsresult SetFromStatement(mozIStorageStatement* aStatement, uint32_t aIndex);
+
+ nsresult SetFromValueArray(mozIStorageValueArray* aValues, uint32_t aIndex);
+
+ static int16_t CompareKeys(const Key& aFirst, const Key& aSecond) {
+ int32_t result = Compare(aFirst.mBuffer, aSecond.mBuffer);
+
+ if (result < 0) {
+ return -1;
+ }
+
+ if (result > 0) {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ private:
+ class MOZ_STACK_CLASS ArrayValueEncoder;
+
+ using EncodedDataType = unsigned char;
+
+ const EncodedDataType* BufferStart() const {
+ // TODO it would be nicer if mBuffer was also using EncodedDataType
+ return reinterpret_cast<const EncodedDataType*>(mBuffer.BeginReading());
+ }
+
+ const EncodedDataType* BufferEnd() const {
+ return reinterpret_cast<const EncodedDataType*>(mBuffer.EndReading());
+ }
+
+ // Encoding helper. Trims trailing zeros off of mBuffer as a post-processing
+ // step.
+ void TrimBuffer() {
+ const char* end = mBuffer.EndReading() - 1;
+ while (!*end) {
+ --end;
+ }
+
+ mBuffer.Truncate(end + 1 - mBuffer.BeginReading());
+ }
+
+ // Encoding functions. These append the encoded value to the end of mBuffer
+ IDBResult<Ok, IDBSpecialValue::Invalid> EncodeJSVal(
+ JSContext* aCx, JS::Handle<JS::Value> aVal, uint8_t aTypeOffset);
+
+ Result<Ok, nsresult> EncodeString(const nsAString& aString,
+ uint8_t aTypeOffset);
+
+ template <typename T>
+ Result<Ok, nsresult> EncodeString(Span<const T> aInput, uint8_t aTypeOffset);
+
+ template <typename T>
+ Result<Ok, nsresult> EncodeAsString(Span<const T> aInput, uint8_t aType);
+
+ Result<Ok, nsresult> EncodeLocaleString(const nsAString& aString,
+ uint8_t aTypeOffset,
+ const nsCString& aLocale);
+
+ Result<Ok, nsresult> EncodeNumber(double aFloat, uint8_t aType);
+
+ Result<Ok, nsresult> EncodeBinary(JSObject* aObject, bool aIsViewObject,
+ uint8_t aTypeOffset);
+
+ // Decoding functions. aPos points into mBuffer and is adjusted to point
+ // past the consumed value. (Note: this may be beyond aEnd).
+ static nsresult DecodeJSVal(const EncodedDataType*& aPos,
+ const EncodedDataType* aEnd, JSContext* aCx,
+ JS::MutableHandle<JS::Value> aVal);
+
+ static nsAutoString DecodeString(const EncodedDataType*& aPos,
+ const EncodedDataType* aEnd);
+
+ static double DecodeNumber(const EncodedDataType*& aPos,
+ const EncodedDataType* aEnd);
+
+ static JSObject* DecodeBinary(const EncodedDataType*& aPos,
+ const EncodedDataType* aEnd, JSContext* aCx);
+
+ // Returns the size of the decoded data for stringy (string or binary),
+ // excluding a null terminator.
+ // On return, aOutSectionEnd points to the last byte behind the current
+ // encoded section, i.e. either aEnd, or the eTerminator.
+ // T is the base type for the decoded data.
+ template <typename T>
+ static uint32_t CalcDecodedStringySize(
+ const EncodedDataType* aBegin, const EncodedDataType* aEnd,
+ const EncodedDataType** aOutEncodedSectionEnd);
+
+ static uint32_t LengthOfEncodedBinary(const EncodedDataType* aPos,
+ const EncodedDataType* aEnd);
+
+ template <typename T>
+ static void DecodeAsStringy(const EncodedDataType* aEncodedSectionBegin,
+ const EncodedDataType* aEncodedSectionEnd,
+ uint32_t aDecodedLength, T* aOut);
+
+ template <EncodedDataType TypeMask, typename T, typename AcquireBuffer,
+ typename AcquireEmpty>
+ static void DecodeStringy(const EncodedDataType*& aPos,
+ const EncodedDataType* aEnd,
+ const AcquireBuffer& acquireBuffer,
+ const AcquireEmpty& acquireEmpty);
+
+ IDBResult<Ok, IDBSpecialValue::Invalid> EncodeJSValInternal(
+ JSContext* aCx, JS::Handle<JS::Value> aVal, uint8_t aTypeOffset,
+ uint16_t aRecursionDepth);
+
+ static nsresult DecodeJSValInternal(const EncodedDataType*& aPos,
+ const EncodedDataType* aEnd,
+ JSContext* aCx, uint8_t aTypeOffset,
+ JS::MutableHandle<JS::Value> aVal,
+ uint16_t aRecursionDepth);
+
+ template <typename T>
+ nsresult SetFromSource(T* aSource, uint32_t aIndex);
+};
+
+} // namespace mozilla::dom::indexedDB
+
+#endif // mozilla_dom_indexeddb_key_h__