21 : uid_root_{std::move(uid_root)} {}
24 std::shared_lock lock(
other.mutex_);
32 std::unique_lock lock(
other.mutex_);
33 uid_root_ = std::move(
other.uid_root_);
34 original_to_anon_ = std::move(
other.original_to_anon_);
35 anon_to_original_ = std::move(
other.anon_to_original_);
36 uid_counter_.store(
other.uid_counter_.load());
41 std::scoped_lock lock(mutex_,
other.mutex_);
42 uid_root_ =
other.uid_root_;
43 original_to_anon_ =
other.original_to_anon_;
44 anon_to_original_ =
other.anon_to_original_;
45 uid_counter_.store(
other.uid_counter_.load());
52 std::scoped_lock lock(mutex_,
other.mutex_);
53 uid_root_ = std::move(
other.uid_root_);
54 original_to_anon_ = std::move(
other.original_to_anon_);
55 anon_to_original_ = std::move(
other.anon_to_original_);
56 uid_counter_.store(
other.uid_counter_.load());
65 std::shared_lock lock(mutex_);
66 auto it = original_to_anon_.find(original_uid);
67 if (it != original_to_anon_.end()) {
73 std::unique_lock lock(mutex_);
76 auto it = original_to_anon_.find(original_uid);
77 if (it != original_to_anon_.end()) {
82 auto new_uid = generate_uid();
83 std::string original_str{original_uid};
85 original_to_anon_[original_str] = new_uid;
86 anon_to_original_[new_uid] = original_str;
92 -> std::optional<std::string> {
93 std::shared_lock lock(mutex_);
94 auto it = original_to_anon_.find(original_uid);
95 if (it != original_to_anon_.end()) {
102 -> std::optional<std::string> {
103 std::shared_lock lock(mutex_);
104 auto it = anon_to_original_.find(anonymized_uid);
105 if (it != anon_to_original_.end()) {
112 std::string_view original_uid,
113 std::string_view anonymized_uid
114) -> kcenon::common::VoidResult {
115 std::unique_lock lock(mutex_);
117 auto existing = original_to_anon_.find(original_uid);
118 if (existing != original_to_anon_.end()) {
119 if (existing->second != anonymized_uid) {
120 return kcenon::common::make_error<std::monostate>(
122 "Original UID already mapped to different value",
126 return kcenon::common::ok();
129 std::string original_str{original_uid};
130 std::string anon_str{anonymized_uid};
132 original_to_anon_[original_str] = anon_str;
133 anon_to_original_[anon_str] = original_str;
135 return kcenon::common::ok();
139 std::shared_lock lock(mutex_);
140 return original_to_anon_.contains(original_uid);
144 std::shared_lock lock(
mutex_);
149 std::shared_lock lock(
mutex_);
154 std::unique_lock lock(
mutex_);
160 std::unique_lock lock(mutex_);
162 auto it = original_to_anon_.find(original_uid);
163 if (it == original_to_anon_.end()) {
167 auto anon_uid = it->second;
168 original_to_anon_.erase(it);
169 anon_to_original_.erase(anon_uid);
175 std::shared_lock lock(
mutex_);
177 std::ostringstream oss;
179 oss <<
" \"uid_root\": \"" <<
uid_root_ <<
"\",\n";
180 oss <<
" \"mappings\": [\n";
187 oss <<
" {\"original\": \"" << original
188 <<
"\", \"anonymized\": \"" << anon <<
"\"}";
199 -> kcenon::common::VoidResult {
202 return kcenon::common::make_error<std::monostate>(
203 1,
"JSON parsing not yet implemented",
"uid_mapping"
208 std::shared_lock other_lock(
other.mutex_);
209 std::unique_lock this_lock(mutex_);
211 std::size_t added = 0;
212 for (
const auto& [original, anon] :
other.original_to_anon_) {
213 if (!original_to_anon_.contains(original)) {
214 original_to_anon_[original] = anon;
215 anon_to_original_[anon] = original;
224 std::unique_lock lock(
mutex_);
229 std::shared_lock lock(
mutex_);
235 auto now = std::chrono::system_clock::now();
236 auto timestamp = std::chrono::duration_cast<std::chrono::microseconds>(
237 now.time_since_epoch()
243 std::random_device rd;
244 std::mt19937 gen(rd());
245 std::uniform_int_distribution<std::uint32_t> dist(0, 999999);
246 auto random_part = dist(gen);
248 std::ostringstream oss;
249 oss <<
uid_root_ <<
"." << timestamp <<
"." << counter <<
"." << random_part;
std::map< std::string, std::string, std::less<> > anon_to_original_
Reverse mapping: anonymized -> original.
auto has_mapping(std::string_view original_uid) const -> bool
Check if an original UID has been mapped.
auto generate_uid() const -> std::string
Generate a new unique UID.
void clear()
Clear all mappings.
auto to_json() const -> std::string
Export mappings to JSON format.
std::map< std::string, std::string, std::less<> > original_to_anon_
Forward mapping: original -> anonymized.
auto get_original(std::string_view anonymized_uid) const -> std::optional< std::string >
Get original UID from anonymized UID (reverse lookup)
std::string uid_root_
UID root for generated UIDs (default: pacs_system root)
auto get_anonymized(std::string_view original_uid) const -> std::optional< std::string >
Get existing mapping without creating new one.
void set_uid_root(std::string root)
Set the UID root for generated UIDs.
auto operator=(const uid_mapping &other) -> uid_mapping &
Copy assignment.
auto empty() const -> bool
Check if the mapping is empty.
auto get_uid_root() const -> std::string
Get the current UID root.
auto get_or_create(std::string_view original_uid) -> kcenon::common::Result< std::string >
Get existing mapping or create new one.
std::shared_mutex mutex_
Mutex for thread-safe access.
uid_mapping()=default
Default constructor - creates empty mapping.
auto size() const -> std::size_t
Get the number of mappings.
auto merge(const uid_mapping &other) -> std::size_t
Merge mappings from another uid_mapping.
auto add_mapping(std::string_view original_uid, std::string_view anonymized_uid) -> kcenon::common::VoidResult
Add a specific mapping.
std::atomic< std::uint64_t > uid_counter_
Counter for UID generation.
auto from_json(std::string_view json) -> kcenon::common::VoidResult
Import mappings from JSON format.
auto remove(std::string_view original_uid) -> bool
Remove a specific mapping.
UID mapping for consistent de-identification across studies.