1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
24 #include <com/sun/star/io/BufferSizeExceededException.hpp>
25 #include <com/sun/star/io/NotConnectedException.hpp>
26 #include <com/sun/star/io/XMarkableStream.hpp>
27 #include <com/sun/star/io/XOutputStream.hpp>
28 #include <com/sun/star/io/XInputStream.hpp>
29 #include <com/sun/star/io/XActiveDataSource.hpp>
30 #include <com/sun/star/io/XActiveDataSink.hpp>
31 #include <com/sun/star/io/XConnectable.hpp>
32 #include <com/sun/star/lang/IllegalArgumentException.hpp>
33 #include <com/sun/star/lang/XServiceInfo.hpp>
35 #include <cppuhelper/weak.hxx>
36 #include <cppuhelper/implbase.hxx>
37 #include <cppuhelper/supportsservice.hxx>
39 #include <osl/mutex.hxx>
40 #include <osl/diagnose.h>
43 using namespace ::std
;
44 using namespace ::cppu
;
45 using namespace ::osl
;
46 using namespace ::com::sun::star::io
;
47 using namespace ::com::sun::star::uno
;
48 using namespace ::com::sun::star::lang
;
50 #include <services.hxx>
51 #include "streamhelper.hxx"
55 /***********************
57 * OMarkableOutputStream.
59 * This object allows to set marks in an outputstream. It is allowed to jump back to the marks and
60 * rewrite the same bytes.
62 * The object must buffer the data since the last mark set. Flush will not
63 * have any effect. As soon as the last mark has been removed, the object may write the data
64 * through to the chained object.
66 **********************/
67 class OMarkableOutputStream
:
68 public WeakImplHelper
< XOutputStream
,
76 OMarkableOutputStream( );
78 public: // XOutputStream
79 virtual void SAL_CALL
writeBytes(const Sequence
< sal_Int8
>& aData
) override
;
80 virtual void SAL_CALL
flush() override
;
81 virtual void SAL_CALL
closeOutput() override
;
84 virtual sal_Int32 SAL_CALL
createMark() override
;
85 virtual void SAL_CALL
deleteMark(sal_Int32 Mark
) override
;
86 virtual void SAL_CALL
jumpToMark(sal_Int32 nMark
) override
;
87 virtual void SAL_CALL
jumpToFurthest() override
;
88 virtual sal_Int32 SAL_CALL
offsetToMark(sal_Int32 nMark
) override
;
90 public: // XActiveDataSource
91 virtual void SAL_CALL
setOutputStream(const Reference
< XOutputStream
> & aStream
) override
;
92 virtual Reference
< XOutputStream
> SAL_CALL
getOutputStream() override
;
94 public: // XConnectable
95 virtual void SAL_CALL
setPredecessor(const Reference
< XConnectable
> & aPredecessor
) override
;
96 virtual Reference
< XConnectable
> SAL_CALL
getPredecessor() override
;
97 virtual void SAL_CALL
setSuccessor(const Reference
< XConnectable
>& aSuccessor
) override
;
98 virtual Reference
< XConnectable
> SAL_CALL
getSuccessor() override
;
100 public: // XServiceInfo
101 OUString SAL_CALL
getImplementationName() override
;
102 Sequence
< OUString
> SAL_CALL
getSupportedServiceNames() override
;
103 sal_Bool SAL_CALL
supportsService(const OUString
& ServiceName
) override
;
107 /// @throws NotConnectedException
108 /// @throws BufferSizeExceededException
109 void checkMarksAndFlush();
111 Reference
< XConnectable
> m_succ
;
112 Reference
< XConnectable
> m_pred
;
114 Reference
< XOutputStream
> m_output
;
117 std::unique_ptr
<MemRingBuffer
> m_pBuffer
;
118 map
<sal_Int32
,sal_Int32
,less
< sal_Int32
> > m_mapMarks
;
119 sal_Int32 m_nCurrentPos
;
120 sal_Int32 m_nCurrentMark
;
125 OMarkableOutputStream::OMarkableOutputStream( )
126 : m_bValidStream(false)
127 , m_pBuffer( new MemRingBuffer
)
134 void OMarkableOutputStream::writeBytes(const Sequence
< sal_Int8
>& aData
)
136 if( !m_bValidStream
) {
137 throw NotConnectedException();
139 if( m_mapMarks
.empty() && ( m_pBuffer
->getSize() == 0 ) ) {
140 // no mark and buffer active, simple write through
141 m_output
->writeBytes( aData
);
144 MutexGuard
guard( m_mutex
);
145 // new data must be buffered
146 m_pBuffer
->writeAt( m_nCurrentPos
, aData
);
147 m_nCurrentPos
+= aData
.getLength();
148 checkMarksAndFlush();
153 void OMarkableOutputStream::flush()
155 Reference
< XOutputStream
> output
;
157 MutexGuard
guard( m_mutex
);
161 // Markable cannot flush buffered data, because the data may get rewritten,
162 // however one can forward the flush to the chained stream to give it
163 // a chance to write data buffered in the chained stream.
170 void OMarkableOutputStream::closeOutput()
172 if( !m_bValidStream
) {
173 throw NotConnectedException();
175 MutexGuard
guard( m_mutex
);
176 // all marks must be cleared and all
179 m_nCurrentPos
= m_pBuffer
->getSize();
180 checkMarksAndFlush();
182 m_output
->closeOutput();
184 setOutputStream( Reference
< XOutputStream
> () );
185 setPredecessor( Reference
< XConnectable
>() );
186 setSuccessor( Reference
< XConnectable
> () );
191 sal_Int32
OMarkableOutputStream::createMark()
193 MutexGuard
guard( m_mutex
);
194 sal_Int32 nMark
= m_nCurrentMark
;
196 m_mapMarks
[nMark
] = m_nCurrentPos
;
202 void OMarkableOutputStream::deleteMark(sal_Int32 Mark
)
204 MutexGuard
guard( m_mutex
);
205 map
<sal_Int32
,sal_Int32
,less
<sal_Int32
> >::iterator ii
= m_mapMarks
.find( Mark
);
207 if( ii
== m_mapMarks
.end() ) {
208 throw IllegalArgumentException(
209 "MarkableOutputStream::deleteMark unknown mark (" + OUString::number(Mark
) + ")",
212 m_mapMarks
.erase( ii
);
213 checkMarksAndFlush();
216 void OMarkableOutputStream::jumpToMark(sal_Int32 nMark
)
218 MutexGuard
guard( m_mutex
);
219 map
<sal_Int32
,sal_Int32
,less
<sal_Int32
> >::iterator ii
= m_mapMarks
.find( nMark
);
221 if( ii
== m_mapMarks
.end() ) {
222 throw IllegalArgumentException(
223 "MarkableOutputStream::jumpToMark unknown mark (" + OUString::number(nMark
) + ")",
226 m_nCurrentPos
= (*ii
).second
;
229 void OMarkableOutputStream::jumpToFurthest()
231 MutexGuard
guard( m_mutex
);
232 m_nCurrentPos
= m_pBuffer
->getSize();
233 checkMarksAndFlush();
236 sal_Int32
OMarkableOutputStream::offsetToMark(sal_Int32 nMark
)
239 MutexGuard
guard( m_mutex
);
240 map
<sal_Int32
,sal_Int32
,less
<sal_Int32
> >::const_iterator ii
= m_mapMarks
.find( nMark
);
242 if( ii
== m_mapMarks
.end() )
244 throw IllegalArgumentException(
245 "MarkableOutputStream::offsetToMark unknown mark (" + OUString::number(nMark
) + ")",
248 return m_nCurrentPos
- (*ii
).second
;
252 // XActiveDataSource2
253 void OMarkableOutputStream::setOutputStream(const Reference
< XOutputStream
>& aStream
)
255 if( m_output
!= aStream
) {
258 Reference
< XConnectable
> succ( m_output
, UNO_QUERY
);
259 setSuccessor( succ
);
261 m_bValidStream
= m_output
.is();
264 Reference
< XOutputStream
> OMarkableOutputStream::getOutputStream()
270 void OMarkableOutputStream::setSuccessor( const Reference
< XConnectable
> &r
)
272 /// if the references match, nothing needs to be done
274 /// store the reference for later use
278 m_succ
->setPredecessor( Reference
< XConnectable
> (
279 static_cast< XConnectable
* >(this) ) );
283 Reference
<XConnectable
> OMarkableOutputStream::getSuccessor()
290 void OMarkableOutputStream::setPredecessor( const Reference
< XConnectable
> &r
)
295 m_pred
->setSuccessor( Reference
< XConnectable
> (
296 static_cast< XConnectable
* >(this ) ) );
300 Reference
< XConnectable
> OMarkableOutputStream::getPredecessor()
308 void OMarkableOutputStream::checkMarksAndFlush()
310 map
<sal_Int32
,sal_Int32
,less
<sal_Int32
> >::iterator ii
;
312 // find the smallest mark
313 sal_Int32 nNextFound
= m_nCurrentPos
;
314 for (auto const& mark
: m_mapMarks
)
316 if( mark
.second
<= nNextFound
) {
317 nNextFound
= mark
.second
;
322 // some data must be released !
323 m_nCurrentPos
-= nNextFound
;
324 for (auto & mark
: m_mapMarks
)
326 mark
.second
-= nNextFound
;
329 Sequence
<sal_Int8
> seq(nNextFound
);
330 m_pBuffer
->readAt( 0 , seq
, nNextFound
);
331 m_pBuffer
->forgetFromStart( nNextFound
);
333 // now write data through to streams
334 m_output
->writeBytes( seq
);
337 // nothing to do. There is a mark or the current cursor position, that prevents
344 OUString
OMarkableOutputStream::getImplementationName()
346 return OMarkableOutputStream_getImplementationName();
350 sal_Bool
OMarkableOutputStream::supportsService(const OUString
& ServiceName
)
352 return cppu::supportsService(this, ServiceName
);
356 Sequence
< OUString
> OMarkableOutputStream::getSupportedServiceNames()
358 return OMarkableOutputStream_getSupportedServiceNames();
361 /*------------------------
365 *------------------------*/
366 Reference
< XInterface
> OMarkableOutputStream_CreateInstance(
367 SAL_UNUSED_PARAMETER
const Reference
< XComponentContext
> & )
369 OMarkableOutputStream
*p
= new OMarkableOutputStream( );
371 return Reference
< XInterface
> ( static_cast<OWeakObject
*>(p
) );
374 OUString
OMarkableOutputStream_getImplementationName()
376 return "com.sun.star.comp.io.stm.MarkableOutputStream";
379 Sequence
<OUString
> OMarkableOutputStream_getSupportedServiceNames()
381 Sequence
<OUString
> aRet
{ "com.sun.star.io.MarkableOutputStream" };
387 // XMarkableInputStream
390 class OMarkableInputStream
:
391 public WeakImplHelper
401 OMarkableInputStream( );
404 public: // XInputStream
405 virtual sal_Int32 SAL_CALL
readBytes(Sequence
< sal_Int8
>& aData
, sal_Int32 nBytesToRead
) override
;
406 virtual sal_Int32 SAL_CALL
readSomeBytes(Sequence
< sal_Int8
>& aData
, sal_Int32 nMaxBytesToRead
) override
;
407 virtual void SAL_CALL
skipBytes(sal_Int32 nBytesToSkip
) override
;
409 virtual sal_Int32 SAL_CALL
available() override
;
410 virtual void SAL_CALL
closeInput() override
;
413 virtual sal_Int32 SAL_CALL
createMark() override
;
414 virtual void SAL_CALL
deleteMark(sal_Int32 Mark
) override
;
415 virtual void SAL_CALL
jumpToMark(sal_Int32 nMark
) override
;
416 virtual void SAL_CALL
jumpToFurthest() override
;
417 virtual sal_Int32 SAL_CALL
offsetToMark(sal_Int32 nMark
) override
;
419 public: // XActiveDataSink
420 virtual void SAL_CALL
setInputStream(const Reference
< XInputStream
> & aStream
) override
;
421 virtual Reference
< XInputStream
> SAL_CALL
getInputStream() override
;
423 public: // XConnectable
424 virtual void SAL_CALL
setPredecessor(const Reference
< XConnectable
> & aPredecessor
) override
;
425 virtual Reference
< XConnectable
> SAL_CALL
getPredecessor() override
;
426 virtual void SAL_CALL
setSuccessor(const Reference
< XConnectable
> & aSuccessor
) override
;
427 virtual Reference
< XConnectable
> SAL_CALL
getSuccessor() override
;
429 public: // XServiceInfo
430 OUString SAL_CALL
getImplementationName() override
;
431 Sequence
< OUString
> SAL_CALL
getSupportedServiceNames() override
;
432 sal_Bool SAL_CALL
supportsService(const OUString
& ServiceName
) override
;
435 void checkMarksAndFlush();
437 Reference
< XConnectable
> m_succ
;
438 Reference
< XConnectable
> m_pred
;
440 Reference
< XInputStream
> m_input
;
443 std::unique_ptr
<MemRingBuffer
> m_pBuffer
;
444 map
<sal_Int32
,sal_Int32
,less
< sal_Int32
> > m_mapMarks
;
445 sal_Int32 m_nCurrentPos
;
446 sal_Int32 m_nCurrentMark
;
451 OMarkableInputStream::OMarkableInputStream()
452 : m_bValidStream(false)
456 m_pBuffer
.reset( new MemRingBuffer
);
462 sal_Int32
OMarkableInputStream::readBytes(Sequence
< sal_Int8
>& aData
, sal_Int32 nBytesToRead
)
464 sal_Int32 nBytesRead
;
466 if( !m_bValidStream
) {
467 throw NotConnectedException(
468 "MarkableInputStream::readBytes NotConnectedException",
471 MutexGuard
guard( m_mutex
);
472 if( m_mapMarks
.empty() && ! m_pBuffer
->getSize() ) {
474 nBytesRead
= m_input
->readBytes( aData
, nBytesToRead
);
480 // read enough bytes into buffer
481 if( m_pBuffer
->getSize() - m_nCurrentPos
< nBytesToRead
) {
482 sal_Int32 nToRead
= nBytesToRead
- ( m_pBuffer
->getSize() - m_nCurrentPos
);
483 nRead
= m_input
->readBytes( aData
, nToRead
);
485 OSL_ASSERT( aData
.getLength() == nRead
);
487 m_pBuffer
->writeAt( m_pBuffer
->getSize() , aData
);
489 if( nRead
< nToRead
) {
490 nBytesToRead
= nBytesToRead
- (nToRead
-nRead
);
494 OSL_ASSERT( m_pBuffer
->getSize() - m_nCurrentPos
>= nBytesToRead
);
496 m_pBuffer
->readAt( m_nCurrentPos
, aData
, nBytesToRead
);
498 m_nCurrentPos
+= nBytesToRead
;
499 nBytesRead
= nBytesToRead
;
506 sal_Int32
OMarkableInputStream::readSomeBytes(Sequence
< sal_Int8
>& aData
, sal_Int32 nMaxBytesToRead
)
509 sal_Int32 nBytesRead
;
510 if( !m_bValidStream
) {
511 throw NotConnectedException(
512 "MarkableInputStream::readSomeBytes NotConnectedException",
516 MutexGuard
guard( m_mutex
);
517 if( m_mapMarks
.empty() && ! m_pBuffer
->getSize() ) {
519 nBytesRead
= m_input
->readSomeBytes( aData
, nMaxBytesToRead
);
524 sal_Int32 nInBuffer
= m_pBuffer
->getSize() - m_nCurrentPos
;
525 sal_Int32 nAdditionalBytesToRead
= std::min
<sal_Int32
>(nMaxBytesToRead
-nInBuffer
,m_input
->available());
526 nAdditionalBytesToRead
= std::max
<sal_Int32
>(0 , nAdditionalBytesToRead
);
528 // read enough bytes into buffer
529 if( 0 == nInBuffer
) {
530 nRead
= m_input
->readSomeBytes( aData
, nMaxBytesToRead
);
532 else if( nAdditionalBytesToRead
) {
533 nRead
= m_input
->readBytes( aData
, nAdditionalBytesToRead
);
537 aData
.realloc( nRead
);
538 m_pBuffer
->writeAt( m_pBuffer
->getSize() , aData
);
541 nBytesRead
= std::min( nMaxBytesToRead
, nInBuffer
+ nRead
);
543 // now take everything from buffer !
544 m_pBuffer
->readAt( m_nCurrentPos
, aData
, nBytesRead
);
546 m_nCurrentPos
+= nBytesRead
;
555 void OMarkableInputStream::skipBytes(sal_Int32 nBytesToSkip
)
557 if ( nBytesToSkip
< 0 )
558 throw BufferSizeExceededException(
559 "precondition not met: XInputStream::skipBytes: non-negative integer required!",
563 // this method is blocking
564 Sequence
<sal_Int8
> seqDummy( nBytesToSkip
);
565 readBytes( seqDummy
, nBytesToSkip
);
568 sal_Int32
OMarkableInputStream::available()
570 if( !m_bValidStream
) {
571 throw NotConnectedException(
572 "MarkableInputStream::available NotConnectedException",
576 MutexGuard
guard( m_mutex
);
577 sal_Int32 nAvail
= m_input
->available() + ( m_pBuffer
->getSize() - m_nCurrentPos
);
582 void OMarkableInputStream::closeInput()
584 if( !m_bValidStream
) {
585 throw NotConnectedException(
586 "MarkableInputStream::closeInput NotConnectedException",
589 MutexGuard
guard( m_mutex
);
591 m_input
->closeInput();
593 setInputStream( Reference
< XInputStream
> () );
594 setPredecessor( Reference
< XConnectable
> () );
595 setSuccessor( Reference
< XConnectable
>() );
604 sal_Int32
OMarkableInputStream::createMark()
606 MutexGuard
guard( m_mutex
);
607 sal_Int32 nMark
= m_nCurrentMark
;
609 m_mapMarks
[nMark
] = m_nCurrentPos
;
615 void OMarkableInputStream::deleteMark(sal_Int32 Mark
)
617 MutexGuard
guard( m_mutex
);
618 map
<sal_Int32
,sal_Int32
,less
<sal_Int32
> >::iterator ii
= m_mapMarks
.find( Mark
);
620 if( ii
== m_mapMarks
.end() ) {
621 throw IllegalArgumentException(
622 "MarkableInputStream::deleteMark unknown mark (" + OUString::number(Mark
) + ")",
625 m_mapMarks
.erase( ii
);
626 checkMarksAndFlush();
629 void OMarkableInputStream::jumpToMark(sal_Int32 nMark
)
631 MutexGuard
guard( m_mutex
);
632 map
<sal_Int32
,sal_Int32
,less
<sal_Int32
> >::iterator ii
= m_mapMarks
.find( nMark
);
634 if( ii
== m_mapMarks
.end() )
636 throw IllegalArgumentException(
637 "MarkableInputStream::jumpToMark unknown mark (" + OUString::number(nMark
) + ")",
640 m_nCurrentPos
= (*ii
).second
;
643 void OMarkableInputStream::jumpToFurthest()
645 MutexGuard
guard( m_mutex
);
646 m_nCurrentPos
= m_pBuffer
->getSize();
647 checkMarksAndFlush();
650 sal_Int32
OMarkableInputStream::offsetToMark(sal_Int32 nMark
)
652 MutexGuard
guard( m_mutex
);
653 map
<sal_Int32
,sal_Int32
,less
<sal_Int32
> >::const_iterator ii
= m_mapMarks
.find( nMark
);
655 if( ii
== m_mapMarks
.end() )
657 throw IllegalArgumentException(
658 "MarkableInputStream::offsetToMark unknown mark (" + OUString::number(nMark
) + ")",
661 return m_nCurrentPos
- (*ii
).second
;
666 void OMarkableInputStream::setInputStream(const Reference
< XInputStream
> & aStream
)
669 if( m_input
!= aStream
) {
672 Reference
< XConnectable
> pred( m_input
, UNO_QUERY
);
673 setPredecessor( pred
);
676 m_bValidStream
= m_input
.is();
680 Reference
< XInputStream
> OMarkableInputStream::getInputStream()
687 void OMarkableInputStream::setSuccessor( const Reference
< XConnectable
> &r
)
689 /// if the references match, nothing needs to be done
691 /// store the reference for later use
695 /// set this instance as the sink !
696 m_succ
->setPredecessor( Reference
< XConnectable
> (
697 static_cast< XConnectable
* >(this) ) );
702 Reference
< XConnectable
> OMarkableInputStream::getSuccessor()
709 void OMarkableInputStream::setPredecessor( const Reference
< XConnectable
> &r
)
714 m_pred
->setSuccessor( Reference
< XConnectable
> (
715 static_cast< XConnectable
* >(this) ) );
719 Reference
< XConnectable
> OMarkableInputStream::getPredecessor()
725 void OMarkableInputStream::checkMarksAndFlush()
727 map
<sal_Int32
,sal_Int32
,less
<sal_Int32
> >::iterator ii
;
729 // find the smallest mark
730 sal_Int32 nNextFound
= m_nCurrentPos
;
731 for (auto const& mark
: m_mapMarks
)
733 if( mark
.second
<= nNextFound
) {
734 nNextFound
= mark
.second
;
739 // some data must be released !
740 m_nCurrentPos
-= nNextFound
;
741 for (auto & mark
: m_mapMarks
)
743 mark
.second
-= nNextFound
;
746 m_pBuffer
->forgetFromStart( nNextFound
);
750 // nothing to do. There is a mark or the current cursor position, that prevents
756 OUString
OMarkableInputStream::getImplementationName()
758 return OMarkableInputStream_getImplementationName();
762 sal_Bool
OMarkableInputStream::supportsService(const OUString
& ServiceName
)
764 return cppu::supportsService(this, ServiceName
);
768 Sequence
< OUString
> OMarkableInputStream::getSupportedServiceNames()
770 return OMarkableInputStream_getSupportedServiceNames();
773 /*------------------------
777 *------------------------*/
778 Reference
< XInterface
> OMarkableInputStream_CreateInstance(
779 SAL_UNUSED_PARAMETER
const Reference
< XComponentContext
> & )
781 OMarkableInputStream
*p
= new OMarkableInputStream( );
782 return Reference
< XInterface
> ( static_cast<OWeakObject
*>(p
) );
785 OUString
OMarkableInputStream_getImplementationName()
787 return "com.sun.star.comp.io.stm.MarkableInputStream";
790 Sequence
<OUString
> OMarkableInputStream_getSupportedServiceNames()
792 Sequence
<OUString
> aRet
{ "com.sun.star.io.MarkableInputStream" };
798 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */