prometheus-client-mmap 0.24.3-aarch64-linux → 0.24.4-aarch64-linux

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b97e8c7f5232d148c704a312ab830d0f9fe5e3895cc4f3f3cf013962546b0dc7
4
- data.tar.gz: d8ed0fbe4d3d23f226d114f0c3846c0409bc57d154e6acbf6b53d5d2f92def84
3
+ metadata.gz: '036069dbaa72e29118060526349e03e4dc12ca727d3460bd984c151ec409dfc9'
4
+ data.tar.gz: 3291d33938421d272ff8322d842636055c5aa42fe11f0dc4752f144e84d17ee2
5
5
  SHA512:
6
- metadata.gz: 9b95f7b78ab931d88937e8156a160fe45b6e4854e2058ff86b65fa5f0e880df1f429d5b8c34758b154535fcee9b7ce3000f327116db169c45c78d20486f16177
7
- data.tar.gz: 2f4ea73f3faef086256383e3fe9ff53cbc9c4ff79cd52f3ea60bb82c05793b8492124694428ad5d383bdd0333ec79863ffe27b7f4be88edb37b1f0adb0002bd7
6
+ metadata.gz: a301f93346803f1470e6be0be03102267b244778ee65029f68882cfcfd81d1189afe7e229c24418b4e236b7d9e42578f3a18e3503a9810b879f7819408ed48d7
7
+ data.tar.gz: ff90c64f85839fee802123c32de0c74a2d1da554bf849bb7a0713e70adf12e901961556befd40966db13071e290795f8d029ce9010f138f68c481f56f3997fb0
@@ -63,35 +63,35 @@ fn parse_names(json: &str) -> Option<MetricNames> {
63
63
  return None;
64
64
  }
65
65
 
66
- // Now: family_name","metric_name",[...
67
- let remainder = json.get(2..)?;
68
-
69
- let names_end = remainder.find('[')?;
70
-
71
- // Save the rest of the slice to parse for labels later.
72
- let label_json = remainder.get(names_end..)?;
73
-
74
- // Now: family_name","metric_name",
75
- let remainder = remainder.get(..names_end)?;
66
+ // Captured: "family_name","metric_name",
67
+ // ^^^^^^^^^^^
68
+ let (family_name, remainder) = scan_str(json.get(1..)?)?;
76
69
 
77
- // Split on commas into:
78
- // family_name","metric_name",
79
- // ^^^^one^^^^^ ^^^^^two^^^^^
80
- let mut token_iter = remainder.split(',');
70
+ // Validate comma separated names
71
+ // ["family_name","metric_name",[...
72
+ // ^
73
+ if !remainder.starts_with("\",") {
74
+ return None;
75
+ }
81
76
 
82
- // Captured: family_name","metric_name",
83
- // ^^^^^^^^^^^
84
- let family_name = token_iter.next()?.trim_end_matches('"');
77
+ // Now: "metric_name",[...
78
+ let remainder = remainder.get(2..)?;
85
79
 
86
80
  // Captured: "family_name","metric_name",
87
81
  // ^^^^^^^^^^^
88
- let metric_name = token_iter.next()?.trim_matches('"');
82
+ let (metric_name, remainder) = scan_str(remainder)?;
89
83
 
