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