prometheus-client-mmap 0.24.4-arm64-darwin → 0.25.0-arm64-darwin

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: a7c304ac6c43d41541ef12ef405d444d68924ece9ead6cc8e508983eb01755f8
4
- data.tar.gz: e3c5804eee4262c557ff532208aa8abfa77149b1474c6f8dd918e9cc88dbd337
3
+ metadata.gz: 7e25b5c221f6dbace3fcd430752e961cbdacb59cea837743f56d26612987fbce
4
+ data.tar.gz: 50f1f162c9147c9b01fa2d0238f55216bd3eb9e74c5c0ff9f011d72c7dfe2b22
5
5
  SHA512:
6
- metadata.gz: ab272da91e3e786360230f337d45046b086a976941cb5c18d1cd729d822eb2de382d4064f4759b3abfcc624b6782877d4d5b9dd8ec224610e88311ba3fe7a8ce
7
- data.tar.gz: f279ed9d3923407b7b0734e99976bf250ede453ce99d7b6f80d6c681bae3490255860d8169808faa5cdcece2f92698104eccefbf49008422c9895c13300bc12e
6
+ metadata.gz: 1ece252ef9705f506e22ce9bc73007053c1536749949a4c38cad377e66a8edbe9081962362c8848fa8df37cd3a80a9db56adb98004c7f6096298255a08b94ae8
7
+ data.tar.gz: cc97ecd47e4f24734a9349fb963e3ae7b6cfd8bc3995fb01b394baa9aeac3364d3e68aa58d694820898b9399089a40c1bdeeed6b4d0fc9e0f299d0496643a7c3
@@ -3,6 +3,7 @@
3
3
  #include <errno.h>
4
4
  #include <fcntl.h>
5
5
  #include <ruby/util.h>
6
+ #include <ruby/version.h>
6
7
  #include <sys/mman.h>
7
8
 
8
9
  #include "file_format.h"
@@ -27,6 +28,16 @@
27
28
  */
28
29
  static VALUE weak_obj_tracker_get_key(VALUE val) { return val; }
29
30
 
31
+ static void update_rstring_len(VALUE val, size_t len) {
32
+ #if defined(RUBY_API_VERSION_MAJOR) && defined(RUBY_API_VERSION_MINOR) && (RUBY_API_VERSION_MAJOR == 3) && \
33
+ (RUBY_API_VERSION_MINOR >= 3)
34
+
35
+ RSTRING(val)->len = len;
36
+ #else
37
+ RSTRING(val)->as.heap.len = len;
38
+ #endif
39
+ }
40
+
30
41
  /**
31
42
  * Adds a T_STRING type to the WeakMap. The WeakMap should be stored
32
43
  * as an instance variable.
@@ -52,7 +63,7 @@ VALUE mm_update_obj_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, self)) {
52
63
  GET_MMAP(self, i_mm, MM_MODIFY);
53
64
 
54
65
  RSTRING(i)->as.heap.ptr = i_mm->t->addr;
55
- RSTRING(i)->as.heap.len = i_mm->t->real;
66
+ update_rstring_len(i, i_mm->t->real);
56
67
 
57
68
  return Qtrue;
58
69
  }
@@ -98,7 +109,7 @@ static VALUE mm_str(VALUE obj, int modify) {
98
109
  ret = rb_obj_alloc(rb_cString);
99
110
  RSTRING(ret)->as.heap.ptr = i_mm->t->addr;
100
111
  RSTRING(ret)->as.heap.aux.capa = i_mm->t->len;
101
- RSTRING(ret)->as.heap.len = i_mm->t->real;
112
+ update_rstring_len(ret, i_mm->t->real);
102
113
 
103
114
  weak_obj_tracker_add(obj, ret);
104
115
 
@@ -30,9 +30,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
30
30
 
31
31
  [[package]]
32
32
  name = "bindgen"
33
- version = "0.60.1"
33
+ version = "0.62.0"
34
34
  source = "registry+https://github.com/rust-lang/crates.io-index"
35
- checksum = "062dddbc1ba4aca46de6338e2bf87771414c335f7b2f2036e8f3e9befebf88e6"
35
+ checksum = "c6720a8b7b2d39dd533285ed438d458f65b31b5c257e6ac7bb3d7e82844dd722"
36
36
  dependencies = [
37
37
  "bitflags",
38
38
  "cexpr",
@@ -45,6 +45,7 @@ dependencies = [
45
45
  "regex",
46
46
  "rustc-hash",
47
47
  "shlex",
48
+ "syn 1.0.109",
48
49
  ]
49
50
 
50
51
  [[package]]
@@ -169,6 +170,9 @@ dependencies = [
169
170
  "nix",
170
171
  "rand",
171
172
  "rb-sys",
173
+ "rb-sys-env",
174
+ "serde",
175
+ "serde_json",
172
176
  "sha2",
173
177
  "smallvec",
174
178
  "tempfile",
@@ -252,6 +256,12 @@ dependencies = [
252
256
  "windows-sys 0.48.0",
253
257
  ]
254
258
 
259
+ [[package]]
260
+ name = "itoa"
261
+ version = "1.0.6"
262
+ source = "registry+https://github.com/rust-lang/crates.io-index"
263
+ checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
264
+
255
265
  [[package]]
256
266
  name = "lazy_static"
257
267
  version = "1.4.0"
@@ -289,7 +299,7 @@ checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f"
289
299
  [[package]]
290
300
  name = "magnus"
291
301
  version = "0.5.0"
292
- source = "git+https://github.com/matsadler/magnus?branch=main#b10aab48119eb87a872bf0bb4480b1fcebe5d1b9"
302
+ source = "git+https://github.com/matsadler/magnus?branch=main#50cab2380103c39c14765a10bdc4b38a74082285"
293
303
  dependencies = [
294
304
  "magnus-macros",
295
305
  "rb-sys",
@@ -300,7 +310,7 @@ dependencies = [
300
310
  [[package]]
301
311
  name = "magnus-macros"
302
312
  version = "0.4.0"
303
- source = "git+https://github.com/matsadler/magnus?branch=main#b10aab48119eb87a872bf0bb4480b1fcebe5d1b9"
313
+ source = "git+https://github.com/matsadler/magnus?branch=main#50cab2380103c39c14765a10bdc4b38a74082285"
304
314
  dependencies = [
305
315
  "proc-macro2",
306
316
  "quote",
@@ -435,18 +445,18 @@ dependencies = [
435
445
 
436
446
  [[package]]
437
447
  name = "rb-sys"
438
- version = "0.9.71"
448
+ version = "0.9.79"
439
449
  source = "registry+https://github.com/rust-lang/crates.io-index"
440
- checksum = "156bfedced1e236600bcaad538477097ff2ed5c6b474e411d15b791e1d24c0f1"
450
+ checksum = "939fb78db3e4f26665c1d4c7b91ca66d3578335a19aba552d4a6445811d07072"
441
451
  dependencies = [
442
452
  "rb-sys-build",
443
453
  ]
444
454
 
445
455
  [[package]]
446
456
  name = "rb-sys-build"
447
- version = "0.9.71"
457
+ version = "0.9.79"
448
458
  source = "registry+https://github.com/rust-lang/crates.io-index"
449
- checksum = "5cb2e4a32cbc290b543a74567072ad24b708aff7bb5dde5a68d5690379cd7938"
459
+ checksum = "335a95eb0420d52fa94ef12019df3c2c250c6b19cbb3c60bd05cb7e9c362072c"
450
460
  dependencies = [
451
461
  "bindgen",
452
462
  "lazy_static",
@@ -515,6 +525,12 @@ dependencies = [
515
525
  "windows-sys 0.48.0",
516
526
  ]
517
527
 
528
+ [[package]]
529
+ name = "ryu"
530
+ version = "1.0.13"
531
+ source = "registry+https://github.com/rust-lang/crates.io-index"
532
+ checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
533
+
518
534
  [[package]]
519
535
  name = "seq-macro"
520
536
  version = "0.3.3"
@@ -526,6 +542,31 @@ name = "serde"
526
542
  version = "1.0.159"
527
543
  source = "registry+https://github.com/rust-lang/crates.io-index"
528
544
  checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065"
545
+ dependencies = [
546
+ "serde_derive",
547
+ ]
548
+
549
+ [[package]]
550
+ name = "serde_derive"
551
+ version = "1.0.159"
552
+ source = "registry+https://github.com/rust-lang/crates.io-index"
553
+ checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585"
554
+ dependencies = [
555
+ "proc-macro2",
556
+ "quote",
557
+ "syn 2.0.13",
558
+ ]
559
+
560
+ [[package]]
561
+ name = "serde_json"
562
+ version = "1.0.96"
563
+ source = "registry+https://github.com/rust-lang/crates.io-index"
564
+ checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
565
+ dependencies = [
566
+ "itoa",
567
+ "ryu",
568
+ "serde",
569
+ ]
529
570
 
530
571
  [[package]]
531
572
  name = "sha2"
@@ -555,6 +596,9 @@ name = "smallvec"
555
596
  version = "1.10.0"
556
597
  source = "registry+https://github.com/rust-lang/crates.io-index"
557
598
  checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
599
+ dependencies = [
600
+ "serde",
601
+ ]
558
602
 
559
603
  [[package]]
560
604
  name = "syn"
@@ -13,7 +13,9 @@ memmap2 = "0.5"
13
13
  # v0.26 cannot be built on CentOS 7 https://github.com/nix-rust/nix/issues/1972
14
14
  nix = { version = "0.25", features = ["mman"] } # mman used for MsFlags
15
15
  rb-sys = "0.9"
16
- smallvec = "1.10"
16
+ serde = { version = "1.0", features = ["derive"] }
17
+ serde_json = { version = "1.0", features = ["raw_value"] }
18
+ smallvec = { version = "1.10", features = ["serde"] }
17
19
  thiserror = "1.0"
18
20
 
19
21
  [dev-dependencies]
@@ -25,6 +27,9 @@ rand = "0.8"
25
27
  sha2 = "0.10"
26
28
  tempfile = "3.5"
27
29
 
30
+ [build-dependencies]
31
+ rb-sys-env = "0.1"
32
+
28
33
  [lib]
29
34
  # Integration tests won't work if crate is only `cdylib`.
30
35
  crate-type = ["cdylib","lib"]
@@ -0,0 +1,5 @@
1
+ fn main() -> Result<(), Box<dyn std::error::Error>> {
2
+ let _ = rb_sys_env::activate()?;
3
+
4
+ Ok(())
5
+ }
@@ -1,10 +1,12 @@
1
1
  use magnus::Symbol;
2
+ use serde::Deserialize;
3
+ use serde_json::value::RawValue;
4
+ use smallvec::SmallVec;
2
5
  use std::fmt::Write;
3
6
  use std::str;
4
7
 
5
8
  use crate::error::{MmapError, RubyError};
6
9
  use crate::file_info::FileInfo;
7
- use crate::parser::{self, MetricText};
8
10
  use crate::raw_entry::RawEntry;
9
11
  use crate::Result;
10
12
  use crate::{SYM_GAUGE, SYM_LIVESUM, SYM_MAX, SYM_MIN};
@@ -16,6 +18,16 @@ pub struct FileEntry {
16
18
  pub meta: EntryMetadata,
17
19
  }
18
20
 
21
+ /// String slices pointing to the fields of a borrowed `Entry`'s JSON data.
22
+ #[derive(Deserialize, Debug)]
23
+ pub struct MetricText<'a> {
24
+ pub family_name: &'a str,
25
+ pub metric_name: &'a str,
26
+ pub labels: SmallVec<[&'a str; 4]>,
27
+ #[serde(borrow)]
28
+ pub values: SmallVec<[&'a RawValue; 4]>,
29
+ }
30
+
19
31
  /// The primary data payload for a `FileEntry`, the JSON string and the
20
32
  /// associated pid, if significant. Used as the key for `EntryMap`.
21
33
  #[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Debug)]
@@ -134,11 +146,16 @@ impl FileEntry {
134
146
  let mut processed_count = 0;
135
147
 
136
148
  for entry in entries {
137
- let metrics_data = match parser::parse_metrics(&entry.data.json) {
138
- Some(m) => m,
149
+ let metrics_data = match serde_json::from_str::<MetricText>(&entry.data.json) {
150
+ Ok(m) => {
151
+ if m.labels.len() != m.values.len() {
152
+ continue;
153
+ }
154
+ m
155
+ }
139
156
  // We don't exit the function here so the total number of invalid
140
157
  // entries can be calculated below.
141
- None => continue,
158
+ Err(_) => continue,
142
159
  };
143
160
 
144
161
  match prev_name.as_ref() {
@@ -197,16 +214,20 @@ impl FileEntry {
197
214
 
198
215
  let it = json_data.labels.iter().zip(json_data.values.iter());
199
216
 
200
- for (i, (&key, &val)) in it.enumerate() {
217
+ for (i, (&key, val)) in it.enumerate() {
201
218
  out.push_str(key);
202
-
203
- out.push_str("=\"");
204
-
205
- // `null` values will be output as `""`.
206
- if val != "null" {
207
- out.push_str(val);
219
+ out.push('=');
220
+
221
+ match val.get() {
222
+ "null" => out.push_str("\"\""),
223
+ s if s.starts_with('"') => out.push_str(s),
224
+ s => {
225
+ // Quote numeric values.
226
+ out.push('"');
227
+ out.push_str(s);
228
+ out.push('"');
229
+ }
208
230
  }
209
- out.push('"');
210
231
 
211
232
  if i < json_data.labels.len() - 1 {
212
233
  out.push(',');
@@ -276,6 +297,20 @@ mod test {
276
297
  "##}),
277
298
  expected_err: None,
278
299
  },
300
+ TestCase {
301
+ name: "many labels",
302
+ multiprocess_mode: "min",
303
+ json: &[
304
+ r#"["family","name",["label_a","label_b","label_c","label_d","label_e"],["value_a","value_b","value_c","value_d","value_e"]]"#,
305
+ ],
306
+ values: &[1.0],
307
+ pids: &["worker-1"],
308
+ expected_out: Some(indoc! {r##"# HELP family Multiprocess metric
309
+ # TYPE family gauge
310
+ name{label_a="value_a",label_b="value_b",label_c="value_c",label_d="value_d",label_e="value_e"} 1
311
+ "##}),
312
+ expected_err: None,
313
+ },
279
314
  TestCase {
280
315
  name: "floating point shown",
281
316
  multiprocess_mode: "min",
@@ -288,6 +323,44 @@ mod test {
288
323
  "##}),
289
324
  expected_err: None,
290
325
  },
326
+ TestCase {
327
+ name: "numeric value",
328
+ multiprocess_mode: "min",
329
+ json: &[
330
+ r#"["family","name",["label_a","label_b","label_c"],["value_a",403,-0.2E5]]"#,
331
+ ],
332
+ values: &[1.5],
333
+ pids: &["worker-1"],
334
+ expected_out: Some(indoc! {r##"# HELP family Multiprocess metric
335
+ # TYPE family gauge
336
+ name{label_a="value_a",label_b="403",label_c="-0.2E5"} 1.5
337
+ "##}),
338
+ expected_err: None,
339
+ },
340
+ TestCase {
341
+ name: "null value",
342
+ multiprocess_mode: "min",
343
+ json: &[r#"["family","name",["label_a","label_b"],["value_a",null]]"#],
344
+ values: &[1.5],
345
+ pids: &["worker-1"],
346
+ expected_out: Some(indoc! {r##"# HELP family Multiprocess metric
347
+ # TYPE family gauge
348
+ name{label_a="value_a",label_b=""} 1.5
349
+ "##}),
350
+ expected_err: None,
351
+ },
352
+ TestCase {
353
+ name: "comma in value",
354
+ multiprocess_mode: "min",
355
+ json: &[r#"["family","name",["label_a","label_b"],["value_a","value,_b"]]"#],
356
+ values: &[1.5],
357
+ pids: &["worker-1"],
358
+ expected_out: Some(indoc! {r##"# HELP family Multiprocess metric
359
+ # TYPE family gauge
360
+ name{label_a="value_a",label_b="value,_b"} 1.5
361
+ "##}),
362
+ expected_err: None,
363
+ },
291
364
  TestCase {
292
365
  name: "no labels, pid significant",
293
366
  multiprocess_mode: "all",
@@ -413,6 +486,138 @@ mod test {
413
486
  RubyError::Runtime,
414
487
  )),
415
488
  },
489
+ TestCase {
490
+ name: "too many values",
491
+ multiprocess_mode: "min",
492
+ json: &[r#"["family","name",["label_a"],["value_a","value,_b"]]"#],
493
+ values: &[1.5],
494
+ pids: &["worker-1"],
495
+ expected_out: None,
496
+ expected_err: Some(MmapError::legacy(
497
+ "Processed entries 0 != map entries 1".to_owned(),
498
+ RubyError::Runtime,
499
+ )),
500
+ },
501
+ TestCase {
502
+ name: "no values",
503
+ multiprocess_mode: "min",
504
+ json: &[r#"["family","name",["label_a"]]"#],
505
+ values: &[1.5],
506
+ pids: &["worker-1"],
507
+ expected_out: None,
508
+ expected_err: Some(MmapError::legacy(
509
+ "Processed entries 0 != map entries 1".to_owned(),
510
+ RubyError::Runtime,
511
+ )),
512
+ },
513
+ TestCase {
514
+ name: "no labels or values",
515
+ multiprocess_mode: "min",
516
+ json: &[r#"["family","name","foo"]"#],
517
+ values: &[1.5],
518
+ pids: &["worker-1"],
519
+ expected_out: None,
520
+ expected_err: Some(MmapError::legacy(
521
+ "Processed entries 0 != map entries 1".to_owned(),
522
+ RubyError::Runtime,
523
+ )),
524
+ },
525
+ TestCase {
526
+ name: "too many leading brackets",
527
+ multiprocess_mode: "min",
528
+ json: &[r#"[["family","name",["label_a","label_b"],["value_a","value_b"]]"#],
529
+ values: &[1.5],
530
+ pids: &["worker-1"],
531
+ expected_out: None,
532
+ expected_err: Some(MmapError::legacy(
533
+ "Processed entries 0 != map entries 1".to_owned(),
534
+ RubyError::Runtime,
535
+ )),
536
+ },
537
+ TestCase {
538
+ name: "too many trailing brackets",
539
+ multiprocess_mode: "min",
540
+ json: &[r#"["family","name",["label_a","label_b"],["value_a","value_b"]]]"#],
541
+ values: &[1.5],
542
+ pids: &["worker-1"],
543
+ expected_out: None,
544
+ expected_err: Some(MmapError::legacy(
545
+ "Processed entries 0 != map entries 1".to_owned(),
546
+ RubyError::Runtime,
547
+ )),
548
+ },
549
+ TestCase {
550
+ name: "too many leading label brackets",
551
+ multiprocess_mode: "min",
552
+ json: &[r#"["family","name",[["label_a","label_b"],["value_a","value_b"]]"#],
553
+ values: &[1.5],
554
+ pids: &["worker-1"],
555
+ expected_out: None,
556
+ expected_err: Some(MmapError::legacy(
557
+ "Processed entries 0 != map entries 1".to_owned(),
558
+ RubyError::Runtime,
559
+ )),
560
+ },
561
+ TestCase {
562
+ name: "too many leading label brackets",
563
+ multiprocess_mode: "min",
564
+ json: &[r#"["family","name",[["label_a","label_b"],["value_a","value_b"]]"#],
565
+ values: &[1.5],
566
+ pids: &["worker-1"],
567
+ expected_out: None,
568
+ expected_err: Some(MmapError::legacy(
569
+ "Processed entries 0 != map entries 1".to_owned(),
570
+ RubyError::Runtime,
571
+ )),
572
+ },
573
+ TestCase {
574
+ name: "too many leading value brackets",
575
+ multiprocess_mode: "min",
576
+ json: &[r#"["family","name",["label_a","label_b"],[["value_a","value_b"]]"#],
577
+ values: &[1.5],
578
+ pids: &["worker-1"],
579
+ expected_out: None,
580
+ expected_err: Some(MmapError::legacy(
581
+ "Processed entries 0 != map entries 1".to_owned(),
582
+ RubyError::Runtime,
583
+ )),
584
+ },
585
+ TestCase {
586
+ name: "misplaced bracket",
587
+ multiprocess_mode: "min",
588
+ json: &[r#"["family","name",["label_a","label_b"],]["value_a","value_b"]]"#],
589
+ values: &[1.5],
590
+ pids: &["worker-1"],
591
+ expected_out: None,
592
+ expected_err: Some(MmapError::legacy(
593
+ "Processed entries 0 != map entries 1".to_owned(),
594
+ RubyError::Runtime,
595
+ )),
596
+ },
597
+ TestCase {
598
+ name: "comma in numeric",
599
+ multiprocess_mode: "min",
600
+ json: &[r#"["family","name",["label_a","label_b"],["value_a",403,0]]"#],
601
+ values: &[1.5],
602
+ pids: &["worker-1"],
603
+ expected_out: None,
604
+ expected_err: Some(MmapError::legacy(
605
+ "Processed entries 0 != map entries 1".to_owned(),
606
+ RubyError::Runtime,
607
+ )),
608
+ },
609
+ TestCase {
610
+ name: "non-e letter in numeric",
611
+ multiprocess_mode: "min",
612
+ json: &[r#"["family","name",["label_a","label_b"],["value_a",-2.0c5]]"#],
613
+ values: &[1.5],
614
+ pids: &["worker-1"],
615
+ expected_out: None,
616
+ expected_err: Some(MmapError::legacy(
617
+ "Processed entries 0 != map entries 1".to_owned(),
618
+ RubyError::Runtime,
619
+ )),
620
+ },
416
621
  ];
417
622
 
418
623
  for case in tc {
@@ -12,7 +12,6 @@ pub mod file_info;
12
12
  mod macros;
13
13
  pub mod map;
14
14
  pub mod mmap;
15
- pub mod parser;
16
15
  pub mod raw_entry;
17
16
  pub mod util;
18
17
 
@@ -418,10 +418,10 @@ impl MmapedFile {
418
418
 
419
419
  raw_str.as_mut().as_.heap.ptr = self.as_mut_ptr().offset(offset);
420
420
 
421
- let current_len = raw_str.as_ref().as_.heap.len;
421
+ let current_len = str.len() as c_long;
422
422
  let new_shared_len = old_cap + current_len;
423
423
 
424
- raw_str.as_mut().as_.heap.len = new_shared_len;
424
+ self.update_rstring_len(raw_str, new_shared_len);
425
425
  continue;
426
426
  }
427
427
 
@@ -436,7 +436,7 @@ impl MmapedFile {
436
436
  //
437
437
  // See https://gitlab.com/gitlab-org/ruby/gems/prometheus-client-mmap/-/issues/45
438
438
  raw_str.as_mut().as_.heap.ptr = self.as_mut_ptr();
439
- raw_str.as_mut().as_.heap.len = new_len;
439
+ self.update_rstring_len(raw_str, new_len);
440
440
  }
441
441
  }
442
442
 
@@ -632,6 +632,16 @@ impl MmapedFile {
632
632
  unsafe fn rb_string_internal(rb_str: RString) -> NonNull<rb_sys::RString> {
633
633
  mem::transmute::<RString, NonNull<rb_sys::RString>>(rb_str)
634
634
  }
635
+
636
+ #[cfg(ruby_lte_3_2)]
637
+ unsafe fn update_rstring_len(&self, mut raw_str: NonNull<rb_sys::RString>, new_len: c_long) {
638
+ raw_str.as_mut().as_.heap.len = new_len;
639
+ }
640
+
641
+ #[cfg(ruby_gte_3_3)]
642
+ unsafe fn update_rstring_len(&self, mut raw_str: NonNull<rb_sys::RString>, new_len: c_long) {
643
+ raw_str.as_mut().len = new_len;
644
+ }
635
645
  }
636
646
 
637
647
  #[cfg(test)]
@@ -772,8 +782,7 @@ mod test {
772
782
  if str.as_raw() == child_id {
773
783
  assert_eq!(parent_id, raw_str.as_ref().as_.heap.aux.shared);
774
784
 
775
- let child_offset =
776
- mmap_len as isize - raw_str.as_ref().as_.heap.len as isize;
785
+ let child_offset = mmap_len as isize - str.len() as isize;
777
786
  assert_eq!(mmap_ptr.offset(child_offset), raw_str.as_ref().as_.heap.ptr);
778
787
 
779
788
  child_checked = true;
@@ -781,7 +790,7 @@ mod test {
781
790
  assert_eq!(parent_id, str.as_raw());
782
791
 
783
792
  assert_eq!(mmap_ptr, raw_str.as_ref().as_.heap.ptr);
784
- assert_eq!(mmap_len as c_long, raw_str.as_ref().as_.heap.len);
793
+ assert_eq!(mmap_len as c_long, str.len() as c_long);
785
794
  assert!(raw_str.as_ref().basic.flags & (STR_SHARED | STR_NOEMBED) > 0);
786
795
  assert!(str.is_frozen());
787
796
 
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -1,5 +1,5 @@
1
1
  module Prometheus
2
2
  module Client
3
- VERSION = '0.24.4'.freeze
3
+ VERSION = '0.25.0'.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.4
4
+ version: 0.25.0
5
5
  platform: arm64-darwin
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-06-12 00:00:00.000000000 Z
14
+ date: 2023-06-15 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: rb_sys
@@ -150,6 +150,7 @@ files:
150
150
  - ext/fast_mmaped_file_rs/Cargo.lock
151
151
  - ext/fast_mmaped_file_rs/Cargo.toml
152
152
  - ext/fast_mmaped_file_rs/README.md
153
+ - ext/fast_mmaped_file_rs/build.rs
153
154
  - ext/fast_mmaped_file_rs/extconf.rb
154
155
  - ext/fast_mmaped_file_rs/src/error.rs
155
156
  - ext/fast_mmaped_file_rs/src/file_entry.rs
@@ -159,7 +160,6 @@ files:
159
160
  - ext/fast_mmaped_file_rs/src/map.rs
160
161
  - ext/fast_mmaped_file_rs/src/mmap.rs
161
162
  - ext/fast_mmaped_file_rs/src/mmap/inner.rs
162
- - ext/fast_mmaped_file_rs/src/parser.rs
163
163
  - ext/fast_mmaped_file_rs/src/raw_entry.rs
164
164
  - ext/fast_mmaped_file_rs/src/testhelper.rs
165
165
  - ext/fast_mmaped_file_rs/src/util.rs
@@ -1,448 +0,0 @@
1
- use smallvec::SmallVec;
2
- use std::str;
3
-
4
- /// String slices pointing to the fields of a borrowed `Entry`'s JSON data.
5
- #[derive(PartialEq, Eq, Debug)]
6
- pub struct MetricText<'a> {
7
- pub family_name: &'a str,
8
- pub metric_name: &'a str,
9
- pub labels: SmallVec<[&'a str; 4]>,
10
- pub values: SmallVec<[&'a str; 4]>,
11
- }
12
-
13
- #[derive(PartialEq, Eq, Debug)]
14
- struct MetricNames<'a> {
15
- label_json: &'a str,
16
- family_name: &'a str,
17
- metric_name: &'a str,
18
- }
19
-
20
- #[derive(PartialEq, Eq, Debug)]
21
- struct MetricLabelVals<'a> {
22
- labels: SmallVec<[&'a str; 4]>,
23
- values: SmallVec<[&'a str; 4]>,
24
- }
25
-
26
- /// Parse Prometheus metric data stored in the following format:
27
- ///
28
- /// ["metric","name",["label_a","label_b"],["value_a","value_b"]]
29
- ///
30
- /// There will be 1-8 trailing spaces to ensure at least a one-byte
31
- /// gap between the json and value, and to pad to an 8-byte alignment.
32
- /// We strip the surrounding double quotes from all items. Values may
33
- /// or may not have surrounding double quotes, depending on their type.
34
- pub fn parse_metrics(json: &str) -> Option<MetricText> {
35
- // It would be preferable to use `serde_json` here, but the values
36
- // may be strings, numbers, or null, and so don't parse easily to a
37
- // defined struct. Using `serde_json::Value` is an option, but since
38
- // we're just copying the literal values into a buffer this will be
39
- // inefficient and verbose.
40
-
41
- // Trim trailing spaces from string before processing. We use
42
- // `trim_end_matches()` instead of `trim_end()` because we know the
43
- // trailing bytes are always ASCII 0x20 bytes. `trim_end()` will also
44
- // check for unicode spaces and consume a few more CPU cycles.
45
- let trimmed = json.trim_end_matches(' ');
46
-
47
- let names = parse_names(trimmed)?;
48
- let label_json = names.label_json;
49
-
50
- let label_vals = parse_label_values(label_json)?;
51
-
52
- Some(MetricText {
53
- family_name: names.family_name,
54
- metric_name: names.metric_name,
55
- labels: label_vals.labels,
56
- values: label_vals.values,
57
- })
58
- }
59
-
60
- fn parse_names(json: &str) -> Option<MetricNames> {
61
- // Starting with: ["family_name","metric_name",[...
62
- if !json.starts_with("[\"") {
63
- return None;
64
- }
65
-
66
- // Captured: "family_name","metric_name",
67
- // ^^^^^^^^^^^
68
- let (family_name, remainder) = scan_str(json.get(1..)?)?;
69
-
70
- // Validate comma separated names
71
- // ["family_name","metric_name",[...
72
- // ^
73
- if !remainder.starts_with("\",") {
74
- return None;
75
- }
76
-
77
- // Now: "metric_name",[...
78
- let remainder = remainder.get(2..)?;
79
-
80
- // Captured: "family_name","metric_name",
81
- // ^^^^^^^^^^^
82
- let (metric_name, remainder) = scan_str(remainder)?;
83
-
84
- // Validate comma separated names
85
- // ["family_name","metric_name",[...
86
- // ^^
87
- if !remainder.starts_with("\",") {
88
- return None;
89
- }
90
-
91
- // Save the rest of the slice to parse for labels later.
92
- // Now: [...
93
- let label_json = remainder.get(2..)?;
94
-
95
- Some(MetricNames {
96
- label_json,
97
- family_name,
98
- metric_name,
99
- })
100
- }
101
-
102
- fn parse_label_values(json: &str) -> Option<MetricLabelVals> {
103
- // Starting with: ["label_a","label_b"],["value_a", "value_b"]]
104
- if !json.starts_with('[') {
105
- return None;
106
- }
107
-
108
- // Now: "label_a","label_b"],["value_a", "value_b"]]
109
- let mut remainder = json.get(1..)?;
110
-
111
- // Validate we either have the start of a label string or an
112
- // empty array, e.g. `["` or `[]`.
113
- if !matches!(json.as_bytes().get(1)?, b'"' | b']') {
114
- return None;
115
- }
116
-
117
- let mut labels = SmallVec::new();
118
-
119
- // Split on commas into:
120
- // "label_a","label_b"
121
- // ^^^one^^^ ^^^two^^^
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..)?;
139
- }
140
-
141
- if !remainder.starts_with("],[") {
142
- return None;
143
- }
144
- // Now: "value_a", "value_b"]]
145
- remainder = remainder.get(3..)?;
146
-
147
- // Validate we don't have extra brackets.
148
- if remainder.starts_with('[') {
149
- return None;
150
- }
151
-
152
- let mut values = SmallVec::new();
153
- // Split on commas into:
154
- // "value_a","value_b"
155
- // ^^^one^^^ ^^^two^^^
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..];
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..)?;
185
- }
186
-
187
- if values.len() != labels.len() {
188
- return None;
189
- }
190
-
191
- // Now: ]]
192
- if remainder != "]]" {
193
- return None;
194
- }
195
-
196
- Some(MetricLabelVals { labels, values })
197
- }
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
-
241
- #[cfg(test)]
242
- mod test {
243
- use smallvec::smallvec;
244
-
245
- use super::*;
246
-
247
- struct TestCase {
248
- name: &'static str,
249
- input: &'static str,
250
- expected: Option<MetricText<'static>>,
251
- }
252
-
253
- #[test]
254
- fn valid_json() {
255
- let tc = vec![
256
- TestCase {
257
- name: "basic",
258
- input: r#"["metric","name",["label_a","label_b"],["value_a","value_b"]]"#,
259
- expected: Some(MetricText {
260
- family_name: "metric",
261
- metric_name: "name",
262
- labels: smallvec!["label_a", "label_b"],
263
- values: smallvec!["value_a", "value_b"],
264
- }),
265
- },
266
- TestCase {
267
- name: "many labels",
268
- input: r#"["metric","name",["label_a","label_b","label_c","label_d","label_e"],["value_a","value_b","value_c","value_d","value_e"]]"#,
269
-
270
- expected: Some(MetricText {
271
- family_name: "metric",
272
- metric_name: "name",
273
- labels: smallvec!["label_a", "label_b", "label_c", "label_d", "label_e"],
274
- values: smallvec!["value_a", "value_b", "value_c", "value_d", "value_e"],
275
- }),
276
- },
277
- TestCase {
278
- name: "numeric value",
279
- input: r#"["metric","name",["label_a","label_b"],["value_a",403]]"#,
280
- expected: Some(MetricText {
281
- family_name: "metric",
282
- metric_name: "name",
283
- labels: smallvec!["label_a", "label_b"],
284
- values: smallvec!["value_a", "403"],
285
- }),
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
- },
297
- TestCase {
298
- name: "null value",
299
- input: r#"["metric","name",["label_a","label_b"],[null,"value_b"]]"#,
300
- expected: Some(MetricText {
301
- family_name: "metric",
302
- metric_name: "name",
303
- labels: smallvec!["label_a", "label_b"],
304
- values: smallvec!["null", "value_b"],
305
- }),
306
- },
307
- TestCase {
308
- name: "no labels",
309
- input: r#"["metric","name",[],[]]"#,
310
- expected: Some(MetricText {
311
- family_name: "metric",
312
- metric_name: "name",
313
- labels: smallvec![],
314
- values: smallvec![],
315
- }),
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
- },
347
- ];
348
-
349
- for case in tc {
350
- assert_eq!(
351
- parse_metrics(case.input),
352
- case.expected,
353
- "test case: {}",
354
- case.name,
355
- );
356
- }
357
- }
358
-
359
- #[test]
360
- fn invalid_json() {
361
- let tc = vec![
362
- TestCase {
363
- name: "not json",
364
- input: "hello, world",
365
- expected: None,
366
- },
367
- TestCase {
368
- name: "no names",
369
- input: r#"[["label_a","label_b"],["value_a","value_b"]]"#,
370
- expected: None,
371
- },
372
- TestCase {
373
- name: "too many names",
374
- input: r#"["metric","name","unexpected_name",["label_a","label_b"],["value_a","value_b"]]"#,
375
- expected: None,
376
- },
377
- TestCase {
378
- name: "too many labels",
379
- input: r#"["metric","name","unexpected_name",["label_a","label_b","label_c"],["value_a","value_b"]]"#,
380
- expected: None,
381
- },
382
- TestCase {
383
- name: "too many values",
384
- input: r#"["metric","name",["label_a","label_b"],["value_a","value_b",null]]"#,
385
- expected: None,
386
- },
387
- TestCase {
388
- name: "no values",
389
- input: r#"["metric","name",["label_a","label_b"]"#,
390
- expected: None,
391
- },
392
- TestCase {
393
- name: "no arrays",
394
- input: r#"["metric","name","label_a","value_a"]"#,
395
- expected: None,
396
- },
397
- TestCase {
398
- name: "too many leading brackets",
399
- input: r#"[["metric","name",["label_a","label_b"],["value_a","value_b"]]"#,
400
- expected: None,
401
- },
402
- TestCase {
403
- name: "too many trailing brackets",
404
- input: r#"["metric","name",["label_a","label_b"],["value_a","value_b"]]]"#,
405
- expected: None,
406
- },
407
- TestCase {
408
- name: "too many leading label brackets",
409
- input: r#"["metric","name",[["label_a","label_b"],["value_a","value_b"]]"#,
410
- expected: None,
411
- },
412
- TestCase {
413
- name: "too many trailing label brackets",
414
- input: r#"["metric","name",["label_a","label_b"]],["value_a","value_b"]]"#,
415
- expected: None,
416
- },
417
- TestCase {
418
- name: "too many leading value brackets",
419
- input: r#"["metric","name",["label_a","label_b"],[["value_a","value_b"]]"#,
420
- expected: None,
421
- },
422
- TestCase {
423
- name: "misplaced bracket",
424
- input: r#"["metric","name",["label_a","label_b"],]["value_a","value_b"]]"#,
425
- expected: None,
426
- },
427
- TestCase {
428
- name: "comma in numeric value",
429
- input: r#"["metric","name",["label_a","label_b"],[400,0,"value_b"]]"#,
430
- expected: None,
431
- },
432
- TestCase {
433
- name: "non-e letter in numeric value",
434
- input: r#"["metric","name",["label_a","label_b"],[400x0,"value_b"]]"#,
435
- expected: None,
436
- },
437
- ];
438
-
439
- for case in tc {
440
- assert_eq!(
441
- case.expected,
442
- parse_metrics(case.input),
443
- "test case: {}",
444
- case.name,
445
- );
446
- }
447
- }
448
- }