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 assert(block.Id >= 0);
96 if(block.Id < 0)
97 return {{}, false};
98
99 std::string hash;
100
101 if (mCache.GetHash(block.Id, hash))
102 return { hash, false };
103
104 const auto sampleFormat = block.Format;
105 const auto sampleCount = block.Block->GetSampleCount();
106 const auto dataSize = sampleCount * SAMPLE_SIZE(sampleFormat);
107
108 sampleData.resize(dataSize);
109
110 const size_t samplesRead = block.Block->GetSamples(
111 sampleData.data(), sampleFormat, 0, sampleCount, false);
112
113 if (samplesRead != sampleCount)
114 return { {}, false };
115
116 hash = crypto::sha256(sampleData);
117
118 return { hash, true };
119 }
120
122 {
123 if (mOnComplete)
124 mOnComplete();
125 }
126
127 std::vector<std::pair<int64_t, std::string>> TakeResult()
128 {
129 std::vector<std::pair<int64_t, std::string>> result;
130
131 for (auto& fut : mResults)
132 {
133 const auto& threadResult = fut.get();
134
135 for (const auto& [id, hash] : threadResult)
136 {
137 result.emplace_back(std::make_pair(id, hash.first));
138
139 if (hash.second)
140 mCache.UpdateHash(id, hash.first);
141 }
142 }
143
144 mResults.clear();
145
146 return result;
147 }
148
149private:
150 const size_t mThreadsCount;
151
153
154 using Result = std::unordered_map<int64_t, std::pair<std::string, bool>>;
155 std::vector<std::future<Result>> mResults;
156 std::future<void> mWaiter;
157
158 std::function<void()> mOnComplete;
159};
160
161BlockHasher::BlockHasher() = default;
162BlockHasher::~BlockHasher() = default;
163
165 BlockHashCache& cache, std::vector<LockedBlock> blocks,
166 std::function<void()> onComplete)
167{
168 if (mWorkers != nullptr && !mWorkers->IsReady())
169 return false;
170
171 if (blocks.empty())
172 {
173 if (onComplete)
174 onComplete();
175
176 return true;
177 }
178
179 mWorkers = std::make_unique<Workers>(
180 cache, std::move(blocks), std::move(onComplete));
181
182 return true;
183}
184
186{
187 return mWorkers != nullptr && mWorkers->IsReady();
188}
189
190std::vector<std::pair<int64_t, std::string>> BlockHasher::TakeResult()
191{
192 if (mWorkers == nullptr)
193 return {};
194
195 return mWorkers->TakeResult();
196}
197
198} // 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.