michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef mozilla_dom_indexeddb_key_h__ michael@0: #define mozilla_dom_indexeddb_key_h__ michael@0: michael@0: #include "mozilla/dom/indexedDB/IndexedDatabase.h" michael@0: michael@0: #include "mozIStorageStatement.h" michael@0: michael@0: #include "js/Value.h" michael@0: michael@0: namespace IPC { michael@0: template struct ParamTraits; michael@0: } // namespace IPC michael@0: michael@0: BEGIN_INDEXEDDB_NAMESPACE michael@0: michael@0: class Key michael@0: { michael@0: friend struct IPC::ParamTraits; michael@0: michael@0: public: michael@0: Key() michael@0: { michael@0: Unset(); michael@0: } michael@0: michael@0: Key& operator=(const nsAString& aString) michael@0: { michael@0: SetFromString(aString); michael@0: return *this; michael@0: } michael@0: michael@0: Key& operator=(int64_t aInt) michael@0: { michael@0: SetFromInteger(aInt); michael@0: return *this; michael@0: } michael@0: michael@0: bool operator==(const Key& aOther) const michael@0: { michael@0: NS_ASSERTION(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid(), michael@0: "Don't compare unset keys!"); michael@0: michael@0: return mBuffer.Equals(aOther.mBuffer); michael@0: } michael@0: michael@0: bool operator!=(const Key& aOther) const michael@0: { michael@0: NS_ASSERTION(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid(), michael@0: "Don't compare unset keys!"); michael@0: michael@0: return !mBuffer.Equals(aOther.mBuffer); michael@0: } michael@0: michael@0: bool operator<(const Key& aOther) const michael@0: { michael@0: NS_ASSERTION(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid(), michael@0: "Don't compare unset keys!"); michael@0: michael@0: return Compare(mBuffer, aOther.mBuffer) < 0; michael@0: } michael@0: michael@0: bool operator>(const Key& aOther) const michael@0: { michael@0: NS_ASSERTION(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid(), michael@0: "Don't compare unset keys!"); michael@0: michael@0: return Compare(mBuffer, aOther.mBuffer) > 0; michael@0: } michael@0: michael@0: bool operator<=(const Key& aOther) const michael@0: { michael@0: NS_ASSERTION(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid(), michael@0: "Don't compare unset keys!"); michael@0: michael@0: return Compare(mBuffer, aOther.mBuffer) <= 0; michael@0: } michael@0: michael@0: bool operator>=(const Key& aOther) const michael@0: { michael@0: NS_ASSERTION(!mBuffer.IsVoid() && !aOther.mBuffer.IsVoid(), michael@0: "Don't compare unset keys!"); michael@0: michael@0: return Compare(mBuffer, aOther.mBuffer) >= 0; michael@0: } michael@0: michael@0: void michael@0: Unset() michael@0: { michael@0: mBuffer.SetIsVoid(true); michael@0: } michael@0: michael@0: bool IsUnset() const michael@0: { michael@0: return mBuffer.IsVoid(); michael@0: } michael@0: michael@0: bool IsFloat() const michael@0: { michael@0: return !IsUnset() && mBuffer.First() == eFloat; michael@0: } michael@0: michael@0: bool IsDate() const michael@0: { michael@0: return !IsUnset() && mBuffer.First() == eDate; michael@0: } michael@0: michael@0: bool IsString() const michael@0: { michael@0: return !IsUnset() && mBuffer.First() == eString; michael@0: } michael@0: michael@0: bool IsArray() const michael@0: { michael@0: return !IsUnset() && mBuffer.First() >= eArray; michael@0: } michael@0: michael@0: double ToFloat() const michael@0: { michael@0: NS_ASSERTION(IsFloat(), "Why'd you call this?"); michael@0: const unsigned char* pos = BufferStart(); michael@0: double res = DecodeNumber(pos, BufferEnd()); michael@0: NS_ASSERTION(pos >= BufferEnd(), "Should consume whole buffer"); michael@0: return res; michael@0: } michael@0: michael@0: double ToDateMsec() const michael@0: { michael@0: NS_ASSERTION(IsDate(), "Why'd you call this?"); michael@0: const unsigned char* pos = BufferStart(); michael@0: double res = DecodeNumber(pos, BufferEnd()); michael@0: NS_ASSERTION(pos >= BufferEnd(), "Should consume whole buffer"); michael@0: return res; michael@0: } michael@0: michael@0: void ToString(nsString& aString) const michael@0: { michael@0: NS_ASSERTION(IsString(), "Why'd you call this?"); michael@0: const unsigned char* pos = BufferStart(); michael@0: DecodeString(pos, BufferEnd(), aString); michael@0: NS_ASSERTION(pos >= BufferEnd(), "Should consume whole buffer"); michael@0: } michael@0: michael@0: void SetFromString(const nsAString& aString) michael@0: { michael@0: mBuffer.Truncate(); michael@0: EncodeString(aString, 0); michael@0: TrimBuffer(); michael@0: } michael@0: michael@0: void SetFromInteger(int64_t aInt) michael@0: { michael@0: mBuffer.Truncate(); michael@0: EncodeNumber(double(aInt), eFloat); michael@0: TrimBuffer(); michael@0: } michael@0: michael@0: nsresult SetFromJSVal(JSContext* aCx, michael@0: JS::Handle aVal) michael@0: { michael@0: mBuffer.Truncate(); michael@0: michael@0: if (aVal.isNull() || aVal.isUndefined()) { michael@0: Unset(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult rv = EncodeJSVal(aCx, aVal, 0); michael@0: if (NS_FAILED(rv)) { michael@0: Unset(); michael@0: return rv; michael@0: } michael@0: TrimBuffer(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult ToJSVal(JSContext* aCx, michael@0: JS::MutableHandle aVal) const michael@0: { michael@0: if (IsUnset()) { michael@0: aVal.setUndefined(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: const unsigned char* pos = BufferStart(); michael@0: nsresult rv = DecodeJSVal(pos, BufferEnd(), aCx, 0, aVal); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: NS_ASSERTION(pos >= BufferEnd(), michael@0: "Didn't consume whole buffer"); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult ToJSVal(JSContext* aCx, michael@0: JS::Heap& aVal) const michael@0: { michael@0: JS::Rooted value(aCx); michael@0: nsresult rv = ToJSVal(aCx, &value); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: aVal = value; michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: nsresult AppendItem(JSContext* aCx, michael@0: bool aFirstOfArray, michael@0: JS::Handle aVal) michael@0: { michael@0: nsresult rv = EncodeJSVal(aCx, aVal, aFirstOfArray ? eMaxType : 0); michael@0: if (NS_FAILED(rv)) { michael@0: Unset(); michael@0: return rv; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void FinishArray() michael@0: { michael@0: TrimBuffer(); michael@0: } michael@0: michael@0: const nsCString& GetBuffer() const michael@0: { michael@0: return mBuffer; michael@0: } michael@0: michael@0: nsresult BindToStatement(mozIStorageStatement* aStatement, michael@0: const nsACString& aParamName) const michael@0: { michael@0: nsresult rv = aStatement->BindBlobByName(aParamName, michael@0: reinterpret_cast(mBuffer.get()), mBuffer.Length()); michael@0: michael@0: return NS_SUCCEEDED(rv) ? NS_OK : NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; michael@0: } michael@0: michael@0: nsresult SetFromStatement(mozIStorageStatement* aStatement, michael@0: uint32_t aIndex) michael@0: { michael@0: uint8_t* data; michael@0: uint32_t dataLength = 0; michael@0: michael@0: nsresult rv = aStatement->GetBlob(aIndex, &dataLength, &data); michael@0: NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); michael@0: michael@0: mBuffer.Adopt( michael@0: reinterpret_cast(const_cast(data)), dataLength); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: static michael@0: int16_t CompareKeys(Key& aFirst, Key& aSecond) michael@0: { michael@0: int32_t result = Compare(aFirst.mBuffer, aSecond.mBuffer); michael@0: michael@0: if (result < 0) { michael@0: return -1; michael@0: } michael@0: michael@0: if (result > 0) { michael@0: return 1; michael@0: } michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: private: michael@0: const unsigned char* BufferStart() const michael@0: { michael@0: return reinterpret_cast(mBuffer.BeginReading()); michael@0: } michael@0: michael@0: const unsigned char* BufferEnd() const michael@0: { michael@0: return reinterpret_cast(mBuffer.EndReading()); michael@0: } michael@0: michael@0: enum { michael@0: eTerminator = 0, michael@0: eFloat = 1, michael@0: eDate = 2, michael@0: eString = 3, michael@0: eArray = 4, michael@0: eMaxType = eArray michael@0: }; michael@0: michael@0: // Encoding helper. Trims trailing zeros off of mBuffer as a post-processing michael@0: // step. michael@0: void TrimBuffer() michael@0: { michael@0: const char* end = mBuffer.EndReading() - 1; michael@0: while (!*end) { michael@0: --end; michael@0: } michael@0: michael@0: mBuffer.Truncate(end + 1 - mBuffer.BeginReading()); michael@0: } michael@0: michael@0: // Encoding functions. These append the encoded value to the end of mBuffer michael@0: inline nsresult EncodeJSVal(JSContext* aCx, JS::Handle aVal, michael@0: uint8_t aTypeOffset) michael@0: { michael@0: return EncodeJSValInternal(aCx, aVal, aTypeOffset, 0); michael@0: } michael@0: void EncodeString(const nsAString& aString, uint8_t aTypeOffset); michael@0: void EncodeNumber(double aFloat, uint8_t aType); michael@0: michael@0: // Decoding functions. aPos points into mBuffer and is adjusted to point michael@0: // past the consumed value. michael@0: static inline nsresult DecodeJSVal(const unsigned char*& aPos, michael@0: const unsigned char* aEnd, JSContext* aCx, michael@0: uint8_t aTypeOffset, JS::MutableHandle aVal) michael@0: { michael@0: return DecodeJSValInternal(aPos, aEnd, aCx, aTypeOffset, aVal, 0); michael@0: } michael@0: michael@0: static void DecodeString(const unsigned char*& aPos, michael@0: const unsigned char* aEnd, michael@0: nsString& aString); michael@0: static double DecodeNumber(const unsigned char*& aPos, michael@0: const unsigned char* aEnd); michael@0: michael@0: nsCString mBuffer; michael@0: michael@0: private: michael@0: nsresult EncodeJSValInternal(JSContext* aCx, JS::Handle aVal, michael@0: uint8_t aTypeOffset, uint16_t aRecursionDepth); michael@0: michael@0: static nsresult DecodeJSValInternal(const unsigned char*& aPos, michael@0: const unsigned char* aEnd, michael@0: JSContext* aCx, uint8_t aTypeOffset, michael@0: JS::MutableHandle aVal, uint16_t aRecursionDepth); michael@0: }; michael@0: michael@0: END_INDEXEDDB_NAMESPACE michael@0: michael@0: #endif /* mozilla_dom_indexeddb_key_h__ */