Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
0cf5628
added FDB compare to CmakeList tool is WIP and currently only loads t…
stefaniereuter Jun 17, 2024
f48b74f
Create FDB from path in tool.cc directly
stefaniereuter Jun 19, 2024
a7baa4d
comparison of mars keys
stefaniereuter Jun 21, 2024
bbfaf0a
can compare FDB but not optimized and exceptiions are missing
stefaniereuter Jul 9, 2024
4f8952f
Bitexact Memcmp, bitexact eccodes, data compare numerical
stefaniereuter Jul 12, 2024
20fbf3c
move from map to vector in md5compare, add usage exampl and command l…
stefaniereuter Jul 16, 2024
d881e5b
changed the way the mars message keys are compared,added option to ig…
stefaniereuter Jul 23, 2024
86f64ff
Adding option to ignore and select grib keys
stefaniereuter Jul 25, 2024
d36ba97
added gribcompare eccodes_detail to allow direct use of detailed check
stefaniereuter Jul 25, 2024
b8b8491
removed annoying debug output
stefaniereuter Jul 26, 2024
8a63c45
Added eccodes hack for buffer size adaption
stefaniereuter Jul 27, 2024
7b7de8e
remove eccodes 'hack' and use grib_string_length instead
stefaniereuter Jul 29, 2024
36f7f31
deleted buffer before return to avoid memleak
stefaniereuter Jul 31, 2024
81d0033
Bugfixes after rebase
stefaniereuter Aug 12, 2025
80e58d7
CLI improvments
stefaniereuter Aug 19, 2025
9eaf3aa
Adding comparison tests
stefaniereuter Aug 19, 2025
9bafd3b
Feature, single fdb divergent experiment
stefaniereuter Aug 24, 2025
a1533d2
Add more tests, bugfix grib comparison header only
stefaniereuter Aug 25, 2025
0bbd8ab
Refactor Mars key compare
stefaniereuter Aug 26, 2025
b15b50c
Mars refactor cleanup, Test fix
stefaniereuter Aug 26, 2025
41ab0fb
refactoring start
stefaniereuter Jan 8, 2026
74d19cb
formatting
stefaniereuter Jan 14, 2026
c52b2bf
Misc naminging and small structure fixes
pgeier Jan 16, 2026
e42976e
Rename and delete unused files
pgeier Jan 16, 2026
9b6e1f6
Use metkit/codes/api instead of directly using eccodes\.h
pgeier Jan 20, 2026
b94f174
Clean up compare/grib structure
pgeier Jan 21, 2026
9d3c367
Rework handling difference in mars requests
pgeier Jan 22, 2026
b59ff8e
Small restructures, remove comments
pgeier Jan 27, 2026
eaeadf2
Remove IComporator und move directories
pgeier Jan 27, 2026
78721dc
Restructure tests
pgeier Jan 28, 2026
09e5647
Fix fdb-compare tests and add FP data checks
pgeier Jan 28, 2026
b643bc8
Add licensce to .h and .cc files
pgeier Jan 28, 2026
1b031b4
Add more documentation; remove grib/Message
pgeier Jan 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/fdb5/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,27 @@ ecbuild_add_executable(
remote/fdb-server.cc
LIBS fdb5 )

ecbuild_add_executable( TARGET fdb-compare
CONDITION HAVE_FDB_BUILD_TOOLS
SOURCES tools/compare/fdb-compare.cc
tools/compare/common/ComparisonMap.cc
tools/compare/common/DataMap.cc
tools/compare/common/Scope.cc
tools/compare/common/Util.cc
tools/compare/mars/Compare.cc
tools/compare/grib/Compare.cc
tools/compare/grib/CompareKeys.cc
tools/compare/grib/CompareHash.cc
tools/compare/grib/CompareDatasection.cc
tools/compare/grib/CompareBitwise.cc
tools/compare/grib/Utils.cc
INCLUDES ${ECCODES_INCLUDE_DIRS} # Please don't remove me, I am needed
tools/compare/grib/
tools/compare/mars/
tools/compare/common/
tools/compare/
LIBS fdb5 )

if ( HAVE_FDB_BUILD_TOOLS )
target_sources( fdb-lock PRIVATE tools/FDBLock.cc tools/FDBLock.h )
target_sources( fdb-unlock PRIVATE tools/FDBLock.cc tools/FDBLock.h )
Expand Down
47 changes: 47 additions & 0 deletions src/fdb5/tools/compare/common/ComparisonMap.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* (C) Copyright 2025- ECMWF.
*
* This software is licensed under the terms of the Apache Licence Version 2.0
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
* In applying this licence, ECMWF does not waive the privileges and immunities
* granted to it by virtue of its status as an intergovernmental organisation nor
* does it submit to any jurisdiction.
*/
#include "common/ComparisonMap.h"


