[Cobalt 1.1 privacy] Decode private indices for FleetwideMeans reports
Adds a tagged union SumOrCount to hold numeric
contributions to FleetwideMeans reports, and
adds a method to decode a private index as a
pair (event_vector, sum_or_count).
Change-Id: Ia6bfa6bb15956fa52c8373958137b4c708eb9cc3
Reviewed-on: https://fuchsia-review.googlesource.com/c/cobalt/+/442593
Commit-Queue: Laura Peskin <pesk@google.com>
Reviewed-by: Alexandre Zani <azani@google.com>
diff --git a/src/lib/privacy/private_index_decoding.cc b/src/lib/privacy/private_index_decoding.cc
index a3a262a..d52ab5d 100644
--- a/src/lib/privacy/private_index_decoding.cc
+++ b/src/lib/privacy/private_index_decoding.cc
@@ -6,6 +6,7 @@
namespace cobalt {
namespace {
+
util::Status ValidateIndexAsInteger(uint64_t index, uint64_t num_index_points) {
if (index >= num_index_points) {
return util::Status(util::INVALID_ARGUMENT,
@@ -13,6 +14,18 @@
}
return util::Status::OK;
}
+
+util::Status ValidateIndexAsCount(uint64_t index, uint64_t num_index_points) {
+ if (index < num_index_points) {
+ return util::Status(util::INVALID_ARGUMENT, "index is less than num_index_points.");
+ }
+ if (index >= 2 * num_index_points) {
+ return util::Status(util::INVALID_ARGUMENT,
+ "index is greater than or equal to 2 * num_index_points.");
+ }
+ return util::Status::OK;
+}
+
} // namespace
util::Status DecodePrivateIndexAsEventVector(
@@ -50,4 +63,38 @@
return util::Status::OK;
}
+cobalt::util::Status DecodePrivateIndexAsSumOrCount(
+ uint64_t index,
+ const google::protobuf::RepeatedPtrField<MetricDefinition::MetricDimension>& metric_dimensions,
+ int64_t min_value, int64_t max_value, uint64_t max_count, uint64_t num_index_points,
+ std::vector<uint32_t>* event_vector, SumOrCount* sum_or_count) {
+ uint64_t event_vector_index;
+ uint64_t value_index;
+ ValueAndEventVectorIndicesFromIndex(index, logger::GetNumEventVectors(metric_dimensions) - 1,
+ &value_index, &event_vector_index);
+ if (util::Status decode_event_vector_index =
+ DecodePrivateIndexAsEventVector(event_vector_index, metric_dimensions, event_vector);
+ !decode_event_vector_index.ok()) {
+ return decode_event_vector_index;
+ }
+
+ if (IsCountIndex(value_index, num_index_points)) {
+ if (util::Status validate_value_index = ValidateIndexAsCount(value_index, num_index_points);
+ !validate_value_index.ok()) {
+ return validate_value_index;
+ }
+ (*sum_or_count).type = SumOrCount::COUNT;
+ (*sum_or_count).count = CountFromIndex(value_index, max_count, num_index_points);
+ return util::Status::OK;
+ }
+
+ if (util::Status validate_value_index = ValidateIndexAsInteger(value_index, num_index_points);
+ !validate_value_index.ok()) {
+ return validate_value_index;
+ }
+ (*sum_or_count).type = SumOrCount::SUM;
+ (*sum_or_count).sum = IntegerFromIndex(value_index, min_value, max_value, num_index_points);
+ return util::Status::OK;
+}
+
} // namespace cobalt
diff --git a/src/lib/privacy/private_index_decoding.h b/src/lib/privacy/private_index_decoding.h
index dd72d29..7cf3386 100644
--- a/src/lib/privacy/private_index_decoding.h
+++ b/src/lib/privacy/private_index_decoding.h
@@ -12,6 +12,16 @@
namespace cobalt {
+// A value which represents either a sum or a count. Used when decoding observations for
+// FleetwideMeans reports.
+struct SumOrCount {
+ enum SumOrCountType { SUM, COUNT };
+ SumOrCountType type;
+
+ int64_t sum = 0;
+ uint64_t count = 0;
+};
+
// Populates |event_vector| with the event vector which corresponds to |index| according to the
// contents of |metric_dimensions| and returns an OK status, or returns an error status if |index|
// does not represent a valid event vector according to |metric_dimensions|.
@@ -30,6 +40,21 @@
int64_t min_value, int64_t max_value, uint64_t num_index_points,
std::vector<uint32_t>* event_vector, int64_t* integer_value);
+// Populates |event_vector| and |sum_or_count| with the event vector and SumAndCount which
+// correspond to |index| according to |metric_dimensions|, |min_value|, |max_value|, |max_count| and
+// |num_index_points|, or returns an error status if |index| does not represent a valid
+// (event_vector, sum) or (event_vector, count) pair.
+//
+// After a successful call, |sum_or_count| will be a SumOrCount struct whose |type| field is set to
+// either SUM or COUNT. If the |type| is SUM, then the |sum| field is populated with a signed int
+// representing a sum; if the |type| is COUNT, then the |count| field is populated with an unsigned
+// int representing a count.
+util::Status DecodePrivateIndexAsSumOrCount(
+ uint64_t index,
+ const google::protobuf::RepeatedPtrField<MetricDefinition::MetricDimension>& metric_dimensions,
+ int64_t min_value, int64_t max_value, uint64_t max_count, uint64_t num_index_points,
+ std::vector<uint32_t>* event_vector, SumOrCount* sum_or_count);
+
} // namespace cobalt
#endif // COBALT_SRC_LIB_PRIVACY_PRIVATE_INDEX_DECODING_H_
diff --git a/src/lib/privacy/private_index_decoding_test.cc b/src/lib/privacy/private_index_decoding_test.cc
index be4be5d..e843c2b 100644
--- a/src/lib/privacy/private_index_decoding_test.cc
+++ b/src/lib/privacy/private_index_decoding_test.cc
@@ -10,6 +10,7 @@
const int64_t kMinValue = -10;
const int64_t kMaxValue = 50;
+const uint64_t kMaxCount = 100;
const uint64_t kNumIndexPoints = 5;
} // namespace
@@ -41,10 +42,14 @@
uint64_t MaxEventVectorIndex() { return logger::GetNumEventVectors(metric_dimensions_) - 1; }
- uint64_t MaxIntegerValueIndex() {
+ uint64_t MaxIndexForIntegerValue() {
return logger::GetNumEventVectors(metric_dimensions_) * kNumIndexPoints - 1;
}
+ uint64_t MinIndexForCount() { return MaxIndexForIntegerValue() + 1; }
+
+ uint64_t MaxIndexForCount() { return 2 * MinIndexForCount() - 1; }
+
google::protobuf::RepeatedPtrField<MetricDefinition::MetricDimension> metric_dimensions_;
};
@@ -80,7 +85,7 @@
EXPECT_EQ(integer_value, kMinValue);
// Check that the maximum private index is decoded correctly.
- status = DecodePrivateIndexAsInteger(MaxIntegerValueIndex(), metric_dimensions_, kMinValue,
+ status = DecodePrivateIndexAsInteger(MaxIndexForIntegerValue(), metric_dimensions_, kMinValue,
kMaxValue, kNumIndexPoints, &event_vector, &integer_value);
ASSERT_TRUE(status.ok());
EXPECT_EQ(event_vector, std::vector<uint32_t>({2, 300, 10}));
@@ -91,9 +96,63 @@
std::vector<uint32_t> event_vector;
int64_t integer_value;
util::Status status =
- DecodePrivateIndexAsInteger(MaxIntegerValueIndex() + 1, metric_dimensions_, kMinValue,
+ DecodePrivateIndexAsInteger(MaxIndexForIntegerValue() + 1, metric_dimensions_, kMinValue,
kMaxValue, kNumIndexPoints, &event_vector, &integer_value);
EXPECT_FALSE(status.ok());
}
+TEST_F(PrivateIndexDecodingTest, DecodeAsSumOrCountValidSum) {
+ std::vector<uint32_t> event_vector;
+ SumOrCount sum_or_count;
+ // Check that the minimum private index that corresponds to sum is decoded correctly.
+ util::Status status =
+ DecodePrivateIndexAsSumOrCount(0, metric_dimensions_, kMinValue, kMaxValue, kMaxCount,
+ kNumIndexPoints, &event_vector, &sum_or_count);
+ ASSERT_TRUE(status.ok());
+ EXPECT_EQ(event_vector, std::vector<uint32_t>({0, 0, 0}));
+ EXPECT_EQ(sum_or_count.type, SumOrCount::SUM);
+ EXPECT_EQ(sum_or_count.sum, kMinValue);
+
+ // Check that the maximum private index that corresponds to a sum is decoded correctly.
+ status = DecodePrivateIndexAsSumOrCount(MaxIndexForIntegerValue(), metric_dimensions_, kMinValue,
+ kMaxValue, kMaxCount, kNumIndexPoints, &event_vector,
+ &sum_or_count);
+ ASSERT_TRUE(status.ok());
+ EXPECT_EQ(event_vector, std::vector<uint32_t>({2, 300, 10}));
+ EXPECT_EQ(sum_or_count.type, SumOrCount::SUM);
+ EXPECT_EQ(sum_or_count.sum, kMaxValue);
+}
+
+TEST_F(PrivateIndexDecodingTest, DecodeAsSumOrCountValidCount) {
+ std::vector<uint32_t> event_vector;
+ SumOrCount sum_or_count;
+ // Check that the minimum private index that corresponds to a count is decoded correctly.
+ util::Status status =
+ DecodePrivateIndexAsSumOrCount(MinIndexForCount(), metric_dimensions_, kMinValue, kMaxValue,
+ kMaxCount, kNumIndexPoints, &event_vector, &sum_or_count);
+ ASSERT_TRUE(status.ok());
+ EXPECT_EQ(event_vector, std::vector<uint32_t>({0, 0, 0}));
+ EXPECT_EQ(sum_or_count.type, SumOrCount::COUNT);
+ EXPECT_EQ(sum_or_count.count, 0u);
+
+ // Check that the maximum private index that corresponds to a count is decoded correctly.
+ status =
+ DecodePrivateIndexAsSumOrCount(MaxIndexForCount(), metric_dimensions_, kMinValue, kMaxValue,
+ kMaxCount, kNumIndexPoints, &event_vector, &sum_or_count);
+ ASSERT_TRUE(status.ok());
+ EXPECT_EQ(event_vector, std::vector<uint32_t>({2, 300, 10}));
+ EXPECT_EQ(sum_or_count.type, SumOrCount::COUNT);
+ EXPECT_EQ(sum_or_count.count, kMaxCount);
+}
+
+TEST_F(PrivateIndexDecodingTest, DecodeAsSumOrCountInvalid) {
+ std::vector<uint32_t> event_vector;
+ SumOrCount sum_or_count;
+
+ util::Status status = DecodePrivateIndexAsSumOrCount(
+ MaxIndexForCount() + 1, metric_dimensions_, kMinValue, kMaxValue, kMaxCount, kNumIndexPoints,
+ &event_vector, &sum_or_count);
+ EXPECT_FALSE(status.ok());
+}
+
} // namespace cobalt