• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022, Alliance for Open Media. All rights reserved
3  *
4  * This source code is subject to the terms of the BSD 3-Clause Clear License
5  * and the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear
6  * License was not distributed with this source code in the LICENSE file, you
7  * can obtain it at www.aomedia.org/license/software-license/bsd-3-c-c. If the
8  * Alliance for Open Media Patent License 1.0 was not distributed with this
9  * source code in the PATENTS file, you can obtain it at
10  * www.aomedia.org/license/patent.
11  */
12 #ifndef COMMON_UTILS_MAP_UTILS_H_
13 #define COMMON_UTILS_MAP_UTILS_H_
14 
15 #include "absl/base/no_destructor.h"
16 #include "absl/container/flat_hash_map.h"
17 #include "absl/functional/function_ref.h"
18 #include "absl/log/check.h"
19 #include "absl/log/log.h"
20 #include "absl/status/status.h"
21 #include "absl/status/statusor.h"
22 #include "absl/strings/str_cat.h"
23 #include "absl/strings/string_view.h"
24 
25 namespace iamf_tools {
26 
27 /*!\brief Looks up a key in a map and returns a status or value.
28  *
29  * When lookup fails the error message will contain the `context` string
30  * followed by "= $KEY", where $KEY is the stringified `key`.
31  *
32  * Some mapping have sufficient context in the typenames, for example:
33  *   - Input Map: A map of `PersonName` to `Birthday`.
34  *   - Typename-based context: "`Birthday` for `PersonName`".
35  *   - Output message: "`Birthday` for `PersonName`= John was not found in the
36  *                       map.".
37  *
38  * Some mappings provide insufficient context in the typenames. Or the typenames
39  * would be easily confused. Variable names or phrases should be used as
40  * context:
41  *   - Input Map: A map of `absl::string_view` names to `int` ages.
42  *   - Variable-based context: "`age` for `name`".
43  *   - Phrase-based context: or "Age for name".
44  * Or:
45  *   - Input Map: A map of `proto::Type` to `iamf_tools::Type`.
46  *   - Phrase-based context: "Internal version of proto `Type`".
47  *
48  * \param map Map to search.
49  * \param key Key to search for.
50  * \param context Context to insert into the error message for debugging
51  *        purposes.
52  * \return Associated value if lookup is successful. `absl::NotFoundError()`
53  *         when lookup fails.
54  */
55 template <typename Key, typename Value>
LookupInMap(const absl::flat_hash_map<Key,Value> & map,const Key & key,absl::string_view context)56 absl::StatusOr<Value> LookupInMap(const absl::flat_hash_map<Key, Value>& map,
57                                   const Key& key, absl::string_view context) {
58   auto iter = map.find(key);
59   if (iter != map.end()) [[likely]] {
60     return iter->second;
61   }
62 
63   return absl::NotFoundError(absl::StrCat(
64       context, "= ", key, " was not found in the map.",
65       map.empty() ? " The map is empty. Did initialization fail?" : ""));
66 }
67 
68 /*!\brief Looks up a key in a map and copies the value to the output argument.
69  *
70  * \param map Map to search.
71  * \param key Key to search for.
72  * \param context Context to insert into the error message for debugging
73  *        purposes. Forwared to `LookupInMap` which has detailed documentation
74  *        on usage.
75  * \param value Output argument to write the value to.
76  * \return `absl::OkStatus()` if lookup is successful. `absl::NotFoundError()`
77  *         when lookup fails.
78  */
79 template <typename Key, typename Value>
CopyFromMap(const absl::flat_hash_map<Key,Value> & map,const Key & key,absl::string_view context,Value & value)80 absl::Status CopyFromMap(const absl::flat_hash_map<Key, Value>& map,
81                          const Key& key, absl::string_view context,
82                          Value& value) {
83   const auto& result = LookupInMap(map, key, context);
84   if (result.ok()) [[likely]] {
85     value = *result;
86   }
87   return result.status();
88 }
89 
90 /*!\brief Looks up a key in a map and calls a setter with the value.
91  *
92  * \param map Map to search.
93  * \param key Key to search for.
94  * \param context Context to insert into the error message for debugging
95  *        purposes. Forwared to `LookupInMap` which has detailed documentation
96  *        on usage.
97  * \param setter Function to call with the found value.
98  * \return `absl::OkStatus()` if lookup is successful. `absl::NotFoundError()`
99  *         when lookup fails. Or an error code forwarded from the setter.
100  */
101 template <typename Key, typename Value>
SetFromMap(const absl::flat_hash_map<Key,Value> & map,const Key & key,absl::string_view context,absl::FunctionRef<void (Value)> setter)102 absl::Status SetFromMap(const absl::flat_hash_map<Key, Value>& map,
103                         const Key& key, absl::string_view context,
104                         absl::FunctionRef<void(Value)> setter) {
105   const auto& result = LookupInMap(map, key, context);
106   if (result.ok()) [[likely]] {
107     setter(*result);
108   }
109   return result.status();
110 }
111 
112 /*!\brief Returns a map for static storage from a container of pairs.
113  *
114  * \param pairs Container of pairs to convert to a map. The first value must be
115  *              unique among all pairs.
116  * \return Map suitable for static storage. Or an empty map if the first value
117  *         of a pair is not unique.
118  */
119 template <class InputContainer>
BuildStaticMapFromPairs(const InputContainer & pairs)120 auto BuildStaticMapFromPairs(const InputContainer& pairs) {
121   typedef absl::flat_hash_map<typename InputContainer::value_type::first_type,
122                               typename InputContainer::value_type::second_type>
123       MapFromPairs;
124   return absl::NoDestructor<MapFromPairs>([&]() {
125     MapFromPairs map_from_pairs;
126     for (const auto& [key, value] : pairs) {
127       const auto& [unused_iter, inserted] = map_from_pairs.insert({key, value});
128       if (!inserted) [[unlikely]] {
129         LOG(ERROR) << "Failed building map from pairs. Duplicate key= "
130                    << absl::StrCat(key) << ".";
131         return MapFromPairs{};
132       }
133     }
134     return map_from_pairs;
135   }());
136 }
137 
138 /*!\brief Returns a map for static storage from a container of inverted pairs.
139  *
140  * \param pairs Container of pairs to invert and to convert to a map. The second
141  *              value must be unique among all pairs.
142  * \return Map suitable for static storage. Or an empty map if the second value
143  *         of a pair is not unique.
144  */
145 template <class InputContainer>
BuildStaticMapFromInvertedPairs(const InputContainer & pairs)146 auto BuildStaticMapFromInvertedPairs(const InputContainer& pairs) {
147   typedef absl::flat_hash_map<typename InputContainer::value_type::second_type,
148                               typename InputContainer::value_type::first_type>
149       MapFromInvertedPairs;
150   return absl::NoDestructor<MapFromInvertedPairs>([&]() {
151     MapFromInvertedPairs map_from_inverted_pairs;
152     for (const auto& [value, key] : pairs) {
153       const auto& [unused_iter, inserted] =
154           map_from_inverted_pairs.insert({key, value});
155       if (!inserted) [[unlikely]] {
156         LOG(ERROR) << "Failed building map from pairs. Duplicate key= "
157                    << absl::StrCat(key) << ".";
158         return MapFromInvertedPairs{};
159       }
160     }
161     return map_from_inverted_pairs;
162   }());
163 }
164 
165 }  // namespace iamf_tools
166 
167 #endif  // COMMON_UTILS_MAP_UTILS_H_
168