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 
21 #include <algorithm>
22 #include <limits>
23 #include <set>
24 #include <string.h>
25 
26 #include <com/sun/star/io/IOException.hpp>
27 #include <com/sun/star/io/XInputStream.hpp>
28 #include <com/sun/star/io/XOutputStream.hpp>
29 #include <com/sun/star/io/XSeekable.hpp>
30 #include <o3tl/safeint.hxx>
31 #include <osl/diagnose.h>
32 #include <svl/instrm.hxx>
33 #include <svl/outstrm.hxx>
34 
35 using namespace com::sun::star;
36 
37 class SvDataPipe_Impl
38 {
39 public:
40     enum SeekResult { SEEK_BEFORE_MARKED, SEEK_OK, SEEK_PAST_END };
41 
42 private:
43     struct Page
44     {
45         Page * m_pPrev;
46         Page * m_pNext;
47         sal_Int8 * m_pStart;
48         sal_Int8 * m_pRead;
49         sal_Int8 * m_pEnd;
50         sal_uInt32 m_nOffset;
51         sal_Int8 m_aBuffer[1];
52     };
53     static const sal_uInt32 m_nPageSize = 1000;
54 
55     std::multiset< sal_uInt32 > m_aMarks;
56     Page * m_pFirstPage;
57     Page * m_pReadPage;
58     Page * m_pWritePage;
59     sal_Int8 * m_pReadBuffer;
60     sal_uInt32 m_nReadBufferSize;
61     sal_uInt32 m_nReadBufferFilled;
62     sal_uInt32 m_nPages;
63     bool m_bEOF;
64 
65     void remove(Page * pPage);
66 
67 public:
68     inline SvDataPipe_Impl();
69 
70     ~SvDataPipe_Impl();
71 
72     inline void setReadBuffer(sal_Int8 * pBuffer, sal_uInt32 nSize);
73 
74     sal_uInt32 read();
75 
clearReadBuffer()76     void clearReadBuffer() { m_pReadBuffer = nullptr; }
77 
78     void write(sal_Int8 const * pBuffer, sal_uInt32 nSize);
79 
setEOF()80     void setEOF() { m_bEOF = true; }
81 
82     inline bool isEOF() const;
83 
84     SeekResult setReadPosition(sal_uInt32 nPosition);
85 };
86 
SvDataPipe_Impl()87 SvDataPipe_Impl::SvDataPipe_Impl()
88     : m_pFirstPage( nullptr )
89     , m_pReadPage( nullptr )
90     , m_pWritePage( nullptr )
91     , m_pReadBuffer( nullptr )
92     , m_nReadBufferSize( 0 )
93     , m_nReadBufferFilled( 0 )
94     , m_nPages( 0 )
95     , m_bEOF( false )
96 {}
97 
setReadBuffer(sal_Int8 * pBuffer,sal_uInt32 nSize)98 inline void SvDataPipe_Impl::setReadBuffer(sal_Int8 * pBuffer,
99                                            sal_uInt32 nSize)
100 {
101     m_pReadBuffer = pBuffer;
102     m_nReadBufferSize = nSize;
103     m_nReadBufferFilled = 0;
104 }
105 
isEOF() const106 inline bool SvDataPipe_Impl::isEOF() const
107 {
108     return m_bEOF && m_pReadPage == m_pWritePage
109            && (!m_pReadPage || m_pReadPage->m_pRead == m_pReadPage->m_pEnd);
110 }
111 
112 
113 
114 //  SvInputStream
115 
open()116 bool SvInputStream::open()
117 {
118     if (GetError() != ERRCODE_NONE)
119         return false;
120     if (!(m_xSeekable.is() || m_pPipe))
121     {
122         if (!m_xStream.is())
123         {
124             SetError(ERRCODE_IO_INVALIDDEVICE);
125             return false;
126         }
127         m_xSeekable.set(m_xStream, uno::UNO_QUERY);
128         if (!m_xSeekable.is())
129             m_pPipe.reset( new SvDataPipe_Impl );
130     }
131     return true;
132 }
133 
134 // virtual
GetData(void * pData,std::size_t const nSize)135 std::size_t SvInputStream::GetData(void * pData, std::size_t const nSize)
136 {
137     if (!open())
138     {
139         SetError(ERRCODE_IO_CANTREAD);
140         return 0;
141     }
142     // check if a truncated STREAM_SEEK_TO_END was passed
143     assert(m_nSeekedFrom != SAL_MAX_UINT32);
144     sal_uInt32 nRead = 0;
145     if (m_xSeekable.is())
146     {
147         if (m_nSeekedFrom != STREAM_SEEK_TO_END)
148         {
149             try
150             {
151                 m_xSeekable->seek(m_nSeekedFrom);
152             }
153             catch (const io::IOException&)
154             {
155                 SetError(ERRCODE_IO_CANTREAD);
156                 return 0;
157             }
158             m_nSeekedFrom = STREAM_SEEK_TO_END;
159         }
160         for (;;)
161         {
162             sal_Int32 nRemain
163                 = sal_Int32(
164                     std::min(std::size_t(nSize - nRead),
165                              std::size_t(std::numeric_limits<sal_Int32>::max())));
166             if (nRemain == 0)
167                 break;
168             uno::Sequence< sal_Int8 > aBuffer;
169             sal_Int32 nCount;
170             try
171             {
172                 nCount = m_xStream->readBytes(aBuffer, nRemain);
173             }
174             catch (const io::IOException&)
175             {
176                 SetError(ERRCODE_IO_CANTREAD);
177                 return nRead;
178             }
179             memcpy(static_cast< sal_Int8 * >(pData) + nRead,
180                            aBuffer.getConstArray(), sal_uInt32(nCount));
181             nRead += nCount;
182             if (nCount < nRemain)
183                 break;
184         }
185     }
186     else
187     {
188         if (m_nSeekedFrom != STREAM_SEEK_TO_END)
189         {
190             SetError(ERRCODE_IO_CANTREAD);
191             return 0;
192         }
193         m_pPipe->setReadBuffer(static_cast< sal_Int8 * >(pData), nSize);
194         nRead = m_pPipe->read();
195         if (nRead < nSize && !m_pPipe->isEOF())
196             for (;;)
197             {
198                 sal_Int32 nRemain
199                     = sal_Int32(
200                         std::min(
201                             std::size_t(nSize - nRead),
202                             std::size_t(std::numeric_limits<sal_Int32>::max())));
203                 if (nRemain == 0)
204                     break;
205                 uno::Sequence< sal_Int8 > aBuffer;
206                 sal_Int32 nCount;
207                 try
208                 {
209                     nCount = m_xStream->readBytes(aBuffer, nRemain);
210                 }
211                 catch (const io::IOException&)
212                 {
213                     SetError(ERRCODE_IO_CANTREAD);
214                     break;
215                 }
216                 m_pPipe->write(aBuffer.getConstArray(), sal_uInt32(nCount));
217                 nRead += m_pPipe->read();
218                 if (nCount < nRemain)
219                 {
220                     m_xStream->closeInput();
221                     m_pPipe->setEOF();
222                     break;
223                 }
224             }
225         m_pPipe->clearReadBuffer();
226     }
227     return nRead;
228 }
229 
230 // virtual
PutData(void const *,std::size_t)231 std::size_t SvInputStream::PutData(void const *, std::size_t)
232 {
233     SetError(ERRCODE_IO_NOTSUPPORTED);
234     return 0;
235 }
236 
237 // virtual
FlushData()238 void SvInputStream::FlushData()
239 {}
240 
241 // virtual
SeekPos(sal_uInt64 const nPos)242 sal_uInt64 SvInputStream::SeekPos(sal_uInt64 const nPos)
243 {
244     // check if a truncated STREAM_SEEK_TO_END was passed
245     assert(nPos != SAL_MAX_UINT32);
246     if (open())
247     {
248         if (nPos == STREAM_SEEK_TO_END)
249         {
250             if (m_nSeekedFrom == STREAM_SEEK_TO_END)
251             {
252                 if (m_xSeekable.is())
253                     try
254                     {
255                         sal_Int64 nLength = m_xSeekable->getLength();
256                         OSL_ASSERT(nLength >= 0);
257                         if (o3tl::make_unsigned(nLength)
258                             < STREAM_SEEK_TO_END)
259                         {
260                             m_nSeekedFrom = Tell();
261                             return sal_uInt64(nLength);
262                         }
263                     }
264                     catch (const io::IOException&)
265                     {
266                     }
267                 else
268                     return Tell(); //@@@
269             }
270             else
271                 return Tell();
272         }
273         else if (nPos == m_nSeekedFrom)
274         {
275             m_nSeekedFrom = STREAM_SEEK_TO_END;
276             return nPos;
277         }
278         else if (m_xSeekable.is())
279         {
280             try
281             {
282                 m_xSeekable->seek(nPos);
283                 m_nSeekedFrom = STREAM_SEEK_TO_END;
284                 return nPos;
285             }
286             catch (const io::IOException&)
287             {
288             }
289         }
290         else if (m_pPipe->setReadPosition(nPos) == SvDataPipe_Impl::SEEK_OK)
291         {
292             m_nSeekedFrom = STREAM_SEEK_TO_END;
293             return nPos;
294         }
295         else if ( nPos > Tell() )
296         {
297             // Read out the bytes
298             sal_Int32 nRead = nPos - Tell();
299             uno::Sequence< sal_Int8 > aBuffer;
300             m_xStream->readBytes( aBuffer, nRead );
301             return nPos;
302         }
303         else if ( nPos == Tell() )
304             return nPos;
305     }
306     SetError(ERRCODE_IO_CANTSEEK);
307     return Tell();
308 }
309 
310 // virtual
SetSize(sal_uInt64)311 void SvInputStream::SetSize(sal_uInt64)
312 {
313     SetError(ERRCODE_IO_NOTSUPPORTED);
314 }
315 
SvInputStream(css::uno::Reference<css::io::XInputStream> const & rTheStream)316 SvInputStream::SvInputStream( css::uno::Reference< css::io::XInputStream > const & rTheStream):
317     m_xStream(rTheStream),
318     m_nSeekedFrom(STREAM_SEEK_TO_END)
319 {
320     SetBufferSize(0);
321 }
322 
323 // virtual
~SvInputStream()324 SvInputStream::~SvInputStream()
325 {
326     if (m_xStream.is())
327     {
328         try
329         {
330             m_xStream->closeInput();
331         }
332         catch (const io::IOException&)
333         {
334         }
335     }
336 }
337 
338 //  SvOutputStream
339 
340 // virtual
GetData(void *,std::size_t)341 std::size_t SvOutputStream::GetData(void *, std::size_t)
342 {
343     SetError(ERRCODE_IO_NOTSUPPORTED);
344     return 0;
345 }
346 
347 // virtual
PutData(void const * pData,std::size_t nSize)348 std::size_t SvOutputStream::PutData(void const * pData, std::size_t nSize)
349 {
350     if (!m_xStream.is())
351     {
352         SetError(ERRCODE_IO_CANTWRITE);
353         return 0;
354     }
355     std::size_t nWritten = 0;
356     for (;;)
357     {
358         sal_Int32 nRemain
359             = sal_Int32(
360                 std::min(std::size_t(nSize - nWritten),
361                          std::size_t(std::numeric_limits<sal_Int32>::max())));
362         if (nRemain == 0)
363             break;
364         try
365         {
366             m_xStream->writeBytes(uno::Sequence< sal_Int8 >(
367                                       static_cast<const sal_Int8 * >(pData)
368                                           + nWritten,
369                                       nRemain));
370         }
371         catch (const io::IOException&)
372         {
373             SetError(ERRCODE_IO_CANTWRITE);
374             break;
375         }
376         nWritten += nRemain;
377     }
378     return nWritten;
379 }
380 
381 // virtual
SeekPos(sal_uInt64)382 sal_uInt64 SvOutputStream::SeekPos(sal_uInt64)
383 {
384     SetError(ERRCODE_IO_NOTSUPPORTED);
385     return 0;
386 }
387 
388 // virtual
FlushData()389 void SvOutputStream::FlushData()
390 {
391     if (!m_xStream.is())
392     {
393         SetError(ERRCODE_IO_INVALIDDEVICE);
394         return;
395     }
396     try
397     {
398         m_xStream->flush();
399     }
400     catch (const io::IOException&)
401     {
402     }
403 }
404 
405 // virtual
SetSize(sal_uInt64)406 void SvOutputStream::SetSize(sal_uInt64)
407 {
408     SetError(ERRCODE_IO_NOTSUPPORTED);
409 }
410 
SvOutputStream(uno::Reference<io::XOutputStream> const & rTheStream)411 SvOutputStream::SvOutputStream(uno::Reference< io::XOutputStream > const &
412                                    rTheStream):
413     m_xStream(rTheStream)
414 {
415     SetBufferSize(0);
416 }
417 
418 // virtual
~SvOutputStream()419 SvOutputStream::~SvOutputStream()
420 {
421     if (m_xStream.is())
422     {
423         try
424         {
425             m_xStream->closeOutput();
426         }
427         catch (const io::IOException&)
428         {
429         }
430     }
431 }
432 
433 
434 //  SvDataPipe_Impl
435 
436 
remove(Page * pPage)437 void SvDataPipe_Impl::remove(Page * pPage)
438 {
439     if (
440         pPage != m_pFirstPage ||
441         m_pReadPage == m_pFirstPage ||
442         (
443          !m_aMarks.empty() &&
444          *m_aMarks.begin() < m_pFirstPage->m_nOffset + m_nPageSize
445         )
446        )
447     {
448         return;
449     }
450 
451     m_pFirstPage = m_pFirstPage->m_pNext;
452 
453     if (m_nPages <= 100) // min pages
454         return;
455 
456     pPage->m_pPrev->m_pNext = pPage->m_pNext;
457     pPage->m_pNext->m_pPrev = pPage->m_pPrev;
458     std::free(pPage);
459     --m_nPages;
460 }
461 
~SvDataPipe_Impl()462 SvDataPipe_Impl::~SvDataPipe_Impl()
463 {
464     if (m_pFirstPage != nullptr)
465         for (Page * pPage = m_pFirstPage;;)
466         {
467             Page * pNext = pPage->m_pNext;
468             std::free(pPage);
469             if (pNext == m_pFirstPage)
470                 break;
471             pPage = pNext;
472         }
473 }
474 
read()475 sal_uInt32 SvDataPipe_Impl::read()
476 {
477     if (m_pReadBuffer == nullptr || m_nReadBufferSize == 0 || m_pReadPage == nullptr)
478         return 0;
479 
480     sal_uInt32 nSize = m_nReadBufferSize;
481     sal_uInt32 nRemain = m_nReadBufferSize - m_nReadBufferFilled;
482 
483     m_pReadBuffer += m_nReadBufferFilled;
484     m_nReadBufferSize -= m_nReadBufferFilled;
485     m_nReadBufferFilled = 0;
486 
487     while (nRemain > 0)
488     {
489         sal_uInt32 nBlock = std::min(sal_uInt32(m_pReadPage->m_pEnd
490                                                     - m_pReadPage->m_pRead),
491                                      nRemain);
492         memcpy(m_pReadBuffer, m_pReadPage->m_pRead, nBlock);
493         m_pReadPage->m_pRead += nBlock;
494         m_pReadBuffer += nBlock;
495         m_nReadBufferSize -= nBlock;
496         m_nReadBufferFilled = 0;
497         nRemain -= nBlock;
498 
499         if (m_pReadPage == m_pWritePage)
500             break;
501 
502         if (m_pReadPage->m_pRead == m_pReadPage->m_pEnd)
503         {
504             Page * pRemove = m_pReadPage;
505             m_pReadPage = pRemove->m_pNext;
506             remove(pRemove);
507         }
508     }
509 
510     return nSize - nRemain;
511 }
512 
write(sal_Int8 const * pBuffer,sal_uInt32 nSize)513 void SvDataPipe_Impl::write(sal_Int8 const * pBuffer, sal_uInt32 nSize)
514 {
515     if (nSize == 0)
516         return;
517 
518     if (m_pWritePage == nullptr)
519     {
520         m_pFirstPage
521             = static_cast< Page * >(std::malloc(sizeof (Page)
522                                            + m_nPageSize
523                                            - 1));
524         m_pFirstPage->m_pPrev = m_pFirstPage;
525         m_pFirstPage->m_pNext = m_pFirstPage;
526         m_pFirstPage->m_pStart = m_pFirstPage->m_aBuffer;
527         m_pFirstPage->m_pRead = m_pFirstPage->m_aBuffer;
528         m_pFirstPage->m_pEnd = m_pFirstPage->m_aBuffer;
529         m_pFirstPage->m_nOffset = 0;
530         m_pReadPage = m_pFirstPage;
531         m_pWritePage = m_pFirstPage;
532         ++m_nPages;
533     }
534 
535     sal_uInt32 nRemain = nSize;
536 
537     if (m_pReadBuffer != nullptr && m_pReadPage == m_pWritePage
538         && m_pReadPage->m_pRead == m_pWritePage->m_pEnd)
539     {
540         sal_uInt32 nBlock = std::min(nRemain,
541                                      sal_uInt32(m_nReadBufferSize
542                                                     - m_nReadBufferFilled));
543         sal_uInt32 nPosition = m_pWritePage->m_nOffset
544                                    + (m_pWritePage->m_pEnd
545                                           - m_pWritePage->m_aBuffer);
546         if (!m_aMarks.empty())
547             nBlock = *m_aMarks.begin() > nPosition ?
548                          std::min(nBlock, sal_uInt32(*m_aMarks.begin()
549                                                          - nPosition)) :
550                          0;
551 
552         if (nBlock > 0)
553         {
554             memcpy(m_pReadBuffer + m_nReadBufferFilled, pBuffer,
555                            nBlock);
556             m_nReadBufferFilled += nBlock;
557             nRemain -= nBlock;
558 
559             nPosition += nBlock;
560             m_pWritePage->m_nOffset = (nPosition / m_nPageSize) * m_nPageSize;
561             m_pWritePage->m_pStart = m_pWritePage->m_aBuffer
562                                          + nPosition % m_nPageSize;
563             m_pWritePage->m_pRead = m_pWritePage->m_pStart;
564             m_pWritePage->m_pEnd = m_pWritePage->m_pStart;
565         }
566     }
567 
568     if (nRemain <= 0)
569         return;
570 
571     for (;;)
572     {
573         sal_uInt32 nBlock
574             = std::min(sal_uInt32(m_pWritePage->m_aBuffer + m_nPageSize
575                                       - m_pWritePage->m_pEnd),
576                        nRemain);
577         memcpy(m_pWritePage->m_pEnd, pBuffer, nBlock);
578         m_pWritePage->m_pEnd += nBlock;
579         pBuffer += nBlock;
580         nRemain -= nBlock;
581 
582         if (nRemain == 0)
583             break;
584 
585         if (m_pWritePage->m_pNext == m_pFirstPage)
586         {
587             if (m_nPages == std::numeric_limits< sal_uInt32 >::max())
588                 break;
589 
590             Page * pNew
591                 = static_cast< Page * >(std::malloc(
592                                             sizeof (Page) + m_nPageSize
593                                                 - 1));
594             pNew->m_pPrev = m_pWritePage;
595             pNew->m_pNext = m_pWritePage->m_pNext;
596 
597             m_pWritePage->m_pNext->m_pPrev = pNew;
598             m_pWritePage->m_pNext = pNew;
599             ++m_nPages;
600         }
601 
602         m_pWritePage->m_pNext->m_nOffset = m_pWritePage->m_nOffset
603                                                + m_nPageSize;
604         m_pWritePage = m_pWritePage->m_pNext;
605         m_pWritePage->m_pStart = m_pWritePage->m_aBuffer;
606         m_pWritePage->m_pRead = m_pWritePage->m_aBuffer;
607         m_pWritePage->m_pEnd = m_pWritePage->m_aBuffer;
608     }
609 }
610 
setReadPosition(sal_uInt32 nPosition)611 SvDataPipe_Impl::SeekResult SvDataPipe_Impl::setReadPosition(sal_uInt32
612                                                                  nPosition)
613 {
614     if (m_pFirstPage == nullptr)
615         return nPosition == 0 ? SEEK_OK : SEEK_PAST_END;
616 
617     if (nPosition
618             <= m_pReadPage->m_nOffset
619                    + (m_pReadPage->m_pRead - m_pReadPage->m_aBuffer))
620     {
621         if (nPosition
622                 < m_pFirstPage->m_nOffset
623                       + (m_pFirstPage->m_pStart - m_pFirstPage->m_aBuffer))
624             return SEEK_BEFORE_MARKED;
625 
626         while (nPosition < m_pReadPage->m_nOffset)
627         {
628             m_pReadPage->m_pRead = m_pReadPage->m_pStart;
629             m_pReadPage = m_pReadPage->m_pPrev;
630         }
631     }
632     else
633     {
634         if (nPosition
635                 > m_pWritePage->m_nOffset
636                       + (m_pWritePage->m_pEnd - m_pWritePage->m_aBuffer))
637             return SEEK_PAST_END;
638 
639         while (m_pReadPage != m_pWritePage
640                && nPosition >= m_pReadPage->m_nOffset + m_nPageSize)
641         {
642             Page * pRemove = m_pReadPage;
643             m_pReadPage = pRemove->m_pNext;
644             remove(pRemove);
645         }
646     }
647 
648     m_pReadPage->m_pRead = m_pReadPage->m_aBuffer
649                                + (nPosition - m_pReadPage->m_nOffset);
650     return SEEK_OK;
651 }
652 
653 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
654