1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=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 file,
5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "mozilla/dom/LSValue.h"
8 
9 #include "mozIStorageStatement.h"
10 #include "mozilla/dom/SnappyUtils.h"
11 #include "mozilla/fallible.h"
12 #include "mozilla/TextUtils.h"
13 #include "nsDebug.h"
14 #include "nsError.h"
15 
16 namespace mozilla::dom {
17 
18 namespace {
19 
PutStringBytesToCString(const nsAString & aSrc,nsCString & aDest)20 bool PutStringBytesToCString(const nsAString& aSrc, nsCString& aDest) {
21   const char16_t* bufferData;
22   const size_t byteLength = sizeof(char16_t) * aSrc.GetData(&bufferData);
23 
24   char* destDataPtr;
25   const auto newLength = aDest.GetMutableData(&destDataPtr, byteLength);
26   if (newLength != byteLength) {
27     return false;
28   }
29   std::memcpy(static_cast<void*>(destDataPtr),
30               static_cast<const void*>(bufferData), byteLength);
31 
32   return true;
33 }
34 
35 template <class T>
36 using TypeBufferResult = Result<std::pair<T, nsCString>, nsresult>;
37 
38 }  // namespace
39 
PutCStringBytesToString(const nsACString & aSrc,nsString & aDest)40 bool PutCStringBytesToString(const nsACString& aSrc, nsString& aDest) {
41   const char* bufferData;
42   const size_t byteLength = aSrc.GetData(&bufferData);
43   const size_t shortLength = byteLength / sizeof(char16_t);
44 
45   char16_t* destDataPtr;
46   const auto newLength = aDest.GetMutableData(&destDataPtr, shortLength);
47   if (newLength != shortLength) {
48     return false;
49   }
50 
51   std::memcpy(static_cast<void*>(destDataPtr),
52               static_cast<const void*>(bufferData), byteLength);
53   return true;
54 }
55 
Converter(const LSValue & aValue)56 LSValue::Converter::Converter(const LSValue& aValue) {
57   using ConversionType = LSValue::ConversionType;
58   using CompressionType = LSValue::CompressionType;
59 
60   if (aValue.mBuffer.IsVoid()) {
61     mBuffer.SetIsVoid(true);
62     return;
63   }
64 
65   const CompressionType compressionType = aValue.GetCompressionType();
66   const ConversionType conversionType = aValue.GetConversionType();
67 
68   const nsCString uncompressed = [compressionType, &aValue]() {
69     if (CompressionType::UNCOMPRESSED != compressionType) {
70       nsCString buffer;
71       MOZ_ASSERT(CompressionType::SNAPPY == compressionType);
72       if (NS_WARN_IF(!SnappyUncompress(aValue.mBuffer, buffer))) {
73         buffer.Truncate();
74       }
75       return buffer;
76     }
77 
78     return aValue.mBuffer;
79   }();
80 
81   if (ConversionType::NONE != conversionType) {
82     MOZ_ASSERT(ConversionType::UTF16_UTF8 == conversionType);
83     if (NS_WARN_IF(!CopyUTF8toUTF16(uncompressed, mBuffer, fallible))) {
84       mBuffer.SetIsVoid(true);
85     }
86     return;
87   }
88 
89   if (NS_WARN_IF(!PutCStringBytesToString(uncompressed, mBuffer))) {
90     mBuffer.SetIsVoid(true);
91   }
92 }
93 
InitFromString(const nsAString & aBuffer)94 bool LSValue::InitFromString(const nsAString& aBuffer) {
95   MOZ_ASSERT(mBuffer.IsVoid());
96   MOZ_ASSERT(!mUTF16Length);
97   MOZ_ASSERT(ConversionType::NONE == mConversionType);
98   MOZ_ASSERT(CompressionType::UNCOMPRESSED == mCompressionType);
99 
100   if (aBuffer.IsVoid()) {
101     return true;
102   }
103 
104   const uint32_t utf16Length = aBuffer.Length();
105 
106   const auto conversionRes = [&aBuffer]() -> TypeBufferResult<ConversionType> {
107     nsCString converted;
108 
109     if (Utf16ValidUpTo(aBuffer) == aBuffer.Length()) {
110       if (NS_WARN_IF(!CopyUTF16toUTF8(aBuffer, converted, fallible))) {
111         return Err(NS_ERROR_OUT_OF_MEMORY);
112       }
113       return std::pair{ConversionType::UTF16_UTF8, std::move(converted)};
114     }
115 
116     if (NS_WARN_IF(!PutStringBytesToCString(aBuffer, converted))) {
117       return Err(NS_ERROR_OUT_OF_MEMORY);
118     }
119     return std::pair{ConversionType::NONE, std::move(converted)};
120   }();
121 
122   if (conversionRes.isErr()) {
123     return false;
124   }
125 
126   const auto& [conversionType, converted] = conversionRes.inspect();
127 
128   const auto compressionRes =
129       [&converted = converted]() -> TypeBufferResult<CompressionType> {
130     nsCString compressed;
131     if (NS_WARN_IF(!SnappyCompress(converted, compressed))) {
132       return Err(NS_ERROR_OUT_OF_MEMORY);
133     }
134     if (!compressed.IsVoid()) {
135       return std::pair{CompressionType::SNAPPY, std::move(compressed)};
136     }
137 
138     compressed = converted;
139     return std::pair{CompressionType::UNCOMPRESSED, std::move(compressed)};
140   }();
141 
142   if (compressionRes.isErr()) {
143     return false;
144   }
145 
146   const auto& [compressionType, compressed] = compressionRes.inspect();
147 
148   mBuffer = compressed;
149   mUTF16Length = utf16Length;
150   mConversionType = conversionType;
151   mCompressionType = compressionType;
152 
153   return true;
154 }
155 
InitFromStatement(mozIStorageStatement * aStatement,uint32_t aIndex)156 nsresult LSValue::InitFromStatement(mozIStorageStatement* aStatement,
157                                     uint32_t aIndex) {
158   MOZ_ASSERT(aStatement);
159   MOZ_ASSERT(mBuffer.IsVoid());
160   MOZ_ASSERT(!mUTF16Length);
161   MOZ_ASSERT(ConversionType::NONE == mConversionType);
162   MOZ_ASSERT(CompressionType::UNCOMPRESSED == mCompressionType);
163 
164   int32_t utf16Length;
165   nsresult rv = aStatement->GetInt32(aIndex, &utf16Length);
166   if (NS_WARN_IF(NS_FAILED(rv))) {
167     return rv;
168   }
169 
170   int32_t conversionType;
171   rv = aStatement->GetInt32(aIndex + 1, &conversionType);
172   if (NS_WARN_IF(NS_FAILED(rv))) {
173     return rv;
174   }
175 
176   int32_t compressionType;
177   rv = aStatement->GetInt32(aIndex + 2, &compressionType);
178   if (NS_WARN_IF(NS_FAILED(rv))) {
179     return rv;
180   }
181 
182   nsCString buffer;
183   rv = aStatement->GetBlobAsUTF8String(aIndex + 3, buffer);
184   if (NS_WARN_IF(NS_FAILED(rv))) {
185     return rv;
186   }
187 
188   mBuffer = buffer;
189   mUTF16Length = static_cast<uint32_t>(utf16Length);
190   mConversionType = static_cast<decltype(mConversionType)>(conversionType);
191   mCompressionType = static_cast<decltype(mCompressionType)>(compressionType);
192 
193   return NS_OK;
194 }
195 
VoidLSValue()196 const LSValue& VoidLSValue() {
197   static const LSValue sVoidLSValue;
198 
199   return sVoidLSValue;
200 }
201 
202 }  // namespace mozilla::dom
203