namespace compare {

bool isSubset(const KeyValueMap& a, const KeyValueMap& b) {
for (const auto& kv : a) {
auto it = b.find(kv.first);
if (it == b.end() || it->second != kv.second)
return false;
}
return true;
}


DataIndex assembleCompareMap(fdb5::FDB& fdb, const fdb5::FDBToolRequest& req, const KeyValueMap& ignore) {
DataIndex out;

auto list = fdb.list(req);
fdb5::ListElement elem;

std::cout << req << std::endl;
std::cout << ignore << std::endl;
while (list.next(elem)) {
KeyValueMap km;
for (const auto& bit : elem.keys()) {
auto d = bit.keyDict();
km.insert(d.begin(), d.end());
}
if (ignore.empty() || !isSubset(ignore, km)) {
out.emplace(km, DataLocation{elem.location().uri().path(), static_cast<long long>(elem.location().offset()),
static_cast<long long>(elem.location().length())});
}
}
std::cout << "[LOG] FDB request: " << req << " resulted in " << out.size() << " entries.\n";
return out;
}
} // namespace compare
25 changes: 25 additions & 0 deletions src/fdb5/tools/compare/common/ComparisonMap.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* (C) Copyright 2025- ECMWF.
*
* This software is licensed under the terms of the Apache Licence Version 2.0
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
* In applying this licence, ECMWF does not waive the privileges and immunities
* granted to it by virtue of its status as an intergovernmental organisation nor
* does it submit to any jurisdiction.
*/
#pragma once
#include <fdb5/api/FDB.h>
#include <fdb5/api/helpers/FDBToolRequest.h>
#include "DataMap.h"

namespace compare {

/// Runs a request on a fdb to create a list of filtered entries to compared on.
///
/// @param fdb FDB on which to operate on
/// @param req Request which performs a selection on the fdb
/// @param ignore Map of key-value pairs to ignore
/// @return filtered DataIndex
DataIndex assembleCompareMap(fdb5::FDB& fdb, const fdb5::FDBToolRequest& req, const KeyValueMap& ignore);

} // namespace compare
137 changes: 137 additions & 0 deletions src/fdb5/tools/compare/common/DataMap.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/*
* (C) Copyright 2025- ECMWF.
*
* This software is licensed under the terms of the Apache Licence Version 2.0
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
* In applying this licence, ECMWF does not waive the privileges and immunities
* granted to it by virtue of its status as an intergovernmental organisation nor
* does it submit to any jurisdiction.
*/
#include "DataMap.h"

#include <sstream>

