1 // Copyright 2020 The Pigweed Authors
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
7 // https://www.apache.org/licenses/LICENSE-2.0
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
19 #include <initializer_list>
23 #include "pw_bytes/span.h"
24 #include "pw_kvs/io.h"
25 #include "pw_status/status_with_size.h"
29 // Returns the value rounded down to the nearest multiple of alignment.
30 constexpr size_t AlignDown(size_t value, size_t alignment) {
31 return (value / alignment) * alignment;
34 // Returns the value rounded up to the nearest multiple of alignment.
35 constexpr size_t AlignUp(size_t value, size_t alignment) {
36 return (value + alignment - 1) / alignment * alignment;
39 // Returns the number of padding bytes required to align the provided length.
40 constexpr size_t Padding(size_t length, size_t alignment) {
41 return AlignUp(length, alignment) - length;
44 // Class for managing aligned writes. Stores data in an intermediate buffer and
45 // calls an output function with aligned data as the buffer becomes full. Any
46 // bytes remaining in the buffer are written to the output when Flush() is
47 // called or the AlignedWriter goes out of scope.
50 AlignedWriter(std::span<std::byte> buffer,
51 size_t alignment_bytes,
53 : buffer_(buffer.data()),
54 write_size_(AlignDown(buffer.size(), alignment_bytes)),
55 alignment_bytes_(alignment_bytes),
59 // TODO(hepler): Add DCHECK to ensure that buffer.size() >= alignment_bytes.
62 ~AlignedWriter() { Flush(); }
64 // Writes bytes to the AlignedWriter. The output may be called if the internal
67 // The size in the return value is the total number of bytes for which a write
68 // has been attempted since Flush or Reset. The size is set for both
69 // successful and failed Write calls. On a failed write call, knowing the
70 // bytes attempted may be important when working with flash memory, since it
71 // can only be written once between erases.
72 StatusWithSize Write(std::span<const std::byte> data);
74 StatusWithSize Write(const void* data, size_t size) {
76 std::span<const std::byte>(static_cast<const std::byte*>(data), size));
79 // Reads size bytes from the input and writes them to the output.
80 StatusWithSize Write(Input& input, size_t size);
82 // Flush and reset the AlignedWriter. Any remaining bytes in the buffer are
83 // zero-padded to an alignment boundary and written to the output. Flush is
84 // automatically called when the AlignedWriter goes out of scope.
85 StatusWithSize Flush();
88 static constexpr std::byte kPadByte = static_cast<std::byte>(0);
90 StatusWithSize AddBytesToBuffer(size_t bytes_added);
92 std::byte* const buffer_;
93 const size_t write_size_;
94 const size_t alignment_bytes_;
97 size_t bytes_written_;
98 size_t bytes_in_buffer_;
101 // Declares an AlignedWriter with a built-in buffer.
102 template <size_t kBufferSize>
103 class AlignedWriterBuffer : public AlignedWriter {
105 template <typename... Args>
106 AlignedWriterBuffer(Args&&... aligned_writer_args)
107 : AlignedWriter(buffer_, std::forward<Args>(aligned_writer_args)...) {}
110 std::byte buffer_[kBufferSize];
113 // Writes data from multiple buffers using an AlignedWriter.
114 template <size_t kBufferSize>
115 StatusWithSize AlignedWrite(Output& output,
116 size_t alignment_bytes,
117 std::span<const std::span<const std::byte>> data) {
118 // TODO: This should convert to PW_CHECK once that is available for use in
120 if (alignment_bytes > kBufferSize) {
121 return StatusWithSize::Internal();
124 AlignedWriterBuffer<kBufferSize> buffer(alignment_bytes, output);
126 for (const std::span<const std::byte>& chunk : data) {
127 StatusWithSize result = buffer.Write(chunk);
133 return buffer.Flush();
136 // Calls AlignedWrite with an initializer list.
137 template <size_t kBufferSize>
138 StatusWithSize AlignedWrite(
140 size_t alignment_bytes,
141 std::initializer_list<std::span<const std::byte>> data) {
142 return AlignedWrite<kBufferSize>(
145 std::span<const ConstByteSpan>(data.begin(), data.size()));