Index: chrome/installer/util/experiment_labels.cc |
diff --git a/chrome/installer/util/experiment_labels.cc b/chrome/installer/util/experiment_labels.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..de30328ed51b2b03631db8503e0bda3036ab06dc |
--- /dev/null |
+++ b/chrome/installer/util/experiment_labels.cc |
@@ -0,0 +1,150 @@ |
+// Copyright 2017 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "chrome/installer/util/experiment_labels.h" |
+ |
+#include <vector> |
+ |
+#include "base/strings/string_split.h" |
+#include "base/strings/stringprintf.h" |
+ |
+namespace installer { |
+ |
+namespace { |
+ |
+constexpr base::StringPiece16::value_type kNameValueSeparator = L'='; |
+constexpr base::StringPiece16::value_type kValueExpirationSeparator = L'|'; |
+constexpr base::StringPiece16::value_type kLabelSeparator = L';'; |
+ |
+// Returns a vector of string pieces, one for each "name=value|expiration" |
+// group in |value|. |
+std::vector<base::StringPiece16> Parse(base::StringPiece16 value) { |
+ static constexpr base::char16 kLabelSeparatorString[] = {kLabelSeparator, |
+ L'\0'}; |
+ return base::SplitStringPiece(value, kLabelSeparatorString, |
+ base::TRIM_WHITESPACE, |
+ base::SPLIT_WANT_NONEMPTY); |
+} |
+ |
+// Returns an abbreviated day name for a zero-based |day_of_week|. |
+const wchar_t* AbbreviatedDayOfWeek(int day_of_week) { |
+ // Matches the abbreviated day names from the ICU "en" locale. |
+ static constexpr const wchar_t* kDays[] = {L"Sun", L"Mon", L"Tue", L"Wed", |
+ L"Thu", L"Fri", L"Sat"}; |
+ return kDays[day_of_week]; |
+} |
+ |
+// Returns an abbreviated month name for a one-based |month|. |
+const wchar_t* AbbreviatedMonth(int month) { |
+ // Matches the abbreviated month names from the ICU "en" locale. |
+ static constexpr const wchar_t* kMonths[] = {L"Jan", L"Feb", L"Mar", L"Apr", |
+ L"May", L"Jun", L"Jul", L"Aug", |
+ L"Sep", L"Oct", L"Nov", L"Dec"}; |
+ return kMonths[month - 1]; |
+} |
+ |
+// Returns a formatted string given a date that is compatible with Omaha (see |
+// https://github.com/google/omaha/blob/master/omaha/base/time.cc#L132). |
+base::string16 FormatDate(base::Time date) { |
+ base::Time::Exploded exploded_time; |
+ date.UTCExplode(&exploded_time); |
+ |
+ // "Fri, 14 Aug 2015 16:13:03 GMT" |
+ return base::StringPrintf(L"%ls, %02d %ls %04d %02d:%02d:%02d GMT", |
+ AbbreviatedDayOfWeek(exploded_time.day_of_week), |
+ exploded_time.day_of_month, |
+ AbbreviatedMonth(exploded_time.month), |
+ exploded_time.year, exploded_time.hour, |
+ exploded_time.minute, exploded_time.second); |
+} |
+ |
+// Appends "label_name=label_value|expiration" to |label|. |
+void AppendLabel(base::StringPiece16 label_name, |
+ base::StringPiece16 label_value, |
+ base::Time expiration, |
+ base::string16* label) { |
+ // 29 characters for the expiration date plus the two separators makes 31. |
+ label->reserve(label->size() + label_name.size() + label_value.size() + 31); |
+ label_name.AppendToString(label); |
+ label->push_back(kNameValueSeparator); |
+ label_value.AppendToString(label); |
+ label->push_back(kValueExpirationSeparator); |
+ label->append(FormatDate(expiration)); |
+} |
+ |
+} // namespace |
+ |
+ExperimentLabels::ExperimentLabels(const base::string16& value) |
+ : value_(value) {} |
+ |
+base::StringPiece16 ExperimentLabels::GetValueForLabel( |
+ base::StringPiece16 label_name) const { |
+ DCHECK(!label_name.empty()); |
+ |
+ return FindLabel(label_name).second; |
+} |
+ |
+void ExperimentLabels::SetValueForLabel(base::StringPiece16 label_name, |
+ base::StringPiece16 label_value, |
+ base::TimeDelta lifetime) { |
+ DCHECK(!label_name.empty()); |
+ DCHECK(!label_value.empty()); |
+ DCHECK(!lifetime.is_zero()); |
+ |
+ SetValueForLabel(label_name, label_value, base::Time::Now() + lifetime); |
+} |
+ |
+void ExperimentLabels::SetValueForLabel(base::StringPiece16 label_name, |
+ base::StringPiece16 label_value, |
+ base::Time expiration) { |
+ DCHECK(!label_name.empty()); |
+ DCHECK(!label_value.empty()); |
+ |
+ LabelAndValue label_and_value = FindLabel(label_name); |
+ if (label_and_value.first.empty()) { |
+ // This label doesn't already exist -- append it to the raw value. |
+ if (!value_.empty()) |
+ value_.push_back(kLabelSeparator); |
+ AppendLabel(label_name, label_value, expiration, &value_); |
+ } else { |
+ // Replace the existing value and expiration. |
+ // Get the stuff before the old label. |
+ base::string16 new_label(value_, 0, |
+ label_and_value.first.data() - value_.data()); |
+ // Append the new label. |
+ AppendLabel(label_name, label_value, expiration, &new_label); |
+ // Find the stuff after the old label and append it. |
+ size_t next_separator = value_.find( |
+ kLabelSeparator, |
+ (label_and_value.second.data() + label_and_value.second.size()) - |
+ value_.data()); |
+ if (next_separator != base::string16::npos) |
+ new_label.append(value_, next_separator, base::string16::npos); |
+ // Presto. |
+ new_label.swap(value_); |
+ } |
+} |
+ |
+ExperimentLabels::LabelAndValue ExperimentLabels::FindLabel( |
+ base::StringPiece16 label_name) const { |
+ DCHECK(!label_name.empty()); |
+ |
+ std::vector<base::StringPiece16> labels = Parse(value_); |
+ for (const auto& label : labels) { |
+ if (label.size() < label_name.size() + 2 || |
+ !label.starts_with(label_name) || |
+ label[label_name.size()] != kNameValueSeparator) { |
+ continue; |
+ } |
+ size_t value_start = label_name.size() + 1; |
+ size_t value_end = label.find(kValueExpirationSeparator, value_start); |
+ if (value_end == base::StringPiece16::npos) |
+ break; |
+ return std::make_pair(label, |
+ label.substr(value_start, value_end - value_start)); |
+ } |
+ return LabelAndValue(); |
+} |
+ |
+} // namespace installer |