49#if !defined(_WIN32) && \
50 !(__has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__))
58#if !defined(__has_feature) || !__has_feature(thread_sanitizer)
62using std::chrono::steady_clock;
76using std::istreambuf_iterator;
79using std::ostringstream;
82#if !defined(__has_feature) || !__has_feature(thread_sanitizer)
99 void anchor()
const override;
104 explicit RAWSPEED_UNLIKELY_FUNCTION RAWSPEED_NOINLINE
115 mutable std::chrono::steady_clock::time_point
start =
116 std::chrono::steady_clock::now();
118 auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
119 std::chrono::steady_clock::now() -
start)
121 start = std::chrono::steady_clock::now();
132 vector<md5::MD5Hasher::state_type> line_hashes(img.
height());
134 for (
int j = 0; j < img.
height(); j++) {
135 line_hashes[j] = md5::md5_hash(
reinterpret_cast<const uint8_t*
>(&img(j, 0)),
139 auto ret = md5::md5_hash(
reinterpret_cast<const uint8_t*
>(&line_hashes[0]),
140 sizeof(line_hashes[0]) * line_hashes.size());
145#pragma GCC diagnostic push
146#pragma GCC diagnostic ignored "-Wpragmas"
147#pragma GCC diagnostic ignored "-Wunknown-warning-option"
148#pragma GCC diagnostic ignored "-Wunknown-pragmas"
149#pragma GCC diagnostic ignored "-Wframe-larger-than="
150#pragma GCC diagnostic ignored "-Wstack-usage="
154 std::array<char, 1024> line;
164std::string img_hash(
const RawImage& r,
bool noSamples) {
168 APPEND(&oss,
"camera support status is unknown due to lack of samples\n");
179 APPEND(&oss,
"blackLevel: %d\n", r->
blackLevel);
181 APPEND(&oss,
"whitePoint: ");
183 APPEND(&oss,
"unknown");
188 APPEND(&oss,
"blackLevelSeparate: ");
190 APPEND(&oss,
"none");
195 blackLevelSeparate1D && blackLevelSeparate1D->size() != 0) {
196 for (
auto l : *blackLevelSeparate1D)
197 APPEND(&oss,
" %d", l);
202 APPEND(&oss,
"wbCoeffs:");
204 APPEND(&oss,
" (none)");
211 APPEND(&oss,
"colorMatrix:");
213 APPEND(&oss,
" (none)");
216 APPEND(&oss,
" %i/%i", e.num, e.den);
220 APPEND(&oss,
"isCFA: %d\n", r->
isCFA);
221 APPEND(&oss,
"cfa: %s\n", r->
cfa.
asString().c_str());
223 APPEND(&oss,
"bpp: %u\n", r->
getBpp());
224 APPEND(&oss,
"cpp: %u\n", r->
getCpp());
225 APPEND(&oss,
"dataType: %u\n",
static_cast<unsigned>(r->
getDataType()));
228 APPEND(&
oss,
"dimUncropped: %dx%d\n", dimUncropped.
x, dimUncropped.
y);
229 APPEND(&
oss,
"dimCropped: %dx%d\n", r->
dim.
x, r->
dim.
y);
239 APPEND(&
oss,
"blackAreas: ");
241 APPEND(&
oss,
"%d:%ux%u, ", ba.isVertical, ba.offset, ba.size);
247 APPEND(&
oss,
"badPixelPositions: ");
257 APPEND(&
oss,
"md5sum of per-line md5sums: %s\n",
260 for (
const auto errors = r->
getErrors();
const std::string& e : errors)
261 APPEND(&
oss,
"WARNING: [rawspeed] %s\n", e.c_str());
266auto fclose = [](std::FILE* fp) { std::fclose(fp); };
273 int width = dimUncropped.
x;
274 int height = dimUncropped.
y;
278 fprintf(f.get(),
"%s\n%d %d\n65535\n",
format.c_str(), width, height);
286 for (
int y = 0;
y < height; ++
y) {
288 for (
int x = 0;
x < width; ++
x)
291 fwrite(&img(
y, 0),
sizeof(
decltype(img)::value_type), width, f.get());
301 int width = dimUncropped.
x;
302 int height = dimUncropped.
y;
306 int len = fprintf(f.get(),
"%s\n%d %d\n-1.0",
format.c_str(), width, height);
311 static const auto dataAlignment = 16;
314 const int realLen = len + 1;
316 const auto paddedLen =
322 const int padding = paddedLen - realLen;
327 len += fprintf(f.get(),
"%0*i\n", padding, 0);
334 assert(ftell(f.get()) == len);
341 for (
int y = 0;
y < height; ++
y) {
343 const int row_in = height - 1 -
y;
346 for (
int x = 0;
x < width; ++
x)
347 img(row_in,
x) = std::bit_cast<float>(
getU32LE(&img(row_in,
x)));
349 fwrite(&img(row_in, 0),
sizeof(
decltype(img)::value_type), width, f.get());
364 __builtin_unreachable();
370 const std::string hashfile(filename +
".hash");
375 ifstream hf(hashfile);
377#if !defined(__has_feature) || !__has_feature(thread_sanitizer)
379#pragma omp critical(io)
381 cout << left << setw(55) << filename <<
": hash "
382 << (o.
create ?
"exists" :
"missing") <<
", skipping" <<
'\n';
388#if !defined(__has_feature) || !__has_feature(thread_sanitizer)
390#pragma omp critical(io)
392 cout << left << setw(55) << filename <<
": starting decoding ... " <<
'\n';
395#if !defined(_WIN32) && \
396 !(__has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__))
403 std::unique_ptr<std::vector<
408 std::tie(storage,
buf) = reader.readFile();
417 decoder->failOnUnknown =
false;
419 bool noSamples = decoder->noSamples;
421 decoder->decodeRaw();
427#if !defined(__has_feature) || !__has_feature(thread_sanitizer)
429#pragma omp critical(io)
431 cout << left << setw(55) << filename <<
": " << internal << setw(3)
432 <<
buf.getSize() / 1000000 <<
" MB / " << setw(4) << time <<
" ms"
438 ofstream f(hashfile);
439 f << img_hash(raw, noSamples);
444 std::string h = img_hash(raw, noSamples);
448 if (!hf.good() && o.
force)
451 std::string truth((istreambuf_iterator<char>(hf)),
452 istreambuf_iterator<char>());
454 ofstream f(filename +
".hash.failed");
465#pragma GCC diagnostic pop
467int results(
const map<std::string, std::string, std::less<>>& failedTests,
469 if (failedTests.empty()) {
470 cout <<
"All good, ";
472 cout <<
"no tests failed!" <<
'\n';
474 cout <<
"all hashes created!" <<
'\n';
478 cerr <<
"WARNING: the following " << failedTests.size()
479 <<
" tests have failed:\n";
481 bool rstestlog =
false;
482 for (
const auto& [test, msg] : failedTests) {
485 const std::string oldhash(test +
".hash");
486 const std::string newhash(oldhash +
".failed");
489 if (!(ifstream(oldhash).good() || ifstream(newhash).good()))
495 std::string cmd(R
"(diff -N -u0 ")");
499 cmd += R"(" >> rstest.log)";
501 if (system(cmd.c_str())) {
507 cerr <<
"See rstest.log for details.\n";
513 cout <<
"usage: " << progname << R
"(
515 [-c] for each file: decode, compute hash and store it.
516 If hash exists, it does not recompute it, unless option -f is set!
517 [-f] if -c is set, then it will final the existing hashes.
518 If -c is not set, and the hash does not exist, then just decode,
519 but do not write the hash!
520 [-d] store decoded image as PPM
521 <FILE[S]> the file[s] to work on.
523 With no options given, each raw with an accompanying hash will be decoded
524 and compared (unless option -f is set!) to the existing hash. A summary of
525 all errors/failed hash comparisons will be reported at the end.
527 Suggested workflow for easy regression testing:
528 1. remove all .hash files and build 'trusted' version of this program
529 2. run with option '-c' -> creates .hash for all supported files
530 3. build new version to test for regressions
531 4. run with no option -> checks files with existing .hash
532 If the second run shows no errors, you have no regressions,
533 otherwise, the diff between hashes is appended to rstest.log
542using rawspeed::rstest::options;
543using rawspeed::rstest::process;
544using rawspeed::rstest::results;
545using rawspeed::rstest::usage;
547int main(
int argc_,
char** argv_) {
550 int remaining_argc = argv.size();
552 auto hasFlag = [&remaining_argc, argv](std::string_view flag) {
554 for (
int i = 1; i < argv.size(); ++i) {
555 if (!argv(i) || argv(i) != flag)
564 if (1 == argv.size() || hasFlag(
"-h"))
565 return usage(argv(0));
568 o.create = hasFlag(
"-c");
569 o.force = hasFlag(
"-f");
570 o.dump = hasFlag(
"-d");
579 map<std::string, std::string, std::less<>> failedTests;
581#pragma omp parallel for default(none) firstprivate(argv, o) shared(metadata) \
582 shared(cerr, failedTests) schedule(dynamic, 1) \
583 reduction(+ : time) if (remaining_argc > 2)
585 for (
int i = 1; i < argv.size(); ++i) {
591 time += process(argv(i), &
metadata, o);
592 }
catch (
const rawspeed::rstest::RstestHashMismatch& e) {
598#pragma omp critical(io)
601 std::string msg = std::string(argv(i)) +
" failed: " + e.what();
602#if !defined(__has_feature) || !__has_feature(thread_sanitizer)
605 failedTests.try_emplace(argv(i), msg);
609 __builtin_unreachable();
613 cout <<
"Total decoding time: "
617 return results(failedTests, o);
assert(dim.area() >=area)
int RAWSPEED_READONLY height() const
int RAWSPEED_READONLY width() const
std::string asString() const
uint32_t getDcrawFilter() const
std::vector< std::string > && getErrors() REQUIRES(!mutex)
Buffer getAsBuffer() const
rawspeed::RawImageType getDataType() const
uint32_t RAWSPEED_READONLY getCpp() const
Optional< Array2DRef< int > > blackLevelSeparate
iPoint2D RAWSPEED_READONLY getCropOffset() const
iPoint2D RAWSPEED_READONLY getUncroppedDim() const
Optional< int > whitePoint
Array2DRef< std::byte > getByteDataAsUncroppedArray2DRef() noexcept
Array2DRef< float > getF32DataAsUncroppedArray2DRef() noexcept
std::vector< BlackArea > blackAreas
Array2DRef< uint16_t > getU16DataAsUncroppedArray2DRef() noexcept
uint32_t RAWSPEED_READONLY getBpp() const
virtual std::unique_ptr< RawDecoder > getDecoder(const CameraMetaData *meta=nullptr)
RAWSPEED_UNLIKELY_FUNCTION RAWSPEED_NOINLINE RawspeedException(const char *msg)
std::array< uint32_t, 4 > state_type
void anchor() const override
RAWSPEED_UNLIKELY_FUNCTION RAWSPEED_NOINLINE RstestHashMismatch(const char *msg, int64_t time_)
static const rawspeed::CameraMetaData metadata
__attribute__((always_inline)) inline void MD5 std::string hash_to_string(const MD5Hasher::state_type &hash) noexcept
int usage(const char *progname)
int results(const map< std::string, std::string, std::less<> > &failedTests, const options &o)
void writeImage(const RawImage &raw, const std::string &fn)
int64_t process(const std::string &filename, const CameraMetaData *metadata, const options &o)
md5::MD5Hasher::state_type imgDataHash(const RawImage &raw)
std::unique_ptr< FILE, decltype(fclose)> file_ptr
void writePFM(const RawImage &raw, const std::string &fn)
rawspeed::md5::MD5Hasher::state_type hash_of_line_hashes
void const char va_list args
void writePPM(const RawImage &raw, const std::string &fn)
constexpr uint64_t RAWSPEED_READNONE roundUp(uint64_t value, uint64_t multiple)
constexpr RAWSPEED_READNONE Ttgt implicit_cast(Tsrc value)
vsnprintf(buf.data(), sizeof(buf), fmt, val)
uint16_t getU16BE(const void *data)
__attribute__((noinline)) __attribute__((visibility("default"))) JPEGStuffedByteStreamGenerator
constexpr RAWSPEED_READNONE bool isAligned(T value, size_t multiple)
void RAWSPEED_UNLIKELY_FUNCTION RAWSPEED_NOINLINE static char buf[bufSize]
Array1DRef(T *data_, int numElts_) -> Array1DRef< T >
uint32_t getU32LE(const void *data)
int64_t operator()() const
std::chrono::steady_clock::time_point start