1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include "rowinputbinary.hxx"
21 #include <com/sun/star/sdbc/DataType.hpp>
22 #include <com/sun/star/io/WrongFormatException.hpp>
23 #include <com/sun/star/io/XConnectable.hpp>
24 #include <com/sun/star/util/Time.hpp>
25 #include <com/sun/star/util/Date.hpp>
26 #include <com/sun/star/util/DateTime.hpp>
27 
28 #include <unotools/ucbstreamhelper.hxx>
29 #include <tools/stream.hxx>
30 #include <rtl/ustrbuf.hxx>
31 
32 #include <boost/date_time/posix_time/posix_time.hpp>
33 
34 namespace
35 {
36 /**
37      * Converts binary represented big integer value to BCD (Binary Coded
38      * Decimal), and returns a string representation of the number.
39      *
40      * Bytes[0] is the most significant part of the number.
41      */
42 OUString lcl_double_dabble(const std::vector<sal_uInt8>& bytes)
43 {
44     size_t nbits = 8 * bytes.size(); // length of array in bits
45     size_t nscratch = nbits / 2; // length of scratch in bytes
46     std::vector<char> scratch(nscratch, 0);
47 
48     for (size_t i = 0; i < bytes.size(); ++i)
49     {
50         for (size_t j = 0; j < 8; ++j)
51         {
52             /* This bit will be shifted in on the right. */
53             int shifted_in = (bytes[i] & (1 << (7 - j))) ? 1 : 0;
54 
55             /* Add 3 everywhere that scratch[k] >= 5. */
56             for (size_t k = 0; k < nscratch; ++k)
57                 scratch[k] += (scratch[k] >= 5) ? 3 : 0;
58 
59             /* Shift scratch to the left by one position. */
60             for (size_t k = 0; k < nscratch - 1; ++k)
61             {
62                 scratch[k] <<= 1;
63                 scratch[k] &= 0xF;
64                 scratch[k] |= (scratch[k + 1] >= 8) ? 1 : 0;
65             }
66 
67             /* Shift in the new bit from arr. */
68             scratch[nscratch - 1] <<= 1;
69             scratch[nscratch - 1] &= 0xF;
70             scratch[nscratch - 1] |= shifted_in;
71         }
72     }
73 
74     auto it = scratch.begin();
75     /* Remove leading zeros from the scratch space. */
76     while (*it == 0 && scratch.size() > 1)
77     {
78         it = scratch.erase(it);
79     }
80 
81     /* Convert the scratch space from BCD digits to ASCII. */
82     for (auto& digit : scratch)
83         digit += '0';
84 
85     /* Resize and return the resulting string. */
86     return OStringToOUString(OString(scratch.data(), scratch.size()), RTL_TEXTENCODING_UTF8);
87 }
88 
89 OUString lcl_makeStringFromBigint(const std::vector<sal_uInt8>& bytes)
90 {
91     std::vector<sal_uInt8> aBytes{ bytes };
92     OUStringBuffer sRet;
93 
94     // two's complement
95     if ((aBytes[0] & 0x80) != 0)
96     {
97         sRet.append("-");
98         for (auto& byte : aBytes)
99             byte = ~byte;
100         // add 1 to byte array
101         // FIXME e.g. 10000 valid ?
102         for (size_t i = aBytes.size() - 1; i != 0; --i)
103         {
104             aBytes[i] += 1;
105             if (aBytes[i] != 0)
106                 break;
107         }
108     }
109     // convert binary to BCD
110     OUString sNum = lcl_double_dabble(aBytes);
111     sRet.append(sNum);
112     return sRet.makeStringAndClear();
113 }
114 
115 OUString lcl_putDot(const OUString& sNum, sal_Int32 nScale)
116 {
117     // e.g. sNum = "0", nScale = 2 -> "0.00"
118     OUStringBuffer sBuf{ sNum };
119     sal_Int32 nNullsToAppend = nScale - sNum.getLength() + 1;
120     for (sal_Int32 i = 0; i < nNullsToAppend; ++i)
121         sBuf.insert(0, "0");
122 
123     if (nScale > 0)
124         sBuf.insert(sBuf.getLength() - 1 - nScale, ".");
125     return sBuf.makeStringAndClear();
126 }
127 }
128 
129 namespace dbahsql
130 {
131 using namespace css::uno;
132 using namespace css::sdbc;
133 using namespace css::io;
134 using namespace boost::posix_time;
135 using namespace boost::gregorian;
136 
137 HsqlRowInputStream::HsqlRowInputStream() {}
138 
139 void HsqlRowInputStream::setInputStream(Reference<XInputStream> const& rStream)
140 {
141     m_pStream = utl::UcbStreamHelper::CreateStream(rStream, true);
142     m_pStream->SetEndian(SvStreamEndian::BIG);
143 }
144 
145 SvStream* HsqlRowInputStream::getInputStream() const { return m_pStream.get(); }
146 
147 void HsqlRowInputStream::seek(sal_Int32 nPos) { m_pStream->Seek(nPos); }
148 
149 OUString HsqlRowInputStream::readString()
150 {
151     sal_Int32 nLen = 0;
152     m_pStream->ReadInt32(nLen);
153     return readUTF(nLen);
154 }
155 
156 OUString HsqlRowInputStream::readUTF(sal_Int32 nUTFLen)
157 {
158     Sequence<sal_Unicode> aBuffer(nUTFLen);
159     sal_Unicode* pStr = aBuffer.getArray();
160 
161     sal_Int32 nCount = 0;
162     sal_Int32 nStrLen = 0;
163     while (nCount < nUTFLen)
164     {
165         unsigned char cIn = 0;
166         m_pStream->ReadUChar(cIn);
167         sal_uInt8 c = reinterpret_cast<sal_uInt8&>(cIn);
168         sal_uInt8 char2, char3;
169         switch (c >> 4)
170         {
171             case 0:
172             case 1:
173             case 2:
174             case 3:
175             case 4:
176             case 5:
177             case 6:
178             case 7:
179                 // 0xxxxxxx
180                 nCount++;
181                 pStr[nStrLen++] = c;
182                 break;
183 
184             case 12:
185             case 13:
186                 // 110x xxxx   10xx xxxx
187                 nCount += 2;
188                 if (nCount > nUTFLen)
189                 {
190                     throw WrongFormatException();
191                 }
192 
193                 m_pStream->ReadUChar(cIn);
194                 char2 = reinterpret_cast<sal_uInt8&>(cIn);
195                 if ((char2 & 0xC0) != 0x80)
196                 {
197                     throw WrongFormatException();
198                 }
199 
200                 pStr[nStrLen++] = (sal_Unicode(c & 0x1F) << 6) | (char2 & 0x3F);
201                 break;
202 
203             case 14:
204                 // 1110 xxxx  10xx xxxx  10xx xxxx
205                 nCount += 3;
206                 if (nCount > nUTFLen)
207                 {
208                     throw WrongFormatException();
209                 }
210 
211                 m_pStream->ReadUChar(cIn);
212                 char2 = reinterpret_cast<sal_uInt8&>(cIn);
213                 m_pStream->ReadUChar(cIn);
214                 char3 = reinterpret_cast<sal_uInt8&>(cIn);
215 
216                 if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80))
217                 {
218                     throw WrongFormatException();
219                 }
220                 pStr[nStrLen++] = (sal_Unicode(c & 0x0F) << 12) | (sal_Unicode(char2 & 0x3F) << 6)
221                                   | (char3 & 0x3F);
222                 break;
223 
224             default:
225                 // 10xx xxxx,  1111 xxxx
226                 throw WrongFormatException();
227         }
228     }
229     return OUString(pStr, nStrLen);
230 }
231 
232 bool HsqlRowInputStream::checkNull()
233 {
234     unsigned char cIn = 0;
235     m_pStream->ReadUChar(cIn);
236     sal_uInt8 nNull = reinterpret_cast<sal_uInt8&>(cIn);
237     return nNull == 0;
238 }
239 
240 std::vector<Any> HsqlRowInputStream::readOneRow(const std::vector<ColumnDefinition>& nColTypes)
241 {
242     auto nLen = nColTypes.size();
243     std::vector<Any> aData;
244 
245     for (size_t i = 0; i < nLen; ++i)
246     {
247         if (checkNull())
248         {
249             aData.push_back(Any());
250             continue;
251         }
252 
253         sal_Int32 nType = nColTypes[i].getDataType();
254 
255         // TODO throw error on EoF
256 
257         switch (nType)
258         {
259             case DataType::CHAR:
260             case DataType::VARCHAR:
261             case DataType::LONGVARCHAR:
262                 aData.push_back(makeAny(readString()));
263                 break;
264             case DataType::TINYINT:
265             case DataType::SMALLINT:
266             {
267                 sal_Int16 value = 0;
268                 m_pStream->ReadInt16(value);
269                 aData.push_back(makeAny(value));
270             }
271             break;
272             case DataType::INTEGER:
273             {
274                 sal_Int32 value = 0;
275                 m_pStream->ReadInt32(value);
276                 aData.push_back(makeAny(value));
277             }
278             break;
279             case DataType::BIGINT:
280             {
281                 sal_Int64 value = 0;
282                 m_pStream->ReadInt64(value);
283                 aData.push_back(makeAny(value));
284             }
285             break;
286             case DataType::REAL:
287             case DataType::FLOAT:
288             case DataType::DOUBLE:
289             {
290                 double value = 0;
291                 m_pStream->ReadDouble(value);
292                 // FIXME double is not necessarily 4 bytes
293                 aData.push_back(makeAny(value));
294             }
295             break;
296             case DataType::NUMERIC:
297             case DataType::DECIMAL:
298             {
299                 sal_Int32 nSize = 0;
300                 m_pStream->ReadInt32(nSize);
301 
302                 std::vector<sal_uInt8> aBytes(nSize);
303                 m_pStream->ReadBytes(aBytes.data(), nSize);
304                 assert(aBytes.size() > 0);
305 
306                 sal_Int32 nScale = 0;
307                 m_pStream->ReadInt32(nScale);
308 
309                 Sequence<Any> result(2);
310                 OUString sNum = lcl_makeStringFromBigint(aBytes);
311                 result[0] <<= lcl_putDot(sNum, nScale);
312                 result[1] <<= nScale;
313                 aData.push_back(makeAny(result));
314             }
315             break;
316             case DataType::DATE:
317             {
318                 sal_Int64 value = 0;
319                 m_pStream->ReadInt64(value); // in millisec, from 1970
320                 ptime epoch = time_from_string("1970-01-01 00:00:00.000");
321                 ptime time = epoch + milliseconds(value);
322                 date asDate = time.date();
323 
324                 css::util::Date loDate(asDate.day(), asDate.month(),
325                                        asDate.year()); // day, month, year
326                 aData.push_back(makeAny(loDate));
327             }
328             break;
329             case DataType::TIME:
330             {
331                 sal_Int64 value = 0;
332                 m_pStream->ReadInt64(value);
333                 auto valueInSecs = value / 1000;
334                 /* Observed valueInSecs fall in the range from
335                    negative one day to positive two days.  Coerce
336                    valueInSecs between zero and positive one day.*/
337                 const int secPerDay = 24 * 60 * 60;
338                 valueInSecs = (valueInSecs + secPerDay) % secPerDay;
339 
340                 auto nHours = valueInSecs / (60 * 60);
341                 valueInSecs = valueInSecs % 3600;
342                 const sal_uInt16 nMins = valueInSecs / 60;
343                 const sal_uInt16 nSecs = valueInSecs % 60;
344                 css::util::Time time((value % 1000) * 1000000, nSecs, nMins, nHours, true);
345                 aData.push_back(makeAny(time));
346             }
347             break;
348             case DataType::TIMESTAMP:
349             {
350                 sal_Int64 nEpochMillis = 0;
351                 m_pStream->ReadInt64(nEpochMillis);
352                 ptime epoch = time_from_string("1970-01-01 00:00:00.000");
353                 ptime time = epoch + milliseconds(nEpochMillis);
354                 date asDate = time.date();
355 
356                 sal_Int32 nNanos = 0;
357                 m_pStream->ReadInt32(nNanos);
358 
359                 // convert into LO internal representation of dateTime
360                 css::util::DateTime dateTime;
361                 dateTime.NanoSeconds = nNanos;
362                 dateTime.Seconds = time.time_of_day().seconds();
363                 dateTime.Minutes = time.time_of_day().minutes();
364                 dateTime.Hours = time.time_of_day().hours();
365                 dateTime.Day = asDate.day();
366                 dateTime.Month = asDate.month();
367                 dateTime.Year = asDate.year();
368                 aData.push_back(makeAny(dateTime));
369             }
370             break;
371             case DataType::BOOLEAN:
372             {
373                 sal_uInt8 nBool = 0;
374                 m_pStream->ReadUChar(nBool);
375                 aData.push_back(makeAny(static_cast<bool>(nBool)));
376             }
377             break;
378             case DataType::OTHER:
379                 aData.push_back(Any{}); // TODO
380                 break;
381             case DataType::BINARY:
382             case DataType::VARBINARY:
383             case DataType::LONGVARBINARY:
384             {
385                 sal_Int32 nSize = 0;
386                 m_pStream->ReadInt32(nSize);
387 
388                 Sequence<sal_Int8> aBytes(nSize);
389                 m_pStream->ReadBytes(aBytes.getArray(), nSize);
390                 aData.push_back(makeAny(aBytes));
391             }
392             break;
393 
394             default:
395                 throw WrongFormatException();
396         }
397     }
398     return aData;
399 }
400 
401 } // namespace dbahsql
402 
403 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
404