1 // Copyright 2020 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://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, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #pragma once
15
16 #include <algorithm>
17 #include <cstddef>
18 #include <cstring>
19 #include <initializer_list>
20 #include <utility>
21
22 #include "pw_bytes/alignment.h"
23 #include "pw_bytes/span.h"
24 #include "pw_kvs/io.h"
25 #include "pw_span/span.h"
26 #include "pw_status/status_with_size.h"
27
28 namespace pw {
29
30 // Class for managing aligned writes. Stores data in an intermediate buffer and
31 // calls an output function with aligned data as the buffer becomes full. Any
32 // bytes remaining in the buffer are written to the output when Flush() is
33 // called or the AlignedWriter goes out of scope.
34 class AlignedWriter {
35 public:
AlignedWriter(span<std::byte> buffer,size_t alignment_bytes,Output & writer)36 AlignedWriter(span<std::byte> buffer, size_t alignment_bytes, Output& writer)
37 : buffer_(buffer.data()),
38 write_size_(AlignDown(buffer.size(), alignment_bytes)),
39 alignment_bytes_(alignment_bytes),
40 output_(writer),
41 bytes_written_(0),
42 bytes_in_buffer_(0) {
43 // TODO(hepler): Add DCHECK to ensure that buffer.size() >= alignment_bytes.
44 }
45
46 AlignedWriter(const AlignedWriter&) = delete;
47 AlignedWriter& operator=(const AlignedWriter&) = delete;
48
~AlignedWriter()49 ~AlignedWriter() {
50 Flush().IgnoreError(); // TODO: b/242598609 - Handle Status properly
51 }
52
53 // Writes bytes to the AlignedWriter. The output may be called if the internal
54 // buffer is filled.
55 //
56 // The size in the return value is the total number of bytes for which a write
57 // has been attempted since Flush or Reset. The size is set for both
58 // successful and failed Write calls. On a failed write call, knowing the
59 // bytes attempted may be important when working with flash memory, since it
60 // can only be written once between erases.
61 StatusWithSize Write(span<const std::byte> data);
62
Write(const void * data,size_t size)63 StatusWithSize Write(const void* data, size_t size) {
64 return Write(
65 span<const std::byte>(static_cast<const std::byte*>(data), size));
66 }
67
68 // Reads size bytes from the input and writes them to the output.
69 StatusWithSize Write(Input& input, size_t size);
70
71 // Flush and reset the AlignedWriter. Any remaining bytes in the buffer are
72 // zero-padded to an alignment boundary and written to the output. Flush is
73 // automatically called when the AlignedWriter goes out of scope.
74 StatusWithSize Flush();
75
76 private:
77 static constexpr std::byte kPadByte = static_cast<std::byte>(0);
78
79 StatusWithSize AddBytesToBuffer(size_t bytes_added);
80
81 std::byte* const buffer_;
82 const size_t write_size_;
83 const size_t alignment_bytes_;
84
85 Output& output_;
86 size_t bytes_written_;
87 size_t bytes_in_buffer_;
88 };
89
90 // Declares an AlignedWriter with a built-in buffer.
91 template <size_t kBufferSize>
92 class AlignedWriterBuffer : public AlignedWriter {
93 public:
94 template <typename... Args>
AlignedWriterBuffer(Args &&...aligned_writer_args)95 AlignedWriterBuffer(Args&&... aligned_writer_args)
96 : AlignedWriter(buffer_, std::forward<Args>(aligned_writer_args)...) {}
97
98 private:
99 std::byte buffer_[kBufferSize];
100 };
101
102 // Writes data from multiple buffers using an AlignedWriter.
103 template <size_t kBufferSize>
AlignedWrite(Output & output,size_t alignment_bytes,span<const span<const std::byte>> data)104 StatusWithSize AlignedWrite(Output& output,
105 size_t alignment_bytes,
106 span<const span<const std::byte>> data) {
107 // TODO(davidrogers): This should convert to PW_CHECK once that is available
108 // for use in host tests.
109 if (alignment_bytes > kBufferSize) {
110 return StatusWithSize::Internal();
111 }
112
113 AlignedWriterBuffer<kBufferSize> buffer(alignment_bytes, output);
114
115 for (const span<const std::byte>& chunk : data) {
116 StatusWithSize result = buffer.Write(chunk);
117 if (!result.ok()) {
118 return result;
119 }
120 }
121
122 return buffer.Flush();
123 }
124
125 // Calls AlignedWrite with an initializer list.
126 template <size_t kBufferSize>
AlignedWrite(Output & output,size_t alignment_bytes,std::initializer_list<span<const std::byte>> data)127 StatusWithSize AlignedWrite(Output& output,
128 size_t alignment_bytes,
129 std::initializer_list<span<const std::byte>> data) {
130 return AlignedWrite<kBufferSize>(
131 output,
132 alignment_bytes,
133 span<const ConstByteSpan>(data.begin(), data.size()));
134 }
135
136 } // namespace pw
137