namespace compare {

std::ostream& operator<<(std::ostream& os, const KeyValueMap& km) {
os << "{";
for (const auto& [k, v] : km) {
os << k << "=" << v << ", ";
}
os << "}";
return os;
}

std::ostream& operator<<(std::ostream& os, const KeyDiffMap& km) {
os << "{";
for (const auto& [k, v] : km) {
os << k << "=" << "(" << (v.first ? *v.first : "<MISSING>") << ", " << (v.second ? *v.second : "<MISSING>")
<< ")" << ", ";
}
os << "}";
return os;
}

std::ostream& operator<<(std::ostream& os, const KeySet& km) {
os << "{";
for (const auto& k : km) {
os << k << ", ";
}
os << "}";
return os;
}

std::ostream& operator<<(std::ostream& os, const DataLocation& loc) {
return os << "(" << loc.path << ", " << loc.offset << ", " << loc.length << ")";
}

std::ostream& operator<<(std::ostream& os, const DataIndex& idx) {
for (const auto& [km, loc] : idx) {
os << "Key: " << km << " -> Value: " << loc << "\n";
}
return os;
}


void parseKeyValues(KeyValueMap& container, const std::string& keyValueStr) {
std::istringstream stream(keyValueStr);
std::string entry;

while (std::getline(stream, entry, ',')) {
std::istringstream pairStream(entry);
std::string key, value;
if (std::getline(pairStream, key, '=') && std::getline(pairStream, value)) {
container[key] = value; // Update or insert key-value pair
}
else {
std::cerr << "Invalid key-value pair: " << entry << std::endl;
}
}
}

KeyValueMap parseKeyValues(const std::string& keyValueStr) {
KeyValueMap container;
parseKeyValues(container, keyValueStr);
return container;
}

void parseKeySet(KeySet& container, const std::string& keyStr) {
std::istringstream stream(keyStr);
std::string entry;

while (std::getline(stream, entry, ',')) {
container.insert(entry); // Insert the whole entry as a single string
}
}

KeySet parseKeySet(const std::string& keyStr) {
KeySet container;
parseKeySet(container, keyStr);
return container;
}

compare::KeyDiffMap requestDiff(const KeyValueMap& l, const KeyValueMap& r) {
compare::KeyDiffMap res;
for (const auto& [lk, lv] : l) {
auto search = r.find(lk);
if (search != r.end()) {
if (search->second != lv) {
res.insert({lk, {lv, search->second}});
}
}
else {
res.insert({lk, {lv, {}}});
}
}

for (const auto& [rk, rv] : r) {
auto search = l.find(rk);
if (search == r.end()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug here. search comes from l.find but is being compared against r.end. Iterators are not comparable between containers.

res.insert({rk, {{}, rv}});
}
}
return res;
}


KeyValueMap applyKeyDiff(KeyValueMap k, const KeyDiffMap& diff, bool swapDiff) {
for (const auto& [field, val_pair] : diff) {
const auto& val = swapDiff ? val_pair.first : val_pair.second;
if (val) {
auto it = k.find(field);
if (it != k.end()) {
it->second = *val; // replace value
}
else {
k.insert({field, *val});
}
}
else {
// Delete
k.erase(field);
}
}
return k; // return modified copy
};

} // namespace compare
81 changes: 81 additions & 0 deletions src/fdb5/tools/compare/common/DataMap.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* (C) Copyright 2025- ECMWF.
*
* This software is licensed under the terms of the Apache Licence Version 2.0
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
* In applying this licence, ECMWF does not waive the privileges and immunities
* granted to it by virtue of its status as an intergovernmental organisation nor
* does it submit to any jurisdiction.
*/
#pragma once
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is called DataMap - but there is no class (nor anything else) called DataMap in it.

#include <functional>
#include <iostream>
#include <map>
#include <optional>
#include <string>
#include <unordered_map>
#include <unordered_set>

namespace compare {

struct DataLocation {
std::string path;
long long offset = 0;
long long length = 0;
};

using KeyValueMap = std::map<std::string, std::string>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the namespace, I think that these names are too generic. They would be appropriate inside (say) a templated class. But here...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is especially true given that you have both DataMap and ComparisonMap headers in the project. What does "MapHash" or "KeyValueMap" belong to in that context?

using ValueDiff = std::pair<std::optional<std::string>, std::optional<std::string>>;
using KeyDiffMap = std::map<std::string, ValueDiff>;

using KeySet = std::unordered_set<std::string>;

struct MapHash {
size_t operator()(const KeyValueMap& m) const noexcept {
size_t h = 0;
std::hash<std::string> hs;
for (const auto& kv : m) {
h ^= (hs(kv.first) * 1315423911u) ^ hs(kv.second);
}
return h;
}
};

struct MapEqual {
bool operator()(const KeyValueMap& a, const KeyValueMap& b) const noexcept { return a == b; }
};

using DataIndex = std::unordered_map<KeyValueMap, DataLocation, MapHash, MapEqual>;

/// Print a KeyValueMap
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unclear why these warrant a comment, when they are absolutely clear what they do, but there is no commentary/docs about what the purpose of these classes is.

std::ostream& operator<<(std::ostream& os, const KeyValueMap& km);

/// Print a KeyDiffMap
std::ostream& operator<<(std::ostream& os, const KeyDiffMap& km);

/// Print a KeySet
std::ostream& operator<<(std::ostream& os, const KeySet& km);

/// Print a DataLocation
std::ostream& operator<<(std::ostream& os, const DataLocation& loc);

/// Print a DataIndex
std::ostream& operator<<(std::ostream& os, const DataIndex& idx);


void parseKeyValues(KeyValueMap& container, const std::string& keyValueStr);
KeyValueMap parseKeyValues(const std::string& keyValueStr);

void parseKeySet(KeySet& container, const std::string& keyValueStr);
KeySet parseKeySet(const std::string& keyValueStr);

compare::KeyDiffMap requestDiff(const KeyValueMap& l, const KeyValueMap& r);

/// @param km Map of keys with values
/// @param diff Map of keys with differences
/// @param swapPair Optional - If false the second pair of the difference is put in the resulting request.
// If true the first one is used.
/// @return New KeyValueMap with differences applied
KeyValueMap applyKeyDiff(KeyValueMap km, const KeyDiffMap& diff, bool swapPair = false);

} // namespace compare
Loading
Loading