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 <cstddef>
17 #include <span>
18 
19 #include "pw_kvs/alignment.h"
20 #include "pw_status/status.h"
21 
22 namespace pw {
23 namespace kvs {
24 
25 class ChecksumAlgorithm {
26  public:
27   // Resets the checksum to its initial state.
28   virtual void Reset() = 0;
29 
30   // Updates the checksum with the provided data.
31   virtual void Update(std::span<const std::byte> data) = 0;
32 
33   // Updates the checksum from a pointer and size.
Update(const void * data,size_t size_bytes)34   void Update(const void* data, size_t size_bytes) {
35     return Update(std::span<const std::byte>(
36         static_cast<const std::byte*>(data), size_bytes));
37   }
38 
39   // Returns the final result of the checksum. Update() can no longer be called
40   // after this. The returned std::span is valid until a call to Reset().
41   //
42   // Finish MUST be called before calling Verify.
Finish()43   std::span<const std::byte> Finish() {
44     Finalize();  // Implemented by derived classes, if required.
45     return state();
46   }
47 
48   // Returns the size of the checksum state.
size_bytes()49   constexpr size_t size_bytes() const { return state_.size(); }
50 
51   // Compares a calculated checksum to this checksum's state. The checksum must
52   // be at least as large as size_bytes(). If it is larger, bytes beyond
53   // size_bytes() are ignored.
54   //
55   // Finish MUST be called before calling Verify.
56   Status Verify(std::span<const std::byte> checksum) const;
57 
58  protected:
59   // A derived class provides a std::span of its state buffer.
ChecksumAlgorithm(std::span<const std::byte> state)60   constexpr ChecksumAlgorithm(std::span<const std::byte> state)
61       : state_(state) {}
62 
63   // Protected destructor prevents deleting ChecksumAlgorithms from the base
64   // class, so that it is safe to have a non-virtual destructor.
65   ~ChecksumAlgorithm() = default;
66 
67   // Returns the current checksum state.
state()68   constexpr std::span<const std::byte> state() const { return state_; }
69 
70  private:
71   // Checksums that require finalizing operations may override this method.
Finalize()72   virtual void Finalize() {}
73 
74   std::span<const std::byte> state_;
75 };
76 
77 // A checksum algorithm for which Verify always passes. This can be used to
78 // disable checksum verification for a particular entry format.
79 class IgnoreChecksum final : public ChecksumAlgorithm {
80  public:
IgnoreChecksum()81   constexpr IgnoreChecksum() : ChecksumAlgorithm({}) {}
82 
Reset()83   void Reset() override {}
Update(std::span<const std::byte>)84   void Update(std::span<const std::byte>) override {}
85 };
86 
87 // Calculates a checksum in kAlignmentBytes chunks. Checksum classes can inherit
88 // from this and implement UpdateAligned and FinalizeAligned instead of Update
89 // and Finalize.
90 template <size_t kAlignmentBytes, size_t kBufferSize = kAlignmentBytes>
91 class AlignedChecksum : public ChecksumAlgorithm {
92  public:
Update(std::span<const std::byte> data)93   void Update(std::span<const std::byte> data) final { writer_.Write(data); }
94 
95  protected:
AlignedChecksum(std::span<const std::byte> state)96   constexpr AlignedChecksum(std::span<const std::byte> state)
97       : ChecksumAlgorithm(state),
98         output_(*this),
99         writer_(kAlignmentBytes, output_) {}
100 
101   ~AlignedChecksum() = default;
102 
103  private:
104   static_assert(kBufferSize >= kAlignmentBytes);
105 
Finalize()106   void Finalize() final {
107     writer_.Flush();
108     FinalizeAligned();
109   }
110 
111   virtual void UpdateAligned(std::span<const std::byte> data) = 0;
112 
113   virtual void FinalizeAligned() = 0;
114 
115   class CallUpdateAligned final : public Output {
116    public:
CallUpdateAligned(AlignedChecksum & object)117     constexpr CallUpdateAligned(AlignedChecksum& object) : object_(object) {}
118 
119    private:
DoWrite(std::span<const std::byte> data)120     StatusWithSize DoWrite(std::span<const std::byte> data) override {
121       object_.UpdateAligned(data);
122       return StatusWithSize(data.size());
123     }
124 
125     AlignedChecksum& object_;
126   };
127 
128   CallUpdateAligned output_;
129   AlignedWriterBuffer<kBufferSize> writer_;
130 };
131 
132 }  // namespace kvs
133 }  // namespace pw
134