xref: /aosp_15_r20/external/pigweed/pw_kvs/public/pw_kvs/alignment.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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