Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
michael@0 | 1 | # This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. |
michael@0 | 4 | |
michael@0 | 5 | import json |
michael@0 | 6 | import math |
michael@0 | 7 | import re |
michael@0 | 8 | |
michael@0 | 9 | from collections import OrderedDict |
michael@0 | 10 | |
michael@0 | 11 | def table_dispatch(kind, table, body): |
michael@0 | 12 | """Call body with table[kind] if it exists. Raise an error otherwise.""" |
michael@0 | 13 | if kind in table: |
michael@0 | 14 | return body(table[kind]) |
michael@0 | 15 | else: |
michael@0 | 16 | raise BaseException, "don't know how to handle a histogram of kind %s" % kind |
michael@0 | 17 | |
michael@0 | 18 | class DefinitionException(BaseException): |
michael@0 | 19 | pass |
michael@0 | 20 | |
michael@0 | 21 | def check_numeric_limits(dmin, dmax, n_buckets): |
michael@0 | 22 | if type(dmin) != int: |
michael@0 | 23 | raise DefinitionException, "minimum is not a number" |
michael@0 | 24 | if type(dmax) != int: |
michael@0 | 25 | raise DefinitionException, "maximum is not a number" |
michael@0 | 26 | if type(n_buckets) != int: |
michael@0 | 27 | raise DefinitionException, "number of buckets is not a number" |
michael@0 | 28 | |
michael@0 | 29 | def linear_buckets(dmin, dmax, n_buckets): |
michael@0 | 30 | check_numeric_limits(dmin, dmax, n_buckets) |
michael@0 | 31 | ret_array = [0] * n_buckets |
michael@0 | 32 | dmin = float(dmin) |
michael@0 | 33 | dmax = float(dmax) |
michael@0 | 34 | for i in range(1, n_buckets): |
michael@0 | 35 | linear_range = (dmin * (n_buckets - 1 - i) + dmax * (i - 1)) / (n_buckets - 2) |
michael@0 | 36 | ret_array[i] = int(linear_range + 0.5) |
michael@0 | 37 | return ret_array |
michael@0 | 38 | |
michael@0 | 39 | def exponential_buckets(dmin, dmax, n_buckets): |
michael@0 | 40 | check_numeric_limits(dmin, dmax, n_buckets) |
michael@0 | 41 | log_max = math.log(dmax); |
michael@0 | 42 | bucket_index = 2; |
michael@0 | 43 | ret_array = [0] * n_buckets |
michael@0 | 44 | current = dmin |
michael@0 | 45 | ret_array[1] = current |
michael@0 | 46 | for bucket_index in range(2, n_buckets): |
michael@0 | 47 | log_current = math.log(current) |
michael@0 | 48 | log_ratio = (log_max - log_current) / (n_buckets - bucket_index) |
michael@0 | 49 | log_next = log_current + log_ratio |
michael@0 | 50 | next_value = int(math.floor(math.exp(log_next) + 0.5)) |
michael@0 | 51 | if next_value > current: |
michael@0 | 52 | current = next_value |
michael@0 | 53 | else: |
michael@0 | 54 | current = current + 1 |
michael@0 | 55 | ret_array[bucket_index] = current |
michael@0 | 56 | return ret_array |
michael@0 | 57 | |
michael@0 | 58 | always_allowed_keys = ['kind', 'description', 'cpp_guard', 'expires_in_version'] |
michael@0 | 59 | |
michael@0 | 60 | class Histogram: |
michael@0 | 61 | """A class for representing a histogram definition.""" |
michael@0 | 62 | |
michael@0 | 63 | def __init__(self, name, definition): |
michael@0 | 64 | """Initialize a histogram named name with the given definition. |
michael@0 | 65 | definition is a dict-like object that must contain at least the keys: |
michael@0 | 66 | |
michael@0 | 67 | - 'kind': The kind of histogram. Must be one of 'boolean', 'flag', |
michael@0 | 68 | 'enumerated', 'linear', or 'exponential'. |
michael@0 | 69 | - 'description': A textual description of the histogram. |
michael@0 | 70 | |
michael@0 | 71 | The key 'cpp_guard' is optional; if present, it denotes a preprocessor |
michael@0 | 72 | symbol that should guard C/C++ definitions associated with the histogram.""" |
michael@0 | 73 | self.verify_attributes(name, definition) |
michael@0 | 74 | self._name = name |
michael@0 | 75 | self._description = definition['description'] |
michael@0 | 76 | self._kind = definition['kind'] |
michael@0 | 77 | self._cpp_guard = definition.get('cpp_guard') |
michael@0 | 78 | self._extended_statistics_ok = definition.get('extended_statistics_ok', False) |
michael@0 | 79 | self._expiration = definition.get('expires_in_version') |
michael@0 | 80 | self.compute_bucket_parameters(definition) |
michael@0 | 81 | table = { 'boolean': 'BOOLEAN', |
michael@0 | 82 | 'flag': 'FLAG', |
michael@0 | 83 | 'enumerated': 'LINEAR', |
michael@0 | 84 | 'linear': 'LINEAR', |
michael@0 | 85 | 'exponential': 'EXPONENTIAL' } |
michael@0 | 86 | table_dispatch(self.kind(), table, |
michael@0 | 87 | lambda k: self._set_nsITelemetry_kind(k)) |
michael@0 | 88 | |
michael@0 | 89 | def name(self): |
michael@0 | 90 | """Return the name of the histogram.""" |
michael@0 | 91 | return self._name |
michael@0 | 92 | |
michael@0 | 93 | def description(self): |
michael@0 | 94 | """Return the description of the histogram.""" |
michael@0 | 95 | return self._description |
michael@0 | 96 | |
michael@0 | 97 | def kind(self): |
michael@0 | 98 | """Return the kind of the histogram. |
michael@0 | 99 | Will be one of 'boolean', 'flag', 'enumerated', 'linear', or 'exponential'.""" |
michael@0 | 100 | return self._kind |
michael@0 | 101 | |
michael@0 | 102 | def expiration(self): |
michael@0 | 103 | """Return the expiration version of the histogram.""" |
michael@0 | 104 | return self._expiration |
michael@0 | 105 | |
michael@0 | 106 | def nsITelemetry_kind(self): |
michael@0 | 107 | """Return the nsITelemetry constant corresponding to the kind of |
michael@0 | 108 | the histogram.""" |
michael@0 | 109 | return self._nsITelemetry_kind |
michael@0 | 110 | |
michael@0 | 111 | def _set_nsITelemetry_kind(self, kind): |
michael@0 | 112 | self._nsITelemetry_kind = "nsITelemetry::HISTOGRAM_%s" % kind |
michael@0 | 113 | |
michael@0 | 114 | def low(self): |
michael@0 | 115 | """Return the lower bound of the histogram. May be a string.""" |
michael@0 | 116 | return self._low |
michael@0 | 117 | |
michael@0 | 118 | def high(self): |
michael@0 | 119 | """Return the high bound of the histogram. May be a string.""" |
michael@0 | 120 | return self._high |
michael@0 | 121 | |
michael@0 | 122 | def n_buckets(self): |
michael@0 | 123 | """Return the number of buckets in the histogram. May be a string.""" |
michael@0 | 124 | return self._n_buckets |
michael@0 | 125 | |
michael@0 | 126 | def cpp_guard(self): |
michael@0 | 127 | """Return the preprocessor symbol that should guard C/C++ definitions |
michael@0 | 128 | associated with the histogram. Returns None if no guarding is necessary.""" |
michael@0 | 129 | return self._cpp_guard |
michael@0 | 130 | |
michael@0 | 131 | def extended_statistics_ok(self): |
michael@0 | 132 | """Return True if gathering extended statistics for this histogram |
michael@0 | 133 | is enabled.""" |
michael@0 | 134 | return self._extended_statistics_ok |
michael@0 | 135 | |
michael@0 | 136 | def ranges(self): |
michael@0 | 137 | """Return an array of lower bounds for each bucket in the histogram.""" |
michael@0 | 138 | table = { 'boolean': linear_buckets, |
michael@0 | 139 | 'flag': linear_buckets, |
michael@0 | 140 | 'enumerated': linear_buckets, |
michael@0 | 141 | 'linear': linear_buckets, |
michael@0 | 142 | 'exponential': exponential_buckets } |
michael@0 | 143 | return table_dispatch(self.kind(), table, |
michael@0 | 144 | lambda p: p(self.low(), self.high(), self.n_buckets())) |
michael@0 | 145 | |
michael@0 | 146 | def compute_bucket_parameters(self, definition): |
michael@0 | 147 | table = { |
michael@0 | 148 | 'boolean': Histogram.boolean_flag_bucket_parameters, |
michael@0 | 149 | 'flag': Histogram.boolean_flag_bucket_parameters, |
michael@0 | 150 | 'enumerated': Histogram.enumerated_bucket_parameters, |
michael@0 | 151 | 'linear': Histogram.linear_bucket_parameters, |
michael@0 | 152 | 'exponential': Histogram.exponential_bucket_parameters |
michael@0 | 153 | } |
michael@0 | 154 | table_dispatch(self.kind(), table, |
michael@0 | 155 | lambda p: self.set_bucket_parameters(*p(definition))) |
michael@0 | 156 | |
michael@0 | 157 | def verify_attributes(self, name, definition): |
michael@0 | 158 | global always_allowed_keys |
michael@0 | 159 | general_keys = always_allowed_keys + ['low', 'high', 'n_buckets'] |
michael@0 | 160 | |
michael@0 | 161 | table = { |
michael@0 | 162 | 'boolean': always_allowed_keys, |
michael@0 | 163 | 'flag': always_allowed_keys, |
michael@0 | 164 | 'enumerated': always_allowed_keys + ['n_values'], |
michael@0 | 165 | 'linear': general_keys, |
michael@0 | 166 | 'exponential': general_keys + ['extended_statistics_ok'] |
michael@0 | 167 | } |
michael@0 | 168 | table_dispatch(definition['kind'], table, |
michael@0 | 169 | lambda allowed_keys: Histogram.check_keys(name, definition, allowed_keys)) |
michael@0 | 170 | |
michael@0 | 171 | Histogram.check_expiration(name, definition) |
michael@0 | 172 | |
michael@0 | 173 | @staticmethod |
michael@0 | 174 | def check_expiration(name, definition): |
michael@0 | 175 | expiration = definition.get('expires_in_version') |
michael@0 | 176 | |
michael@0 | 177 | if not expiration: |
michael@0 | 178 | return |
michael@0 | 179 | |
michael@0 | 180 | if re.match(r'^[1-9][0-9]*$', expiration): |
michael@0 | 181 | expiration = expiration + ".0a1" |
michael@0 | 182 | elif re.match(r'^[1-9][0-9]*\.0$', expiration): |
michael@0 | 183 | expiration = expiration + "a1" |
michael@0 | 184 | |
michael@0 | 185 | definition['expires_in_version'] = expiration |
michael@0 | 186 | |
michael@0 | 187 | @staticmethod |
michael@0 | 188 | def check_keys(name, definition, allowed_keys): |
michael@0 | 189 | for key in definition.iterkeys(): |
michael@0 | 190 | if key not in allowed_keys: |
michael@0 | 191 | raise KeyError, '%s not permitted for %s' % (key, name) |
michael@0 | 192 | |
michael@0 | 193 | def set_bucket_parameters(self, low, high, n_buckets): |
michael@0 | 194 | def try_to_coerce_to_number(v): |
michael@0 | 195 | try: |
michael@0 | 196 | return eval(v, {}) |
michael@0 | 197 | except: |
michael@0 | 198 | return v |
michael@0 | 199 | self._low = try_to_coerce_to_number(low) |
michael@0 | 200 | self._high = try_to_coerce_to_number(high) |
michael@0 | 201 | self._n_buckets = try_to_coerce_to_number(n_buckets) |
michael@0 | 202 | |
michael@0 | 203 | @staticmethod |
michael@0 | 204 | def boolean_flag_bucket_parameters(definition): |
michael@0 | 205 | return (1, 2, 3) |
michael@0 | 206 | |
michael@0 | 207 | @staticmethod |
michael@0 | 208 | def linear_bucket_parameters(definition): |
michael@0 | 209 | return (definition.get('low', 1), |
michael@0 | 210 | definition['high'], |
michael@0 | 211 | definition['n_buckets']) |
michael@0 | 212 | |
michael@0 | 213 | @staticmethod |
michael@0 | 214 | def enumerated_bucket_parameters(definition): |
michael@0 | 215 | n_values = definition['n_values'] |
michael@0 | 216 | return (1, n_values, "%s+1" % n_values) |
michael@0 | 217 | |
michael@0 | 218 | @staticmethod |
michael@0 | 219 | def exponential_bucket_parameters(definition): |
michael@0 | 220 | return (definition.get('low', 1), |
michael@0 | 221 | definition['high'], |
michael@0 | 222 | definition['n_buckets']) |
michael@0 | 223 | |
michael@0 | 224 | def from_file(filename): |
michael@0 | 225 | """Return an iterator that provides a sequence of Histograms for |
michael@0 | 226 | the histograms defined in filename. |
michael@0 | 227 | """ |
michael@0 | 228 | with open(filename, 'r') as f: |
michael@0 | 229 | histograms = json.load(f, object_pairs_hook=OrderedDict) |
michael@0 | 230 | for (name, definition) in histograms.iteritems(): |
michael@0 | 231 | yield Histogram(name, definition) |