Coverage Report

Created: 2025-11-16 09:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/package/source/xstor/switchpersistencestream.cxx
Line
Count
Source
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 <osl/diagnose.h>
21
#include <com/sun/star/io/IOException.hpp>
22
#include <com/sun/star/io/NotConnectedException.hpp>
23
#include <com/sun/star/io/TempFile.hpp>
24
#include <comphelper/storagehelper.hxx>
25
#include <unotools/tempfile.hxx>
26
#include <utility>
27
#include "switchpersistencestream.hxx"
28
29
using namespace ::com::sun::star;
30
31
struct SPStreamData_Impl
32
{
33
    bool m_bInStreamBased;
34
35
    // the streams below are not visible from outside so there is no need to remember position
36
37
    // original stream related members
38
    uno::Reference< io::XTruncate > m_xOrigTruncate;
39
    uno::Reference< io::XSeekable > m_xOrigSeekable;
40
    uno::Reference< io::XInputStream > m_xOrigInStream;
41
    uno::Reference< io::XOutputStream > m_xOrigOutStream;
42
    comphelper::ByteReader* m_pByteReader;
43
44
    bool m_bInOpen;
45
    bool m_bOutOpen;
46
47
    SPStreamData_Impl(
48
            bool bInStreamBased,
49
            uno::Reference< io::XTruncate > xOrigTruncate,
50
            uno::Reference< io::XSeekable > xOrigSeekable,
51
            uno::Reference< io::XInputStream > xOrigInStream,
52
            uno::Reference< io::XOutputStream > xOrigOutStream,
53
            bool bInOpen,
54
            bool bOutOpen )
55
185k
    : m_bInStreamBased( bInStreamBased )
56
185k
    , m_xOrigTruncate(std::move( xOrigTruncate ))
57
185k
    , m_xOrigSeekable(std::move( xOrigSeekable ))
58
185k
    , m_xOrigInStream(std::move( xOrigInStream ))
59
185k
    , m_xOrigOutStream(std::move( xOrigOutStream ))
60
185k
    , m_pByteReader(dynamic_cast<comphelper::ByteReader*>(m_xOrigInStream.get()))
61
185k
    , m_bInOpen( bInOpen )
62
185k
    , m_bOutOpen( bOutOpen )
63
185k
    {
64
185k
    }
65
};
66
67
SwitchablePersistenceStream::SwitchablePersistenceStream(
68
        const uno::Reference< io::XStream >& xStream )
69
137k
{
70
137k
    SwitchPersistenceTo( xStream );
71
137k
}
72
73
SwitchablePersistenceStream::SwitchablePersistenceStream(
74
        const uno::Reference< io::XInputStream >& xInputStream )
