2020#include < google/protobuf/descriptor.h>
2121#include < google/protobuf/message.h>
2222#include < google/type/date.pb.h>
23+ #include < algorithm>
2324
2425namespace google {
2526namespace cloud {
@@ -68,6 +69,12 @@ bool StructEqual( // NOLINT(misc-no-recursion)
6869 google::bigtable::v2::Type const & pt2,
6970 google::bigtable::v2::Value const & pv2);
7071
72+ bool MapEqual ( // NOLINT(misc-no-recursion)
73+ google::bigtable::v2::Type const & pt1,
74+ google::bigtable::v2::Value const & pv1,
75+ google::bigtable::v2::Type const & pt2,
76+ google::bigtable::v2::Value const & pv2);
77+
7178// Compares two sets of Type and Value protos for equality. This method calls
7279// itself recursively to compare subtypes and subvalues.
7380bool Equal (google::bigtable::v2::Type const & pt1, // NOLINT(misc-no-recursion)
@@ -106,6 +113,9 @@ bool Equal(google::bigtable::v2::Type const& pt1, // NOLINT(misc-no-recursion)
106113 if (pt1.has_struct_type ()) {
107114 return StructEqual (pt1, pv1, pt2, pv2);
108115 }
116+ if (pt1.has_map_type ()) {
117+ return MapEqual (pt1, pv1, pt2, pv2);
118+ }
109119 return false ;
110120}
111121
@@ -158,6 +168,44 @@ bool StructEqual( // NOLINT(misc-no-recursion)
158168 return true ;
159169}
160170
171+ // Compares two sets of Type and Value protos that represent a MAP for
172+ // equality.
173+ bool MapEqual ( // NOLINT(misc-no-recursion)
174+ google::bigtable::v2::Type const & pt1,
175+ google::bigtable::v2::Value const & pv1,
176+ google::bigtable::v2::Type const & pt2,
177+ google::bigtable::v2::Value const & pv2) {
178+ auto const & kt1 = pt1.map_type ().key_type ();
179+ auto const & kt2 = pt2.map_type ().key_type ();
180+ auto const & vt1 = pt1.map_type ().value_type ();
181+ auto const & vt2 = pt2.map_type ().value_type ();
182+ if (kt1.kind_case () != kt2.kind_case ()) return false ;
183+ if (vt1.kind_case () != vt2.kind_case ()) return false ;
184+
185+ auto const & mv1 = pv1.array_value ().values ();
186+ auto const & mv2 = pv2.array_value ().values ();
187+ if (mv1.size () != mv2.size ()) return false ;
188+ // We double-check that all subarrays are of size 2;
189+ for (int i = 0 ; i < mv1.size (); ++i) {
190+ auto const & f1 = mv1.Get (i);
191+ auto const & f2 = mv2.Get (i);
192+ if (f1.array_value ().values_size () != 2 ) return false ;
193+ if (f2.array_value ().values_size () != 2 ) return false ;
194+ }
195+ // NOLINTNEXTLINE(misc-no-recursion)
196+ auto comparison_function = [&kt1, &kt2, &vt1, &vt2](
197+ google::bigtable::v2::Value const & f1,
198+ google::bigtable::v2::Value const & f2) {
199+ auto const & k1 = f1.array_value ().values (0 );
200+ auto const & k2 = f2.array_value ().values (0 );
201+ auto const & v1 = f1.array_value ().values (1 );
202+ auto const & v2 = f2.array_value ().values (1 );
203+ return Equal (kt1, k1, kt2, k2) && Equal (vt1, v1, vt2, v2);
204+ };
205+ return std::is_permutation (mv1.begin (), mv1.end (), mv2.begin (), mv2.end (),
206+ comparison_function);
207+ }
208+
161209// From the proto description, `NULL` values are represented by having a kind
162210// equal to KIND_NOT_SET
163211bool IsNullValue (google::bigtable::v2::Value const & value) {
@@ -180,6 +228,11 @@ std::ostream& EscapeQuotes(std::ostream& os, std::string const& s) {
180228// format themselves differently in each case.
181229enum class StreamMode { kScalar , kAggregate };
182230
231+ std::ostream& MapStreamHelper (std::ostream& os, // NOLINT(misc-no-recursion)
232+ google::bigtable::v2::Value const & v,
233+ google::bigtable::v2::Type const & t,
234+ StreamMode mode);
235+
183236std::ostream& StreamHelper (std::ostream& os, // NOLINT(misc-no-recursion)
184237 google::bigtable::v2::Value const & v,
185238 google::bigtable::v2::Type const & t,
@@ -188,17 +241,16 @@ std::ostream& StreamHelper(std::ostream& os, // NOLINT(misc-no-recursion)
188241 return os << " NULL" ;
189242 }
190243
191- if (t.kind_case () == google::bigtable::v2::Type:: kBoolType ) {
244+ if (t.has_bool_type () ) {
192245 return os << v.bool_value ();
193246 }
194- if (t.kind_case () == google::bigtable::v2::Type:: kInt64Type ) {
247+ if (t.has_int64_type () ) {
195248 return os << v.int_value ();
196249 }
197- if (t.kind_case () == google::bigtable::v2::Type::kFloat32Type ||
198- t.kind_case () == google::bigtable::v2::Type::kFloat64Type ) {
250+ if (t.has_float32_type () || t.has_float64_type ()) {
199251 return os << v.float_value ();
200252 }
201- if (t.kind_case () == google::bigtable::v2::Type:: kStringType ) {
253+ if (t.has_string_type () ) {
202254 switch (mode) {
203255 case StreamMode::kScalar :
204256 return os << v.string_value ();
@@ -209,23 +261,23 @@ std::ostream& StreamHelper(std::ostream& os, // NOLINT(misc-no-recursion)
209261 }
210262 return os; // Unreachable, but quiets warning.
211263 }
212- if (t.kind_case () == google::bigtable::v2::Type:: kBytesType ) {
264+ if (t.has_bytes_type () ) {
213265 return os << Bytes (AsString (v.bytes_value ()));
214266 }
215- if (t.kind_case () == google::bigtable::v2::Type:: kTimestampType ) {
267+ if (t.has_timestamp_type () ) {
216268 auto ts = MakeTimestamp (v.timestamp_value ());
217269 if (!ts) {
218270 internal::ThrowStatus (ts.status ());
219271 }
220272 return os << ts.value ();
221273 }
222- if (t.kind_case () == google::bigtable::v2::Type:: kDateType ) {
274+ if (t.has_date_type () ) {
223275 auto date =
224276 bigtable_internal::FromProto (t, v).get <absl::CivilDay>().value ();
225277 return os << date;
226278 }
227- if (t.kind_case () == google::bigtable::v2::Type:: kArrayType ) {
228- char const * delimiter = " " ;
279+ if (t.has_array_type () ) {
280+ auto const * delimiter = " " ;
229281 os << ' [' ;
230282 for (auto && val : v.array_value ().values ()) {
231283 os << delimiter;
@@ -235,8 +287,8 @@ std::ostream& StreamHelper(std::ostream& os, // NOLINT(misc-no-recursion)
235287 }
236288 return os << ' ]' ;
237289 }
238- if (t.kind_case () == google::bigtable::v2::Type:: kStructType ) {
239- char const * delimiter = " " ;
290+ if (t.has_struct_type () ) {
291+ auto const * delimiter = " " ;
240292 os << ' (' ;
241293 for (int i = 0 ; i < v.array_value ().values_size (); ++i) {
242294 os << delimiter;
@@ -251,9 +303,36 @@ std::ostream& StreamHelper(std::ostream& os, // NOLINT(misc-no-recursion)
251303 }
252304 return os << ' )' ;
253305 }
306+ if (t.has_map_type ()) {
307+ return MapStreamHelper (os, v, t, mode);
308+ }
254309 // this should include type name
255310 return os << " Error: unknown value type code " << t.kind_case ();
256311}
312+ std::ostream& MapStreamHelper (std::ostream& os, // NOLINT(misc-no-recursion)
313+ google::bigtable::v2::Value const & v,
314+ google::bigtable::v2::Type const & t, StreamMode) {
315+ auto const * delimiter = " " ;
316+ os << ' {' ;
317+ for (int i = 0 ; i < v.array_value ().values_size (); ++i) {
318+ os << delimiter;
319+ os << " {" ;
320+ auto const & kv = v.array_value ().values (i);
321+ if (!kv.has_array_value () || kv.array_value ().values_size () != 2 ) {
322+ os << " malformed key-value pair" ;
323+ delimiter = " , " ;
324+ continue ;
325+ }
326+ StreamHelper (os, kv.array_value ().values (0 ), t.map_type ().key_type (),
327+ StreamMode::kAggregate );
328+ os << " : " ;
329+ StreamHelper (os, kv.array_value ().values (1 ), t.map_type ().value_type (),
330+ StreamMode::kAggregate );
331+ os << " }" ;
332+ delimiter = " , " ;
333+ }
334+ return os << ' }' ;
335+ }
257336} // namespace
258337
259338bool operator ==(Value const & a, Value const & b) {
0 commit comments