• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/span.h"
23 #include "pw_kvs/io.h"
24 #include "pw_span/span.h"
25 #include "pw_status/status_with_size.h"
26 
27 namespace pw {
28 
29 // Returns the value rounded down to the nearest multiple of alignment.
AlignDown(size_t value,size_t alignment)30 constexpr size_t AlignDown(size_t value, size_t alignment) {
31   return (value / alignment) * alignment;
32 }
33 
34 // Returns the value rounded up to the nearest multiple of alignment.
AlignUp(size_t value,size_t alignment)35 constexpr size_t AlignUp(size_t value, size_t alignment) {
36   return (value + alignment - 1) / alignment * alignment;
37 }
38 
39 // Returns the number of padding bytes required to align the provided length.
Padding(size_t length,size_t alignment)40 constexpr size_t Padding(size_t length, size_t alignment) {
41   return AlignUp(length, alignment) - length;
42 }
43 
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.
48 class AlignedWriter {
49  public:
AlignedWriter(span<std::byte> buffer,size_t alignment_bytes,Output & writer)50   AlignedWriter(span<std::byte> buffer, size_t alignment_bytes, Output& writer)
51       : buffer_(buffer.data()),
52         write_size_(AlignDown(buffer.size(), alignment_bytes)),
53         alignment_bytes_(alignment_bytes),
54         output_(writer),
55         bytes_written_(0),
56         bytes_in_buffer_(0) {
57     // TODO(hepler): Add DCHECK to ensure that buffer.size() >= alignment_bytes.
58   }
59 
60   AlignedWriter(const AlignedWriter&) = delete;
61   AlignedWriter& operator=(const AlignedWriter&) = delete;
62 
~AlignedWriter()63   ~AlignedWriter() {
64     Flush().IgnoreError();  // TODO(b/242598609): Handle Status properly
65   }
66 
67   // Writes bytes to the AlignedWriter. The output may be called if the internal
68   // buffer is filled.
69   //
70   // The size in the return value is the total number of bytes for which a write
71   // has been attempted since Flush or Reset. The size is set for both
72   // successful and failed Write calls. On a failed write call, knowing the
73   // bytes attempted may be important when working with flash memory, since it
74   // can only be written once between erases.
75   StatusWithSize Write(span<const std::byte> data);
76 
Write(const void * data,size_t size)77   StatusWithSize Write(const void* data, size_t size) {
78     return Write(
79         span<const std::byte>(static_cast<const std::byte*>(data), size));
80   }
81 
82   // Reads size bytes from the input and writes them to the output.
83   StatusWithSize Write(Input& input, size_t size);
84 
85   // Flush and reset the AlignedWriter. Any remaining bytes in the buffer are
86   // zero-padded to an alignment boundary and written to the output. Flush is
87   // automatically called when the AlignedWriter goes out of scope.
88   StatusWithSize Flush();
89 
90  private:
91   static constexpr std::byte kPadByte = static_cast<std::byte>(0);
92 
93   StatusWithSize AddBytesToBuffer(size_t bytes_added);
94 
95   std::byte* const buffer_;
96   const size_t write_size_;
97   const size_t alignment_bytes_;
98 
99   Output& output_;
100   size_t bytes_written_;
101   size_t bytes_in_buffer_;
102 };
103 
104 // Declares an AlignedWriter with a built-in buffer.
105 template <size_t kBufferSize>
106 class AlignedWriterBuffer : public AlignedWriter {
107  public:
108   template <typename... Args>
AlignedWriterBuffer(Args &&...aligned_writer_args)109   AlignedWriterBuffer(Args&&... aligned_writer_args)
110       : AlignedWriter(buffer_, std::forward<Args>(aligned_writer_args)...) {}
111 
112  private:
113   std::byte buffer_[kBufferSize];
114 };
115 
116 // Writes data from multiple buffers using an AlignedWriter.
117 template <size_t kBufferSize>
AlignedWrite(Output & output,size_t alignment_bytes,span<const span<const std::byte>> data)118 StatusWithSize AlignedWrite(Output& output,
119                             size_t alignment_bytes,
120                             span<const span<const std::byte>> data) {
121   // TODO(davidrogers): This should convert to PW_CHECK once that is available
122   // for use in host tests.
123   if (alignment_bytes > kBufferSize) {
124     return StatusWithSize::Internal();
125   }
126 
127   AlignedWriterBuffer<kBufferSize> buffer(alignment_bytes, output);
128 
129   for (const span<const std::byte>& chunk : data) {
130     StatusWithSize result = buffer.Write(chunk);
131     if (!result.ok()) {
132       return result;
133     }
134   }
135 
136   return buffer.Flush();
137 }
138 
139 // Calls AlignedWrite with an initializer list.
140 template <size_t kBufferSize>
AlignedWrite(Output & output,size_t alignment_bytes,std::initializer_list<span<const std::byte>> data)141 StatusWithSize AlignedWrite(Output& output,
142                             size_t alignment_bytes,
143                             std::initializer_list<span<const std::byte>> data) {
144   return AlignedWrite<kBufferSize>(
145       output,
146       alignment_bytes,
147       span<const ConstByteSpan>(data.begin(), data.size()));
148 }
149 
150 }  // namespace pw
151