libxcks  0.1.0.1
ckcalculator.cpp
Go to the documentation of this file.
1 /*
2  * libxcks
3  * Copyright (C) 2022 Julien Couot
4  *
5  * This program is free software: you can redistribute it and/or modify it
6  * under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or (at your
8  * option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
13  * License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this program. If not, see <https://www.gnu.org/licenses/>.
17  */
18 
24 //---------------------------------------------------------------------------
25 #include <thread>
26 #include <mutex>
27 #include <shared_mutex>
28 #include <vector>
29 #include <algorithm>
30 
31 #include "libxcks/ckcalculator.hpp"
32 #include "libxcks/checksum.hpp"
33 #include "libxcks/ckfactory.hpp"
34 //---------------------------------------------------------------------------
35 
36 
38 using namespace std;
39 
40 
41 namespace libxcks
42 {
44 using ArrayChecksum = vector<Checksum*>;
45 
46 
47 //###########################################################################
48 // A thread safe checksums calculator.
49 //###########################################################################
50 
55 {
56  protected:
58  size_t curCk;
59  mutable shared_mutex mutex;
60 
61  public:
64 
67 
70 
76  ThreadSafeChecksumCalculator(const ArrayChecksum& ckArray) : checksums(ckArray)
77  {
78  resetChecksumToProcess();
79  }
80 
83  {
84  unique_lock lock(mutex); // shared_lock for read only
85  curCk = 0u;
86  }
87 
95  {
96  unique_lock lock(mutex);
97  if (curCk < checksums.size())
98  return checksums[curCk++];
99  else
100  return nullptr;
101  }
102 
110  static void updateChecksum(ThreadSafeChecksumCalculator* c, const uint8_t* buf, size_t len)
111  {
112  Checksum* chk;
113 
114  while ((chk = c->nextChecksumToProcess()) != nullptr)
115  chk->update(buf, len);
116  }
117 };
118 //---------------------------------------------------------------------------
119 
120 
121 
122 
123 //###########################################################################
124 // ChecksumCalculator members
125 //###########################################################################
126 
127 // Default progress checksum calculator instance.
128 DefaultChecksumCalculatorProgress ChecksumCalculator::defaultChecksumCalculatorProgress;
129 
130 
131 /*
132  * Constructor.
133  */
134 ChecksumCalculator::ChecksumCalculator(ChecksumCalculatorProgress& progressHandler,
135  const unsigned int newNbThreads,
136  const size_t newBufferSize) :
137  progress(progressHandler)
138 {
139  setBufferSize(newBufferSize);
140  setNbThreads(newNbThreads);
141 }
142 //---------------------------------------------------------------------------
143 
144 
145 /*
146  * Gets the size of the buffer to use for reading in the input stream.
147  */
149 {
150  return bufferSize;
151 }
152 //---------------------------------------------------------------------------
153 
154 
155 /*
156  * Sets the size of the buffer to use for reading in the input stream.
157  */
158 size_t ChecksumCalculator::setBufferSize(const size_t bufSize)
159 {
160  size_t old = bufferSize;
161  if ((bufSize >= min_buffer_size) && (bufSize <= max_buffer_size))
162  bufferSize = bufSize;
163  else
165  return old;
166 }
167 //---------------------------------------------------------------------------
168 
169 
170 /*
171  * Returns the number of threads to use to compute checksums.
172  */
174 {
175  return nbThreads;
176 }
177 //---------------------------------------------------------------------------
178 
179 
180 /*
181  * Sets the number of threads to use to compute checksums.
182  */
183 unsigned int ChecksumCalculator::setNbThreads(const unsigned int newNbThreads)
184 {
185  unsigned int old = nbThreads;
186 
187  if (newNbThreads == automatic_thread_number)
188  {
189  unsigned int autoNbThreads = thread::hardware_concurrency();
190  if (autoNbThreads == 0u)
192  else
193  {
194  const int ckfKnownAlgos = ChecksumFactory::getAlgorithmsCount();
195  const unsigned int knownAlgos = (ckfKnownAlgos <= 0) ? 1u : ckfKnownAlgos;
196  nbThreads = min(knownAlgos, autoNbThreads);
197  }
198  }
199  else
200  {
201  const int ckfKnownAlgos = ChecksumFactory::getAlgorithmsCount();
202  const unsigned int knownAlgos = (ckfKnownAlgos <= 0) ? 1u : ckfKnownAlgos;
203  const unsigned int maxNbThreads = max(max_thread_number, knownAlgos);
204  nbThreads = min(newNbThreads, maxNbThreads);
205  }
206 
207  return old;
208 }
209 //---------------------------------------------------------------------------
210 
211 
212 /*
213  * Gets the progress handler used to show the progression.
214  */
216 {
217  return progress;
218 }
219 //---------------------------------------------------------------------------
220 
221 
222 /*
223  * Calculates the checksum from the given stream.
224  */
226  const ChecksumAlgoId id,
227  std::istream& is)
228 {
230  ids.push_back(id);
231  ArrayChecksumValue values;
232  State res = calculate(values, ids, is);
233  if (!values.empty())
234  sumValue = values.front();
235  return res;
236 }
237 //---------------------------------------------------------------------------
238 
239 
240 /*
241  * Calculates the checksums from the given stream.
242  */
244  const ArrayChecksumAlgoId& ids,
245  std::istream& is)
246 {
247  // Check if the input stream is valid.
248  if (!is.good())
249  return State::ReadError;
250 
251  // Retrieve algorithms for computing the sums.
252  ArrayChecksum checksums;
253  checksums.reserve(ids.size());
254  for (const ChecksumAlgoId id : ids)
255  {
256  bool notFound = true;
257  ArrayChecksum::const_iterator it = checksums.cbegin();
258  while (notFound && it != checksums.cend())
259  {
260  if ((*it)->getID() == id)
261  notFound = false;
262  else
263  it++;
264  }
265  if (notFound)
266  {
268  if (c != nullptr)
269  checksums.push_back(c);
270  }
271  }
272 
273  // Check if at least a checksum instance is OK.
274  if (checksums.empty())
275  // Nothing has been added in the checksums array, so don't clear values with
276  // delete.
277  return State::NoValidAlgoId;
278 
279  // Initializes the buffer.
280  const size_t bufSize = getBufferSize();
281  uint8_t* buff = new uint8_t[bufSize];
282  if (buff == nullptr)
283  return State::OutOfMemory;
284 
285  // Calculating the checksum.
286  bool cancelled = false;
287  size_t read;
288  ios::iostate errorState = ios::goodbit;
289  size_t i;
290  const size_t s = checksums.size();
291 
292  // Initialising the thread with the number of available threads.
293  unsigned int nThreads = getNbThreads();
294  if (nThreads > static_cast<unsigned int>(s))
295  nThreads = static_cast<unsigned int>(s);
296  if (nThreads == 0u)
297  nThreads = 1u;
298 
299  if (nThreads > 1u)
300  {
301  vector<thread> threads(nThreads);
302  ThreadSafeChecksumCalculator tscc(checksums);
303 
304  while (!cancelled && !is.eof() && (errorState & ios::goodbit) == ios::goodbit)
305  {
306  read = is.read(reinterpret_cast<char*>(buff), bufSize).gcount();
307  if (read > 0u && read <= bufSize)
308  {
309  for (i = 0u; i < nThreads; i++)
310  threads[i] = thread(ThreadSafeChecksumCalculator::updateChecksum, &tscc, buff, read);
311  for (i = 0u; i < nThreads; i++)
312  threads[i].join();
313  tscc.resetChecksumToProcess();
314  getChecksumProgress().update(read, cancelled);
315  }
316  errorState = is.rdstate();
317  }
318  }
319  else // one algorithm, faster with no threads
320  {
321  while (!cancelled && !is.eof() && (errorState & ios::goodbit) == ios::goodbit)
322  {
323  read = is.read(reinterpret_cast<char*>(buff), bufSize).gcount();
324  if (read > 0u && read <= bufSize)
325  {
326  for (i = 0u; i < s; i++)
327  checksums[i]->update(buff, read);
328  getChecksumProgress().update(read, cancelled);
329  }
330  errorState = is.rdstate();
331  }
332  }
333 
334  // Cleans-up the memory
335  delete[] buff;
336 
337  if (cancelled)
338  {
339  for_each(checksums.begin(), checksums.end(), [](Checksum* c){ delete c; });
340  return State::CancelledByUser;
341  }
342 
343  if (((errorState & ios::goodbit) != ios::goodbit && (errorState & ios::eofbit) != ios::eofbit))
344  {
345  for_each(checksums.begin(), checksums.end(), [](Checksum* c){ delete c; });
346  return State::ReadError;
347  }
348 
349  // Clearing the array of calculated values of the checksums
350  sumValues.clear();
351 
352  // Copying to values in the calculated values of the checksums array
353  for (const Checksum* c : checksums)
354  {
355  sumValues.emplace_back(ChecksumValue(*c));
356  delete c;
357  }
358  //for_each(checksums.begin(), checksums.end(), [](Checksum* c){ delete c; }); // done upper
359 
360  return State::Ok;
361 }
362 //---------------------------------------------------------------------------
363 
364 
365 
366 
367 //###########################################################################
368 // ChecksumFileCalculator members
369 //###########################################################################
370 
371 
372 /*
373  * Constructor.
374  */
376  const unsigned int newNbThreads,
377  const size_t newBufferSize) :
378  ChecksumCalculator(progressHandler,
379  newNbThreads,
380  newBufferSize)
381 {
382 }
383 //---------------------------------------------------------------------------
384 
385 
386 /*
387  * Calculates the checksum from the given file.
388  */
390  const ChecksumAlgoId id,
391  const std::filesystem::path& filepath)
392 {
393  error_code ec;
394  if (!filesystem::exists(filepath, ec))
395  return State::FileNotFound;
396 
397  ifstream is(filepath, ios_base::in | ios_base::binary);
398  if (!is)
399  return State::CantOpenFile;
400 
401  return ChecksumCalculator::calculate(sumValue, id, is);
402 }
403 //---------------------------------------------------------------------------
404 
405 
406 /*
407  * Calculates the checksums from the given file.
408  */
410  const ArrayChecksumAlgoId& ids,
411  const std::filesystem::path& filepath)
412 {
413  error_code ec;
414  if (!filesystem::exists(filepath, ec))
415  return State::FileNotFound;
416 
417  ifstream is(filepath, ios_base::in | ios_base::binary);
418  if (!is)
419  return State::CantOpenFile;
420 
421  return ChecksumCalculator::calculate(sumValues, ids, is);
422 }
423 //---------------------------------------------------------------------------
424 
425 } // namespace libxcks
426 //---------------------------------------------------------------------------
Interface for classes that compute checksums.
vector< Checksum * > ArrayChecksum
Array of pointers on checksums.
Utility classes to compute and verify the checksums.
Classes for enumerate and create all the checksums' algorithms that the application knows.
std::vector< ChecksumValue > ArrayChecksumValue
Array of values of checksum.
Definition: ckvalue.hpp:113
Handles the progression of the process of computing of a checksum.
virtual void update(size_t read, bool &cancelled)=0
Updates the progress of the computing of a checksum.
Calculates a checksum.
size_t setBufferSize(const size_t newBufSize)
Sets the size of the buffer to use for reading in the input stream.
ChecksumCalculatorProgress & progress
The progress handler used to show the progression.
State
States that can be returned by the calculate or the check method.
@ FileNotFound
The file has been not found.
@ Ok
Checksums have been calculated.
@ OutOfMemory
No enough memory left.
@ CantOpenFile
Can't open the stream.
@ NoValidAlgoId
No valid id of algorithm of checksum has been provided.
@ CancelledByUser
User has cancelled the calculation.
@ ReadError
Error while reading the stream.
unsigned int setNbThreads(const unsigned int newNbThreads)
Sets the number of threads to use to compute checksums.
static constexpr unsigned int automatic_thread_number
System dependent value for number of threads for computing checksums.
static constexpr size_t max_buffer_size
Maximal buffer size for reading streams.
size_t bufferSize
The size of the buffer to use for reading the input stream.
static constexpr size_t default_buffer_size
Default buffer size for reading streams.
ChecksumCalculatorProgress & getChecksumProgress() const
Gets the progress handler used to show the progression.
static constexpr unsigned int max_thread_number
Maximal value for number of threads for computing checksums.
static constexpr size_t min_buffer_size
Minimal buffer size for reading streams.
static constexpr unsigned int default_thread_number
Default number of threads for computing checksums if the number of cores of the system cannot be auto...
unsigned int nbThreads
Number of threads to compute checksums.
size_t getBufferSize() const
Gets the size of the buffer to use for reading in the input stream.
State calculate(ChecksumValue &sumValue, const ChecksumAlgoId id, std::istream &is)
Calculates the checksum from the given stream.
unsigned int getNbThreads() const
Returns the number of threads to use to compute checksums.
static Checksum * getNewInstance(const ChecksumAlgoId id)
Gives a pointer on a new instance of the specified checksum or hash identifier.
Definition: ckfactory.cpp:282
static int getAlgorithmsCount()
Returns the number of available checksum or hash algorithms.
Definition: ckfactory.cpp:500
State calculate(ChecksumValue &sumValue, const ChecksumAlgoId id, const std::filesystem::path &filepath)
Calculates the checksum from the given file.
ChecksumFileCalculator(ChecksumCalculatorProgress &progressHandler=defaultChecksumCalculatorProgress, const unsigned int newNbThreads=automatic_thread_number, const size_t newBufferSize=default_file_buffer_size)
Constructor.
Stores the value of a checksum.
Definition: ckvalue.hpp:45
Computes a checksum from a byte stream.
Definition: checksum.hpp:54
virtual void update(const uint8_t *buf, size_t len)=0
Updates the checksum with specified array of bytes.
A thread safe checksums calculator.
ArrayChecksum checksums
Array of checksums.
ThreadSafeChecksumCalculator & operator=(const ThreadSafeChecksumCalculator &)=delete
Delete assignment operator.
size_t curCk
Current checksum to process.
Checksum * nextChecksumToProcess()
Gets the next checksum to process.
ThreadSafeChecksumCalculator()=delete
Deleted default constructor.
ThreadSafeChecksumCalculator(const ThreadSafeChecksumCalculator &)=delete
Deleted copy constructor.
void resetChecksumToProcess()
Resets the current checksum to process.
static void updateChecksum(ThreadSafeChecksumCalculator *c, const uint8_t *buf, size_t len)
Updates the checksums with specified array of bytes.
ThreadSafeChecksumCalculator(const ArrayChecksum &ckArray)
Constructor.
std::vector< ChecksumAlgoId > ArrayChecksumAlgoId
Array of ids of algorithms of checksums.
Definition: types.hpp:92
ChecksumAlgoId
Ids of algorithms of checksums.
Definition: types.hpp:65