/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=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/. */ #include "mozilla/dom/LSValue.h" #include "mozIStorageStatement.h" #include "mozilla/dom/SnappyUtils.h" #include "mozilla/fallible.h" #include "mozilla/TextUtils.h" #include "nsDebug.h" #include "nsError.h" namespace mozilla::dom { namespace { bool PutStringBytesToCString(const nsAString& aSrc, nsCString& aDest) { const char16_t* bufferData; const size_t byteLength = sizeof(char16_t) * aSrc.GetData(&bufferData); char* destDataPtr; const auto newLength = aDest.GetMutableData(&destDataPtr, byteLength); if (newLength != byteLength) { return false; } std::memcpy(static_cast(destDataPtr), static_cast(bufferData), byteLength); return true; } template using TypeBufferResult = Result, nsresult>; } // namespace bool PutCStringBytesToString(const nsACString& aSrc, nsString& aDest) { const char* bufferData; const size_t byteLength = aSrc.GetData(&bufferData); const size_t shortLength = byteLength / sizeof(char16_t); char16_t* destDataPtr; const auto newLength = aDest.GetMutableData(&destDataPtr, shortLength); if (newLength != shortLength) { return false; } std::memcpy(static_cast(destDataPtr), static_cast(bufferData), byteLength); return true; } LSValue::Converter::Converter(const LSValue& aValue) { using ConversionType = LSValue::ConversionType; using CompressionType = LSValue::CompressionType; if (aValue.mBuffer.IsVoid()) { mBuffer.SetIsVoid(true); return; } const CompressionType compressionType = aValue.GetCompressionType(); const ConversionType conversionType = aValue.GetConversionType(); const nsCString uncompressed = [compressionType, &aValue]() { if (CompressionType::UNCOMPRESSED != compressionType) { nsCString buffer; MOZ_ASSERT(CompressionType::SNAPPY == compressionType); if (NS_WARN_IF(!SnappyUncompress(aValue.mBuffer, buffer))) { buffer.Truncate(); } return buffer; } return aValue.mBuffer; }(); if (ConversionType::NONE != conversionType) { MOZ_ASSERT(ConversionType::UTF16_UTF8 == conversionType); if (NS_WARN_IF(!CopyUTF8toUTF16(uncompressed, mBuffer, fallible))) { mBuffer.SetIsVoid(true); } return; } if (NS_WARN_IF(!PutCStringBytesToString(uncompressed, mBuffer))) { mBuffer.SetIsVoid(true); } } bool LSValue::InitFromString(const nsAString& aBuffer) { MOZ_ASSERT(mBuffer.IsVoid()); MOZ_ASSERT(!mUTF16Length); MOZ_ASSERT(ConversionType::NONE == mConversionType); MOZ_ASSERT(CompressionType::UNCOMPRESSED == mCompressionType); if (aBuffer.IsVoid()) { return true; } const uint32_t utf16Length = aBuffer.Length(); const auto conversionRes = [&aBuffer]() -> TypeBufferResult { nsCString converted; if (Utf16ValidUpTo(aBuffer) == aBuffer.Length()) { if (NS_WARN_IF(!CopyUTF16toUTF8(aBuffer, converted, fallible))) { return Err(NS_ERROR_OUT_OF_MEMORY); } return std::pair{ConversionType::UTF16_UTF8, std::move(converted)}; } if (NS_WARN_IF(!PutStringBytesToCString(aBuffer, converted))) { return Err(NS_ERROR_OUT_OF_MEMORY); } return std::pair{ConversionType::NONE, std::move(converted)}; }(); if (conversionRes.isErr()) { return false; } const auto& [conversionType, converted] = conversionRes.inspect(); const auto compressionRes = [&converted = converted]() -> TypeBufferResult { nsCString compressed; if (NS_WARN_IF(!SnappyCompress(converted, compressed))) { return Err(NS_ERROR_OUT_OF_MEMORY); } if (!compressed.IsVoid()) { return std::pair{CompressionType::SNAPPY, std::move(compressed)}; } compressed = converted; return std::pair{CompressionType::UNCOMPRESSED, std::move(compressed)}; }(); if (compressionRes.isErr()) { return false; } const auto& [compressionType, compressed] = compressionRes.inspect(); mBuffer = compressed; mUTF16Length = utf16Length; mConversionType = conversionType; mCompressionType = compressionType; return true; } nsresult LSValue::InitFromStatement(mozIStorageStatement* aStatement, uint32_t aIndex) { MOZ_ASSERT(aStatement); MOZ_ASSERT(mBuffer.IsVoid()); MOZ_ASSERT(!mUTF16Length); MOZ_ASSERT(ConversionType::NONE == mConversionType); MOZ_ASSERT(CompressionType::UNCOMPRESSED == mCompressionType); int32_t utf16Length; nsresult rv = aStatement->GetInt32(aIndex, &utf16Length); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } int32_t conversionType; rv = aStatement->GetInt32(aIndex + 1, &conversionType); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } int32_t compressionType; rv = aStatement->GetInt32(aIndex + 2, &compressionType); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } nsCString buffer; rv = aStatement->GetBlobAsUTF8String(aIndex + 3, buffer); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } mBuffer = buffer; mUTF16Length = static_cast(utf16Length); mConversionType = static_cast(conversionType); mCompressionType = static_cast(compressionType); return NS_OK; } const LSValue& VoidLSValue() { static const LSValue sVoidLSValue; return sVoidLSValue; } } // namespace mozilla::dom