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