75
47.4k
{
76
47.4k
    SwitchPersistenceTo( xInputStream );
77
47.4k
}
78
79
SwitchablePersistenceStream::~SwitchablePersistenceStream()
80
185k
{
81
185k
    CloseAll_Impl();
82
185k
}
83
84
void SwitchablePersistenceStream::SwitchPersistenceTo( const uno::Reference< io::XStream >& xStream )
85
137k
{
86
137k
    uno::Reference< io::XTruncate > xNewTruncate( xStream, uno::UNO_QUERY_THROW );
87
137k
    uno::Reference< io::XSeekable > xNewSeekable( xStream, uno::UNO_QUERY_THROW );
88
137k
    uno::Reference< io::XInputStream > xNewInStream = xStream->getInputStream();
89
137k
    uno::Reference< io::XOutputStream > xNewOutStream = xStream->getOutputStream();
90
137k
    if ( !xNewInStream.is() || !xNewOutStream.is() )
91
0
        throw uno::RuntimeException();
92
93
137k
    sal_Int64 nPos = 0;
94
137k
    bool bInOpen = false;
95
137k
    bool bOutOpen = false;
96
97
137k
    if ( m_pStreamData && m_pStreamData->m_xOrigSeekable.is() )
98
0
    {
99
        // check that the length is the same
100
0
        if ( m_pStreamData->m_xOrigSeekable->getLength() != xNewSeekable->getLength() )
101
0
            throw uno::RuntimeException();
102
103
        // get the current position
104
0
        nPos = m_pStreamData->m_xOrigSeekable->getPosition();
105
0
        bInOpen = m_pStreamData->m_bInOpen;
106
0
        bOutOpen = m_pStreamData->m_bOutOpen;
107
0
    }
108
109
137k
    xNewSeekable->seek( nPos );
110
111
137k
    CloseAll_Impl();
112
113
137k
    m_pStreamData.reset( new SPStreamData_Impl( false,
114
137k
                                            xNewTruncate, xNewSeekable, xNewInStream, xNewOutStream,
115
137k
                                            bInOpen, bOutOpen ) );
116
137k
}
117
118
void SwitchablePersistenceStream::SwitchPersistenceTo( const uno::Reference< io::XInputStream >& xInputStream )
119
47.4k
{
120
47.4k
    uno::Reference< io::XTruncate > xNewTruncate;
121
47.4k
    uno::Reference< io::XSeekable > xNewSeekable( xInputStream, uno::UNO_QUERY_THROW );
122
47.4k
    uno::Reference< io::XOutputStream > xNewOutStream;
123
47.4k
    if ( !xInputStream.is() )
124
0
        throw uno::RuntimeException();
125
126
47.4k
    sal_Int64 nPos = 0;
127
47.4k
    bool bInOpen = false;
128
47.4k
    bool bOutOpen = false;
129
130
47.4k
    if ( m_pStreamData && m_pStreamData->m_xOrigSeekable.is() )
131
0
    {
132
        // check that the length is the same
133
0
        if ( m_pStreamData->m_xOrigSeekable->getLength() != xNewSeekable->getLength() )
134
0
            throw uno::RuntimeException();
135
136
        // get the current position
137
0
        nPos = m_pStreamData->m_xOrigSeekable->getPosition();
138
0
        bInOpen = m_pStreamData->m_bInOpen;
139
0
        bOutOpen = m_pStreamData->m_bOutOpen;
140
0
    }
141
142
47.4k
    xNewSeekable->seek( nPos );
143
144
47.4k
    CloseAll_Impl();
145
146
47.4k
    m_pStreamData.reset( new SPStreamData_Impl( true,
147
47.4k
                                            xNewTruncate, xNewSeekable, xInputStream, xNewOutStream,
148
47.4k
                                            bInOpen, bOutOpen ) );
149
150
47.4k
}
151
152
void SwitchablePersistenceStream::CopyAndSwitchPersistenceTo( const uno::Reference< io::XStream >& xStream )
153
0
{
154
0
    uno::Reference< io::XStream > xTargetStream = xStream;
155
0
    uno::Reference< io::XSeekable > xTargetSeek;
156
157
0
    if ( !xTargetStream.is() )
158
0
    {
159
0
        xTargetStream.set( new utl::TempFileFastService );
160
0
        xTargetSeek.set( xTargetStream, uno::UNO_QUERY_THROW );
161
0
    }
162
0
    else
163
0
    {
164
        // the provided stream must be empty
165
0
        xTargetSeek.set( xTargetStream, uno::UNO_QUERY_THROW );
166
0
        if ( xTargetSeek->getLength() )
167
0
            throw io::IOException(u"provided stream not empty"_ustr);
168
0
    }
169
170
0
    uno::Reference< io::XTruncate > xTargetTruncate( xTargetStream, uno::UNO_QUERY_THROW );
171
0
    uno::Reference< io::XInputStream > xTargetInStream = xTargetStream->getInputStream();
172
0
    uno::Reference< io::XOutputStream > xTargetOutStream = xTargetStream->getOutputStream();
173
0
    if ( !xTargetInStream.is() || !xTargetOutStream.is() )
174
0
        throw uno::RuntimeException();
175
176
0
    if ( !m_pStreamData->m_xOrigInStream.is() || !m_pStreamData->m_xOrigSeekable.is() )
177
0
        throw uno::RuntimeException();
178
179
0
    sal_Int64 nPos = m_pStreamData->m_xOrigSeekable->getPosition();
180
0
    m_pStreamData->m_xOrigSeekable->seek( 0 );
181
0
    ::comphelper::OStorageHelper::CopyInputToOutput( m_pStreamData->m_xOrigInStream, xTargetOutStream );
182
0
    xTargetOutStream->flush();
183
0
    xTargetSeek->seek( nPos );
184
185
0
    bool bInOpen = m_pStreamData->m_bInOpen;
186
0
    bool bOutOpen = m_pStreamData->m_bOutOpen;
187
188
0
    CloseAll_Impl();
189
190
0
    m_pStreamData.reset( new SPStreamData_Impl( false,
191
0
                                        xTargetTruncate, xTargetSeek, xTargetInStream, xTargetOutStream,
192
0
                                        bInOpen, bOutOpen ) );
193
0
}
194
195
void SwitchablePersistenceStream::CloseAll_Impl()
196
370k
{
197
370k
    m_pStreamData.reset();
198
370k
}
199
200
// css::io::XStream
201
uno::Reference< io::XInputStream > SAL_CALL SwitchablePersistenceStream::getInputStream(  )
202
208k
{
203
208k
    std::scoped_lock aGuard( m_aMutex );
204
205
208k
    if ( m_pStreamData )
206
208k
        m_pStreamData->m_bInOpen = true;
207
208k
    return static_cast< io::XInputStream* >( this );
208
208k
}
209
210
uno::Reference< io::XOutputStream > SAL_CALL SwitchablePersistenceStream::getOutputStream(  )
211
0
{
212
0
    std::scoped_lock aGuard( m_aMutex );
213
214
0
    if ( m_pStreamData )
215
0
        m_pStreamData->m_bOutOpen = true;
216
0
    return static_cast< io::XOutputStream* >( this );
217
0
}
218
219
// css::io::XInputStream
220
::sal_Int32 SAL_CALL SwitchablePersistenceStream::readBytes( uno::Sequence< ::sal_Int8 >& aData, ::sal_Int32 nBytesToRead )
221
207k
{
222
207k
    std::scoped_lock aGuard( m_aMutex );
223
224
207k
    if ( !m_pStreamData )
225
0
        throw io::NotConnectedException();
226
227
    // the original stream data should be provided
228
207k
    if ( !m_pStreamData->m_xOrigInStream.is() )
229
0
        throw uno::RuntimeException();
230
231
207k
    return m_pStreamData->m_xOrigInStream->readBytes( aData, nBytesToRead );
232
207k
}
233
234
::sal_Int32 SAL_CALL SwitchablePersistenceStream::readSomeBytes( uno::Sequence< ::sal_Int8 >& aData, ::sal_Int32 nMaxBytesToRead )
235
0
{
236
0
    std::scoped_lock aGuard( m_aMutex );
237
238
0
    if ( !m_pStreamData )
239
0
        throw io::NotConnectedException();
240
241
    // the original stream data should be provided
242
0
    if ( !m_pStreamData->m_xOrigInStream.is() )
243
0
        throw uno::RuntimeException();
244
245
0
    return m_pStreamData->m_xOrigInStream->readBytes( aData, nMaxBytesToRead );
246
0
}
247
248
::sal_Int32 SwitchablePersistenceStream::readSomeBytes( sal_Int8* aData, sal_Int32 nBytesToRead)
249
1.76M
{
250
1.76M
    std::scoped_lock aGuard( m_aMutex );
251
252
1.76M
    if ( !m_pStreamData )
253
0
        throw io::NotConnectedException();
254
255
    // the original stream data should be provided
256
1.76M
    if ( !m_pStreamData->m_xOrigInStream.is() )
257
0
        throw uno::RuntimeException();
258
259
1.76M
    if (m_pStreamData->m_pByteReader)
260
1.76M
        return m_pStreamData->m_pByteReader->readSomeBytes( aData, nBytesToRead );
261
0
    else
262
0
    {
263
0
        css::uno::Sequence < sal_Int8 > aSequence;
264
0
        sal_Int32 nBytesActuallyRead = m_pStreamData->m_xOrigInStream->readSomeBytes( aSequence, nBytesToRead );
265
0
        memcpy(aData, aSequence.getConstArray(), nBytesActuallyRead);
266
0
        return nBytesActuallyRead;
267
0
    }
268
1.76M
}
269
270
void SAL_CALL SwitchablePersistenceStream::skipBytes( ::sal_Int32 nBytesToSkip )
271
0
{
272
0
    std::scoped_lock aGuard( m_aMutex );
273
274
0
    if ( !m_pStreamData )
275
0
        throw io::NotConnectedException();
276
277
    // the original stream data should be provided
278
0
    if ( !m_pStreamData->m_xOrigInStream.is() )
279
0
        throw uno::RuntimeException();
280
281
0
    m_pStreamData->m_xOrigInStream->skipBytes( nBytesToSkip );
282
0
}
283
284
::sal_Int32 SAL_CALL SwitchablePersistenceStream::available(  )
285
0
{
286
0
    std::scoped_lock aGuard( m_aMutex );
287
288
0
    if ( !m_pStreamData )
289
0
        throw io::NotConnectedException();
290
291
    // the original stream data should be provided
292
0
    if ( !m_pStreamData->m_xOrigInStream.is() )
293
0
        throw uno::RuntimeException();
294
295
0
    return m_pStreamData->m_xOrigInStream->available();
296
0
}
297
298
void SAL_CALL SwitchablePersistenceStream::closeInput()
299
0
{
300
0
    std::scoped_lock aGuard( m_aMutex );
301
302
0
    if ( !m_pStreamData )
303
0
        throw io::NotConnectedException();
304
305
0
    m_pStreamData->m_bInOpen = false;
306
0
    if ( !m_pStreamData->m_bOutOpen )
307
0
        CloseAll_Impl();
308
0
}
309
310
// css::io::XOutputStream
311
void SAL_CALL SwitchablePersistenceStream::writeBytes( const uno::Sequence< ::sal_Int8 >& aData )
312
0
{
313
0
    std::scoped_lock aGuard( m_aMutex );
314
315
0
    if ( !m_pStreamData )
316
0
        throw io::NotConnectedException();
317
318
0
    if ( m_pStreamData->m_bInStreamBased )
319
0
        throw io::IOException();
320
321
    // the original stream data should be provided
322
0
    if ( !m_pStreamData->m_xOrigOutStream.is() )
323
0
        throw uno::RuntimeException();
324
325
0
    m_pStreamData->m_xOrigOutStream->writeBytes( aData );
326
0
}
327
328
void SAL_CALL SwitchablePersistenceStream::flush(  )
329
0
{
330
0
    std::scoped_lock aGuard( m_aMutex );
331
332
0
    if ( !m_pStreamData || m_pStreamData->m_bInStreamBased )
333
0
    {
334
0
        OSL_FAIL( "flush() is not acceptable!" );
335
0
        return;
336
        // in future throw exception, for now some code might call flush() on closed stream
337
        // since file ucp implementation allows it
338
        // throw io::NotConnectedException();
339
0
    }
340
341
    // the original stream data should be provided
342
0
    if ( !m_pStreamData->m_xOrigOutStream.is() )
343
0
        throw uno::RuntimeException();
344
345
0
    m_pStreamData->m_xOrigOutStream->flush();
346
0
}
347
348
void SAL_CALL SwitchablePersistenceStream::closeOutput(  )
349
0
{
350
0
    std::scoped_lock aGuard( m_aMutex );
351
352
0
    if ( !m_pStreamData )
353
0
        throw io::NotConnectedException();
354
355
0
    m_pStreamData->m_bOutOpen = false;
356
0
    if ( !m_pStreamData->m_bInOpen )
357
0
        CloseAll_Impl();
358
0
}
359
360
// css::io::XTruncate
361
void SAL_CALL SwitchablePersistenceStream::truncate(  )
362
0
{
363
0
    std::scoped_lock aGuard( m_aMutex );
364
365
0
    if ( !m_pStreamData )
366
0
        throw io::NotConnectedException();
367
368
0
    if ( m_pStreamData->m_bInStreamBased )
369
0
        throw io::IOException();
370
371
    // the original stream data should be provided
372
0
    if ( !m_pStreamData->m_xOrigTruncate.is() )
373
0
        throw uno::RuntimeException();
374
375
0
    m_pStreamData->m_xOrigTruncate->truncate();
376
0
}
377
378
// css::io::XSeekable
379
void SAL_CALL SwitchablePersistenceStream::seek( ::sal_Int64 location )
380
875k
{
381
875k
    std::scoped_lock aGuard( m_aMutex );
382
383
875k
    if ( !m_pStreamData )
384
0
        throw io::NotConnectedException();
385
386
    // the original stream data should be provided
387
875k
    if ( !m_pStreamData->m_xOrigSeekable.is() )
388
0
        throw uno::RuntimeException();
389
390
875k
    m_pStreamData->m_xOrigSeekable->seek( location );
391
875k
}
392
393
::sal_Int64 SAL_CALL SwitchablePersistenceStream::getPosition(  )
394
920k
{
395
920k
    std::scoped_lock aGuard( m_aMutex );
396
397
920k
    if ( !m_pStreamData )
398
0
        throw io::NotConnectedException();
399
400
    // the original stream data should be provided
401
920k
    if ( !m_pStreamData->m_xOrigSeekable.is() )
402
0
        throw uno::RuntimeException();
403
404
920k
    return m_pStreamData->m_xOrigSeekable->getPosition();
405
920k
}
406
407
::sal_Int64 SAL_CALL SwitchablePersistenceStream::getLength(  )
408
183k
{
409
183k
    std::scoped_lock aGuard( m_aMutex );
410
411
183k
    if ( !m_pStreamData )
412
0
        throw io::NotConnectedException();
413
414
    // the original stream data should be provided
415
183k
    if ( !m_pStreamData->m_xOrigSeekable.is() )
416
0
        throw uno::RuntimeException();
417
418
183k
    return m_pStreamData->m_xOrigSeekable->getLength();
419
183k
}
420
421
void SAL_CALL SwitchablePersistenceStream::waitForCompletion()
422
0
{
423
0
    if ( !m_pStreamData )
424
0
        throw io::NotConnectedException();
425
426
0
    uno::Reference< io::XAsyncOutputMonitor > asyncOutputMonitor( m_pStreamData->m_xOrigOutStream, uno::UNO_QUERY );
427
0
    if ( asyncOutputMonitor.is() )
428
0
        asyncOutputMonitor->waitForCompletion();
429
0
}
430
431
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */