Audacity 3.2.0
BlockHasher.cpp
Go to the documentation of this file.
1/* SPDX-License-Identifier: GPL-2.0-or-later */
2/*!********************************************************************
3
4 Audacity: A Digital Audio Editor
5
6 BlockHasher.cpp
7
8 Dmitry Vedenko
9
10**********************************************************************/
11#include "BlockHasher.h"
12
13#include <algorithm>
14#include <atomic>
15#include <future>
16#include <utility>
17
18#include "MemoryX.h"
19#include "SampleBlock.h"
20
21#include "crypto/SHA256.h"
22
24{
26{
27public:
28 using SampleData = std::vector<std::remove_pointer_t<samplePtr>>;
29
30 explicit Workers(
31 BlockHashCache& cache, const std::vector<LockedBlock> blocks,
32 std::function<void()> onComplete)
33 : mThreadsCount { std::max(1u, std::thread::hardware_concurrency() / 2) }
34 , mCache { cache }
35 , mOnComplete { std::move(onComplete) }
36 {
37 mResults.reserve(mThreadsCount);
38
39 const auto blocksCount = blocks.size();
40 // Try to add no more that 1 extra block per thread
41 const size_t blockPerThread = blocks.size() / mThreadsCount + 1;
42
43 for (size_t i = 0; i < mThreadsCount; ++i)
44 {
45 const size_t startIndex = i;
46
47 if (startIndex >= blocks.size())
48 break;
49
50 std::vector<LockedBlock> threadBlocks;
51 threadBlocks.reserve(blockPerThread);
52
53 for (size_t j = startIndex; j < blocksCount; j += mThreadsCount)
54 threadBlocks.emplace_back(blocks[j]);
55
56 mResults.emplace_back(std::async(
57 std::launch::async,
58 [this, threadBlocks = std::move(threadBlocks)]()
59 {
60 Result result;
61 SampleData sampleData;
62
63 for (const auto& block : threadBlocks)
64 result.emplace(block.Id, ComputeHash(sampleData, block));
65
66 return result;
67 }));
68 }
69
70 mWaiter = std::async(
71 std::launch::async,
72 [this]
73 {
74 for (auto& fut : mResults)
75 fut.wait();
76
78 });
79 }
80
81 bool IsReady() const
82 {
83 return std::all_of(
84 mResults.begin(), mResults.end(),
85 [](const auto& result)
86 {
87 return result.wait_for(std::chrono::seconds(0)) ==
88 std::future_status::ready;
89 });
90 }
91
92 std::pair<std::string, bool>
93 ComputeHash(SampleData& sampleData, const LockedBlock& block) const
94 {
95 std::string hash;
96
97 if (mCache.GetHash(block.Id, hash))
98 return { hash, false };
99
100 const auto sampleFormat = block.Format;
101 const auto sampleCount = block.Block->GetSampleCount();
102 const auto dataSize = sampleCount * SAMPLE_SIZE(sampleFormat);
103
104 sampleData.resize(dataSize);
105
106 const size_t samplesRead = block.Block->GetSamples(
107 sampleData.data(), sampleFormat, 0, sampleCount, false);
108
109 if (samplesRead != sampleCount)
110 return { {}, false };
111
112 hash = crypto::sha256(sampleData);
113
114 return { hash, true };
115 }
116
118 {
119 if (mOnComplete)
120 mOnComplete();
121 }
122
123 std::vector<std::pair<int64_t, std::string>> TakeResult()
124 {
125 std::vector<std::pair<int64_t, std::string>> result;
126
127 for (auto& fut : mResults)
128 {
129 const auto& threadResult = fut.get();
130
131 for (const auto& [id, hash] : threadResult)
132 {
133 result.emplace_back(std::make_pair(id, hash.first));
134
135 if (hash.second)
136 mCache.UpdateHash(id, hash.first);
137 }
138 }
139
140 mResults.clear();
141
142 return result;
143 }
144
145private:
146 const size_t mThreadsCount;
147
149
150 using Result = std::unordered_map<int64_t, std::pair<std::string, bool>>;
151 std::vector<std::future<Result>> mResults;
152 std::future<void> mWaiter;
153
154 std::function<void()> mOnComplete;
155};
156
157BlockHasher::BlockHasher() = default;
158BlockHasher::~BlockHasher() = default;
159
161 BlockHashCache& cache, std::vector<LockedBlock> blocks,
162 std::function<void()> onComplete)
163{
164 if (mWorkers != nullptr && !mWorkers->IsReady())
165 return false;
166
167 if (blocks.empty())
168 {
169 if (onComplete)
170 onComplete();
171
172 return true;
173 }
174
175 mWorkers = std::make_unique<Workers>(
176 cache, std::move(blocks), std::move(onComplete));
177
178 return true;
179}
180
182{
183 return mWorkers != nullptr && mWorkers->IsReady();
184}
185
186std::vector<std::pair<int64_t, std::string>> BlockHasher::TakeResult()
187{
188 if (mWorkers == nullptr)
189 return {};
190
191 return mWorkers->TakeResult();
192}
193
194} // namespace audacity::cloud::audiocom::sync
sampleFormat
The ordering of these values with operator < agrees with the order of increasing bit width.
Definition: SampleFormat.h:30
#define SAMPLE_SIZE(SampleFormat)
Definition: SampleFormat.h:52
virtual void UpdateHash(int64_t blockId, const std::string &hash)=0
virtual bool GetHash(int64_t blockId, std::string &hash) const =0
std::vector< std::future< Result > > mResults
std::pair< std::string, bool > ComputeHash(SampleData &sampleData, const LockedBlock &block) const
Definition: BlockHasher.cpp:93
Workers(BlockHashCache &cache, const std::vector< LockedBlock > blocks, std::function< void()> onComplete)
Definition: BlockHasher.cpp:30
std::unordered_map< int64_t, std::pair< std::string, bool > > Result
std::vector< std::pair< int64_t, std::string > > TakeResult()
std::vector< std::remove_pointer_t< samplePtr > > SampleData
Definition: BlockHasher.cpp:28
bool ComputeHashes(BlockHashCache &cache, std::vector< LockedBlock > blocks, std::function< void()> onComplete)
std::unique_ptr< Workers > mWorkers
Definition: BlockHasher.h:50
std::vector< std::pair< int64_t, std::string > > TakeResult()
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:19
std::string sha256(const T &data)
Definition: SHA256.h:50
STL namespace.