90
- // Confirm the final entry of the iter is empty, the the trailing ','.
91
- if !token_iter.next()?.is_empty() {
84
+ // Validate comma separated names
85
+ // ["family_name","metric_name",[...
86
+ // ^^
87
+ if !remainder.starts_with("\",") {
92
88
  return None;
93
89
  }
94
90
 
91
+ // Save the rest of the slice to parse for labels later.
92
+ // Now: [...
93
+ let label_json = remainder.get(2..)?;
94
+
95
95
  Some(MetricNames {
96
96
  label_json,
97
97
  family_name,
@@ -101,76 +101,143 @@ fn parse_names(json: &str) -> Option<MetricNames> {
101
101
 
102
102
  fn parse_label_values(json: &str) -> Option<MetricLabelVals> {
103
103
  // Starting with: ["label_a","label_b"],["value_a", "value_b"]]
104
- if !(json.starts_with('[') && json.ends_with("]]")) {
104
+ if !json.starts_with('[') {
105
105
  return None;
106
106
  }
107
107
 
108
+ // Now: "label_a","label_b"],["value_a", "value_b"]]
109
+ let mut remainder = json.get(1..)?;
110
+
108
111
  // Validate we either have the start of a label string or an
109
112
  // empty array, e.g. `["` or `[]`.
110
113
  if !matches!(json.as_bytes().get(1)?, b'"' | b']') {
111
114
  return None;
112
115
  }
113
116
 
114
- // Now: "label_a","label_b"
115
- let labels_end = json.find(']')?;
116
- let label_range = json.get(1..labels_end)?;
117
-
118
117
  let mut labels = SmallVec::new();
119
118
 
120
119
  // Split on commas into:
121
120
  // "label_a","label_b"
122
121
  // ^^^one^^^ ^^^two^^^
123
- for label in label_range.split(',') {
124
- // Captured: "label_a","label_b"
125
- // ^^^^^^^
126
- // If there are no labels, e.g. `[][]`, then don't capture anything.
127
- if !label.is_empty() {
128
- labels.push(label.trim_matches('"'));
129
- }
122
+ loop {
123
+ let Some((label, label_rem)) = scan_str(remainder) else {
124
+ // No further keys.
125
+ break;
126
+ };
127
+ labels.push(label);
128
+
129
+ // Advance past trailing quote.
130
+ remainder = label_rem.get(1..)?;
131
+ match remainder.as_bytes().first() {
132
+ Some(b']') => break, // No further labels.
133
+ Some(b',') => {} // More labels expected.
134
+ _ => return None, // Invalid.
135
+ };
136
+
137
+ // Advance past comma.
138
+ remainder = remainder.get(1..)?;
130
139
  }
131
140
 
132
- // Now: ],["value_a", "value_b"]]
133
- let mut values_range = json.get(labels_end..)?;
134
-
135
- // Validate we have a separating comma with one and only one leading bracket.
136
- if !(values_range.starts_with("],[") && values_range.as_bytes().get(3)? != &b'[') {
141
+ if !remainder.starts_with("],[") {
137
142
  return None;
138
143
  }
139
-
140
144
  // Now: "value_a", "value_b"]]
141
- values_range = values_range.get(3..)?;
142
-
143
- let values_end = values_range.find(']')?;
145
+ remainder = remainder.get(3..)?;
144
146
 
145
- // Validate we have only two trailing brackets.
146
- if values_range.get(values_end..)?.len() > 2 {
147
+ // Validate we don't have extra brackets.
148
+ if remainder.starts_with('[') {
147
149
  return None;
148
150
  }
149
151
 
150
- // Now: "value_a", "value_b"
151
- values_range = values_range.get(..values_end)?;
152
-
153
152
  let mut values = SmallVec::new();
154
-
155
153
  // Split on commas into:
156
154
  // "value_a","value_b"
157
155
  // ^^^one^^^ ^^^two^^^
158
- for value in values_range.split(',') {
159
- // Captured: "value_a","value_b"
160
- // ^^^^^^^^^
161
- // If there are no values, e.g. `[][]`, then don't capture anything.
162
- if !value.is_empty() {
163
- values.push(value.trim_matches('"'));
156
+ loop {
157
+ if remainder.starts_with('"') {
158
+ let (value, value_rem) = scan_str(remainder)?;
159
+ values.push(value);
160
+
161
+ // Advance past trailing quote.
162
+ remainder = value_rem.get(1..)?;
163
+ } else {
164
+ // An unquoted value, such as `true` or `404`.
165
+ let i = remainder.find(|c| c == ',' || c == ']')?;
166
+ let value = &remainder[..i];
167
+
168
+ match (value.is_empty(), is_valid_json_literal(value)) {
169
+ (true, _) => {} // Empty string, do nothing.
170
+ (false, true) => values.push(value), // Valid literal.
171
+ (false, false) => return None, // Invalid literal, fail.
172
+ }
173
+
174
+ remainder = &remainder[i..];
164
175
  }
176
+
177
+ match remainder.as_bytes().first() {
178
+ Some(b']') => break, // End of values.
179
+ Some(b',') => {} // More values expected.
180
+ _ => return None, // Invalid.
181
+ };
182
+
183
+ // Advance past comma.
184
+ remainder = remainder.get(1..)?;
165
185
  }
166
186
 
167
187
  if values.len() != labels.len() {
168
188
  return None;
169
189
  }
170
190
 
191
+ // Now: ]]
192
+ if remainder != "]]" {
193
+ return None;
194
+ }
195
+
171
196
  Some(MetricLabelVals { labels, values })
172
197
  }
173
198
 
199
+ // Read a JSON-encoded str that includes starting and ending double quotes.
200
+ // Returns the validated str with the double quotes trimmed and the remainder
201
+ // of the input str.
202
+ fn scan_str(json: &str) -> Option<(&str, &str)> {
203
+ let mut escaping = false;
204
+
205
+ if !json.starts_with('"') {
206
+ return None;
207
+ }
208
+
209
+ // Trim leading double quote.
210
+ let json = json.get(1..)?;
211
+
212
+ for (i, &c) in json.as_bytes().iter().enumerate() {
213
+ if c == b'\\' {
214
+ escaping = true;
215
+ continue;
216
+ }
217
+
218
+ if c == b'"' && !escaping {
219
+ return Some((json.get(..i)?, json.get(i..)?));
220
+ }
221
+
222
+ escaping = false;
223
+ }
224
+
225
+ None
226
+ }
227
+
228
+ // Confirm an unquoted JSON literal is a boolean, null, or has a passing
229
+ // resemblance to a number. We do not confirm numeric formatting, only
230
+ // that all characters are valid. See https://www.json.org/json-en.html
231
+ // for details.
232
+ fn is_valid_json_literal(s: &str) -> bool {
233
+ match s {
234
+ "true" | "false" | "null" => true,
235
+ _ => s.chars().all(|c| {
236
+ c.is_ascii_digit() || c == '.' || c == '+' || c == '-' || c == 'e' || c == 'E'
237
+ }),
238
+ }
239
+ }
240
+
174
241
  #[cfg(test)]
175
242
  mod test {
176
243
  use smallvec::smallvec;
@@ -217,6 +284,16 @@ mod test {
217
284
  values: smallvec!["value_a", "403"],
218
285
  }),
219
286
  },
287
+ TestCase {
288
+ name: "scientific notation literal",
289
+ input: r#"["metric","name",["label_a"],[-2.0e-5]]"#,
290
+ expected: Some(MetricText {
291
+ family_name: "metric",
292
+ metric_name: "name",
293
+ labels: smallvec!["label_a"],
294
+ values: smallvec!["-2.0e-5"],
295
+ }),
296
+ },
220
297
  TestCase {
221
298
  name: "null value",
222
299
  input: r#"["metric","name",["label_a","label_b"],[null,"value_b"]]"#,
@@ -237,6 +314,36 @@ mod test {
237
314
  values: smallvec![],
238
315
  }),
239
316
  },
317
+ TestCase {
318
+ name: "comma in items",
319
+ input: r#"["met,ric","na,me",["label,_a","label_b"],["value,_a","value_b"]]"#,
320
+ expected: Some(MetricText {
321
+ family_name: "met,ric",
322
+ metric_name: "na,me",
323
+ labels: smallvec!["label,_a", "label_b"],
324
+ values: smallvec!["value,_a", "value_b"],
325
+ }),
326
+ },
327
+ TestCase {
328
+ name: "bracket in value",
329
+ input: r#"["met[r]ic","na[m]e",["label[_]a","label_b"],["value_a","val[ue]_b"]]"#,
330
+ expected: Some(MetricText {
331
+ family_name: "met[r]ic",
332
+ metric_name: "na[m]e",
333
+ labels: smallvec!["label[_]a", "label_b"],
334
+ values: smallvec!["value_a", "val[ue]_b"],
335
+ }),
336
+ },
337
+ TestCase {
338
+ name: "escaped quotes",
339
+ input: r#"["met\"ric","na\"me",["label\"_a","label_b"],["value\"_a","value_b"]]"#,
340
+ expected: Some(MetricText {
341
+ family_name: r#"met\"ric"#,
342
+ metric_name: r#"na\"me"#,
343
+ labels: smallvec![r#"label\"_a"#, "label_b"],
344
+ values: smallvec![r#"value\"_a"#, "value_b"],
345
+ }),
346
+ },
240
347
  ];
241
348
 
242
349
  for case in tc {
@@ -313,23 +420,18 @@ mod test {
313
420
  expected: None,
314
421
  },
315
422
  TestCase {
316
- name: "comma in family name",
317
- input: r#"["met,ric","name",["label_a","label_b"],["value_a","value_b"]]"#,
318
- expected: None,
319
- },
320
- TestCase {
321
- name: "comma in metric name",
322
- input: r#"["metric","na,me",["label_a","label_b"],["value_a","value_b"]]"#,
423
+ name: "misplaced bracket",
424
+ input: r#"["metric","name",["label_a","label_b"],]["value_a","value_b"]]"#,
323
425
  expected: None,
324
426
  },
325
427
  TestCase {
326
- name: "comma in value",
327
- input: r#"["metric","na,me",["label_a","label_b"],["val,ue_a","value_b"]]"#,
428
+ name: "comma in numeric value",
429
+ input: r#"["metric","name",["label_a","label_b"],[400,0,"value_b"]]"#,
328
430
  expected: None,
329
431
  },
330
432
  TestCase {
331
- name: "comma in numeric value",
332
- input: r#"["metric","name",["label_a","label_b"],[400,0,"value_b"]]"#,
433
+ name: "non-e letter in numeric value",
434
+ input: r#"["metric","name",["label_a","label_b"],[400x0,"value_b"]]"#,
333
435
  expected: None,
334
436
  },
335
437
  ];
Binary file
Binary file
Binary file
Binary file
@@ -1,5 +1,5 @@
1
1
  module Prometheus
2
2
  module Client
3
- VERSION = '0.24.3'.freeze
3
+ VERSION = '0.24.4'.freeze
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prometheus-client-mmap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.24.3
4
+ version: 0.24.4
5
5
  platform: aarch64-linux
6
6
  authors:
7
7
  - Tobias Schmidt
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2023-05-20 00:00:00.000000000 Z
14
+ date: 2023-06-12 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: rb_sys