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>
34 #include <com/sun/star/uno/XComponentContext.hpp>
36 #include <cppuhelper/weak.hxx>
37 #include <cppuhelper/implbase.hxx>
38 #include <cppuhelper/supportsservice.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 "streamhelper.hxx"
56 /***********************
58 * OMarkableOutputStream.
60 * This object allows to set marks in an outputstream. It is allowed to jump back to the marks and
61 * rewrite the same bytes.
63 * The object must buffer the data since the last mark set. Flush will not
64 * have any effect. As soon as the last mark has been removed, the object may write the data
65 * through to the chained object.
67 **********************/
68 class OMarkableOutputStream
:
69 public WeakImplHelper
< XOutputStream
,
77 OMarkableOutputStream( );
79 public: // XOutputStream
80 virtual void SAL_CALL
writeBytes(const Sequence
< sal_Int8
>& aData
) override
;
81 virtual void SAL_CALL
flush() override
;
82 virtual void SAL_CALL
closeOutput() override
;
85 virtual sal_Int32 SAL_CALL
createMark() override
;
86 virtual void SAL_CALL
deleteMark(sal_Int32 Mark
) override
;
87 virtual void SAL_CALL
jumpToMark(sal_Int32 nMark
) override
;
88 virtual void SAL_CALL
jumpToFurthest() override
;
89 virtual sal_Int32 SAL_CALL
offsetToMark(sal_Int32 nMark
) override
;
91 public: // XActiveDataSource
92 virtual void SAL_CALL
setOutputStream(const Reference
< XOutputStream
> & aStream
) override
;
93 virtual Reference
< XOutputStream
> SAL_CALL
getOutputStream() override
;
95 public: // XConnectable
96 virtual void SAL_CALL
setPredecessor(const Reference
< XConnectable
> & aPredecessor
) override
;
97 virtual Reference
< XConnectable
> SAL_CALL
getPredecessor() override
;
98 virtual void SAL_CALL
setSuccessor(const Reference
< XConnectable
>& aSuccessor
) override
;
99 virtual Reference
< XConnectable
> SAL_CALL
getSuccessor() override
;
101 public: // XServiceInfo
102 OUString SAL_CALL
getImplementationName() override
;
103 Sequence
< OUString
> SAL_CALL
getSupportedServiceNames() override
;
104 sal_Bool SAL_CALL
supportsService(const OUString
& ServiceName
) override
;
108 /// @throws NotConnectedException
109 /// @throws BufferSizeExceededException
110 void checkMarksAndFlush();
112 Reference
< XConnectable
> m_succ
;
113 Reference
< XConnectable
> m_pred
;
115 Reference
< XOutputStream
> m_output
;
118 MemRingBuffer m_aRingBuffer
;
119 map
<sal_Int32
,sal_Int32
,less
< sal_Int32
> > m_mapMarks
;
120 sal_Int32 m_nCurrentPos
;
121 sal_Int32 m_nCurrentMark
;
128 OMarkableOutputStream::OMarkableOutputStream( )
129 : m_bValidStream(false)
136 void OMarkableOutputStream::writeBytes(const Sequence
< sal_Int8
>& aData
)
138 if( !m_bValidStream
) {
139 throw NotConnectedException();
141 if( m_mapMarks
.empty() && ( m_aRingBuffer
.getSize() == 0 ) ) {
142 // no mark and buffer active, simple write through
143 m_output
->writeBytes( aData
);
146 std::unique_lock
guard( m_mutex
);
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 map
<sal_Int32
,sal_Int32
,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 map
<sal_Int32
,sal_Int32
,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 map
<sal_Int32
,sal_Int32
,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 "com.sun.star.comp.io.stm.MarkableOutputStream";
350 sal_Bool
OMarkableOutputStream::supportsService(const OUString
& ServiceName
)
352 return cppu::supportsService(this, ServiceName
);
356 Sequence
< OUString
> OMarkableOutputStream::getSupportedServiceNames()
358 return { "com.sun.star.io.MarkableOutputStream" };
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::unique_ptr
<MemRingBuffer
> m_pBuffer
;
427 map
<sal_Int32
,sal_Int32
,less
< sal_Int32
> > m_mapMarks
;
428 sal_Int32 m_nCurrentPos
;
429 sal_Int32 m_nCurrentMark
;
436 OMarkableInputStream::OMarkableInputStream()
437 : m_bValidStream(false)
441 m_pBuffer
.reset( new MemRingBuffer
);
447 sal_Int32
OMarkableInputStream::readBytes(Sequence
< sal_Int8
>& aData
, sal_Int32 nBytesToRead
)
449 sal_Int32 nBytesRead
;
451 if( !m_bValidStream
) {
452 throw NotConnectedException(
453 "MarkableInputStream::readBytes NotConnectedException",
456 std::unique_lock
guard( m_mutex
);
457 if( m_mapMarks
.empty() && ! m_pBuffer
->getSize() ) {
459 nBytesRead
= m_input
->readBytes( aData
, nBytesToRead
);
465 // read enough bytes into buffer
466 if( m_pBuffer
->getSize() - m_nCurrentPos
< nBytesToRead
) {
467 sal_Int32 nToRead
= nBytesToRead
- ( m_pBuffer
->getSize() - m_nCurrentPos
);
468 nRead
= m_input
->readBytes( aData
, nToRead
);
470 OSL_ASSERT( aData
.getLength() == nRead
);
472 m_pBuffer
->writeAt( m_pBuffer
->getSize() , aData
);
474 if( nRead
< nToRead
) {
475 nBytesToRead
= nBytesToRead
- (nToRead
-nRead
);
479 OSL_ASSERT( m_pBuffer
->getSize() - m_nCurrentPos
>= nBytesToRead
);
481 m_pBuffer
->readAt( m_nCurrentPos
, aData
, nBytesToRead
);
483 m_nCurrentPos
+= nBytesToRead
;
484 nBytesRead
= nBytesToRead
;
491 sal_Int32
OMarkableInputStream::readSomeBytes(Sequence
< sal_Int8
>& aData
, sal_Int32 nMaxBytesToRead
)
494 sal_Int32 nBytesRead
;
495 if( !m_bValidStream
) {
496 throw NotConnectedException(
497 "MarkableInputStream::readSomeBytes NotConnectedException",
501 std::unique_lock
guard( m_mutex
);
502 if( m_mapMarks
.empty() && ! m_pBuffer
->getSize() ) {
504 nBytesRead
= m_input
->readSomeBytes( aData
, nMaxBytesToRead
);
509 sal_Int32 nInBuffer
= m_pBuffer
->getSize() - m_nCurrentPos
;
510 sal_Int32 nAdditionalBytesToRead
= std::min
<sal_Int32
>(nMaxBytesToRead
-nInBuffer
,m_input
->available());
511 nAdditionalBytesToRead
= std::max
<sal_Int32
>(0 , nAdditionalBytesToRead
);
513 // read enough bytes into buffer
514 if( 0 == nInBuffer
) {
515 nRead
= m_input
->readSomeBytes( aData
, nMaxBytesToRead
);
517 else if( nAdditionalBytesToRead
) {
518 nRead
= m_input
->readBytes( aData
, nAdditionalBytesToRead
);
522 aData
.realloc( nRead
);
523 m_pBuffer
->writeAt( m_pBuffer
->getSize() , aData
);
526 nBytesRead
= std::min( nMaxBytesToRead
, nInBuffer
+ nRead
);
528 // now take everything from buffer !
529 m_pBuffer
->readAt( m_nCurrentPos
, aData
, nBytesRead
);
531 m_nCurrentPos
+= nBytesRead
;
540 void OMarkableInputStream::skipBytes(sal_Int32 nBytesToSkip
)
542 if ( nBytesToSkip
< 0 )
543 throw BufferSizeExceededException(
544 "precondition not met: XInputStream::skipBytes: non-negative integer required!",
548 // this method is blocking
549 Sequence
<sal_Int8
> seqDummy( nBytesToSkip
);
550 readBytes( seqDummy
, nBytesToSkip
);
553 sal_Int32
OMarkableInputStream::available()
555 if( !m_bValidStream
) {
556 throw NotConnectedException(
557 "MarkableInputStream::available NotConnectedException",
561 std::unique_lock
guard( m_mutex
);
562 sal_Int32 nAvail
= m_input
->available() + ( m_pBuffer
->getSize() - m_nCurrentPos
);
567 void OMarkableInputStream::closeInput()
569 if( !m_bValidStream
) {
570 throw NotConnectedException(
571 "MarkableInputStream::closeInput NotConnectedException",
574 std::unique_lock
guard( m_mutex
);
576 m_input
->closeInput();
578 setInputStream( Reference
< XInputStream
> () );
579 setPredecessor( Reference
< XConnectable
> () );
580 setSuccessor( Reference
< XConnectable
>() );
589 sal_Int32
OMarkableInputStream::createMark()
591 std::unique_lock
guard( m_mutex
);
592 sal_Int32 nMark
= m_nCurrentMark
;
594 m_mapMarks
[nMark
] = m_nCurrentPos
;
600 void OMarkableInputStream::deleteMark(sal_Int32 Mark
)
602 std::unique_lock
guard( m_mutex
);
603 map
<sal_Int32
,sal_Int32
,less
<sal_Int32
> >::iterator ii
= m_mapMarks
.find( Mark
);
605 if( ii
== m_mapMarks
.end() ) {
606 throw IllegalArgumentException(
607 "MarkableInputStream::deleteMark unknown mark (" + OUString::number(Mark
) + ")",
610 m_mapMarks
.erase( ii
);
611 checkMarksAndFlush();
614 void OMarkableInputStream::jumpToMark(sal_Int32 nMark
)
616 std::unique_lock
guard( m_mutex
);
617 map
<sal_Int32
,sal_Int32
,less
<sal_Int32
> >::iterator ii
= m_mapMarks
.find( nMark
);
619 if( ii
== m_mapMarks
.end() )
621 throw IllegalArgumentException(
622 "MarkableInputStream::jumpToMark unknown mark (" + OUString::number(nMark
) + ")",
625 m_nCurrentPos
= (*ii
).second
;
628 void OMarkableInputStream::jumpToFurthest()
630 std::unique_lock
guard( m_mutex
);
631 m_nCurrentPos
= m_pBuffer
->getSize();
632 checkMarksAndFlush();
635 sal_Int32
OMarkableInputStream::offsetToMark(sal_Int32 nMark
)
637 std::unique_lock
guard( m_mutex
);
638 map
<sal_Int32
,sal_Int32
,less
<sal_Int32
> >::const_iterator ii
= m_mapMarks
.find( nMark
);
640 if( ii
== m_mapMarks
.end() )
642 throw IllegalArgumentException(
643 "MarkableInputStream::offsetToMark unknown mark (" + OUString::number(nMark
) + ")",
646 return m_nCurrentPos
- (*ii
).second
;
651 void OMarkableInputStream::setInputStream(const Reference
< XInputStream
> & aStream
)
654 if( m_input
!= aStream
) {
657 Reference
< XConnectable
> pred( m_input
, UNO_QUERY
);
658 setPredecessor( pred
);
661 m_bValidStream
= m_input
.is();
665 Reference
< XInputStream
> OMarkableInputStream::getInputStream()
672 void OMarkableInputStream::setSuccessor( const Reference
< XConnectable
> &r
)
674 /// if the references match, nothing needs to be done
676 /// store the reference for later use
680 /// set this instance as the sink !
681 m_succ
->setPredecessor( Reference
< XConnectable
> (
682 static_cast< XConnectable
* >(this) ) );
687 Reference
< XConnectable
> OMarkableInputStream::getSuccessor()
694 void OMarkableInputStream::setPredecessor( const Reference
< XConnectable
> &r
)
699 m_pred
->setSuccessor( Reference
< XConnectable
> (
700 static_cast< XConnectable
* >(this) ) );
704 Reference
< XConnectable
> OMarkableInputStream::getPredecessor()
710 void OMarkableInputStream::checkMarksAndFlush()
712 // find the smallest mark
713 sal_Int32 nNextFound
= m_nCurrentPos
;
714 for (auto const& mark
: m_mapMarks
)
716 if( mark
.second
<= nNextFound
) {
717 nNextFound
= mark
.second
;
722 // some data must be released !
723 m_nCurrentPos
-= nNextFound
;
724 for (auto & mark
: m_mapMarks
)
726 mark
.second
-= nNextFound
;
729 m_pBuffer
->forgetFromStart( nNextFound
);
733 // nothing to do. There is a mark or the current cursor position, that prevents
739 OUString
OMarkableInputStream::getImplementationName()
741 return "com.sun.star.comp.io.stm.MarkableInputStream";
745 sal_Bool
OMarkableInputStream::supportsService(const OUString
& ServiceName
)
747 return cppu::supportsService(this, ServiceName
);
751 Sequence
< OUString
> OMarkableInputStream::getSupportedServiceNames()
753 return { "com.sun.star.io.MarkableInputStream" };
756 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
757 io_OMarkableInputStream_get_implementation(
758 css::uno::XComponentContext
* , css::uno::Sequence
<css::uno::Any
> const&)
760 return cppu::acquire(new OMarkableInputStream());
765 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */