• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / mender / 1022567176

02 Oct 2023 07:50AM UTC coverage: 80.127% (+2.5%) from 77.645%
1022567176

push

gitlab-ci

kacf
chore: Centralize selection of `std::filesystem` library.

Signed-off-by: Kristian Amlie <kristian.amlie@northern.tech>

6447 of 8046 relevant lines covered (80.13%)

9912.21 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/common/io.hpp
1
// Copyright 2023 Northern.tech AS
2
//
3
//    Licensed under the Apache License, Version 2.0 (the "License");
4
//    you may not use this file except in compliance with the License.
5
//    You may obtain a copy of the License at
6
//
7
//        http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//    Unless required by applicable law or agreed to in writing, software
10
//    distributed under the License is distributed on an "AS IS" BASIS,
11
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//    See the License for the specific language governing permissions and
13
//    limitations under the License.
14

15
#ifndef MENDER_COMMON_IO_HPP
16
#define MENDER_COMMON_IO_HPP
17

18
#include <common/error.hpp>
19
#include <common/expected.hpp>
20

21
#include <algorithm>
22
#include <cstdint>
23
#include <fstream>
24
#include <iostream>
25
#include <istream>
26
#include <iterator>
27
#include <limits>
28
#include <memory>
29
#include <ostream>
30
#include <sstream>
31
#include <string>
32
#include <system_error>
33
#include <vector>
34

35
namespace mender {
36
namespace common {
37
namespace io {
38

39
using namespace std;
40

41
namespace expected = mender::common::expected;
42

43
using mender::common::error::Error;
44
using mender::common::error::NoError;
45

46
using mender::common::expected::ExpectedSize;
47

48
namespace paths {
49
extern const string Stdin;
50
}
51

52
class Reader {
53
public:
54
        virtual ~Reader() {};
55

56
        virtual ExpectedSize Read(vector<uint8_t>::iterator start, vector<uint8_t>::iterator end) = 0;
57

58
        unique_ptr<istream> GetStream();
59
};
60
using ReaderPtr = shared_ptr<Reader>;
61
using ExpectedReaderPtr = expected::expected<ReaderPtr, Error>;
62

63
class Writer {
64
public:
65
        virtual ~Writer() {};
66

67
        virtual ExpectedSize Write(
68
                vector<uint8_t>::const_iterator start, vector<uint8_t>::const_iterator end) = 0;
69
};
70
using WriterPtr = shared_ptr<Writer>;
71
using ExpectedWriterPtr = expected::expected<WriterPtr, Error>;
72

73
class ReadWriter : virtual public Reader, virtual public Writer {};
74
using ReadWriterPtr = shared_ptr<ReadWriter>;
75
using ExpectedReadWriterPtr = expected::expected<ReadWriterPtr, Error>;
76

77
class Canceller {
78
public:
79
        virtual ~Canceller() {
80
        }
81

82
        virtual void Cancel() = 0;
83
};
84

85
enum class Repeat {
86
        Yes,
87
        No,
88
};
89

90
using AsyncIoHandler = function<void(ExpectedSize)>;
91
using RepeatedAsyncIoHandler = function<Repeat(ExpectedSize)>;
92

93
class AsyncReader : virtual public Canceller {
94
public:
95
        // Note: iterators generally need to remain valid until either the handler or `Cancel()` is
96
        // called.
97
        virtual error::Error AsyncRead(
98
                vector<uint8_t>::iterator start, vector<uint8_t>::iterator end, AsyncIoHandler handler) = 0;
99

100
        // Calls AsyncRead repeatedly with the same iterators and handler, until the stream is
101
        // exhausted or an error occurs. All errors will be returned through the handler, even
102
        // initial errors from AsyncRead.
103
        void RepeatedAsyncRead(
104
                vector<uint8_t>::iterator start,
105
                vector<uint8_t>::iterator end,
106
                RepeatedAsyncIoHandler handler);
107
};
108
using AsyncReaderPtr = shared_ptr<AsyncReader>;
109

110
class AsyncWriter : virtual public Canceller {
111
public:
112
        // Note: iterators generally need to remain valid until either the handler or `Cancel()` is
113
        // called.
114
        virtual error::Error AsyncWrite(
115
                vector<uint8_t>::const_iterator start,
116
                vector<uint8_t>::const_iterator end,
117
                AsyncIoHandler handler) = 0;
118
};
119
using AsyncWriterPtr = shared_ptr<AsyncWriter>;
120

121
class AsyncReadWriter : virtual public AsyncReader, virtual public AsyncWriter {};
122
using AsyncReadWriterPtr = shared_ptr<AsyncReadWriter>;
123

124
using ExpectedAsyncReaderPtr = expected::expected<AsyncReaderPtr, error::Error>;
125
using ExpectedAsyncWriterPtr = expected::expected<AsyncWriterPtr, error::Error>;
126
using ExpectedAsyncReadWriterPtr = expected::expected<AsyncReadWriterPtr, error::Error>;
127

128
/**
129
 * Stream the data from `src` to `dst` until encountering EOF or an error.
130
 */
131
Error Copy(Writer &dst, Reader &src);
132

133
/**
134
 * Stream the data from `src` to `dst` until encountering EOF or an error, using `buffer` as an
135
 * intermediate. The block size will be the size of `buffer`.
136
 */
137
Error Copy(Writer &dst, Reader &src, vector<uint8_t> &buffer);
138

139
/**
140
 * Stream the data from `src` to `dst` until encountering EOF or an error. The reading end is async,
141
 * the writing end is not, so it should be "quick", otherwise it may still stall.
142
 */
143
void AsyncCopy(
144
        Writer &dst,
145
        AsyncReader &src,
146
        function<void(Error)> finished_handler,
147
        size_t stop_after = numeric_limits<size_t>::max());
148
void AsyncCopy(
149
        WriterPtr dst,
150
        AsyncReaderPtr src,
151
        function<void(Error)> finished_handler,
152
        size_t stop_after = numeric_limits<size_t>::max());
153

154
/**
155
 * Stream the data from `src` to `dst` until encountering EOF or an error. The writing end is async,
156
 * the reading end is not, so it should be "quick", otherwise it may still stall.
157
 */
158
void AsyncCopy(
159
        AsyncWriter &dst,
160
        Reader &src,
161
        function<void(Error)> finished_handler,
162
        size_t stop_after = numeric_limits<size_t>::max());
163
void AsyncCopy(
164
        AsyncWriterPtr dst,
165
        ReaderPtr src,
166
        function<void(Error)> finished_handler,
167
        size_t stop_after = numeric_limits<size_t>::max());
168

169
/**
170
 * Stream the data from `src` to `dst` until encountering EOF or an error.
171
 */
172
void AsyncCopy(
173
        AsyncWriter &dst,
174
        AsyncReader &src,
175
        function<void(Error)> finished_handler,
176
        size_t stop_after = numeric_limits<size_t>::max());
177
void AsyncCopy(
178
        AsyncWriterPtr dst,
179
        AsyncReaderPtr src,
180
        function<void(Error)> finished_handler,
181
        size_t stop_after = numeric_limits<size_t>::max());
182

183
class StreamReader : virtual public Reader {
184
protected:
185
        shared_ptr<std::istream> is_;
186

187
public:
188
        StreamReader(std::istream &stream) :
189
                // For references, initialize a shared_ptr with a null deleter, since we don't own
190
                // the object.
191
                is_ {&stream, [](std::istream *stream) {}} {
192
        }
193
        StreamReader(shared_ptr<std::istream> stream) :
194
                is_ {stream} {
195
        }
196
        ExpectedSize Read(vector<uint8_t>::iterator start, vector<uint8_t>::iterator end) override;
197
};
198

199
using ExpectedIfstream = expected::expected<ifstream, error::Error>;
200
using ExpectedSharedIfstream = expected::expected<shared_ptr<ifstream>, error::Error>;
201
using ExpectedOfstream = expected::expected<ofstream, error::Error>;
202
using ExpectedSharedOfstream = expected::expected<shared_ptr<ofstream>, error::Error>;
203
ExpectedIfstream OpenIfstream(const string &path);
204
ExpectedSharedIfstream OpenSharedIfstream(const string &path);
205
ExpectedOfstream OpenOfstream(const string &path, bool append = false);
206
ExpectedSharedOfstream OpenSharedOfstream(const string &path, bool append = false);
207

208
class FileReader : virtual public StreamReader {
209
public:
210
        FileReader(const string &path) :
211
                StreamReader(shared_ptr<std::istream>()),
212
                path_ {path} {};
213

214
        ExpectedSize Read(vector<uint8_t>::iterator start, vector<uint8_t>::iterator end) override {
×
215
                // We cannot open the stream in the constructor because it can fail and
216
                // there's no way to report that error. However, the check below is
217
                // cheap compared to the I/O that happens in this function, so a very
218
                // little overhead.
219
                if (!is_) {
×
220
                        auto ex_is = OpenSharedIfstream(path_);
×
221
                        if (!ex_is) {
×
222
                                return expected::unexpected(ex_is.error());
×
223
                        }
224
                        is_ = ex_is.value();
×
225
                }
226
                return StreamReader::Read(start, end);
×
227
        }
228

229
        error::Error Rewind();
230

231
private:
232
        string path_;
233
};
234

235
/* Discards all data written to it */
236
class Discard : virtual public Writer {
237
        ExpectedSize Write(
238
                vector<uint8_t>::const_iterator start, vector<uint8_t>::const_iterator end) override {
239
                return end - start;
240
        }
241
};
242

243
class StringReader : virtual public Reader {
244
private:
245
        std::stringstream s_;
246
        unique_ptr<StreamReader> reader_;
247

248
public:
249
        StringReader(const string &str) :
250
                s_ {str},
251
                reader_ {new StreamReader(s_)} {
252
        }
253
        StringReader(string &&str) :
254
                s_ {str},
255
                reader_ {new StreamReader(s_)} {
256
        }
257
        StringReader(StringReader &&sr) :
258
                s_ {std::move(sr.s_)},
259
                reader_ {new StreamReader(s_)} {
260
        }
261
        StringReader &operator=(StringReader &&sr) {
262
                s_ = std::move(sr.s_);
263
                reader_.reset(new StreamReader(s_));
264
                return *this;
265
        }
266

267
        ExpectedSize Read(vector<uint8_t>::iterator start, vector<uint8_t>::iterator end) override {
×
268
                return reader_->Read(start, end);
×
269
        }
270
};
271

272
using Vsize = vector<uint8_t>::size_type;
273

274
class ByteWriter : virtual public Writer {
275
private:
276
        shared_ptr<vector<uint8_t>> receiver_;
277
        Vsize bytes_written_ {0};
278
        bool unlimited_ {false};
279

280
public:
281
        ByteWriter(vector<uint8_t> &receiver) :
282
                receiver_ {&receiver, [](vector<uint8_t> *vec) {}} {
283
        }
284

285
        ByteWriter(shared_ptr<vector<uint8_t>> receiver) :
286
                receiver_ {receiver} {
287
        }
288

289
        // Will extend the vector if necessary. Probably a bad idea in production code, but useful
290
        // in tests.
291
        void SetUnlimited(bool enabled);
292

293
        ExpectedSize Write(
294
                vector<uint8_t>::const_iterator start, vector<uint8_t>::const_iterator end) override;
295
};
296

297
class StreamWriter : virtual public Writer {
298
private:
299
        shared_ptr<std::ostream> os_;
300

301
public:
302
        StreamWriter(std::ostream &stream) :
303
                os_ {&stream, [](std::ostream *str) {}} {
304
        }
305
        StreamWriter(shared_ptr<std::ostream> stream) :
306
                os_ {stream} {
307
        }
308
        ExpectedSize Write(
309
                vector<uint8_t>::const_iterator start, vector<uint8_t>::const_iterator end) override;
310
};
311

312
error::Error WriteStringIntoOfstream(ofstream &os, const string &data);
313

314
expected::ExpectedSize FileSize(const string &path);
315

316
} // namespace io
317
} // namespace common
318
} // namespace mender
319

320
#endif // MENDER_COMMON_IO_HPP
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc