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 .
25 #include <com/sun/star/io/BufferSizeExceededException.hpp>
26 #include <com/sun/star/io/NotConnectedException.hpp>
27 #include <com/sun/star/io/XMarkableStream.hpp>
28 #include <com/sun/star/io/XOutputStream.hpp>
29 #include <com/sun/star/io/XInputStream.hpp>
30 #include <com/sun/star/io/XActiveDataSource.hpp>
31 #include <com/sun/star/io/XActiveDataSink.hpp>
32 #include <com/sun/star/io/XConnectable.hpp>
33 #include <com/sun/star/lang/IllegalArgumentException.hpp>
34 #include <com/sun/star/lang/XServiceInfo.hpp>
35 #include <com/sun/star/uno/XComponentContext.hpp>
37 #include <cppuhelper/weak.hxx>
38 #include <cppuhelper/implbase.hxx>
39 #include <cppuhelper/supportsservice.hxx>
41 #include <osl/diagnose.h>
44 using namespace ::cppu
;
45 using namespace ::com::sun::star::io
;
46 using namespace ::com::sun::star::uno
;
47 using namespace ::com::sun::star::lang
;
49 #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 MemRingBuffer m_aRingBuffer
;
118 std::map
<sal_Int32
,sal_Int32
,std::less
< sal_Int32
> > m_mapMarks
;
119 sal_Int32 m_nCurrentPos
;
120 sal_Int32 m_nCurrentMark
;
127 OMarkableOutputStream::OMarkableOutputStream( )
128 : m_bValidStream(false)
135 void OMarkableOutputStream::writeBytes(const Sequence
< sal_Int8
>& aData
)
137 std::unique_lock
guard( m_mutex
);
139 if( !m_bValidStream
) {
140 throw NotConnectedException();
142 if( m_mapMarks
.empty() && ( m_aRingBuffer
.getSize() == 0 ) ) {
143 // no mark and buffer active, simple write through
144 m_output
->writeBytes( aData
);
147 // new data must be buffered
148 m_aRingBuffer
.writeAt( m_nCurrentPos
, aData
);
149 m_nCurrentPos
+= aData
.getLength();
150 checkMarksAndFlush();
155 void OMarkableOutputStream::flush()
157 Reference
< XOutputStream
> output
;
159 std::unique_lock
guard( m_mutex
);
163 // Markable cannot flush buffered data, because the data may get rewritten,
164 // however one can forward the flush to the chained stream to give it
165 // a chance to write data buffered in the chained stream.
172 void OMarkableOutputStream::closeOutput()
174 if( !m_bValidStream
) {
175 throw NotConnectedException();
177 std::unique_lock
guard( m_mutex
);
178 // all marks must be cleared and all
181 m_nCurrentPos
= m_aRingBuffer
.getSize();
182 checkMarksAndFlush();
184 m_output
->closeOutput();
186 setOutputStream( Reference
< XOutputStream
> () );
187 setPredecessor( Reference
< XConnectable
>() );
188 setSuccessor( Reference
< XConnectable
> () );
193 sal_Int32
OMarkableOutputStream::createMark()
195 std::unique_lock
guard( m_mutex
);
196 sal_Int32 nMark
= m_nCurrentMark
;
198 m_mapMarks
[nMark
] = m_nCurrentPos
;
204 void OMarkableOutputStream::deleteMark(sal_Int32 Mark
)
206 std::unique_lock
guard( m_mutex
);
207 std::map
<sal_Int32
,sal_Int32
,std::less
<sal_Int32
> >::iterator ii
= m_mapMarks
.find( Mark
);
209 if( ii
== m_mapMarks
.end() ) {
210 throw IllegalArgumentException(
211 "MarkableOutputStream::deleteMark unknown mark (" + OUString::number(Mark
) + ")",
214 m_mapMarks
.erase( ii
);
215 checkMarksAndFlush();
218 void OMarkableOutputStream::jumpToMark(sal_Int32 nMark
)
220 std::unique_lock
guard( m_mutex
);
221 std::map
<sal_Int32
,sal_Int32
,std::less
<sal_Int32
> >::iterator ii
= m_mapMarks
.find( nMark
);
223 if( ii
== m_mapMarks
.end() ) {
224 throw IllegalArgumentException(
225 "MarkableOutputStream::jumpToMark unknown mark (" + OUString::number(nMark
) + ")",
228 m_nCurrentPos
= (*ii
).second
;
231 void OMarkableOutputStream::jumpToFurthest()
233 std::unique_lock
guard( m_mutex
);
234 m_nCurrentPos
= m_aRingBuffer
.getSize();
235 checkMarksAndFlush();
238 sal_Int32
OMarkableOutputStream::offsetToMark(sal_Int32 nMark
)
241 std::unique_lock
guard( m_mutex
);
242 std::map
<sal_Int32
,sal_Int32
,std::less
<sal_Int32
> >::const_iterator ii
= m_mapMarks
.find( nMark
);
244 if( ii
== m_mapMarks
.end() )
246 throw IllegalArgumentException(
247 "MarkableOutputStream::offsetToMark unknown mark (" + OUString::number(nMark
) + ")",
250 return m_nCurrentPos
- (*ii
).second
;
254 // XActiveDataSource2
255 void OMarkableOutputStream::setOutputStream(const Reference
< XOutputStream
>& aStream
)
257 if( m_output
!= aStream
) {
260 Reference
< XConnectable
> succ( m_output
, UNO_QUERY
);
261 setSuccessor( succ
);
263 m_bValidStream
= m_output
.is();
266 Reference
< XOutputStream
> OMarkableOutputStream::getOutputStream()
272 void OMarkableOutputStream::setSuccessor( const Reference
< XConnectable
> &r
)
274 /// if the references match, nothing needs to be done
276 /// store the reference for later use
280 m_succ
->setPredecessor( Reference
< XConnectable
> (
281 static_cast< XConnectable
* >(this) ) );
285 Reference
<XConnectable
> OMarkableOutputStream::getSuccessor()
292 void OMarkableOutputStream::setPredecessor( const Reference
< XConnectable
> &r
)
297 m_pred
->setSuccessor( Reference
< XConnectable
> (
298 static_cast< XConnectable
* >(this ) ) );
302 Reference
< XConnectable
> OMarkableOutputStream::getPredecessor()
310 void OMarkableOutputStream::checkMarksAndFlush()
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_aRingBuffer
.readAt( 0 , seq
, nNextFound
);
331 m_aRingBuffer
.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 u
"com.sun.star.comp.io.stm.MarkableOutputStream"_ustr
;
350 sal_Bool
OMarkableOutputStream::supportsService(const OUString
& ServiceName
)
352 return cppu::supportsService(this, ServiceName
);
356 Sequence
< OUString
> OMarkableOutputStream::getSupportedServiceNames()
358 return { u
"com.sun.star.io.MarkableOutputStream"_ustr
};
361 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
362 io_OMarkableOutputStream_get_implementation(
363 css::uno::XComponentContext
* , css::uno::Sequence
<css::uno::Any
> const&)
365 return cppu::acquire(new OMarkableOutputStream());
369 // XMarkableInputStream
373 class OMarkableInputStream
:
374 public WeakImplHelper
384 OMarkableInputStream( );
387 public: // XInputStream
388 virtual sal_Int32 SAL_CALL
readBytes(Sequence
< sal_Int8
>& aData
, sal_Int32 nBytesToRead
) override
;
389 virtual sal_Int32 SAL_CALL
readSomeBytes(Sequence
< sal_Int8
>& aData
, sal_Int32 nMaxBytesToRead
) override
;
390 virtual void SAL_CALL
skipBytes(sal_Int32 nBytesToSkip
) override
;
392 virtual sal_Int32 SAL_CALL
available() override
;
393 virtual void SAL_CALL
closeInput() override
;
396 virtual sal_Int32 SAL_CALL
createMark() override
;
397 virtual void SAL_CALL
deleteMark(sal_Int32 Mark
) override
;
398 virtual void SAL_CALL
jumpToMark(sal_Int32 nMark
) override
;
399 virtual void SAL_CALL
jumpToFurthest() override
;
400 virtual sal_Int32 SAL_CALL
offsetToMark(sal_Int32 nMark
) override
;
402 public: // XActiveDataSink
403 virtual void SAL_CALL
setInputStream(const Reference
< XInputStream
> & aStream
) override
;
404 virtual Reference
< XInputStream
> SAL_CALL
getInputStream() override
;
406 public: // XConnectable
407 virtual void SAL_CALL
setPredecessor(const Reference
< XConnectable
> & aPredecessor
) override
;
408 virtual Reference
< XConnectable
> SAL_CALL
getPredecessor() override
;
409 virtual void SAL_CALL
setSuccessor(const Reference
< XConnectable
> & aSuccessor
) override
;
410 virtual Reference
< XConnectable
> SAL_CALL
getSuccessor() override
;
412 public: // XServiceInfo
413 OUString SAL_CALL
getImplementationName() override
;
414 Sequence
< OUString
> SAL_CALL
getSupportedServiceNames() override
;
415 sal_Bool SAL_CALL
supportsService(const OUString
& ServiceName
) override
;
418 void checkMarksAndFlush();
420 Reference
< XConnectable
> m_succ
;
421 Reference
< XConnectable
> m_pred
;
423 Reference
< XInputStream
> m_input
;
426 std::optional
<MemRingBuffer
> m_oBuffer
;
427 std::map
<sal_Int32
,sal_Int32
,std::less
< sal_Int32
> > m_mapMarks
;
428 sal_Int32 m_nCurrentPos
;
429 sal_Int32 m_nCurrentMark
;
436 OMarkableInputStream::OMarkableInputStream()
437 : m_bValidStream(false)
447 sal_Int32
OMarkableInputStream::readBytes(Sequence
< sal_Int8
>& aData
, sal_Int32 nBytesToRead
)
449 std::unique_lock
guard( m_mutex
);
451 if( !m_bValidStream
) {
452 throw NotConnectedException(
453 u
"MarkableInputStream::readBytes NotConnectedException"_ustr
,
457 sal_Int32 nBytesRead
;
458 if( m_mapMarks
.empty() && ! m_oBuffer
->getSize() ) {
460 nBytesRead
= m_input
->readBytes( aData
, nBytesToRead
);
466 // read enough bytes into buffer
467 if( m_oBuffer
->getSize() - m_nCurrentPos
< nBytesToRead
) {
468 sal_Int32 nToRead
= nBytesToRead
- ( m_oBuffer
->getSize() - m_nCurrentPos
);
469 nRead
= m_input
->readBytes( aData
, nToRead
);
471 OSL_ASSERT( aData
.getLength() == nRead
);
473 m_oBuffer
->writeAt( m_oBuffer
->getSize() , aData
);
475 if( nRead
< nToRead
) {
476 nBytesToRead
= nBytesToRead
- (nToRead
-nRead
);
480 OSL_ASSERT( m_oBuffer
->getSize() - m_nCurrentPos
>= nBytesToRead
);
482 m_oBuffer
->readAt( m_nCurrentPos
, aData
, nBytesToRead
);
484 m_nCurrentPos
+= nBytesToRead
;
485 nBytesRead
= nBytesToRead
;
492 sal_Int32
OMarkableInputStream::readSomeBytes(Sequence
< sal_Int8
>& aData
, sal_Int32 nMaxBytesToRead
)
494 std::unique_lock
guard( m_mutex
);
496 if( !m_bValidStream
) {
497 throw NotConnectedException(
498 u
"MarkableInputStream::readSomeBytes NotConnectedException"_ustr
,
502 sal_Int32 nBytesRead
;
503 if( m_mapMarks
.empty() && ! m_oBuffer
->getSize() ) {
505 nBytesRead
= m_input
->readSomeBytes( aData
, nMaxBytesToRead
);
510 sal_Int32 nInBuffer
= m_oBuffer
->getSize() - m_nCurrentPos
;
511 sal_Int32 nAdditionalBytesToRead
= std::min
<sal_Int32
>(nMaxBytesToRead
-nInBuffer
,m_input
->available());
512 nAdditionalBytesToRead
= std::max
<sal_Int32
>(0 , nAdditionalBytesToRead
);
514 // read enough bytes into buffer
515 if( 0 == nInBuffer
) {
516 nRead
= m_input
->readSomeBytes( aData
, nMaxBytesToRead
);
518 else if( nAdditionalBytesToRead
) {
519 nRead
= m_input
->readBytes( aData
, nAdditionalBytesToRead
);
523 aData
.realloc( nRead
);
524 m_oBuffer
->writeAt( m_oBuffer
->getSize() , aData
);
527 nBytesRead
= std::min( nMaxBytesToRead
, nInBuffer
+ nRead
);
529 // now take everything from buffer !
530 m_oBuffer
->readAt( m_nCurrentPos
, aData
, nBytesRead
);
532 m_nCurrentPos
+= nBytesRead
;
541 void OMarkableInputStream::skipBytes(sal_Int32 nBytesToSkip
)
543 if ( nBytesToSkip
< 0 )
544 throw BufferSizeExceededException(
545 u
"precondition not met: XInputStream::skipBytes: non-negative integer required!"_ustr
,
549 // this method is blocking
550 Sequence
<sal_Int8
> seqDummy( nBytesToSkip
);
551 readBytes( seqDummy
, nBytesToSkip
);
554 sal_Int32
OMarkableInputStream::available()
556 std::unique_lock
guard( m_mutex
);
558 if( !m_bValidStream
) {
559 throw NotConnectedException(
560 u
"MarkableInputStream::available NotConnectedException"_ustr
,
564 sal_Int32 nAvail
= m_input
->available() + ( m_oBuffer
->getSize() - m_nCurrentPos
);
569 void OMarkableInputStream::closeInput()
571 std::unique_lock
guard( m_mutex
);
573 if( !m_bValidStream
) {
574 throw NotConnectedException(
575 u
"MarkableInputStream::closeInput NotConnectedException"_ustr
,
579 m_input
->closeInput();
586 m_bValidStream
= false;
594 sal_Int32
OMarkableInputStream::createMark()
596 std::unique_lock
guard( m_mutex
);
597 sal_Int32 nMark
= m_nCurrentMark
;
599 m_mapMarks
[nMark
] = m_nCurrentPos
;
605 void OMarkableInputStream::deleteMark(sal_Int32 Mark
)
607 std::unique_lock
guard( m_mutex
);
608 std::map
<sal_Int32
,sal_Int32
,std::less
<sal_Int32
> >::iterator ii
= m_mapMarks
.find( Mark
);
610 if( ii
== m_mapMarks
.end() ) {
611 throw IllegalArgumentException(
612 "MarkableInputStream::deleteMark unknown mark (" + OUString::number(Mark
) + ")",
615 m_mapMarks
.erase( ii
);
616 checkMarksAndFlush();
619 void OMarkableInputStream::jumpToMark(sal_Int32 nMark
)
621 std::unique_lock
guard( m_mutex
);
622 std::map
<sal_Int32
,sal_Int32
,std::less
<sal_Int32
> >::iterator ii
= m_mapMarks
.find( nMark
);
624 if( ii
== m_mapMarks
.end() )
626 throw IllegalArgumentException(
627 "MarkableInputStream::jumpToMark unknown mark (" + OUString::number(nMark
) + ")",
630 m_nCurrentPos
= (*ii
).second
;
633 void OMarkableInputStream::jumpToFurthest()
635 std::unique_lock
guard( m_mutex
);
636 m_nCurrentPos
= m_oBuffer
->getSize();
637 checkMarksAndFlush();
640 sal_Int32
OMarkableInputStream::offsetToMark(sal_Int32 nMark
)
642 std::unique_lock
guard( m_mutex
);
643 std::map
<sal_Int32
,sal_Int32
,std::less
<sal_Int32
> >::const_iterator ii
= m_mapMarks
.find( nMark
);
645 if( ii
== m_mapMarks
.end() )
647 throw IllegalArgumentException(
648 "MarkableInputStream::offsetToMark unknown mark (" + OUString::number(nMark
) + ")",
651 return m_nCurrentPos
- (*ii
).second
;
656 void OMarkableInputStream::setInputStream(const Reference
< XInputStream
> & aStream
)
658 Reference
< XConnectable
> pred
;
660 std::unique_lock
guard( m_mutex
);
661 if( m_input
== aStream
)
665 m_bValidStream
= m_input
.is();
666 pred
.set( m_input
, UNO_QUERY
);
668 setPredecessor( pred
);
671 Reference
< XInputStream
> OMarkableInputStream::getInputStream()
673 std::unique_lock
guard( m_mutex
);
679 void OMarkableInputStream::setSuccessor( const Reference
< XConnectable
> &r
)
682 std::unique_lock
guard( m_mutex
);
683 /// if the references match, nothing needs to be done
687 /// store the reference for later use
691 /// set this instance as the sink !
692 r
->setPredecessor( Reference
< XConnectable
> ( static_cast< XConnectable
* >(this) ) );
696 Reference
< XConnectable
> OMarkableInputStream::getSuccessor()
698 std::unique_lock
guard( m_mutex
);
704 void OMarkableInputStream::setPredecessor( const Reference
< XConnectable
> &r
)
707 std::unique_lock
guard( m_mutex
);
713 r
->setSuccessor( Reference
< XConnectable
> (
714 static_cast< XConnectable
* >(this) ) );
718 Reference
< XConnectable
> OMarkableInputStream::getPredecessor()
720 std::unique_lock
guard( m_mutex
);
725 void OMarkableInputStream::checkMarksAndFlush()
727 // find the smallest mark
728 sal_Int32 nNextFound
= m_nCurrentPos
;
729 for (auto const& mark
: m_mapMarks
)
731 if( mark
.second
<= nNextFound
) {
732 nNextFound
= mark
.second
;
737 // some data must be released !
738 m_nCurrentPos
-= nNextFound
;
739 for (auto & mark
: m_mapMarks
)
741 mark
.second
-= nNextFound
;
744 m_oBuffer
->forgetFromStart( nNextFound
);
748 // nothing to do. There is a mark or the current cursor position, that prevents
754 OUString
OMarkableInputStream::getImplementationName()
756 return u
"com.sun.star.comp.io.stm.MarkableInputStream"_ustr
;
760 sal_Bool
OMarkableInputStream::supportsService(const OUString
& ServiceName
)
762 return cppu::supportsService(this, ServiceName
);
766 Sequence
< OUString
> OMarkableInputStream::getSupportedServiceNames()
768 return { u
"com.sun.star.io.MarkableInputStream"_ustr
};
771 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
772 io_OMarkableInputStream_get_implementation(
773 css::uno::XComponentContext
* , css::uno::Sequence
<css::uno::Any
> const&)
775 return cppu::acquire(new OMarkableInputStream());
780 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */