From ed5640d8b587fbcfed7dd7967f3de04b37a76f26 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 11:06:44 +0200 Subject: Adding upstream version 4:7.4.7. Signed-off-by: Daniel Baumann --- dbaccess/source/filter/hsqldb/rowinputbinary.cxx | 399 +++++++++++++++++++++++ 1 file changed, 399 insertions(+) create mode 100644 dbaccess/source/filter/hsqldb/rowinputbinary.cxx (limited to 'dbaccess/source/filter/hsqldb/rowinputbinary.cxx') diff --git a/dbaccess/source/filter/hsqldb/rowinputbinary.cxx b/dbaccess/source/filter/hsqldb/rowinputbinary.cxx new file mode 100644 index 000000000..95ce05f69 --- /dev/null +++ b/dbaccess/source/filter/hsqldb/rowinputbinary.cxx @@ -0,0 +1,399 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include "rowinputbinary.hxx" +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace +{ +/** + * Converts binary represented big integer value to BCD (Binary Coded + * Decimal), and returns a string representation of the number. + * + * Bytes[0] is the most significant part of the number. + */ +OUString lcl_double_dabble(const std::vector& bytes) +{ + size_t nbits = 8 * bytes.size(); // length of array in bits + size_t nscratch = nbits / 2; // length of scratch in bytes + std::vector scratch(nscratch, 0); + + for (size_t i = 0; i < bytes.size(); ++i) + { + for (size_t j = 0; j < 8; ++j) + { + /* This bit will be shifted in on the right. */ + int shifted_in = (bytes[i] & (1 << (7 - j))) ? 1 : 0; + + /* Add 3 everywhere that scratch[k] >= 5. */ + for (size_t k = 0; k < nscratch; ++k) + scratch[k] += (scratch[k] >= 5) ? 3 : 0; + + /* Shift scratch to the left by one position. */ + for (size_t k = 0; k < nscratch - 1; ++k) + { + scratch[k] <<= 1; + scratch[k] &= 0xF; + scratch[k] |= (scratch[k + 1] >= 8) ? 1 : 0; + } + + /* Shift in the new bit from arr. */ + scratch[nscratch - 1] <<= 1; + scratch[nscratch - 1] &= 0xF; + scratch[nscratch - 1] |= shifted_in; + } + } + + auto it = scratch.begin(); + /* Remove leading zeros from the scratch space. */ + while (*it == 0 && scratch.size() > 1) + { + it = scratch.erase(it); + } + + /* Convert the scratch space from BCD digits to ASCII. */ + for (auto& digit : scratch) + digit += '0'; + + /* Resize and return the resulting string. */ + return OStringToOUString(std::string_view(scratch.data(), scratch.size()), + RTL_TEXTENCODING_UTF8); +} + +OUString lcl_makeStringFromBigint(std::vector&& aBytes) +{ + OUStringBuffer sRet; + + // two's complement + if ((aBytes[0] & 0x80) != 0) + { + sRet.append("-"); + for (auto& byte : aBytes) + byte = ~byte; + // add 1 to byte array + // FIXME e.g. 10000 valid ? + for (size_t i = aBytes.size() - 1; i != 0; --i) + { + aBytes[i] += 1; + if (aBytes[i] != 0) + break; + } + } + // convert binary to BCD + OUString sNum = lcl_double_dabble(aBytes); + sRet.append(sNum); + return sRet.makeStringAndClear(); +} + +OUString lcl_putDot(const OUString& sNum, sal_Int32 nScale) +{ + // e.g. sNum = "0", nScale = 2 -> "0.00" + OUStringBuffer sBuf{ sNum }; + sal_Int32 nNullsToAppend = nScale - sNum.getLength() + 1; + for (sal_Int32 i = 0; i < nNullsToAppend; ++i) + sBuf.insert(0, "0"); + + if (nScale > 0) + sBuf.insert(sBuf.getLength() - 1 - nScale, "."); + return sBuf.makeStringAndClear(); +} +} + +namespace dbahsql +{ +using namespace css::uno; +using namespace css::sdbc; +using namespace css::io; +using namespace boost::posix_time; +using namespace boost::gregorian; + +HsqlRowInputStream::HsqlRowInputStream() {} + +void HsqlRowInputStream::setInputStream(Reference const& rStream) +{ + m_pStream = utl::UcbStreamHelper::CreateStream(rStream, true); + m_pStream->SetEndian(SvStreamEndian::BIG); +} + +SvStream* HsqlRowInputStream::getInputStream() const { return m_pStream.get(); } + +void HsqlRowInputStream::seek(sal_Int32 nPos) { m_pStream->Seek(nPos); } + +OUString HsqlRowInputStream::readString() +{ + sal_Int32 nLen = 0; + m_pStream->ReadInt32(nLen); + return readUTF(nLen); +} + +OUString HsqlRowInputStream::readUTF(sal_Int32 nUTFLen) +{ + Sequence aBuffer(nUTFLen); + sal_Unicode* pStr = aBuffer.getArray(); + + sal_Int32 nCount = 0; + sal_Int32 nStrLen = 0; + while (nCount < nUTFLen) + { + sal_uInt8 c = 0; + m_pStream->ReadUChar(c); + sal_uInt8 char2, char3; + switch (c >> 4) + { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + // 0xxxxxxx + nCount++; + pStr[nStrLen++] = c; + break; + + case 12: + case 13: + // 110x xxxx 10xx xxxx + nCount += 2; + if (nCount > nUTFLen) + { + throw WrongFormatException(); + } + + m_pStream->ReadUChar(char2); + if ((char2 & 0xC0) != 0x80) + { + throw WrongFormatException(); + } + + pStr[nStrLen++] = (sal_Unicode(c & 0x1F) << 6) | (char2 & 0x3F); + break; + + case 14: + // 1110 xxxx 10xx xxxx 10xx xxxx + nCount += 3; + if (nCount > nUTFLen) + { + throw WrongFormatException(); + } + + m_pStream->ReadUChar(char2); + m_pStream->ReadUChar(char3); + + if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80)) + { + throw WrongFormatException(); + } + pStr[nStrLen++] = (sal_Unicode(c & 0x0F) << 12) | (sal_Unicode(char2 & 0x3F) << 6) + | (char3 & 0x3F); + break; + + default: + // 10xx xxxx, 1111 xxxx + throw WrongFormatException(); + } + } + return OUString(pStr, nStrLen); +} + +bool HsqlRowInputStream::checkNull() +{ + sal_uInt8 nNull = 0; + m_pStream->ReadUChar(nNull); + return nNull == 0; +} + +std::vector HsqlRowInputStream::readOneRow(const std::vector& nColTypes) +{ + auto nLen = nColTypes.size(); + std::vector aData; + + for (size_t i = 0; i < nLen; ++i) + { + if (checkNull()) + { + aData.push_back(Any()); + continue; + } + + sal_Int32 nType = nColTypes[i].getDataType(); + + // TODO throw error on EoF + + switch (nType) + { + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::LONGVARCHAR: + aData.push_back(Any(readString())); + break; + case DataType::TINYINT: + case DataType::SMALLINT: + { + sal_Int16 value = 0; + m_pStream->ReadInt16(value); + aData.push_back(Any(value)); + } + break; + case DataType::INTEGER: + { + sal_Int32 value = 0; + m_pStream->ReadInt32(value); + aData.push_back(Any(value)); + } + break; + case DataType::BIGINT: + { + sal_Int64 value = 0; + m_pStream->ReadInt64(value); + aData.push_back(Any(value)); + } + break; + case DataType::REAL: + case DataType::FLOAT: + case DataType::DOUBLE: + { + double value = 0; + m_pStream->ReadDouble(value); + // FIXME double is not necessarily 4 bytes + aData.push_back(Any(value)); + } + break; + case DataType::NUMERIC: + case DataType::DECIMAL: + { + sal_Int32 nSize = 0; + m_pStream->ReadInt32(nSize); + + std::vector aBytes(nSize); + m_pStream->ReadBytes(aBytes.data(), nSize); + assert(aBytes.size() > 0); + + sal_Int32 nScale = 0; + m_pStream->ReadInt32(nScale); + + OUString sNum = lcl_makeStringFromBigint(std::move(aBytes)); + Sequence result{ Any(lcl_putDot(sNum, nScale)), Any(nScale) }; + aData.push_back(Any(result)); + } + break; + case DataType::DATE: + { + sal_Int64 value = 0; + m_pStream->ReadInt64(value); // in millisec, from 1970 + ptime epoch = time_from_string("1970-01-01 00:00:00.000"); + ptime time = epoch + milliseconds(value); + date asDate = time.date(); + + css::util::Date loDate(asDate.day(), asDate.month(), + asDate.year()); // day, month, year + aData.push_back(Any(loDate)); + } + break; + case DataType::TIME: + { + sal_Int64 value = 0; + m_pStream->ReadInt64(value); + auto valueInSecs = value / 1000; + /* Observed valueInSecs fall in the range from + negative one day to positive two days. Coerce + valueInSecs between zero and positive one day.*/ + const int secPerDay = 24 * 60 * 60; + valueInSecs = (valueInSecs + secPerDay) % secPerDay; + + auto nHours = valueInSecs / (60 * 60); + valueInSecs = valueInSecs % 3600; + const sal_uInt16 nMins = valueInSecs / 60; + const sal_uInt16 nSecs = valueInSecs % 60; + css::util::Time time((value % 1000) * 1000000, nSecs, nMins, nHours, true); + aData.push_back(Any(time)); + } + break; + case DataType::TIMESTAMP: + { + sal_Int64 nEpochMillis = 0; + m_pStream->ReadInt64(nEpochMillis); + ptime epoch = time_from_string("1970-01-01 00:00:00.000"); + ptime time = epoch + milliseconds(nEpochMillis); + date asDate = time.date(); + + sal_Int32 nNanos = 0; + m_pStream->ReadInt32(nNanos); + + // convert into LO internal representation of dateTime + css::util::DateTime dateTime; + dateTime.NanoSeconds = nNanos; + dateTime.Seconds = time.time_of_day().seconds(); + dateTime.Minutes = time.time_of_day().minutes(); + dateTime.Hours = time.time_of_day().hours(); + dateTime.Day = asDate.day(); + dateTime.Month = asDate.month(); + dateTime.Year = asDate.year(); + aData.push_back(Any(dateTime)); + } + break; + case DataType::BOOLEAN: + { + sal_uInt8 nBool = 0; + m_pStream->ReadUChar(nBool); + aData.push_back(Any(static_cast(nBool))); + } + break; + case DataType::OTHER: + aData.push_back(Any{}); // TODO + break; + case DataType::BINARY: + case DataType::VARBINARY: + case DataType::LONGVARBINARY: + { + sal_Int32 nSize = 0; + m_pStream->ReadInt32(nSize); + + Sequence aBytes(nSize); + m_pStream->ReadBytes(aBytes.getArray(), nSize); + aData.push_back(Any(aBytes)); + } + break; + + default: + throw WrongFormatException(); + } + } + return aData; +} + +} // namespace dbahsql + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- cgit v1.2.3